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.script; 020 021import java.util.ArrayList; 022import java.util.HashMap; 023import java.util.Iterator; 024import java.util.Map; 025import java.util.Map.Entry; 026 027import lucee.commons.lang.ExceptionUtil; 028import lucee.commons.lang.StringUtil; 029import lucee.commons.lang.types.RefBoolean; 030import lucee.commons.lang.types.RefBooleanImpl; 031import lucee.runtime.Component; 032import lucee.runtime.PageSource; 033import lucee.runtime.exp.TemplateException; 034import lucee.runtime.functions.system.CFFunction; 035import lucee.runtime.type.util.ArrayUtil; 036import lucee.runtime.type.util.ComponentUtil; 037import lucee.transformer.bytecode.Body; 038import lucee.transformer.bytecode.BodyBase; 039import lucee.transformer.bytecode.BytecodeException; 040import lucee.transformer.bytecode.FunctionBody; 041import lucee.transformer.bytecode.Position; 042import lucee.transformer.bytecode.ScriptBody; 043import lucee.transformer.bytecode.Statement; 044import lucee.transformer.bytecode.cast.CastBoolean; 045import lucee.transformer.bytecode.cast.CastOther; 046import lucee.transformer.bytecode.cast.CastString; 047import lucee.transformer.bytecode.expression.ClosureAsExpression; 048import lucee.transformer.bytecode.expression.ExprBoolean; 049import lucee.transformer.bytecode.expression.Expression; 050import lucee.transformer.bytecode.expression.var.Variable; 051import lucee.transformer.bytecode.literal.LitBoolean; 052import lucee.transformer.bytecode.literal.LitString; 053import lucee.transformer.bytecode.statement.Condition; 054import lucee.transformer.bytecode.statement.Condition.Pair; 055import lucee.transformer.bytecode.statement.DoWhile; 056import lucee.transformer.bytecode.statement.ExpressionAsStatement; 057import lucee.transformer.bytecode.statement.For; 058import lucee.transformer.bytecode.statement.ForEach; 059import lucee.transformer.bytecode.statement.Return; 060import lucee.transformer.bytecode.statement.Switch; 061import lucee.transformer.bytecode.statement.TryCatchFinally; 062import lucee.transformer.bytecode.statement.While; 063import lucee.transformer.bytecode.statement.tag.Attribute; 064import lucee.transformer.bytecode.statement.tag.Tag; 065import lucee.transformer.bytecode.statement.tag.TagOther; 066import lucee.transformer.bytecode.statement.tag.TagParam; 067import lucee.transformer.bytecode.statement.udf.Closure; 068import lucee.transformer.bytecode.statement.udf.Function; 069import lucee.transformer.bytecode.statement.udf.FunctionImpl; 070import lucee.transformer.cfml.evaluator.EvaluatorException; 071import lucee.transformer.cfml.evaluator.impl.ProcessingDirectiveException; 072import lucee.transformer.cfml.expression.AbstrCFMLExprTransformer; 073import lucee.transformer.cfml.tag.CFMLTransformer; 074import lucee.transformer.library.function.FunctionLibFunction; 075import lucee.transformer.library.tag.TagLib; 076import lucee.transformer.library.tag.TagLibException; 077import lucee.transformer.library.tag.TagLibTag; 078import lucee.transformer.library.tag.TagLibTagAttr; 079import lucee.transformer.library.tag.TagLibTagScript; 080import lucee.transformer.util.CFMLString; 081 082 083/** 084 * Innerhalb des Tag script kann in CFML eine eigene Scriptsprache verwendet werden, 085 * welche sich an Javascript orientiert. 086 * Da der data.cfml Transformer keine Spezialfaelle zulaesst, 087 * also Tags einfach anhand der eingegeben TLD einliest und transformiert, 088 * aus diesem Grund wird der Inhalt des Tag script einfach als Zeichenkette eingelesen. 089 * Erst durch den Evaluator (siehe 3.3), der fuer das Tag script definiert ist, 090 * wird der Inhalt des Tag script uebersetzt. 091 * 092 */ 093public abstract class AbstrCFMLScriptTransformer extends AbstrCFMLExprTransformer { 094 095 private static final String[] IGNORE_LIST_COMPONENT = new String[]{ 096 "output","synchronized","extends","implements","displayname","style","persistent","accessors"}; 097 private static final String[] IGNORE_LIST_INTERFACE = new String[]{ 098 "output","extends","displayname","style","persistent","accessors"}; 099 private static final String[] IGNORE_LIST_PROPERTY = new String[]{ 100 "default","fieldtype","name","type","persistent","remotingFetch","column","generator","length", 101 "ormtype","params","unSavedValue","dbdefault","formula","generated","insert","optimisticlock", 102 "update","notnull","precision","scale","unique","uniquekey","source" 103 }; 104 105 106 private static EndCondition SEMI_BLOCK=new EndCondition() { 107 public boolean isEnd(ExprData data) { 108 return data.cfml.isCurrent('{') || data.cfml.isCurrent(';'); 109 } 110 }; 111 private static EndCondition SEMI=new EndCondition() { 112 public boolean isEnd(ExprData data) { 113 return data.cfml.isCurrent(';'); 114 } 115 }; 116 private static EndCondition COMMA_ENDBRACKED=new EndCondition() { 117 public boolean isEnd(ExprData data) { 118 return data.cfml.isCurrent(',') || data.cfml.isCurrent(')'); 119 } 120 }; 121 122 private static EndCondition BRACKED=new EndCondition() { 123 public boolean isEnd(ExprData data) { 124 return data.cfml.isCurrent(')'); 125 } 126 }; 127 128 129 130 131 132 private short ATTR_TYPE_NONE=TagLibTagAttr.SCRIPT_SUPPORT_NONE; 133 private short ATTR_TYPE_OPTIONAL=TagLibTagAttr.SCRIPT_SUPPORT_OPTIONAL; 134 private short ATTR_TYPE_REQUIRED=TagLibTagAttr.SCRIPT_SUPPORT_REQUIRED; 135 136 public static class ComponentTemplateException extends TemplateException { 137 private static final long serialVersionUID = -8103635220891288231L; 138 139 private TemplateException te; 140 141 public ComponentTemplateException(TemplateException te){ 142 super(te.getPageSource(),te.getLine(),0,te.getMessage()); 143 this.te=te; 144 145 } 146 147 /** 148 * @return the te 149 */ 150 public TemplateException getTemplateException() { 151 return te; 152 } 153 } 154 155 private static final Expression NULL = LitString.toExprString("NULL"); 156 private static final Attribute ANY = new Attribute(false,"type",LitString.toExprString("any"),"string"); 157 private static final char NO_ATTR_SEP = 0; 158 159 /** 160 * Liest saemtliche Statements des CFScriptString ein. 161 * <br /> 162 * EBNF:<br /> 163 * <code>{statement spaces};</code> 164 * @return a statement 165 * @throws TemplateException 166 */ 167 protected final Body statements(ExprData data) throws TemplateException { 168 ScriptBody body=new ScriptBody(); 169 170 statements(data,body,true); 171 return body; 172 } 173 174 /** 175 * Liest saemtliche Statements des CFScriptString ein. 176 * <br /> 177 * EBNF:<br /> 178 * <code>{statement spaces};</code> 179 * @param parent uebergeornetes Element dem das Statement zugewiesen wird. 180 * @param isRoot befindet sich der Parser im root des data.cfml Docs 181 * @throws TemplateException 182 */ 183 private final void statements(ExprData data,Body body, boolean isRoot) throws TemplateException { 184 do { 185 if(isRoot && isFinish(data))return; 186 statement(data,body); 187 comments(data); 188 } 189 while(data.cfml.isValidIndex() && !data.cfml.isCurrent('}')); 190 } 191 192 /** 193 * Liest ein einzelnes Statement ein (if,for,while usw.). 194 * <br /> 195 * EBNF:<br /> 196 * <code>";" | "if" spaces "(" ifStatement | "function " funcStatement | "while" spaces "(" whileStatement | 197 "do" spaces "{" doStatement | "for" spaces "(" forStatement | "return" returnStatement | 198 "break" breakStatement | "continue" continueStatement | "/*" comment | expressionStatement;</code> 199 * @param parent uebergeornetes Element dem das Statement zugewiesen wird. 200 * @throws TemplateException 201 */ 202 private final void statement(ExprData data,Body parent) throws TemplateException { 203 statement(data, parent, data.context); 204 } 205 private boolean statement(ExprData data,Body parent,short context) throws TemplateException { 206 short prior=data.context; 207 data.context=context; 208 comments(data); 209 Statement child=null; 210 if(data.cfml.forwardIfCurrent(';')){return true;} 211 else if((child=ifStatement(data))!=null) parent.addStatement(child); 212 else if((child=propertyStatement(data,parent))!=null) parent.addStatement(child); 213 else if((child=paramStatement(data,parent))!=null) parent.addStatement(child); 214 else if((child=funcStatement(data,parent))!=null) parent.addStatement(child); 215 else if((child=whileStatement(data))!=null) parent.addStatement(child); 216 else if((child=doStatement(data))!=null) parent.addStatement(child); 217 else if((child=forStatement(data))!=null) parent.addStatement(child); 218 else if((child=returnStatement(data))!=null) parent.addStatement(child); 219 else if((child=switchStatement(data))!=null) parent.addStatement(child); 220 else if((child=tryStatement(data))!=null) parent.addStatement(child); 221 else if((child=tagStatement(data,parent))!=null) parent.addStatement(child); 222 else if((child=cftagStatement(data,parent))!=null) parent.addStatement(child); 223 else if(block(data,parent)){} 224 else parent.addStatement(expressionStatement(data,parent)); 225 data.docComment=null; 226 data.context=prior; 227 228 return false; 229 } 230 231 /** 232 * Liest ein if Statement ein. 233 * <br /> 234 * EBNF:<br /> 235 * <code>spaces condition spaces ")" spaces block {"else if" spaces "(" elseifStatement spaces } 236 [("else" spaces "(" | "else ") elseStatement spaces];</code> 237 * @return if Statement 238 * @throws TemplateException 239 */ 240 private final Statement ifStatement(ExprData data) throws TemplateException { 241 if(!data.cfml.forwardIfCurrent("if",'(')) return null; 242 243 244 Position line = data.cfml.getPosition(); 245 246 Body body=new BodyBase(); 247 Condition cont=new Condition(condition(data),body,line,null); 248 249 if(!data.cfml.forwardIfCurrent(')')) throw new TemplateException(data.cfml,"if statement must end with a [)]"); 250 // ex block 251 statement(data,body,CTX_IF); 252 // else if 253 comments(data); 254 while(elseifStatement(data,cont)) { 255 comments(data); 256 } 257 // else 258 if(elseStatement(data,cont)) { 259 comments(data); 260 } 261 262 cont.setEnd(data.cfml.getPosition()); 263 return cont; 264 } 265 266 /** 267 * Liest ein else if Statement ein. 268 * <br /> 269 * EBNF:<br /> 270 * <code>spaces condition spaces ")" spaces block;</code> 271 * @return else if Statement 272 * @throws TemplateException 273 */ 274 private final boolean elseifStatement(ExprData data,Condition cont) throws TemplateException { 275 int pos=data.cfml.getPos(); 276 if(!data.cfml.forwardIfCurrent("else")) return false; 277 278 comments(data); 279 if(!data.cfml.forwardIfCurrent("if",'(')) { 280 data.cfml.setPos(pos); 281 return false; 282 } 283 284 Position line = data.cfml.getPosition(); 285 Body body=new BodyBase(); 286 Pair pair = cont.addElseIf(condition(data), body, line,null); 287 288 if(!data.cfml.forwardIfCurrent(')')) 289 throw new TemplateException(data.cfml,"else if statement must end with a [)]"); 290 // ex block 291 statement(data,body,CTX_ELSE_IF); 292 pair.end=data.cfml.getPosition(); 293 return true; 294 } 295 296 /** 297 * Liest ein else Statement ein. 298 * <br /> 299 * EBNF:<br /> 300 * <code>block;</code> 301 * @return else Statement 302 * @throws TemplateException 303 * 304 */ 305 private final boolean elseStatement(ExprData data,Condition cont) throws TemplateException { 306 if(!data.cfml.forwardIfCurrent("else",'{') && !data.cfml.forwardIfCurrent("else ") && !data.cfml.forwardIfCurrent("else",'/')) 307 return false; 308 309 // start ( 310 data.cfml.previous(); 311 // ex block 312 Body body=new BodyBase(); 313 Pair p = cont.setElse(body, data.cfml.getPosition(),null); 314 statement(data,body,CTX_ELSE); 315 p.end=data.cfml.getPosition(); 316 return true; 317 } 318 319 320 private final boolean finallyStatement(ExprData data,TryCatchFinally tcf) throws TemplateException { 321 if(!data.cfml.forwardIfCurrent("finally",'{') && !data.cfml.forwardIfCurrent("finally ") && !data.cfml.forwardIfCurrent("finally",'/')) 322 return false; 323 324 // start ( 325 data.cfml.previous(); 326 // ex block 327 Body body=new BodyBase(); 328 tcf.setFinally(body, data.cfml.getPosition()); 329 statement(data,body,CTX_FINALLY); 330 331 return true; 332 } 333 334 /** 335 * Liest ein while Statement ein. 336 * <br /> 337 * EBNF:<br /> 338 * <code>spaces condition spaces ")" spaces block;</code> 339 * @return while Statement 340 * @throws TemplateException 341 */ 342 private final While whileStatement(ExprData data) throws TemplateException { 343 int pos=data.cfml.getPos(); 344 345 // id 346 String id=variableDec(data, false); 347 if(id==null) { 348 data.cfml.setPos(pos); 349 return null; 350 } 351 if(id.equalsIgnoreCase("while")){ 352 id=null; 353 data.cfml.removeSpace(); 354 if(!data.cfml.forwardIfCurrent('(')){ 355 data.cfml.setPos(pos); 356 return null; 357 } 358 } 359 else { 360 data.cfml.removeSpace(); 361 if(!data.cfml.forwardIfCurrent(':')){ 362 data.cfml.setPos(pos); 363 return null; 364 } 365 data.cfml.removeSpace(); 366 367 if(!data.cfml.forwardIfCurrent("while",'(')){ 368 data.cfml.setPos(pos); 369 return null; 370 } 371 } 372 373 Position line = data.cfml.getPosition(); 374 Body body=new BodyBase(); 375 While whil=new While(condition(data),body,line,null,id); 376 377 if(!data.cfml.forwardIfCurrent(')')) 378 throw new TemplateException(data.cfml,"while statement must end with a [)]"); 379 380 statement(data,body,CTX_WHILE); 381 whil.setEnd(data.cfml.getPosition()); 382 return whil; 383 } 384 385 /** 386 * Liest ein switch Statment ein 387 * @return switch Statement 388 * @throws TemplateException 389 */ 390 private final Switch switchStatement(ExprData data) throws TemplateException { 391 if(!data.cfml.forwardIfCurrent("switch",'(')) 392 return null; 393 394 Position line = data.cfml.getPosition(); 395 396 comments(data); 397 Expression expr = super.expression(data); 398 comments(data); 399 // end ) 400 if(!data.cfml.forwardIfCurrent(')')) 401 throw new TemplateException(data.cfml,"switch statement must end with a [)]"); 402 comments(data); 403 404 if(!data.cfml.forwardIfCurrent('{')) 405 throw new TemplateException(data.cfml,"switch statement must have a starting [{]"); 406 407 Switch swit=new Switch(expr,line,null); 408 409 // cases 410 //Node child=null; 411 comments(data); 412 while(caseStatement(data,swit)) { 413 comments(data); 414 } 415 // default 416 if(defaultStatement(data,swit)) { 417 comments(data); 418 } 419 420 while(caseStatement(data,swit)) { 421 comments(data); 422 } 423 424 425 // } 426 if(!data.cfml.forwardIfCurrent('}')) 427 throw new TemplateException(data.cfml,"invalid construct in switch statement"); 428 swit.setEnd(data.cfml.getPosition()); 429 return swit; 430 } 431 432 /** 433 * Liest ein Case Statement ein 434 * @return case Statement 435 * @throws TemplateException 436 */ 437 private final boolean caseStatement(ExprData data,Switch swit) throws TemplateException { 438 if(!data.cfml.forwardIfCurrentAndNoWordAfter("case")) 439 return false; 440 441 //int line=data.cfml.getLine(); 442 comments(data); 443 Expression expr = super.expression(data); 444 comments(data); 445 446 if(!data.cfml.forwardIfCurrent(':')) 447 throw new TemplateException(data.cfml,"case body must start with [:]"); 448 449 Body body=new BodyBase(); 450 switchBlock(data,body); 451 swit.addCase(expr, body); 452 return true; 453 } 454 455 /** 456 * Liest ein default Statement ein 457 * @return default Statement 458 * @throws TemplateException 459 */ 460 private final boolean defaultStatement(ExprData data,Switch swit) throws TemplateException { 461 if(!data.cfml.forwardIfCurrent("default",':')) 462 return false; 463 464 //int line=data.cfml.getLine(); 465 466 Body body=new BodyBase(); 467 swit.setDefaultCase(body); 468 switchBlock(data,body); 469 return true; 470 } 471 472 /** 473 * Liest ein Switch Block ein 474 * @param block 475 * @throws TemplateException 476 */ 477 private final void switchBlock(ExprData data,Body body) throws TemplateException { 478 while(data.cfml.isValidIndex()) { 479 comments(data); 480 if(data.cfml.isCurrent("case ") || data.cfml.isCurrent("default",':') || data.cfml.isCurrent('}')) 481 return; 482 statement(data,body,CTX_SWITCH); 483 } 484 } 485 486 487 /** 488 * Liest ein do Statement ein. 489 * <br /> 490 * EBNF:<br /> 491 * <code>block spaces "while" spaces "(" spaces condition spaces ")";</code> 492 * @return do Statement 493 * @throws TemplateException 494 */ 495 private final DoWhile doStatement(ExprData data) throws TemplateException { 496 int pos=data.cfml.getPos(); 497 498 // id 499 String id=variableDec(data, false); 500 if(id==null) { 501 data.cfml.setPos(pos); 502 return null; 503 } 504 if(id.equalsIgnoreCase("do")){ 505 id=null; 506 if(!data.cfml.isCurrent('{') && !data.cfml.isCurrent(' ') && !data.cfml.isCurrent('/')) { 507 data.cfml.setPos(pos); 508 return null; 509 } 510 } 511 else { 512 data.cfml.removeSpace(); 513 if(!data.cfml.forwardIfCurrent(':')){ 514 data.cfml.setPos(pos); 515 return null; 516 } 517 data.cfml.removeSpace(); 518 519 if(!data.cfml.forwardIfCurrent("do",'{') && !data.cfml.forwardIfCurrent("do ") && !data.cfml.forwardIfCurrent("do",'/')) { 520 data.cfml.setPos(pos); 521 return null; 522 } 523 data.cfml.previous(); 524 } 525 526 Position line = data.cfml.getPosition(); 527 Body body=new BodyBase(); 528 529 statement(data,body,CTX_DO_WHILE); 530 531 532 comments(data); 533 if(!data.cfml.forwardIfCurrent("while",'(')) 534 throw new TemplateException(data.cfml,"do statement must have a while at the end"); 535 536 DoWhile doWhile=new DoWhile(condition(data),body,line,data.cfml.getPosition(),id); 537 538 if(!data.cfml.forwardIfCurrent(')')) 539 throw new TemplateException(data.cfml,"do statement must end with a [)]"); 540 541 542 return doWhile; 543 } 544 545 /** 546 * Liest ein for Statement ein. 547 * <br /> 548 * EBNF:<br /> 549 * <code>expression spaces ";" spaces condition spaces ";" spaces expression spaces ")" spaces block;</code> 550 * @return for Statement 551 * @throws TemplateException 552 */ 553 private final Statement forStatement(ExprData data) throws TemplateException { 554 555int pos=data.cfml.getPos(); 556 557 // id 558 String id=variableDec(data, false); 559 if(id==null) { 560 data.cfml.setPos(pos); 561 return null; 562 } 563 if(id.equalsIgnoreCase("for")){ 564 id=null; 565 data.cfml.removeSpace(); 566 if(!data.cfml.forwardIfCurrent('(')){ 567 data.cfml.setPos(pos); 568 return null; 569 } 570 } 571 else { 572 data.cfml.removeSpace(); 573 if(!data.cfml.forwardIfCurrent(':')){ 574 data.cfml.setPos(pos); 575 return null; 576 } 577 data.cfml.removeSpace(); 578 579 if(!data.cfml.forwardIfCurrent("for",'(')){ 580 data.cfml.setPos(pos); 581 return null; 582 } 583 } 584 585 586 587 588 //if(!data.cfml.forwardIfCurrent("for",'(')) 589 // return null; 590 591 592 593 Expression left=null; 594 Body body=new BodyBase(); 595 Position line = data.cfml.getPosition(); 596 comments(data); 597 if(!data.cfml.isCurrent(';')) { 598 // left 599 left=expression(data); 600 comments(data); 601 } 602 // middle for 603 if(data.cfml.forwardIfCurrent(';')) { 604 605 Expression cont=null; 606 Expression update=null; 607 // condition 608 comments(data); 609 if(!data.cfml.isCurrent(';')) { 610 cont=condition(data); 611 comments(data); 612 } 613 // middle 614 if(!data.cfml.forwardIfCurrent(';')) 615 throw new TemplateException(data.cfml,"invalid syntax in for statement"); 616 // update 617 comments(data); 618 if(!data.cfml.isCurrent(')')) { 619 update=expression(data); 620 comments(data); 621 } 622 // start ) 623 if(!data.cfml.forwardIfCurrent(')')) 624 throw new TemplateException(data.cfml,"invalid syntax in for statement, for statement must end with a [)]"); 625 // ex block 626 statement(data,body,CTX_FOR); 627 628 return new For(left,cont,update,body,line,data.cfml.getPosition(),id); 629 } 630 // middle foreach 631 else if(data.cfml.forwardIfCurrent("in")) { 632 // condition 633 comments(data); 634 Expression value = expression(data); 635 comments(data); 636 if(!data.cfml.forwardIfCurrent(')')) 637 throw new TemplateException(data.cfml,"invalid syntax in for statement, for statement must end with a [)]"); 638 639 // ex block 640 statement(data,body,CTX_FOR); 641 if(!(left instanceof Variable)) 642 throw new TemplateException(data.cfml,"invalid syntax in for statement, left value is invalid"); 643 644 //if(!(value instanceof Variable || value instanceof LitString)) 645 // throw new TemplateException(data.cfml,"invalid syntax in for statement, right value is invalid"); 646 return new ForEach((Variable)left,value,body,line,data.cfml.getPosition(),id); 647 } 648 else 649 throw new TemplateException(data.cfml,"invalid syntax in for statement"); 650 } 651 652 /** 653 * Liest ein function Statement ein. 654 * <br /> 655 * EBNF:<br /> 656 * <code>identifier spaces "(" spaces identifier spaces {"," spaces identifier spaces} ")" spaces block;</code> 657 * @return function Statement 658 * @throws TemplateException 659 */ 660 private final Function funcStatement(ExprData data,Body parent) throws TemplateException { 661 int pos=data.cfml.getPos(); 662 663 // access modifier 664 String strAccess=variableDec(data, false); 665 if(strAccess==null) { 666 data.cfml.setPos(pos); 667 return null; 668 } 669 670 String rtnType=null; 671 if(strAccess.equalsIgnoreCase("FUNCTION")){ 672 strAccess=null; 673 comments(data); 674 // only happens when return type is function 675 if(data.cfml.forwardIfCurrent("function ")){ 676 rtnType="function"; 677 comments(data); 678 } 679 } 680 else{ 681 comments(data); 682 rtnType=variableDec(data, false); 683 if(rtnType==null){ 684 data.cfml.setPos(pos); 685 return null; 686 } 687 if(rtnType.equalsIgnoreCase("FUNCTION")){ 688 comments(data); 689 // only happens when return type is function 690 if(data.cfml.forwardIfCurrent("function ")){ 691 comments(data); 692 } 693 else rtnType=null; 694 } 695 comments(data); 696 697 if(rtnType!=null && !data.cfml.forwardIfCurrent("function ") && !rtnType.equalsIgnoreCase("FUNCTION")){ 698 data.cfml.setPos(pos); 699 return null; 700 } 701 comments(data); 702 } 703 704 // check access returntype 705 int access=Component.ACCESS_PUBLIC; 706 if(strAccess!=null && rtnType!=null){ 707 access = ComponentUtil.toIntAccess(strAccess,-1); 708 if(access==-1) 709 throw new TemplateException(data.cfml,"invalid access type ["+strAccess+"], access types are remote, public, package, private"); 710 } 711 if(strAccess!=null && rtnType==null){ 712 access = ComponentUtil.toIntAccess(strAccess,-1); 713 if(access==-1){ 714 rtnType=strAccess; 715 strAccess=null; 716 access=Component.ACCESS_PUBLIC; 717 } 718 } 719 720 721 722 Position line = data.cfml.getPosition(); 723 724 comments(data); 725 726 // Name 727 String id=identifier(data,false); 728 729 730 if(id==null) { 731 if(data.cfml.isCurrent('(')) { 732 data.cfml.setPos(pos); 733 return null; 734 } 735 throw new TemplateException(data.cfml,"invalid name for a function"); 736 } 737 738 if(!data.isCFC && !data.isInterface){ 739 FunctionLibFunction flf = getFLF(data,id); 740 if(flf!=null && flf.getClazz()!=CFFunction.class) { 741 PageSource ps = data.cfml.getPageSource(); 742 String path=null; 743 if(ps!=null) { 744 path = ps.getDisplayPath(); 745 path=path.replace('\\', '/'); 746 } 747 if(path==null || path.indexOf("/library/function/")==-1)// TODO make better 748 throw new TemplateException(data.cfml,"The name ["+id+"] is already used by a built in Function"); 749 } 750 } 751 return closurePart(data, id,access,rtnType,line,false); 752 } 753 754 protected final Function closurePart(ExprData data, String id, int access, String rtnType, Position line,boolean closure) throws TemplateException { 755 756 Body body=new FunctionBody(); 757 Function func=closure? 758 new Closure(data.page,id,access,rtnType,body,line,null) 759 :new FunctionImpl(data.page,id,access,rtnType,body,line,null); 760 761 comments(data); 762 if(!data.cfml.forwardIfCurrent('(')) 763 throw new TemplateException(data.cfml,"invalid syntax in function head, missing begin [(]"); 764 765 // arguments 766 LitBoolean passByRef; 767 Expression displayName; 768 Expression hint; 769 Map<String,Attribute> meta; 770 String _name; 771 do { 772 comments(data); 773 // finish 774 if(data.cfml.isCurrent(')'))break; 775 776 // attribute 777 778 // name 779 //String idName=identifier(data,false,true); 780 boolean required=false; 781 782 String idName = variableDec(data, false); 783 // required 784 if("required".equalsIgnoreCase(idName)){ 785 comments(data); 786 String idName2=variableDec(data, false); 787 if(idName2!=null){ 788 idName=idName2; 789 required=true; 790 } 791 } 792 793 794 String typeName="any"; 795 if(idName==null) throw new TemplateException(data.cfml,"invalid argument definition"); 796 comments(data); 797 if(!data.cfml.isCurrent(')') && !data.cfml.isCurrent('=') && !data.cfml.isCurrent(':') && !data.cfml.isCurrent(',')) { 798 typeName=idName.toLowerCase(); 799 idName=identifier(data,false); // MUST was upper case before, is this a problem? 800 } 801 else if(idName.indexOf('.')!=-1 || idName.indexOf('[')!=-1) { 802 throw new TemplateException(data.cfml,"invalid argument name ["+idName+"] definition"); 803 } 804 805 comments(data); 806 Expression defaultValue; 807 if(data.cfml.isCurrent('=') || data.cfml.isCurrent(':')) { 808 data.cfml.next(); 809 comments(data); 810 defaultValue=expression(data); 811 } 812 else defaultValue=null; 813 814 // assign meta data defined in doc comment 815 passByRef = LitBoolean.TRUE; 816 displayName=LitString.EMPTY; 817 hint=LitString.EMPTY; 818 meta=null; 819 if(data.docComment!=null){ 820 Map<String, Attribute> params = data.docComment.getParams(); 821 Attribute[] attrs = params.values().toArray(new Attribute[params.size()]); 822 Attribute attr; 823 String name; 824 825 for(int i=0;i<attrs.length;i++){ 826 attr=attrs[i]; 827 name=attr.getName(); 828 // hint 829 if(idName.equalsIgnoreCase(name) || name.equalsIgnoreCase(idName+".hint")) { 830 hint=CastString.toExprString(attr.getValue()); 831 params.remove(name); 832 } 833 //meta 834 if(StringUtil.startsWithIgnoreCase(name, idName+".")) { 835 if(name.length()>idName.length()+1){ 836 if(meta==null) meta=new HashMap<String, Attribute>(); 837 _name=name.substring(idName.length()+1); 838 meta.put(_name, new Attribute(attr.isDynamicType(), _name,attr.getValue(), attr.getType())); 839 } 840 params.remove(name); 841 } 842 } 843 844 } 845 846 // argument attributes 847 Attribute[] _attrs = attributes(null,null,data,COMMA_ENDBRACKED,LitString.EMPTY,Boolean.TRUE,null,false,NO_ATTR_SEP,true); 848 Attribute _attr; 849 if(!ArrayUtil.isEmpty(_attrs)){ 850 if(meta==null) meta=new HashMap<String, Attribute>(); 851 for(int i=0;i<_attrs.length;i++){ 852 _attr=_attrs[i]; 853 meta.put(_attr.getName(), _attr); 854 } 855 } 856 857 func.addArgument( 858 LitString.toExprString(idName), 859 LitString.toExprString(typeName), 860 LitBoolean.toExprBoolean(required), 861 defaultValue,passByRef,displayName,hint,meta); 862 863 comments(data); 864 } 865 while(data.cfml.forwardIfCurrent(',')); 866 867 868 // end ) 869 comments(data); 870 if(!data.cfml.forwardIfCurrent(')')) 871 throw new TemplateException(data.cfml,"invalid syntax in function head, missing ending [)]"); 872 873 //TagLibTag tlt = CFMLTransformer.getTLT(data.cfml,"function"); 874 875 // doc comment 876 if(data.docComment!=null){ 877 func.setHint(data.docComment.getHint()); 878 879 880 // params 881 /*Map<String, Attribute> params = data.docComment.getParams(); 882 Iterator<Attribute> it = params.values().iterator(); 883 Attribute attr; 884 String name; 885 while(it.hasNext()){ 886 attr=it.next(); 887 name=attr.getName(); 888 }*/ 889 890 func.setMetaData(data.docComment.getParams()); 891 data.docComment=null; 892 } 893 894 comments(data); 895 896 // attributes 897 Attribute[] attrs = attributes(null,null,data,SEMI_BLOCK,LitString.EMPTY,Boolean.TRUE,null,false,NO_ATTR_SEP,true); 898 for(int i=0;i<attrs.length;i++){ 899 func.addAttribute(attrs[i]); 900 } 901 902 // body 903 boolean oldInsideFunction=data.insideFunction; 904 data.insideFunction=true; 905 try { 906 // ex block 907 statement(data,body,CTX_FUNCTION); 908 } 909 finally{ 910 data.insideFunction=oldInsideFunction; 911 } 912 func.setEnd(data.cfml.getPosition()); 913 914 if(closure) comments(data); 915 916 return func; 917 } 918 919 920 921 private Statement tagStatement(ExprData data, Body parent) throws TemplateException { 922 Statement child; 923 924 for(int i=0;i<data.scriptTags.length;i++){ 925 // single 926 if(data.scriptTags[i].getScript().getType()==TagLibTagScript.TYPE_SINGLE) { 927 if((child=_singleAttrStatement(parent,data,data.scriptTags[i]))!=null)return child; 928 } 929 // multiple 930 else {//if(tags[i].getScript().getType()==TagLibTagScript.TYPE_MULTIPLE) { 931 if((child=_multiAttrStatement(parent,data,data.scriptTags[i]))!=null)return child; 932 } 933 } 934 return null; 935 } 936 937 938 939 940 941 private final Statement _multiAttrStatement(Body parent, ExprData data,TagLibTag tlt) throws TemplateException { 942 int pos = data.cfml.getPos(); 943 try { 944 return __multiAttrStatement(parent,data,tlt); 945 } 946 catch (ProcessingDirectiveException e) { 947 throw e; 948 } 949 catch (TemplateException e) { 950 try { 951 data.cfml.setPos(pos); 952 return expressionStatement(data,parent); 953 } catch (TemplateException e1) { 954 if(tlt.getScript().getContext()==CTX_CFC) throw new ComponentTemplateException(e); 955 throw e; 956 } 957 } 958 } 959 960 private final Tag __multiAttrStatement(Body parent, ExprData data,TagLibTag tlt) throws TemplateException { 961 if(data.ep==null) return null; 962 String type=tlt.getName(); 963 if(data.cfml.forwardIfCurrent(type)) { 964 boolean isValid=(data.cfml.isCurrent(' ') || (tlt.getHasBody() && data.cfml.isCurrent('{'))); 965 if(!isValid){ 966 data.cfml.setPos(data.cfml.getPos()-type.length()); 967 return null; 968 } 969 } 970 else return null; 971 Position line = data.cfml.getPosition(); 972 973 TagLibTagScript script = tlt.getScript(); 974 //TagLibTag tlt = CFMLTransformer.getTLT(data.cfml,type); 975 if(script.getContext()==CTX_CFC)data.isCFC=true; 976 else if(script.getContext()==CTX_INTERFACE)data.isInterface=true; 977 //Tag tag=new TagComponent(line); 978 Tag tag=getTag(data,parent,tlt, line,null); 979 tag.setTagLibTag(tlt); 980 tag.setScriptBase(true); 981 982 // add component meta data 983 if(data.isCFC) { 984 addMetaData(data,tag,IGNORE_LIST_COMPONENT); 985 } 986 if(data.isInterface) { 987 addMetaData(data,tag,IGNORE_LIST_INTERFACE); 988 } 989 //EvaluatorPool.getPool(); 990 comments(data); 991 992 // attributes 993 //attributes(func,data); 994 Attribute[] attrs = attributes(tag,tlt,data,SEMI_BLOCK,LitString.EMPTY,script.getRtexpr()?Boolean.TRUE:Boolean.FALSE,null,false,',',false); 995 996 for(int i=0;i<attrs.length;i++){ 997 tag.addAttribute(attrs[i]); 998 } 999 1000 comments(data); 1001 1002 // body 1003 if(tlt.getHasBody()){ 1004 Body body=new BodyBase(); 1005 boolean wasSemiColon=statement(data,body,script.getContext()); 1006 if(!wasSemiColon || !tlt.isBodyFree() || body.hasStatements()) 1007 tag.setBody(body); 1008 1009 1010 1011 } 1012 else checkSemiColonLineFeed(data,true,true,true); 1013 1014 tag.setEnd(data.cfml.getPosition()); 1015 eval(tlt,data,tag); 1016 return tag; 1017 } 1018 1019 private Statement cftagStatement(ExprData data, Body parent) throws TemplateException { 1020 if(data.ep==null) return null; // that is because cfloop-contition evaluator does not pass this 1021 1022 int start = data.cfml.getPos(); 1023 1024 // namespace and separator 1025 TagLib tagLib=CFMLTransformer.nameSpace(data); 1026 if(tagLib==null) return null; 1027 1028 //print.e("namespace:"+tagLib.getNameSpaceAndSeparator()); 1029 1030 // get the name of the tag 1031 String id = CFMLTransformer.identifier(data.cfml, false,true); 1032 1033 1034 if(id==null) { 1035 data.cfml.setPos(start); 1036 return null; 1037 } 1038 1039 id=id.toLowerCase(); 1040 String appendix=null; 1041 TagLibTag tlt=tagLib.getTag(id); 1042 1043 //print.e("tlt:"+tlt); 1044 1045 1046 // get taglib 1047 if(tlt==null) { 1048 tlt=tagLib.getAppendixTag(id); 1049 //print.e("appendix:"+tlt); 1050 1051 if(tlt==null) { 1052 //if(tagLib.getIgnoreUnknowTags()){ if we do this a expression like the following no longer work cfwhatever=1; 1053 data.cfml.setPos(start); 1054 return null; 1055 //} 1056 //throw new TemplateException(data.cfml,"undefined tag ["+tagLib.getNameSpaceAndSeparator()+id+"]"); 1057 } 1058 appendix=StringUtil.removeStartingIgnoreCase(id,tlt.getName()); 1059 } 1060 if(tagLib.isCore() && tlt.getScript()==null) { 1061 data.cfml.setPos(start); 1062 return null; 1063 } 1064 1065 // check for opening bracked or closing semicolon 1066 comments(data); 1067 boolean noAttrs=false; 1068 if(!data.cfml.forwardIfCurrent('(')){ 1069 if(checkSemiColonLineFeed(data, false, false, false)){ 1070 noAttrs=true; 1071 } 1072 else { 1073 data.cfml.setPos(start); 1074 return null; 1075 } 1076 } 1077 1078 Position line = data.cfml.getPosition(); 1079 1080 // script specific behavior 1081 short context=CTX_OTHER; 1082 Boolean allowExpression=Boolean.TRUE; 1083 { 1084 1085 TagLibTagScript script = tlt.getScript(); 1086 if(script!=null) { 1087 context=script.getContext(); 1088 // always true for this tags allowExpression=script.getRtexpr()?Boolean.TRUE:Boolean.FALSE; 1089 if(context==CTX_CFC)data.isCFC=true; 1090 else if(context==CTX_INTERFACE)data.isInterface=true; 1091 } 1092 } 1093 1094 Tag tag=getTag(data,parent,tlt, line,null); 1095 if(appendix!=null) { 1096 tag.setAppendix(appendix); 1097 tag.setFullname(tlt.getFullName().concat(appendix)); 1098 } 1099 else { 1100 tag.setFullname(tlt.getFullName()); 1101 } 1102 1103 1104 tag.setTagLibTag(tlt); 1105 tag.setScriptBase(true); 1106 1107 // add component meta data 1108 if(data.isCFC) { 1109 addMetaData(data,tag,IGNORE_LIST_COMPONENT); 1110 } 1111 if(data.isInterface) { 1112 addMetaData(data,tag,IGNORE_LIST_INTERFACE); 1113 } 1114 comments(data); 1115 1116 // attributes 1117 Attribute[] attrs = noAttrs?new Attribute[0] : attributes(tag,tlt,data,BRACKED,LitString.EMPTY,allowExpression,null,false,',',true); 1118 data.cfml.forwardIfCurrent(')'); 1119 1120 for(int i=0;i<attrs.length;i++){ 1121 tag.addAttribute(attrs[i]); 1122 } 1123 1124 comments(data); 1125 1126 // body 1127 if(tlt.getHasBody()){ 1128 Body body=new BodyBase(); 1129 boolean wasSemiColon=statement(data,body,context); 1130 if(!wasSemiColon || !tlt.isBodyFree() || body.hasStatements()) 1131 tag.setBody(body); 1132 1133 1134 1135 } 1136 else checkSemiColonLineFeed(data,true,true,true); 1137 1138 1139 tag.setEnd(data.cfml.getPosition()); 1140 eval(tlt,data,tag); 1141 return tag; 1142 } 1143 1144 1145 1146 private final void addMetaData(ExprData data, Tag tag, String[] ignoreList) { 1147 if(data.docComment==null) return; 1148 1149 1150 tag.addMetaData(data.docComment.getHintAsAttribute()); 1151 1152 Map<String, Attribute> params = data.docComment.getParams(); 1153 Iterator<Attribute> it = params.values().iterator(); 1154 Attribute attr; 1155 outer:while(it.hasNext()){ 1156 attr = it.next(); 1157 // ignore list 1158 if(!ArrayUtil.isEmpty(ignoreList)) { 1159 for(int i=0;i<ignoreList.length;i++){ 1160 if(ignoreList[i].equalsIgnoreCase(attr.getName())) continue outer; 1161 } 1162 } 1163 tag.addMetaData(attr); 1164 } 1165 data.docComment=null; 1166 } 1167 1168 private final Statement propertyStatement(ExprData data,Body parent) throws TemplateException { 1169 int pos = data.cfml.getPos(); 1170 try { 1171 return _propertyStatement(data, parent); 1172 } catch (TemplateException e) { 1173 try { 1174 data.cfml.setPos(pos); 1175 return expressionStatement(data,parent); 1176 } catch (TemplateException e1) { 1177 throw e; 1178 } 1179 } 1180 } 1181 1182 private final Tag _propertyStatement(ExprData data,Body parent) throws TemplateException { 1183 if(data.context!=CTX_CFC || !data.cfml.forwardIfCurrent("property ")) 1184 return null; 1185 Position line = data.cfml.getPosition(); 1186 1187 TagLibTag tlt = CFMLTransformer.getTLT(data.cfml,"property"); 1188 Tag property=new TagOther(line,null); 1189 addMetaData(data, property,IGNORE_LIST_PROPERTY); 1190 1191 1192 boolean hasName=false,hasType=false; 1193 1194 // TODO allow the following pattern property "a.b.C" d; 1195 //Expression t = string(data); 1196 // print.o("name:"+t.getClass().getName()); 1197 1198 int pos = data.cfml.getPos(); 1199 String tmp=variableDec(data, true); 1200 if(!StringUtil.isEmpty(tmp)) { 1201 if(tmp.indexOf('.')!=-1) { 1202 property.addAttribute(new Attribute(false,"type",LitString.toExprString(tmp),"string")); 1203 hasType=true; 1204 } 1205 else { 1206 data.cfml.setPos(pos); 1207 } 1208 } 1209 else data.cfml.setPos(pos); 1210 1211 1212 1213 // folgend wird tlt extra nicht uebergeben, sonst findet pruefung statt 1214 Attribute[] attrs = attributes(property,tlt,data,SEMI, NULL,Boolean.FALSE,"name",true,NO_ATTR_SEP,false); 1215 1216 checkSemiColonLineFeed(data,true,true,false); 1217 1218 property.setTagLibTag(tlt); 1219 property.setScriptBase(true); 1220 1221 1222 Attribute attr; 1223 1224 // first fill all regular attribute -> name="value" 1225 for(int i=attrs.length-1;i>=0;i--){ 1226 attr=attrs[i]; 1227 if(!attr.getValue().equals(NULL)){ 1228 if(attr.getName().equalsIgnoreCase("name")){ 1229 hasName=true; 1230 //attr=new Attribute(attr.isDynamicType(),attr.getName(),CastString.toExprString(attr.getValue()),"string"); 1231 } 1232 else if(attr.getName().equalsIgnoreCase("type")){ 1233 hasType=true; 1234 //attr=new Attribute(attr.isDynamicType(),attr.getName(),CastString.toExprString(attr.getValue()),"string"); 1235 } 1236 property.addAttribute(attr); 1237 } 1238 } 1239 1240 // now fill name named attributes -> attr1 attr2 1241 1242 String first=null,second=null; 1243 for(int i=0;i<attrs.length;i++){ 1244 attr=attrs[i]; 1245 1246 if(attr.getValue().equals(NULL)){ 1247 // type 1248 if(first==null && ((!hasName && !hasType) || !hasName)){ 1249 first=attr.getNameOC(); 1250 } 1251 // name 1252 else if(second==null && !hasName && !hasType){ 1253 second=attr.getNameOC(); 1254 } 1255 // attr with no value 1256 else { 1257 attr=new Attribute(true,attr.getName(),LitString.EMPTY,"string"); 1258 property.addAttribute(attr); 1259 } 1260 } 1261 } 1262 1263 1264 1265 if(first!=null) { 1266 hasName=true; 1267 if(second!=null){ 1268 hasType=true; 1269 property.addAttribute(new Attribute(false,"name",LitString.toExprString(second),"string")); 1270 property.addAttribute(new Attribute(false,"type",LitString.toExprString(first),"string")); 1271 } 1272 else { 1273 property.addAttribute(new Attribute(false,"name",LitString.toExprString(first),"string")); 1274 } 1275 } 1276 1277 if(!hasType) 1278 property.addAttribute(ANY); 1279 1280 if(!hasName) 1281 throw new TemplateException(data.cfml,"missing name declaration for property"); 1282 1283 /*Tag property=new TagBase(line); 1284 property.setTagLibTag(tlt); 1285 property.addAttribute(new Attribute(false,"name",LitString.toExprString(name),"string")); 1286 property.addAttribute(new Attribute(false,"type",LitString.toExprString(type),"string")); 1287 */ 1288 property.setEnd(data.cfml.getPosition()); 1289 1290 return property; 1291 } 1292 1293 public Statement paramStatement(ExprData data,Body parent) throws TemplateException { 1294 int pos = data.cfml.getPos(); 1295 try { 1296 return _paramStatement(data, parent); 1297 } catch (TemplateException e) { 1298 try { 1299 data.cfml.setPos(pos); 1300 return expressionStatement(data,parent); 1301 } catch (TemplateException e1) { 1302 throw e; 1303 } 1304 } 1305 } 1306 1307 private Tag _paramStatement(ExprData data,Body parent) throws TemplateException { 1308 if(!data.cfml.forwardIfCurrent("param ")) 1309 return null; 1310 Position line = data.cfml.getPosition(); 1311 1312 TagLibTag tlt = CFMLTransformer.getTLT(data.cfml,"param"); 1313 TagParam param=new TagParam(line,null); 1314 1315 // type 1316 boolean hasType=false; 1317 boolean hasName=false; 1318 int pos = data.cfml.getPos(); 1319 1320 1321 // first 2 arguments can be type/name directly 1322 String tmp=variableDec(data, true); 1323 do { 1324 if(!StringUtil.isEmpty(tmp)) { 1325 TagLibTagAttr attr = tlt.getAttribute(tmp.toLowerCase(),true); 1326 // name is not a defined attribute 1327 1328 if(attr==null) { 1329 comments(data); 1330 // it could be a name followed by default value 1331 if(data.cfml.forwardIfCurrent('=')) { 1332 comments(data); 1333 Expression v=attributeValue(data,true); 1334 param.addAttribute(new Attribute(false,"name",LitString.toExprString(tmp),"string")); 1335 param.addAttribute(new Attribute(false,"default",v,"string")); 1336 hasName=true; 1337 break; // if we had a value this was already name 1338 } 1339 // can be type or name 1340 int pos2 = data.cfml.getPos(); 1341 1342 // first could be type, followed by name 1343 comments(data); 1344 String tmp2=variableDec(data, true); 1345 1346 if(!StringUtil.isEmpty(tmp2)) { 1347 attr = tlt.getAttribute(tmp2.toLowerCase(),true); 1348 if(attr==null) { 1349 param.addAttribute(new Attribute(false,"name",LitString.toExprString(tmp2),"string")); 1350 param.addAttribute(new Attribute(false,"type",LitString.toExprString(tmp),"string")); 1351 1352 if(data.cfml.forwardIfCurrent('=')) { 1353 Expression v=attributeValue(data,true); 1354 param.addAttribute(new Attribute(false,"default",v,"string")); 1355 } 1356 1357 hasName=true; 1358 hasType=true; 1359 break; 1360 } 1361 } 1362 param.addAttribute(new Attribute(false,"name",LitString.toExprString(tmp),"string")); 1363 data.cfml.setPos(pos2); 1364 hasName=true; 1365 } 1366 else data.cfml.setPos(pos); 1367 } 1368 else data.cfml.setPos(pos); 1369 }while(false); 1370 1371 1372 // folgend wird tlt extra nicht uebergeben, sonst findet pruefung statt 1373 Attribute[] attrs = attributes(param,tlt,data,SEMI, NULL,Boolean.TRUE,"name",true,',',false); 1374 checkSemiColonLineFeed(data,true,true,true); 1375 1376 param.setTagLibTag(tlt); 1377 param.setScriptBase(true); 1378 1379 1380 Attribute attr; 1381 1382 // first fill all regular attribute -> name="value" 1383 boolean hasDynamic=false; 1384 for(int i=attrs.length-1;i>=0;i--){ 1385 attr=attrs[i]; 1386 if(!attr.getValue().equals(NULL)){ 1387 if(attr.getName().equalsIgnoreCase("name")){ 1388 hasName=true; 1389 param.addAttribute(attr); 1390 } 1391 else if(attr.getName().equalsIgnoreCase("type")){ 1392 hasType=true; 1393 param.addAttribute(attr); 1394 } 1395 else if(attr.isDynamicType()){ 1396 hasName=true; 1397 if(hasDynamic) throw attrNotSupported(data.cfml,tlt,attr.getName()); 1398 hasDynamic=true; 1399 param.addAttribute(new Attribute(false,"name",LitString.toExprString(attr.getName()),"string")); 1400 param.addAttribute(new Attribute(false,"default",attr.getValue(),"any")); 1401 } 1402 else 1403 param.addAttribute(attr); 1404 } 1405 } 1406 1407 // now fill name named attributes -> attr1 attr2 1408 String first=null,second=null; 1409 for(int i=0;i<attrs.length;i++){ 1410 attr=attrs[i]; 1411 1412 if(attr.getValue().equals(NULL)){ 1413 // type 1414 if(first==null && (!hasName || !hasType)){ 1415 first=attr.getName(); 1416 } 1417 // name 1418 else if(second==null && !hasName && !hasType){ 1419 second=attr.getName(); 1420 } 1421 // attr with no value 1422 else { 1423 attr=new Attribute(true,attr.getName(),LitString.EMPTY,"string"); 1424 param.addAttribute(attr); 1425 } 1426 } 1427 } 1428 1429 1430 if(first!=null) { 1431 if(second!=null){ 1432 hasName=true; 1433 hasType=true; 1434 if(hasDynamic) throw attrNotSupported(data.cfml,tlt,first); 1435 hasDynamic=true; 1436 param.addAttribute(new Attribute(false,"name",LitString.toExprString(second),"string")); 1437 param.addAttribute(new Attribute(false,"type",LitString.toExprString(first),"string")); 1438 } 1439 else { 1440 param.addAttribute(new Attribute(false,hasName?"type":"name",LitString.toExprString(first),"string")); 1441 hasName=true; 1442 } 1443 } 1444 1445 //if(!hasType) 1446 // param.addAttribute(ANY); 1447 1448 if(!hasName) { 1449 throw new TemplateException(data.cfml,"missing name declaration for param"); 1450 } 1451 param.setEnd(data.cfml.getPosition()); 1452 return param; 1453 } 1454 1455 1456 private TemplateException attrNotSupported(CFMLString cfml, TagLibTag tag, String id) { 1457 String names=tag.getAttributeNames(); 1458 if(StringUtil.isEmpty(names)) 1459 return new TemplateException(cfml,"Attribute "+id+" is not allowed for tag "+tag.getFullName()); 1460 1461 return new TemplateException(cfml, 1462 "Attribute "+id+" is not allowed for statement "+tag.getName(), 1463 "valid attribute names are ["+names+"]"); 1464 } 1465 1466 1467 1468 private final String variableDec(ExprData data,boolean firstCanBeNumber) { 1469 1470 String id=identifier(data, firstCanBeNumber); 1471 if(id==null) return null; 1472 1473 StringBuilder rtn=new StringBuilder(id); 1474 data.cfml.removeSpace(); 1475 1476 while(data.cfml.forwardIfCurrent('.')){ 1477 data.cfml.removeSpace(); 1478 rtn.append('.'); 1479 id=identifier(data, firstCanBeNumber); 1480 if(id==null)return null; 1481 rtn.append(id); 1482 data.cfml.removeSpace(); 1483 } 1484 1485 while(data.cfml.forwardIfCurrent("[]")){ 1486 data.cfml.removeSpace(); 1487 rtn.append("[]"); 1488 } 1489 1490 data.cfml.revertRemoveSpace(); 1491 1492 1493 return rtn.toString(); 1494 } 1495 1496 /** 1497 * Liest ein return Statement ein. 1498 * <br /> 1499 * EBNF:<br /> 1500 * <code>spaces expressionStatement spaces;</code> 1501 * @return return Statement 1502 * @throws TemplateException 1503 */ 1504 private final Return returnStatement(ExprData data) throws TemplateException { 1505 if(!data.cfml.forwardIfCurrentAndNoVarExt("return")) return null; 1506 1507 Position line = data.cfml.getPosition(); 1508 Return rtn; 1509 1510 comments(data); 1511 if(checkSemiColonLineFeed(data, false,false,false)) rtn=new Return(line,data.cfml.getPosition()); 1512 else { 1513 Expression expr = expression(data); 1514 checkSemiColonLineFeed(data, true,true,false); 1515 rtn=new Return(expr,line,data.cfml.getPosition()); 1516 } 1517 comments(data); 1518 1519 return rtn; 1520 } 1521 1522 1523 private final Statement _singleAttrStatement(Body parent, ExprData data, TagLibTag tlt) throws TemplateException { 1524 int pos = data.cfml.getPos(); 1525 try { 1526 return __singleAttrStatement(parent,data,tlt, false); 1527 } 1528 catch (ProcessingDirectiveException e) { 1529 throw e; 1530 } 1531 catch (TemplateException e) { 1532 data.cfml.setPos(pos); 1533 try { 1534 return expressionStatement(data,parent); 1535 } catch (TemplateException e1) { 1536 throw e; 1537 } 1538 } 1539 } 1540 1541 private final Statement __singleAttrStatement(Body parent, ExprData data, TagLibTag tlt, boolean allowTwiceAttr) throws TemplateException { 1542 String tagName = tlt.getName(); 1543 if(data.cfml.forwardIfCurrent(tagName)){ 1544 if(!data.cfml.isCurrent(' ') && !data.cfml.isCurrent(';')){ 1545 data.cfml.setPos(data.cfml.getPos()-tagName.length()); 1546 return null; 1547 } 1548 } 1549 else return null; 1550 1551 1552 int pos=data.cfml.getPos()-tagName.length(); 1553 Position line = data.cfml.getPosition(); 1554 //TagLibTag tlt = CFMLTransformer.getTLT(data.cfml,tagName.equals("pageencoding")?"processingdirective":tagName); 1555 1556 Tag tag=getTag(data,parent,tlt,line,null); 1557 tag.setScriptBase(true); 1558 tag.setTagLibTag(tlt); 1559 1560 comments(data); 1561 1562 // attribute 1563 TagLibTagAttr attr = tlt.getScript().getSingleAttr(); 1564 String attrName=null; 1565 Expression attrValue=null; 1566 short attrType=ATTR_TYPE_NONE; 1567 if(attr!=null){ 1568 attrType = attr.getScriptSupport(); 1569 char c = data.cfml.getCurrent(); 1570 if(ATTR_TYPE_REQUIRED==attrType || ((!data.cfml.isCurrent(';')) && ATTR_TYPE_OPTIONAL==attrType)) { 1571 if(data.cfml.isCurrent('{')) {// this can be only a json string 1572 int p=data.cfml.getPos(); 1573 try{ 1574 attrValue=isSimpleValue(attr.getType())?null:json(data,JSON_STRUCT,'{','}'); 1575 } 1576 catch(Throwable t){ 1577 ExceptionUtil.rethrowIfNecessary(t); 1578 data.cfml.setPos(p); 1579 } 1580 } 1581 else attrValue =attributeValue(data, tlt.getScript().getRtexpr()); 1582 if(attrValue!=null && isOperator(c)) { 1583 data.cfml.setPos(pos); 1584 return null; 1585 } 1586 } 1587 } 1588 1589 if(attrValue!=null){ 1590 attrName=attr.getName(); 1591 TagLibTagAttr tlta = tlt.getAttribute(attr.getName(),true); 1592 tag.addAttribute(new Attribute(false,attrName,CastOther.toExpression(attrValue,tlta.getType()),tlta.getType())); 1593 } 1594 else if(ATTR_TYPE_REQUIRED==attrType){ 1595 data.cfml.setPos(pos); 1596 return null; 1597 } 1598 1599 // body 1600 if(tlt.getHasBody()){ 1601 Body body=new BodyBase(); 1602 boolean wasSemiColon=statement(data,body,tlt.getScript().getContext()); 1603 if(!wasSemiColon || !tlt.isBodyFree() || body.hasStatements()) 1604 tag.setBody(body); 1605 } 1606 else checkSemiColonLineFeed(data,true,true,true); 1607 1608 1609 if(!StringUtil.isEmpty(tlt.getTteClassName()))data.ep.add(tlt, tag, data.flibs, data.cfml); 1610 1611 if(!StringUtil.isEmpty(attrName))validateAttributeName(attrName, data.cfml, new ArrayList<String>(), tlt, new RefBooleanImpl(false), new StringBuffer(), allowTwiceAttr); 1612 tag.setEnd(data.cfml.getPosition()); 1613 eval(tlt,data,tag); 1614 return tag; 1615 } 1616 1617 private boolean isSimpleValue(String type) { 1618 return type.equalsIgnoreCase("string") || type.equalsIgnoreCase("boolean") || type.equalsIgnoreCase("number") || type.equalsIgnoreCase("numeric"); 1619 } 1620 1621 private boolean isOperator(char c) { 1622 return c=='=' || c=='+' || c=='-'; 1623 } 1624 1625 /*protected Statement __singleAttrStatement(Body parent, Data data, String tagName,String attrName,int attrType, boolean allowExpression, boolean allowTwiceAttr) throws TemplateException { 1626 1627 if(data.cfml.forwardIfCurrent(tagName)){ 1628 if(!data.cfml.isCurrent(' ') && !data.cfml.isCurrent(';')){ 1629 data.cfml.setPos(data.cfml.getPos()-tagName.length()); 1630 return null; 1631 } 1632 } 1633 else return null; 1634 1635 1636 int pos=data.cfml.getPos()-tagName.length(); 1637 int line=data.cfml.getLine(); 1638 TagLibTag tlt = CFMLTransformer.getTLT(data.cfml,tagName.equals("pageencoding")?"processingdirective":tagName); 1639 1640 Tag tag=getTag(parent,tlt,line); 1641 tag.setScriptBase(true); 1642 tag.setTagLibTag(tlt); 1643 1644 comments(data); 1645 1646 // attribute 1647 Expression attrValue=null; 1648 if(ATTR_TYPE_REQUIRED==attrType || (!data.cfml.isCurrent(';') && ATTR_TYPE_OPTIONAL==attrType)) 1649 attrValue =attributeValue(data, allowExpression); 1650 //allowExpression?super.expression(data):string(data); 1651 1652 if(attrValue!=null){ 1653 TagLibTagAttr tlta = tlt.getAttribute(attrName); 1654 tag.addAttribute(new Attribute(false,attrName,Cast.toExpression(attrValue,tlta.getType()),tlta.getType())); 1655 } 1656 else if(ATTR_TYPE_REQUIRED==attrType){ 1657 data.cfml.setPos(pos); 1658 return null; 1659 } 1660 1661 checkSemiColonLineFeed(data,true); 1662 if(!StringUtil.isEmpty(tlt.getTteClassName()))data.ep.add(tlt, tag, data.fld, data.cfml); 1663 1664 if(!StringUtil.isEmpty(attrName))validateAttributeName(attrName, data.cfml, new ArrayList<String>(), tlt, new RefBooleanImpl(false), new StringBuffer(), allowTwiceAttr); 1665 1666 eval(tlt,data,tag); 1667 return tag; 1668 }*/ 1669 1670 1671 1672 private final void eval(TagLibTag tlt, lucee.transformer.cfml.expression.CFMLExprTransformer.ExprData data, Tag tag) throws TemplateException { 1673 if(!StringUtil.isEmpty(tlt.getTteClassName())){ 1674 try { 1675 tlt.getEvaluator().execute(data.config, tag, tlt,data.flibs, data); 1676 } catch (EvaluatorException e) { 1677 throw new TemplateException(e.getMessage()); 1678 } 1679 data.ep.add(tlt, tag, data.flibs, data.cfml); 1680 } 1681 } 1682 1683 private final Tag getTag(ExprData data,Body parent, TagLibTag tlt, Position start,Position end) throws TemplateException { 1684 try { 1685 Tag tag = tlt.getTag(start, end); 1686 tag.setParent(parent); 1687 return tag; 1688 } catch (TagLibException e) { 1689 throw new TemplateException(data.cfml,e); 1690 } 1691 /*if(StringUtil.isEmpty(tlt.getTttClassName()))tag= new TagBase(line); 1692 else { 1693 try { 1694 Class<Tag> clazz = ClassUtil.loadClass(tlt.getTttClassName()); 1695 Constructor<Tag> constr = clazz.getConstructor(new Class[]{Position.class}); 1696 tag = constr.newInstance(new Object[]{line}); 1697 1698 } 1699 catch (Exception e) { 1700 e.printStackTrace(); 1701 tag= new TagBase(line); 1702 } 1703 }*/ 1704 1705 } 1706 1707 1708 1709 /** 1710 * List mithilfe des data.cfmlExprTransformer einen Ausruck ein. 1711 * <br /> 1712 * EBNF:<br /> 1713 * <code>expression ";";</code> 1714 * @param parent 1715 * @return Ausdruck 1716 * @throws TemplateException 1717 */ 1718 private Statement expressionStatement(ExprData data, Body parent) throws TemplateException { 1719 Expression expr=expression(data); 1720 checkSemiColonLineFeed(data,true,true,false); 1721 if(expr instanceof ClosureAsExpression) 1722 return ((ClosureAsExpression)expr).getClosure(); 1723 1724 return new ExpressionAsStatement(expr); 1725 } 1726 1727 private final boolean checkSemiColonLineFeed(ExprData data,boolean throwError, boolean checkNLBefore,boolean allowEmptyCurlyBracked) throws TemplateException { 1728 comments(data); 1729 if(!data.cfml.forwardIfCurrent(';')){ 1730 1731 // curly brackets? 1732 if(allowEmptyCurlyBracked) { 1733 int pos = data.cfml.getPos(); 1734 if(data.cfml.forwardIfCurrent('{')) { 1735 comments(data); 1736 if(data.cfml.forwardIfCurrent('}')) return true; 1737 data.cfml.setPos(pos); 1738 } 1739 } 1740 1741 1742 if((!checkNLBefore || !data.cfml.hasNLBefore()) && !data.cfml.isCurrent("</",data.tagName) && !data.cfml.isCurrent('}')){ 1743 if(!throwError) return false; 1744 throw new TemplateException(data.cfml,"Missing [;] or [line feed] after expression"); 1745 } 1746 } 1747 return true; 1748 } 1749 1750 1751 /** 1752 * Ruft die Methode expression der zu vererbenten Klasse auf 1753 * und prueft ob der Rueckgabewert einen boolschen Wert repraesentiert und castet den Wert allenfalls. 1754 * <br /> 1755 * EBNF:<br /> 1756 * <code>TemplateException::expression;</code> 1757 * @return condition 1758 * @throws TemplateException 1759 */ 1760 private final ExprBoolean condition(ExprData data) throws TemplateException { 1761 ExprBoolean condition=null; 1762 comments(data); 1763 condition=CastBoolean.toExprBoolean(super.expression(data)); 1764 comments(data); 1765 return condition; 1766 } 1767 1768 /** 1769 * Liest eine try Block ein 1770 * <br /> 1771 * EBNF:<br /> 1772 * <code>;</code> 1773 * @return Try Block 1774 * @throws TemplateException 1775 */ 1776 private final TryCatchFinally tryStatement(ExprData data) throws TemplateException { 1777 if(!data.cfml.forwardIfCurrent("try",'{') && !data.cfml.forwardIfCurrent("try ") && !data.cfml.forwardIfCurrent("try",'/')) 1778 return null; 1779 data.cfml.previous(); 1780 1781 Body body=new BodyBase(); 1782 TryCatchFinally tryCatchFinally=new TryCatchFinally(body,data.cfml.getPosition(),null); 1783 1784 statement(data,body,CTX_TRY); 1785 comments(data); 1786 1787 // catches 1788 short catchCount=0; 1789 while(data.cfml.forwardIfCurrent("catch",'(')) { 1790 catchCount++; 1791 comments(data); 1792 1793 // type 1794 int pos=data.cfml.getPos(); 1795 Position line=data.cfml.getPosition(); 1796 Expression name = null,type = null; 1797 1798 StringBuffer sbType=new StringBuffer(); 1799 String id; 1800 while(true) { 1801 id=identifier(data,false); 1802 if(id==null)break; 1803 sbType.append(id); 1804 data.cfml.removeSpace(); 1805 if(!data.cfml.forwardIfCurrent('.'))break; 1806 sbType.append('.'); 1807 data.cfml.removeSpace(); 1808 } 1809 1810 1811 if(sbType.length()==0) { 1812 type=string(data); 1813 if(type==null) 1814 throw new TemplateException(data.cfml,"a catch statement must begin with the throwing type (query, application ...)."); 1815 } 1816 else { 1817 type=LitString.toExprString(sbType.toString()); 1818 } 1819 1820 1821 //name = expression(); 1822 comments(data); 1823 1824 // name 1825 if(!data.cfml.isCurrent(')')) { 1826 name=expression(data); 1827 } 1828 else { 1829 data.cfml.setPos(pos); 1830 name=expression(data); 1831 type = LitString.toExprString( "any" ); 1832 } 1833 comments(data); 1834 1835 Body b=new BodyBase(); 1836 try { 1837 tryCatchFinally.addCatch(type,name,b,line); 1838 } 1839 catch (BytecodeException e) { 1840 throw new TemplateException(data.cfml,e.getMessage()); 1841 } 1842 comments(data); 1843 1844 if(!data.cfml.forwardIfCurrent(')')) throw new TemplateException(data.cfml,"invalid catch statement, missing closing )"); 1845 1846 statement(data,b,CTX_CATCH); 1847 comments(data); 1848 } 1849 1850 1851// finally 1852 if(finallyStatement(data,tryCatchFinally)) { 1853 comments(data); 1854 } 1855 else if(catchCount==0) 1856 throw new TemplateException(data.cfml,"a try statement must have at least one catch statement"); 1857 1858 //if(body.isEmpty()) return null; 1859 tryCatchFinally.setEnd(data.cfml.getPosition()); 1860 return tryCatchFinally; 1861 } 1862 1863 /** 1864 * Prueft ob sich der Zeiger am Ende eines Script Blockes befindet 1865 * @return Ende ScriptBlock? 1866 * @throws TemplateException 1867 */ 1868 private final boolean isFinish(ExprData data) throws TemplateException { 1869 comments(data); 1870 return data.cfml.isCurrent("</",data.tagName); 1871 } 1872 1873 1874 /** 1875 * Liest den Block mit Statements ein. 1876 * <br /> 1877 * EBNF:<br /> 1878 * <code>"{" spaces {statements} "}" | statement;</code> 1879 * @param block 1880 * @return was a block 1881 * @throws TemplateException 1882 */ 1883 private final boolean block(ExprData data,Body body) throws TemplateException { 1884 if(!data.cfml.forwardIfCurrent('{')) 1885 return false; 1886 comments(data); 1887 if(data.cfml.forwardIfCurrent('}')) { 1888 1889 return true; 1890 } 1891 statements(data,body,false); 1892 1893 if(!data.cfml.forwardIfCurrent('}')) 1894 throw new TemplateException(data.cfml,"Missing ending [}]"); 1895 return true; 1896 } 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 private final Attribute[] attributes(Tag tag,TagLibTag tlt, ExprData data, EndCondition endCond,Expression defaultValue, 1913 Object oAllowExpression, String ignoreAttrReqFor, boolean allowTwiceAttr, char attributeSeparator,boolean allowColonAsNameValueSeparator) throws TemplateException { 1914 ArrayList<Attribute> attrs=new ArrayList<Attribute>(); 1915 ArrayList<String> ids=new ArrayList<String>(); 1916 while(data.cfml.isValidIndex()) { 1917 data.cfml.removeSpace(); 1918 // if no more attributes break 1919 if(endCond.isEnd(data)) break; 1920 Attribute attr = attribute(tlt,data,ids,defaultValue,oAllowExpression, allowTwiceAttr,allowColonAsNameValueSeparator); 1921 attrs.add(attr); 1922 1923 // seperator 1924 if(attributeSeparator>0) { 1925 data.cfml.removeSpace(); 1926 data.cfml.forwardIfCurrent(attributeSeparator); 1927 } 1928 1929 } 1930 1931 // not defined attributes 1932 if(tlt!=null){ 1933 boolean hasAttributeCollection=attrs.contains("attributecollection"); 1934 int type=tlt.getAttributeType(); 1935 if(type==TagLibTag.ATTRIBUTE_TYPE_FIXED || type==TagLibTag.ATTRIBUTE_TYPE_MIXED) { 1936 Map<String, TagLibTagAttr> hash = tlt.getAttributes(); 1937 Iterator<Entry<String, TagLibTagAttr>> it = hash.entrySet().iterator(); 1938 Entry<String, TagLibTagAttr> e; 1939 while(it.hasNext()) { 1940 e = it.next(); 1941 TagLibTagAttr att=e.getValue(); 1942 if(att.isRequired() && !contains(attrs,att) && att.getDefaultValue()==null && !att.getName().equals(ignoreAttrReqFor)) { 1943 if(!hasAttributeCollection)throw new TemplateException(data.cfml,"attribute "+att.getName()+" is required for statement "+tlt.getName()); 1944 if(tag!=null)tag.addMissingAttribute(att); 1945 } 1946 } 1947 } 1948 } 1949 return attrs.toArray(new Attribute[attrs.size()]); 1950 } 1951 1952 private final boolean contains(ArrayList<Attribute> attrs, TagLibTagAttr attr) { 1953 1954 Iterator<Attribute> it = attrs.iterator(); 1955 String name; 1956 String[] alias; 1957 while(it.hasNext()){ 1958 name=it.next().getName(); 1959 1960 // check name 1961 if(name.equals(attr.getName())) return true; 1962 1963 // and aliases 1964 alias = attr.getAlias(); 1965 if(!ArrayUtil.isEmpty(alias)) for(int i=0;i<alias.length;i++){ 1966 if(alias[i].equals(attr.getName())) return true; 1967 } 1968 } 1969 1970 return false; 1971 } 1972 1973 private final Attribute attribute(TagLibTag tlt, ExprData data, ArrayList<String> args, Expression defaultValue,Object oAllowExpression, boolean allowTwiceAttr, boolean allowColonSeparator) throws TemplateException { 1974 StringBuffer sbType=new StringBuffer(); 1975 RefBoolean dynamic=new RefBooleanImpl(false); 1976 1977 // Name 1978 String name=attributeName(data.cfml,args,tlt,dynamic,sbType, allowTwiceAttr,!allowColonSeparator); 1979 String nameLC=name==null?null:name.toLowerCase(); 1980 boolean allowExpression=false; 1981 if(oAllowExpression instanceof Boolean)allowExpression=((Boolean)oAllowExpression).booleanValue(); 1982 else if(oAllowExpression instanceof String)allowExpression=((String)oAllowExpression).equalsIgnoreCase(name); 1983 1984 Expression value=null; 1985 comments(data); 1986 1987 // value 1988 boolean b=data.cfml.forwardIfCurrent('=') || (allowColonSeparator && data.cfml.forwardIfCurrent(':')); 1989 if(b) { 1990 comments(data); 1991 value=attributeValue(data,allowExpression); 1992 1993 } 1994 else { 1995 value=defaultValue; 1996 } 1997 comments(data); 1998 1999 2000 // Type 2001 TagLibTagAttr tlta=null; 2002 if(tlt!=null){ 2003 tlta = tlt.getAttribute(nameLC,true); 2004 if(tlta!=null && tlta.getName()!=null)nameLC=tlta.getName(); 2005 } 2006 return new Attribute(dynamic.toBooleanValue(),name,tlta!=null?CastOther.toExpression(value, tlta.getType()):value,sbType.toString()); 2007 } 2008 2009 private final String attributeName(CFMLString cfml, ArrayList<String> args,TagLibTag tag, RefBoolean dynamic, StringBuffer sbType, boolean allowTwiceAttr, boolean allowColon) throws TemplateException { 2010 String id=CFMLTransformer.identifier(cfml,true,allowColon); 2011 return validateAttributeName(id, cfml, args, tag, dynamic, sbType,allowTwiceAttr); 2012 } 2013 2014 2015 2016 private final String validateAttributeName(String idOC,CFMLString cfml, ArrayList<String> args,TagLibTag tag, RefBoolean dynamic, StringBuffer sbType, boolean allowTwiceAttr) throws TemplateException { 2017 String idLC=idOC.toLowerCase(); 2018 2019 if(args.contains(idLC) && !allowTwiceAttr) throw new TemplateException(cfml,"you can't use the same attribute ["+idOC+"] twice"); 2020 args.add(idLC); 2021 2022 2023 if(tag==null) return idOC; 2024 int typeDef=tag.getAttributeType(); 2025 if("attributecollection".equals(idLC)){ 2026 dynamic.setValue(tag.getAttribute(idLC,true)==null); 2027 sbType.append("struct"); 2028 } 2029 else if(typeDef==TagLibTag.ATTRIBUTE_TYPE_FIXED || typeDef==TagLibTag.ATTRIBUTE_TYPE_MIXED) { 2030 TagLibTagAttr attr=tag.getAttribute(idLC,true); 2031 if(attr==null) { 2032 if(typeDef==TagLibTag.ATTRIBUTE_TYPE_FIXED) { 2033 String names=tag.getAttributeNames(); 2034 if(StringUtil.isEmpty(names)) 2035 throw new TemplateException(cfml,"Attribute "+idOC+" is not allowed for tag "+tag.getFullName()); 2036 2037 throw new TemplateException(cfml, 2038 "Attribute "+idOC+" is not allowed for statement "+tag.getName(), 2039 "valid attribute names are ["+names+"]"); 2040 } 2041 dynamic.setValue(true); 2042 2043 } 2044 else { 2045 sbType.append(attr.getType()); 2046 //parseExpression[0]=attr.getRtexpr(); 2047 } 2048 } 2049 else if(typeDef==TagLibTag.ATTRIBUTE_TYPE_DYNAMIC){ 2050 dynamic.setValue(true); 2051 } 2052 return idOC; 2053 } 2054 2055 2056 private final Expression attributeValue(ExprData data, boolean allowExpression) throws TemplateException { 2057 return allowExpression?super.expression(data):transformAsString(data,new String[]{" ", ";", "{"}); 2058 } 2059 2060 public static interface EndCondition { 2061 public boolean isEnd(ExprData data); 2062 } 2063}