package railo.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 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;
import railo.commons.digest.MD5;
import railo.commons.io.IOUtil;
import railo.commons.io.res.Resource;
import railo.commons.io.res.util.ResourceUtil;
import railo.commons.lang.ClassUtil;
import railo.commons.lang.PhysicalClassLoader;
import railo.commons.lang.StringUtil;
import railo.commons.lang.types.RefBoolean;
import railo.runtime.Component;
import railo.runtime.ComponentPro;
import railo.runtime.ComponentWrap;
import railo.runtime.Mapping;
import railo.runtime.Page;
import railo.runtime.PageContext;
import railo.runtime.PageContextImpl;
import railo.runtime.PageSource;
import railo.runtime.PageSourceImpl;
import railo.runtime.component.Property;
import railo.runtime.config.Config;
import railo.runtime.engine.ThreadLocalPageContext;
import railo.runtime.exp.ExpressionException;
import railo.runtime.exp.PageException;
import railo.runtime.net.rpc.AxisCaster;
import railo.runtime.net.rpc.Pojo;
import railo.runtime.net.rpc.server.ComponentController;
import railo.runtime.net.rpc.server.RPCServer;
import railo.runtime.op.Caster;
import railo.runtime.type.Array;
import railo.runtime.type.ArrayImpl;
import railo.runtime.type.Collection;
import railo.runtime.type.Collection.Key;
import railo.runtime.type.FunctionArgument;
import railo.runtime.type.KeyImpl;
import railo.runtime.type.Struct;
import railo.runtime.type.StructImpl;
import railo.runtime.type.UDF;
import railo.runtime.type.UDFPropertiesImpl;
import railo.runtime.type.cfc.ComponentAccess;
import railo.transformer.bytecode.BytecodeContext;
import railo.transformer.bytecode.literal.LitString;
import railo.transformer.bytecode.util.ASMProperty;
import railo.transformer.bytecode.util.ASMUtil;
import railo.transformer.bytecode.util.Types;
import railo.transformer.bytecode.visitor.ArrayVisitor;
// TODO doc
public final class ComponentUtil {
private final static Method CONSTRUCTOR_OBJECT = Method.getMethod("void <init> ()");
private static final Type COMPONENT_CONTROLLER = Type.getType(ComponentController.class);
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,ComponentAccess component, RefBoolean isNew,boolean create,boolean writeLog, boolean supressWSbeforeArg) throws PageException {
isNew.setValue(false);
String classNameOriginal=component.getPageSource().getFullClassName();
String className=getClassname(component).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){}
}
if(!create) return null;
isNew.setValue(true);
//print.out("new");
// CREATE CLASS
ClassWriter cw = ASMUtil.getClassWriter();
//ClassWriter cw = new ClassWriter(true);
cw.visit(Opcodes.V1_2, Opcodes.ACC_PUBLIC, real, null, "java/lang/Object", null);
//GeneratorAdapter ga = new GeneratorAdapter(Opcodes.ACC_PUBLIC,Page.STATIC_CONSTRUCTOR,null,null,cw);
BytecodeContext statConstr = null;//new BytecodeContext(null,null,null,cw,real,ga,Page.STATIC_CONSTRUCTOR);
///ga = new GeneratorAdapter(Opcodes.ACC_PUBLIC,Page.CONSTRUCTOR,null,null,cw);
BytecodeContext constr = null;//new BytecodeContext(null,null,null,cw,real,ga,Page.CONSTRUCTOR);
// field component
//FieldVisitor fv = cw.visitField(Opcodes.ACC_PRIVATE, "c", "Lrailo/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(statConstr,constr,_keys,cw,real,component.get(keys[i]),max, writeLog,supressWSbeforeArg))!=-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);
railo.transformer.bytecode.Page.registerFields(new BytecodeContext(null,statConstr,constr,getPage(statConstr,constr),_keys,cw,real,adapter,CONSTRUCTOR_OBJECT,writeLog,supressWSbeforeArg), _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) {
throw Caster.toPageException(t);
}
}
private static railo.transformer.bytecode.Page getPage(BytecodeContext bc1, BytecodeContext bc2) {
railo.transformer.bytecode.Page page=null;
if(bc1!=null)page=bc1.getPage();
if(page==null && 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) {
boolean b= hasChangesOfChildren(last,ThreadLocalPageContext.get(),clazz);
return b;
}
/**
* 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.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) {
PageSource ps = component.getPageSource();
String path=ps.getDisplayPath();// Must remove webroot
Config config = ps.getMapping().getConfig();
String root = config.getRootDirectory().getAbsolutePath();
if(path.startsWith(root))
path=path.substring(root.length());
path=path.replace('\\', '/').toLowerCase();
path=ListUtil.trim(path, "/");
String[] arr = ListUtil.listToStringArray(path, '/');
StringBuffer rtn=new StringBuffer();
for(int i=0;i<arr.length;i++) {
if(i+1==arr.length) {
rtn.append(StringUtil.toVariableName(StringUtil.replaceLast(arr[i],".cfc","")));
}
else {
rtn.append(StringUtil.toVariableName(arr[i]));
rtn.append('.');
}
}
return rtn.toString();
}
/*
* includes the application context javasettings
* @param pc
* @param className
* @param properties
* @return
* @throws PageException
*/
public static Object getClientComponentPropertiesObject(PageContext pc, String className, ASMProperty[] properties) throws PageException {
try {
return _getClientComponentPropertiesObject(pc,pc.getConfig(), className, properties);
} 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 Object getClientComponentPropertiesObject(Config config, String className, ASMProperty[] properties) throws PageException {
try {
return _getClientComponentPropertiesObject(null,config, className, properties);
} catch (Exception e) {
throw Caster.toPageException(e);
}
}
private static Object _getClientComponentPropertiesObject(PageContext pc, Config secondChanceConfig, String className, ASMProperty[] properties) 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 ClassUtil.loadInstance(clazz);
}
}
catch(Exception e) {
}
}
// create file
byte[] barr = ASMUtil.createPojo(real, properties,Object.class,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 ClassUtil.loadInstance(cl.loadClass(className));
}
public static Class getServerComponentPropertiesClass(PageContext pc,Component component) throws PageException {
try {
return _getServerComponentPropertiesClass(pc,component);
}
catch (Exception e) {
throw Caster.toPageException(e);
}
}
private static Class _getServerComponentPropertiesClass(PageContext pc,Component component) throws PageException, IOException, ClassNotFoundException {
String className=getClassname(component);//StringUtil.replaceLast(classNameOriginal,"$cfc","");
String real=className.replace('.','/');
Mapping mapping = component.getPageSource().getMapping();
PhysicalClassLoader cl = (PhysicalClassLoader)((PageContextImpl)pc).getRPCClassLoader(false);
Resource classFile = cl.getDirectory().getRealResource(real.concat(".class"));
String classNameOriginal=component.getPageSource().getFullClassName();
String realOriginal=classNameOriginal.replace('.','/');
Resource classFileOriginal = mapping.getClassRootDirectory().getRealResource(realOriginal.concat(".class"));
// load existing class
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){}
}
// create file
byte[] barr = ASMUtil.createPojo(real, ASMUtil.toASMProperties(
ComponentUtil.getProperties(component, false, true, false, false)),Object.class,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));
}
private static int createMethod(BytecodeContext statConstr,BytecodeContext constr, java.util.List<LitString> keys,ClassWriter cw,String className, Object member,int max,boolean writeLog, boolean supressWSbeforeArg) throws PageException {
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);//Type.getType(Caster.cfTypeToClass(args[y].getTypeAsString()));
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,statConstr,constr,getPage(statConstr,constr),keys,cw,className,adapter,method,writeLog,supressWSbeforeArg);
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(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, ExpressionException {
return md5(ComponentWrap.toComponentWrap(Component.ACCESS_PRIVATE,c));
}
public static String md5(ComponentWrap 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 ExpressionException {
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 ExpressionException("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 ExpressionException {
String res = toStringAccess(access,null);
if(res!=null) return res;
throw new ExpressionException("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;
if(c instanceof ComponentAccess)
other=((ComponentAccess)c).keys(access);
else
other=CollectionUtil.keys(c);
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) {
if(c instanceof ComponentPro)
return ((ComponentPro)c).getProperties(onlyPeristent, includeBaseProperties,preferBaseProperties,preferBaseProperties);
return c.getProperties(onlyPeristent);
}
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 ComponentAccess toComponentAccess(Component comp, ComponentAccess defaultValue) {
if(comp instanceof ComponentAccess) return (ComponentAccess) comp;
if(comp instanceof ComponentWrap) return ((ComponentWrap) 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 ComponentAccess getActiveComponent(PageContext pc, ComponentAccess current) {
if(pc.getActiveComponent()==null) return current;
if(pc.getActiveUDF()!=null && (pc.getActiveComponent()).getPageSource()==(pc.getActiveUDF().getOwnerComponent()).getPageSource()){
return (ComponentAccess) pc.getActiveUDF().getOwnerComponent();
}
return (ComponentAccess) pc.getActiveComponent();//+++
}
public static long getCompileTime(PageContext pc, PageSource ps,long defaultValue) {
try {
return getCompileTime(pc, ps);
} catch (Throwable 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);
}
public static Struct getPropertiesAsStruct(ComponentAccess ca, boolean onlyPersistent) {
Property[] props = ca.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,UDFPropertiesImpl udf) throws PageException {
StructImpl func=new StructImpl();
pc=ThreadLocalPageContext.get(pc);
// TODO func.set("roles", value);
// TODO func.set("userMetadata", value); neo unterst゚tzt irgendwelche a
// meta data
Struct meta = udf.meta;
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.hint;
if(!StringUtil.isEmpty(hint))func.set(KeyConstants._hint,hint);
String displayname=udf.displayName;
if(!StringUtil.isEmpty(displayname))func.set(KeyConstants._displayname,displayname);
func.set(KeyConstants._name,udf.functionName);
func.set(KeyConstants._output,Caster.toBoolean(udf.output));
func.set(KeyConstants._returntype, udf.strReturnType);
func.set(KeyConstants._description, udf.description);
func.set(KeyConstants._owner, udf.pageSource.getDisplayPath());
int format = udf.returnFormat;
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.arguments;
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){
param.set(KeyConstants._default,
UDFUtil.getDefaultValue(pc, udf.pageSource, udf.index, y, null));
}
hint=args[y].getHint();
if(!StringUtil.isEmpty(hint))param.set(KeyConstants._hint,hint);
// TODO func.set("userMetadata", value); neo unterst゚tzt irgendwelche attr, die dann hier ausgebenen werden blレdsinn
// meta data
m=args[y].getMetaData();
if(m!=null) StructUtil.copy(m, param, true);
params.append(param);
}
func.set(KeyConstants._parameters,params);
return func;
}
}