/**
* 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.util.ArrayList;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import lucee.commons.lang.StringUtil;
import lucee.runtime.Component;
import lucee.runtime.PageContext;
import lucee.runtime.dump.DumpData;
import lucee.runtime.dump.DumpProperties;
import lucee.runtime.dump.DumpRow;
import lucee.runtime.dump.DumpTable;
import lucee.runtime.dump.SimpleDumpData;
import lucee.runtime.exp.ApplicationException;
import lucee.runtime.exp.ExpressionException;
import lucee.runtime.exp.PageException;
import lucee.runtime.exp.PageExceptionImpl;
import lucee.runtime.op.Caster;
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.UDF;
import lucee.runtime.type.UDFGSProperty;
import lucee.runtime.type.scope.Argument;
import lucee.runtime.type.scope.ArgumentIntKey;
import lucee.transformer.library.function.FunctionLibFunction;
import lucee.transformer.library.function.FunctionLibFunctionArg;
public class UDFUtil {
public static final short TYPE_UDF=1;
public static final short TYPE_BIF=2;
public static final short TYPE_CLOSURE=4;
public static final short TYPE_LAMBDA=8;
private static final char CACHE_DEL = ';';
private static final char CACHE_DEL2 = ':';
private static final FunctionArgument[] EMPTY = new FunctionArgument[0];
/**
* add detailed function documentation to the exception
* @param pe
* @param flf
*/
public static void addFunctionDoc(PageExceptionImpl pe,FunctionLibFunction flf) {
ArrayList<FunctionLibFunctionArg> args=flf.getArg();
Iterator<FunctionLibFunctionArg> it = args.iterator();
// Pattern
StringBuilder pattern=new StringBuilder(flf.getName());
StringBuilder end=new StringBuilder();
pattern.append("(");
FunctionLibFunctionArg arg;
int c=0;
while(it.hasNext()){
arg = it.next();
if(!arg.isRequired()) {
pattern.append(" [");
end.append("]");
}
if(c++>0)pattern.append(", ");
pattern.append(arg.getName());
pattern.append(":");
pattern.append(arg.getTypeAsString());
}
pattern.append(end);
pattern.append("):");
pattern.append(flf.getReturnTypeAsString());
pe.setAdditional(KeyConstants._Pattern, pattern);
// Documentation
StringBuilder doc=new StringBuilder(flf.getDescription());
StringBuilder req=new StringBuilder();
StringBuilder opt=new StringBuilder();
StringBuilder tmp;
doc.append("\n");
it = args.iterator();
while(it.hasNext()){
arg = it.next();
tmp=arg.isRequired()?req:opt;
tmp.append("- ");
tmp.append(arg.getName());
tmp.append(" (");
tmp.append(arg.getTypeAsString());
tmp.append("): ");
tmp.append(arg.getDescription());
tmp.append("\n");
}
if(req.length()>0)doc.append("\nRequired:\n").append(req);
if(opt.length()>0)doc.append("\nOptional:\n").append(opt);
pe.setAdditional(KeyImpl.init("Documentation"), doc);
}
public static void argumentCollection(Struct values) {
argumentCollection(values,EMPTY);
}
public static void argumentCollection(Struct values, FunctionArgument[] funcArgs) {
Object value=values.removeEL(KeyConstants._argumentCollection);
if(value !=null) {
value=Caster.unwrap(value,value);
if(value instanceof Argument) {
Argument argColl=(Argument) value;
Iterator<Key> it = argColl.keyIterator();
Key k;
int i=-1;
while(it.hasNext()) {
i++;
k = it.next();
if(funcArgs.length>i && k instanceof ArgumentIntKey) {
if(!values.containsKey(funcArgs[i].getName()))
values.setEL(funcArgs[i].getName(),argColl.get(k,Argument.NULL));
else
values.setEL(k,argColl.get(k,Argument.NULL));
}
else if(!values.containsKey(k)){
values.setEL(k,argColl.get(k,Argument.NULL));
}
}
}
else if(value instanceof Collection) {
Collection argColl=(Collection) value;
//Collection.Key[] keys = argColl.keys();
Iterator<Key> it = argColl.keyIterator();
Key k;
while(it.hasNext()) {
k = it.next();
if(!values.containsKey(k)){
values.setEL(k,argColl.get(k,Argument.NULL));
}
}
}
else if(value instanceof Map) {
Map map=(Map) value;
Iterator it = map.entrySet().iterator();
Map.Entry entry;
Key key;
while(it.hasNext()) {
entry=(Entry) it.next();
key = Caster.toKey(entry.getKey(),null);
if(!values.containsKey(key)){
values.setEL(key,entry.getValue());
}
}
}
else if(value instanceof java.util.List) {
java.util.List list=(java.util.List) value;
Iterator it = list.iterator();
Object v;
int index=0;
Key k;
while(it.hasNext()) {
v= it.next();
k=ArgumentIntKey.init(++index);
if(!values.containsKey(k)){
values.setEL(k,v);
}
}
}
else {
values.setEL(KeyConstants._argumentCollection,value);
}
}
}
public static String toReturnFormat(int returnFormat,String defaultValue) {
if(UDF.RETURN_FORMAT_WDDX==returnFormat) return "wddx";
else if(UDF.RETURN_FORMAT_JSON==returnFormat) return "json";
else if(UDF.RETURN_FORMAT_PLAIN==returnFormat) return "plain";
else if(UDF.RETURN_FORMAT_SERIALIZE==returnFormat) return "cfml";
else if(UDF.RETURN_FORMAT_JAVA==returnFormat) return "java";
// NO XML else if(UDFPlus.RETURN_FORMAT_XML==returnFormat) return "xml";
else return defaultValue;
}
public static boolean isValidReturnFormat(int returnFormat) {
return toReturnFormat(returnFormat,null)!=null;
}
public static int toReturnFormat(String[] returnFormats, int defaultValue) {
if(ArrayUtil.isEmpty(returnFormats)) return defaultValue;
int rf;
for(int i=0;i<returnFormats.length;i++){
rf=toReturnFormat(returnFormats[i].trim(), -1);
if(rf!=-1) return rf;
}
return defaultValue;
}
public static int toReturnFormat(String returnFormat, int defaultValue) {
if(StringUtil.isEmpty(returnFormat,true)) return defaultValue;
returnFormat=returnFormat.trim().toLowerCase();
if("wddx".equals(returnFormat)) return UDF.RETURN_FORMAT_WDDX;
else if("json".equals(returnFormat)) return UDF.RETURN_FORMAT_JSON;
else if("plain".equals(returnFormat)) return UDF.RETURN_FORMAT_PLAIN;
else if("text".equals(returnFormat)) return UDF.RETURN_FORMAT_PLAIN;
else if("serialize".equals(returnFormat)) return UDF.RETURN_FORMAT_SERIALIZE;
else if("cfml".equals(returnFormat)) return UDF.RETURN_FORMAT_SERIALIZE;
else if("cfm".equals(returnFormat)) return UDF.RETURN_FORMAT_SERIALIZE;
else if("xml".equals(returnFormat)) return UDF.RETURN_FORMAT_XML;
else if("java".equals(returnFormat)) return UDF.RETURN_FORMAT_JAVA;
return defaultValue;
}
public static int toReturnFormat(String returnFormat) throws ExpressionException {
int rf = toReturnFormat(returnFormat,-1);
if(rf!=-1) return rf;
throw new ExpressionException("invalid returnFormat definition ["+returnFormat+"], valid values are [wddx,plain,json,cfml]");
}
public static String toReturnFormat(int returnFormat) throws ExpressionException {
if(UDF.RETURN_FORMAT_WDDX==returnFormat) return "wddx";
else if(UDF.RETURN_FORMAT_JSON==returnFormat) return "json";
else if(UDF.RETURN_FORMAT_PLAIN==returnFormat) return "plain";
else if(UDF.RETURN_FORMAT_SERIALIZE==returnFormat) return "cfml";
else if(UDF.RETURN_FORMAT_JAVA==returnFormat) return "java";
else throw new ExpressionException("invalid returnFormat definition, valid values are [wddx,plain,json,cfml]");
}
public static DumpData toDumpData(PageContext pageContext, int maxlevel, DumpProperties dp,UDF udf, short type) {
if(!dp.getShowUDFs()) {
if(TYPE_UDF==type) return new SimpleDumpData("<UDF>");
if(TYPE_BIF==type) return new SimpleDumpData("<BIF>");
if(TYPE_CLOSURE==type) return new SimpleDumpData("<Closure>");
if(TYPE_LAMBDA==type) return new SimpleDumpData("<Lambda>");
}
// arguments
FunctionArgument[] args = udf.getFunctionArguments();
DumpTable atts;
if(TYPE_UDF==type) atts= new DumpTable("udf","#ca5095","#e9accc","#000000");
else if(TYPE_CLOSURE==type) atts= new DumpTable("udf","#9cb770","#c7e1ba","#000000");
else if(TYPE_BIF==type) atts= new DumpTable("udf","#e1c039","#f1e2a3","#000000");
else atts= new DumpTable("udf","#f3d5bd","#f6e4cc","#000000");
atts.appendRow(new DumpRow(63,new DumpData[]{new SimpleDumpData("label"),new SimpleDumpData("name"),new SimpleDumpData("required"),new SimpleDumpData("type"),new SimpleDumpData("default"),new SimpleDumpData("hint")}));
for(int i=0;i<args.length;i++) {
FunctionArgument arg=args[i];
DumpData def;
try {
Object oa = udf.getDefaultValue(pageContext, i,null);
if(oa==null)oa="null";
def=new SimpleDumpData(Caster.toString(oa));
}
catch (PageException e) {
def=new SimpleDumpData("");
}
atts.appendRow(new DumpRow(0,new DumpData[]{
new SimpleDumpData(arg.getDisplayName()),
new SimpleDumpData(arg.getName().getString()),
new SimpleDumpData(arg.isRequired()),
new SimpleDumpData(arg.getTypeAsString()),
def,
new SimpleDumpData(arg.getHint())}));
//atts.setRow(0,arg.getHint());
}
DumpTable func;
String label = udf.getDisplayName();
if(TYPE_CLOSURE==type) {
func=new DumpTable("#9cb770","#c7e1ba","#000000");
func.setTitle(StringUtil.isEmpty(label)?"Closure":"Closure "+label);
}
else if(TYPE_UDF==type) {
func=new DumpTable("#ca5095","#e9accc","#000000");
String f="Function ";
try {
f=StringUtil.ucFirst(ComponentUtil.toStringAccess(udf.getAccess()).toLowerCase())+" "+f;
}
catch (ApplicationException e) {}
f+=udf.getFunctionName();
if(udf instanceof UDFGSProperty) f+=" (generated)";
func.setTitle(f);
}
else if(TYPE_BIF==type) {
String f="Build in Function "+(!StringUtil.isEmpty(label)?label: udf.getFunctionName());
func=new DumpTable("#e1c039","#f1e2a3","#000000");
func.setTitle(f);
}
else {
func=new DumpTable("#f3d5bd","#f6e4cc","#000000");
func.setTitle(StringUtil.isEmpty(label)?"Lambda":"Lambda "+label);
}
// Source
String src = udf.getSource();
if(!StringUtil.isEmpty(src)) func.setComment("source:"+src);
String hint=udf.getHint();
String desc=udf.getDescription();
if(!StringUtil.isEmpty(desc))addComment(func,desc);
if(!StringUtil.isEmpty(hint))addComment(func,hint);
if(Component.MODIFIER_NONE!=udf.getModifier())
func.appendRow(1,new SimpleDumpData("modifier"),new SimpleDumpData(ComponentUtil.toModifier(udf.getModifier(),"")));
func.appendRow(1,new SimpleDumpData("arguments"),atts);
func.appendRow(1,new SimpleDumpData("return type"),new SimpleDumpData(udf.getReturnTypeAsString()));
return func;
}
private static void addComment(DumpTable dt,String comment) {
if(StringUtil.isEmpty(dt.getComment()) || dt.getComment().indexOf(comment)!=-1) dt.setComment(comment);
else dt.setComment(dt.getComment()+"\n"+comment);
}
}