/** * * Copyright (c) 2014, the Railo Company Ltd. All rights reserved. * * 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.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import lucee.print; import lucee.commons.lang.ExceptionUtil; import lucee.commons.lang.StringUtil; import lucee.loader.engine.CFMLEngine; import lucee.runtime.PageContext; import lucee.runtime.config.ConfigWebImpl; import lucee.runtime.exp.ExpressionException; import lucee.runtime.exp.PageException; import lucee.runtime.interpreter.ref.Ref; import lucee.runtime.interpreter.ref.cast.Casting; import lucee.runtime.interpreter.ref.func.BIFCall; import lucee.runtime.interpreter.ref.literal.LFunctionValue; import lucee.runtime.interpreter.ref.literal.LString; import lucee.runtime.op.Caster; import lucee.runtime.reflection.Reflector; import lucee.runtime.reflection.pairs.MethodInstance; import lucee.runtime.type.Collection; import lucee.runtime.type.Collection.Key; import lucee.runtime.type.KeyImpl; import lucee.runtime.type.Struct; import lucee.runtime.type.scope.Undefined; import lucee.transformer.library.function.FunctionLib; import lucee.transformer.library.function.FunctionLibFunction; import lucee.transformer.library.function.FunctionLibFunctionArg; public class MemberUtil { private static final Object DEFAULT = new Object(); private static Map<Short,Map<Collection.Key,FunctionLibFunction>> matchesLucee=new HashMap<Short, Map<Collection.Key,FunctionLibFunction>>(); private static Map<Short,Map<Collection.Key,FunctionLibFunction>> matchesCFML=new HashMap<Short, Map<Collection.Key,FunctionLibFunction>>(); public static Map<Collection.Key,FunctionLibFunction> getMembers(PageContext pc, short type) { Map<Short, Map<Key, FunctionLibFunction>> matches = pc.getCurrentTemplateDialect()==CFMLEngine.DIALECT_LUCEE?matchesLucee:matchesCFML; Map<Key, FunctionLibFunction> match = matches.get(type); if(match!=null) return match; FunctionLib[] flds = ((ConfigWebImpl)pc.getConfig()).getFLDs(pc.getCurrentTemplateDialect()); Iterator<FunctionLibFunction> it; FunctionLibFunction f; match=new HashMap<Collection.Key,FunctionLibFunction>(); String[] names; for(int i=0;i<flds.length;i++){ it = flds[i].getFunctions().values().iterator(); while(it.hasNext()){ f = it.next(); names = f.getMemberNames(); if(!ArrayUtil.isEmpty(names) && f.getMemberType()==type && f.getArgType()==FunctionLibFunction.ARG_FIX) { for(int y=0;y<names.length;y++) match.put(KeyImpl.getInstance(names[y]),f); } } } matches.put(type, match); return match; } public static Object call(PageContext pc, Object coll,Collection.Key methodName, Object[] args, short[] types, String[] strTypes) throws PageException { // look for members short type; String strType; Map<Key, FunctionLibFunction> members=null; for(int i=0;i<types.length;i++) { type=types[i]; strType=strTypes[i]; members = getMembers(pc, type); FunctionLibFunction member=members.get(methodName); if(member!=null) { List<FunctionLibFunctionArg> _args = member.getArg(); if(args.length<_args.size()){ ArrayList<Ref> refs=new ArrayList<Ref>(); int pos = member.getMemberPosition(); FunctionLibFunctionArg flfa; Iterator<FunctionLibFunctionArg> it = _args.iterator(); int glbIndex=0,argIndex=-1; while(it.hasNext()){ glbIndex++; flfa = it.next(); if(glbIndex==pos) { refs.add(new Casting(strType,type,coll)); } else if(args.length>++argIndex) { // careful, argIndex is only incremented when condition above is false refs.add(new Casting(flfa.getTypeAsString(),flfa.getType(),args[argIndex])); } } return new BIFCall(coll, member, refs.toArray(new Ref[refs.size()])).getValue(pc); } } } // do reflection if(pc.getConfig().getSecurityManager().getAccess(lucee.runtime.security.SecurityManager.TYPE_DIRECT_JAVA_ACCESS)==lucee.runtime.security.SecurityManager.VALUE_YES) { if(!(coll instanceof Undefined)) { Object res = callMethod(coll,methodName,args); if(res!=DEFAULT) return res; } } // merge if(types.length>1) { Map<Key, FunctionLibFunction> tmp; members=null; for(int i=0;i<types.length;i++) { tmp = getMembers(pc, types[i]); if(members==null) members=tmp; else { Iterator<Entry<Key, FunctionLibFunction>> it = tmp.entrySet().iterator(); Entry<Key, FunctionLibFunction> e; while(it.hasNext()) { e=it.next(); members.put(e.getKey(), e.getValue()); } } } } Set<Key> set = members.keySet(); String msg=ExceptionUtil.similarKeyMessage(set.toArray(new Key[set.size()]), methodName.getString(), "function", "functions", "Object", true); throw new ExpressionException(msg); //throw new ExpressionException("No matching function member ["+methodName+"] found, available function members are ["+ // lucee.runtime.type.util.ListUtil.sort(CollectionUtil.getKeyList(members.keySet().iterator(), ","),"textnocase","asc",",")+"]"); } private static Object callMethod(Object obj, Collection.Key methodName, Object[] args) throws PageException { MethodInstance mi=Reflector.getMethodInstanceEL(obj,obj.getClass(), methodName, args); if(mi==null) return DEFAULT; try { return mi.invoke(obj); } catch (Exception e) { throw Caster.toPageException(e); } } public static Object callWithNamedValues(PageContext pc,Object coll, Collection.Key methodName, Struct args,short type, String strType) throws PageException { Map<Key, FunctionLibFunction> members = getMembers(pc, type); FunctionLibFunction member=members.get(methodName); if(member!=null){ List<FunctionLibFunctionArg> _args = member.getArg(); FunctionLibFunctionArg arg; if(args.size()<_args.size()){ Object val; ArrayList<Ref> refs=new ArrayList<Ref>(); arg=_args.get(0); refs.add(new Casting(arg.getTypeAsString(),arg.getType(),new LFunctionValue(new LString(arg.getName()),coll))); for(int y=1;y<_args.size();y++){ arg = _args.get(y); // match by name val = args.get(arg.getName(),null); //match by alias if(val==null) { String alias=arg.getAlias(); if(!StringUtil.isEmpty(alias,true)) { String[] aliases = lucee.runtime.type.util.ListUtil.trimItems(lucee.runtime.type.util.ListUtil.listToStringArray(alias,',')); for(int x=0;x<aliases.length;x++){ val = args.get(aliases[x],null); if(val!=null) break; } } } if(val==null) { if(arg.getRequired()) { String[] names = member.getMemberNames(); String n=ArrayUtil.isEmpty(names)?"":names[0]; throw new ExpressionException("missing required argument ["+arg.getName()+"] for member function call ["+n+"]"); } } else{ refs.add(new Casting(arg.getTypeAsString(),arg.getType(),new LFunctionValue(new LString(arg.getName()),val))); //refs.add(new LFunctionValue(new LString(arg.getName()),new Casting(pc,arg.getTypeAsString(),arg.getType(),val))); } } return new BIFCall(coll,member, refs.toArray(new Ref[refs.size()])).getValue(pc); } } throw new ExpressionException("No matching function member ["+methodName+"] for call with named arguments found, available function members are ["+lucee.runtime.type.util.ListUtil.sort(CollectionUtil.getKeyList(members.keySet().iterator(), ","),"textnocase","asc",",")+"]"); } }