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