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 knnen, 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 Mglichen 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();
}
}