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