package railo.transformer.cfml.expression; import java.util.ArrayList; import java.util.Iterator; import railo.runtime.Component; import railo.runtime.config.NullSupportHelper; import railo.runtime.exp.CasterException; import railo.runtime.exp.TemplateException; import railo.runtime.functions.other.CreateUniqueId; import railo.runtime.op.Caster; import railo.runtime.type.scope.Scope; import railo.runtime.type.scope.ScopeSupport; import railo.runtime.type.util.UDFUtil; import railo.transformer.bytecode.BytecodeException; import railo.transformer.bytecode.Literal; import railo.transformer.bytecode.Page; import railo.transformer.bytecode.Position; import railo.transformer.bytecode.cast.CastDouble; import railo.transformer.bytecode.cast.CastString; import railo.transformer.bytecode.expression.ClosureAsExpression; import railo.transformer.bytecode.expression.ExprDouble; import railo.transformer.bytecode.expression.ExprString; import railo.transformer.bytecode.expression.Expression; import railo.transformer.bytecode.expression.ExpressionInvoker; import railo.transformer.bytecode.expression.Invoker; import railo.transformer.bytecode.expression.var.Argument; import railo.transformer.bytecode.expression.var.Assign; import railo.transformer.bytecode.expression.var.BIF; import railo.transformer.bytecode.expression.var.DataMember; import railo.transformer.bytecode.expression.var.DynAssign; import railo.transformer.bytecode.expression.var.FunctionMember; import railo.transformer.bytecode.expression.var.Member; import railo.transformer.bytecode.expression.var.NamedArgument; import railo.transformer.bytecode.expression.var.UDF; import railo.transformer.bytecode.expression.var.Variable; import railo.transformer.bytecode.literal.Identifier; import railo.transformer.bytecode.literal.LitBoolean; import railo.transformer.bytecode.literal.LitDouble; import railo.transformer.bytecode.literal.LitString; import railo.transformer.bytecode.literal.Null; import railo.transformer.bytecode.op.OPDecision; import railo.transformer.bytecode.op.OpBool; import railo.transformer.bytecode.op.OpContional; import railo.transformer.bytecode.op.OpDouble; import railo.transformer.bytecode.op.OpElvis; import railo.transformer.bytecode.op.OpNegate; import railo.transformer.bytecode.op.OpNegateNumber; import railo.transformer.bytecode.op.OpString; import railo.transformer.bytecode.op.OpVariable; import railo.transformer.bytecode.statement.tag.Attribute; import railo.transformer.bytecode.statement.udf.Closure; import railo.transformer.bytecode.statement.udf.Function; import railo.transformer.bytecode.util.ASMUtil; import railo.transformer.cfml.Data; import railo.transformer.cfml.TransfomerSettings; import railo.transformer.cfml.evaluator.EvaluatorPool; import railo.transformer.cfml.script.DocComment; import railo.transformer.cfml.script.DocCommentTransformer; import railo.transformer.cfml.tag.CFMLTransformer; import railo.transformer.library.function.FunctionLib; import railo.transformer.library.function.FunctionLibFunction; import railo.transformer.library.function.FunctionLibFunctionArg; import railo.transformer.library.tag.TagLibTag; import railo.transformer.library.tag.TagLibTagAttr; import railo.transformer.library.tag.TagLibTagScript; import railo.transformer.util.CFMLString; /** * * Der CFMLExprTransfomer implementiert das Interface ExprTransfomer, er bildet die Parser Grammatik ab, die unten definiert ist. Er erh¦lt als Eingabe CFML Code, als String oder CFMLString, der einen CFML Expression erh¦lt und liefert ein CFXD Element zurck, das diesen Ausdruck abbildet. Mithilfe der FunctionLibメs, kann er Funktionsaufrufe, die Teil eines Ausdruck sein k￶nnen, erkennen und validieren. Dies geschieht innerhalb der Methode function. Falls ein Funktionsaufruf, einer Funktion innerhalb einer FunctionLib entspricht, werden diese gegeneinander verglichen und der Aufruf wird als Build-In-Funktion bernommen, andernfalls wird der Funktionsaufruf als User-Defined-Funktion interpretiert. Die Klasse Cast, Operator und ElementFactory (siehe 3.2) helfen ihm beim erstellen des Ausgabedokument CFXD. * <pre> * Parser Grammatik EBNF (Extended Backus-Naur Form) transform = spaces impOp; impOp = eqvOp {"imp" spaces eqvOp}; eqvOp = xorOp {"eqv" spaces xorOp}; xorOp = orOp {"xor" spaces orOp}; orOp = andOp {("or" | "||") spaces andOp}; (* "||" Existiert in CFMX nicht *) andOp = notOp {("and" | "&&") spaces notOp}; (* "&&" Existiert in CFMX nicht *) notOp = [("not"|"!") spaces] decsionOp; (* "!" Existiert in CFMX nicht *) decsionOp = concatOp {("neq"|"eq"|"gte"|"gt"|"lte"|"lt"|"ct"| "contains"|"nct"|"does not contain") spaces concatOp}; (* "ct"=conatains und "nct"=does not contain; Existiert in CFMX nicht *) concatOp = plusMinusOp {"&" spaces plusMinusOp}; plusMinusOp = modOp {("-"|"+") spaces modOp}; modOp = divMultiOp {("mod" | "%") spaces divMultiOp}; (* modulus operator , "%" Existiert in CFMX nicht *) divMultiOp = expoOp {("*"|"/") spaces expoOp}; expoOp = clip {("exp"|"^") spaces clip}; (*exponent operator, " exp " Existiert in CFMX nicht *) clip = ("(" spaces impOp ")" spaces) | checker; checker = string | number | dynamic | sharp; string = ("'" {"##"|"''"|"#" impOp "#"| ?-"#"-"'" } "'") | (""" {"##"|""""|"#" impOp "#"| ?-"#"-""" } """); number = ["+"|"-"] digit {digit} {"." digit {digit}}; digit = "0"|..|"9"; dynamic = "true" | "false" | "yes" | "no" | startElement {("." identifier | "[" structElement "]")[function] }; startElement = identifier "(" functionArg ")" | scope | identifier; scope = "variable" | "cgi" | "url" | "form" | "session" | "application" | "arguments" | "cookie" | "client "; identifier = (letter | "_") {letter | "_"|digit}; structElement = "[" impOp "]"; functionArg = [impOp{"," impOp}]; sharp = "#" checker "#"; spaces = {space}; space = "\s"|"\t"|"\f"|"\t"|"\n"; letter = "a"|..|"z"|"A"|..|"Z"; {"x"}= 0 bis n mal "x" ["x"]= 0 bis 1 mal "x" ("x" | "y")"z" = "xz" oder "yz" </pre> * */ public abstract class AbstrCFMLExprTransformer { private static final short STATIC=0; private static final short DYNAMIC=1; private static FunctionLibFunction JSON_ARRAY = null; private static FunctionLibFunction JSON_STRUCT = null; public static final short CTX_OTHER = TagLibTagScript.CTX_OTHER; public static final short CTX_NONE = TagLibTagScript.CTX_NONE; public static final short CTX_IF = TagLibTagScript.CTX_IF; public static final short CTX_ELSE_IF = TagLibTagScript.CTX_ELSE_IF; public static final short CTX_ELSE = TagLibTagScript.CTX_ELSE; public static final short CTX_FOR = TagLibTagScript.CTX_FOR; public static final short CTX_WHILE = TagLibTagScript.CTX_WHILE; public static final short CTX_DO_WHILE = TagLibTagScript.CTX_DO_WHILE; public static final short CTX_CFC = TagLibTagScript.CTX_CFC; public static final short CTX_INTERFACE = TagLibTagScript.CTX_INTERFACE; public static final short CTX_FUNCTION =TagLibTagScript.CTX_FUNCTION; public static final short CTX_BLOCK = TagLibTagScript.CTX_BLOCK; public static final short CTX_FINALLY = TagLibTagScript.CTX_FINALLY; public static final short CTX_SWITCH = TagLibTagScript.CTX_SWITCH; public static final short CTX_TRY = TagLibTagScript.CTX_TRY; public static final short CTX_CATCH = TagLibTagScript.CTX_CATCH; public static final short CTX_TRANSACTION = TagLibTagScript.CTX_TRANSACTION; public static final short CTX_THREAD = TagLibTagScript.CTX_THREAD; public static final short CTX_SAVECONTENT = TagLibTagScript.CTX_SAVECONTENT; public static final short CTX_LOCK = TagLibTagScript.CTX_LOCK; public static final short CTX_LOOP = TagLibTagScript.CTX_LOOP; public static final short CTX_QUERY = TagLibTagScript.CTX_QUERY; public static final short CTX_ZIP = TagLibTagScript.CTX_ZIP; private DocCommentTransformer docCommentTransformer= new DocCommentTransformer(); protected short ATTR_TYPE_NONE=TagLibTagAttr.SCRIPT_SUPPORT_NONE; protected short ATTR_TYPE_OPTIONAL=TagLibTagAttr.SCRIPT_SUPPORT_OPTIONAL; protected short ATTR_TYPE_REQUIRED=TagLibTagAttr.SCRIPT_SUPPORT_REQUIRED; protected static final Expression NULL = LitString.toExprString("NULL"); protected static final Attribute ANY = new Attribute(false,"type",LitString.toExprString("any"),"string"); protected static EndCondition SEMI_BLOCK=new EndCondition() { public boolean isEnd(ExprData data) { return data.cfml.isCurrent('{') || data.cfml.isCurrent(';'); } }; protected static EndCondition SEMI=new EndCondition() { public boolean isEnd(ExprData data) { return data.cfml.isCurrent(';'); } }; protected static EndCondition COMMA_ENDBRACKED=new EndCondition() { public boolean isEnd(ExprData data) { return data.cfml.isCurrent(',') || data.cfml.isCurrent(')'); } }; public static interface EndCondition { public boolean isEnd(ExprData data); } /*private short mode=0; protected CFMLString cfml; protected FunctionLib[] fld; private boolean ignoreScopes=false; private boolean allowLowerThan;*/ public class ExprData extends Data { private short mode=0; private boolean ignoreScopes=false; private boolean allowLowerThan; public boolean insideFunction; public String tagName; public boolean isCFC; public boolean isInterface; public short context=CTX_NONE; public DocComment docComment; public ExprData(Page page, EvaluatorPool ep, CFMLString cfml, FunctionLib[] flibs, TransfomerSettings settings,boolean allowLowerThan,TagLibTag[] scriptTags) { super(page,cfml,ep,settings,flibs,scriptTags); this.allowLowerThan=allowLowerThan; } } protected Expression transformAsString(ExprData data,String[] breakConditions) throws TemplateException { Expression el=null; // parse the houle Page String comments(data); // String if((el=string(data))!=null) { data.mode=STATIC; return el; } // Sharp if((el=sharp(data))!=null) { data.mode=DYNAMIC; return el; } // Simple return simple(data,breakConditions); } /** * Initialmethode, wird aufgerufen um den internen Zustand des Objektes zu setzten. * @param fld Function Libraries zum validieren der Funktionen * @param cfml CFML Code der transfomiert werden soll. */ protected ExprData init(Page page,EvaluatorPool ep,FunctionLib[] fld,TagLibTag[] scriptTags, CFMLString cfml, TransfomerSettings settings, boolean allowLowerThan) { ExprData data = new ExprData(page,ep,cfml,fld,settings,allowLowerThan,scriptTags); if(JSON_ARRAY==null)JSON_ARRAY=getFLF(data,"_jsonArray"); if(JSON_STRUCT==null)JSON_STRUCT=getFLF(data,"_jsonStruct"); return data; //this.allowLowerThan=allowLowerThan; //this.fld = fld; //this.cfml = cfml; } /** * Startpunkt zum transfomieren einer Expression, ohne dass das Objekt neu initialisiert wird, * dient vererbten Objekten als Einstiegspunkt. * @return Element * @throws TemplateException */ protected Expression expression(ExprData data) throws TemplateException { return assignOp(data); } /** * Liest einen gelableten Funktionsparamter ein * <br /> * EBNF:<br /> * <code>assignOp [":" spaces assignOp];</code> * @return CFXD Element * @throws TemplateException */ private Argument functionArgument(ExprData data, boolean varKeyUpperCase) throws TemplateException { return functionArgument(data,null,varKeyUpperCase); } private Argument functionArgument(ExprData data,String type, boolean varKeyUpperCase) throws TemplateException { Expression expr = assignOp(data); try{ if (data.cfml.forwardIfCurrent(":")) { comments(data); return new NamedArgument(expr,assignOp(data),type,varKeyUpperCase); } else if(expr instanceof DynAssign){ DynAssign da=(DynAssign) expr; return new NamedArgument(da.getName(),da.getValue(),type,varKeyUpperCase); } else if(expr instanceof Assign && !(expr instanceof OpVariable)){ Assign a=(Assign) expr; return new NamedArgument(a.getVariable(),a.getValue(),type,varKeyUpperCase); } } catch(BytecodeException be) { throw new TemplateException(data.cfml,be.getMessage()); } return new Argument(expr,type); } /** * Transfomiert Zuweisungs Operation. * <br /> * EBNF:<br /> * <code>eqvOp ["=" spaces assignOp];</code> * @return CFXD Element * @throws TemplateException */ protected Expression assignOp(ExprData data) throws TemplateException { Expression expr = conditionalOp(data); if (data.cfml.forwardIfCurrent('=')) { comments(data); if(data.mode==STATIC) expr=new DynAssign(expr,assignOp(data)); else { if(expr instanceof Variable) expr=new Assign((Variable)expr,assignOp(data)); else if(expr instanceof Null) { expr=new Assign(((Null)expr).toVariable(),assignOp(data)); } else throw new TemplateException(data.cfml,"invalid assignment left-hand side ("+expr.getClass().getName()+")"); } } return expr; } private Expression conditionalOp(ExprData data) throws TemplateException { Expression expr = impOp(data); if (data.cfml.forwardIfCurrent('?')) { comments(data); // Elvis if(data.cfml.forwardIfCurrent(':')) { comments(data); Expression right = assignOp(data); if(!(expr instanceof Variable) ) throw new TemplateException(data.cfml,"left operant of the Elvis operator has to be a variable or a function call"); return OpElvis.toExpr((Variable)expr, right); } Expression left = assignOp(data); comments(data); if(!data.cfml.forwardIfCurrent(':'))throw new TemplateException("invalid conditional operator"); comments(data); Expression right = assignOp(data); expr=OpContional.toExpr(expr, left, right); } return expr; } /** * Transfomiert eine Implication (imp) Operation. * <br /> * EBNF:<br /> * <code>eqvOp {"imp" spaces eqvOp};</code> * @return CFXD Element * @throws TemplateException */ private Expression impOp(ExprData data) throws TemplateException { Expression expr = eqvOp(data); while(data.cfml.forwardIfCurrentAndNoWordAfter("imp")) { comments(data); expr=OpBool.toExprBoolean(expr, eqvOp(data), OpBool.IMP); } return expr; } /** * Transfomiert eine Equivalence (eqv) Operation. * <br /> * EBNF:<br /> * <code>xorOp {"eqv" spaces xorOp};</code> * @return CFXD Element * @throws TemplateException */ private Expression eqvOp(ExprData data) throws TemplateException { Expression expr = xorOp(data); while(data.cfml.forwardIfCurrentAndNoWordAfter("eqv")) { comments(data); expr=OpBool.toExprBoolean(expr, xorOp(data), OpBool.EQV); } return expr; } /** * Transfomiert eine Xor (xor) Operation. * <br /> * EBNF:<br /> * <code>orOp {"xor" spaces orOp};</code> * @return CFXD Element * @throws TemplateException */ private Expression xorOp(ExprData data) throws TemplateException { Expression expr = orOp(data); while(data.cfml.forwardIfCurrentAndNoWordAfter("xor")) { comments(data); expr=OpBool.toExprBoolean(expr, orOp(data), OpBool.XOR); } return expr; } /** * Transfomiert eine Or (or) Operation. Im Gegensatz zu CFMX , * werden "||" Zeichen auch als Or Operatoren anerkannt. * <br /> * EBNF:<br /> * <code>andOp {("or" | "||") spaces andOp}; (* "||" Existiert in CFMX nicht *)</code> * @return CFXD Element * @throws TemplateException */ private Expression orOp(ExprData data) throws TemplateException { Expression expr = andOp(data); while(data.cfml.forwardIfCurrent("||") || data.cfml.forwardIfCurrentAndNoWordAfter("or")) { comments(data); expr=OpBool.toExprBoolean(expr, andOp(data), OpBool.OR); } return expr; } /** * Transfomiert eine And (and) Operation. Im Gegensatz zu CFMX , * werden "&&" Zeichen auch als And Operatoren anerkannt. * <br /> * EBNF:<br /> * <code>notOp {("and" | "&&") spaces notOp}; (* "&&" Existiert in CFMX nicht *)</code> * @return CFXD Element * @throws TemplateException */ private Expression andOp(ExprData data) throws TemplateException { Expression expr = notOp(data); while(data.cfml.forwardIfCurrent("&&") || data.cfml.forwardIfCurrentAndNoWordAfter("and")) { comments(data); expr=OpBool.toExprBoolean(expr, notOp(data), OpBool.AND); } return expr; } /** * Transfomiert eine Not (not) Operation. Im Gegensatz zu CFMX , * wird das "!" Zeichen auch als Not Operator anerkannt. * <br /> * EBNF:<br /> * <code>[("not"|"!") spaces] decsionOp; (* "!" Existiert in CFMX nicht *)</code> * @return CFXD Element * @throws TemplateException */ private Expression notOp(ExprData data) throws TemplateException { // And Operation Position line = data.cfml.getPosition(); if (data.cfml.isCurrent('!') && !data.cfml.isCurrent("!=")) { data.cfml.next(); comments(data); return OpNegate.toExprBoolean(notOp(data),line,data.cfml.getPosition()); } else if (data.cfml.forwardIfCurrentAndNoWordAfter("not")) { comments(data); return OpNegate.toExprBoolean(notOp(data),line,data.cfml.getPosition()); } return decsionOp(data); } /** * <font f>Transfomiert eine Vergleichs Operation. * <br /> * EBNF:<br /> * <code>concatOp {("neq"|"eq"|"gte"|"gt"|"lte"|"lt"|"ct"| "contains"|"nct"|"does not contain") spaces concatOp}; (* "ct"=conatains und "nct"=does not contain; Existiert in CFMX nicht *)</code> * @return CFXD Element * @throws TemplateException */ private Expression decsionOp(ExprData data) throws TemplateException { Expression expr = concatOp(data); boolean hasChanged=false; // ct, contains do { hasChanged=false; if(data.cfml.isCurrent('c')) { if (data.cfml.forwardIfCurrent("ct",false,true)) {expr = decisionOpCreate(data,OPDecision.CT,expr);hasChanged=true;} else if (data.cfml.forwardIfCurrent("contains",false,true)){ expr = decisionOpCreate(data,OPDecision.CT,expr);hasChanged=true;} } // does not contain else if (data.cfml.forwardIfCurrent("does","not","contain",false,true)){ expr = decisionOpCreate(data,OPDecision.NCT,expr); hasChanged=true;} // equal, eq else if (data.cfml.isCurrent("eq") && !data.cfml.isCurrent("eqv")) { int plus=2; data.cfml.setPos(data.cfml.getPos()+2); if(data.cfml.forwardIfCurrent("ual"))plus=5; if(data.cfml.isCurrentVariableCharacter()) { data.cfml.setPos(data.cfml.getPos()-plus); } else { expr = decisionOpCreate(data,OPDecision.EQ,expr); hasChanged=true; } } // == else if (data.cfml.forwardIfCurrent("==")) { if(data.cfml.forwardIfCurrent('=')) expr = decisionOpCreate(data,OPDecision.EEQ,expr); else expr = decisionOpCreate(data,OPDecision.EQ,expr); hasChanged=true; } // != else if (data.cfml.forwardIfCurrent("!=")) { if(data.cfml.forwardIfCurrent('=')) expr = decisionOpCreate(data,OPDecision.NEEQ,expr); else expr = decisionOpCreate(data,OPDecision.NEQ,expr); hasChanged=true; } // <=/</<> else if (data.cfml.isCurrent('<')) { hasChanged=true; if(data.cfml.isNext('=')) { data.cfml.next();data.cfml.next(); expr = decisionOpCreate(data,OPDecision.LTE,expr); } else if(data.cfml.isNext('>')) { data.cfml.next();data.cfml.next(); expr = decisionOpCreate(data,OPDecision.NEQ,expr); } else if(data.cfml.isNext('/')) { hasChanged=false; } else { data.cfml.next(); expr = decisionOpCreate(data,OPDecision.LT,expr); } } // >=/> else if (data.allowLowerThan && data.cfml.forwardIfCurrent('>')) { if(data.cfml.forwardIfCurrent('=')) expr = decisionOpCreate(data,OPDecision.GTE,expr); else expr = decisionOpCreate(data,OPDecision.GT,expr); hasChanged=true; } // gt, gte, greater than or equal to, greater than else if (data.cfml.isCurrent('g')) { if (data.cfml.forwardIfCurrent("gt")) { if(data.cfml.forwardIfCurrentAndNoWordAfter("e")) { if(data.cfml.isCurrentVariableCharacter()) { data.cfml.setPos(data.cfml.getPos()-3); } else { expr = decisionOpCreate(data,OPDecision.GTE,expr); hasChanged=true; } } else { if(data.cfml.isCurrentVariableCharacter()) { data.cfml.setPos(data.cfml.getPos()-2); } else { expr = decisionOpCreate(data,OPDecision.GT,expr); hasChanged=true; } } } else if (data.cfml.forwardIfCurrent("greater", "than",false,true)) { if(data.cfml.forwardIfCurrent("or","equal", "to",true,true)) expr = decisionOpCreate(data,OPDecision.GTE,expr); else expr = decisionOpCreate(data,OPDecision.GT,expr); hasChanged=true; } else if (data.cfml.forwardIfCurrent("ge",false,true)) { expr = decisionOpCreate(data,OPDecision.GTE,expr); hasChanged=true; } } // is, is not else if (data.cfml.forwardIfCurrent("is",false,true)) { if(data.cfml.forwardIfCurrent("not",true,true)) expr = decisionOpCreate(data,OPDecision.NEQ,expr); else expr = decisionOpCreate(data,OPDecision.EQ,expr); hasChanged=true; } // lt, lte, less than, less than or equal to else if (data.cfml.isCurrent('l')) { if (data.cfml.forwardIfCurrent("lt")) { if(data.cfml.forwardIfCurrentAndNoWordAfter("e")) { if(data.cfml.isCurrentVariableCharacter()) { data.cfml.setPos(data.cfml.getPos()-3); } else { expr = decisionOpCreate(data,OPDecision.LTE,expr); hasChanged=true; } } else { if(data.cfml.isCurrentVariableCharacter()) { data.cfml.setPos(data.cfml.getPos()-2); } else { expr = decisionOpCreate(data,OPDecision.LT,expr); hasChanged=true; } } } else if (data.cfml.forwardIfCurrent("less","than",false,true)) { if(data.cfml.forwardIfCurrent("or", "equal", "to",true,true)) expr = decisionOpCreate(data,OPDecision.LTE,expr); else expr = decisionOpCreate(data,OPDecision.LT,expr); hasChanged=true; } else if (data.cfml.forwardIfCurrent("le",false,true)) { expr = decisionOpCreate(data,OPDecision.LTE,expr); hasChanged=true; } } // neq, not equal, nct else if (data.cfml.isCurrent('n')) { // Not Equal if (data.cfml.forwardIfCurrent("neq",false,true)){ expr = decisionOpCreate(data,OPDecision.NEQ,expr); hasChanged=true;} // Not Equal (Alias) else if (data.cfml.forwardIfCurrent("not","equal",false,true)){ expr = decisionOpCreate(data,OPDecision.NEQ,expr);hasChanged=true; } // nct else if (data.cfml.forwardIfCurrent("nct",false,true)){ expr = decisionOpCreate(data,OPDecision.NCT,expr); hasChanged=true;} } } while(hasChanged); return expr; } private Expression decisionOpCreate(ExprData data,int operation, Expression left) throws TemplateException { comments(data); return OPDecision.toExprBoolean(left, concatOp(data), operation); } /** * Transfomiert eine Konkatinations-Operator (&) Operation. Im Gegensatz zu CFMX , * wird das "!" Zeichen auch als Not Operator anerkannt. * <br /> * EBNF:<br /> * <code>plusMinusOp {"&" spaces concatOp};</code> * @return CFXD Element * @throws TemplateException */ private Expression concatOp(ExprData data) throws TemplateException { Expression expr = plusMinusOp(data); while(data.cfml.isCurrent('&') && !data.cfml.isCurrent("&&")) { data.cfml.next(); // &= if (data.cfml.isCurrent('=') && expr instanceof Variable) { data.cfml.next(); comments(data); Expression right = assignOp(data); ExprString res = OpString.toExprString(expr, right); expr=new OpVariable((Variable)expr,res); } else { comments(data); expr=OpString.toExprString(expr, plusMinusOp(data)); } } return expr; } /** * Transfomiert die mathematischen Operatoren Plus und Minus (1,-). * <br /> * EBNF:<br /> * <code>modOp [("-"|"+") spaces plusMinusOp];</code> * @return CFXD Element * @throws TemplateException */ private Expression plusMinusOp(ExprData data) throws TemplateException { Expression expr = modOp(data); while(!data.cfml.isLast()) { // Plus Operation if (data.cfml.forwardIfCurrent('+')) expr=_plusMinusOp(data,expr,OpDouble.PLUS); // Minus Operation else if (data.cfml.forwardIfCurrent('-')) expr=_plusMinusOp(data,expr,OpDouble.MINUS); else break; } return expr; } private Expression _plusMinusOp(ExprData data,Expression expr,int opr) throws TemplateException { // += if (data.cfml.isCurrent('=') && expr instanceof Variable) { data.cfml.next(); comments(data); Expression right = assignOp(data); ExprDouble res = OpDouble.toExprDouble(expr, right,opr); expr=new OpVariable((Variable)expr,res); } /*/ ++ else if (data.cfml.isCurrent(opr==OpDouble.PLUS?'+':'-') && expr instanceof Variable) { data.cfml.next(); comments(data); ExprDouble res = OpDouble.toExprDouble(expr, LitDouble.toExprDouble(1D,-1),opr); expr=new OpVariable((Variable)expr,res); expr=OpDouble.toExprDouble(expr,LitDouble.toExprDouble(1D, -1),opr==OpDouble.PLUS? OpDouble.MINUS:OpDouble.PLUS); //comments(data); }*/ else { comments(data); expr=OpDouble.toExprDouble(expr, modOp(data), opr); } return expr; } /** * Transfomiert eine Modulus Operation. Im Gegensatz zu CFMX , * wird das "%" Zeichen auch als Modulus Operator anerkannt. * <br /> * EBNF:<br /> * <code>divMultiOp {("mod" | "%") spaces divMultiOp}; (* modulus operator , "%" Existiert in CFMX nicht *)</code> * @return CFXD Element * @throws TemplateException */ private Expression modOp(ExprData data) throws TemplateException { Expression expr = divMultiOp(data); // Modulus Operation while(data.cfml.forwardIfCurrent('%') || data.cfml.forwardIfCurrentAndNoWordAfter("mod")) { expr=_modOp(data,expr); //comments(data); //expr=OpDouble.toExprDouble(expr, divMultiOp(), OpDouble.MODULUS); } return expr; } private Expression _modOp(ExprData data,Expression expr) throws TemplateException { if (data.cfml.isCurrent('=') && expr instanceof Variable) { data.cfml.next(); comments(data); Expression right = assignOp(data); ExprDouble res = OpDouble.toExprDouble(expr, right,OpDouble.MODULUS); return new OpVariable((Variable)expr,res); } comments(data); return OpDouble.toExprDouble(expr, expoOp(data), OpDouble.MODULUS); } /** * Transfomiert die mathematischen Operatoren Mal und Durch (*,/). * <br /> * EBNF:<br /> * <code>expoOp {("*"|"/") spaces expoOp};</code> * @return CFXD Element * @throws TemplateException */ private Expression divMultiOp(ExprData data) throws TemplateException { Expression expr = expoOp(data); while (!data.cfml.isLast()) { // Multiply Operation if(data.cfml.forwardIfCurrent('*')) { expr=_divMultiOp(data,expr,OpDouble.MULTIPLY); //comments(data); //expr=OpDouble.toExprDouble(expr, expoOp(), OpDouble.MULTIPLY); } // Divide Operation else if (data.cfml.isCurrent('/') && (!data.cfml.isCurrent('/','>') )) { data.cfml.next(); expr=_divMultiOp(data,expr,OpDouble.DIVIDE); //comments(data); //expr=OpDouble.toExprDouble(expr, expoOp(), OpDouble.DIVIDE); } // Divide Operation else if (data.cfml.isCurrent('\\')) { data.cfml.next(); expr=_divMultiOp(data,expr,OpDouble.INTDIV); //comments(data); //expr=OpDouble.toExprDouble(expr, expoOp(), OpDouble.INTDIV); } else { break; } } return expr; } private Expression _divMultiOp(ExprData data,Expression expr, int iOp) throws TemplateException { if (data.cfml.isCurrent('=') && expr instanceof Variable) { data.cfml.next(); comments(data); Expression right = assignOp(data); ExprDouble res = OpDouble.toExprDouble(expr, right,iOp); return new OpVariable((Variable)expr,res); } comments(data); return OpDouble.toExprDouble(expr, expoOp(data), iOp); } /** * Transfomiert den Exponent Operator (^,exp). Im Gegensatz zu CFMX , * werden die Zeichen " exp " auch als Exponent anerkannt. * <br /> * EBNF:<br /> * <code>clip {("exp"|"^") spaces clip};</code> * @return CFXD Element * @throws TemplateException */ private Expression expoOp(ExprData data) throws TemplateException { Expression expr = unaryOp(data); // Modulus Operation while(data.cfml.forwardIfCurrent('^') || data.cfml.forwardIfCurrentAndNoWordAfter("exp")) { comments(data); expr=OpDouble.toExprDouble(expr, unaryOp(data), OpDouble.EXP); } return expr; } private Expression unaryOp(ExprData data) throws TemplateException { Expression expr = negatePlusMinusOp(data); // Plus Operation if (data.cfml.forwardIfCurrent("++") && expr instanceof Variable) expr=_unaryOp(data,expr,OpDouble.PLUS); // Minus Operation else if (data.cfml.forwardIfCurrent("--") && expr instanceof Variable) expr=_unaryOp(data,expr,OpDouble.MINUS); return expr; } private Expression _unaryOp(ExprData data,Expression expr,int opr) throws TemplateException { Position leftEnd = expr.getEnd(),start=null,end=null; comments(data); if(leftEnd!=null){ start=leftEnd; end=new Position(leftEnd.line, leftEnd.column+2, leftEnd.pos+2); } ExprDouble res = OpDouble.toExprDouble(expr, LitDouble.toExprDouble(1D,start,end),opr); expr=new OpVariable((Variable)expr,res); return OpDouble.toExprDouble(expr,LitDouble.toExprDouble(1D,start,end),opr==OpDouble.PLUS? OpDouble.MINUS:OpDouble.PLUS); } /** * Negate Numbers * @return CFXD Element * @throws TemplateException */ private Expression negatePlusMinusOp(ExprData data) throws TemplateException { // And Operation Position line=data.cfml.getPosition(); if (data.cfml.forwardIfCurrent('-')) { if (data.cfml.forwardIfCurrent('-')) { comments(data); Expression expr = clip(data); ExprDouble res = OpDouble.toExprDouble(expr, LitDouble.toExprDouble(1D),OpDouble.MINUS); return new OpVariable((Variable)expr,res); } comments(data); return OpNegateNumber.toExprDouble(clip(data),OpNegateNumber.MINUS,line,data.cfml.getPosition()); } else if (data.cfml.forwardIfCurrent('+')) { if (data.cfml.forwardIfCurrent('+')) { comments(data); Expression expr = clip(data); ExprDouble res = OpDouble.toExprDouble(expr, LitDouble.toExprDouble(1D),OpDouble.PLUS); return new OpVariable((Variable)expr,res); } comments(data); return CastDouble.toExprDouble(clip(data));//OpNegateNumber.toExprDouble(clip(),OpNegateNumber.PLUS,line); } return clip(data); } /** * Verarbeitet Ausdrcke die inerhalb einer Klammer stehen. * <br /> * EBNF:<br /> * <code>("(" spaces impOp ")" spaces) | checker;</code> * @return CFXD Element * @throws TemplateException */ private Expression clip(ExprData data) throws TemplateException { return checker(data); } /** * Hier werden die verschiedenen M￶glichen Werte erkannt * und jenachdem wird mit der passenden Methode weitergefahren * <br /> * EBNF:<br /> * <code>string | number | dynamic | sharp;</code> * @return CFXD Element * @throws TemplateException */ private Expression checker(ExprData data) throws TemplateException { Expression expr=null; // String if((expr=string(data))!=null) { expr = subDynamic(data,expr); data.mode=STATIC;//(expr instanceof Literal)?STATIC:DYNAMIC;// STATIC return expr; } // Number if((expr=number(data))!=null) { expr = subDynamic(data,expr); data.mode=STATIC;//(expr instanceof Literal)?STATIC:DYNAMIC;// STATIC return expr; } // closure if((expr=closure(data))!=null) { data.mode=DYNAMIC; return expr; } // Dynamic if((expr=dynamic(data))!=null) { expr = newOp(data, expr); //if(res==expr) expr = subDynamic(data,expr); //else expr=res; data.mode=DYNAMIC; return expr; } // Sharp if((expr=sharp(data))!=null) { data.mode=DYNAMIC; return expr; } // JSON if((expr=json(data,JSON_ARRAY,'[',']'))!=null) { data.mode=DYNAMIC; return expr; } if((expr=json(data,JSON_STRUCT,'{','}'))!=null) { data.mode=DYNAMIC; return expr; } // else Error throw new TemplateException(data.cfml,"Syntax Error, Invalid Construct"); } /*private Expression variable(Data data) throws TemplateException { Expression expr=null; // Dynamic if((expr=dynamic(data))!=null) { expr = subDynamic(data,expr); data.mode=DYNAMIC; return expr; } return null; }*/ /** * Transfomiert einen lierale Zeichenkette. * <br /> * EBNF:<br /> * <code>("'" {"##"|"''"|"#" impOp "#"| ?-"#"-"'" } "'") | (""" {"##"|""""|"#" impOp "#"| ?-"#"-""" } """);</code> * @param data * @return CFXD Element * @throws TemplateException */ protected Expression string(ExprData data) throws TemplateException { // check starting character for a string literal if(!data.cfml.isCurrent('"')&& !data.cfml.isCurrent('\'')) return null; Position line = data.cfml.getPosition(); // Init Parameter char quoter = data.cfml.getCurrentLower(); StringBuffer str=new StringBuffer(); Expression expr=null; while(data.cfml.hasNext()) { data.cfml.next(); // check sharp if(data.cfml.isCurrent('#')) { // Ecaped sharp if(data.cfml.isNext('#')){ data.cfml.next(); str.append('#'); } // get Content of sharp else { data.cfml.next(); comments(data); Expression inner=assignOp(data); comments(data); if (!data.cfml.isCurrent('#')) throw new TemplateException(data.cfml,"Invalid Syntax Closing [#] not found"); ExprString exprStr=null; if(str.length()!=0) { exprStr=new LitString(str.toString(),line,data.cfml.getPosition()); if(expr!=null){ expr = OpString.toExprString(expr, exprStr); } else expr=exprStr; str=new StringBuffer(); } if(expr==null) { expr=inner; } else { expr = OpString.toExprString(expr, inner); } } } // check quoter else if(data.cfml.isCurrent(quoter)) { // Ecaped sharp if(data.cfml.isNext(quoter)){ data.cfml.next(); str.append(quoter); } // finsish else { break; } } // all other character else { str.append(data.cfml.getCurrent()); } } if(!data.cfml.forwardIfCurrent(quoter)) throw new TemplateException(data.cfml,"Invalid Syntax Closing ["+quoter+"] not found"); if(expr==null) expr=new LitString(str.toString(),line,data.cfml.getPosition()); else if(str.length()!=0) { expr = OpString.toExprString(expr, new LitString(str.toString(),line,data.cfml.getPosition())); } comments(data); if(expr instanceof Variable) { Variable var=(Variable) expr; var.setFromHash(true); } return expr; } /** * Transfomiert einen numerische Wert. * Die L¦nge des numerischen Wertes interessiert nicht zu ᅵbersetzungszeit, * ein "Overflow" fhrt zu einem Laufzeitfehler. * Da die zu erstellende CFXD, bzw. dieser Transfomer, keine Vorwegnahme des Laufzeitsystems vornimmt. * <br /> * EBNF:<br /> * <code>["+"|"-"] digit {digit} {"." digit {digit}};</code> * @return CFXD Element * @throws TemplateException */ private LitDouble number(ExprData data) throws TemplateException { // check first character is a number literal representation if(!(data.cfml.isCurrentBetween('0','9') || data.cfml.isCurrent('.'))) return null; Position line = data.cfml.getPosition(); StringBuffer rtn=new StringBuffer(); // get digit on the left site of the dot if(data.cfml.isCurrent('.')) rtn.append('0'); else rtn.append(digit(data)); // read dot if exist if(data.cfml.forwardIfCurrent('.')) { rtn.append('.'); String rightSite=digit(data); if(rightSite.length()> 0 && data.cfml.forwardIfCurrent('e')) { Boolean expOp=null; if(data.cfml.forwardIfCurrent('+')) expOp=Boolean.TRUE; else if(data.cfml.forwardIfCurrent('-')) expOp=Boolean.FALSE; if(data.cfml.isCurrentBetween('0','9')) { if(expOp==Boolean.FALSE) rightSite+="e-"; else if(expOp==Boolean.TRUE) rightSite+="e+"; else rightSite+="e"; rightSite+=digit(data); } else { if(expOp!=null) data.cfml.previous(); data.cfml.previous(); } } // read right side of the dot if(rightSite.length()==0) rightSite="0";//throw new TemplateException(cfml, "Number can't end with [.]"); // DIFF 23 rtn.append(rightSite); } comments(data); try { return LitDouble.toExprDouble(Caster.toDoubleValue(rtn.toString()),line,data.cfml.getPosition()); } catch (CasterException e) { throw new TemplateException(data.cfml,e.getMessage()); } } /** * Liest die reinen Zahlen innerhalb des CFMLString aus und gibt diese als Zeichenkette zurck. * <br /> * EBNF:<br /> * <code>"0"|..|"9";</code> * @return digit Ausgelesene Zahlen als Zeichenkette. */ private String digit(ExprData data) { String rtn=""; while (data.cfml.isValidIndex()) { if(!data.cfml.isCurrentBetween('0','9'))break; rtn+=data.cfml.getCurrentLower(); data.cfml.next(); } return rtn; } /** * Liest den folgenden idetifier ein und prft ob dieser ein boolscher Wert ist. * Im Gegensatz zu CFMX wird auch "yes" und "no" als bolscher <wert akzeptiert, * was bei CFMX nur beim Umwandeln einer Zeichenkette zu einem boolschen Wert der Fall ist.<br /> * Wenn es sich um keinen bolschen Wert handelt wird der folgende Wert eingelesen mit seiner ganzen Hirarchie. * <br /> * EBNF:<br /> * <code>"true" | "false" | "yes" | "no" | startElement {("." identifier | "[" structElement "]" )[function] };</code> * @return CFXD Element * @throws TemplateException */ private Expression dynamic(ExprData data) throws TemplateException { // Die Implementation weicht ein wenig von der Grammatik ab, // aber nicht in der Logik sondern rein wie es umgesetzt wurde. // get First Element of the Variable Position line = data.cfml.getPosition(); Identifier id = identifier(data,false,true); if(id == null) { if (!data.cfml.forwardIfCurrent('(')) return null; comments(data); Expression expr = assignOp(data); if (!data.cfml.forwardIfCurrent(')')) throw new TemplateException( data.cfml, "Invalid Syntax Closing [)] not found"); comments(data); return expr;//subDynamic(expr); } Variable var; comments(data); // Boolean constant if(id.getString().equalsIgnoreCase("TRUE")) {// || name.equals("YES")) { comments(data); return new LitBoolean(true,line,data.cfml.getPosition()); } else if(id.getString().equalsIgnoreCase("FALSE")) {// || name.equals("NO")) { comments(data); return new LitBoolean(false,line,data.cfml.getPosition()); } else if(NullSupportHelper.full() && id.getString().equalsIgnoreCase("NULL")) { comments(data); return new Null(line,data.cfml.getPosition()); } // Extract Scope from the Variable //int c=data.cfml.getColumn(); Position l=data.cfml.getPosition(); var = startElement(data,id,line); var.setStart(l); var.setEnd(data.cfml.getPosition()); return var; } private Expression json(ExprData data,FunctionLibFunction flf, char start, char end) throws TemplateException { if(!data.cfml.forwardIfCurrent(start))return null; Position line = data.cfml.getPosition(); BIF bif=new BIF(flf.getName(),flf); bif.setArgType(flf.getArgType()); bif.setClass(flf.getClazz()); bif.setReturnType(flf.getReturnTypeAsString()); do { comments(data); if (data.cfml.isCurrent(end))break; bif.addArgument(functionArgument(data,data.settings.dotNotationUpper)); comments(data); } while (data.cfml.forwardIfCurrent(',')); comments(data); if (!data.cfml.forwardIfCurrent(end)) throw new TemplateException(data.cfml,"Invalid Syntax Closing ["+end+"] not found"); comments(data); Variable var=new Variable(line,data.cfml.getPosition()); var.addMember(bif); return var; } private Expression closure(ExprData data) throws TemplateException { if(!data.cfml.forwardIfCurrent("function",'('))return null; data.cfml.previous(); return new ClosureAsExpression((Closure) closurePart(data, "closure_"+CreateUniqueId.invoke(), Component.ACCESS_PUBLIC, "any", data.cfml.getPosition(),true)); } protected abstract Function closurePart(ExprData data, String id, int access, String rtnType, Position line,boolean closure) throws TemplateException; protected FunctionLibFunction getFLF(ExprData data,String name) { FunctionLibFunction flf=null; for (int i = 0; i < data.flibs.length; i++) { flf = data.flibs[i].getFunction(name); if (flf != null) break; } return flf; } private Expression subDynamic(ExprData data,Expression expr) throws TemplateException { String name=null; Invoker invoker=null; // Loop over nested Variables while (data.cfml.isValidIndex()) { ExprString nameProp = null,namePropUC = null; // . if (data.cfml.forwardIfCurrent('.')) { // Extract next Var String comments(data); Position line=data.cfml.getPosition(); name = identifier(data,true); if(name==null) throw new TemplateException(data.cfml, "Invalid identifier"); comments(data); nameProp=Identifier.toIdentifier(name,line,data.cfml.getPosition()); namePropUC=Identifier.toIdentifier(name,data.settings.dotNotationUpper?Identifier.CASE_UPPER:Identifier.CASE_ORIGNAL,line,data.cfml.getPosition()); } // [] else if (data.cfml.forwardIfCurrent('[')) { // get Next Var nameProp = structElement(data); namePropUC=nameProp; // Valid Syntax ??? if (!data.cfml.forwardIfCurrent(']')) throw new TemplateException( data.cfml, "Invalid Syntax Closing []] not found"); } /* / : else if (data.cfml.forwardIfCurrent(':')) { // Extract next Var String comments(data); int line=data.cfml.getLine(); name = identifier(true,true); if(name==null) throw new TemplateException(cfml, "Invalid identifier"); comments(data); nameProp=LitString.toExprString(name,line); }*/ // finish else { break; } comments(data); if(expr instanceof Invoker) { invoker=(Invoker) expr; } else { invoker=new ExpressionInvoker(expr); expr=invoker; } // Method if (data.cfml.isCurrent('(')) { if(nameProp==null && name!=null)nameProp=Identifier.toIdentifier(name, Identifier.CASE_ORIGNAL,null,null);// properly this is never used invoker.addMember(getFunctionMember(data,nameProp, false)); } // property else invoker.addMember(new DataMember(namePropUC)); } return expr; } private Expression newOp(ExprData data,Expression expr) throws TemplateException { if(!(expr instanceof Variable)) return expr; Variable var=(Variable) expr; Member m= var.getFirstMember(); if(!(m instanceof DataMember)) return expr; ExprString n = ((DataMember)m).getName(); if(!(n instanceof LitString)) return expr; LitString ls=(LitString) n; if(!"new".equalsIgnoreCase(ls.getString())) return expr; int start=data.cfml.getPos(); String name=null; // first identifier name = identifier(data,true); ExprString exprName; if(name!=null) { StringBuilder fullName=new StringBuilder(); fullName.append(name); // Loop over addional identifier while (data.cfml.isValidIndex()) { if (data.cfml.forwardIfCurrent('.')) { comments(data); name = identifier(data,true); if(name==null) { data.cfml.setPos(start); return expr;//throw new TemplateException(data.cfml,"invalid Component declaration "); } fullName.append('.'); fullName.append(name); comments(data); } else break; } exprName=LitString.toExprString(fullName.toString()); } else { Expression str=string(data); if(str!=null){ exprName=CastString.toExprString(str); } else { data.cfml.setPos(start); return expr; } } comments(data); if (data.cfml.isCurrent('(')) { FunctionMember func = getFunctionMember(data,Identifier.toIdentifier("_createComponent",Identifier.CASE_ORIGNAL,null,null), true); func.addArgument(new Argument(exprName,"string")); Variable v=new Variable(expr.getStart(),expr.getEnd()); v.addMember(func); comments(data); return v; } data.cfml.setPos(start); return expr;//throw new TemplateException(data.cfml,"invalid Component declaration "); } /** * Extrahiert den Start Element einer Variale, * dies ist entweder eine Funktion, eine Scope Definition oder eine undefinierte Variable. * <br /> * EBNF:<br /> * <code>identifier "(" functionArg ")" | scope | identifier;</code> * @param name Einstiegsname * @return CFXD Element * @throws TemplateException */ private Variable startElement(ExprData data,Identifier name, Position line) throws TemplateException { // check function if (data.cfml.isCurrent('(')) { FunctionMember func = getFunctionMember(data,name, true); Variable var=new Variable(line,data.cfml.getPosition()); var.addMember(func); comments(data); return var; } //check scope Variable var = scope(data,name,line); if(var!=null) return var; // undefined variable var=new Variable(line,data.cfml.getPosition()); var.addMember(new DataMember(name)); comments(data); return var; } /** * Liest einen CFML Scope aus, * falls der folgende identifier keinem Scope entspricht, * gibt die Variable null zurck. * <br /> * EBNF:<br /> * <code>"variable" | "cgi" | "url" | "form" | "session" | "application" | "arguments" | "cookie" | " client";</code> * @param id String identifier, * wird aus Optimierungszwechen nicht innerhalb dieser Funktion ausgelsen. * @return CFXD Variable Element oder null * @throws TemplateException */ private Variable scope(ExprData data,Identifier id, Position line) throws TemplateException { String idStr=id.getUpper(); if(data.ignoreScopes)return null; if (idStr.equals("CGI")) return new Variable(Scope.SCOPE_CGI,line,data.cfml.getPosition()); else if (idStr.equals("ARGUMENTS")) return new Variable(Scope.SCOPE_ARGUMENTS,line,data.cfml.getPosition()); else if (idStr.equals("REQUEST")) return new Variable(Scope.SCOPE_REQUEST,line,data.cfml.getPosition()); else if (idStr.equals("SESSION")) return new Variable(Scope.SCOPE_SESSION,line,data.cfml.getPosition()); else if (idStr.equals("APPLICATION")) return new Variable(Scope.SCOPE_APPLICATION,line,data.cfml.getPosition()); else if (idStr.equals("VARIABLES")) return new Variable(Scope.SCOPE_VARIABLES,line,data.cfml.getPosition()); else if (idStr.equals("FORM")) return new Variable(Scope.SCOPE_FORM,line,data.cfml.getPosition()); else if (idStr.equals("URL")) return new Variable(Scope.SCOPE_URL,line,data.cfml.getPosition()); else if (idStr.equals("SERVER")) return new Variable(Scope.SCOPE_SERVER,line,data.cfml.getPosition()); else if (idStr.equals("CLIENT")) return new Variable(Scope.SCOPE_CLIENT,line,data.cfml.getPosition()); else if (idStr.equals("COOKIE")) return new Variable(Scope.SCOPE_COOKIE,line,data.cfml.getPosition()); else if (idStr.equals("CLUSTER")) return new Variable(Scope.SCOPE_CLUSTER,line,data.cfml.getPosition()); else if (idStr.equals("LOCAL")) return new Variable(Scope.SCOPE_LOCAL,line,data.cfml.getPosition()); else if (idStr.equals("VAR")) { Identifier _id = identifier(data,false,true); if(_id!=null){ comments(data); Variable local = new Variable(ScopeSupport.SCOPE_VAR,line,data.cfml.getPosition()); if(!"LOCAL".equalsIgnoreCase(_id.getString()))local.addMember(new DataMember(_id)); else { local.ignoredFirstMember(true); } return local; } } return null; } /** * Liest einen Identifier aus und gibt diesen als String zurck. * <br /> * EBNF:<br /> * <code>(letter | "_") {letter | "_"|digit};</code> * @param firstCanBeNumber * @param upper * @return Identifier. */ protected Identifier identifier(ExprData data,boolean firstCanBeNumber,boolean upper) { Position start = data.cfml.getPosition(); if(!data.cfml.isCurrentLetter() && !data.cfml.isCurrentSpecial() ) { if(!firstCanBeNumber) return null; else if(!data.cfml.isCurrentBetween('0','9'))return null; } do { data.cfml.next(); if(!(data.cfml.isCurrentLetter() || data.cfml.isCurrentBetween('0','9') || data.cfml.isCurrentSpecial())) { break; } } while (data.cfml.isValidIndex()); return Identifier.toIdentifier(data.cfml.substring(start.pos,data.cfml.getPos()-start.pos), upper && data.settings.dotNotationUpper?Identifier.CASE_UPPER:Identifier.CASE_ORIGNAL, start,data.cfml.getPosition()); } protected String identifier(ExprData data,boolean firstCanBeNumber) { int start = data.cfml.getPos(); if(!data.cfml.isCurrentLetter() && !data.cfml.isCurrentSpecial() ) { if(!firstCanBeNumber) return null; else if(!data.cfml.isCurrentBetween('0','9'))return null; } do { data.cfml.next(); if(!(data.cfml.isCurrentLetter() || data.cfml.isCurrentBetween('0','9') || data.cfml.isCurrentSpecial())) { break; } } while (data.cfml.isValidIndex()); return data.cfml.substring(start,data.cfml.getPos()-start); } /** * Transfomiert ein Collection Element das in eckigen Klammern aufgerufen wird. * <br /> * EBNF:<br /> * <code>"[" impOp "]"</code> * @return CFXD Element * @throws TemplateException */ private ExprString structElement(ExprData data) throws TemplateException { comments(data); ExprString name = CastString.toExprString(assignOp(data)); if(name instanceof LitString)((LitString)name).fromBracket(true); comments(data); return name; } /** * Liest die Argumente eines Funktonsaufruf ein und prft ob die Funktion * innerhalb der FLD (Function Library Descriptor) definiert ist. * Falls sie existiert wird die Funktion gegen diese geprft und ein build-in-function CFXD Element generiert, * ansonsten ein normales funcion-call Element. * <br /> * EBNF:<br /> * <code>[impOp{"," impOp}];</code> * @param name Identifier der Funktion als Zeichenkette * @param checkLibrary Soll geprft werden ob die Funktion innerhalb der Library existiert. * @return CFXD Element * @throws TemplateException */ private FunctionMember getFunctionMember(ExprData data, final ExprString name, boolean checkLibrary) throws TemplateException { // get Function Library checkLibrary=checkLibrary && data.flibs!=null; FunctionLibFunction flf = null; if (checkLibrary) { if(!(name instanceof Literal)) throw new TemplateException(data.cfml,"syntax error"); // should never happen! for (int i = 0; i < data.flibs.length; i++) { flf = data.flibs[i].getFunction(((Literal)name).getString()); if (flf != null)break; } if (flf == null) { checkLibrary = false; } } // Element Function FunctionMember fm; if(checkLibrary) { BIF bif=new BIF(name,flf); bif.setArgType(flf.getArgType()); bif.setClass(flf.getClazz()); bif.setReturnType(flf.getReturnTypeAsString()); fm=bif; if(flf.getArgType()== FunctionLibFunction.ARG_DYNAMIC && flf.hasDefaultValues()){ ArrayList<FunctionLibFunctionArg> args = flf.getArg(); Iterator<FunctionLibFunctionArg> it = args.iterator(); FunctionLibFunctionArg arg; while(it.hasNext()){ arg=it.next(); if(arg.getDefaultValue()!=null) bif.addArgument( new NamedArgument( LitString.toExprString(arg.getName()), LitString.toExprString(arg.getDefaultValue()), arg.getTypeAsString(),false )); } } } else { fm = new UDF(name); } // Function Attributes ArrayList<FunctionLibFunctionArg> arrFuncLibAtt = null; int libLen = 0; if (checkLibrary) { arrFuncLibAtt = flf.getArg(); libLen = arrFuncLibAtt.size(); } int count = 0; do { data.cfml.next(); comments(data); // finish if (count==0 && data.cfml.isCurrent(')')) break; // too many Attributes boolean isDynamic=false; int max=-1; if(checkLibrary) { isDynamic=flf.getArgType()==FunctionLibFunction.ARG_DYNAMIC; max=flf.getArgMax(); // Dynamic if(isDynamic) { if(max!=-1 && max <= count) throw new TemplateException( data.cfml, "too many Attributes in function [" + ASMUtil.display(name) + "]"); } // Fix else { if(libLen <= count){ TemplateException te = new TemplateException( data.cfml, "too many Attributes in function call [" + ASMUtil.display(name) + "]"); UDFUtil.addFunctionDoc(te, flf); throw te; } } } //Argument arg; if (checkLibrary && !isDynamic) { // current attribues from library FunctionLibFunctionArg funcLibAtt =arrFuncLibAtt.get(count); fm.addArgument(functionArgument(data,funcLibAtt.getTypeAsString(),false)); } else { fm.addArgument(functionArgument(data,false)); } comments(data); count++; if (data.cfml.isCurrent(')')) break; } while (data.cfml.isCurrent(',')); // end with ) ?? if (!data.cfml.forwardIfCurrent(')')) throw new TemplateException( data.cfml, "Invalid Syntax Closing [)] for function [" + ASMUtil.display(name) + "] not found"); // check min attributes if (checkLibrary && flf.getArgMin() > count){ TemplateException te = new TemplateException( data.cfml, "too few attributes in function [" + ASMUtil.display(name) + "]"); if(flf.getArgType()==FunctionLibFunction.ARG_FIX) UDFUtil.addFunctionDoc(te, flf); throw te; } comments(data); // evaluator if(checkLibrary && flf.hasTteClass()){ flf.getEvaluator().evaluate((BIF) fm, flf); } return fm; } /** * Sharps (#) die innerhalb von Expressions auftauchen haben in CFML keine weitere Beteutung * und werden durch diese Methode einfach entfernt. * <br /> * Beispiel:<br /> * <code>arrayLen(#arr#)</code> und <code>arrayLen(arr)</code> sind identisch. * EBNF:<br /> * <code>"#" checker "#";</code> * @return CFXD Element * @throws TemplateException */ private Expression sharp(ExprData data) throws TemplateException { if(!data.cfml.forwardIfCurrent('#')) return null; Expression expr; comments(data); boolean old=data.allowLowerThan; data.allowLowerThan=true; expr = assignOp(data); data.allowLowerThan=old; comments(data); if (!data.cfml.forwardIfCurrent('#')) throw new TemplateException( data.cfml, "Syntax Error, Invalid Construct "+(data.cfml.length()<30?data.cfml.toString():"")); comments(data); return expr; } /** * @param data * @return parsed Element * @throws TemplateException */ private Expression simple(ExprData data,String[] breakConditions) throws TemplateException { StringBuffer sb=new StringBuffer(); Position line = data.cfml.getPosition(); outer:while(data.cfml.isValidIndex()) { for(int i=0;i<breakConditions.length;i++){ if(data.cfml.isCurrent(breakConditions[i]))break outer; } //if(data.cfml.isCurrent(' ') || data.cfml.isCurrent('>') || data.cfml.isCurrent("/>")) break; if(data.cfml.isCurrent('"') || data.cfml.isCurrent('#') || data.cfml.isCurrent('\'')) { throw new TemplateException(data.cfml,"simple attribute value can't contain ["+data.cfml.getCurrent()+"]"); } sb.append(data.cfml.getCurrent()); data.cfml.next(); } comments(data); return LitString.toExprString(sb.toString(),line,data.cfml.getPosition()); } /** * Liest alle folgenden Komentare ein. * <br /> * EBNF:<br /> * <code>{?-"\n"} "\n";</code> * @param data * @throws TemplateException */ protected void comments(ExprData data) throws TemplateException { data.cfml.removeSpace(); while(comment(data)){data.cfml.removeSpace();} } /** * Liest einen Einzeiligen Kommentar ein. * <br /> * EBNF:<br /> * <code>{?-"\n"} "\n";</code> * @return bool Wurde ein Kommentar entfernt? * @throws TemplateException */ private boolean comment(ExprData data) throws TemplateException { if(singleLineComment(data.cfml) || multiLineComment(data) || CFMLTransformer.comment(data.cfml)) return true; return false; } /** * Liest einen Mehrzeiligen Kommentar ein. * <br /> * EBNF:<br /> * <code>?-"*<!-- -->/";</code> * @return bool Wurde ein Kommentar entfernt? * @throws TemplateException */ private boolean multiLineComment(ExprData data) throws TemplateException { CFMLString cfml = data.cfml; if(!cfml.forwardIfCurrent("/*")) return false; int pos=cfml.getPos(); boolean isDocComment=cfml.isCurrent('*'); while(cfml.isValidIndex()) { if(cfml.isCurrent("*/")) break; cfml.next(); } if(!cfml.forwardIfCurrent("*/")){ cfml.setPos(pos); throw new TemplateException(cfml,"comment is not closed"); } if(isDocComment) { String comment = cfml.substring(pos-2,cfml.getPos()-pos); data.docComment=docCommentTransformer.transform(comment); } return true; } /** * Liest einen Einzeiligen Kommentar ein. * <br /> * EBNF:<br /> * <code>{?-"\n"} "\n";</code> * @return bool Wurde ein Kommentar entfernt? */ private boolean singleLineComment(CFMLString cfml) { if(!cfml.forwardIfCurrent("//")) return false; return cfml.nextLine(); } }