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 k￶nnen, 
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 k￶nnen 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