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