001    package railo.runtime.type.util;
002    
003    import java.io.ByteArrayInputStream;
004    import java.io.IOException;
005    import java.lang.reflect.Field;
006    import java.util.ArrayList;
007    import java.util.Arrays;
008    
009    import org.apache.axis.AxisFault;
010    import org.objectweb.asm.ClassWriter;
011    import org.objectweb.asm.Label;
012    import org.objectweb.asm.Opcodes;
013    import org.objectweb.asm.Type;
014    import org.objectweb.asm.commons.GeneratorAdapter;
015    import org.objectweb.asm.commons.Method;
016    
017    import railo.commons.digest.MD5;
018    import railo.commons.io.IOUtil;
019    import railo.commons.io.res.Resource;
020    import railo.commons.io.res.util.ResourceUtil;
021    import railo.commons.lang.ClassUtil;
022    import railo.commons.lang.PhysicalClassLoader;
023    import railo.commons.lang.StringUtil;
024    import railo.commons.lang.types.RefBoolean;
025    import railo.runtime.Component;
026    import railo.runtime.ComponentPro;
027    import railo.runtime.ComponentWrap;
028    import railo.runtime.Mapping;
029    import railo.runtime.Page;
030    import railo.runtime.PageContext;
031    import railo.runtime.PageContextImpl;
032    import railo.runtime.PageSource;
033    import railo.runtime.PageSourceImpl;
034    import railo.runtime.component.Property;
035    import railo.runtime.config.Config;
036    import railo.runtime.engine.ThreadLocalPageContext;
037    import railo.runtime.exp.ExpressionException;
038    import railo.runtime.exp.PageException;
039    import railo.runtime.net.rpc.AxisCaster;
040    import railo.runtime.net.rpc.Pojo;
041    import railo.runtime.net.rpc.server.ComponentController;
042    import railo.runtime.net.rpc.server.RPCServer;
043    import railo.runtime.op.Caster;
044    import railo.runtime.type.Array;
045    import railo.runtime.type.ArrayImpl;
046    import railo.runtime.type.Collection;
047    import railo.runtime.type.Collection.Key;
048    import railo.runtime.type.FunctionArgument;
049    import railo.runtime.type.KeyImpl;
050    import railo.runtime.type.Struct;
051    import railo.runtime.type.StructImpl;
052    import railo.runtime.type.UDF;
053    import railo.runtime.type.UDFPropertiesImpl;
054    import railo.runtime.type.cfc.ComponentAccess;
055    import railo.transformer.bytecode.BytecodeContext;
056    import railo.transformer.bytecode.literal.LitString;
057    import railo.transformer.bytecode.util.ASMProperty;
058    import railo.transformer.bytecode.util.ASMUtil;
059    import railo.transformer.bytecode.util.Types;
060    import railo.transformer.bytecode.visitor.ArrayVisitor;
061    // TODO doc
062    public final class ComponentUtil {
063            
064    
065            private final static Method CONSTRUCTOR_OBJECT = Method.getMethod("void <init> ()");
066            private static final Type COMPONENT_CONTROLLER = Type.getType(ComponentController.class); 
067            private static final Method INVOKE = new Method("invoke",Types.OBJECT,new Type[]{Types.STRING,Types.OBJECT_ARRAY});
068            private static final Collection.Key FIELD_TYPE = KeyConstants._fieldtype;
069            
070            //private static final Method INVOKE_PROPERTY = new Method("invoke",Types.OBJECT,new Type[]{Types.STRING,Types.OBJECT_ARRAY});
071            
072        /**
073         * generate a ComponentJavaAccess (CJA) class from a component
074         * a CJA is a dynamic genarted java class that has all method defined inside a component as java methods.
075             * 
076             * This is used to generated server side Webservices.
077             * @param component
078         * @param isNew 
079         * @return
080         * @throws PageException
081         */
082            public static Class getComponentJavaAccess(PageContext pc,ComponentAccess component, RefBoolean isNew,boolean create,boolean writeLog, boolean supressWSbeforeArg) throws PageException {
083            isNew.setValue(false);
084            String classNameOriginal=component.getPageSource().getFullClassName();
085            String className=getClassname(component).concat("_wrap");
086            String real=className.replace('.','/');
087            String realOriginal=classNameOriginal.replace('.','/');
088            Mapping mapping = component.getPageSource().getMapping();
089                    PhysicalClassLoader cl=null;
090                    try {
091                            cl = (PhysicalClassLoader) ((PageContextImpl)pc).getRPCClassLoader(false);
092                    } catch (IOException e) {
093                            throw Caster.toPageException(e);
094                    }
095                    Resource classFile = cl.getDirectory().getRealResource(real.concat(".class"));
096                    Resource classFileOriginal = mapping.getClassRootDirectory().getRealResource(realOriginal.concat(".class"));
097                    
098            // LOAD CLASS
099            //print.out(className);
100                    // check last Mod
101                    if(classFile.lastModified()>=classFileOriginal.lastModified()) {
102                            try {
103                                    Class clazz=cl.loadClass(className);
104                                    if(clazz!=null && !hasChangesOfChildren(classFile.lastModified(),clazz))return registerTypeMapping(clazz);
105                            }
106                            catch(Throwable t){}
107                    }
108                    if(!create) return null;
109                    isNew.setValue(true);
110            //print.out("new");
111        // CREATE CLASS     
112                    ClassWriter cw = ASMUtil.getClassWriter();
113            //ClassWriter cw = new ClassWriter(true);
114            cw.visit(Opcodes.V1_2, Opcodes.ACC_PUBLIC, real, null, "java/lang/Object", null);
115    
116            //GeneratorAdapter ga = new GeneratorAdapter(Opcodes.ACC_PUBLIC,Page.STATIC_CONSTRUCTOR,null,null,cw);
117                    BytecodeContext statConstr = null;//new BytecodeContext(null,null,null,cw,real,ga,Page.STATIC_CONSTRUCTOR);
118    
119            ///ga = new GeneratorAdapter(Opcodes.ACC_PUBLIC,Page.CONSTRUCTOR,null,null,cw);
120                    BytecodeContext constr = null;//new BytecodeContext(null,null,null,cw,real,ga,Page.CONSTRUCTOR);
121                    
122            
123            // field component
124            //FieldVisitor fv = cw.visitField(Opcodes.ACC_PRIVATE, "c", "Lrailo/runtime/ComponentImpl;", null, null);
125            //fv.visitEnd();
126            
127            java.util.List<LitString> _keys=new ArrayList<LitString>();
128        
129            // remote methods
130            Collection.Key[] keys = component.keys(Component.ACCESS_REMOTE);
131            int max;
132            for(int i=0;i<keys.length;i++){
133                    max=-1;
134                    while((max=createMethod(statConstr,constr,_keys,cw,real,component.get(keys[i]),max, writeLog,supressWSbeforeArg))!=-1){
135                            break;// for overload remove this
136                    }
137            }
138            
139            // Constructor
140            GeneratorAdapter adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC,CONSTRUCTOR_OBJECT,null,null,cw);
141                    adapter.loadThis();
142            adapter.invokeConstructor(Types.OBJECT, CONSTRUCTOR_OBJECT);
143            railo.transformer.bytecode.Page.registerFields(new BytecodeContext(statConstr,constr,getPage(statConstr,constr),null,_keys,cw,real,adapter,CONSTRUCTOR_OBJECT,writeLog,supressWSbeforeArg), _keys);
144            adapter.returnValue();
145            adapter.endMethod();
146            
147            
148            cw.visitEnd();
149            byte[] barr = cw.toByteArray();
150            
151            try {
152                    ResourceUtil.touch(classFile);
153                    IOUtil.copy(new ByteArrayInputStream(barr), classFile,true);
154                    
155                    cl = (PhysicalClassLoader) ((PageContextImpl)pc).getRPCClassLoader(true);
156                    
157                    return registerTypeMapping(cl.loadClass(className, barr));
158            }
159            catch(Throwable t) {
160                    throw Caster.toPageException(t);
161            }
162        }
163    
164        private static railo.transformer.bytecode.Page getPage(BytecodeContext bc1, BytecodeContext bc2) {
165            railo.transformer.bytecode.Page page=null;
166            if(bc1!=null)page=bc1.getPage();
167            if(page==null && bc2!=null)page=bc2.getPage();
168            return page;
169            }
170    
171            /**
172             * check if one of the children is changed
173             * @param component
174             * @param clazz
175             * @return return true if children has changed
176             */
177            private static boolean hasChangesOfChildren(long last, Class clazz) {
178    
179                    boolean b= hasChangesOfChildren(last,ThreadLocalPageContext.get(),clazz);
180                    return b;
181            }
182    
183            /**
184             * check if one of the children is changed
185             * @param component
186             * @param pc
187             * @param clazz
188             * @return return true if children has changed
189             */
190            private static boolean hasChangesOfChildren(long last,PageContext pc, Class clazz) {
191    
192            java.lang.reflect.Method[] methods = clazz.getMethods();
193            java.lang.reflect.Method method;
194            Class[] params;
195            for(int i=0;i<methods.length;i++){
196                    method=methods[i];
197                    if(method.getDeclaringClass()==clazz){
198                            if(_hasChangesOfChildren(pc,   last,method.getReturnType())) return true;
199                            params = method.getParameterTypes();
200                            for(int y=0;y<params.length;y++){
201                                    if(_hasChangesOfChildren(pc,  last, params[y])) return true;
202                            }
203                    }
204            }
205                    return false;
206            }
207    
208            private static boolean _hasChangesOfChildren(PageContext pc, long last, Class clazz) {
209                    clazz=ClassUtil.toComponentType(clazz);
210                    java.lang.reflect.Method m = getComplexTypeMethod(clazz);
211                    if(m==null) return false;
212                    try {
213                            String path=Caster.toString(m.invoke(null, new Object[0]));
214                            Resource res = ResourceUtil.toResourceExisting(pc, path);
215                            if(last<res.lastModified()) {
216                                    return true;
217                            }
218                    } 
219                    catch (Exception e) {
220                            return true;
221                    }
222                    // possible that a child of the Cmplex Object is also a complex object
223                    return hasChangesOfChildren(last, pc, clazz);
224            }
225    
226            private static boolean isComplexType(Class clazz) {
227                    return getComplexTypeMethod(clazz)!=null;
228                    
229            }
230            private static java.lang.reflect.Method getComplexTypeMethod(Class clazz) {
231                    try {
232                            return clazz.getMethod("_srcName", new Class[0]);
233                    } 
234                    catch (Exception e) {
235                            return null;
236                    }
237            }
238    
239            /**
240         * search in methods of a class for complex types
241         * @param clazz
242         * @return
243         */
244        private static Class registerTypeMapping(Class clazz) throws AxisFault {
245            PageContext pc = ThreadLocalPageContext.get();
246            RPCServer server=RPCServer.getInstance(pc.getId(),pc.getServletContext());
247                    return registerTypeMapping(server, clazz);
248        }
249        /**
250         * search in methods of a class for complex types
251         * @param server
252         * @param clazz
253         * @return
254         */
255        private static Class registerTypeMapping(RPCServer server, Class clazz) {
256            java.lang.reflect.Method[] methods = clazz.getMethods();
257            java.lang.reflect.Method method;
258            Class[] params;
259            for(int i=0;i<methods.length;i++){
260                    method=methods[i];
261                    if(method.getDeclaringClass()==clazz){
262                            _registerTypeMapping(server, method.getReturnType());
263                            params = method.getParameterTypes();
264                            for(int y=0;y<params.length;y++){
265                                    _registerTypeMapping(server, params[y]);
266                            }
267                    }
268            }
269            return clazz;
270            }
271    
272            /**
273             * register ComplexType
274             * @param server
275             * @param clazz
276             */
277            private static void _registerTypeMapping(RPCServer server, Class clazz) {
278                    if(clazz==null) return;
279                    
280                    if(!isComplexType(clazz)) {
281                            if(clazz.isArray()) {
282                                    _registerTypeMapping(server, clazz.getComponentType());
283                            }
284                            return;
285                    }
286                    server.registerTypeMapping(clazz);
287                    registerTypeMapping(server,clazz);
288            }
289    
290            public static String getClassname(Component component) {
291            PageSource ps = component.getPageSource();
292            String path=ps.getDisplayPath();// Must remove webroot
293            Config config = ps.getMapping().getConfig();
294            String root = config.getRootDirectory().getAbsolutePath();
295            if(path.startsWith(root))
296                    path=path.substring(root.length());
297    
298            path=path.replace('\\', '/').toLowerCase();
299            path=ListUtil.trim(path, "/");
300            String[] arr = ListUtil.listToStringArray(path, '/');
301            
302            StringBuffer rtn=new StringBuffer();
303            for(int i=0;i<arr.length;i++) {
304                    if(i+1==arr.length) {
305                            rtn.append(StringUtil.toVariableName(StringUtil.replaceLast(arr[i],".cfc","")));
306                    }
307                    else {
308                            rtn.append(StringUtil.toVariableName(arr[i]));
309                            rtn.append('.');
310                    }
311            }
312            return rtn.toString();
313            }
314    
315            /*
316             * includes the application context javasettings 
317             * @param pc
318             * @param className
319             * @param properties
320             * @return
321             * @throws PageException
322             */
323            public static Object getClientComponentPropertiesObject(PageContext pc, String className, ASMProperty[] properties) throws PageException {
324                    try {
325                            return _getClientComponentPropertiesObject(pc,pc.getConfig(), className, properties);
326                    } catch (Exception e) {
327                            throw Caster.toPageException(e);
328                    }
329            }
330            /*
331             * does not include the application context javasettings 
332             * @param pc
333             * @param className
334             * @param properties
335             * @return
336             * @throws PageException
337             */
338            public static Object getClientComponentPropertiesObject(Config config, String className, ASMProperty[] properties) throws PageException {
339                    try {
340                            return _getClientComponentPropertiesObject(null,config, className, properties);
341                    } catch (Exception e) {
342                            throw Caster.toPageException(e);
343                    }
344            }
345            
346    
347        
348        private static Object _getClientComponentPropertiesObject(PageContext pc, Config secondChanceConfig, String className, ASMProperty[] properties) throws PageException, IOException, ClassNotFoundException {
349            String real=className.replace('.','/');
350            
351                    PhysicalClassLoader cl;
352            if(pc==null)cl = (PhysicalClassLoader)secondChanceConfig.getRPCClassLoader(false);
353            else cl = (PhysicalClassLoader)((PageContextImpl)pc).getRPCClassLoader(false);
354                    
355                    Resource rootDir = cl.getDirectory();
356                    Resource classFile = rootDir.getRealResource(real.concat(".class"));
357                    
358                    if(classFile.exists()) {
359                            try {
360                                    Class clazz = cl.loadClass(className);
361                                    Field field = clazz.getField("_md5_");
362                                    if(ASMUtil.createMD5(properties).equals(field.get(null))){
363                                    //if(equalInterface(properties,clazz)) {
364                                            return ClassUtil.loadInstance(clazz);
365                                    }
366                            }
367                            catch(Exception e) {
368                                    
369                            }
370                    }
371                    // create file
372                    byte[] barr = ASMUtil.createPojo(real, properties,Object.class,new Class[]{Pojo.class},null);
373            boolean exist=classFile.exists();
374                    ResourceUtil.touch(classFile);
375            IOUtil.copy(new ByteArrayInputStream(barr), classFile,true);
376            
377            if(pc==null)cl = (PhysicalClassLoader)secondChanceConfig.getRPCClassLoader(exist);
378            else cl = (PhysicalClassLoader)((PageContextImpl)pc).getRPCClassLoader(exist);
379            
380                    return ClassUtil.loadInstance(cl.loadClass(className));
381    
382            }
383    
384            
385        
386            public static Class getServerComponentPropertiesClass(PageContext pc,Component component) throws PageException {
387                    try {
388                    return _getServerComponentPropertiesClass(pc,component);
389                    }
390            catch (Exception e) {
391                            throw Caster.toPageException(e);
392                    }
393        }
394        
395        private static Class _getServerComponentPropertiesClass(PageContext pc,Component component) throws PageException, IOException, ClassNotFoundException {
396            String className=getClassname(component);//StringUtil.replaceLast(classNameOriginal,"$cfc","");
397            String real=className.replace('.','/');
398    
399            Mapping mapping = component.getPageSource().getMapping();
400                    PhysicalClassLoader cl = (PhysicalClassLoader)((PageContextImpl)pc).getRPCClassLoader(false);
401                    
402                    Resource classFile = cl.getDirectory().getRealResource(real.concat(".class"));
403                    
404            String classNameOriginal=component.getPageSource().getFullClassName();
405            String realOriginal=classNameOriginal.replace('.','/');
406                    Resource classFileOriginal = mapping.getClassRootDirectory().getRealResource(realOriginal.concat(".class"));
407    
408                    // load existing class
409                    if(classFile.lastModified()>=classFileOriginal.lastModified()) {
410                            try {
411                                    Class clazz=cl.loadClass(className);
412                                    if(clazz!=null && !hasChangesOfChildren(classFile.lastModified(), clazz))return clazz;//ClassUtil.loadInstance(clazz);
413                            }
414                            catch(Throwable t){}
415                    }
416    
417                    // create file
418                    byte[] barr = ASMUtil.createPojo(real, ASMUtil.toASMProperties(
419                                    ComponentUtil.getProperties(component, false, true, false, false)),Object.class,new Class[]{Pojo.class},component.getPageSource().getDisplayPath());
420            ResourceUtil.touch(classFile);
421            IOUtil.copy(new ByteArrayInputStream(barr), classFile,true);
422            cl = (PhysicalClassLoader)((PageContextImpl)pc).getRPCClassLoader(true);
423                    return cl.loadClass(className); //ClassUtil.loadInstance(cl.loadClass(className));
424        }
425    
426            private static int createMethod(BytecodeContext statConstr,BytecodeContext constr, java.util.List<LitString> keys,ClassWriter cw,String className, Object member,int max,boolean writeLog, boolean supressWSbeforeArg) throws PageException {
427                    
428                    boolean hasOptionalArgs=false;
429                    
430            if(member instanceof UDF) {
431                    UDF udf = (UDF) member;
432                    FunctionArgument[] args = udf.getFunctionArguments();
433                    Type[] types=new Type[max<0?args.length:max];
434                    for(int y=0;y<types.length;y++){
435                            types[y]=toType(args[y].getTypeAsString(),true);//Type.getType(Caster.cfTypeToClass(args[y].getTypeAsString()));
436                            if(!args[y].isRequired())hasOptionalArgs=true;
437                    }
438                    Type rtnType=toType(udf.getReturnTypeAsString(),true);
439                Method method = new Method(
440                            udf.getFunctionName(),
441                            rtnType,
442                                    types
443                            );
444                GeneratorAdapter adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL , method, null, null, cw);
445                BytecodeContext bc = new BytecodeContext(statConstr,constr,getPage(statConstr,constr),null,keys,cw,className,adapter,method,writeLog,supressWSbeforeArg);
446                Label start=adapter.newLabel();
447                adapter.visitLabel(start);
448                
449           //ComponentController.invoke(name, args);
450                // name
451                adapter.push(udf.getFunctionName());
452                
453                // args
454                ArrayVisitor av=new ArrayVisitor();
455                av.visitBegin(adapter,Types.OBJECT,types.length);
456                for(int y=0;y<types.length;y++){
457                            av.visitBeginItem(adapter, y);
458                                    adapter.loadArg(y);
459                            av.visitEndItem(bc.getAdapter());
460                }
461                av.visitEnd();
462                adapter.invokeStatic(COMPONENT_CONTROLLER, INVOKE);
463                adapter.checkCast(rtnType);
464                
465                //ASMConstants.NULL(adapter);
466                adapter.returnValue();
467                Label end=adapter.newLabel();
468                adapter.visitLabel(end);
469                
470                for(int y=0;y<types.length;y++){
471                            adapter.visitLocalVariable(args[y].getName().getString(), types[y].getDescriptor(), null, start, end, y+1);
472                }
473                adapter.endMethod();
474                
475                if(hasOptionalArgs) {
476                    if(max==-1)max=args.length-1;
477                    else max--;
478                    return max;
479                }
480            }
481            return -1;
482            }
483    
484    
485    
486            private static Type toType(String cfType, boolean axistype) throws PageException {
487                    Class clazz=Caster.cfTypeToClass(cfType);
488                    if(axistype)clazz=AxisCaster.toAxisTypeClass(clazz);
489                    return Type.getType(clazz);
490                    
491            }
492    
493    
494    
495            public static String md5(Component c) throws IOException, ExpressionException {
496                    ComponentWrap cw = ComponentWrap.toComponentWrap(Component.ACCESS_PRIVATE,c);
497                    Key[] keys = cw.keys();
498                    Arrays.sort(keys);
499                    
500                    StringBuffer _interface=new StringBuffer();
501                    
502                    Object member;
503            UDF udf;
504            FunctionArgument[] args;
505            FunctionArgument arg;
506            for(int y=0;y<keys.length;y++) {
507                    member = cw.get(keys[y],null);
508                    if(member instanceof UDF) {
509                            udf=(UDF) member;
510                            //print.out(udf.);
511                            _interface.append(udf.getAccess());
512                            _interface.append(udf.getOutput());
513                            _interface.append(udf.getFunctionName());
514                            _interface.append(udf.getReturnTypeAsString());
515                            args = udf.getFunctionArguments();
516                            for(int i=0;i<args.length;i++){
517                                    arg=args[i];
518                            _interface.append(arg.isRequired());
519                            _interface.append(arg.getName());
520                            _interface.append(arg.getTypeAsString());
521                            }
522                    }
523            }
524                    return  MD5.getDigestAsString(_interface.toString().toLowerCase());
525            }
526            
527    
528        /**
529         * cast a strong access definition to the int type
530         * @param access access type
531         * @return int access type
532         * @throws ExpressionException
533         */
534            public static int toIntAccess(String access) throws ExpressionException {
535            access=StringUtil.toLowerCase(access.trim());
536            if(access.equals("package"))return Component.ACCESS_PACKAGE;
537            else if(access.equals("private"))return Component.ACCESS_PRIVATE;
538            else if(access.equals("public"))return Component.ACCESS_PUBLIC;
539            else if(access.equals("remote"))return Component.ACCESS_REMOTE;
540            throw new ExpressionException("invalid access type ["+access+"], access types are remote, public, package, private");
541            
542        }
543            
544            public static int toIntAccess(String access, int defaultValue) {
545            access=StringUtil.toLowerCase(access.trim());
546            if(access.equals("package"))return Component.ACCESS_PACKAGE;
547            else if(access.equals("private"))return Component.ACCESS_PRIVATE;
548            else if(access.equals("public"))return Component.ACCESS_PUBLIC;
549            else if(access.equals("remote"))return Component.ACCESS_REMOTE;
550            return defaultValue;
551        }
552        
553        /**
554         * cast int type to string type
555         * @param access
556         * @return String access type
557         * @throws ExpressionException
558         */
559        public static String toStringAccess(int access) throws ExpressionException {
560            String res = toStringAccess(access,null);
561            if(res!=null) return res; 
562            throw new ExpressionException("invalid access type ["+access+"], access types are Component.ACCESS_PACKAGE, Component.ACCESS_PRIVATE, Component.ACCESS_PUBLIC, Component.ACCESS_REMOTE");
563        }
564        
565        public static String toStringAccess(int access,String defaultValue)  {
566            switch(access) {
567                case Component.ACCESS_PACKAGE:      return "package";
568                case Component.ACCESS_PRIVATE:      return "private";
569                case Component.ACCESS_PUBLIC:       return "public";
570                case Component.ACCESS_REMOTE:       return "remote";
571            }
572            return defaultValue;
573        }
574    
575            public static ExpressionException notFunction(Component c,Collection.Key key, Object member,int access) {
576                    if(member==null) {
577                            String strAccess = toStringAccess(access,"");
578                            
579                            Collection.Key[] other;
580                            if(c instanceof ComponentAccess)
581                                    other=((ComponentAccess)c).keys(access);
582                            else 
583                                    other=CollectionUtil.keys(c);
584                            
585                            if(other.length==0)
586                                    return new ExpressionException(
587                                                    "component ["+c.getCallName()+"] has no "+strAccess+" function with name ["+key+"]");
588                            
589                            return new ExpressionException(
590                                            "component ["+c.getCallName()+"] has no "+strAccess+" function with name ["+key+"]",
591                                            "accessible functions are ["+ListUtil.arrayToList(other,",")+"]");
592                    }
593                    return new ExpressionException("member ["+key+"] of component ["+c.getCallName()+"] is not a function", "Member is of type ["+Caster.toTypeName(member)+"]");
594            }
595    
596            public static Property[] getProperties(Component c,boolean onlyPeristent, boolean includeBaseProperties, boolean preferBaseProperties, boolean inheritedMappedSuperClassOnly) {
597                    if(c instanceof ComponentPro)
598                            return ((ComponentPro)c).getProperties(onlyPeristent, includeBaseProperties,preferBaseProperties,preferBaseProperties);
599                    return c.getProperties(onlyPeristent);
600            }
601            
602            public static Property[] getIDProperties(Component c,boolean onlyPeristent, boolean includeBaseProperties) {
603                    Property[] props = getProperties(c,onlyPeristent,includeBaseProperties,false,false);
604                    java.util.List<Property> tmp=new ArrayList<Property>();
605                    for(int i=0;i<props.length;i++){
606                            if("id".equalsIgnoreCase(Caster.toString(props[i].getDynamicAttributes().get(FIELD_TYPE,null),"")))
607                                    tmp.add(props[i]);
608                    }
609                    return tmp.toArray(new Property[tmp.size()]);
610            }
611    
612            public static ComponentAccess toComponentAccess(Component comp) throws ExpressionException {
613                    ComponentAccess ca = toComponentAccess(comp, null);
614                    if(ca!=null) return ca;
615                    throw new ExpressionException("can't cast class ["+Caster.toClassName(comp)+"] to a class of type ComponentAccess");
616            }
617    
618            public static ComponentAccess toComponentAccess(Component comp, ComponentAccess defaultValue) {
619                    if(comp instanceof ComponentAccess) return (ComponentAccess) comp;
620                    if(comp instanceof ComponentWrap) return ((ComponentWrap) comp).getComponentAccess();
621                    return defaultValue;
622            }
623            
624            
625            
626            public static Component toComponent(Object obj) throws ExpressionException {
627                    if(obj instanceof Component) return (Component) obj;
628                    throw new ExpressionException("can't cast class ["+Caster.toClassName(obj)+"] to a class of type Component");
629            }
630            
631    
632    
633            public static PageSource getPageSource(Component cfc) {
634                    // TODO Auto-generated method stub
635                    try {
636                            return toComponent(cfc).getPageSource();
637                    } catch (ExpressionException e) {
638                            return null;
639                    }
640            }
641    
642            public static ComponentAccess getActiveComponent(PageContext pc, ComponentAccess current) {
643                    if(pc.getActiveComponent()==null) return current; 
644                    if(pc.getActiveUDF()!=null && (pc.getActiveComponent()).getPageSource()==(pc.getActiveUDF().getOwnerComponent()).getPageSource()){
645                            
646                            return (ComponentAccess) pc.getActiveUDF().getOwnerComponent();
647                    }
648                    return (ComponentAccess) pc.getActiveComponent();//+++
649                    
650                    
651            }
652    
653            public static long getCompileTime(PageContext pc, PageSource ps,long defaultValue) {
654                    try {
655                            return getCompileTime(pc, ps);
656                    } catch (Throwable t) {
657                            return defaultValue;
658                    }
659            }
660    
661            public static long getCompileTime(PageContext pc, PageSource ps) throws PageException {
662                    return getPage(pc,ps).getCompileTime();
663            }
664    
665            public static Page getPage(PageContext pc, PageSource ps) throws PageException {
666                    PageSourceImpl psi = (PageSourceImpl)ps;
667                    
668                    Page p = psi.getPage();
669                    if(p!=null){
670                            //print.o("getPage(existing):"+ps.getDisplayPath()+":"+psi.hashCode()+":"+p.hashCode());
671                            return p;
672                    }
673                    pc=ThreadLocalPageContext.get(pc);
674                    return psi.loadPage(pc);
675            }
676    
677            public static Struct getPropertiesAsStruct(ComponentAccess ca, boolean onlyPersistent) {
678                    Property[] props = ca.getProperties(onlyPersistent);
679                    Struct sct=new StructImpl();
680                    if(props!=null)for(int i=0;i<props.length;i++){
681                            sct.setEL(KeyImpl.getInstance(props[i].getName()), props[i]);
682                    }
683                    return sct;
684            }
685            public static Struct getMetaData(PageContext pc,UDFPropertiesImpl udf) throws PageException {
686                    StructImpl func=new StructImpl();
687            pc=ThreadLocalPageContext.get(pc);
688                    // TODO func.set("roles", value);
689            // TODO func.set("userMetadata", value); neo unterst゚tzt irgendwelche a
690            // meta data
691            Struct meta = udf.meta;
692            if(meta!=null) StructUtil.copy(meta, func, true);
693            
694            func.setEL(KeyConstants._closure, Boolean.FALSE);
695                    
696                    func.set(KeyConstants._access,ComponentUtil.toStringAccess(udf.getAccess()));
697            String hint=udf.hint;
698            if(!StringUtil.isEmpty(hint))func.set(KeyConstants._hint,hint);
699            String displayname=udf.displayName;
700            if(!StringUtil.isEmpty(displayname))func.set(KeyConstants._displayname,displayname);
701            func.set(KeyConstants._name,udf.functionName);
702            func.set(KeyConstants._output,Caster.toBoolean(udf.output));
703            func.set(KeyConstants._returntype, udf.strReturnType);
704            func.set(KeyConstants._description, udf.description);
705            
706            func.set(KeyConstants._owner, udf.pageSource.getDisplayPath());
707            
708                       
709                int format = udf.returnFormat;
710            if(format==UDF.RETURN_FORMAT_WDDX)                      func.set(KeyConstants._returnFormat, "wddx");
711            else if(format==UDF.RETURN_FORMAT_PLAIN)        func.set(KeyConstants._returnFormat, "plain");
712            else if(format==UDF.RETURN_FORMAT_JSON) func.set(KeyConstants._returnFormat, "json");
713            else if(format==UDF.RETURN_FORMAT_SERIALIZE)func.set(KeyConstants._returnFormat, "cfml");
714            
715            
716            FunctionArgument[] args =  udf.arguments;
717            Array params=new ArrayImpl();
718            //Object defaultValue;
719            Struct m;
720            //Object defaultValue;
721            for(int y=0;y<args.length;y++) {
722                StructImpl param=new StructImpl();
723                param.set(KeyConstants._name,args[y].getName().getString());
724                param.set(KeyConstants._required,Caster.toBoolean(args[y].isRequired()));
725                param.set(KeyConstants._type,args[y].getTypeAsString());
726                displayname=args[y].getDisplayName();
727                if(!StringUtil.isEmpty(displayname)) param.set(KeyConstants._displayname,displayname);
728                
729                int defType = args[y].getDefaultType();
730                if(defType==FunctionArgument.DEFAULT_TYPE_RUNTIME_EXPRESSION){
731                    param.set(KeyConstants._default, "[runtime expression]");
732                }
733                else if(defType==FunctionArgument.DEFAULT_TYPE_LITERAL){
734                    param.set(KeyConstants._default, 
735                                    UDFUtil.getDefaultValue(pc, udf.pageSource, udf.index, y, null));
736                }
737                
738                hint=args[y].getHint();
739                if(!StringUtil.isEmpty(hint))param.set(KeyConstants._hint,hint);
740                // TODO func.set("userMetadata", value); neo unterst゚tzt irgendwelche attr, die dann hier ausgebenen werden blレdsinn
741                
742                // meta data
743                m=args[y].getMetaData();
744                if(m!=null) StructUtil.copy(m, param, true);
745                    
746                params.append(param);
747            }
748            func.set(KeyConstants._parameters,params);
749                    return func;
750            }
751    
752    
753    }