001/** 002 * 003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved. 004 * 005 * This library is free software; you can redistribute it and/or 006 * modify it under the terms of the GNU Lesser General Public 007 * License as published by the Free Software Foundation; either 008 * version 2.1 of the License, or (at your option) any later version. 009 * 010 * This library is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013 * Lesser General Public License for more details. 014 * 015 * You should have received a copy of the GNU Lesser General Public 016 * License along with this library. If not, see <http://www.gnu.org/licenses/>. 017 * 018 **/ 019package lucee.transformer.cfml.expression; 020 021import java.util.ArrayList; 022import java.util.Iterator; 023 024import lucee.runtime.Component; 025import lucee.runtime.config.NullSupportHelper; 026import lucee.runtime.exp.CasterException; 027import lucee.runtime.exp.TemplateException; 028import lucee.runtime.functions.other.CreateUniqueId; 029import lucee.runtime.op.Caster; 030import lucee.runtime.type.scope.Scope; 031import lucee.runtime.type.scope.ScopeSupport; 032import lucee.runtime.type.util.UDFUtil; 033import lucee.transformer.bytecode.BytecodeException; 034import lucee.transformer.bytecode.Literal; 035import lucee.transformer.bytecode.Page; 036import lucee.transformer.bytecode.Position; 037import lucee.transformer.bytecode.cast.CastDouble; 038import lucee.transformer.bytecode.cast.CastString; 039import lucee.transformer.bytecode.expression.ClosureAsExpression; 040import lucee.transformer.bytecode.expression.ExprBoolean; 041import lucee.transformer.bytecode.expression.ExprDouble; 042import lucee.transformer.bytecode.expression.ExprString; 043import lucee.transformer.bytecode.expression.Expression; 044import lucee.transformer.bytecode.expression.ExpressionInvoker; 045import lucee.transformer.bytecode.expression.Invoker; 046import lucee.transformer.bytecode.expression.var.Argument; 047import lucee.transformer.bytecode.expression.var.Assign; 048import lucee.transformer.bytecode.expression.var.BIF; 049import lucee.transformer.bytecode.expression.var.DataMember; 050import lucee.transformer.bytecode.expression.var.DynAssign; 051import lucee.transformer.bytecode.expression.var.FunctionMember; 052import lucee.transformer.bytecode.expression.var.Member; 053import lucee.transformer.bytecode.expression.var.NamedArgument; 054import lucee.transformer.bytecode.expression.var.UDF; 055import lucee.transformer.bytecode.expression.var.Variable; 056import lucee.transformer.bytecode.literal.Identifier; 057import lucee.transformer.bytecode.literal.LitBoolean; 058import lucee.transformer.bytecode.literal.LitDouble; 059import lucee.transformer.bytecode.literal.LitString; 060import lucee.transformer.bytecode.literal.Null; 061import lucee.transformer.bytecode.op.OPDecision; 062import lucee.transformer.bytecode.op.OPUnary; 063import lucee.transformer.bytecode.op.OpBool; 064import lucee.transformer.bytecode.op.OpContional; 065import lucee.transformer.bytecode.op.OpDouble; 066import lucee.transformer.bytecode.op.OpElvis; 067import lucee.transformer.bytecode.op.OpNegate; 068import lucee.transformer.bytecode.op.OpNegateNumber; 069import lucee.transformer.bytecode.op.OpString; 070import lucee.transformer.bytecode.op.OpVariable; 071import lucee.transformer.bytecode.statement.tag.Attribute; 072import lucee.transformer.bytecode.statement.udf.Closure; 073import lucee.transformer.bytecode.statement.udf.Function; 074import lucee.transformer.bytecode.util.ASMUtil; 075import lucee.transformer.cfml.Data; 076import lucee.transformer.cfml.TransfomerSettings; 077import lucee.transformer.cfml.evaluator.EvaluatorPool; 078import lucee.transformer.cfml.script.DocComment; 079import lucee.transformer.cfml.script.DocCommentTransformer; 080import lucee.transformer.cfml.tag.CFMLTransformer; 081import lucee.transformer.library.function.FunctionLib; 082import lucee.transformer.library.function.FunctionLibFunction; 083import lucee.transformer.library.function.FunctionLibFunctionArg; 084import lucee.transformer.library.tag.TagLib; 085import lucee.transformer.library.tag.TagLibTag; 086import lucee.transformer.library.tag.TagLibTagAttr; 087import lucee.transformer.library.tag.TagLibTagScript; 088import lucee.transformer.util.CFMLString; 089 090/** 091 * 092 * 093 Der CFMLExprTransfomer implementiert das Interface ExprTransfomer, 094 er bildet die Parser Grammatik ab, die unten definiert ist. 095 Er erhaelt als Eingabe CFML Code, als String oder CFMLString, 096 der einen CFML Expression erhaelt und liefert ein CFXD Element zurueck, 097 das diesen Ausdruck abbildet. 098 Mithilfe der FunctionLibs, kann er Funktionsaufrufe, 099 die Teil eines Ausdruck sein koennen, erkennen und validieren. 100 Dies geschieht innerhalb der Methode function. 101 Falls ein Funktionsaufruf, einer Funktion innerhalb einer FunctionLib entspricht, 102 werden diese gegeneinander verglichen und der Aufruf wird als Build-In-Funktion uebernommen, 103 andernfalls wird der Funktionsaufruf als User-Defined-Funktion interpretiert. 104 Die Klasse Cast, Operator und ElementFactory (siehe 3.2) helfen ihm beim erstellen des Ausgabedokument CFXD. 105 106 * <pre> 107 * Parser Grammatik EBNF (Extended Backus-Naur Form) 108 109 transform = spaces impOp; 110 impOp = eqvOp {"imp" spaces eqvOp}; 111 eqvOp = xorOp {"eqv" spaces xorOp}; 112 xorOp = orOp {"xor" spaces orOp}; 113 orOp = andOp {("or" | "||") spaces andOp}; 114 (* "||" Existiert in CFMX nicht *) 115 andOp = notOp {("and" | "&&") spaces notOp}; 116 (* "&&" Existiert in CFMX nicht *) 117 notOp = [("not"|"!") spaces] decsionOp; 118 (* "!" Existiert in CFMX nicht *) 119 decsionOp = concatOp {("neq"|"eq"|"gte"|"gt"|"lte"|"lt"|"ct"| 120 "contains"|"nct"|"does not contain") spaces concatOp}; 121 (* "ct"=conatains und "nct"=does not contain; Existiert in CFMX nicht *) 122 concatOp = plusMinusOp {"&" spaces plusMinusOp}; 123 plusMinusOp = modOp {("-"|"+") spaces modOp}; 124 modOp = divMultiOp {("mod" | "%") spaces divMultiOp}; 125 (* modulus operator , "%" Existiert in CFMX nicht *) 126 divMultiOp = expoOp {("*"|"/") spaces expoOp}; 127 expoOp = clip {("exp"|"^") spaces clip}; 128 (*exponent operator, " exp " Existiert in CFMX nicht *) 129 clip = ("(" spaces impOp ")" spaces) | checker; 130 checker = string | number | dynamic | sharp; 131 string = ("'" {"##"|"''"|"#" impOp "#"| ?-"#"-"'" } "'") | 132 (""" {"##"|""""|"#" impOp "#"| ?-"#"-""" } """); 133 number = ["+"|"-"] digit {digit} {"." digit {digit}}; 134 digit = "0"|..|"9"; 135 dynamic = "true" | "false" | "yes" | "no" | startElement 136 {("." identifier | "[" structElement "]")[function] }; 137 startElement = identifier "(" functionArg ")" | scope | identifier; 138 scope = "variable" | "cgi" | "url" | "form" | "session" | "application" | 139 "arguments" | "cookie" | "client "; 140 identifier = (letter | "_") {letter | "_"|digit}; 141 structElement = "[" impOp "]"; 142 functionArg = [impOp{"," impOp}]; 143 sharp = "#" checker "#"; 144 spaces = {space}; 145 space = "\s"|"\t"|"\f"|"\t"|"\n"; 146 letter = "a"|..|"z"|"A"|..|"Z"; 147 148{"x"}= 0 bis n mal "x" 149["x"]= 0 bis 1 mal "x" 150("x" | "y")"z" = "xz" oder "yz" 151 152</pre> 153 * 154 */ 155public abstract class AbstrCFMLExprTransformer { 156 157 private static final short STATIC=0; 158 private static final short DYNAMIC=1; 159 private static FunctionLibFunction JSON_ARRAY = null; 160 protected static FunctionLibFunction JSON_STRUCT = null; 161 162 public static final short CTX_OTHER = TagLibTagScript.CTX_OTHER; 163 public static final short CTX_NONE = TagLibTagScript.CTX_NONE; 164 public static final short CTX_IF = TagLibTagScript.CTX_IF; 165 public static final short CTX_ELSE_IF = TagLibTagScript.CTX_ELSE_IF; 166 public static final short CTX_ELSE = TagLibTagScript.CTX_ELSE; 167 public static final short CTX_FOR = TagLibTagScript.CTX_FOR; 168 public static final short CTX_WHILE = TagLibTagScript.CTX_WHILE; 169 public static final short CTX_DO_WHILE = TagLibTagScript.CTX_DO_WHILE; 170 public static final short CTX_CFC = TagLibTagScript.CTX_CFC; 171 public static final short CTX_INTERFACE = TagLibTagScript.CTX_INTERFACE; 172 public static final short CTX_FUNCTION =TagLibTagScript.CTX_FUNCTION; 173 public static final short CTX_BLOCK = TagLibTagScript.CTX_BLOCK; 174 public static final short CTX_FINALLY = TagLibTagScript.CTX_FINALLY; 175 public static final short CTX_SWITCH = TagLibTagScript.CTX_SWITCH; 176 public static final short CTX_TRY = TagLibTagScript.CTX_TRY; 177 public static final short CTX_CATCH = TagLibTagScript.CTX_CATCH; 178 public static final short CTX_TRANSACTION = TagLibTagScript.CTX_TRANSACTION; 179 public static final short CTX_THREAD = TagLibTagScript.CTX_THREAD; 180 public static final short CTX_SAVECONTENT = TagLibTagScript.CTX_SAVECONTENT; 181 public static final short CTX_LOCK = TagLibTagScript.CTX_LOCK; 182 public static final short CTX_LOOP = TagLibTagScript.CTX_LOOP; 183 public static final short CTX_QUERY = TagLibTagScript.CTX_QUERY; 184 public static final short CTX_ZIP = TagLibTagScript.CTX_ZIP; 185 186 187 private DocCommentTransformer docCommentTransformer= new DocCommentTransformer(); 188 189 190 protected short ATTR_TYPE_NONE=TagLibTagAttr.SCRIPT_SUPPORT_NONE; 191 protected short ATTR_TYPE_OPTIONAL=TagLibTagAttr.SCRIPT_SUPPORT_OPTIONAL; 192 protected short ATTR_TYPE_REQUIRED=TagLibTagAttr.SCRIPT_SUPPORT_REQUIRED; 193 194 protected static final Expression NULL = LitString.toExprString("NULL"); 195 protected static final Attribute ANY = new Attribute(false,"type",LitString.toExprString("any"),"string"); 196 197 198 protected static EndCondition SEMI_BLOCK=new EndCondition() { 199 public boolean isEnd(ExprData data) { 200 return data.cfml.isCurrent('{') || data.cfml.isCurrent(';'); 201 } 202 }; 203 protected static EndCondition SEMI=new EndCondition() { 204 public boolean isEnd(ExprData data) { 205 return data.cfml.isCurrent(';'); 206 } 207 }; 208 protected static EndCondition COMMA_ENDBRACKED=new EndCondition() { 209 public boolean isEnd(ExprData data) { 210 return data.cfml.isCurrent(',') || data.cfml.isCurrent(')'); 211 } 212 }; 213 214 public static interface EndCondition { 215 public boolean isEnd(ExprData data); 216 } 217 218 /*private short mode=0; 219 protected CFMLString cfml; 220 protected FunctionLib[] fld; 221 private boolean ignoreScopes=false; 222 private boolean allowLowerThan;*/ 223 224 public class ExprData extends Data { 225 226 private short mode=0; 227 private boolean ignoreScopes=false; 228 private boolean allowLowerThan; 229 public boolean insideFunction; 230 public String tagName; 231 public boolean isCFC; 232 public boolean isInterface; 233 public short context=CTX_NONE; 234 public DocComment docComment; 235 236 public ExprData(Page page, EvaluatorPool ep, CFMLString cfml, TagLib[][] tlibs,FunctionLib[] flibs, TransfomerSettings settings,boolean allowLowerThan,TagLibTag[] scriptTags) { 237 super(page,cfml,ep,settings,tlibs,flibs,scriptTags); 238 this.allowLowerThan=allowLowerThan; 239 } 240 } 241 242 protected Expression transformAsString(ExprData data,String[] breakConditions) throws TemplateException { 243 Expression el=null; 244 245 // parse the houle Page String 246 comments(data); 247 248 // String 249 if((el=string(data))!=null) { 250 data.mode=STATIC; 251 return el; 252 } 253 // Sharp 254 if((el=sharp(data))!=null) { 255 data.mode=DYNAMIC; 256 return el; 257 } 258 // Simple 259 return simple(data,breakConditions); 260 } 261 262 263 264 /** 265 * Initialmethode, wird aufgerufen um den internen Zustand des Objektes zu setzten. 266 * @param fld Function Libraries zum validieren der Funktionen 267 * @param cfml CFML Code der transfomiert werden soll. 268 */ 269 protected ExprData init(Page page,EvaluatorPool ep,TagLib[][] tld, FunctionLib[] fld,TagLibTag[] scriptTags, CFMLString cfml, TransfomerSettings settings, boolean allowLowerThan) { 270 ExprData data = new ExprData(page,ep,cfml,tld,fld,settings,allowLowerThan,scriptTags); 271 if(JSON_ARRAY==null)JSON_ARRAY=getFLF(data,"_literalArray"); 272 if(JSON_STRUCT==null)JSON_STRUCT=getFLF(data,"_literalStruct"); 273 return data; 274 //this.allowLowerThan=allowLowerThan; 275 //this.fld = fld; 276 //this.cfml = cfml; 277 } 278 279 /** 280 * Startpunkt zum transfomieren einer Expression, ohne dass das Objekt neu initialisiert wird, 281 * dient vererbten Objekten als Einstiegspunkt. 282 * @return Element 283 * @throws TemplateException 284 */ 285 protected Expression expression(ExprData data) throws TemplateException { 286 return assignOp(data); 287 } 288 289 /** 290 * Liest einen gelableten Funktionsparamter ein 291 * <br /> 292 * EBNF:<br /> 293 * <code>assignOp [":" spaces assignOp];</code> 294 * @return CFXD Element 295 * @throws TemplateException 296 */ 297 private Argument functionArgument(ExprData data, boolean varKeyUpperCase) throws TemplateException { 298 return functionArgument(data,null,varKeyUpperCase); 299 } 300 301 private Argument functionArgument(ExprData data,String type, boolean varKeyUpperCase) throws TemplateException { 302 Expression expr = assignOp(data); 303 try{ 304 if (data.cfml.forwardIfCurrent(":")) { 305 comments(data); 306 return new NamedArgument(expr,assignOp(data),type,varKeyUpperCase); 307 } 308 else if(expr instanceof DynAssign){ 309 DynAssign da=(DynAssign) expr; 310 return new NamedArgument(da.getName(),da.getValue(),type,varKeyUpperCase); 311 } 312 else if(expr instanceof Assign && !(expr instanceof OpVariable)){ 313 Assign a=(Assign) expr; 314 return new NamedArgument(a.getVariable(),a.getValue(),type,varKeyUpperCase); 315 } 316 } 317 catch(BytecodeException be) { 318 throw new TemplateException(data.cfml,be.getMessage()); 319 } 320 return new Argument(expr,type); 321 } 322 323 324 325 326 /** 327 * Transfomiert Zuweisungs Operation. 328 * <br /> 329 * EBNF:<br /> 330 * <code>eqvOp ["=" spaces assignOp];</code> 331 * @return CFXD Element 332 * @throws TemplateException 333 */ 334 protected Expression assignOp(ExprData data) throws TemplateException { 335 336 Expression expr = conditionalOp(data); 337 if (data.cfml.forwardIfCurrent('=')) { 338 339 comments(data); 340 if(data.mode==STATIC) expr=new DynAssign(expr,assignOp(data)); 341 else { 342 if(expr instanceof Variable) { 343 Expression value = assignOp(data); 344 expr=new Assign((Variable)expr,value,data.cfml.getPosition()); 345 } 346 else if(expr instanceof Null) { 347 Variable var = ((Null)expr).toVariable(); 348 Expression value = assignOp(data); 349 expr=new Assign(var,value,data.cfml.getPosition()); 350 } 351 else 352 throw new TemplateException(data.cfml,"invalid assignment left-hand side ("+expr.getClass().getName()+")"); 353 } 354 } 355 return expr; 356 } 357 358 private Expression conditionalOp(ExprData data) throws TemplateException { 359 360 Expression expr = impOp(data); 361 if (data.cfml.forwardIfCurrent('?')) { 362 comments(data); 363 // Elvis 364 if(data.cfml.forwardIfCurrent(':')) { 365 366 comments(data); 367 Expression right = assignOp(data); 368 369 if (expr instanceof ExprBoolean) 370 return expr; 371 372 if(!(expr instanceof Variable) ) 373 throw new TemplateException(data.cfml,"left operant of the Elvis operator has to be a variable or a function call"); 374 375 return OpElvis.toExpr((Variable)expr, right); 376 } 377 378 Expression left = assignOp(data); 379 comments(data); 380 if(!data.cfml.forwardIfCurrent(':'))throw new TemplateException("invalid conditional operator"); 381 comments(data); 382 Expression right = assignOp(data); 383 384 expr=OpContional.toExpr(expr, left, right); 385 } 386 return expr; 387 } 388 389 /** 390 * Transfomiert eine Implication (imp) Operation. 391 * <br /> 392 * EBNF:<br /> 393 * <code>eqvOp {"imp" spaces eqvOp};</code> 394 * @return CFXD Element 395 * @throws TemplateException 396 */ 397 private Expression impOp(ExprData data) throws TemplateException { 398 Expression expr = eqvOp(data); 399 while(data.cfml.forwardIfCurrentAndNoWordAfter("imp")) { 400 comments(data); 401 expr=OpBool.toExprBoolean(expr, eqvOp(data), OpBool.IMP); 402 } 403 return expr; 404 } 405 406 /** 407 * Transfomiert eine Equivalence (eqv) Operation. 408 * <br /> 409 * EBNF:<br /> 410 * <code>xorOp {"eqv" spaces xorOp};</code> 411 * @return CFXD Element 412 * @throws TemplateException 413 */ 414 private Expression eqvOp(ExprData data) throws TemplateException { 415 Expression expr = xorOp(data); 416 while(data.cfml.forwardIfCurrentAndNoWordAfter("eqv")) { 417 comments(data); 418 expr=OpBool.toExprBoolean(expr, xorOp(data), OpBool.EQV); 419 } 420 return expr; 421 } 422 423 /** 424 * Transfomiert eine Xor (xor) Operation. 425 * <br /> 426 * EBNF:<br /> 427 * <code>orOp {"xor" spaces orOp};</code> 428 * @return CFXD Element 429 * @throws TemplateException 430 */ 431 private Expression xorOp(ExprData data) throws TemplateException { 432 Expression expr = orOp(data); 433 while(data.cfml.forwardIfCurrentAndNoWordAfter("xor")) { 434 comments(data); 435 expr=OpBool.toExprBoolean(expr, orOp(data), OpBool.XOR); 436 } 437 return expr; 438 } 439 440 /** 441 * Transfomiert eine Or (or) Operation. Im Gegensatz zu CFMX , 442 * werden "||" Zeichen auch als Or Operatoren anerkannt. 443 * <br /> 444 * EBNF:<br /> 445 * <code>andOp {("or" | "||") spaces andOp}; (* "||" Existiert in CFMX nicht *)</code> 446 * @return CFXD Element 447 * @throws TemplateException 448 */ 449 private Expression orOp(ExprData data) throws TemplateException { 450 Expression expr = andOp(data); 451 452 while(data.cfml.forwardIfCurrent("||") || data.cfml.forwardIfCurrentAndNoWordAfter("or")) { 453 comments(data); 454 expr=OpBool.toExprBoolean(expr, andOp(data), OpBool.OR); 455 } 456 return expr; 457 } 458 459 /** 460 * Transfomiert eine And (and) Operation. Im Gegensatz zu CFMX , 461 * werden "&&" Zeichen auch als And Operatoren anerkannt. 462 * <br /> 463 * EBNF:<br /> 464 * <code>notOp {("and" | "&&") spaces notOp}; (* "&&" Existiert in CFMX nicht *)</code> 465 * @return CFXD Element 466 * @throws TemplateException 467 */ 468 private Expression andOp(ExprData data) throws TemplateException { 469 Expression expr = notOp(data); 470 471 while(data.cfml.forwardIfCurrent("&&") || data.cfml.forwardIfCurrentAndNoWordAfter("and")) { 472 comments(data); 473 expr=OpBool.toExprBoolean(expr, notOp(data), OpBool.AND); 474 } 475 return expr; 476 } 477 478 /** 479 * Transfomiert eine Not (not) Operation. Im Gegensatz zu CFMX , 480 * wird das "!" Zeichen auch als Not Operator anerkannt. 481 * <br /> 482 * EBNF:<br /> 483 * <code>[("not"|"!") spaces] decsionOp; (* "!" Existiert in CFMX nicht *)</code> 484 * @return CFXD Element 485 * @throws TemplateException 486 */ 487 private Expression notOp(ExprData data) throws TemplateException { 488 // And Operation 489 Position line = data.cfml.getPosition(); 490 if (data.cfml.isCurrent('!') && !data.cfml.isCurrent("!=")) { 491 data.cfml.next(); 492 comments(data); 493 return OpNegate.toExprBoolean(notOp(data),line,data.cfml.getPosition()); 494 } 495 else if (data.cfml.forwardIfCurrentAndNoWordAfter("not")) { 496 comments(data); 497 return OpNegate.toExprBoolean(notOp(data),line,data.cfml.getPosition()); 498 } 499 return decsionOp(data); 500 } 501 502 /** 503 * <font f>Transfomiert eine Vergleichs Operation. 504 * <br /> 505 * EBNF:<br /> 506 * <code>concatOp {("neq"|"eq"|"gte"|"gt"|"lte"|"lt"|"ct"| 507 "contains"|"nct"|"does not contain") spaces concatOp}; 508 (* "ct"=conatains und "nct"=does not contain; Existiert in CFMX nicht *)</code> 509 * @return CFXD Element 510 * @throws TemplateException 511 */ 512 private Expression decsionOp(ExprData data) throws TemplateException { 513 514 Expression expr = concatOp(data); 515 boolean hasChanged=false; 516 // ct, contains 517 do { 518 hasChanged=false; 519 if(data.cfml.isCurrent('c')) { 520 if (data.cfml.forwardIfCurrent("ct",false,true)) {expr = decisionOpCreate(data,OPDecision.CT,expr);hasChanged=true;} 521 else if (data.cfml.forwardIfCurrent("contains",false,true)){ expr = decisionOpCreate(data,OPDecision.CT,expr);hasChanged=true;} 522 } 523 // does not contain 524 else if (data.cfml.forwardIfCurrent("does","not","contain",false,true)){ expr = decisionOpCreate(data,OPDecision.NCT,expr); hasChanged=true;} 525 526 // equal, eq 527 else if (data.cfml.isCurrent("eq") && !data.cfml.isCurrent("eqv")) { 528 int plus=2; 529 data.cfml.setPos(data.cfml.getPos()+2); 530 if(data.cfml.forwardIfCurrent("ual"))plus=5; 531 532 if(data.cfml.isCurrentVariableCharacter()) { 533 data.cfml.setPos(data.cfml.getPos()-plus); 534 } 535 else { 536 expr = decisionOpCreate(data,OPDecision.EQ,expr); 537 hasChanged=true; 538 } 539 540 } 541 // == 542 else if (data.cfml.forwardIfCurrent("==")) { 543 if(data.cfml.forwardIfCurrent('=')) expr = decisionOpCreate(data,OPDecision.EEQ,expr); 544 else expr = decisionOpCreate(data,OPDecision.EQ,expr); 545 hasChanged=true; 546 } 547 // != 548 else if (data.cfml.forwardIfCurrent("!=")) { 549 if(data.cfml.forwardIfCurrent('=')) expr = decisionOpCreate(data,OPDecision.NEEQ,expr); 550 else expr = decisionOpCreate(data,OPDecision.NEQ,expr); 551 hasChanged=true; 552 } 553 // <=/</<> 554 else if (data.cfml.isCurrent('<')) { 555 hasChanged=true; 556 if(data.cfml.isNext('=')) { 557 data.cfml.next();data.cfml.next(); 558 expr = decisionOpCreate(data,OPDecision.LTE,expr); 559 } 560 else if(data.cfml.isNext('>')) { 561 data.cfml.next();data.cfml.next(); 562 expr = decisionOpCreate(data,OPDecision.NEQ,expr); 563 } 564 else if(data.cfml.isNext('/')) { 565 hasChanged=false; 566 } 567 else { 568 data.cfml.next(); 569 expr = decisionOpCreate(data,OPDecision.LT,expr); 570 } 571 } 572 // >=/> 573 else if (data.allowLowerThan && data.cfml.forwardIfCurrent('>')) { 574 if(data.cfml.forwardIfCurrent('=')) expr = decisionOpCreate(data,OPDecision.GTE,expr); 575 else expr = decisionOpCreate(data,OPDecision.GT,expr); 576 hasChanged=true; 577 } 578 579 // gt, gte, greater than or equal to, greater than 580 else if (data.cfml.isCurrent('g')) { 581 if (data.cfml.forwardIfCurrent("gt")) { 582 if(data.cfml.forwardIfCurrentAndNoWordAfter("e")) { 583 if(data.cfml.isCurrentVariableCharacter()) { 584 data.cfml.setPos(data.cfml.getPos()-3); 585 } 586 else { 587 expr = decisionOpCreate(data,OPDecision.GTE,expr); 588 hasChanged=true; 589 } 590 } 591 else { 592 if(data.cfml.isCurrentVariableCharacter()) { 593 data.cfml.setPos(data.cfml.getPos()-2); 594 } 595 else { 596 expr = decisionOpCreate(data,OPDecision.GT,expr); 597 hasChanged=true; 598 } 599 } 600 } 601 else if (data.cfml.forwardIfCurrent("greater", "than",false,true)) { 602 if(data.cfml.forwardIfCurrent("or","equal", "to",true,true)) expr = decisionOpCreate(data,OPDecision.GTE,expr); 603 else expr = decisionOpCreate(data,OPDecision.GT,expr); 604 hasChanged=true; 605 } 606 else if (data.cfml.forwardIfCurrent("ge",false,true)) { 607 expr = decisionOpCreate(data,OPDecision.GTE,expr); 608 hasChanged=true; 609 } 610 } 611 612 // is, is not 613 else if (data.cfml.forwardIfCurrent("is",false,true)) { 614 if(data.cfml.forwardIfCurrent("not",true,true)) expr = decisionOpCreate(data,OPDecision.NEQ,expr); 615 else expr = decisionOpCreate(data,OPDecision.EQ,expr); 616 hasChanged=true; 617 } 618 619 // lt, lte, less than, less than or equal to 620 else if (data.cfml.isCurrent('l')) { 621 if (data.cfml.forwardIfCurrent("lt")) { 622 if(data.cfml.forwardIfCurrentAndNoWordAfter("e")) { 623 if(data.cfml.isCurrentVariableCharacter()) { 624 data.cfml.setPos(data.cfml.getPos()-3); 625 } 626 else { 627 expr = decisionOpCreate(data,OPDecision.LTE,expr); 628 hasChanged=true; 629 } 630 } 631 else { 632 if(data.cfml.isCurrentVariableCharacter()) { 633 data.cfml.setPos(data.cfml.getPos()-2); 634 } 635 else { 636 expr = decisionOpCreate(data,OPDecision.LT,expr); 637 hasChanged=true; 638 } 639 } 640 } 641 else if (data.cfml.forwardIfCurrent("less","than",false,true)) { 642 if(data.cfml.forwardIfCurrent("or", "equal", "to",true,true)) expr = decisionOpCreate(data,OPDecision.LTE,expr); 643 else expr = decisionOpCreate(data,OPDecision.LT,expr); 644 hasChanged=true; 645 } 646 else if (data.cfml.forwardIfCurrent("le",false,true)) { 647 expr = decisionOpCreate(data,OPDecision.LTE,expr); 648 hasChanged=true; 649 } 650 } 651 652 // neq, not equal, nct 653 else if (data.cfml.isCurrent('n')) { 654 // Not Equal 655 if (data.cfml.forwardIfCurrent("neq",false,true)){ expr = decisionOpCreate(data,OPDecision.NEQ,expr); hasChanged=true;} 656 // Not Equal (Alias) 657 else if (data.cfml.forwardIfCurrent("not","equal",false,true)){ expr = decisionOpCreate(data,OPDecision.NEQ,expr);hasChanged=true; } 658 // nct 659 else if (data.cfml.forwardIfCurrent("nct",false,true)){ expr = decisionOpCreate(data,OPDecision.NCT,expr); hasChanged=true;} 660 } 661 662 } 663 while(hasChanged); 664 return expr; 665 } 666 private Expression decisionOpCreate(ExprData data,int operation, Expression left) throws TemplateException { 667 comments(data); 668 return OPDecision.toExprBoolean(left, concatOp(data), operation); 669 } 670 671 /** 672 * Transfomiert eine Konkatinations-Operator (&) Operation. Im Gegensatz zu CFMX , 673 * wird das "!" Zeichen auch als Not Operator anerkannt. 674 * <br /> 675 * EBNF:<br /> 676 * <code>plusMinusOp {"&" spaces concatOp};</code> 677 * @return CFXD Element 678 * @throws TemplateException 679 */ 680 private Expression concatOp(ExprData data) throws TemplateException { 681 Expression expr = plusMinusOp(data); 682 683 while(data.cfml.isCurrent('&') && !data.cfml.isCurrent("&&")) { 684 data.cfml.next(); 685 686 // &= 687 if (data.cfml.isCurrent('=') && expr instanceof Variable) { 688 data.cfml.next(); 689 comments(data); 690 Expression value = assignOp(data); 691 692 expr = new OPUnary((Variable)expr,value,OPUnary.PRE,OPUnary.CONCAT,expr.getStart(),data.cfml.getPosition()); 693 694 695 //ExprString res = OpString.toExprString(expr, right); 696 //expr=new OpVariable((Variable)expr,res,data.cfml.getPosition()); 697 } 698 else { 699 comments(data); 700 expr=OpString.toExprString(expr, plusMinusOp(data)); 701 } 702 703 } 704 return expr; 705 } 706 707 /** 708 * Transfomiert die mathematischen Operatoren Plus und Minus (1,-). 709 * <br /> 710 * EBNF:<br /> 711 * <code>modOp [("-"|"+") spaces plusMinusOp];</code> 712 * @return CFXD Element 713 * @throws TemplateException 714 */ 715 private Expression plusMinusOp(ExprData data) throws TemplateException { 716 Expression expr = modOp(data); 717 718 while(!data.cfml.isLast()) { 719 720 // Plus Operation 721 if (data.cfml.forwardIfCurrent('+')) expr=_plusMinusOp(data,expr,OpDouble.PLUS); 722 // Minus Operation 723 else if (data.cfml.forwardIfCurrent('-')) expr=_plusMinusOp(data,expr,OpDouble.MINUS); 724 else break; 725 } 726 return expr; 727 } 728 729 730 731 private Expression _plusMinusOp(ExprData data,Expression expr,int opr) throws TemplateException { 732 // += 733 // plus|Minus Assignment 734 if (data.cfml.isCurrent('=') && expr instanceof Variable) { 735 data.cfml.next(); 736 comments(data); 737 Expression value = assignOp(data); 738 //if(opr==OpDouble.MINUS) value=OpNegateNumber.toExprDouble(value, null, null); 739 740 expr = new OPUnary((Variable)expr,value,OPUnary.PRE,opr,expr.getStart(),data.cfml.getPosition()); 741 742 //ExprDouble res = OpDouble.toExprDouble(expr, right,opr); 743 //expr=new OpVariable((Variable)expr,res,data.cfml.getPosition()); 744 } 745 746 else { 747 comments(data); 748 expr=OpDouble.toExprDouble(expr, modOp(data), opr); 749 } 750 return expr; 751 } 752 753 754 /** 755 * Transfomiert eine Modulus Operation. Im Gegensatz zu CFMX , 756 * wird das "%" Zeichen auch als Modulus Operator anerkannt. 757 * <br /> 758 * EBNF:<br /> 759 * <code>divMultiOp {("mod" | "%") spaces divMultiOp}; (* modulus operator , "%" Existiert in CFMX nicht *)</code> 760 * @return CFXD Element 761 * @throws TemplateException 762 */ 763 private Expression modOp(ExprData data) throws TemplateException { 764 Expression expr = divMultiOp(data); 765 766 // Modulus Operation 767 while(data.cfml.forwardIfCurrent('%') || data.cfml.forwardIfCurrentAndNoWordAfter("mod")) { 768 expr=_modOp(data,expr); 769 //comments(data); 770 //expr=OpDouble.toExprDouble(expr, divMultiOp(), OpDouble.MODULUS); 771 } 772 return expr; 773 } 774 775 private Expression _modOp(ExprData data,Expression expr) throws TemplateException { 776 if (data.cfml.isCurrent('=') && expr instanceof Variable) { 777 data.cfml.next(); 778 comments(data); 779 Expression right = assignOp(data); 780 ExprDouble res = OpDouble.toExprDouble(expr, right,OpDouble.MODULUS); 781 return new OpVariable((Variable)expr,res,data.cfml.getPosition()); 782 } 783 comments(data); 784 return OpDouble.toExprDouble(expr, expoOp(data), OpDouble.MODULUS); 785 } 786 787 /** 788 * Transfomiert die mathematischen Operatoren Mal und Durch (*,/). 789 * <br /> 790 * EBNF:<br /> 791 * <code>expoOp {("*"|"/") spaces expoOp};</code> 792 * @return CFXD Element 793 * @throws TemplateException 794 */ 795 private Expression divMultiOp(ExprData data) throws TemplateException { 796 Expression expr = expoOp(data); 797 798 while (!data.cfml.isLast()) { 799 800 // Multiply Operation 801 if(data.cfml.forwardIfCurrent('*')) { 802 expr=_divMultiOp(data,expr,OpDouble.MULTIPLY); 803 //comments(data); 804 //expr=OpDouble.toExprDouble(expr, expoOp(), OpDouble.MULTIPLY); 805 } 806 // Divide Operation 807 else if (data.cfml.isCurrent('/') && (!data.cfml.isCurrent('/','>') )) { 808 data.cfml.next(); 809 expr=_divMultiOp(data,expr,OpDouble.DIVIDE); 810 //comments(data); 811 //expr=OpDouble.toExprDouble(expr, expoOp(), OpDouble.DIVIDE); 812 } 813 // Divide Operation 814 else if (data.cfml.isCurrent('\\')) { 815 data.cfml.next(); 816 expr=_divMultiOp(data,expr,OpDouble.INTDIV); 817 //comments(data); 818 //expr=OpDouble.toExprDouble(expr, expoOp(), OpDouble.INTDIV); 819 } 820 else { 821 break; 822 } 823 824 } 825 return expr; 826 } 827 828 private Expression _divMultiOp(ExprData data,Expression expr, int iOp) throws TemplateException { 829 if (data.cfml.isCurrent('=') && expr instanceof Variable) { 830 data.cfml.next(); 831 comments(data); 832 Expression value = assignOp(data); 833 834 return new OPUnary((Variable)expr,value,OPUnary.PRE,iOp,expr.getStart(),data.cfml.getPosition()); 835 836 837 838 839 //ExprDouble res = OpDouble.toExprDouble(expr, right,iOp); 840 //return new OpVariable((Variable)expr,res,data.cfml.getPosition()); 841 } 842 comments(data); 843 return OpDouble.toExprDouble(expr, expoOp(data), iOp); 844 } 845 846 /** 847 * Transfomiert den Exponent Operator (^,exp). Im Gegensatz zu CFMX , 848 * werden die Zeichen " exp " auch als Exponent anerkannt. 849 * <br /> 850 * EBNF:<br /> 851 * <code>clip {("exp"|"^") spaces clip};</code> 852 * @return CFXD Element 853 * @throws TemplateException 854 */ 855 private Expression expoOp(ExprData data) throws TemplateException { 856 Expression expr = unaryOp(data); 857 858 // Modulus Operation 859 while(data.cfml.forwardIfCurrent('^') || data.cfml.forwardIfCurrentAndNoWordAfter("exp")) { 860 comments(data); 861 expr=OpDouble.toExprDouble(expr, unaryOp(data), OpDouble.EXP); 862 } 863 return expr; 864 } 865 866 private Expression unaryOp(ExprData data) throws TemplateException { 867 Expression expr = negatePlusMinusOp(data); 868 869 // Plus Operation 870 if (data.cfml.forwardIfCurrent("++") && expr instanceof Variable) 871 expr=_unaryOp(data,expr,OpDouble.PLUS); 872 // Minus Operation 873 else if (data.cfml.forwardIfCurrent("--") && expr instanceof Variable) 874 expr=_unaryOp(data,expr,OpDouble.MINUS); 875 return expr; 876 } 877 878 private Expression _unaryOp(ExprData data,Expression expr,int op) throws TemplateException { 879 Position leftEnd = expr.getEnd(),start=null,end=null; 880 comments(data); 881 if(leftEnd!=null){ 882 start=leftEnd; 883 end=new Position(leftEnd.line, leftEnd.column+2, leftEnd.pos+2); 884 } 885 return new OPUnary((Variable)expr,LitDouble.ONE,OPUnary.POST,op,start,end); 886 887 888 889 //ExprDouble res = OpDouble.toExprDouble(expr, LitDouble.toExprDouble(1D,start,end),opr); 890 //expr=new OpVariable((Variable)expr,res,data.cfml.getPosition()); 891 //return OpDouble.toExprDouble(expr,LitDouble.toExprDouble(1D,start,end),opr==OpDouble.PLUS? OpDouble.MINUS:OpDouble.PLUS); 892 } 893 894 895 896 897 /** 898 * Negate Numbers 899 * @return CFXD Element 900 * @throws TemplateException 901 */ 902 private Expression negatePlusMinusOp(ExprData data) throws TemplateException { 903 // And Operation 904 Position line=data.cfml.getPosition(); 905 if (data.cfml.forwardIfCurrent('-')) { 906 // pre increment 907 if (data.cfml.forwardIfCurrent('-')) { 908 comments(data); 909 Expression expr = clip(data); 910 return new OPUnary((Variable)expr,LitDouble.ONE,OPUnary.PRE,OpDouble.MINUS,line,data.cfml.getPosition()); 911 912 //ExprDouble res = OpDouble.toExprDouble(expr, LitDouble.toExprDouble(1D),OpDouble.MINUS); 913 //return new OpVariable((Variable)expr,res,data.cfml.getPosition()); 914 915 916 } 917 comments(data); 918 return OpNegateNumber.toExprDouble(clip(data),OpNegateNumber.MINUS,line,data.cfml.getPosition()); 919 920 } 921 else if (data.cfml.forwardIfCurrent('+')) { 922 if (data.cfml.forwardIfCurrent('+')) { 923 comments(data); 924 Expression expr = clip(data); 925 926 return new OPUnary((Variable)expr,LitDouble.ONE,OPUnary.PRE,OpDouble.PLUS,line,data.cfml.getPosition()); 927 928 //ExprDouble res = OpDouble.toExprDouble(expr, LitDouble.toExprDouble(1D),OpDouble.PLUS); 929 //return new OpVariable((Variable)expr,res,data.cfml.getPosition()); 930 } 931 comments(data); 932 return CastDouble.toExprDouble(clip(data));//OpNegateNumber.toExprDouble(clip(),OpNegateNumber.PLUS,line); 933 } 934 return clip(data); 935 } 936 937 /** 938 * Verarbeitet Ausdruecke die inerhalb einer Klammer stehen. 939 * <br /> 940 * EBNF:<br /> 941 * <code>("(" spaces impOp ")" spaces) | checker;</code> 942 * @return CFXD Element 943 * @throws TemplateException 944 */ 945 private Expression clip(ExprData data) throws TemplateException { 946 return checker(data); 947 } 948 /** 949 * Hier werden die verschiedenen Moeglichen Werte erkannt 950 * und jenachdem wird mit der passenden Methode weitergefahren 951 * <br /> 952 * EBNF:<br /> 953 * <code>string | number | dynamic | sharp;</code> 954 * @return CFXD Element 955 * @throws TemplateException 956 */ 957 private Expression checker(ExprData data) throws TemplateException { 958 Expression expr=null; 959 // String 960 if((expr=string(data))!=null) { 961 expr = subDynamic(data,expr); 962 data.mode=STATIC;//(expr instanceof Literal)?STATIC:DYNAMIC;// STATIC 963 return expr; 964 } 965 // Number 966 if((expr=number(data))!=null) { 967 expr = subDynamic(data,expr); 968 data.mode=STATIC;//(expr instanceof Literal)?STATIC:DYNAMIC;// STATIC 969 return expr; 970 } 971 // closure 972 if((expr=closure(data))!=null) { 973 data.mode=DYNAMIC; 974 return expr; 975 } 976 977 // Dynamic 978 if((expr=dynamic(data))!=null) { 979 expr = newOp(data, expr); 980 expr = subDynamic(data,expr); 981 data.mode=DYNAMIC; 982 return expr; 983 } 984 // Sharp 985 if((expr=sharp(data))!=null) { 986 data.mode=DYNAMIC; 987 return expr; 988 } 989 // JSON 990 if((expr=json(data,JSON_ARRAY,'[',']'))!=null) { 991 expr = subDynamic(data,expr); 992 data.mode=DYNAMIC; 993 return expr; 994 } 995 if((expr=json(data,JSON_STRUCT,'{','}'))!=null) { 996 expr = subDynamic(data,expr); 997 data.mode=DYNAMIC; 998 return expr; 999 } 1000 // else Error 1001 throw new TemplateException(data.cfml,"Syntax Error, Invalid Construct"); 1002 } 1003 1004 /*private Expression variable(Data data) throws TemplateException { 1005 Expression expr=null; 1006 1007 // Dynamic 1008 if((expr=dynamic(data))!=null) { 1009 expr = subDynamic(data,expr); 1010 data.mode=DYNAMIC; 1011 return expr; 1012 } 1013 return null; 1014 }*/ 1015 1016 /** 1017 * Transfomiert einen lierale Zeichenkette. 1018 * <br /> 1019 * EBNF:<br /> 1020 * <code>("'" {"##"|"''"|"#" impOp "#"| ?-"#"-"'" } "'") | 1021 (""" {"##"|""""|"#" impOp "#"| ?-"#"-""" } """);</code> 1022 * @param data 1023 * @return CFXD Element 1024 * @throws TemplateException 1025 */ 1026 protected Expression string(ExprData data) throws TemplateException { 1027 1028 // check starting character for a string literal 1029 if(!data.cfml.isCurrent('"')&& !data.cfml.isCurrent('\'')) 1030 return null; 1031 Position line = data.cfml.getPosition(); 1032 1033 // Init Parameter 1034 char quoter = data.cfml.getCurrentLower(); 1035 StringBuilder str=new StringBuilder(); 1036 Expression expr=null; 1037 1038 while(data.cfml.hasNext()) { 1039 data.cfml.next(); 1040 // check sharp 1041 if(data.cfml.isCurrent('#')) { 1042 1043 // Ecaped sharp 1044 if(data.cfml.isNext('#')){ 1045 data.cfml.next(); 1046 str.append('#'); 1047 } 1048 // get Content of sharp 1049 else { 1050 data.cfml.next(); 1051 comments(data); 1052 Expression inner=assignOp(data); 1053 comments(data); 1054 if (!data.cfml.isCurrent('#')) 1055 throw new TemplateException(data.cfml,"Invalid Syntax Closing [#] not found"); 1056 1057 ExprString exprStr=null; 1058 if(str.length()!=0) { 1059 exprStr=LitString.toExprString(str.toString(),line,data.cfml.getPosition()); 1060 if(expr!=null){ 1061 expr = OpString.toExprString(expr, exprStr); 1062 } 1063 else expr=exprStr; 1064 str=new StringBuilder(); 1065 } 1066 if(expr==null) { 1067 expr=inner; 1068 } 1069 else { 1070 expr = OpString.toExprString(expr, inner); 1071 } 1072 } 1073 } 1074 // check quoter 1075 else if(data.cfml.isCurrent(quoter)) { 1076 // Ecaped sharp 1077 if(data.cfml.isNext(quoter)){ 1078 data.cfml.next(); 1079 str.append(quoter); 1080 } 1081 // finsish 1082 else { 1083 break; 1084 } 1085 } 1086 // all other character 1087 else { 1088 str.append(data.cfml.getCurrent()); 1089 } 1090 } 1091 if(!data.cfml.forwardIfCurrent(quoter)) 1092 throw new TemplateException(data.cfml,"Invalid Syntax Closing ["+quoter+"] not found"); 1093 1094 if(expr==null) 1095 expr=LitString.toExprString(str.toString(),line,data.cfml.getPosition()); 1096 else if(str.length()!=0) { 1097 expr = OpString.toExprString(expr, LitString.toExprString(str.toString(),line,data.cfml.getPosition())); 1098 } 1099 comments(data); 1100 1101 if(expr instanceof Variable) { 1102 Variable var=(Variable) expr; 1103 var.setFromHash(true); 1104 } 1105 1106 return expr; 1107 1108 } 1109 1110 /** 1111 * Transfomiert einen numerische Wert. 1112 * Die Laenge des numerischen Wertes interessiert nicht zu Uebersetzungszeit, 1113 * ein "Overflow" fuehrt zu einem Laufzeitfehler. 1114 * Da die zu erstellende CFXD, bzw. dieser Transfomer, keine Vorwegnahme des Laufzeitsystems vornimmt. 1115 * <br /> 1116 * EBNF:<br /> 1117 * <code>["+"|"-"] digit {digit} {"." digit {digit}};</code> 1118 * @return CFXD Element 1119 * @throws TemplateException 1120 */ 1121 private LitDouble number(ExprData data) throws TemplateException { 1122 // check first character is a number literal representation 1123 if(!(data.cfml.isCurrentBetween('0','9') || data.cfml.isCurrent('.'))) return null; 1124 1125 Position line = data.cfml.getPosition(); 1126 StringBuffer rtn=new StringBuffer(); 1127 1128 // get digit on the left site of the dot 1129 if(data.cfml.isCurrent('.')) rtn.append('0'); 1130 else rtn.append(digit(data)); 1131 // read dot if exist 1132 if(data.cfml.forwardIfCurrent('.')) { 1133 rtn.append('.'); 1134 String rightSite=digit(data); 1135 if(rightSite.length()> 0 && data.cfml.forwardIfCurrent('e')) { 1136 Boolean expOp=null; 1137 if(data.cfml.forwardIfCurrent('+')) expOp=Boolean.TRUE; 1138 else if(data.cfml.forwardIfCurrent('-')) expOp=Boolean.FALSE; 1139 1140 if(data.cfml.isCurrentBetween('0','9')) { 1141 if(expOp==Boolean.FALSE) rightSite+="e-"; 1142 else if(expOp==Boolean.TRUE) rightSite+="e+"; 1143 else rightSite+="e"; 1144 rightSite+=digit(data); 1145 } 1146 else { 1147 if(expOp!=null) data.cfml.previous(); 1148 data.cfml.previous(); 1149 } 1150 } 1151 // read right side of the dot 1152 if(rightSite.length()==0) 1153 rightSite="0";//throw new TemplateException(cfml, "Number can't end with [.]"); // DIFF 23 1154 rtn.append(rightSite); 1155 } 1156 else if(data.cfml.forwardIfCurrent('e')) { 1157 Boolean expOp=null; 1158 if(data.cfml.forwardIfCurrent('+')) expOp=Boolean.TRUE; 1159 else if(data.cfml.forwardIfCurrent('-')) expOp=Boolean.FALSE; 1160 1161 if(data.cfml.isCurrentBetween('0','9')) { 1162 String rightSite = "e"; 1163 if(expOp==Boolean.FALSE) rightSite+="-"; 1164 else if(expOp==Boolean.TRUE) rightSite+="+"; 1165 rightSite+=digit(data); 1166 rtn.append(rightSite); 1167 } 1168 else { 1169 if(expOp!=null) data.cfml.previous(); 1170 data.cfml.previous(); 1171 } 1172 1173 } 1174 comments(data); 1175 1176 try { 1177 return LitDouble.toExprDouble(Caster.toDoubleValue(rtn.toString()),line,data.cfml.getPosition()); 1178 } catch (CasterException e) { 1179 throw new TemplateException(data.cfml,e.getMessage()); 1180 } 1181 1182 } 1183 1184 1185 1186 /** 1187 * Liest die reinen Zahlen innerhalb des CFMLString aus und gibt diese als Zeichenkette zurueck. 1188 * <br /> 1189 * EBNF:<br /> 1190 * <code>"0"|..|"9";</code> 1191 * @return digit Ausgelesene Zahlen als Zeichenkette. 1192 */ 1193 private String digit(ExprData data) { 1194 String rtn=""; 1195 while (data.cfml.isValidIndex()) { 1196 if(!data.cfml.isCurrentBetween('0','9'))break; 1197 rtn+=data.cfml.getCurrentLower(); 1198 data.cfml.next(); 1199 } 1200 return rtn; 1201 } 1202 1203 /** 1204 * Liest den folgenden idetifier ein und prueft ob dieser ein boolscher Wert ist. 1205 * Im Gegensatz zu CFMX wird auch "yes" und "no" als bolscher <wert akzeptiert, 1206 * was bei CFMX nur beim Umwandeln einer Zeichenkette zu einem boolschen Wert der Fall ist.<br /> 1207 * Wenn es sich um keinen bolschen Wert handelt wird der folgende Wert eingelesen mit seiner ganzen Hirarchie. 1208 * <br /> 1209 * EBNF:<br /> 1210 * <code>"true" | "false" | "yes" | "no" | startElement {("." identifier | "[" structElement "]" )[function] };</code> 1211 * @return CFXD Element 1212 * @throws TemplateException 1213 */ 1214 private Expression dynamic(ExprData data) throws TemplateException { 1215 // Die Implementation weicht ein wenig von der Grammatik ab, 1216 // aber nicht in der Logik sondern rein wie es umgesetzt wurde. 1217 1218 1219 1220 // get First Element of the Variable 1221 Position line = data.cfml.getPosition(); 1222 Identifier id = identifier(data,false,true); 1223 if(id == null) { 1224 if (!data.cfml.forwardIfCurrent('(')) return null; 1225 1226 comments(data); 1227 Expression expr = assignOp(data); 1228 1229 if (!data.cfml.forwardIfCurrent(')')) 1230 throw new TemplateException( 1231 data.cfml, 1232 "Invalid Syntax Closing [)] not found"); 1233 comments(data); 1234 return expr;//subDynamic(expr); 1235 1236 } 1237 1238 Variable var; 1239 comments(data); 1240 1241 // Boolean constant 1242 if(id.getString().equalsIgnoreCase("TRUE")) {// || name.equals("YES")) { 1243 comments(data); 1244 return new LitBoolean(true,line,data.cfml.getPosition()); 1245 } 1246 else if(id.getString().equalsIgnoreCase("FALSE")) {// || name.equals("NO")) { 1247 comments(data); 1248 return new LitBoolean(false,line,data.cfml.getPosition()); 1249 } 1250 else if(NullSupportHelper.full() && id.getString().equalsIgnoreCase("NULL")) { 1251 comments(data); 1252 return new Null(line,data.cfml.getPosition()); 1253 } 1254 1255 // Extract Scope from the Variable 1256 //int c=data.cfml.getColumn(); 1257 //Position l=data.cfml.getPosition(); 1258 var = startElement(data,id,line); 1259 var.setStart(line); 1260 var.setEnd(data.cfml.getPosition()); 1261 return var; 1262 } 1263 1264 1265 1266 protected Expression json(ExprData data,FunctionLibFunction flf, char start, char end) throws TemplateException { 1267 if(!data.cfml.forwardIfCurrent(start))return null; 1268 1269 Position line = data.cfml.getPosition(); 1270 BIF bif=new BIF(flf.getName(),flf); 1271 bif.setArgType(flf.getArgType()); 1272 bif.setClass(flf.getClazz()); 1273 bif.setReturnType(flf.getReturnTypeAsString()); 1274 1275 do { 1276 comments(data); 1277 if (data.cfml.isCurrent(end))break; 1278 1279 bif.addArgument(functionArgument(data,data.settings.dotNotationUpper)); 1280 comments(data); 1281 } 1282 while (data.cfml.forwardIfCurrent(',')); 1283 comments(data); 1284 1285 if (!data.cfml.forwardIfCurrent(end)) 1286 throw new TemplateException(data.cfml,"Invalid Syntax Closing ["+end+"] not found"); 1287 comments(data); 1288 Variable var=new Variable(line,data.cfml.getPosition()); 1289 var.addMember(bif); 1290 return var; 1291 } 1292 1293 private Expression closure(ExprData data) throws TemplateException { 1294 if(!data.cfml.forwardIfCurrent("function",'('))return null; 1295 data.cfml.previous(); 1296 return new ClosureAsExpression((Closure) closurePart(data, "closure_"+CreateUniqueId.invoke(), Component.ACCESS_PUBLIC, "any", data.cfml.getPosition(),true)); 1297 } 1298 1299 protected abstract Function closurePart(ExprData data, String id, int access, String rtnType, Position line,boolean closure) throws TemplateException; 1300 1301 1302 protected FunctionLibFunction getFLF(ExprData data,String name) { 1303 FunctionLibFunction flf=null; 1304 for (int i = 0; i < data.flibs.length; i++) { 1305 flf = data.flibs[i].getFunction(name); 1306 if (flf != null) 1307 break; 1308 } 1309 return flf; 1310 } 1311 1312 private Expression subDynamic(ExprData data,Expression expr) throws TemplateException { 1313 1314 1315 1316 1317 String name=null; 1318 Invoker invoker=null; 1319 // Loop over nested Variables 1320 while (data.cfml.isValidIndex()) { 1321 ExprString nameProp = null,namePropUC = null; 1322 // . 1323 if (data.cfml.forwardIfCurrent('.')) { 1324 // Extract next Var String 1325 comments(data); 1326 Position line=data.cfml.getPosition(); 1327 name = identifier(data,true); 1328 if(name==null) 1329 throw new TemplateException(data.cfml, "Invalid identifier"); 1330 comments(data); 1331 nameProp=Identifier.toIdentifier(name,line,data.cfml.getPosition()); 1332 namePropUC=Identifier.toIdentifier(name,data.settings.dotNotationUpper?Identifier.CASE_UPPER:Identifier.CASE_ORIGNAL,line,data.cfml.getPosition()); 1333 } 1334 // [] 1335 else if (data.cfml.forwardIfCurrent('[')) { 1336 1337 // get Next Var 1338 nameProp = structElement(data); 1339 namePropUC=nameProp; 1340 // Valid Syntax ??? 1341 if (!data.cfml.forwardIfCurrent(']')) 1342 throw new TemplateException( 1343 data.cfml, 1344 "Invalid Syntax Closing []] not found"); 1345 } 1346 /* / : 1347 else if (data.cfml.forwardIfCurrent(':')) { 1348 // Extract next Var String 1349 comments(data); 1350 int line=data.cfml.getLine(); 1351 name = identifier(true,true); 1352 if(name==null) 1353 throw new TemplateException(cfml, "Invalid identifier"); 1354 comments(data); 1355 1356 nameProp=LitString.toExprString(name,line); 1357 }*/ 1358 // finish 1359 else { 1360 break; 1361 } 1362 1363 comments(data); 1364 1365 if(expr instanceof Invoker) { 1366 invoker=(Invoker) expr; 1367 } 1368 else { 1369 invoker=new ExpressionInvoker(expr); 1370 expr=invoker; 1371 } 1372 // Method 1373 if (data.cfml.isCurrent('(')) { 1374 if(nameProp==null && name!=null)nameProp=Identifier.toIdentifier(name, Identifier.CASE_ORIGNAL,null,null);// properly this is never used 1375 invoker.addMember(getFunctionMember(data,nameProp, false)); 1376 } 1377 1378 // property 1379 else invoker.addMember(new DataMember(namePropUC)); 1380 1381 } 1382 1383 return expr; 1384 } 1385 1386 private Expression newOp(ExprData data,Expression expr) throws TemplateException { 1387 if(!(expr instanceof Variable)) return expr; 1388 Variable var=(Variable) expr; 1389 Member m= var.getFirstMember(); 1390 if(!(m instanceof DataMember)) return expr; 1391 1392 ExprString n = ((DataMember)m).getName(); 1393 if(!(n instanceof LitString)) return expr; 1394 1395 LitString ls=(LitString) n; 1396 1397 1398 if(!"new".equalsIgnoreCase(ls.getString())) return expr; 1399 1400 int start=data.cfml.getPos(); 1401 String name=null; 1402 1403 1404 // first identifier 1405 name = identifier(data,true); 1406 1407 1408 ExprString exprName; 1409 if(name!=null) { 1410 StringBuilder fullName=new StringBuilder(); 1411 fullName.append(name); 1412 // Loop over addional identifier 1413 while (data.cfml.isValidIndex()) { 1414 if (data.cfml.forwardIfCurrent('.')) { 1415 comments(data); 1416 name = identifier(data,true); 1417 if(name==null) { 1418 data.cfml.setPos(start); 1419 return expr;//throw new TemplateException(data.cfml,"invalid Component declaration "); 1420 } 1421 fullName.append('.'); 1422 fullName.append(name); 1423 comments(data); 1424 } 1425 else break; 1426 } 1427 1428 exprName=LitString.toExprString(fullName.toString()); 1429 } 1430 else { 1431 1432 Expression str=string(data); 1433 if(str!=null){ 1434 exprName=CastString.toExprString(str); 1435 } 1436 else { 1437 data.cfml.setPos(start); 1438 return expr; 1439 } 1440 } 1441 1442 comments(data); 1443 1444 if (data.cfml.isCurrent('(')) { 1445 FunctionMember func = getFunctionMember(data,Identifier.toIdentifier("_createComponent",Identifier.CASE_ORIGNAL,null,null), true); 1446 func.addArgument(new Argument(exprName,"string")); 1447 Variable v=new Variable(expr.getStart(),expr.getEnd()); 1448 v.addMember(func); 1449 comments(data); 1450 return v; 1451 } 1452 data.cfml.setPos(start); 1453 return expr;//throw new TemplateException(data.cfml,"invalid Component declaration "); 1454 1455 } 1456 1457 1458 1459 1460 1461 /** 1462 * Extrahiert den Start Element einer Variale, 1463 * dies ist entweder eine Funktion, eine Scope Definition oder eine undefinierte Variable. 1464 * <br /> 1465 * EBNF:<br /> 1466 * <code>identifier "(" functionArg ")" | scope | identifier;</code> 1467 * @param name Einstiegsname 1468 * @return CFXD Element 1469 * @throws TemplateException 1470 */ 1471 private Variable startElement(ExprData data,Identifier name, Position line) throws TemplateException { 1472 1473 1474 1475 1476 // check function 1477 if (data.cfml.isCurrent('(')) { 1478 FunctionMember func = getFunctionMember(data,name, true); 1479 1480 Variable var=new Variable(line,data.cfml.getPosition()); 1481 var.addMember(func); 1482 comments(data); 1483 return var; 1484 } 1485 1486 //check scope 1487 Variable var = scope(data,name,line); 1488 if(var!=null) return var; 1489 1490 // undefined variable 1491 var=new Variable(line,data.cfml.getPosition()); 1492 var.addMember(new DataMember(name)); 1493 1494 comments(data); 1495 return var; 1496 1497 } 1498 1499 /** 1500 * Liest einen CFML Scope aus, 1501 * falls der folgende identifier keinem Scope entspricht, 1502 * gibt die Variable null zurueck. 1503 * <br /> 1504 * EBNF:<br /> 1505 * <code>"variable" | "cgi" | "url" | "form" | "session" | "application" | "arguments" | "cookie" | " client";</code> 1506 * @param id String identifier, 1507 * wird aus Optimierungszwechen nicht innerhalb dieser Funktion ausgelsen. 1508 * @return CFXD Variable Element oder null 1509 * @throws TemplateException 1510 */ 1511 private Variable scope(ExprData data,Identifier id, Position line) throws TemplateException { 1512 String idStr=id.getUpper(); 1513 if(data.ignoreScopes)return null; 1514 if (idStr.equals("CGI")) return new Variable(Scope.SCOPE_CGI,line,data.cfml.getPosition()); 1515 else if (idStr.equals("ARGUMENTS")) return new Variable(Scope.SCOPE_ARGUMENTS,line,data.cfml.getPosition()); 1516 else if (idStr.equals("REQUEST")) return new Variable(Scope.SCOPE_REQUEST,line,data.cfml.getPosition()); 1517 else if (idStr.equals("SESSION")) return new Variable(Scope.SCOPE_SESSION,line,data.cfml.getPosition()); 1518 else if (idStr.equals("APPLICATION")) return new Variable(Scope.SCOPE_APPLICATION,line,data.cfml.getPosition()); 1519 else if (idStr.equals("VARIABLES")) return new Variable(Scope.SCOPE_VARIABLES,line,data.cfml.getPosition()); 1520 else if (idStr.equals("FORM")) return new Variable(Scope.SCOPE_FORM,line,data.cfml.getPosition()); 1521 else if (idStr.equals("URL")) return new Variable(Scope.SCOPE_URL,line,data.cfml.getPosition()); 1522 else if (idStr.equals("SERVER")) return new Variable(Scope.SCOPE_SERVER,line,data.cfml.getPosition()); 1523 else if (idStr.equals("CLIENT")) return new Variable(Scope.SCOPE_CLIENT,line,data.cfml.getPosition()); 1524 else if (idStr.equals("COOKIE")) return new Variable(Scope.SCOPE_COOKIE,line,data.cfml.getPosition()); 1525 else if (idStr.equals("CLUSTER")) return new Variable(Scope.SCOPE_CLUSTER,line,data.cfml.getPosition()); 1526 else if (idStr.equals("LOCAL")) return new Variable(Scope.SCOPE_LOCAL,line,data.cfml.getPosition()); 1527 else if (idStr.equals("VAR")) { 1528 Identifier _id = identifier(data,false,true); 1529 if(_id!=null){ 1530 comments(data); 1531 Variable local = new Variable(ScopeSupport.SCOPE_VAR,line,data.cfml.getPosition()); 1532 if(!"LOCAL".equalsIgnoreCase(_id.getString()))local.addMember(new DataMember(_id)); 1533 else { 1534 local.ignoredFirstMember(true); 1535 } 1536 return local; 1537 } 1538 } 1539 return null; 1540 } 1541 1542 /** 1543 * Liest einen Identifier aus und gibt diesen als String zurueck. 1544 * <br /> 1545 * EBNF:<br /> 1546 * <code>(letter | "_") {letter | "_"|digit};</code> 1547 * @param firstCanBeNumber 1548 * @param upper 1549 * @return Identifier. 1550 */ 1551 protected Identifier identifier(ExprData data,boolean firstCanBeNumber,boolean upper) { 1552 Position start = data.cfml.getPosition(); 1553 if(!data.cfml.isCurrentLetter() && !data.cfml.isCurrentSpecial() ) { 1554 if(!firstCanBeNumber) return null; 1555 else if(!data.cfml.isCurrentBetween('0','9'))return null; 1556 } 1557 do { 1558 data.cfml.next(); 1559 if(!(data.cfml.isCurrentLetter() 1560 || data.cfml.isCurrentBetween('0','9') 1561 || data.cfml.isCurrentSpecial())) { 1562 break; 1563 } 1564 } 1565 while (data.cfml.isValidIndex()); 1566 return Identifier.toIdentifier(data.cfml.substring(start.pos,data.cfml.getPos()-start.pos), 1567 upper && data.settings.dotNotationUpper?Identifier.CASE_UPPER:Identifier.CASE_ORIGNAL, start,data.cfml.getPosition()); 1568 } 1569 1570 protected String identifier(ExprData data,boolean firstCanBeNumber) { 1571 int start = data.cfml.getPos(); 1572 if(!data.cfml.isCurrentLetter() && !data.cfml.isCurrentSpecial() ) { 1573 if(!firstCanBeNumber) return null; 1574 else if(!data.cfml.isCurrentBetween('0','9'))return null; 1575 } 1576 do { 1577 data.cfml.next(); 1578 if(!(data.cfml.isCurrentLetter() 1579 || data.cfml.isCurrentBetween('0','9') 1580 || data.cfml.isCurrentSpecial())) { 1581 break; 1582 } 1583 } 1584 while (data.cfml.isValidIndex()); 1585 return data.cfml.substring(start,data.cfml.getPos()-start); 1586 } 1587 1588 /** 1589 * Transfomiert ein Collection Element das in eckigen Klammern aufgerufen wird. 1590 * <br /> 1591 * EBNF:<br /> 1592 * <code>"[" impOp "]"</code> 1593 * @return CFXD Element 1594 * @throws TemplateException 1595 */ 1596 private ExprString structElement(ExprData data) throws TemplateException { 1597 comments(data); 1598 ExprString name = CastString.toExprString(assignOp(data)); 1599 if(name instanceof LitString)((LitString)name).fromBracket(true); 1600 comments(data); 1601 return name; 1602 } 1603 1604 /** 1605 * Liest die Argumente eines Funktonsaufruf ein und prueft ob die Funktion 1606 * innerhalb der FLD (Function Library Descriptor) definiert ist. 1607 * Falls sie existiert wird die Funktion gegen diese geprueft und ein build-in-function CFXD Element generiert, 1608 * ansonsten ein normales funcion-call Element. 1609 * <br /> 1610 * EBNF:<br /> 1611 * <code>[impOp{"," impOp}];</code> 1612 * @param name Identifier der Funktion als Zeichenkette 1613 * @param checkLibrary Soll geprueft werden ob die Funktion innerhalb der Library existiert. 1614 * @return CFXD Element 1615 * @throws TemplateException 1616 */ 1617 private FunctionMember getFunctionMember(ExprData data, 1618 final ExprString name, 1619 boolean checkLibrary) 1620 throws TemplateException { 1621 1622 // get Function Library 1623 checkLibrary=checkLibrary && data.flibs!=null; 1624 FunctionLibFunction flf = null; 1625 if (checkLibrary) { 1626 if(!(name instanceof Literal)) 1627 throw new TemplateException(data.cfml,"syntax error"); // should never happen! 1628 1629 for (int i = 0; i < data.flibs.length; i++) { 1630 flf = data.flibs[i].getFunction(((Literal)name).getString()); 1631 if (flf != null)break; 1632 } 1633 if (flf == null) { 1634 checkLibrary = false; 1635 } 1636 } 1637 // Element Function 1638 FunctionMember fm; 1639 if(checkLibrary) { 1640 BIF bif=new BIF(name,flf); 1641 bif.setArgType(flf.getArgType()); 1642 bif.setClass(flf.getClazz()); 1643 bif.setReturnType(flf.getReturnTypeAsString()); 1644 fm=bif; 1645 1646 if(flf.getArgType()== FunctionLibFunction.ARG_DYNAMIC && flf.hasDefaultValues()){ 1647 ArrayList<FunctionLibFunctionArg> args = flf.getArg(); 1648 Iterator<FunctionLibFunctionArg> it = args.iterator(); 1649 FunctionLibFunctionArg arg; 1650 while(it.hasNext()){ 1651 arg=it.next(); 1652 if(arg.getDefaultValue()!=null) 1653 bif.addArgument( 1654 new NamedArgument( 1655 LitString.toExprString(arg.getName()), 1656 LitString.toExprString(arg.getDefaultValue()), 1657 arg.getTypeAsString(),false 1658 )); 1659 } 1660 } 1661 } 1662 else { 1663 fm = new UDF(name); 1664 } 1665 1666 1667 1668 1669 // Function Attributes 1670 ArrayList<FunctionLibFunctionArg> arrFuncLibAtt = null; 1671 int libLen = 0; 1672 if (checkLibrary) { 1673 arrFuncLibAtt = flf.getArg(); 1674 libLen = arrFuncLibAtt.size(); 1675 } 1676 int count = 0; 1677 do { 1678 data.cfml.next(); 1679 comments(data); 1680 1681 // finish 1682 if (count==0 && data.cfml.isCurrent(')')) 1683 break; 1684 1685 // too many Attributes 1686 boolean isDynamic=false; 1687 int max=-1; 1688 if(checkLibrary) { 1689 isDynamic=flf.getArgType()==FunctionLibFunction.ARG_DYNAMIC; 1690 max=flf.getArgMax(); 1691 // Dynamic 1692 if(isDynamic) { 1693 if(max!=-1 && max <= count) 1694 throw new TemplateException( 1695 data.cfml, 1696 "too many Attributes in function [" + ASMUtil.display(name) + "]"); 1697 } 1698 // Fix 1699 else { 1700 if(libLen <= count){ 1701 1702 TemplateException te = new TemplateException( 1703 data.cfml, 1704 "too many Attributes in function call [" + ASMUtil.display(name) + "]"); 1705 UDFUtil.addFunctionDoc(te, flf); 1706 throw te; 1707 } 1708 } 1709 1710 } 1711 1712 //Argument arg; 1713 if (checkLibrary && !isDynamic) { 1714 // current attribues from library 1715 FunctionLibFunctionArg funcLibAtt =arrFuncLibAtt.get(count); 1716 fm.addArgument(functionArgument(data,funcLibAtt.getTypeAsString(),false)); 1717 } 1718 else { 1719 fm.addArgument(functionArgument(data,false)); 1720 } 1721 1722 comments(data); 1723 count++; 1724 if (data.cfml.isCurrent(')')) 1725 break; 1726 } 1727 while (data.cfml.isCurrent(',')); 1728 1729 // end with ) ?? 1730 if (!data.cfml.forwardIfCurrent(')')) 1731 throw new TemplateException( 1732 data.cfml, 1733 "Invalid Syntax Closing [)] for function [" 1734 + ASMUtil.display(name) 1735 + "] not found"); 1736 1737 // check min attributes 1738 if (checkLibrary && flf.getArgMin() > count){ 1739 TemplateException te = new TemplateException( 1740 data.cfml, 1741 "too few attributes in function [" + ASMUtil.display(name) + "]"); 1742 if(flf.getArgType()==FunctionLibFunction.ARG_FIX) UDFUtil.addFunctionDoc(te, flf); 1743 throw te; 1744 } 1745 1746 comments(data); 1747 1748 // evaluator 1749 if(checkLibrary && flf.hasTteClass()){ 1750 flf.getEvaluator().evaluate((BIF) fm, flf); 1751 } 1752 1753 return fm; 1754 } 1755 1756 /** 1757 * Sharps (#) die innerhalb von Expressions auftauchen haben in CFML keine weitere Beteutung 1758 * und werden durch diese Methode einfach entfernt. 1759 * <br /> 1760 * Beispiel:<br /> 1761 * <code>arrayLen(#arr#)</code> und <code>arrayLen(arr)</code> sind identisch. 1762 * EBNF:<br /> 1763 * <code>"#" checker "#";</code> 1764 * @return CFXD Element 1765 * @throws TemplateException 1766 */ 1767 private Expression sharp(ExprData data) throws TemplateException { 1768 if(!data.cfml.forwardIfCurrent('#')) 1769 return null; 1770 Expression expr; 1771 comments(data); 1772 boolean old=data.allowLowerThan; 1773 data.allowLowerThan=true; 1774 expr = assignOp(data); 1775 data.allowLowerThan=old; 1776 comments(data); 1777 if (!data.cfml.forwardIfCurrent('#')) 1778 throw new TemplateException( 1779 data.cfml, 1780 "Syntax Error, Invalid Construct "+(data.cfml.length()<30?data.cfml.toString():"")); 1781 comments(data); 1782 return expr; 1783 } 1784 1785 /** 1786 * @param data 1787 * @return parsed Element 1788 * @throws TemplateException 1789 */ 1790 private Expression simple(ExprData data,String[] breakConditions) throws TemplateException { 1791 StringBuffer sb=new StringBuffer(); 1792 Position line = data.cfml.getPosition(); 1793 outer:while(data.cfml.isValidIndex()) { 1794 for(int i=0;i<breakConditions.length;i++){ 1795 if(data.cfml.isCurrent(breakConditions[i]))break outer; 1796 } 1797 1798 //if(data.cfml.isCurrent(' ') || data.cfml.isCurrent('>') || data.cfml.isCurrent("/>")) break; 1799 1800 if(data.cfml.isCurrent('"') || data.cfml.isCurrent('#') || data.cfml.isCurrent('\'')) { 1801 throw new TemplateException(data.cfml,"simple attribute value can't contain ["+data.cfml.getCurrent()+"]"); 1802 } 1803 sb.append(data.cfml.getCurrent()); 1804 data.cfml.next(); 1805 } 1806 comments(data); 1807 1808 return LitString.toExprString(sb.toString(),line,data.cfml.getPosition()); 1809 } 1810 1811 1812 /** 1813 * Liest alle folgenden Komentare ein. 1814 * <br /> 1815 * EBNF:<br /> 1816 * <code>{?-"\n"} "\n";</code> 1817 * @param data 1818 * @throws TemplateException 1819 */ 1820 protected void comments(ExprData data) throws TemplateException { 1821 data.cfml.removeSpace(); 1822 while(comment(data)){data.cfml.removeSpace();} 1823 } 1824 1825 /** 1826 * Liest einen Einzeiligen Kommentar ein. 1827 * <br /> 1828 * EBNF:<br /> 1829 * <code>{?-"\n"} "\n";</code> 1830 * @return bool Wurde ein Kommentar entfernt? 1831 * @throws TemplateException 1832 */ 1833 private boolean comment(ExprData data) throws TemplateException { 1834 if(singleLineComment(data.cfml) || multiLineComment(data) || CFMLTransformer.comment(data.cfml)) return true; 1835 return false; 1836 } 1837 1838 /** 1839 * Liest einen Mehrzeiligen Kommentar ein. 1840 * <br /> 1841 * EBNF:<br /> 1842 * <code>?-"*<!-- -->/";</code> 1843 * @return bool Wurde ein Kommentar entfernt? 1844 * @throws TemplateException 1845 */ 1846 private boolean multiLineComment(ExprData data) throws TemplateException { 1847 CFMLString cfml = data.cfml; 1848 if(!cfml.forwardIfCurrent("/*")) return false; 1849 int pos=cfml.getPos(); 1850 boolean isDocComment=cfml.isCurrent('*'); 1851 while(cfml.isValidIndex()) { 1852 if(cfml.isCurrent("*/")) break; 1853 cfml.next(); 1854 } 1855 if(!cfml.forwardIfCurrent("*/")){ 1856 cfml.setPos(pos); 1857 throw new TemplateException(cfml,"comment is not closed"); 1858 } 1859 if(isDocComment) { 1860 String comment = cfml.substring(pos-2,cfml.getPos()-pos); 1861 data.docComment=docCommentTransformer.transform(comment); 1862 } 1863 return true; 1864 } 1865 1866 1867 1868 /** 1869 * Liest einen Einzeiligen Kommentar ein. 1870 * <br /> 1871 * EBNF:<br /> 1872 * <code>{?-"\n"} "\n";</code> 1873 * @return bool Wurde ein Kommentar entfernt? 1874 */ 1875 private boolean singleLineComment(CFMLString cfml) { 1876 if(!cfml.forwardIfCurrent("//")) return false; 1877 return cfml.nextLine(); 1878 } 1879 1880}