001 package railo.transformer.cfml.tag; 002 003 import java.io.IOException; 004 import java.util.ArrayList; 005 import java.util.Iterator; 006 import java.util.Map; 007 import java.util.Map.Entry; 008 009 import railo.commons.io.res.util.ResourceUtil; 010 import railo.commons.lang.StringUtil; 011 import railo.commons.lang.types.RefBoolean; 012 import railo.commons.lang.types.RefBooleanImpl; 013 import railo.runtime.Info; 014 import railo.runtime.SourceFile; 015 import railo.runtime.config.Config; 016 import railo.runtime.config.ConfigImpl; 017 import railo.runtime.exp.ApplicationException; 018 import railo.runtime.exp.PageExceptionImpl; 019 import railo.runtime.exp.TemplateException; 020 import railo.runtime.op.Caster; 021 import railo.transformer.bytecode.Body; 022 import railo.transformer.bytecode.BodyBase; 023 import railo.transformer.bytecode.Page; 024 import railo.transformer.bytecode.cast.Cast; 025 import railo.transformer.bytecode.expression.Expression; 026 import railo.transformer.bytecode.expression.var.NullExpression; 027 import railo.transformer.bytecode.literal.LitBoolean; 028 import railo.transformer.bytecode.literal.LitString; 029 import railo.transformer.bytecode.statement.PrintOut; 030 import railo.transformer.bytecode.statement.StatementBase; 031 import railo.transformer.bytecode.statement.tag.Attribute; 032 import railo.transformer.bytecode.statement.tag.Tag; 033 import railo.transformer.bytecode.util.ASMUtil; 034 import railo.transformer.cfml.ExprTransformer; 035 import railo.transformer.cfml.attributes.AttributeEvaluatorException; 036 import railo.transformer.cfml.evaluator.EvaluatorException; 037 import railo.transformer.cfml.evaluator.EvaluatorPool; 038 import railo.transformer.cfml.evaluator.impl.ProcessingDirectiveException; 039 import railo.transformer.cfml.expression.SimpleExprTransformer; 040 import railo.transformer.cfml.script.CFMLScriptTransformer; 041 import railo.transformer.cfml.script.CFMLScriptTransformer.ComponentTemplateException; 042 import railo.transformer.library.function.FunctionLib; 043 import railo.transformer.library.tag.CustomTagLib; 044 import railo.transformer.library.tag.TagLib; 045 import railo.transformer.library.tag.TagLibException; 046 import railo.transformer.library.tag.TagLibFactory; 047 import railo.transformer.library.tag.TagLibTag; 048 import railo.transformer.library.tag.TagLibTagAttr; 049 import railo.transformer.util.CFMLString; 050 051 052 /** 053 * Die Klasse CFMLTransformer ist das Herzstck des ᅵbersetzungsprozess, 054 * es liest die bergebene CFML Datei ein und bersetzt diese in ein valid (CFXD) XML Dokument 055 * in der Form eines org.w3c.dom.Document Object, 056 * die dann als weitere Vorlage zum bersetzten in PHP dient. 057 * Der CFMLTransformer bersetzt nur die Tags die innerhalb einer CFML Seite vorkommen, 058 * nicht die Ausdrcke die innerhalb von Attributen und dem Body eines Tag vorkommen knnen, 059 * fr dies ist der ExprTransformer zust¦ndig, 060 * der in der jeweiligen Tag Library definiert ist. 061 * Der CFMLTransformer kann zwar durch seine Grammatik, 062 * Tags erkennen aber nicht validieren. 063 * Erst mithilfe der im zugeteilten Tag Libraries kann er vergleichen ob ein Tag nur 064 * ein normaler HTML Tag ist, das er einfach als literale Zeichenkette aufnimmt, 065 * oder ob es sich um einen Tag handelt der eine konkrete Anweisung implementiert. 066 * Die Tag Library definiert alle in CFML vorhanden Tags, 067 * deren individuelle Grammatik und deren Aufbau und Verhalten. 068 069 * <pre> 070 Parser Grammatik nach EBNF (Extended Backus-Naur Form) 071 072 transform = {body} 073 body = [comment] ("</" | "<" tag body | literal body); 074 comment = "<!---" {?-"--->"} "--->"; 075 literal = ("<" | {?-"#"-"<"} "<" | {"#" expression "#"} "<" ) | ({?-"<"} "<") 076 (* Welcher Teil der "oder" Bedingung ausgefhrt wird, ist abh¦ngig was die Tag-Lib vorgibt, 077 dass Expression geparst werden sollen oder nicht. *) 078 tag = name-space identifier spaces attributes ("/>" | ">" [body "</" identifier spaces ">"]); 079 (* Ob dem Tag ein Body und ein End-Tag folgt ist abh¦ngig von Definition des body-content in Tag-Lib, gleices gilt fr appendix *) 080 name-space = < tagLib[].getNameSpaceAndSeperator() >; 081 (* Vergleicht Zeichen mit den Namespacedefinitionen der Tag Libraries. *) 082 attributes = ({spaces attribute} "/>" | {spaces attribute} ">") | attribute-value; 083 (* Welcher Teil der "oder" Bedingung ausgefhrt wird, ist abh¦ngig von der Tag Attribute Definition in der Tag Lib. *) 084 attribute = attribute-name spaces "=" spaces attribute-value; 085 attribute-name = ("expression"|'expression'|expression) | identifier; 086 (* Ruft identifier oder den Expression Transformer auf je nach Attribute Definition in der Tag Lib. *) 087 attribute-value = expression; 088 identifier = (letter | "_") {letter | "_"|digit}; 089 letter = "a".."z"|"A".."Z"; 090 digit = "0".."9"; 091 expression = <ExprTransfomer.expression()>; (* Ruft den Expression Transformer auf. *) 092 spaces = {space}; 093 space = "\s"|"\t"|"\f"|"\t"|"\n"; 094 095 {"x"}= 0 bis n mal "x" 096 ["x"]= 0 bis 1 mal "x" 097 ("x" | "y")"z" = "xz" oder "yz" 098 </pre> 099 * 100 * 101 */ 102 public final class CFMLTransformer { 103 104 public static short TAG_LIB_GLOBAL=0; 105 public static short TAG_LIB_PAGE=1; 106 107 public class Data { 108 109 private TagLib[][] tlibs;//=new TagLib[][]{null,new TagLib[0]}; 110 private FunctionLib[] flibs; 111 private CFMLString cfml; 112 private EvaluatorPool ep=new EvaluatorPool(); 113 private SimpleExprTransformer set; 114 private Config config; 115 private Page page; 116 117 public Data(TagLib[][] tlibs, FunctionLib[] flibs, CFMLString cfml,Config config,Page page) { 118 super(); 119 this.tlibs = tlibs; 120 this.flibs = flibs; 121 this.cfml = cfml; 122 this.config = config; 123 this.page = page; 124 } 125 } 126 127 /** 128 * Startmethode zum transfomieren einer CFML Datei. 129 * <br /> 130 * EBNF:<br /> 131 * <code>{body}</code> 132 * @param config 133 * @param sf CFML File 134 * @param tlibs Tag Library Deskriptoren, nach denen innerhalb der CFML Datei geprft werden soll. 135 * @param flibs Function Library Deskriptoren, nach denen innerhalb der Expressions der CFML Datei geprft werden soll. 136 * @return ᅵbersetztes CFXD Dokument Element. 137 * @throws TemplateException 138 * @throws IOException 139 */ 140 public Page transform(ConfigImpl config,SourceFile sf, TagLib[] tlibs, FunctionLib[] flibs) throws TemplateException, IOException { 141 Page p; 142 CFMLString cfml; 143 String charset; 144 boolean writeLog; 145 146 writeLog=config.getExecutionLogEnabled(); 147 charset=config.getTemplateCharset(); 148 149 150 151 while(true){ 152 try { 153 cfml=new CFMLString(sf,charset,writeLog); 154 p = transform(config,cfml,tlibs,flibs,sf.getFile().lastModified()); 155 break; 156 } 157 catch(ProcessingDirectiveException pde) { 158 writeLog=pde.getWriteLog(); 159 charset=pde.getCharset(); 160 } 161 } 162 163 // if cfc has no component tag or is script without cfscript 164 if(p.isPage() && ResourceUtil.getExtension(sf.getFile(),"").equalsIgnoreCase(config.getCFCExtension())){ 165 cfml.setPos(0); 166 TagLibTag tlt; 167 CFMLString original = cfml; 168 169 // try inside a cfscript 170 tlt = CFMLTransformer.getTLT(original,"script"); 171 String text="<"+tlt.getFullName()+">"+original.getText()+"</"+tlt.getFullName()+">"; 172 cfml=new CFMLString(text,charset,writeLog,sf); 173 174 try { 175 while(true){ 176 if(cfml==null){ 177 cfml=new CFMLString(sf,charset,writeLog); 178 text="<"+tlt.getFullName()+">"+cfml.getText()+"</"+tlt.getFullName()+">"; 179 cfml=new CFMLString(text,charset,writeLog,sf); 180 } 181 try { 182 p= transform(config,cfml,tlibs,flibs,sf.getFile().lastModified()); 183 break; 184 } 185 catch(ProcessingDirectiveException pde) { 186 writeLog=pde.getWriteLog(); 187 charset=pde.getCharset(); 188 cfml=null; 189 } 190 } 191 } 192 catch (ComponentTemplateException e) { 193 throw e.getTemplateException(); 194 } 195 catch (TemplateException e) { 196 //print.printST(e); 197 } 198 199 200 201 202 // try inside a component 203 if(p.isPage()){ 204 tlt = CFMLTransformer.getTLT(original,"component"); 205 text="<"+tlt.getFullName()+">"+original.getText()+"</"+tlt.getFullName()+">"; 206 cfml=new CFMLString(text,charset,writeLog,sf); 207 208 while(true){ 209 if(cfml==null){ 210 cfml=new CFMLString(sf,charset,writeLog); 211 text="<"+tlt.getFullName()+">"+cfml.getText()+"</"+tlt.getFullName()+">"; 212 cfml=new CFMLString(text,charset,writeLog,sf); 213 } 214 try { 215 p= transform(config,cfml,tlibs,flibs,sf.getFile().lastModified()); 216 break; 217 } 218 catch(ProcessingDirectiveException pde) { 219 writeLog=pde.getWriteLog(); 220 charset=pde.getCharset(); 221 cfml=null; 222 } 223 } 224 } 225 226 } 227 228 229 return p; 230 } 231 232 233 public static TagLibTag getTLT(CFMLString cfml,String name) throws TemplateException { 234 TagLib tl; 235 try { 236 // this is already loaded, oherwise we where not here 237 tl = TagLibFactory.loadFromSystem(); 238 return tl.getTag(name); 239 } 240 catch (TagLibException e) { 241 throw new TemplateException(cfml,e); 242 } 243 } 244 245 246 /** 247 * Startmethode zum transfomieren einer CFMLString. 248 * <br /> 249 * EBNF:<br /> 250 * <code>{body}</code> 251 * @param config 252 * @param cfml CFMLString 253 * @param tlibs Tag Library Deskriptoren, nach denen innerhalb der CFML Datei geprft werden soll. 254 * @param flibs Function Library Deskriptoren, nach denen innerhalb der Expressions der CFML Datei geprft werden soll. 255 * @param sourceLastModified 256 * @return ᅵbersetztes CFXD Dokument Element. 257 * @throws TemplateException 258 */ 259 public Page transform(ConfigImpl config,CFMLString cfml,TagLib[] tlibs,FunctionLib[] flibs, long sourceLastModified) throws TemplateException { 260 261 TagLib[][] _tlibs=new TagLib[][]{null,new TagLib[0]}; 262 _tlibs[TAG_LIB_GLOBAL]=tlibs; 263 // reset page tlds 264 if(_tlibs[TAG_LIB_PAGE].length>0) { 265 _tlibs[TAG_LIB_PAGE]=new TagLib[0]; 266 } 267 268 269 270 271 SourceFile source=cfml.getSourceFile(); 272 273 Page page=new Page(source.getPhyscalFile(),source.getFullClassName(),Info.getFullVersionInfo(),sourceLastModified,cfml.getWriteLog()); 274 Data data = new Data(_tlibs,flibs,cfml,config,page); 275 276 //Body body=page; 277 try { 278 do { 279 280 body(data,page,false,null); 281 282 if(data.cfml.isAfterLast()) break; 283 if(data.cfml.forwardIfCurrent("</")){ 284 TagLib tagLib=nameSpace(data); 285 if(tagLib==null){ 286 page.addPrintOut("</", data.cfml.getLine()); 287 } 288 else { 289 throw new TemplateException(cfml,"no matching start tag for end tag ["+tagLib.getNameSpaceAndSeparator()+identifier(data.cfml,true)+"]"); 290 291 } 292 } 293 else 294 throw new TemplateException(cfml,"Error while transforming CFML File"); 295 }while(true); 296 297 // call-back of evaluators 298 data.ep.run(); 299 300 return page; 301 } 302 catch(TemplateException e) { 303 data.ep.clear(); 304 /*if(e instanceof ProcessingDirectiveException) throw e; 305 throw new TemplateException( 306 "\n-----------------------------------------------------\n"+ 307 "line:"+e.getLine()+"\n"+ 308 "message:"+e.getMessage()+"\n"+ 309 data.cfml.toString()+"\n-----------------------------------------------------\n"); 310 */throw e; 311 } 312 } 313 314 /** 315 * Liest den Body eines Tag ein. Kommentare, Tags und Literale inkl. Expressions. 316 * <br /> 317 * EBNF:<br /> 318 * <code>[comment] ("</" | "<" tag body | literal body);</code> 319 * @param body CFXD Body Element dem der Inhalt zugeteilt werden soll. 320 * @param parseExpression Definiert ob Expressions innerhalb von Literalen bersetzt werden sollen oder nicht. 321 * @param transformer Expression Transfomer zum bersetzten von Expression. 322 * @throws TemplateException 323 */ 324 private void body(Data data,Body body, boolean parseExpression, ExprTransformer transformer) throws TemplateException { 325 boolean parseLiteral=true; 326 327 // Comment 328 comment(data.cfml,false); 329 // Tag 330 // is Tag Beginning 331 if(data.cfml.isCurrent('<')) { 332 // return if end tag and inside tag 333 if(data.cfml.isNext('/')) { 334 //railo.print.ln("early return"); 335 return; 336 } 337 parseLiteral=!tag(data,body,parseExpression); 338 } 339 // no Tag 340 if(parseLiteral) { 341 literal(data,body, parseExpression, transformer); 342 } 343 // not at the end 344 if(data.cfml.isValidIndex()) 345 body(data,body,parseExpression, transformer); 346 } 347 348 /** 349 * Liest einen Kommentar ein, Kommentare werden nicht in die CFXD bertragen sondern verworfen. 350 * Komentare knnen auch Kommentare enthalten. 351 * <br /> 352 * EBNF:<br /> 353 * <code>"<!---" {?-"--->"} "--->";</code> 354 * @throws TemplateException 355 */ 356 357 public static void comment(CFMLString cfml,boolean removeSpace) throws TemplateException { 358 if(!removeSpace) { 359 comment(cfml); 360 } 361 else { 362 cfml.removeSpace(); 363 if(comment(cfml))cfml.removeSpace(); 364 } 365 366 } 367 368 public static boolean comment(CFMLString cfml) throws TemplateException { 369 if(!cfml.forwardIfCurrent("<!---")) 370 return false; 371 372 int start=cfml.getPos(); 373 short counter=1; 374 while(true) { 375 if(cfml.isAfterLast()) { 376 cfml.setPos(start); 377 throw new TemplateException(cfml,"no end comment found"); 378 } 379 else if(cfml.forwardIfCurrent("<!---")) { 380 counter++; 381 } 382 else if(cfml.forwardIfCurrent("--->")) { 383 if(--counter==0) { 384 comment(cfml); 385 return true; 386 } 387 } 388 else { 389 cfml.next(); 390 } 391 } 392 } 393 394 395 396 /** 397 * Liest Literale Zeichenketten ein die sich innerhalb und auserhalb von tgas befinden, 398 * beim Einlesen wird unterschieden ob Expression geparsst werden mssen oder nicht, 399 * dies ist abh¦ngig, von der Definition des Tag in dem man sich allenfalls befindet, innerhalb der TLD. 400 * @param parent bergeordnetes Element. 401 * @param parseExpression Definiert on Expressions geparset werden sollen oder nicht. 402 * @param transformer Expression Transfomer zum bersetzen der Expressions innerhalb des Literals. 403 * @throws TemplateException 404 * 405 * <br /> 406 * EBNF:<br /> 407 * <code>("<" | {?-"#"-"<"} "<" | {"#" expression "#"} "<" ) | ({?-"<"} "<") 408 (* Welcher Teil der "oder" Bedingung ausgefhrt wird, ist abh¦ngig ob die Tag-Lib vorgibt, 409 dass Expression geparst werden sollen oder nicht. *)</code> 410 */ 411 private void literal(Data data,Body parent,boolean parseExpression, ExprTransformer transformer) throws TemplateException { 412 // with expression 413 if(parseExpression) { 414 if(data.cfml.isAfterLast())return; 415 // data.cfml.getCurrent() 416 StringBuffer text=new StringBuffer(); 417 int count=0; 418 while(data.cfml.isValidIndex()) { 419 count++; 420 // # 421 if(data.cfml.isCurrent('#')) { 422 data.cfml.next(); 423 if(data.cfml.isCurrent('#')) { 424 text.append('#'); 425 } 426 else { 427 if(text.length()>0) { 428 parent.addPrintOut(text.toString(),-1); 429 text=new StringBuffer(); 430 } 431 int line=data.cfml.getLine(); 432 parent.addStatement(new PrintOut(transformer.transform(data.ep,data.flibs,data.cfml),line)); 433 434 if(!data.cfml.isCurrent('#')) 435 throw new TemplateException(data.cfml,"missing terminating [#] for expression"); 436 } 437 } 438 else if(data.cfml.isCurrent('<') && count>1) { 439 break; 440 } 441 else 442 text.append(data.cfml.getCurrent()); 443 data.cfml.next(); 444 } 445 if(text.length()>0)parent.addPrintOut(text.toString(), -1); 446 } 447 // no expression 448 else { 449 int start=data.cfml.getPos(); 450 data.cfml.next(); 451 int end=data.cfml.indexOfNext('<'); 452 String text; 453 if(end==-1) { 454 text=data.cfml.substring(start); 455 data.cfml.setPos(data.cfml.length()); 456 } 457 else { 458 text=data.cfml.substring(start,end-start); 459 data.cfml.setPos(end); 460 } 461 parent.addPrintOut(text, -1); 462 463 } 464 } 465 466 /** 467 * Liest einen Tag ein, prft hierbei ob das Tag innerhalb einer der geladenen Tag-Lib existiert, 468 * ansonsten wird ein Tag einfach als literal-string aufgenommen. 469 * <br /> 470 * EBNF:<br /> 471 * <code>name-space identifier spaces attributes ("/>" | ">" [body "</" identifier spaces ">"]);(* Ob dem Tag ein Body und ein End-Tag folgt ist abh¦ngig von Definition des body-content in Tag-Lib, gleices gilt fr appendix *)</code> 472 * @param parent bergeornetes Tag 473 * @param parseExpression sollen Expresson innerhalb des Body geparste werden oder nicht. 474 * @return Gibt zurck ob es sich um ein Tag as einer Tag-Lib handelte oder nicht. 475 * @throws TemplateException 476 */ 477 private boolean tag(Data data,Body parent,boolean parseExpression) throws TemplateException { 478 //railo.print.ln("--->"+data.cfml.getCurrent()); 479 boolean hasBody=false; 480 481 int line=data.cfml.getLine(); 482 //int column=data.cfml.getColumn(); 483 int start=data.cfml.getPos(); 484 data.cfml.next(); 485 486 // read in namesapce of tag 487 TagLib tagLib=nameSpace(data); 488 489 // return if no matching tag lib 490 if(tagLib==null) { 491 data.cfml.previous(); 492 return false; 493 } 494 495 // Get matching tag from tag lib 496 String strNameNormal=identifier(data.cfml,false); 497 if(strNameNormal==null) { 498 data.cfml.setPos((data.cfml.getPos()-tagLib.getNameSpaceAndSeparator().length())-1); 499 return false; 500 } 501 502 String strName=strNameNormal.toLowerCase(); 503 String appendix=null; 504 TagLibTag tagLibTag=tagLib.getTag(strName); 505 506 // get taglib 507 if(tagLibTag==null) { 508 tagLibTag=tagLib.getAppendixTag(strName); 509 if(tagLibTag==null) 510 throw new TemplateException(data.cfml,"undefined tag ["+tagLib.getNameSpaceAndSeparator()+strName+"]"); 511 appendix=StringUtil.removeStartingIgnoreCase(strNameNormal,tagLibTag.getName()); 512 } 513 514 // CFXD Element 515 Tag tag; 516 try { 517 tag = tagLibTag.getTag(line,data.cfml.getLine()); 518 } 519 catch (Exception e) { 520 throw new TemplateException(data.cfml,e); 521 } 522 parent.addStatement(tag); 523 524 // get tag from tag library 525 if(appendix!=null) { 526 tag.setAppendix(appendix); 527 tag.setFullname(tagLibTag.getFullName().concat(appendix)); 528 } 529 else { 530 tag.setFullname(tagLibTag.getFullName()); 531 } 532 if(tag.getFullname().equalsIgnoreCase("cfcomponent"))data.page.setIsComponent(true); // MUST to hardcoded, to better 533 else if(tag.getFullname().equalsIgnoreCase("cfinterface"))data.page.setIsInterface(true); // MUST to hardcoded, to better 534 535 tag.setTagLibTag(tagLibTag); 536 comment(data.cfml,true); 537 538 // Tag Translator Evaluator 539 if(tagLibTag.hasTteClass()) { 540 data.ep.add(tagLibTag,tag,data.flibs,data.cfml); 541 } 542 543 //get Attributes 544 attributes(data,tagLibTag,tag); 545 546 if(tagLibTag.hasAttributeEvaluator()) { 547 try { 548 tagLibTag=tagLibTag.getAttributeEvaluator().evaluate(tagLibTag,tag); 549 } catch (AttributeEvaluatorException e) { 550 551 throw new TemplateException(data.cfml, e); 552 } 553 } 554 555 556 557 // End of begin Tag 558 // TODO muss erlaubt sein 559 if(data.cfml.forwardIfCurrent('>')) { 560 hasBody=tagLibTag.getHasBody(); 561 } 562 else if(data.cfml.forwardIfCurrent('/','>')) { 563 if(tagLibTag.getHasBody())tag.setBody(new BodyBase()); 564 } 565 else { 566 throw createTemplateException(data.cfml, "tag ["+tagLibTag.getFullName()+"] is not closed",tagLibTag); 567 } 568 569 570 // Body 571 if(hasBody) { 572 573 574 // get Body 575 if(tagLibTag.isTagDependent()) { 576 // get TagDependentBodyTransformer 577 TagDependentBodyTransformer tdbt=null; 578 try { 579 tdbt=tagLibTag.getBodyTransformer(); 580 } catch (TagLibException e) { 581 throw new TemplateException(data.cfml,e); 582 } 583 if(tdbt==null) throw createTemplateException(data.cfml,"Tag dependent body Transformer is invalid for Tag ["+tagLibTag.getFullName()+"]",tagLibTag); 584 tdbt.transform(data.config,this,data.ep,data.flibs,tag,tagLibTag,data.cfml); 585 586 // get TagLib of end Tag 587 if(!data.cfml.forwardIfCurrent("</")) { 588 // MUST this is a patch, do a more proper implementation 589 TemplateException te = new TemplateException(data.cfml,"invalid construct"); 590 if(tdbt!=null && tdbt instanceof CFMLScriptTransformer && ASMUtil.containsComponent(tag.getBody())) { 591 throw new CFMLScriptTransformer.ComponentTemplateException(te); 592 } 593 throw te; 594 } 595 596 TagLib tagLibEnd=nameSpace(data); 597 // same NameSpace 598 if(!(tagLibEnd!=null && tagLibEnd.getNameSpaceAndSeparator().equals(tagLib.getNameSpaceAndSeparator()))) 599 throw new TemplateException(data.cfml,"invalid construct"); 600 // get end Tag 601 String strNameEnd=identifier(data.cfml,true).toLowerCase(); 602 603 // not the same name Tag 604 if(!strName.equals(strNameEnd)) { 605 data.cfml.setPos(start); 606 throw new TemplateException(data.cfml,"Start and End Tag has not the same Name ["+tagLib.getNameSpaceAndSeparator()+strName+"-"+tagLibEnd.getNameSpaceAndSeparator()+strNameEnd+"]"); 607 } 608 data.cfml.removeSpace(); 609 if(!data.cfml.forwardIfCurrent('>')) 610 throw new TemplateException(data.cfml,"End Tag ["+tagLibEnd.getNameSpaceAndSeparator()+strNameEnd+"] not closed"); 611 } 612 else { 613 // get body of Tag 614 BodyBase body=new BodyBase(); 615 body.setParent(tag); 616 //tag.setBody(body); 617 //parseExpression=(tagLibTag.getParseBody())?true:parseExpression; 618 if(tagLibTag.getParseBody())parseExpression=true; 619 620 while(true) { 621 622 // Load Expession Transformer from TagLib 623 ExprTransformer transfomer=null; 624 if(parseExpression) { 625 try { 626 transfomer = tagLibTag.getTagLib().getExprTransfomer(); 627 } catch (TagLibException e) { 628 throw new TemplateException(data.cfml,e); 629 } 630 } 631 632 633 // call body 634 635 body(data,body,parseExpression,transfomer); 636 637 638 // no End Tag 639 if(data.cfml.isAfterLast()) { 640 641 if(tagLibTag.isBodyReq()) { 642 data.cfml.setPos(start); 643 throw createTemplateException(data.cfml,"No matching end tag found for tag ["+tagLibTag.getFullName()+"]",tagLibTag); 644 } 645 body.moveStatmentsTo(parent); 646 return executeEvaluator(data,tagLibTag, tag); 647 } 648 649 // Invalid Construct 650 int posBeforeEndTag=data.cfml.getPos(); 651 if(!data.cfml.forwardIfCurrent('<','/')) 652 throw createTemplateException(data.cfml,"Missing end tag for ["+tagLibTag.getFullName()+"]",tagLibTag); 653 654 // get TagLib of end Tag 655 TagLib tagLibEnd=nameSpace(data); 656 657 // same NameSpace 658 if(tagLibEnd!=null) { 659 String strNameEnd=""; 660 //railo.print.ln(data.cfml.getLine()+" - "+data.cfml.getColumn()+" - "+tagLibEnd.getNameSpaceAndSeperator()+".equals("+tagLib.getNameSpaceAndSeperator()+")"); 661 if(tagLibEnd.getNameSpaceAndSeparator().equals(tagLib.getNameSpaceAndSeparator())) { 662 663 // get end Tag 664 strNameEnd=identifier(data.cfml,true).toLowerCase(); 665 // not the same name Tag 666 667 // new part 668 data.cfml.removeSpace(); 669 if(strName.equals(strNameEnd)) { 670 if(!data.cfml.forwardIfCurrent('>')) 671 throw new TemplateException(data.cfml,"End Tag ["+tagLibEnd.getNameSpaceAndSeparator()+strNameEnd+"] not closed"); 672 break; 673 } 674 675 } 676 // new part 677 if(tagLibTag.isBodyReq()) { 678 TagLibTag endTag = tagLibEnd.getTag(strNameEnd); 679 if(endTag!=null && !endTag.getHasBody()) 680 throw new TemplateException(data.cfml, 681 "End Tag ["+ 682 tagLibEnd.getNameSpaceAndSeparator()+strNameEnd+"] is not allowed, for this tag only a Start Tag is allowed"); 683 data.cfml.setPos(start); 684 685 throw new TemplateException(data.cfml, 686 "Start and End Tag has not the same Name ["+ 687 tagLib.getNameSpaceAndSeparator()+strName+"-"+tagLibEnd.getNameSpaceAndSeparator()+strNameEnd+"]"); 688 } 689 body.moveStatmentsTo(parent); 690 data.cfml.setPos(posBeforeEndTag); 691 return executeEvaluator(data,tagLibTag, tag); 692 /// new part 693 } 694 body.addPrintOut("</",data.cfml.getLine()); 695 696 } 697 tag.setBody(body); 698 699 } 700 } 701 if(tag instanceof StatementBase) 702 ((StatementBase)tag).setEndLine(data.cfml.getLine()); 703 // Tag Translator Evaluator 704 705 return executeEvaluator(data,tagLibTag, tag); 706 707 } 708 709 private boolean executeEvaluator(Data data,TagLibTag tagLibTag, Tag tag) throws TemplateException { 710 if(tagLibTag.hasTteClass()) { 711 try { 712 TagLib lib=tagLibTag.getEvaluator().execute(data.config,tag,tagLibTag,data.flibs,data.cfml); 713 if(lib!=null) { 714 // set 715 for(int i=0;i<data.tlibs[TAG_LIB_PAGE].length;i++) { 716 if(data.tlibs[TAG_LIB_PAGE][i].getNameSpaceAndSeparator().equalsIgnoreCase(lib.getNameSpaceAndSeparator())){ 717 boolean extIsCustom=data.tlibs[TAG_LIB_PAGE][i] instanceof CustomTagLib; 718 boolean newIsCustom=lib instanceof CustomTagLib; 719 // TagLib + CustomTagLib (visa/versa) 720 if(extIsCustom){ 721 ((CustomTagLib)data.tlibs[TAG_LIB_PAGE][i]).append(lib); 722 return true; 723 } 724 else if(newIsCustom){ 725 ((CustomTagLib)lib).append(data.tlibs[TAG_LIB_PAGE][i]); 726 data.tlibs[TAG_LIB_PAGE][i]=lib; 727 return true; 728 } 729 } 730 } 731 // TODO make sure longer namespace ar checked firts to support subsets, same for core libs 732 // insert 733 TagLib[] newTlibs=new TagLib[data.tlibs[TAG_LIB_PAGE].length+1]; 734 for(int i=0;i<data.tlibs[TAG_LIB_PAGE].length;i++) { 735 newTlibs[i]=data.tlibs[TAG_LIB_PAGE][i]; 736 } 737 newTlibs[data.tlibs[TAG_LIB_PAGE].length]=lib; 738 data.tlibs[TAG_LIB_PAGE]=newTlibs; 739 } 740 } 741 catch (EvaluatorException e) { 742 throw new TemplateException(data.cfml,e); 743 } 744 } 745 return true; 746 } 747 748 /** 749 * Vergleicht folgende Zeichen mit den Namespacedefinitionen der Tag Libraries, 750 * gibt eine Tag-Lib zurck falls eine passt, ansonsten null. 751 * <br /> 752 * EBNF:<br /> 753 * <code>< tagLib[].getNameSpaceAndSeperator() >(* Vergleicht Zeichen mit den Namespacedefinitionen der Tag Libraries. *) </code> 754 * @return TagLib Passende Tag Lirary oder null. 755 */ 756 private TagLib nameSpace(Data data) { 757 boolean hasTag=false; 758 int start = data.cfml.getPos(); 759 TagLib tagLib=null; 760 761 // loop over NameSpaces 762 for(int i=1;i>=0;i--) { 763 for(int ii=0;ii<data.tlibs[i].length;ii++) { 764 tagLib= data.tlibs[i][ii]; 765 char[] c=tagLib.getNameSpaceAndSeperatorAsCharArray(); 766 // Loop over char of NameSpace and Sepearator 767 hasTag=true; 768 for(int y=0;y<c.length;y++) { 769 if(!(data.cfml.isValidIndex() && c[y]==data.cfml.getCurrentLower())) { 770 //hasTag=true; 771 //} else { 772 hasTag=false; 773 data.cfml.setPos(start); 774 break; 775 } 776 data.cfml.next(); 777 } 778 if(hasTag)return tagLib;//break; 779 } 780 //if(hasTag) return tagLib; 781 } 782 return null; 783 } 784 785 /** 786 * Liest die Attribute eines Tags ein, dies Abh¦ngig von der Definition innerhalb der Tag-Lib. 787 * Hierbei unterscheiden wir vier verschiedene Arten von Attributen:<br> 788 * <ul> 789 * <li>FIX: Definierte Attribute Fix, fr jedes Attribut ist definiert ob es required ist oder nicht (gleich wie JSP). </li> 790 * <li>DYNAMIC: Die Attribute des Tag sind frei, keine Namen sind vorgegeben. 791 * Es kann aber definiert sein wieviele Attribute maximal und minimal verwendetet werden drfen.</li> 792 * <li>FULLDYNAMIC: Gleich wie DYNAMIC, jedoch kann der Name des Attribut auch ein dynamischer Wert sein (wie bei cfset).</li> 793 * <li>NONAME: Ein Tag welches nur ein Attribut besitzt ohne Name, sondern einfach nur mit einem Attribut Wert</li> 794 * </ul> 795 * <br /> 796 * EBNF:<br /> 797 * <code>({spaces attribute} "/>" | {spaces attribute} ">") | attribute-value;(* Welcher Teil der "oder" Bedingung ausgefhrt wird, ist abh¦ngig von der Tag Attribute Definition in der Tag Lib. *)</code> 798 * @param tag 799 * @param parent 800 * @throws TemplateException 801 */ 802 public static void attributes(Data data,TagLibTag tag, Tag parent) throws TemplateException { 803 int type=tag.getAttributeType(); 804 805 // Tag with attribute names 806 if( type!=TagLibTag.ATTRIBUTE_TYPE_NONAME) { 807 int min=tag.getMin(); 808 int max=tag.getMax(); 809 int count=0; 810 ArrayList<String> args=new ArrayList<String>(); 811 RefBoolean allowDefaultValue=new RefBooleanImpl(tag.getDefaultAttribute()!=null); 812 while(data.cfml.isValidIndex()) { 813 data.cfml.removeSpace(); 814 // if no more attributes break 815 if(data.cfml.isCurrent('/') || data.cfml.isCurrent('>')) break; 816 817 parent.addAttribute(attribute(data,tag,args,allowDefaultValue)); 818 count++; 819 } 820 821 // set default values 822 if(tag.hasDefaultValue()) { 823 Map<String, TagLibTagAttr> hash = tag.getAttributes(); 824 Iterator<String> it = hash.keySet().iterator(); 825 826 while(it.hasNext()) { 827 TagLibTagAttr att=(TagLibTagAttr) hash.get(it.next()); 828 if(!parent.containsAttribute(att.getName()) && att.hasDefaultValue()) { 829 830 Attribute attr=new Attribute(tag.getAttributeType()==TagLibTag.ATTRIBUTE_TYPE_DYNAMIC, 831 att.getName(), 832 Cast.toExpression(LitString.toExprString(Caster.toString(att.getDefaultValue(),null), -1),att.getType()),att.getType() 833 ); 834 parent.addAttribute(attr); 835 } 836 } 837 } 838 839 boolean hasAttributeCollection=args.contains("attributecollection"); 840 841 // to less attributes 842 if(!hasAttributeCollection && min>count) 843 throw createTemplateException(data.cfml,"the tag "+tag.getFullName()+" must have "+min+" attributes at least",tag); 844 845 // too much attributes 846 if(!hasAttributeCollection && max>0 && max<count) 847 throw createTemplateException(data.cfml,"the tag "+tag.getFullName()+" can have "+max+" attributes maximal",tag); 848 849 // not defined attributes 850 if(type==TagLibTag.ATTRIBUTE_TYPE_FIXED || type==TagLibTag.ATTRIBUTE_TYPE_MIXED) { 851 Map<String, TagLibTagAttr> hash = tag.getAttributes(); 852 Iterator<String> it = hash.keySet().iterator(); 853 854 while(it.hasNext()) { 855 TagLibTagAttr att=(TagLibTagAttr) hash.get(it.next()); 856 if(att.isRequired() && !args.contains(att.getName()) && att.getDefaultValue()==null) { 857 if(!hasAttributeCollection)throw createTemplateException(data.cfml,"attribute "+att.getName()+" is required for tag "+tag.getFullName(),tag); 858 parent.addMissingAttribute(att.getName(),att.getType()); 859 } 860 } 861 } 862 } 863 // tag without attributes name 864 else { 865 TagLibTagAttr attr=tag.getFirstAttribute(); 866 String strName="noname"; 867 String strType="any"; 868 boolean pe=true; 869 if(attr!=null) { 870 strName=attr.getName(); 871 strType=attr.getType(); 872 pe=attr.getRtexpr(); 873 } 874 //LitString.toExprString("",-1); 875 Attribute att=new Attribute(false,strName,attributeValue(data,tag,strType,pe,true,NullExpression.NULL_EXPRESSION),strType); 876 parent.addAttribute(att); 877 } 878 } 879 880 /** 881 * Liest ein einzelnes Atribut eines tag ein (nicht NONAME). 882 * <br /> 883 * EBNF:<br /> 884 * <code>attribute-name spaces "=" spaces attribute-value;</code> 885 * @param tag Definition des Tag das dieses Attribut enth¦lt. 886 * @param args Container zum Speichern einzelner Attribute Namen zum nachtr¦glichen Prufen gegen die Tag-Lib. 887 * @return Element Attribute Element. 888 * @throws TemplateException 889 */ 890 private static Attribute attribute(Data data,TagLibTag tag, ArrayList<String> args,RefBoolean allowDefaultValue) throws TemplateException { 891 Expression value=null; 892 893 // Name 894 StringBuffer sbType=new StringBuffer(); 895 RefBoolean dynamic=new RefBooleanImpl(false); 896 boolean isDefaultValue=false; 897 boolean[] parseExpression=new boolean[2]; 898 parseExpression[0]=true; 899 parseExpression[1]=false; 900 String name=attributeName(data.cfml,dynamic,args,tag,sbType,parseExpression,allowDefaultValue.toBooleanValue()); 901 902 // mixed in a noname attribute 903 if(StringUtil.isEmpty(name)){ 904 allowDefaultValue.setValue(false); 905 TagLibTagAttr attr = tag.getDefaultAttribute(); 906 if(attr==null) 907 throw new TemplateException(data.cfml,"Invalid Identifer."); 908 name=attr.getName(); 909 sbType.append(attr.getType()); 910 isDefaultValue=true; 911 } 912 913 914 comment(data.cfml,true); 915 916 if(isDefaultValue || data.cfml.forwardIfCurrent('=')) { 917 comment(data.cfml,true); 918 // Value 919 value=attributeValue(data,tag,sbType.toString(),parseExpression[0],false,LitString.toExprString("",-1)); 920 } 921 // default value boolean true 922 else { 923 value=LitBoolean.toExprBoolean(true, -1); 924 if(sbType.toString().length()>0) { 925 value=Cast.toExpression(value, sbType.toString()); 926 } 927 } 928 comment(data.cfml,true); 929 930 return new Attribute(dynamic.toBooleanValue(),name,value,sbType.toString()); 931 } 932 933 /** 934 * Liest den Namen eines Attribut ein, je nach Attribut-Definition innerhalb der Tag-Lib, 935 * wird der Name ber den identifier oder den Expression Transformer eingelesen. 936 * <ul> 937 * <li>FIX und DYNAMIC --> identifier </li> 938 * <li>FULLDYNAMIC --> Expression Transformer </li> 939 * </ul> 940 * <br /> 941 * EBNF:<br /> 942 * <code>("expression"|'expression'|expression) | identifier;(* Ruft identifier oder den Expression Transformer auf je nach Attribute Definition in der Tag Lib. *)</code> 943 * @param dynamic 944 * @param args Container zum Speichern einzelner Attribute Namen zum nachtr¦glichen Prufen gegen die Tag-Lib. 945 * @param tag Aktuelles tag aus der Tag-Lib 946 * @param sbType Die Methode speichert innerhalb von sbType den Typ des Tags, zur Interpretation in der attribute Methode. 947 * @param parseExpression Soll der Wert des Attributes geparst werden 948 * @return Attribute Name 949 * @throws TemplateException 950 */ 951 private static String attributeName(CFMLString cfml,RefBoolean dynamic, ArrayList<String> args, TagLibTag tag, 952 StringBuffer sbType, boolean[] parseExpression,boolean allowDefaultValue) throws TemplateException { 953 954 String _id = identifier(cfml,!allowDefaultValue); 955 if(StringUtil.isEmpty(_id)){ 956 return null; 957 } 958 959 int typeDef=tag.getAttributeType(); 960 String id=StringUtil.toLowerCase(_id); 961 if(args.contains(id)) throw createTemplateException(cfml,"you can't use the same tag attribute ["+id+"] twice",tag); 962 args.add(id); 963 964 if("attributecollection".equals(id)){ 965 dynamic.setValue(tag.getAttribute(id)==null); 966 sbType.append("struct"); 967 parseExpression[0]=true; 968 parseExpression[1]=true; 969 } 970 else if(typeDef==TagLibTag.ATTRIBUTE_TYPE_FIXED || typeDef==TagLibTag.ATTRIBUTE_TYPE_MIXED) { 971 TagLibTagAttr attr=tag.getAttribute(id); 972 if(attr==null) { 973 if(typeDef==TagLibTag.ATTRIBUTE_TYPE_FIXED) { 974 String names=tag.getAttributeNames(); 975 if(StringUtil.isEmpty(names)) 976 throw createTemplateException(cfml, 977 "Attribute "+id+" is not allowed for tag "+tag.getFullName(),tag); 978 979 throw createTemplateException(cfml, 980 "Attribute "+id+" is not allowed for tag "+tag.getFullName(), 981 "valid attribute names are ["+names+"]",tag); 982 } 983 dynamic.setValue(true); 984 } 985 else { 986 sbType.append(attr.getType()); 987 parseExpression[0]=attr.getRtexpr(); 988 } 989 } 990 else if(typeDef==TagLibTag.ATTRIBUTE_TYPE_DYNAMIC){ 991 dynamic.setValue(true); 992 } 993 return id; 994 } 995 996 997 998 999 1000 1001 1002 /** 1003 * Liest den Wert eines Attribut, mithilfe des innerhalb der Tag-Lib definierten Expression Transformer, ein. 1004 * <br /> 1005 * EBNF:<br /> 1006 * <code>expression;</code> 1007 * @param tag 1008 * @param type 1009 * @param parseExpression 1010 * @param isNonName 1011 * @return Element Eingelesener bersetzer Wert des Attributes. 1012 * @throws TemplateException 1013 */ 1014 public static Expression attributeValue(Data data,TagLibTag tag, String type,boolean parseExpression,boolean isNonName, Expression noExpression) throws TemplateException { 1015 Expression expr; 1016 try { 1017 ExprTransformer transfomer=null; 1018 if(parseExpression){ 1019 transfomer = tag.getTagLib().getExprTransfomer(); 1020 } 1021 else { 1022 if(data.set==null) { 1023 data.set=new SimpleExprTransformer('#'); 1024 //set.setSpecialChar(); 1025 } 1026 transfomer=data.set; 1027 } 1028 if(isNonName) { 1029 int pos=data.cfml.getPos(); 1030 try { 1031 expr=transfomer.transform(data.ep,data.flibs,data.cfml); 1032 } 1033 catch(TemplateException ete) { 1034 if(data.cfml.getPos()==pos)expr=noExpression; 1035 else throw ete; 1036 } 1037 } 1038 else expr=transfomer.transformAsString(data.ep,data.flibs,data.cfml,true); 1039 if(type.length()>0) { 1040 expr=Cast.toExpression(expr, type); 1041 } 1042 } catch (TagLibException e) { 1043 throw new TemplateException(data.cfml,e); 1044 } 1045 return expr; 1046 } 1047 1048 /** 1049 * Liest einen Identifier ein und gibt diesen als String zurck. 1050 * <br /> 1051 * EBNF:<br /> 1052 * <code>(letter | "_") {letter | "_"|digit};</code> 1053 * @param throwError throw error or return null if name is invalid 1054 * @return Identifier String. 1055 * @throws TemplateException 1056 */ 1057 public static String identifier(CFMLString cfml,boolean throwError) throws TemplateException { 1058 int start = cfml.getPos(); 1059 if(!cfml.isCurrentBetween('a','z') && !cfml.isCurrent('_')) { 1060 if(throwError)throw new TemplateException(cfml,"Invalid Identifer."); 1061 return null; 1062 } 1063 do { 1064 cfml.next(); 1065 if(!(cfml.isCurrentBetween('a','z') 1066 || cfml.isCurrentBetween('0','9') 1067 || cfml.isCurrent('_') 1068 || cfml.isCurrent(':') 1069 || cfml.isCurrent('-'))) { 1070 break; 1071 } 1072 } 1073 while (cfml.isValidIndex()); 1074 return cfml.substring(start,cfml.getPos()-start); 1075 } 1076 1077 1078 public static TemplateException createTemplateException(CFMLString cfml,String msg, String detail,TagLibTag tag) { 1079 TemplateException te = new TemplateException(cfml,msg,detail); 1080 setAddional(te,tag); 1081 return te; 1082 } 1083 public static TemplateException createTemplateException(CFMLString cfml,String msg, TagLibTag tag) { 1084 TemplateException te = new TemplateException(cfml,msg); 1085 setAddional(te,tag); 1086 return te; 1087 } 1088 1089 public static TemplateException setAddional(TemplateException te, TagLibTag tlt) { 1090 setAddional((PageExceptionImpl)te, tlt); 1091 return te; 1092 } 1093 1094 public static ApplicationException setAddional(ApplicationException ae, TagLibTag tlt) { 1095 setAddional((PageExceptionImpl)ae, tlt); 1096 return ae; 1097 } 1098 1099 1100 private static void setAddional(PageExceptionImpl pe, TagLibTag tlt) { 1101 Map<String, TagLibTagAttr> attrs = tlt.getAttributes(); 1102 Iterator<Entry<String, TagLibTagAttr>> it = attrs.entrySet().iterator(); 1103 Entry<String, TagLibTagAttr> entry; 1104 TagLibTagAttr attr; 1105 1106 // Pattern 1107 StringBuilder pattern=new StringBuilder("<"); 1108 pattern.append((tlt.getFullName())); 1109 StringBuilder req=new StringBuilder(); 1110 StringBuilder opt=new StringBuilder(); 1111 StringBuilder tmp; 1112 1113 1114 pattern.append(" "); 1115 int c=0; 1116 while(it.hasNext()){ 1117 entry = it.next(); 1118 attr=entry.getValue(); 1119 tmp=attr.isRequired()?req:opt; 1120 1121 tmp.append(" "); 1122 if(!attr.isRequired()) tmp.append("["); 1123 if(c++>0)pattern.append(" "); 1124 tmp.append(attr.getName()); 1125 tmp.append("\""); 1126 tmp.append(attr.getType()); 1127 tmp.append("\""); 1128 if(!attr.isRequired()) tmp.append("]"); 1129 } 1130 1131 if(req.length()>0)pattern.append(req); 1132 if(opt.length()>0)pattern.append(opt); 1133 1134 if(tlt.getAttributeType()==TagLibTag.ATTRIBUTE_TYPE_MIXED || tlt.getAttributeType()==TagLibTag.ATTRIBUTE_TYPE_DYNAMIC) 1135 pattern.append(" ..."); 1136 pattern.append(">"); 1137 if(tlt.getHasBody()) { 1138 if(tlt.isBodyReq()){ 1139 pattern.append("</"); 1140 pattern.append(tlt.getFullName()); 1141 pattern.append(">"); 1142 } 1143 else if(tlt.isBodyFree()){ 1144 pattern.append("[</"); 1145 pattern.append(tlt.getFullName()); 1146 pattern.append(">]"); 1147 } 1148 } 1149 1150 pe.setAdditional("Pattern", pattern); 1151 1152 // Documentation 1153 StringBuilder doc=new StringBuilder(tlt.getDescription()); 1154 req=new StringBuilder(); 1155 opt=new StringBuilder(); 1156 1157 doc.append("\n"); 1158 1159 it = attrs.entrySet().iterator(); 1160 while(it.hasNext()){ 1161 entry = it.next(); 1162 attr=entry.getValue(); 1163 tmp=attr.isRequired()?req:opt; 1164 1165 tmp.append("* "); 1166 tmp.append(attr.getName()); 1167 tmp.append(" ("); 1168 tmp.append(attr.getType()); 1169 tmp.append("): "); 1170 tmp.append(attr.getDescription()); 1171 tmp.append("\n"); 1172 } 1173 1174 if(req.length()>0)doc.append("\nRequired:\n").append(req); 1175 if(opt.length()>0)doc.append("\nOptional:\n").append(opt); 1176 1177 pe.setAdditional("Documentation", doc); 1178 } 1179 1180 } 1181 1182 1183 1184 1185 1186 1187 1188