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