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