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