package railo.runtime;

import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;

import railo.commons.collection.HashMapPro;
import railo.commons.collection.MapFactory;
import railo.commons.collection.MapPro;
import railo.commons.io.DevNullOutputStream;
import railo.commons.lang.CFTypes;
import railo.commons.lang.ExceptionUtil;
import railo.commons.lang.Pair;
import railo.commons.lang.SizeOf;
import railo.commons.lang.StringUtil;
import railo.commons.lang.types.RefBoolean;
import railo.commons.lang.types.RefBooleanImpl;
import railo.runtime.component.ComponentLoader;
import railo.runtime.component.DataMember;
import railo.runtime.component.InterfaceCollection;
import railo.runtime.component.Member;
import railo.runtime.component.MetaDataSoftReference;
import railo.runtime.component.MetadataUtil;
import railo.runtime.component.Property;
import railo.runtime.config.ConfigImpl;
import railo.runtime.config.ConfigWeb;
import railo.runtime.config.ConfigWebImpl;
import railo.runtime.config.NullSupportHelper;
import railo.runtime.debug.DebugEntryTemplate;
import railo.runtime.dump.DumpData;
import railo.runtime.dump.DumpProperties;
import railo.runtime.dump.DumpTable;
import railo.runtime.dump.DumpUtil;
import railo.runtime.dump.SimpleDumpData;
import railo.runtime.engine.ThreadLocalPageContext;
import railo.runtime.exp.ApplicationException;
import railo.runtime.exp.ExpressionException;
import railo.runtime.exp.PageException;
import railo.runtime.functions.dynamicEvaluation.EvaluateComponent;
import railo.runtime.functions.system.ContractPath;
import railo.runtime.interpreter.CFMLExpressionInterpreter;
import railo.runtime.op.Caster;
import railo.runtime.op.Duplicator;
import railo.runtime.op.Operator;
import railo.runtime.op.ThreadLocalDuplication;
import railo.runtime.op.date.DateCaster;
import railo.runtime.thread.ThreadUtil;
import railo.runtime.type.ArrayImpl;
import railo.runtime.type.Collection;
import railo.runtime.type.FunctionArgument;
import railo.runtime.type.KeyImpl;
import railo.runtime.type.Sizeable;
import railo.runtime.type.Struct;
import railo.runtime.type.StructImpl;
import railo.runtime.type.UDF;
import railo.runtime.type.UDFGSProperty;
import railo.runtime.type.UDFImpl;
import railo.runtime.type.UDFPlus;
import railo.runtime.type.UDFProperties;
import railo.runtime.type.UDFPropertiesImpl;
import railo.runtime.type.cfc.ComponentAccess;
import railo.runtime.type.cfc.ComponentAccessEntryIterator;
import railo.runtime.type.cfc.ComponentAccessValueIterator;
import railo.runtime.type.comparator.ArrayOfStructComparator;
import railo.runtime.type.dt.DateTime;
import railo.runtime.type.it.StringIterator;
import railo.runtime.type.scope.Argument;
import railo.runtime.type.scope.ArgumentImpl;
import railo.runtime.type.scope.ArgumentIntKey;
import railo.runtime.type.scope.Variables;
import railo.runtime.type.util.ArrayUtil;
import railo.runtime.type.util.ComponentUtil;
import railo.runtime.type.util.KeyConstants;
import railo.runtime.type.util.ListUtil;
import railo.runtime.type.util.PropertyFactory;
import railo.runtime.type.util.StructSupport;
import railo.runtime.type.util.StructUtil;
import railo.runtime.type.util.UDFUtil;

/**
 * %**%
 * MUST add handling for new attributes (style, namespace, serviceportname, porttypename, wsdlfile, bindingname, and output)
 */ 
public final class ComponentImpl extends StructSupport implements Externalizable,ComponentAccess,coldfusion.runtime.TemplateProxy,Sizeable {
	private static final long serialVersionUID = -245618330485511484L; // do not change this


	/*
	 * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
	 * Any change here must be changed in the method writeExternal,readExternal as well
	 * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
	 * */
	private ComponentProperties properties;
	private MapPro<Key,Member> _data;
    private MapPro<Key,UDF> _udfs;

	ComponentImpl top=this;
    ComponentImpl base;
    private PageSource pageSource;
    private ComponentScope scope;
    
    // for all the same
    private int dataMemberDefaultAccess;
	private Boolean _triggerDataMember;
    	
	// state control of component
	boolean isInit=false;

	private InterfaceCollection interfaceCollection;


	private boolean useShadow;
	private boolean entity;
	boolean afterConstructor;
	private Map<Key,UDF> constructorUDFs;
	private boolean loaded;
	private boolean hasInjectedFunctions;



	
	public long sizeOf() {
		return 
			SizeOf.size(properties)+
			SizeOf.size(_data)+
			SizeOf.size(scope)+
			SizeOf.size(dataMemberDefaultAccess)+
			SizeOf.size(false)+
			SizeOf.size(interfaceCollection)+
			SizeOf.size(useShadow)+
			SizeOf.size(entity)+
			SizeOf.size(afterConstructor)+
			SizeOf.size(base);
	}
	
    /**
     * Constructor of the Component, USED ONLY FOR DESERIALIZE
     */
	 public ComponentImpl() {
	 }
	
    /**
     * Constructor of the class
     * @param componentPage
     * @param output
     * @param _synchronized
     * @param extend
     * @param implement
     * @param hint
     * @param dspName
     * @param callPath
     * @param realPath
     * @param style
     * @param meta
     * @throws ApplicationException
     */

    public ComponentImpl(ComponentPage componentPage,Boolean output,boolean _synchronized, 
    		String extend, String implement, String hint, String dspName,String callPath, boolean realPath, 
    		String style,StructImpl meta) throws ApplicationException {
    	this(componentPage,output,_synchronized, 
        		extend, implement, hint, dspName,callPath, realPath, style,false, false,meta);
    }
    
    public ComponentImpl(ComponentPage componentPage,Boolean output,boolean _synchronized, 
    		String extend, String implement, String hint, String dspName,String callPath, boolean realPath, 
    		String style,boolean persistent,StructImpl meta) throws ApplicationException {
    	this(componentPage,output,_synchronized, 
        		extend, implement, hint, dspName,callPath, realPath, style,persistent, false,meta);
    }
	 
    public ComponentImpl(ComponentPage componentPage,Boolean output,boolean _synchronized, 
    		String extend, String implement, String hint, String dspName,String callPath, boolean realPath, 
    		String style,boolean persistent,boolean accessors,StructImpl meta) throws ApplicationException {
    	this.properties=new ComponentProperties(dspName,extend.trim(),implement,hint,output,callPath,realPath,_synchronized,null,persistent,accessors,meta);
    	//this.componentPage=componentPage instanceof ComponentPageProxy?componentPage:PageProxy.toProxy(componentPage);
    	this.pageSource=componentPage.getPageSource();
    	
    	if(!StringUtil.isEmpty(style) && !"rpc".equals(style))
    		throw new ApplicationException("style ["+style+"] is not supported, only the following styles are supported: [rpc]");
    }
    

    @Override

    public Collection duplicate(boolean deepCopy) {
    	ComponentImpl top= _duplicate(deepCopy,true);
    	setTop(top,top);
    	
		
		return top;
    }
	
    
    
    
    private ComponentImpl _duplicate( boolean deepCopy, boolean isTop) {
    	ComponentImpl trg=new ComponentImpl();
    	ThreadLocalDuplication.set(this, trg);
    	try{
			// attributes
	    	trg.pageSource=pageSource;
	        trg._triggerDataMember=_triggerDataMember;
	        trg.useShadow=useShadow;
	        trg.entity=entity;
	        trg.hasInjectedFunctions=hasInjectedFunctions;
	        trg.afterConstructor=afterConstructor;
	        trg.dataMemberDefaultAccess=dataMemberDefaultAccess;
			trg.properties=properties.duplicate();
			trg.isInit=isInit;
			trg.interfaceCollection=interfaceCollection;
	    	
			boolean useShadow=scope instanceof ComponentScopeShadow;
			if(!useShadow)trg.scope=new ComponentScopeThis(trg);
			
	    	if(base!=null){
				trg.base=base._duplicate(deepCopy,false);
				
				trg._data=trg.base._data;
				trg._udfs=duplicateUTFMap(this,trg, _udfs,new HashMapPro<Key,UDF>(trg.base._udfs));

	    		if(useShadow) trg.scope=new ComponentScopeShadow(trg,(ComponentScopeShadow)trg.base.scope,false);
			}
	    	else {
	    		// clone data member, ignore udfs for the moment
	    		trg._data=duplicateDataMember(trg, _data, new HashMapPro(), deepCopy);
	    		trg._udfs=duplicateUTFMap(this,trg, _udfs,new HashMapPro<Key, UDF>());
	    		
	    		if(useShadow) {
	    			ComponentScopeShadow css = (ComponentScopeShadow)scope;
	    			trg.scope=new ComponentScopeShadow(trg,duplicateDataMember(trg,css.getShadow(),MapFactory.getConcurrentMap(),deepCopy));
	    		}
	    	}
	    	
	    	// at the moment this makes no sense, becuae this map is no more used after constructor has runned and for a clone the constructo is not executed, but perhaps this is used in future
	    	if(constructorUDFs!=null){
	    		trg.constructorUDFs=new HashMap<Collection.Key, UDF>();
	    		addUDFS(trg, constructorUDFs, trg.constructorUDFs);
	    	}
			
			
	    	if(isTop) {
	    		setTop(trg,trg);
	    		
	    		addUDFS(trg,_data,trg._data);
	    		if(useShadow){
	    			addUDFS(trg,((ComponentScopeShadow)scope).getShadow(),((ComponentScopeShadow)trg.scope).getShadow());
	    		}
	    	}
	    	
	    	
	    	
	    	
    	}
    	finally {
    		// ThreadLocalDuplication.remove(this); removed "remove" to catch sisters and brothers
    	}
    	
		return trg;
	}
    
    
    private static void addUDFS(ComponentImpl trgComp, Map src, Map trg) {
		Iterator it = src.entrySet().iterator();
		Map.Entry entry;
		Object key,value;
		UDF udf;
		ComponentImpl comp,owner;
		boolean done;
    	while(it.hasNext()){
    		entry=(Entry) it.next();
    		key=entry.getKey();
    		value=entry.getValue();
    		if(value instanceof UDF) {
    			udf=(UDF) value;
    			done=false;
    			// get udf from _udf
    			owner = (ComponentImpl)udf.getOwnerComponent();
    			if(owner!=null) {
	    			comp=trgComp;
	    			do{
	    				if(owner.pageSource==comp.pageSource)
	    					break;
	    			}
	    			while((comp=comp.base)!=null);
	    			if(comp!=null) {
	    				value=comp._udfs.get(key);
	    				trg.put(key, value);
	    				done=true;
	    			}
    			}
    			// udf with no owner
    			if(!done) 
    				trg.put(key, udf.duplicate());
    			
    			//print.o(owner.pageSource.getComponentName()+":"+udf.getFunctionName());
    		}
    	}
	}

    /**
     * duplicate the datamember in the map, ignores the udfs
     * @param c
     * @param map
     * @param newMap
     * @param deepCopy
     * @return
     */
    public static MapPro duplicateDataMember(ComponentImpl c,MapPro map,MapPro newMap,boolean deepCopy){
        Iterator it=map.entrySet().iterator();
        Map.Entry entry;
        Object value;
        while(it.hasNext()) {
            entry=(Entry) it.next();
        	value=entry.getValue();
            
            if(!(value instanceof UDF)) {
            	if(deepCopy) value=Duplicator.duplicate(value,deepCopy);
            	newMap.put(entry.getKey(),value);
            }
        }
        return newMap;
    }
    
    public static MapPro<Key, UDF> duplicateUTFMap(ComponentImpl src,ComponentImpl trg,MapPro<Key,UDF> srcMap, MapPro<Key, UDF> trgMap){
    	Iterator<Entry<Key, UDF>> it = srcMap.entrySet().iterator();
        Entry<Key, UDF> entry;
        UDF udf;
        while(it.hasNext()) {
            entry=it.next();
            udf=entry.getValue();
        	
            if(udf.getOwnerComponent()==src) {
            	UDFPlus clone=(UDFPlus) entry.getValue().duplicate();
        		clone.setOwnerComponent(trg);
        		clone.setAccess(udf.getAccess());
        		trgMap.put(entry.getKey(),clone);	
            }
        	
        }
        return trgMap;
    }
    

	/**
     * initalize the Component
     * @param pageContext
     * @param componentPage
     * @throws PageException
     */    
    public void init(PageContext pageContext, ComponentPage componentPage) throws PageException {
    	//this.componentPage=componentPage;
    	this.pageSource=componentPage.getPageSource();

        // extends
	    if(!StringUtil.isEmpty(properties.extend)) {
			base= ComponentLoader.loadComponent(pageContext,componentPage.getPageSource(),properties.extend,Boolean.TRUE,null);
		}
	    else { 
	    	Page p=((ConfigWebImpl)pageContext.getConfig()).getBaseComponentPage(pageContext);
	    	if(!componentPage.getPageSource().equals(p.getPageSource())) {
            	base=ComponentLoader.loadComponent(pageContext,p,p.getPageSource(),"Component",false);
	        } 
	    }
    	
	    if(base!=null){
	    	this.dataMemberDefaultAccess=base.dataMemberDefaultAccess;
	    	this._triggerDataMember=base._triggerDataMember;
	    	_data=base._data;
	    	_udfs=new HashMapPro<Key,UDF>(base._udfs);
	    	setTop(this,base);
	    }
	    else {
	    	this.dataMemberDefaultAccess=pageContext.getConfig().getComponentDataMemberDefaultAccess();
	    	// TODO get per CFC setting this._triggerDataMember=pageContext.getConfig().getTriggerComponentDataMember();
		    _udfs=new HashMapPro<Key,UDF>();
		    _data=MapFactory.getConcurrentMap();
	    }
	    
	    // implements
	    if(!StringUtil.isEmpty(properties.implement)) {
	    	interfaceCollection=new InterfaceCollection((PageContextImpl)pageContext,getPageSource(),properties.implement);
	    }
	    
	    // scope
	    if(useShadow=pageContext.getConfig().useComponentShadow()) {
	        if(base==null) scope=new ComponentScopeShadow(this,MapFactory.getConcurrentMap());
		    else scope=new ComponentScopeShadow(this,(ComponentScopeShadow)base.scope,false);
	    }
	    else {
	    	scope=new ComponentScopeThis(this);
	    }
	    initProperties();
	}
    
    public void checkInterface(PageContext pc, ComponentPage componentPage) throws PageException {
    	if(interfaceCollection==null || interfaceCollection.lastUpdate()<=componentPage.lastCheck()) return;
    	
	    Iterator it = interfaceCollection.getUdfs().entrySet().iterator();
	    Map.Entry entry;
	    UDFPlus iUdf,cUdf;
	    FunctionArgument[] iFA,cFA;
    	while(it.hasNext()){
    		
    		entry=(Entry) it.next();
    		iUdf=(UDFPlus) entry.getValue();
    		cUdf=(UDFPlus) _udfs.get(entry.getKey());
    		
    		// UDF does not exist
    		if(cUdf==null ) {
    			throw new ExpressionException(
      					 "component ["+componentPage.getPageSource().getDisplayPath()+
      					 "] does not implement the function ["+iUdf.toString().toLowerCase()+"] of the interface ["+
      					 iUdf.getPageSource().getDisplayPath()+"]");
      					
    		}
    		
    		iFA=iUdf.getFunctionArguments();
    		cFA=cUdf.getFunctionArguments();
    // access
    		if(cUdf.getAccess()>Component.ACCESS_PUBLIC){
    			throw new ExpressionException( _getErrorMessage(cUdf,iUdf),
    				"access ["+ComponentUtil.toStringAccess(cUdf.getAccess())+"] has to be at least [public]");
    		}
    		
    // return type
    		if(iUdf.getReturnType()!=cUdf.getReturnType()){
				throw new ExpressionException( _getErrorMessage(cUdf,iUdf),
					"return type ["+cUdf.getReturnTypeAsString()+"] does not match interface function return type ["+iUdf.getReturnTypeAsString()+"]");
			}
			// none base types
			if(iUdf.getReturnType()==CFTypes.TYPE_UNKNOW && !iUdf.getReturnTypeAsString().equalsIgnoreCase(cUdf.getReturnTypeAsString())) {
				throw new ExpressionException( _getErrorMessage(cUdf,iUdf),
					"return type ["+cUdf.getReturnTypeAsString()+"] does not match interface function return type ["+iUdf.getReturnTypeAsString()+"]");
			}
	// output
    		if(iUdf.getOutput()!=cUdf.getOutput()){
				throw new ExpressionException( _getErrorMessage(cUdf,iUdf),
					"output does not match interface function output definition");
			}
			
    // arguments
    		if(iFA.length!=cFA.length) {
    			throw new ExpressionException( _getErrorMessage(cUdf,iUdf),"not the same argument count");
   			}
    		
    		for(int i=0;i<iFA.length;i++) {
    			// type
    			if(iFA[i].getType()!=cFA[i].getType()){
    				throw new ExpressionException( _getErrorMessage(cUdf,iUdf),
    					"argument type ["+cFA[i].getTypeAsString()+"] does not match interface function argument type ["+iFA[i].getTypeAsString()+"]");
    			}
    			// none base types
    			if(iFA[i].getType()==CFTypes.TYPE_UNKNOW && !iFA[i].getTypeAsString().equalsIgnoreCase(cFA[i].getTypeAsString())) {
    				throw new ExpressionException( _getErrorMessage(cUdf,iUdf),
        					"argument type ["+cFA[i].getTypeAsString()+"] does not match interface function argument type ["+iFA[i].getTypeAsString()+"]");
        		}
    			// name
    			if(!iFA[i].getName().equalsIgnoreCase(cFA[i].getName())){
    				throw new ExpressionException( _getErrorMessage(cUdf,iUdf),
    					"argument name ["+cFA[i].getName()+"] does not match interface function argument name ["+iFA[i].getName()+"]");
    			}
    			// required
    			if(iFA[i].isRequired()!=cFA[i].isRequired()){
    				throw new ExpressionException( _getErrorMessage(cUdf,iUdf),
    					"argument ["+cFA[i].getName()+"] should "+(iFA[i].isRequired()?"":"not ")+"be required");
    			}
    		}
    	}
	    componentPage.ckecked();
    }

	private String _getErrorMessage(UDFPlus cUdf,UDFPlus iUdf) {
		return "function ["+cUdf.toString().toLowerCase()+"] of component " +
			 "["+pageSource.getDisplayPath()+"]" +
			 " does not match the function declaration ["+iUdf.toString().toLowerCase()+"] of the interface " +
			 "["+iUdf.getPageSource().getDisplayPath()+"]";
	}


	private static void setTop(ComponentImpl top,ComponentImpl trg) {
		while(trg!=null){
			trg.top=top;
			trg=trg.base;
		}
	}

    Object _call(PageContext pc, Collection.Key key, Struct namedArgs, Object[] args,boolean superAccess) throws PageException {
    	
    	Member member=getMember(pc,key,false, superAccess);
    	
    	if(member instanceof UDFPlus) {
    		return _call(pc,key,(UDFPlus)member,namedArgs,args);
        }
        return onMissingMethod(pc, -1, member, key.getString(), args, namedArgs, superAccess);
    }

    Object _call(PageContext pc, int access, Collection.Key key, Struct namedArgs, Object[] args,boolean superAccess) throws PageException {
        Member member=getMember(access,key,false,superAccess);
        if(member instanceof UDF) {
            return _call(pc,key,(UDFPlus)member,namedArgs,args);
        }
        return onMissingMethod(pc, access, member, key.getString(), args, namedArgs, superAccess);
    }
    
    public Object onMissingMethod(PageContext pc, int access,Member member,String name,Object _args[],Struct _namedArgs, boolean superAccess) throws PageException {
    	Member ommm = access==-1?
    			getMember(pc,KeyConstants._onmissingmethod,false, superAccess):
    			getMember(access,KeyConstants._onmissingmethod,false, superAccess);
        if(ommm instanceof UDF) {
        	Argument args=new ArgumentImpl();
        	if(_args!=null) {
        		for(int i=0;i<_args.length;i++) {
        			args.setEL(ArgumentIntKey.init(i+1), _args[i]);
        		}
        	}
        	else if(_namedArgs!=null) {
        		UDFUtil.argumentCollection(_namedArgs, new FunctionArgument[]{});
        		
        		Iterator<Entry<Key, Object>> it = _namedArgs.entryIterator();
        		Entry<Key, Object> e;
        		while(it.hasNext()){
        			e = it.next();
        			args.setEL(e.getKey(),e.getValue());
        		}
        		
        	}
        	
        	//Struct newArgs=new StructImpl(StructImpl.TYPE_SYNC);
        	//newArgs.setEL(MISSING_METHOD_NAME, name);
        	//newArgs.setEL(MISSING_METHOD_ARGS, args); 
        	Object[] newArgs=new Object[]{name,args};
        	
        	return _call(pc,KeyConstants._onmissingmethod,(UDFPlus)ommm,null,newArgs);
        }
        if(member==null)throw ComponentUtil.notFunction(this, KeyImpl.init(name), null,access);
        throw ComponentUtil.notFunction(this, KeyImpl.init(name), member.getValue(),access);
    }
	
	Object _call(PageContext pc, Collection.Key calledName,UDFPlus udf, Struct namedArgs, Object[] args) throws PageException {
			
		Object rtn=null;
		Variables parent=null;
        
		// INFO duplicate code is for faster execution -> less contions
		
		
		// debug yes
		if(pc.getConfig().debug()) {
		    DebugEntryTemplate debugEntry=pc.getDebugger().getEntry(pc,pageSource,udf.getFunctionName());//new DebugEntry(src,udf.getFunctionName());
			int currTime=pc.getExecutionTime();
			long time=System.nanoTime();
			
			// sync yes
			if(top.properties._synchronized){
				synchronized (this) {
					try {
						parent=beforeCall(pc);
						if(args!=null)rtn=udf.call(pc,calledName,args,true);
						else rtn=udf.callWithNamedValues(pc,calledName,namedArgs,true);
					}		
					finally {
						pc.setVariablesScope(parent);
						int diff= ((int)(System.nanoTime()-time)-(pc.getExecutionTime()-currTime));
						pc.setExecutionTime(pc.getExecutionTime()+diff);
						debugEntry.updateExeTime(diff);	
					}	
				}
			}

			// sync no
			else {
				try {
					parent=beforeCall(pc);
					if(args!=null)rtn=udf.call(pc,calledName,args,true);
					else rtn=udf.callWithNamedValues(pc,calledName,namedArgs,true);
				}		
				finally {
					pc.setVariablesScope(parent);
					int diff= ((int)(System.nanoTime()-time)-(pc.getExecutionTime()-currTime));
					pc.setExecutionTime(pc.getExecutionTime()+diff);
					debugEntry.updateExeTime(diff);	
				}	
			}
			
			
		}
		
		// debug no
		else {
			
			// sync yes
			if(top.properties._synchronized){
				synchronized (this) {
				    try {
		            	parent=beforeCall(pc); 
		            	if(args!=null)rtn=udf.call(pc,calledName,args,true);
						else rtn=udf.callWithNamedValues(pc,calledName,namedArgs,true);
					}		
					finally {
						pc.setVariablesScope(parent);
					}
				}
			}
			
			// sync no 385|263
			else {
			    try {
	            	parent=beforeCall(pc);
	            	if(args!=null)rtn=udf.call(pc,calledName,args,true);
					else rtn=udf.callWithNamedValues(pc,calledName,namedArgs,true);
				}		
				finally {
					pc.setVariablesScope(parent);
				}
			}
		}
		return rtn;
	}
	
	/**
     * will be called before executing method or constructor
     * @param pc
     * @return the old scope map
     */
	public Variables beforeCall(PageContext pc) {
    	Variables parent=pc.variablesScope();
    	pc.setVariablesScope(scope);
    	return parent;
    }
	
	/**
     * will be called after invoking constructor, only invoked by constructor (component body execution)
	 * @param pc
	 * @param parent
	 */
    public void afterConstructor(PageContext pc, Variables parent) {
    	pc.setVariablesScope(parent);
    	this.afterConstructor=true;
    	
    	if(constructorUDFs!=null){
    		Iterator<Entry<Key, UDF>> it = constructorUDFs.entrySet().iterator();
    		Map.Entry<Key, UDF> entry;
    		Key key;
    		UDFPlus udf;
    		while(it.hasNext()){
    			entry=it.next();
    			key=entry.getKey();
    			udf=(UDFPlus) entry.getValue();
    			registerUDF(key, udf,false,true);
    		}
    	}
	}
    
    /**
     * this function may be called by generated code inside a ra file
     * @deprecated replaced with <code>afterConstructor(PageContext pc, Variables parent)</code>
     * @param pc
     * @param parent
     */
    public void afterCall(PageContext pc, Variables parent) {
    	afterConstructor(pc, parent);
	}
	
    /**
     * sets the callpath
     * @param callPath
     * /
    public void setCallPath(String callPath) {
		properties.callPath=callPath;
	}*/

	/**
     * rerturn the size
	 * @param access
	 * @return size
	 */
    public int size(int access) {
	    return keys(access).length;
	}

    /**
     * list of keys
     * @param c 
     * @param access
     * @param doBase 
     * @return key set
     */
	public Set<Key> keySet(int access) {
    	HashSet<Key> set=new HashSet<Key>();
        Map.Entry<Key, Member> entry;    
        Iterator<Entry<Key, Member>> it = _data.entrySet().iterator();
        while(it.hasNext()) {
            entry=it.next();
            if(entry.getValue().getAccess()<=access)set.add(entry.getKey());
        }
        return set;
    }
    
    /*protected Set<Key> udfKeySet(int access) {
    	Set<Key> set=new HashSet<Key>();
        Member m;
        Map.Entry<Key, UDF> entry;
        Iterator<Entry<Key, UDF>> it = _udfs.entrySet().iterator();
        while(it.hasNext()) {
            entry= it.next();
            m=entry.getValue();
            if(m.getAccess()<=access)set.add(entry.getKey());
        }
        return set;
    }*/
    
    
    protected java.util.List<Member> getMembers(int access) {
        java.util.List<Member> members=new ArrayList<Member>();
        Member e;
        Iterator<Entry<Key, Member>> it = _data.entrySet().iterator();
        while(it.hasNext()) {
        	e=it.next().getValue();
            if(e.getAccess()<=access)members.add(e);
        }
        return members;
    }


    @Override
	public Iterator<Collection.Key> keyIterator(int access) {
        return keySet(access).iterator();
    }
    
    @Override
	public Iterator<String> keysAsStringIterator(int access) {
        return new StringIterator(keys(access));
    }

	@Override
	public Iterator<Entry<Key, Object>> entryIterator(int access) {
		return new ComponentAccessEntryIterator(this, keys(access),access);
	}

	@Override
	public Iterator<Object> valueIterator(int access) {
		return new ComponentAccessValueIterator(this,keys(access),access);
	}

	
	@Override
	public Iterator<Object> valueIterator() {
		return valueIterator(getAccess(ThreadLocalPageContext.get()));
	}

	@Override
	 public Collection.Key[] keys(int access) {
        Set<Key> set = keySet(access);
        return set.toArray(new Collection.Key[set.size()]);
    }
	
	@Override
	public void clear() {
		_data.clear();
		_udfs.clear();
	}

	@Override
	public Member getMember(int access,Collection.Key key, boolean dataMember,boolean superAccess) {
    	// check super
        if(dataMember && access==ACCESS_PRIVATE && key.equalsIgnoreCase(KeyConstants._super)) {
        	return SuperComponent.superMember((ComponentImpl)ComponentUtil.getActiveComponent(ThreadLocalPageContext.get(),this)._base());
            //return SuperComponent . superMember(base);
        }
    	if(superAccess) {
    		return  _udfs.get(key);
        }
        // check data
        Member member=_data.get(key);
        if(member!=null) {
            if(member.getAccess()<=access)return member;
            return null;
        }
        return null;
    }


	/**
     * get entry matching key
     * @param access 
     * @param keyLowerCase key lower case (case sensitive)
     * @param doBase do check also base component
     * @param dataMember do also check if key super
     * @return matching entry if exists otherwise null
     */
	protected Member getMember(PageContext pc, Collection.Key key, boolean dataMember,boolean superAccess) {
        // check super
        if(dataMember && isPrivate(pc) && key.equalsIgnoreCase(KeyConstants._super)) {
        	return SuperComponent.superMember((ComponentImpl)ComponentUtil.getActiveComponent(pc,this)._base());
        }
        if(superAccess) 
        	return  _udfs.get(key);
        
        // check data
        Member member=_data.get(key);
        if(isAccessible(pc,member)) return member;
        return null;
    }
    
    private boolean isAccessible(PageContext pc, Member member) {
        // TODO geschwindigkeit 
    	if(member!=null) {
    		int access=member.getAccess();
    		if(access<=ACCESS_PUBLIC) return true;
    		else if(access==ACCESS_PRIVATE && isPrivate(pc)) return true;
    		else if(access==ACCESS_PACKAGE && isPackage(pc)) return true;
        }
        return false;
	}
    
    private boolean isAccessible(PageContext pc, int access) {
    	if(access<=ACCESS_PUBLIC) return true;
		else if(access==ACCESS_PRIVATE && isPrivate(pc)) return true;
		else if(access==ACCESS_PACKAGE && isPackage(pc)) return true;
    	return false;
	}

    /**
     * @param pc
     * @return returns if is private
     */
    private boolean isPrivate(PageContext pc) {
    	if(pc==null) return true;
    	Component ac = pc.getActiveComponent();
    	return (ac!=null && (ac==this || 
                ((ComponentImpl)ac).top.pageSource.equals(top.pageSource))) ;
    }
    /**
     * @param pc
     * @return returns if is package
     */
    private boolean isPackage(PageContext pc) {
        Component ac = pc.getActiveComponent();
        if(ac!=null) {
            if(ac==this) return true;
            ComponentImpl aci = ((ComponentImpl)ac);
            if(aci.top.pageSource.equals(top.pageSource))return true;
            
            int index;
            String other=aci.top.getAbsName();
            index=other.lastIndexOf('.');
            if(index==-1)other="";
            else other=other.substring(0,index);
            
            String my=top.getAbsName();
            index=my.lastIndexOf('.');
            if(index==-1)my="";
            else my=my.substring(0,index);
            
            return my.equalsIgnoreCase(other);
        }
        return false;
    }
    
	/**
	 * return the access of a member
	 * @param key
	 * @return returns the access (Component.ACCESS_REMOTE, ACCESS_PUBLIC, ACCESS_PACKAGE,Component.ACCESS_PRIVATE)
	 */
	private int getAccess(Collection.Key key){
        Member member=getMember(ACCESS_PRIVATE,key,false,false);
        if(member==null) return Component.ACCESS_PRIVATE;
        return member.getAccess();
	}
    
    /** 
     * returns current access to this component
     * @param pc
     * @return access
     */
    private int getAccess(PageContext pc) {
        if(pc==null) return ACCESS_PUBLIC;
        
        if(isPrivate(pc)) return ACCESS_PRIVATE;
        if(isPackage(pc)) return ACCESS_PACKAGE;
        return ACCESS_PUBLIC;
    }
	
    @Override
	public DumpData toDumpData(PageContext pageContext, int maxlevel, DumpProperties dp) {
		return toDumpData(pageContext,maxlevel,dp,getAccess(pageContext));
    }
    

    /**
     * to html output print only with access less than given access
     * @param pageContext
     * @param access
     * @return html output
     */
    public DumpData toDumpData(PageContext pageContext, int maxlevel, DumpProperties dp, int access) {
	    DumpTable table = new DumpTable("component","#99cc99","#ccffcc","#000000");
        table.setTitle("Component "+getCallPath()+""+(" "+StringUtil.escapeHTML(top.properties.dspName)));
        table.setComment("Only the functions and data members that are accessible from your location are displayed");
        if(top.properties.extend.length()>0)table.appendRow(1,new SimpleDumpData("Extends"),new SimpleDumpData(top.properties.extend));
        if(top.properties.hint.trim().length()>0)table.appendRow(1,new SimpleDumpData("Hint"),new SimpleDumpData(top.properties.hint));
        
        DumpTable content = _toDumpData(top,pageContext,maxlevel,dp,access);
        if(!content.isEmpty())table.appendRow(1,new SimpleDumpData(""),content);
        return table;
    }
    
	static DumpTable _toDumpData(ComponentImpl ci,PageContext pc, int maxlevel, DumpProperties dp,int access) {
		maxlevel--;
		ComponentWrap cw=new ComponentWrap(Component.ACCESS_PRIVATE, ci);
		Collection.Key[] keys= cw.keys();
		
		
		
		DumpTable[] accesses=new DumpTable[4];
		accesses[Component.ACCESS_PRIVATE] = new DumpTable("#ff6633","#ff9966","#000000");
		accesses[Component.ACCESS_PRIVATE].setTitle("private");
		accesses[Component.ACCESS_PRIVATE].setWidth("100%");
		//accesses[Component.ACCESS_PRIVATE].setRow(1,"100%");
		accesses[Component.ACCESS_PACKAGE] = new DumpTable("#ff9966","#ffcc99","#000000");
		accesses[Component.ACCESS_PACKAGE].setTitle("package");
		accesses[Component.ACCESS_PACKAGE].setWidth("100%");
		accesses[Component.ACCESS_PUBLIC] = new DumpTable("#ffcc99","#ffffcc","#000000");
		accesses[Component.ACCESS_PUBLIC].setTitle("public");
		accesses[Component.ACCESS_PUBLIC].setWidth("100%");
		accesses[Component.ACCESS_REMOTE] = new DumpTable("#ccffcc","#ffffff","#000000");
		accesses[Component.ACCESS_REMOTE].setTitle("remote");
		accesses[Component.ACCESS_REMOTE].setWidth("100%");
		
		Collection.Key key;
		for(int i=0;i<keys.length;i++) {
			key=keys[i];
			int a=ci.getAccess(key);
			DumpTable box=accesses[a];
			Object o=cw.get(key,null);
			if(o==ci)o="[this]";
			if(DumpUtil.keyValid(dp,maxlevel, key))
				box.appendRow(1,new SimpleDumpData(key.getString()),DumpUtil.toDumpData(o,pc,maxlevel,dp));
		}
		
		
		DumpTable table=new DumpTable("#ffffff","#cccccc","#000000");
		
		// properties
		if(ci.top.properties.persistent || ci.top.properties.accessors){
			Property[] properties=ci.getProperties(false);
			DumpTable prop = new DumpTable("#99cc99","#ccffcc","#000000");

			prop.setTitle("Properties");
			prop.setWidth("100%");
			Property p;
			Object child;
			for(int i=0;i<properties.length;i++) {
				p=properties[i];
				child = ci.scope.get(KeyImpl.init(p.getName()),null);
				DumpData dd;
				if(child instanceof Component) {
					DumpTable t = new DumpTable("component","#99cc99","#ffffff","#000000");
					t.appendRow(1,new SimpleDumpData("Component"),new SimpleDumpData(((Component)child).getCallName()));
					dd=t;
					
				}
				else 
					dd=DumpUtil.toDumpData(child, pc, maxlevel-1, dp);
				
				
				
				prop.appendRow(1, new SimpleDumpData(p.getName()),dd);
			}
			
			if(access>=ACCESS_PUBLIC && !prop.isEmpty()) {
				table.appendRow(0,prop);
			}
		}
		

		

		if(!accesses[ACCESS_REMOTE].isEmpty()) {
			table.appendRow(0,accesses[Component.ACCESS_REMOTE]);
		}
		if(!accesses[ACCESS_PUBLIC].isEmpty()) {
			table.appendRow(0,accesses[Component.ACCESS_PUBLIC]);
		}
		if(!accesses[ACCESS_PACKAGE].isEmpty()) {
			table.appendRow(0,accesses[Component.ACCESS_PACKAGE]);
		}
		if(!accesses[ACCESS_PRIVATE].isEmpty()) {
			table.appendRow(0,accesses[Component.ACCESS_PRIVATE]);
		}
		return table;
	}
	
	/**
	 * @return return call path
	 */
	protected String getCallPath() {
		if(StringUtil.isEmpty(top.properties.callPath)) return getName();
		try {
            return "("+ListUtil.arrayToList(ListUtil.listToArrayTrim(top.properties.callPath.replace('/','.').replace('\\','.'),"."),".")+")";
        } catch (PageException e) {
            return top.properties.callPath;
        }
	}

    @Override
    public String getDisplayName() {
		return top.properties.dspName;
	}
	
    @Override
    public String getExtends() {
		return top.properties.extend;
	}
    public String getBaseAbsName() {
		return top.base.pageSource.getComponentName();
	}
    
    public boolean isBasePeristent() {
		return top.base!=null && top.base.properties.persistent;
	}
	
	
    @Override
    public String getHint() {
		return top.properties.hint;
	}
    
    @Override
    public String getWSDLFile() {
		return top.properties.getWsdlFile();
	}

    @Override
    public String getName() {
	    if(top.properties.callPath==null) return "";
	    return ListUtil.last(top.properties.callPath,"./",true);
	}
    public String _getName() { // MUST nicht so toll
	    if(properties.callPath==null) return "";
	    return ListUtil.last(properties.callPath,"./",true);
	}
    public PageSource _getPageSource() {
    	return pageSource;
	}
	
    @Override
    public String getCallName() {
	    return top.properties.callPath;
	}
    
    @Override
    public String getAbsName() {
    	return top.pageSource.getComponentName();
	}
    

    @Override
    public boolean getOutput() {
    	if(top.properties.output==null) return true;
        return top.properties.output.booleanValue();
    }

    @Override
    public boolean instanceOf(String type) {
    	
    	ComponentImpl c=top;
    	do {
        	if(type.equalsIgnoreCase(c.properties.callPath)) return true;
            if(type.equalsIgnoreCase(c.pageSource.getComponentName())) return true;
            if(type.equalsIgnoreCase(c._getName())) return true;       
            
    		// check interfaces
    		if(c.interfaceCollection!=null){
	    		InterfaceImpl[] interfaces = c.interfaceCollection.getInterfaces();
	    		if(interfaces!=null)for(int i=0;i<interfaces.length;i++){
	        		if(interfaces[i].instanceOf(type))return true;
	        	}
    		}
    		c=c.base;
    	}
    	while(c!=null);
    	if(StringUtil.endsWithIgnoreCase(type, "component")){
    		if(type.equalsIgnoreCase("component"))							return true;
    		if(type.equalsIgnoreCase("web-inf.cftags.component"))			return true;
    		//if(type.equalsIgnoreCase("web-inf.railo.context.component"))	return true;
    		
    	}
    	return false;
    }
    
    public boolean equalTo(String type) {
    	ComponentImpl c=top;
    	
    	if(type.equalsIgnoreCase(c.properties.callPath)) return true;
        if(type.equalsIgnoreCase(c.pageSource.getComponentName())) return true;
        if(type.equalsIgnoreCase(c._getName())) return true;       
            
		// check interfaces
		if(c.interfaceCollection!=null){
    		InterfaceImpl[] interfaces = c.interfaceCollection.getInterfaces();
    		if(interfaces!=null)for(int i=0;i<interfaces.length;i++){
        		if(interfaces[i].instanceOf(type))return true;
        	}
		}
		
    	if(StringUtil.endsWithIgnoreCase(type, "component")){
    		if(type.equalsIgnoreCase("component"))							return true;
    		if(type.equalsIgnoreCase("web-inf.cftags.component"))			return true;
    	}
    	return false;
    }
    

    @Override
    public boolean isValidAccess(int access) {
		return !(access <0 || access>ACCESS_COUNT);
	}
    
    @Override
    public PageSource getPageSource() {
        return top.pageSource;
    }
    

    @Override
    public String castToString() throws PageException {
    	return castToString(false);
    }
    
	@Override
	public String castToString(String defaultValue) {
		return castToString(false,defaultValue);
	}
    
    String castToString(boolean superAccess) throws PageException {
    	// magic function
    	PageContext pc = ThreadLocalPageContext.get();
		if(pc!=null) {
			Member member = getMember(pc,KeyConstants.__toString,true,superAccess);
			//Object o = get(pc,"_toString",null);
			if(member instanceof UDFPlus) {
				UDFPlus udf = (UDFPlus)member;
				if(udf.getReturnType()==CFTypes.TYPE_STRING && udf.getFunctionArguments().length==0) {
					return Caster.toString(_call(pc, KeyConstants.__toString,udf, null, new Object[0]));
				}
			}
		}
		
		
		throw ExceptionUtil.addHint(new ExpressionException("Can't cast Component ["+getName()+"] to String"),"Add a User-Defined-Function to Component with the following pattern [_toString():String] to cast it to a String or use Built-In-Function \"serialize(Component):String\" to convert it to a serialized String");
        
    }
    
    
    
    
    String castToString(boolean superAccess,String defaultValue) {
    	// magic function
    	PageContext pc = ThreadLocalPageContext.get();
		if(pc!=null) {
			Member member = getMember(pc,KeyConstants.__toString,true,superAccess);
			//Object o = get(pc,"_toString",null);
			if(member instanceof UDFPlus) {
				UDFPlus udf = (UDFPlus)member;
				if(udf.getReturnType()==CFTypes.TYPE_STRING && udf.getFunctionArguments().length==0) {
					try {
						return Caster.toString(_call(pc,KeyConstants.__toString, udf, null, new Object[0]),defaultValue);
					} catch (PageException e) {
						return defaultValue;
					}
				}
			}
		}
		return defaultValue;
    }

    @Override
    public boolean castToBooleanValue() throws PageException {
    	return castToBooleanValue(false);
    }
    
    @Override
    public Boolean castToBoolean(Boolean defaultValue) {
        return castToBoolean(false, defaultValue);
    }

    boolean castToBooleanValue(boolean superAccess) throws PageException {
    	// magic function
    	PageContext pc = ThreadLocalPageContext.get();
		if(pc!=null) {
			Member member = getMember(pc,KeyConstants.__toBoolean,true,superAccess);
			//Object o = get(pc,"_toBoolean",null);
			if(member instanceof UDFPlus) {
				UDFPlus udf = (UDFPlus)member;
				if(udf.getReturnType()==CFTypes.TYPE_BOOLEAN && udf.getFunctionArguments().length==0) {
					return Caster.toBooleanValue(_call(pc, KeyConstants.__toBoolean,udf, null, new Object[0]));
				}
			}
		}
    	
        throw ExceptionUtil.addHint(new ExpressionException("Can't cast Component ["+getName()+"] to a boolean value"),
                "Add a User-Defined-Function to Component with the following pattern [_toBoolean():boolean] to cast it to a boolean value");
    }
    
    Boolean castToBoolean(boolean superAccess,Boolean defaultValue) {
    	// magic function
    	PageContext pc = ThreadLocalPageContext.get();
		if(pc!=null) {
			Member member = getMember(pc,KeyConstants.__toBoolean,true,superAccess);
			//Object o = get(pc,"_toBoolean",null);
			if(member instanceof UDFPlus) {
				UDFPlus udf = (UDFPlus)member;
				if(udf.getReturnType()==CFTypes.TYPE_BOOLEAN && udf.getFunctionArguments().length==0) {
					try {
						return Caster.toBoolean(_call(pc,KeyConstants.__toBoolean, udf, null, new Object[0]),defaultValue);
					} catch (PageException e) {
						return defaultValue;
					}
				}
			}
		}
    	return defaultValue;
    }

    @Override
    public double castToDoubleValue() throws PageException {
    	return castToDoubleValue(false);
    }
    
    @Override
    public double castToDoubleValue(double defaultValue) {
        return castToDoubleValue(false, defaultValue);
    }
    
    
    double castToDoubleValue(boolean superAccess) throws PageException {
    	// magic function
    	PageContext pc = ThreadLocalPageContext.get();
		if(pc!=null) {
			Member member = getMember(pc,KeyConstants.__toNumeric,true,superAccess);
			//Object o = get(pc,"_toNumeric",null);
			if(member instanceof UDFPlus) {
				UDFPlus udf = (UDFPlus)member;
				if(udf.getReturnType()==CFTypes.TYPE_NUMERIC && udf.getFunctionArguments().length==0) {
					return Caster.toDoubleValue(_call(pc, KeyConstants.__toNumeric,udf, null, new Object[0]));
				}
			}
		}
    
        throw ExceptionUtil.addHint(new ExpressionException("Can't cast Component ["+getName()+"] to a numeric value"),
                "Add a User-Defined-Function to Component with the following pattern [_toNumeric():numeric] to cast it to a numeric value");
    }
    double castToDoubleValue(boolean superAccess,double defaultValue) {
    	// magic function
    	PageContext pc = ThreadLocalPageContext.get();
		if(pc!=null) {
			Member member = getMember(pc,KeyConstants.__toNumeric,true,superAccess);
			//Object o = get(pc,"_toNumeric",null);
			if(member instanceof UDFPlus) {
				UDFPlus udf = (UDFPlus)member;
				if(udf.getReturnType()==CFTypes.TYPE_NUMERIC && udf.getFunctionArguments().length==0) {
					try {
						return Caster.toDoubleValue(_call(pc, KeyConstants.__toNumeric,udf, null, new Object[0]),defaultValue);
					} catch (PageException e) {
						return defaultValue;
					}
				}
			}
		}
		return defaultValue;
    }

    @Override
    public DateTime castToDateTime() throws PageException {
    	return castToDateTime(false);
    }
    
    @Override
    public DateTime castToDateTime(DateTime defaultValue) {
        return castToDateTime(false, defaultValue);
    }

    DateTime castToDateTime(boolean superAccess) throws PageException {
    	// magic function
    	PageContext pc = ThreadLocalPageContext.get();
		if(pc!=null) {
			Member member = getMember(pc,KeyConstants.__toDateTime,true,superAccess);
			//Object o = get(pc,"_toDateTime",null);
			if(member instanceof UDFPlus) {
				UDFPlus udf = (UDFPlus)member;
				if(udf.getReturnType()==CFTypes.TYPE_DATETIME && udf.getFunctionArguments().length==0) {
					return Caster.toDate(_call(pc, KeyConstants.__toDateTime,udf, null, new Object[0]),pc.getTimeZone());
				}
			}
		}
    
		throw ExceptionUtil.addHint(new ExpressionException("Can't cast Component ["+getName()+"] to a date"),
                "Add a User-Defined-Function to Component with the following pattern [_toDateTime():datetime] to cast it to a date");
    }
    DateTime castToDateTime(boolean superAccess,DateTime defaultValue) {
    	// magic function
    	PageContext pc = ThreadLocalPageContext.get();
		if(pc!=null) {
			Member member = getMember(pc,KeyConstants.__toDateTime,true,superAccess);
			//Object o = get(pc,"_toDateTime",null);
			if(member instanceof UDFPlus) {
				UDFPlus udf = (UDFPlus)member;
				if(udf.getReturnType()==CFTypes.TYPE_DATETIME && udf.getFunctionArguments().length==0) {
					
					try {
						return DateCaster.toDateAdvanced(_call(pc, KeyConstants.__toDateTime,udf, null, new Object[0]),true,pc.getTimeZone(),defaultValue);
					} catch (PageException e) {
						return defaultValue;
					}
					
				}
			}
		}
		return defaultValue;
    }

    @Override
    public synchronized Struct getMetaData(PageContext pc) throws PageException {
    	return getMetaData(ACCESS_PRIVATE,pc,top,false);
    }
    

    public synchronized Object getMetaStructItem(Collection.Key name) {
    	if(top.properties.meta!=null) {
        	return top.properties.meta.get(name,null);
        }
    	return null;
    }

    protected static Struct getMetaData(int access,PageContext pc, ComponentImpl comp, boolean ignoreCache) throws PageException {
    	// Cache
    	Page page=MetadataUtil.getPageWhenMetaDataStillValid(pc, comp, ignoreCache);
    	if(page!=null && page.metaData!=null && page.metaData.get()!=null){
    		return page.metaData.get();
    	}
    	long creationTime=System.currentTimeMillis(); 
    	StructImpl sct=new StructImpl();
    	
        // fill udfs
        metaUDFs(pc, comp, sct,access);
        
        // meta
        if(comp.properties.meta!=null) 
        	StructUtil.copy(comp.properties.meta, sct, true);
            
        String hint=comp.properties.hint;
        String displayname=comp.properties.dspName;
        if(!StringUtil.isEmpty(hint))sct.set(KeyConstants._hint,hint);
        if(!StringUtil.isEmpty(displayname))sct.set(KeyConstants._displayname,displayname);
        
        sct.set(KeyConstants._persistent,comp.properties.persistent);
        sct.set(KeyConstants._hashCode,comp.hashCode());
        sct.set(KeyConstants._accessors,comp.properties.accessors);
        sct.set(KeyConstants._synchronized,comp.properties._synchronized);
        if(comp.properties.output!=null)
        sct.set(KeyConstants._output,comp.properties.output);
            
        // extends
        Struct ex=null;
        if(comp.base!=null) ex=getMetaData(access,pc,comp.base,true);
        if(ex!=null)sct.set(KeyConstants._extends,ex);
        
        // implements
        InterfaceCollection ic = comp.interfaceCollection;
        if(ic!=null){
        	Set<String> set = ListUtil.listToSet(comp.properties.implement, ",",true);
            InterfaceImpl[] interfaces = comp.interfaceCollection.getInterfaces();
            if(!ArrayUtil.isEmpty(interfaces)){
	            Struct imp=new StructImpl();
            	for(int i=0;i<interfaces.length;i++){
            		if(!set.contains(interfaces[i].getCallPath())) continue;
            		//print.e("-"+interfaces[i].getCallPath());
            		imp.setEL(KeyImpl.init(interfaces[i].getCallPath()), interfaces[i].getMetaData(pc,true));
	            }
	            sct.set(KeyConstants._implements,imp);
            }
        }
         
        // PageSource
        PageSource ps = comp.pageSource;
        sct.set(KeyConstants._fullname,ps.getComponentName());
        sct.set(KeyConstants._name,ps.getComponentName());
        sct.set(KeyConstants._path,ps.getDisplayPath());
        sct.set(KeyConstants._type,"component");
            
        Class<?> skeleton = comp.getJavaAccessClass(pc,new RefBooleanImpl(false),((ConfigImpl)pc.getConfig()).getExecutionLogEnabled(),false,false,((ConfigImpl)pc.getConfig()).getSupressWSBeforeArg());
        if(skeleton !=null)sct.set(KeyConstants._skeleton, skeleton);
        
        HttpServletRequest req = pc.getHttpServletRequest();
            try {
            	String path=ContractPath.call(pc, ps.getDisplayPath()); // MUST better impl !!!
				sct.set("remoteAddress",""+new URL(req.getScheme(),req.getServerName(),req.getServerPort(),req.getContextPath()+path+"?wsdl"));
			} catch (Throwable t) {}
            
        
        // Properties
        if(comp.properties.properties!=null) {
        	ArrayImpl parr = new ArrayImpl();
        	Property p;
        	Iterator<Entry<String, Property>> pit = comp.properties.properties.entrySet().iterator();
        	while(pit.hasNext()){
        		p=pit.next().getValue();
        		parr.append(p.getMetaData());
        	}
        	parr.sort(new ArrayOfStructComparator(KeyConstants._name));
        	sct.set(KeyConstants._properties,parr);
        }

        page.metaData=new MetaDataSoftReference<Struct>(sct,creationTime);
        return sct;
    }    



	

	private static void metaUDFs(PageContext pc,ComponentImpl comp,Struct sct, int access) throws PageException {
    	ArrayImpl arr=new ArrayImpl();
    	//Collection.Key name;
        
    	Page page = ((PageSourceImpl)comp._getPageSource()).getPage();
    	if(page!=null && page.udfs!=null){
    		for(int i=0;i<page.udfs.length;i++){
    			if(page.udfs[i].getAccess()>access) continue;
        		arr.append(ComponentUtil.getMetaData(pc,(UDFPropertiesImpl) page.udfs[i]));
    		}
    	}
    	
    	// property functions
    	Iterator<Entry<Key, UDF>> it = comp._udfs.entrySet().iterator();
        Entry<Key, UDF> entry;
		UDF udf;
		while(it.hasNext()) {
    		entry= it.next();
    		udf=entry.getValue();
            if(udf.getAccess()>access || !(udf instanceof UDFGSProperty)) continue;
    			if(comp.base!=null) {
            		if(udf==comp.base.getMember(access,entry.getKey(),true,true))
            			continue;
            	}
            	arr.append(udf.getMetaData(pc));
            
        }
        if(arr.size()!=0)sct.set(KeyConstants._functions,arr);
	}

	public boolean isInitalized() {
        return isInit;
    }
    
    public void setInitalized(boolean isInit) {
        this.isInit=isInit;;
    }
    
        
    /**
     * sets a value to the current Component, dont to base Component
     * @param key
     * @param value
     * @return value set
     * @throws ExpressionException 
     */
    private synchronized Object _set(PageContext pc,Collection.Key key, Object value) throws ExpressionException {
    	if(value instanceof UDFPlus) {
        	UDFPlus udf = (UDFPlus)((UDFPlus)value).duplicate();
        	//udf.isComponentMember(true);///+++
        	udf.setOwnerComponent(this);
        	if(udf.getAccess()>Component.ACCESS_PUBLIC)
        		udf.setAccess(Component.ACCESS_PUBLIC);
        	_data.put(key,udf);
        	_udfs.put(key,udf);
        	hasInjectedFunctions=true;
        }
        else {
        	if(loaded && !isAccessible(ThreadLocalPageContext.get(pc), dataMemberDefaultAccess))
        		throw new ExpressionException("Component ["+getCallName()+"] has no accessible Member with name ["+key+"]","enable [trigger data member] in admininistrator to also invoke getters and setters");
            
        	_data.put(key,new DataMember(dataMemberDefaultAccess,value));
        }
        return value;
    }

    

    public void reg(Collection.Key key, UDFPlus udf) {
    	registerUDF(key, udf,useShadow,false);
    }
    public void reg(String key, UDFPlus udf) {
    	registerUDF(KeyImpl.init(key), udf,useShadow,false);
    }

    public void registerUDF(String key, UDF udf) {
    	registerUDF(KeyImpl.init(key), (UDFPlus) udf,useShadow,false);
    }
    public void registerUDF(String key, UDFProperties prop) {
    	registerUDF(KeyImpl.init(key), new UDFImpl( prop),useShadow,false);
    }

    public void registerUDF(Collection.Key key, UDF udf) {
    	registerUDF(key, (UDFPlus) udf,useShadow,false);
    }
    public void registerUDF(Collection.Key key, UDFProperties prop) {
    	registerUDF(key, new UDFImpl( prop),useShadow,false);
    }
    
    /*
     *  @deprecated injected is not used
     */
    public void registerUDF(Collection.Key key, UDFPlus udf,boolean useShadow,boolean injected) {
    	udf.setOwnerComponent(this);//+++
    	_udfs.put(key,udf);
    	_data.put(key,udf);
    	if(useShadow)scope.setEL(key, udf);
    }
    
	@Override
	public Object remove(Key key) throws PageException {
    	return _data.remove(key);
	}

    public Object removeEL(Collection.Key key) {
    	// MUST access muss beruecksichtigt werden
    	return _data.remove(key);
    }

    /*public Object set(PageContext pc, String name, Object value) throws PageException {
    	return set(pc, KeyImpl.init(name), value);
    }*/

    @Override
    public Object set(PageContext pc, Collection.Key key, Object value) throws PageException {
    	if(pc==null)pc=ThreadLocalPageContext.get();
    	if(triggerDataMember(pc) && isInit) {
    		if(!isPrivate(pc)) {
        		return callSetter(pc, key, value);
        	}
        }
    	return _set(pc,key,value);
    }

	@Override
	public Object set(Collection.Key key, Object value) throws PageException {
        return set(null,key,value);
	}

    /*public Object setEL(PageContext pc, String name, Object value) {
    	try {return set(pc, name, value);} 
    	catch (PageException e) {return null;}
    }*/
    
    @Override
    public Object setEL(PageContext pc, Collection.Key name, Object value) {
    	try {return set(pc, name, value);} 
    	catch (PageException e) {return null;}
    }

	@Override
	public Object setEL(Key key, Object value) {
    	return setEL(null, key, value);
	}
	
	@Override
	public final Object put(Object key, Object value) {
		// TODO find a better solution
		// when a orm entity the data given by put or also written to the variables scope
		if(entity) {
			getComponentScope().put(key, value);
		}
		return super.put(key, value);
	}
    
    /*public Object get(PageContext pc, String name) throws PageException {
        return get(pc, KeyImpl.init(name));
    }*/
    
    public Object get(PageContext pc, Collection.Key key) throws PageException {
        Member member=getMember(pc,key,true,false);
        if(member!=null) return member.getValue();
        
        // trigger
        if(triggerDataMember(pc) && !isPrivate(pc)) {
        	return callGetter(pc,key);
        }
        throw new ExpressionException("Component ["+getCallName()+"] has no accessible Member with name ["+key+"]","enable [trigger data member] in admininistrator to also invoke getters and setters");
        //throw new ExpressionException("Component ["+getCallName()+"] has no accessible Member with name ["+name+"]");
    }

    private Object callGetter(PageContext pc,Collection.Key key) throws PageException {
    	Key getterName = KeyImpl.getInstance("get"+key.getLowerString());
    	Member member=getMember(pc,getterName,false,false);
        if(member instanceof UDFPlus) {
            UDFPlus udf = (UDFPlus)member;
            if(udf.getFunctionArguments().length==0 && udf.getReturnType()!=CFTypes.TYPE_VOID) {
                return _call(pc,getterName,udf,null,ArrayUtil.OBJECT_EMPTY);
            }
        } 
        throw new ExpressionException("Component ["+getCallName()+"] has no accessible Member with name ["+key+"]");
	}
    
    private Object callGetter(PageContext pc,Collection.Key key, Object defaultValue) {
    	Key getterName = KeyImpl.getInstance("get"+key.getLowerString());
    	Member member=getMember(pc,getterName,false,false);
        if(member instanceof UDFPlus) {
            UDFPlus udf = (UDFPlus)member;
            if(udf.getFunctionArguments().length==0 && udf.getReturnType()!=CFTypes.TYPE_VOID) {
                try {
					return _call(pc,getterName,udf,null,ArrayUtil.OBJECT_EMPTY);
				} catch (PageException e) {
					return defaultValue;
				}
            }
        } 
        return defaultValue;
	}
    
    private Object callSetter(PageContext pc,Collection.Key key, Object value) throws PageException {
    	Collection.Key setterName = KeyImpl.getInstance("set"+key.getLowerString());
    	Member member=getMember(pc,setterName,false,false);
    	if(member instanceof UDFPlus) {
        	UDFPlus udf = (UDFPlus)member;
        	if(udf.getFunctionArguments().length==1 && (udf.getReturnType()==CFTypes.TYPE_VOID) || udf.getReturnType()==CFTypes.TYPE_ANY   ) {// TDOO support int return type
                return _call(pc,setterName,udf,null,new Object[]{value});
            }    
        }
        return _set(pc,key,value);
	}
    

	/**
     * return element that has at least given access or null
     * @param access
     * @param name
     * @return matching value
     * @throws PageException
     */
    public Object get(int access, String name) throws PageException {
        return get(access, KeyImpl.init(name));
    }
    
    public Object get(int access, Collection.Key key) throws PageException {
        Member member=getMember(access,key,true,false);
        if(member!=null) return member.getValue();
        
        // Trigger
        PageContext pc = ThreadLocalPageContext.get();
        if(triggerDataMember(pc) && !isPrivate(pc)) {
        	return callGetter(pc,key);
        }
        throw new ExpressionException("Component ["+getCallName()+"] has no accessible Member with name ["+key+"]");
    }

    /*public Object get(PageContext pc, String name, Object defaultValue) {
        return get(pc, KeyImpl.init(name), defaultValue);
    }*/

    @Override
    public Object get(PageContext pc, Collection.Key key, Object defaultValue) {
        Member member=getMember(pc,key,true,false);
        if(member!=null) return member.getValue();
        
        // trigger
        if(triggerDataMember(pc) && !isPrivate(pc)) {
        	return callGetter(pc,key,defaultValue);
        }
        return defaultValue;
    }
    
    /**
     * return element that has at least given access or null
     * @param access
     * @param name
     * @return matching value
     */
    protected Object get(int access, String name, Object defaultValue) {
        return get(access, KeyImpl.init(name), defaultValue);
    }

    /**
     * @param access
     * @param key
     * @param defaultValue
     * @return
     */
    public Object get(int access, Collection.Key key, Object defaultValue) { 
        Member member=getMember(access,key,true,false);
        if(member!=null) return member.getValue();
        
        // trigger
        PageContext pc = ThreadLocalPageContext.get();
        if(triggerDataMember(pc) && !isPrivate(pc)) {
        	return callGetter(pc,key,defaultValue);
        }
        return defaultValue;
    }
    
	@Override
	public Object get(Collection.Key key) throws PageException {
    	return get(ThreadLocalPageContext.get(),key);
	}

	@Override
	public Object get(Collection.Key key, Object defaultValue) {
    	return get(ThreadLocalPageContext.get(),key,defaultValue);
	}

    @Override
    public Object call(PageContext pc, String name, Object[] args) throws PageException {
        return _call(pc,KeyImpl.init(name),null,args,false);
    }

	public Object call(PageContext pc, Collection.Key name, Object[] args) throws PageException {
		return _call(pc,name,null,args,false);
	}
    
    protected Object call(PageContext pc, int access, String name, Object[] args) throws PageException {
        return _call(pc,access,KeyImpl.init(name),null,args,false);
    }
    
    public Object call(PageContext pc, int access, Collection.Key name, Object[] args) throws PageException {
        return _call(pc,access,name,null,args,false);
    }

    @Override
    public Object callWithNamedValues(PageContext pc, String name, Struct args) throws PageException {
        return _call(pc,KeyImpl.init(name),args,null,false);
    }

    public Object callWithNamedValues(PageContext pc, Collection.Key methodName, Struct args) throws PageException {
		return _call(pc,methodName,args,null,false);
	}
    
    protected Object callWithNamedValues(PageContext pc, int access, String name, Struct args) throws PageException {
        return _call(pc,access,KeyImpl.init(name),args,null,false);
    }
    
    public Object callWithNamedValues(PageContext pc, int access, Collection.Key name, Struct args) throws PageException {
        return _call(pc,access,name,args,null,false);
    }

    public boolean contains(PageContext pc,String name) {
       	return get(getAccess(pc),name,NullSupportHelper.NULL())!=NullSupportHelper.NULL();
    }

	/**
	 * @param pc
	 * @param key
	 * @return
	 */
	public boolean contains(PageContext pc,Key key) {
	   	return get(getAccess(pc),key,NullSupportHelper.NULL())!=NullSupportHelper.NULL();
	}
	
	@Override
	public boolean containsKey(Key key) {
	   	return contains(ThreadLocalPageContext.get(),key);
	}
    
    public boolean contains(int access,String name) {
    	return get(access,name,NullSupportHelper.NULL())!=NullSupportHelper.NULL();
   }
    
    public boolean contains(int access,Key name) {
    	return get(access,name,NullSupportHelper.NULL())!=NullSupportHelper.NULL();
    }

    @Override
	public Iterator<Collection.Key> keyIterator() {
    	return keyIterator(getAccess(ThreadLocalPageContext.get()));
    }
    
	@Override
	public Iterator<String> keysAsStringIterator() {
    	return keysAsStringIterator(getAccess(ThreadLocalPageContext.get()));
    }
	
	@Override
	public Iterator<Entry<Key, Object>> entryIterator() {
		return entryIterator(getAccess(ThreadLocalPageContext.get()));
	}

    public Collection.Key[] keys() {
    	return keys(getAccess(ThreadLocalPageContext.get()));
    }

    @Override
    public int size() {
    	return size(getAccess(ThreadLocalPageContext.get()));
    }

    
    @Override
    public Class getJavaAccessClass(RefBoolean isNew) throws PageException {
    	return getJavaAccessClass(ThreadLocalPageContext.get(),isNew, false,true,true,true);
    }
    
    public Class getJavaAccessClass(PageContext pc,RefBoolean isNew) throws PageException {
    	return getJavaAccessClass(pc,isNew, false,true,true,true);
    }

    public Class getJavaAccessClass(PageContext pc,RefBoolean isNew,boolean writeLog, boolean takeTop, boolean create, boolean supressWSbeforeArg) throws PageException {
    	isNew.setValue(false);
    	ComponentProperties props =(takeTop)?top.properties:properties;
    	if(props.javaAccessClass==null) {
    		props.javaAccessClass=ComponentUtil.getComponentJavaAccess(pc,this,isNew,create,writeLog,supressWSbeforeArg);
		}
    	return props.javaAccessClass;
    }
    
    public boolean isPersistent() {
    	return top.properties.persistent;
    }
    
    public boolean isAccessors() {
    	return top.properties.accessors;
    }

	public void setProperty(Property property) throws PageException {
		top.properties.properties.put(StringUtil.toLowerCase(property.getName()),property);
		if(top.properties.persistent || top.properties.accessors){
			if(property.getDefault()!=null)scope.setEL(KeyImpl.init(property.getName()), property.getDefault());
			PropertyFactory.createPropertyUDFs(this,property);
		}
	}

	

	private void initProperties() throws PageException {
		top.properties.properties=new LinkedHashMap<String,Property>();
		
		// MappedSuperClass  
		if(isPersistent() && !isBasePeristent() && top.base!=null && top.base.properties.properties!=null && top.base.properties.meta!=null) {
			boolean msc = Caster.toBooleanValue(top.base.properties.meta.get(KeyConstants._mappedSuperClass,Boolean.FALSE),false);
			if(msc){
				Property p;
				Iterator<Entry<String, Property>> it = top.base.properties.properties.entrySet().iterator();
				while(it.hasNext())	{
					p = it.next().getValue();
					if(p.isPeristent()) {
						
						setProperty(p);
					}
				}
			}
		}
	}

	public Property[] getProperties(boolean onlyPeristent) {
		return getProperties(onlyPeristent, false,false,false);
	}

	public Property[] getProperties(boolean onlyPeristent, boolean includeBaseProperties, boolean preferBaseProperties, boolean inheritedMappedSuperClassOnly) {
		Map<String,Property> props=new LinkedHashMap<String,Property>();
		_getProperties(top,props,onlyPeristent, includeBaseProperties, preferBaseProperties, inheritedMappedSuperClassOnly);
		return props.values().toArray(new Property[props.size()]);
	}

	private static void _getProperties(ComponentImpl c,Map<String,Property> props,boolean onlyPeristent, boolean includeBaseProperties, boolean preferBaseProperties, boolean inheritedMappedSuperClassOnly) {
		//if(c.properties.properties==null) return new Property[0];
		
		// collect with filter
		if(c.properties.properties!=null){
			Property p;
			Iterator<Entry<String, Property>> it = c.properties.properties.entrySet().iterator();
			while(it.hasNext())	{
				p = it.next().getValue();
				if(!onlyPeristent || p.isPeristent()) {
					if (!preferBaseProperties || !props.containsKey(p.getName().toLowerCase())) {
						props.put(p.getName().toLowerCase(),p);
					}
				}
			}
		}

		// MZ: Moved to the bottom to allow base properties to override inherited versions
		if(includeBaseProperties && c.base!=null) {
			if (!inheritedMappedSuperClassOnly || (c.base.properties.meta != null && Caster.toBooleanValue(c.base.properties.meta.get(KeyConstants._mappedSuperClass, Boolean.FALSE), false))) {
				_getProperties(c.base, props, onlyPeristent, includeBaseProperties, preferBaseProperties, inheritedMappedSuperClassOnly);
			}
		}

	}

	public ComponentScope getComponentScope() {
		return scope;
	}


	@Override
	public int compareTo(boolean b) throws PageException {
		return Operator.compare(castToBooleanValue(), b);
	}

	@Override
	public int compareTo(DateTime dt) throws PageException {
		return Operator.compare((Date)castToDateTime(), (Date)dt);
	}

	@Override
	public int compareTo(double d) throws PageException {
		return Operator.compare(castToDoubleValue(), d);
	}

	@Override
	public int compareTo(String str) throws PageException {
		return Operator.compare(castToString(), str);
	}

	public void addConstructorUDF(Key key, UDF value) {
		if(constructorUDFs==null)
			constructorUDFs=new HashMap<Key,UDF>();
		constructorUDFs.put(key, value);
	}

// MUST more native impl
	public void readExternal(ObjectInput in) throws IOException,ClassNotFoundException {
		boolean pcCreated=false;
		PageContext pc = ThreadLocalPageContext.get();
		// MUST this is just a workaround
		if(pc==null){
			pcCreated=true;
			ConfigWeb config = (ConfigWeb) ThreadLocalPageContext.getConfig();
			Pair[] parr = new Pair[0];
			pc=ThreadUtil.createPageContext(config, DevNullOutputStream.DEV_NULL_OUTPUT_STREAM, "localhost", "/","", new Cookie[0], parr, parr, new StructImpl());
		}
		
		// reading fails for serialized data from Railo version 4.1.2.002
		String name = in.readUTF();
		
		if(name.startsWith("evaluateComponent('") && name.endsWith("})")) {
			readExternalOldStyle(pc, name);
			return;
		}
		
		String md5 = in.readUTF();
		Struct _this = Caster.toStruct(in.readObject(),null);
		Struct _var = Caster.toStruct(in.readObject(),null);
	    
		try {
			ComponentImpl other=(ComponentImpl)EvaluateComponent.invoke(pc, name, md5, _this,_var);
			_readExternal(other);
		}
		catch (PageException e) {
			throw ExceptionUtil.toIOException(e);
		}
		finally {
			if(pcCreated)ThreadLocalPageContext.release();
		}
	}

	private void readExternalOldStyle(PageContext pc, String str) throws IOException {
		try {
			ComponentImpl other=(ComponentImpl) new CFMLExpressionInterpreter().interpret(pc,str);
			_readExternal(other);
		}
		catch (PageException e) {
			throw ExceptionUtil.toIOException(e);
		}
	}

	private void _readExternal(ComponentImpl other) {
		this._data=other._data;
		this._udfs=other._udfs;
		setOwner(_udfs);
		setOwner(_data);
		this.afterConstructor=other.afterConstructor;
		this.base=other.base;
		//this.componentPage=other.componentPage;
		this.pageSource=other.pageSource;
		this.constructorUDFs=other.constructorUDFs;
		this.dataMemberDefaultAccess=other.dataMemberDefaultAccess;
		this.interfaceCollection=other.interfaceCollection;
		this.isInit=other.isInit;
		this.properties=other.properties;
		this.scope=other.scope;
		this.top=this;
		this._triggerDataMember=other._triggerDataMember;
		this.hasInjectedFunctions=other.hasInjectedFunctions;
		this.useShadow=other.useShadow;
		this.entity=other.entity;
	}

	private void  setOwner(Map<Key,? extends Member> data) {
		Member m;
		Iterator<? extends Member> it = data.values().iterator();
		while(it.hasNext()){
			m=it.next();
			if(m instanceof UDFPlus) {
				((UDFPlus)m).setOwnerComponent(this);
			}
		}
	}

	public void writeExternal(ObjectOutput out) throws IOException {
		ComponentWrap cw = new ComponentWrap(Component.ACCESS_PRIVATE,this);  
        Struct _this=new StructImpl();
		Struct _var=new StructImpl();
		
		
		// this scope (removing all UDFs)
		Object member;
	    {
	    	Iterator<Entry<Key, Object>> it = cw.entryIterator();
	        Entry<Key, Object> e;
	        while(it.hasNext()) {
	            e = it.next();
	            member = e.getValue();
	            if(member instanceof UDF)continue;
	            _this.setEL(e.getKey(), member);
	        }
		}
		
	    
	    // variables scope (removing all UDFs and key "this")
	    {
        	ComponentScope scope = getComponentScope();
        	Iterator<Entry<Key, Object>> it = scope.entryIterator();
            Entry<Key, Object> e;
        	Key k;
        	while(it.hasNext()) {
        		e = it.next();
        		k = e.getKey();
                if(KeyConstants._THIS.equalsIgnoreCase(k))continue;
                member = e.getValue();
                if(member instanceof UDF)continue;
	            _var.setEL(e.getKey(), member);
            }
        }
	    
	    out.writeUTF(getAbsName());
		out.writeUTF(ComponentUtil.md5(cw));
		out.writeObject(_this);
		out.writeObject(_var);
		
	}

	@Override
	public ComponentAccess _base() {
		return base;
	}	
	

	public InterfaceCollection _interfaceCollection() {
		return interfaceCollection;
	}

	private boolean triggerDataMember(PageContext pc) {
		if(_triggerDataMember!=null) return _triggerDataMember.booleanValue();
		if(pc==null || pc.getApplicationContext()==null){
			//print.ds(""+(pc==null));// TODO why this is true sometimes?
			return false;
		}
		return pc.getApplicationContext().getTriggerComponentDataMember();
	}

	public void setLoaded(boolean loaded) {
		this.loaded=loaded;
	}

	public boolean hasInjectedFunctions() {
		return hasInjectedFunctions;
	}

	@Override
	public void setEntity(boolean entity) {
		this.entity=entity;
	}

	@Override
	public boolean isEntity() {
		return entity;
	}
}