/*******************************************************************************
 * Copyright (c) 2007 Exadel, Inc. and Red Hat, Inc.
 * Distributed under license by Red Hat, Inc. All rights reserved.
 * This program is made available under the terms of the
 * Eclipse Public License v1.0 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Exadel, Inc. and Red Hat, Inc. - initial API and implementation
 ******************************************************************************/ 
package org.jboss.tools.common.meta.impl;

import org.w3c.dom.*;
import org.jboss.tools.common.meta.*;
import org.jboss.tools.common.meta.constraint.*;
import org.jboss.tools.common.model.*;
import org.jboss.tools.common.model.plugin.ModelPlugin;
import org.jboss.tools.common.model.util.*;
import org.jboss.tools.common.meta.constraint.impl.*;

public class XAttributeImpl extends XMetaElementImpl implements XAttribute {
	private XModelEntity entity;
    protected XAttributeEditor m_Editor;
    protected String m_DefValue;
    protected boolean m_Visible;
    protected ConstraintHolder constraint = new ConstraintHolder(this);
    protected boolean m_Required;
    protected boolean m_Editable;
    protected boolean trimmable;
    protected boolean copyable;
    protected String m_XMLName;
    protected AdapterHolder adapter = new AdapterHolder();

    public XAttributeImpl() {}
    
    public XAttributeImpl copy() {
    	XAttributeImpl copy = new XAttributeImpl();
    	copy.name = name;
    	copy.displayName = displayName;
    	copy.p = p;
    	copy.m_Editor = m_Editor;
    	copy.m_DefValue = m_DefValue;
    	copy.m_Visible = m_Visible;
    	copy.constraint = constraint;
    	copy.m_Required = m_Required;
    	copy.m_Editable = m_Editable;
    	copy.trimmable = trimmable;
    	copy.copyable = copyable;
    	copy.m_XMLName = m_XMLName;
    	copy.adapter = adapter;
    	return copy;
    }

    public void valueChanged(XModelObject object) {
////        for (int i = 0; i < valuelisteners.length; i++)
////          valuelisteners[i].valueChanged(object, getName());
    }

	public XModelEntity getModelEntity() {
		return entity;
	}
	
	void setModelEntity(XModelEntity entity) {
		this.entity = entity;
	}
	
    public XAttributeEditor getEditor() {
        return m_Editor;
    }

    void setEditor(XAttributeEditor ed) {
        m_Editor = ed;
    }

    public String getDefaultValue() {
        return m_DefValue;
    }

    public void setDefaultValue(String value) {
        m_DefValue = replace(value, "%cr%", "\n");  //$NON-NLS-1$//$NON-NLS-2$
    }

    public boolean isTrimmable() {
        return trimmable;
    }

    public boolean isCopyable() {
        return copyable;
    }

    public boolean isVisible() {
        return m_Visible;
    }

    void setVisible(boolean v) {
        m_Visible = v;
    }

    public XAttributeConstraint getConstraint() {
        return constraint.getConstraint();
    }

    public boolean isRequired() {
        return m_Required;
    }

    void setRequired(boolean req) {
        m_Required = req;
    }

    public boolean isEditable() {
        return m_Editable;
    }

    void setEditable(boolean ed) {
        m_Editable = ed;
    }

    void setXMLName(String xname) {
        m_XMLName = xname;
    }

    public String getXMLName() {
        return m_XMLName;
    }

    public boolean isFake() {
        return "FAKE".equals(m_XMLName); //$NON-NLS-1$
    }

    public void load(Element el){
    	if(XMLUtil.getUniqueChild(el, EDITOR) == null) {
    		XAttributeEditorImpl editor = new XAttributeEditorImpl();
    		editor.setName(null);
    		setEditor(editor);
    	} else {
    		setEditor((XAttributeEditor)XMetaDataLoader.loadMetaElement(el, EDITOR, XAttributeEditorImpl.class, false));
    	}
        Element c = XMetaDataLoader.getUniqueChild(el, CONSTRAINT);
        if (c != null) loadConstraint(c);
        setVisible(XMetaDataLoader.getBoolean(el, VISIBLE, true));
        setRequired(XMetaDataLoader.getBoolean(el, REQUIRED, false));
        setEditable(XMetaDataLoader.getBoolean(el, EDITABLE, false));
        setDefaultValue(el.getAttribute(DEFAULT_VALUE));
        setXMLName(el.getAttribute(XML_NAME));
        loadAdapter(el);
        trimmable = !XModelObjectConstants.NO.equals(el.getAttribute("TRIM"));  //$NON-NLS-1$
        copyable = !XModelObjectConstants.NO.equals(el.getAttribute("COPYABLE"));  //$NON-NLS-1$
        loadProperties(el);
    }

    public String toString(){
        return getName();
    }

    public static final String LOADER = "loader"; //$NON-NLS-1$
    public static final String CONSTRAINT_PREFIX =
       "org.jboss.tools.common.meta.constraint.impl.XAttributeConstraint"; //$NON-NLS-1$
    public static final String ATTRIBUTE_PREFIX =
       "org.jboss.tools.common.meta.impl.adapters.XAdapter"; //$NON-NLS-1$

    public void loadConstraint(Element element) {
        if(element == null) return;
        String loader = element.getAttribute(LOADER);
        if(!XMetaDataLoader.hasAttribute(element, LOADER) || loader.trim().length() == 0) {
        	loader = ""; //$NON-NLS-1$
        }
        if(loader != null && loader.startsWith("%")) { //$NON-NLS-1$
        	loader = expand(loader, "AttributeConstraints"); //$NON-NLS-1$
        }
        constraint.init(loader, element);
    }

    public void loadAdapter(Element element) {
        String loader = element.getAttribute(LOADER);
        if(loader.length() == 0 && getXMLName().length() > 0) loader = "ModelElement"; //$NON-NLS-1$
        adapter.init(this, loader, element);
        loadValueListeners(element);
    }

    public XAdapter getAdapter() {
        return adapter.getAdapter();
    }

    public void loadValueListeners(Element element) {
    }

    private static String replace(String source, String a, String b) {
        if(source == null || source.indexOf(a) < 0) return source;
        StringBuffer sb = new StringBuffer();
        int i = 0, sL = source.length(), aL = a.length();
        while(i < sL) {
            int j = source.indexOf(a, i);
            if(j < 0) {
                sb.append(source.substring(i));
                break;
            }
            sb.append(source.substring(i, j)).append(b);
            i = j + aL;
        }
        return sb.toString();
    }

}

class AdapterHolder {
	private XAttributeImpl attribute;
	private XAdapter adapter;
	private String loader;
	private Element element;
	
	public AdapterHolder() {}

	public void init(XAttributeImpl attribute, String loader, Element element) {
		this.attribute = attribute;
		this.loader = loader;
		this.element = element;
		adapter = null;
	}
	
	public XAdapter getAdapter() {
		if(adapter != null) return adapter;
			doGetAdapter();
		if(adapter == null) adapter = new XAdapter();
		return adapter;
	}

	private synchronized void doGetAdapter() {
		String loader = this.loader;
		Element element = this.element;
		if(this.adapter != null) return;
		XAdapter adapter = null;
        if(loader == null || loader.length() == 0) {
            adapter = new XAdapter();
        } else {
        	String clsname = loader.indexOf('.') >= 0 ? loader : XAttributeImpl.ATTRIBUTE_PREFIX + loader;
            try {
                if(loader.length() > 0) {
                	adapter = (XAdapter)ModelFeatureFactory.getInstance().createFeatureInstance(clsname);
                }
            } catch (ClassCastException e) {
            	ModelPlugin.getPluginLog().logError("XAttributeImpl:loadAdapter:" + e.getMessage()); //$NON-NLS-1$
            }
        }
		if(adapter == null) adapter = new XAdapter();
        if(adapter != null) {
        	adapter.reload(element);
            adapter.setConstraint(attribute.getConstraint());
        }
		
		loader = null;
		this.element = null;
		this.adapter = adapter;
	}

}

class ConstraintHolder {
	XAttributeImpl attribute;
	private XAttributeConstraint constraint;
	private String loader;
	private Element element;
	
	public ConstraintHolder(XAttributeImpl attribute) {
		this.attribute = attribute;
	}

	public void init(String loader, Element element) {
		this.loader = loader;
		this.element = element;
		constraint = null;
	}
	
	public XAttributeConstraint getConstraint() {
		if(constraint == null) {
				doGetConstraint();
			if(constraint == null) constraint = new XAttributeConstraintImpl();
		}
		return constraint;
	}
	
	private void doGetConstraint() {
		String loader = this.loader;
		Element element = this.element;
		if(this.constraint != null) return;
		XAttributeConstraint constraint = null;
        if(loader != null) try {
        	String clsname = loader.indexOf('.') >= 0 ? loader : XAttributeImpl.CONSTRAINT_PREFIX + loader;
            if(loader.length() > 0) {
            	constraint = (XAttributeConstraint)ModelFeatureFactory.getInstance().createFeatureInstance(clsname);
            }
        } catch (ClassCastException e) {
        	ModelPlugin.getPluginLog().logError("XAttributeImpl:loadConstraint:" + e.getMessage(), e); //$NON-NLS-1$
        }
		if(constraint == null) constraint = new XAttributeConstraintImpl();
		
		if(constraint instanceof XAttributeConstraintImpl) {
			((XAttributeConstraintImpl)constraint).setAttribute(attribute);
		}
		if(element != null) 
			((XAttributeConstraintImpl)constraint).load(element);
		loader = null;
		element = null;
		this.constraint = constraint;
	}

}
