/** * Copyright (c) 2014, the Railo Company Ltd. * Copyright (c) 2015, Lucee Assosication Switzerland * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see <http://www.gnu.org/licenses/>. * */ package lucee.runtime.type.util; import java.io.ByteArrayInputStream; import java.io.IOException; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.Map.Entry; import lucee.commons.digest.MD5; import lucee.commons.io.IOUtil; import lucee.commons.io.res.Resource; import lucee.commons.io.res.util.ResourceUtil; import lucee.commons.lang.ClassUtil; import lucee.commons.lang.ExceptionUtil; import lucee.commons.lang.PhysicalClassLoader; import lucee.commons.lang.StringUtil; import lucee.commons.lang.types.RefBoolean; import lucee.runtime.Component; import lucee.runtime.ComponentSpecificAccess; import lucee.runtime.Mapping; import lucee.runtime.Page; import lucee.runtime.PageContext; import lucee.runtime.PageContextImpl; import lucee.runtime.PageSource; import lucee.runtime.PageSourceImpl; import lucee.runtime.component.Property; import lucee.runtime.config.Config; import lucee.runtime.engine.ThreadLocalPageContext; import lucee.runtime.exp.ApplicationException; import lucee.runtime.exp.ExpressionException; import lucee.runtime.exp.PageException; import lucee.runtime.listener.AppListenerUtil; import lucee.runtime.net.rpc.AxisCaster; import lucee.runtime.net.rpc.Pojo; import lucee.runtime.net.rpc.server.RPCServer; import lucee.runtime.op.Caster; import lucee.runtime.type.Array; import lucee.runtime.type.ArrayImpl; import lucee.runtime.type.Collection; import lucee.runtime.type.Collection.Key; import lucee.runtime.type.FunctionArgument; import lucee.runtime.type.KeyImpl; import lucee.runtime.type.Struct; import lucee.runtime.type.StructImpl; import lucee.runtime.type.UDF; import lucee.runtime.type.UDFPropertiesBase; import lucee.transformer.bytecode.BytecodeContext; import lucee.transformer.bytecode.ConstrBytecodeContext; import lucee.transformer.bytecode.util.ASMProperty; import lucee.transformer.bytecode.util.ASMPropertyImpl; import lucee.transformer.bytecode.util.ASMUtil; import lucee.transformer.bytecode.util.Types; import lucee.transformer.bytecode.visitor.ArrayVisitor; import lucee.transformer.expression.literal.LitString; import org.apache.axis.AxisFault; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Label; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.objectweb.asm.commons.GeneratorAdapter; import org.objectweb.asm.commons.Method; public final class ComponentUtil { private final static Method CONSTRUCTOR_OBJECT = Method.getMethod("void <init> ()"); private static final Method INVOKE = new Method("invoke",Types.OBJECT,new Type[]{Types.STRING,Types.OBJECT_ARRAY}); //private static final Method INVOKE_PROPERTY = new Method("invoke",Types.OBJECT,new Type[]{Types.STRING,Types.OBJECT_ARRAY}); /** * generate a ComponentJavaAccess (CJA) class from a component * a CJA is a dynamic genarted java class that has all method defined inside a component as java methods. * * This is used to generated server side Webservices. * @param component * @param isNew * @return * @throws PageException */ public static Class getComponentJavaAccess(PageContext pc,Component component, RefBoolean isNew,boolean create,boolean writeLog, boolean suppressWSbeforeArg, boolean output, boolean returnValue) throws PageException { isNew.setValue(false); String classNameOriginal=component.getPageSource().getClassName(); String className=getClassname(component,null).concat("_wrap"); String real=className.replace('.','/'); String realOriginal=classNameOriginal.replace('.','/'); Mapping mapping = component.getPageSource().getMapping(); PhysicalClassLoader cl=null; try { cl = (PhysicalClassLoader) ((PageContextImpl)pc).getRPCClassLoader(false); } catch (IOException e) { throw Caster.toPageException(e); } Resource classFile = cl.getDirectory().getRealResource(real.concat(".class")); Resource classFileOriginal = mapping.getClassRootDirectory().getRealResource(realOriginal.concat(".class")); // LOAD CLASS //print.out(className); // check last Mod if(classFile.lastModified()>=classFileOriginal.lastModified()) { try { Class clazz=cl.loadClass(className); if(clazz!=null && !hasChangesOfChildren(classFile.lastModified(),clazz))return registerTypeMapping(clazz); } catch(Throwable t) {ExceptionUtil.rethrowIfNecessary(t);} } if(!create) return null; isNew.setValue(true); //print.out("new"); // CREATE CLASS ClassWriter cw = ASMUtil.getClassWriter(); cw.visit(Opcodes.V1_6, Opcodes.ACC_PUBLIC, real, null, "java/lang/Object", null); //GeneratorAdapter ga = new GeneratorAdapter(Opcodes.ACC_PUBLIC,Page.STATIC_CONSTRUCTOR,null,null,cw); //StaticConstrBytecodeContext statConstr = null;//new BytecodeContext(null,null,null,cw,real,ga,Page.STATIC_CONSTRUCTOR); ///ga = new GeneratorAdapter(Opcodes.ACC_PUBLIC,Page.CONSTRUCTOR,null,null,cw); ConstrBytecodeContext constr = null;//new BytecodeContext(null,null,null,cw,real,ga,Page.CONSTRUCTOR); // field component //FieldVisitor fv = cw.visitField(Opcodes.ACC_PRIVATE, "c", "Llucee/runtime/ComponentImpl;", null, null); //fv.visitEnd(); java.util.List<LitString> _keys=new ArrayList<LitString>(); // remote methods Collection.Key[] keys = component.keys(Component.ACCESS_REMOTE); int max; for(int i=0;i<keys.length;i++){ max=-1; while((max=createMethod(constr,_keys,cw,real,component.get(keys[i]),max, writeLog,suppressWSbeforeArg,output,returnValue))!=-1){ break;// for overload remove this } } // Constructor GeneratorAdapter adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC,CONSTRUCTOR_OBJECT,null,null,cw); adapter.loadThis(); adapter.invokeConstructor(Types.OBJECT, CONSTRUCTOR_OBJECT); lucee.transformer.bytecode.Page.registerFields(new BytecodeContext(null,constr,getPage(constr),_keys,cw,real,adapter,CONSTRUCTOR_OBJECT,writeLog,suppressWSbeforeArg,output,returnValue), _keys); adapter.returnValue(); adapter.endMethod(); cw.visitEnd(); byte[] barr = cw.toByteArray(); try { ResourceUtil.touch(classFile); IOUtil.copy(new ByteArrayInputStream(barr), classFile,true); cl = (PhysicalClassLoader) ((PageContextImpl)pc).getRPCClassLoader(true); return registerTypeMapping(cl.loadClass(className, barr)); } catch(Throwable t) { ExceptionUtil.rethrowIfNecessary(t); throw Caster.toPageException(t); } } private static lucee.transformer.bytecode.Page getPage( BytecodeContext bc2) { lucee.transformer.bytecode.Page page=null; //if(bc1!=null)page=bc1.getPage(); if(bc2!=null)page=bc2.getPage(); return page; } /** * check if one of the children is changed * @param component * @param clazz * @return return true if children has changed */ private static boolean hasChangesOfChildren(long last, Class clazz) { return hasChangesOfChildren(last,ThreadLocalPageContext.get(),clazz); } /** * check if one of the children is changed * @param component * @param pc * @param clazz * @return return true if children has changed */ private static boolean hasChangesOfChildren(long last,PageContext pc, Class clazz) { java.lang.reflect.Method[] methods = clazz.getMethods(); java.lang.reflect.Method method; Class[] params; for(int i=0;i<methods.length;i++){ method=methods[i]; if(method.getDeclaringClass()==clazz){ if(_hasChangesOfChildren(pc, last,method.getReturnType())) return true; params = method.getParameterTypes(); for(int y=0;y<params.length;y++){ if(_hasChangesOfChildren(pc, last, params[y])) return true; } } } return false; } private static boolean _hasChangesOfChildren(PageContext pc, long last, Class clazz) { clazz=ClassUtil.toComponentType(clazz); java.lang.reflect.Method m = getComplexTypeMethod(clazz); if(m==null) return false; try { String path=Caster.toString(m.invoke(null, new Object[0])); Resource res = ResourceUtil.toResourceExisting(pc, path); if(last<res.lastModified()) { return true; } } catch (Exception e) { return true; } // possible that a child of the Cmplex Object is also a complex object return hasChangesOfChildren(last, pc, clazz); } private static boolean isComplexType(Class clazz) { return getComplexTypeMethod(clazz)!=null; } private static java.lang.reflect.Method getComplexTypeMethod(Class clazz) { try { return clazz.getMethod("_srcName", new Class[0]); } catch (Exception e) { return null; } } /** * search in methods of a class for complex types * @param clazz * @return */ private static Class registerTypeMapping(Class clazz) throws AxisFault { PageContext pc = ThreadLocalPageContext.get(); RPCServer server=RPCServer.getInstance(pc.getId(),pc,pc.getServletContext()); return registerTypeMapping(server, clazz); } /** * search in methods of a class for complex types * @param server * @param clazz * @return */ private static Class registerTypeMapping(RPCServer server, Class clazz) { java.lang.reflect.Method[] methods = clazz.getMethods(); java.lang.reflect.Method method; Class[] params; for(int i=0;i<methods.length;i++){ method=methods[i]; if(method.getDeclaringClass()==clazz){ _registerTypeMapping(server, method.getReturnType()); params = method.getParameterTypes(); for(int y=0;y<params.length;y++){ _registerTypeMapping(server, params[y]); } } } return clazz; } /** * register ComplexType * @param server * @param clazz */ private static void _registerTypeMapping(RPCServer server, Class clazz) { if(clazz==null) return; if(!isComplexType(clazz)) { if(clazz.isArray()) { _registerTypeMapping(server, clazz.getComponentType()); } return; } server.registerTypeMapping(clazz); registerTypeMapping(server,clazz); } public static String getClassname(Component component, ASMProperty[] props) { String prefix=""; /*if(props!=null) { StringBuilder sb=new StringBuilder(); for(int i=0;i<props.length;i++){ sb.append(props[i].toString()).append(';'); } prefix = Long.toString(HashUtil.create64BitHash(sb),Character.MAX_RADIX); char c=prefix.charAt(0); if(c>='0' && c<='9') prefix="a"+prefix; prefix=prefix+"."; }*/ PageSource ps = component.getPageSource(); return prefix+ps.getComponentName(); } /* * includes the application context javasettings * @param pc * @param className * @param properties * @return * @throws PageException */ public static Class getClientComponentPropertiesClass(PageContext pc, String className, ASMProperty[] properties, Class extendsClass) throws PageException { try { return _getComponentPropertiesClass(pc,pc.getConfig(), className, properties,extendsClass); } catch (Exception e) { throw Caster.toPageException(e); } } /* * does not include the application context javasettings * @param pc * @param className * @param properties * @return * @throws PageException */ public static Class getComponentPropertiesClass(Config config, String className, ASMProperty[] properties,Class extendsClass) throws PageException { try { return _getComponentPropertiesClass(null,config, className, properties,extendsClass); } catch (Exception e) { throw Caster.toPageException(e); } } private static Class _getComponentPropertiesClass(PageContext pc, Config secondChanceConfig, String className, ASMProperty[] properties, Class extendsClass) throws PageException, IOException, ClassNotFoundException { String real=className.replace('.','/'); PhysicalClassLoader cl; if(pc==null)cl = (PhysicalClassLoader)secondChanceConfig.getRPCClassLoader(false); else cl = (PhysicalClassLoader)((PageContextImpl)pc).getRPCClassLoader(false); Resource rootDir = cl.getDirectory(); Resource classFile = rootDir.getRealResource(real.concat(".class")); if(classFile.exists()) { try { Class clazz = cl.loadClass(className); Field field = clazz.getField("_md5_"); if(ASMUtil.createMD5(properties).equals(field.get(null))){ //if(equalInterface(properties,clazz)) { return clazz; } } catch(Exception e) { } } // create file if(extendsClass==null)extendsClass=Object.class; byte[] barr = ASMUtil.createPojo(real, properties,extendsClass,new Class[]{Pojo.class},null); boolean exist=classFile.exists(); ResourceUtil.touch(classFile); IOUtil.copy(new ByteArrayInputStream(barr), classFile,true); if(pc==null)cl = (PhysicalClassLoader)secondChanceConfig.getRPCClassLoader(exist); else cl = (PhysicalClassLoader)((PageContextImpl)pc).getRPCClassLoader(exist); return cl.loadClass(className); } public static Class getComponentPropertiesClass(PageContext pc,Component component) throws PageException { try { return _getComponentPropertiesClass(pc,component); } catch (Exception e) { throw Caster.toPageException(e); } } private static Class _getComponentPropertiesClass(PageContext pc,Component component) throws PageException, IOException, ClassNotFoundException { ASMProperty[] props = ASMUtil.toASMProperties(component.getProperties(false, true, false, false)); String className=getClassname(component,props); String real=className.replace('.','/'); Mapping mapping = component.getPageSource().getMapping(); PhysicalClassLoader cl = (PhysicalClassLoader)((PageContextImpl)pc).getRPCClassLoader(false); Resource classFile = cl.getDirectory().getRealResource(real.concat(".class")); // get component class information String classNameOriginal=component.getPageSource().getClassName(); String realOriginal=classNameOriginal.replace('.','/'); Resource classFileOriginal = mapping.getClassRootDirectory().getRealResource(realOriginal.concat(".class")); // load existing class when pojo is still newer than component class file if(classFile.lastModified()>=classFileOriginal.lastModified()) { try { Class clazz=cl.loadClass(className); if(clazz!=null && !hasChangesOfChildren(classFile.lastModified(), clazz))return clazz;//ClassUtil.loadInstance(clazz); } catch(Throwable t) {ExceptionUtil.rethrowIfNecessary(t);} } // extends String strExt = component.getExtends(); Class<?> ext=Object.class; if(!StringUtil.isEmpty(strExt,true)) { ext = Caster.cfTypeToClass(strExt); } // // create file byte[] barr = ASMUtil.createPojo(real, props,ext,new Class[]{Pojo.class},component.getPageSource().getDisplayPath()); ResourceUtil.touch(classFile); IOUtil.copy(new ByteArrayInputStream(barr), classFile,true); cl = (PhysicalClassLoader)((PageContextImpl)pc).getRPCClassLoader(true); return cl.loadClass(className); //ClassUtil.loadInstance(cl.loadClass(className)); } public static Class getStructPropertiesClass(PageContext pc,Struct sct, PhysicalClassLoader cl) throws PageException { try { return _getStructPropertiesClass(pc,sct,cl); } catch (Exception e) { throw Caster.toPageException(e); } } private static Class _getStructPropertiesClass(PageContext pc,Struct sct, PhysicalClassLoader cl) throws PageException, IOException, ClassNotFoundException { // create hash based on the keys of the struct String hash = StructUtil.keyHash(sct); char c=hash.charAt(0); if(c>='0' && c<='9') hash="a"+hash; // create class name (struct class name + hash) String className=sct.getClass().getName()+"."+hash; // create physcal location for the file String real=className.replace('.','/'); Resource classFile = cl.getDirectory().getRealResource(real.concat(".class")); // load existing class if(classFile.exists()) { try { Class clazz=cl.loadClass(className); if(clazz!=null )return clazz; } catch(Throwable t) {ExceptionUtil.rethrowIfNecessary(t);} } // Properties List<ASMProperty> props=new ArrayList<ASMProperty>(); Iterator<Entry<Key, Object>> it = sct.entryIterator(); Entry<Key, Object> e; while(it.hasNext()){ e = it.next(); props.add(new ASMPropertyImpl( ASMUtil.toType(e.getValue()==null?Object.class:Object.class/*e.getValue().getClass()*/, true) ,e.getKey().getString() )); } // create file byte[] barr = ASMUtil.createPojo(real, props.toArray(new ASMProperty[props.size()]) ,Object.class,new Class[]{Pojo.class},null); // create class file from bytecode ResourceUtil.touch(classFile); IOUtil.copy(new ByteArrayInputStream(barr), classFile,true); cl = (PhysicalClassLoader)((PageContextImpl)pc).getRPCClassLoader(true); return cl.loadClass(className); } private static int createMethod(ConstrBytecodeContext constr, java.util.List<LitString> keys,ClassWriter cw,String className, Object member,int max,boolean writeLog, boolean suppressWSbeforeArg,boolean output,boolean returnValue) throws PageException { boolean hasOptionalArgs=false; if(member instanceof UDF) { UDF udf = (UDF) member; FunctionArgument[] args = udf.getFunctionArguments(); Type[] types=new Type[max<0?args.length:max]; for(int y=0;y<types.length;y++){ types[y]=toType(args[y].getTypeAsString(),true); if(!args[y].isRequired())hasOptionalArgs=true; } Type rtnType=toType(udf.getReturnTypeAsString(),true); Method method = new Method( udf.getFunctionName(), rtnType, types ); GeneratorAdapter adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL , method, null, null, cw); BytecodeContext bc = new BytecodeContext(null,constr,getPage(constr),keys,cw,className,adapter,method,writeLog,suppressWSbeforeArg,output,returnValue); Label start=adapter.newLabel(); adapter.visitLabel(start); //ComponentController.invoke(name, args); // name adapter.push(udf.getFunctionName()); // args ArrayVisitor av=new ArrayVisitor(); av.visitBegin(adapter,Types.OBJECT,types.length); for(int y=0;y<types.length;y++){ av.visitBeginItem(adapter, y); adapter.loadArg(y); av.visitEndItem(bc.getAdapter()); } av.visitEnd(); adapter.invokeStatic(Types.COMPONENT_CONTROLLER, INVOKE); adapter.checkCast(rtnType); //ASMConstants.NULL(adapter); adapter.returnValue(); Label end=adapter.newLabel(); adapter.visitLabel(end); for(int y=0;y<types.length;y++){ adapter.visitLocalVariable(args[y].getName().getString(), types[y].getDescriptor(), null, start, end, y+1); } adapter.endMethod(); if(hasOptionalArgs) { if(max==-1)max=args.length-1; else max--; return max; } } return -1; } private static Type toType(String cfType, boolean axistype) throws PageException { Class clazz=Caster.cfTypeToClass(cfType); if(axistype)clazz=AxisCaster.toAxisTypeClass(clazz); return Type.getType(clazz); } public static String md5(Component c) throws IOException { return md5(ComponentSpecificAccess.toComponentSpecificAccess(Component.ACCESS_PRIVATE,c)); } public static String md5(ComponentSpecificAccess cw) throws IOException { Key[] keys = cw.keys(); Arrays.sort(keys); StringBuffer _interface=new StringBuffer(); Object member; UDF udf; FunctionArgument[] args; FunctionArgument arg; for(int y=0;y<keys.length;y++) { member = cw.get(keys[y],null); if(member instanceof UDF) { udf=(UDF) member; //print.out(udf.); _interface.append(udf.getAccess()); _interface.append(udf.getOutput()); _interface.append(udf.getFunctionName()); _interface.append(udf.getReturnTypeAsString()); args = udf.getFunctionArguments(); for(int i=0;i<args.length;i++){ arg=args[i]; _interface.append(arg.isRequired()); _interface.append(arg.getName()); _interface.append(arg.getTypeAsString()); } } } return MD5.getDigestAsString(_interface.toString().toLowerCase()); } /** * cast a strong access definition to the int type * @param access access type * @return int access type * @throws ExpressionException */ public static int toIntAccess(String access) throws ApplicationException { access=StringUtil.toLowerCase(access.trim()); if(access.equals("package"))return Component.ACCESS_PACKAGE; else if(access.equals("private"))return Component.ACCESS_PRIVATE; else if(access.equals("public"))return Component.ACCESS_PUBLIC; else if(access.equals("remote"))return Component.ACCESS_REMOTE; throw new ApplicationException("invalid access type ["+access+"], access types are remote, public, package, private"); } public static int toIntAccess(String access, int defaultValue) { access=StringUtil.toLowerCase(access.trim()); if(access.equals("package"))return Component.ACCESS_PACKAGE; else if(access.equals("private"))return Component.ACCESS_PRIVATE; else if(access.equals("public"))return Component.ACCESS_PUBLIC; else if(access.equals("remote"))return Component.ACCESS_REMOTE; return defaultValue; } /** * cast int type to string type * @param access * @return String access type * @throws ExpressionException */ public static String toStringAccess(int access) throws ApplicationException { String res = toStringAccess(access,null); if(res!=null) return res; throw new ApplicationException("invalid access type ["+access+"], access types are Component.ACCESS_PACKAGE, Component.ACCESS_PRIVATE, Component.ACCESS_PUBLIC, Component.ACCESS_REMOTE"); } public static String toStringAccess(int access,String defaultValue) { switch(access) { case Component.ACCESS_PACKAGE: return "package"; case Component.ACCESS_PRIVATE: return "private"; case Component.ACCESS_PUBLIC: return "public"; case Component.ACCESS_REMOTE: return "remote"; } return defaultValue; } public static ExpressionException notFunction(Component c,Collection.Key key, Object member,int access) { if(member==null) { String strAccess = toStringAccess(access,""); Collection.Key[] other=c.keys(access); if(other.length==0) return new ExpressionException( "component ["+c.getCallName()+"] has no "+strAccess+" function with name ["+key+"]"); return new ExpressionException( "component ["+c.getCallName()+"] has no "+strAccess+" function with name ["+key+"]", "accessible functions are ["+ListUtil.arrayToList(other,",")+"]"); } return new ExpressionException("member ["+key+"] of component ["+c.getCallName()+"] is not a function", "Member is of type ["+Caster.toTypeName(member)+"]"); } public static Property[] getProperties(Component c,boolean onlyPeristent, boolean includeBaseProperties, boolean preferBaseProperties, boolean inheritedMappedSuperClassOnly) { return c.getProperties(onlyPeristent, includeBaseProperties,preferBaseProperties,preferBaseProperties); } /*public static ComponentAccess toComponentAccess(Component comp) throws ExpressionException { ComponentAccess ca = toComponentAccess(comp, null); if(ca!=null) return ca; throw new ExpressionException("can't cast class ["+Caster.toClassName(comp)+"] to a class of type ComponentAccess"); }*/ /*public static Component toComponentAccess(Component comp, Component defaultValue) { if(comp instanceof ComponentAccess) return (ComponentAccess) comp; if(comp instanceof ComponentSpecificAccess) return ((ComponentSpecificAccess) comp).getComponentAccess(); return defaultValue; }*/ public static Component toComponent(Object obj) throws ExpressionException { if(obj instanceof Component) return (Component) obj; throw new ExpressionException("can't cast class ["+Caster.toClassName(obj)+"] to a class of type Component"); } public static PageSource getPageSource(Component cfc) { // TODO Auto-generated method stub try { return toComponent(cfc).getPageSource(); } catch (ExpressionException e) { return null; } } public static Component getActiveComponent(PageContext pc, Component current) { if(pc.getActiveComponent()==null) return current; if(pc.getActiveUDF()!=null && (pc.getActiveComponent()).getPageSource()==(pc.getActiveUDF().getOwnerComponent()).getPageSource()){ return pc.getActiveUDF().getOwnerComponent(); } return pc.getActiveComponent(); } public static long getCompileTime(PageContext pc, PageSource ps,long defaultValue) { try { return getCompileTime(pc, ps); } catch(Throwable t) { ExceptionUtil.rethrowIfNecessary(t); return defaultValue; } } public static long getCompileTime(PageContext pc, PageSource ps) throws PageException { return getPage(pc,ps).getCompileTime(); } public static Page getPage(PageContext pc, PageSource ps) throws PageException { PageSourceImpl psi = (PageSourceImpl)ps; Page p = psi.getPage(); if(p!=null){ //print.o("getPage(existing):"+ps.getDisplayPath()+":"+psi.hashCode()+":"+p.hashCode()); return p; } pc=ThreadLocalPageContext.get(pc); return psi.loadPage(pc,false); } public static Struct getPropertiesAsStruct(Component c, boolean onlyPersistent) { Property[] props = c.getProperties(onlyPersistent); Struct sct=new StructImpl(); if(props!=null)for(int i=0;i<props.length;i++){ sct.setEL(KeyImpl.getInstance(props[i].getName()), props[i]); } return sct; } public static Struct getMetaData(PageContext pc,UDFPropertiesBase udf) throws PageException { StructImpl func=new StructImpl(); pc=ThreadLocalPageContext.get(pc); // TODO func.set("roles", value); // TODO func.set("userMetadata", value); neo unterstuetzt irgendwelche a // meta data Struct meta = udf.getMeta(); if(meta!=null) StructUtil.copy(meta, func, true); func.setEL(KeyConstants._closure, Boolean.FALSE); func.set(KeyConstants._access,ComponentUtil.toStringAccess(udf.getAccess())); String hint=udf.getHint(); if(!StringUtil.isEmpty(hint))func.set(KeyConstants._hint,hint); String displayname=udf.getDisplayName(); if(!StringUtil.isEmpty(displayname))func.set(KeyConstants._displayname,displayname); func.set(KeyConstants._name,udf.getFunctionName()); func.set(KeyConstants._output,Caster.toBoolean(udf.getOutput())); func.set(KeyConstants._returntype, udf.getReturnTypeAsString()); func.set("modifier", udf.getModifier()==Component.MODIFIER_NONE?"":ComponentUtil.toModifier(udf.getModifier(), "")); func.set(KeyConstants._description, udf.getDescription()); if(udf.getLocalMode()!=null)func.set("localMode", AppListenerUtil.toLocalMode(udf.getLocalMode().intValue(), "")); if(udf.getPageSource()!=null) func.set(KeyConstants._owner, udf.getPageSource().getDisplayPath()); int format = udf.getReturnFormat(); if(format<0 || format==UDF.RETURN_FORMAT_WDDX) func.set(KeyConstants._returnFormat, "wddx"); else if(format==UDF.RETURN_FORMAT_PLAIN) func.set(KeyConstants._returnFormat, "plain"); else if(format==UDF.RETURN_FORMAT_JSON) func.set(KeyConstants._returnFormat, "json"); else if(format==UDF.RETURN_FORMAT_SERIALIZE)func.set(KeyConstants._returnFormat, "cfml"); FunctionArgument[] args = udf.getFunctionArguments(); Array params=new ArrayImpl(); //Object defaultValue; Struct m; //Object defaultValue; for(int y=0;y<args.length;y++) { StructImpl param=new StructImpl(); param.set(KeyConstants._name,args[y].getName().getString()); param.set(KeyConstants._required,Caster.toBoolean(args[y].isRequired())); param.set(KeyConstants._type,args[y].getTypeAsString()); displayname=args[y].getDisplayName(); if(!StringUtil.isEmpty(displayname)) param.set(KeyConstants._displayname,displayname); int defType = args[y].getDefaultType(); if(defType==FunctionArgument.DEFAULT_TYPE_RUNTIME_EXPRESSION){ param.set(KeyConstants._default, "[runtime expression]"); } else if(defType==FunctionArgument.DEFAULT_TYPE_LITERAL){ Page p=udf.getPage(pc); param.set(KeyConstants._default, p.udfDefaultValue(pc,udf.getIndex(),y,null)); } hint=args[y].getHint(); if(!StringUtil.isEmpty(hint))param.set(KeyConstants._hint,hint); // TODO func.set("userMetadata", value); neo unterstuetzt irgendwelche attr, die dann hier ausgebenen werden bloedsinn // meta data m=args[y].getMetaData(); if(m!=null) StructUtil.copy(m, param, true); params.append(param); } func.set(KeyConstants._parameters,params); return func; } public static int toModifier(String str, int emptyValue, int defaultValue) { if(StringUtil.isEmpty(str,true)) return emptyValue; str=str.trim(); if("abstract".equalsIgnoreCase(str)) return Component.MODIFIER_ABSTRACT; if("final".equalsIgnoreCase(str)) return Component.MODIFIER_FINAL; if("none".equalsIgnoreCase(str)) return Component.MODIFIER_NONE; return defaultValue; } public static String toModifier(int modifier, String defaultValue) { if(Component.MODIFIER_ABSTRACT==modifier) return "abstract"; if(Component.MODIFIER_FINAL==modifier) return "final"; if(Component.MODIFIER_NONE==modifier) return "none"; return defaultValue; } }