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.script;
020
021import java.util.ArrayList;
022import java.util.HashMap;
023import java.util.Iterator;
024import java.util.Map;
025import java.util.Map.Entry;
026
027import lucee.commons.lang.ExceptionUtil;
028import lucee.commons.lang.StringUtil;
029import lucee.commons.lang.types.RefBoolean;
030import lucee.commons.lang.types.RefBooleanImpl;
031import lucee.runtime.Component;
032import lucee.runtime.PageSource;
033import lucee.runtime.exp.TemplateException;
034import lucee.runtime.functions.system.CFFunction;
035import lucee.runtime.type.util.ArrayUtil;
036import lucee.runtime.type.util.ComponentUtil;
037import lucee.transformer.bytecode.Body;
038import lucee.transformer.bytecode.BodyBase;
039import lucee.transformer.bytecode.BytecodeException;
040import lucee.transformer.bytecode.FunctionBody;
041import lucee.transformer.bytecode.Position;
042import lucee.transformer.bytecode.ScriptBody;
043import lucee.transformer.bytecode.Statement;
044import lucee.transformer.bytecode.cast.CastBoolean;
045import lucee.transformer.bytecode.cast.CastOther;
046import lucee.transformer.bytecode.cast.CastString;
047import lucee.transformer.bytecode.expression.ClosureAsExpression;
048import lucee.transformer.bytecode.expression.ExprBoolean;
049import lucee.transformer.bytecode.expression.Expression;
050import lucee.transformer.bytecode.expression.var.Variable;
051import lucee.transformer.bytecode.literal.LitBoolean;
052import lucee.transformer.bytecode.literal.LitString;
053import lucee.transformer.bytecode.statement.Condition;
054import lucee.transformer.bytecode.statement.Condition.Pair;
055import lucee.transformer.bytecode.statement.DoWhile;
056import lucee.transformer.bytecode.statement.ExpressionAsStatement;
057import lucee.transformer.bytecode.statement.For;
058import lucee.transformer.bytecode.statement.ForEach;
059import lucee.transformer.bytecode.statement.Return;
060import lucee.transformer.bytecode.statement.Switch;
061import lucee.transformer.bytecode.statement.TryCatchFinally;
062import lucee.transformer.bytecode.statement.While;
063import lucee.transformer.bytecode.statement.tag.Attribute;
064import lucee.transformer.bytecode.statement.tag.Tag;
065import lucee.transformer.bytecode.statement.tag.TagOther;
066import lucee.transformer.bytecode.statement.tag.TagParam;
067import lucee.transformer.bytecode.statement.udf.Closure;
068import lucee.transformer.bytecode.statement.udf.Function;
069import lucee.transformer.bytecode.statement.udf.FunctionImpl;
070import lucee.transformer.cfml.evaluator.EvaluatorException;
071import lucee.transformer.cfml.evaluator.impl.ProcessingDirectiveException;
072import lucee.transformer.cfml.expression.AbstrCFMLExprTransformer;
073import lucee.transformer.cfml.tag.CFMLTransformer;
074import lucee.transformer.library.function.FunctionLibFunction;
075import lucee.transformer.library.tag.TagLib;
076import lucee.transformer.library.tag.TagLibException;
077import lucee.transformer.library.tag.TagLibTag;
078import lucee.transformer.library.tag.TagLibTagAttr;
079import lucee.transformer.library.tag.TagLibTagScript;
080import lucee.transformer.util.CFMLString;
081
082
083/**     
084 * Innerhalb des Tag script kann in CFML eine eigene Scriptsprache verwendet werden, 
085 * welche sich an Javascript orientiert. 
086 * Da der data.cfml Transformer keine Spezialfaelle zulaesst, 
087 * also Tags einfach anhand der eingegeben TLD einliest und transformiert, 
088 * aus diesem Grund wird der Inhalt des Tag script einfach als Zeichenkette eingelesen.
089 * Erst durch den Evaluator (siehe 3.3), der fuer das Tag script definiert ist, 
090 * wird der Inhalt des Tag script uebersetzt.
091 * 
092 */
093public abstract class AbstrCFMLScriptTransformer extends AbstrCFMLExprTransformer {
094
095        private static final String[] IGNORE_LIST_COMPONENT = new String[]{
096                "output","synchronized","extends","implements","displayname","style","persistent","accessors"};
097        private static final String[] IGNORE_LIST_INTERFACE = new String[]{
098                "output","extends","displayname","style","persistent","accessors"};
099        private static final String[] IGNORE_LIST_PROPERTY = new String[]{
100                "default","fieldtype","name","type","persistent","remotingFetch","column","generator","length",
101                "ormtype","params","unSavedValue","dbdefault","formula","generated","insert","optimisticlock",
102                "update","notnull","precision","scale","unique","uniquekey","source"
103        };
104 
105
106        private static EndCondition SEMI_BLOCK=new EndCondition() {
107                public boolean isEnd(ExprData data) {
108                        return data.cfml.isCurrent('{') || data.cfml.isCurrent(';');
109                }
110        };
111        private static EndCondition SEMI=new EndCondition() {
112                public boolean isEnd(ExprData data) {
113                        return data.cfml.isCurrent(';');
114                }
115        };
116        private static EndCondition COMMA_ENDBRACKED=new EndCondition() {
117                public boolean isEnd(ExprData data) {
118                        return data.cfml.isCurrent(',') || data.cfml.isCurrent(')');
119                }
120        };
121        
122        private static EndCondition BRACKED=new EndCondition() {
123                public boolean isEnd(ExprData data) {
124                        return data.cfml.isCurrent(')');
125                }
126        };
127        
128        
129        
130        
131
132        private short ATTR_TYPE_NONE=TagLibTagAttr.SCRIPT_SUPPORT_NONE;
133        private short ATTR_TYPE_OPTIONAL=TagLibTagAttr.SCRIPT_SUPPORT_OPTIONAL;
134        private short ATTR_TYPE_REQUIRED=TagLibTagAttr.SCRIPT_SUPPORT_REQUIRED;
135        
136        public static class ComponentTemplateException extends TemplateException {
137                private static final long serialVersionUID = -8103635220891288231L;
138                
139                private TemplateException te;
140
141                public ComponentTemplateException(TemplateException te){
142                        super(te.getPageSource(),te.getLine(),0,te.getMessage());
143                        this.te=te;
144                        
145                }
146
147                /**
148                 * @return the te
149                 */
150                public TemplateException getTemplateException() {
151                        return te;
152                }
153        }
154
155        private static final Expression NULL = LitString.toExprString("NULL"); 
156        private static final Attribute ANY = new Attribute(false,"type",LitString.toExprString("any"),"string");
157        private static final char NO_ATTR_SEP = 0;
158        
159        /** 
160         * Liest saemtliche Statements des CFScriptString ein. 
161         * <br />
162         * EBNF:<br />
163         * <code>{statement spaces};</code>
164         * @return a statement
165         * @throws TemplateException
166         */
167        protected final Body statements(ExprData data) throws TemplateException {
168                ScriptBody body=new ScriptBody();
169                
170                statements(data,body,true);
171        return body;
172        }
173        
174        /**
175         * Liest saemtliche Statements des CFScriptString ein. 
176         * <br />
177         * EBNF:<br />
178         * <code>{statement spaces};</code>
179         * @param parent uebergeornetes Element dem das Statement zugewiesen wird.
180         * @param isRoot befindet sich der Parser im root des data.cfml Docs
181         * @throws TemplateException
182         */
183        private final void statements(ExprData data,Body body, boolean isRoot) throws TemplateException {
184                do {
185                        if(isRoot && isFinish(data))return;
186                        statement(data,body);
187                        comments(data);
188                }
189                while(data.cfml.isValidIndex() && !data.cfml.isCurrent('}'));
190        }
191        
192        /** 
193         * Liest ein einzelnes Statement ein (if,for,while usw.).
194         * <br />
195         * EBNF:<br />
196         * <code>";" | "if" spaces "(" ifStatement | "function " funcStatement |  "while" spaces "(" whileStatement  |  
197                          "do" spaces "{" doStatement  | "for" spaces "(" forStatement | "return" returnStatement | 
198                          "break" breakStatement | "continue" continueStatement | "/*" comment | expressionStatement;</code>
199         * @param parent uebergeornetes Element dem das Statement zugewiesen wird.
200         * @throws TemplateException
201         */
202        private final void statement(ExprData data,Body parent) throws TemplateException {
203                statement(data, parent, data.context);
204        }
205        private boolean statement(ExprData data,Body parent,short context) throws TemplateException {
206                short prior=data.context;
207                data.context=context;
208                comments(data);
209                Statement child=null;
210                if(data.cfml.forwardIfCurrent(';')){return true;}
211                else if((child=ifStatement(data))!=null)                                parent.addStatement(child);
212                else if((child=propertyStatement(data,parent))!=null)   parent.addStatement(child);
213                else if((child=paramStatement(data,parent))!=null)      parent.addStatement(child);
214                else if((child=funcStatement(data,parent))!=null)               parent.addStatement(child);
215                else if((child=whileStatement(data))!=null)                     parent.addStatement(child);
216                else if((child=doStatement(data))!=null)                                parent.addStatement(child);
217                else if((child=forStatement(data))!=null)                               parent.addStatement(child);
218                else if((child=returnStatement(data))!=null)                    parent.addStatement(child);
219                else if((child=switchStatement(data))!=null)                    parent.addStatement(child);
220                else if((child=tryStatement(data))!=null)                               parent.addStatement(child);
221                else if((child=tagStatement(data,parent))!=null)        parent.addStatement(child);
222                else if((child=cftagStatement(data,parent))!=null)      parent.addStatement(child);
223                else if(block(data,parent)){}
224                else parent.addStatement(expressionStatement(data,parent));
225                data.docComment=null;
226                data.context=prior;
227                
228                return false;
229        }
230        
231        /**
232         * Liest ein if Statement ein.
233         * <br />
234         * EBNF:<br />
235         * <code>spaces condition spaces ")" spaces block {"else if" spaces "(" elseifStatement spaces }
236                         [("else"  spaces "(" | "else ") elseStatement spaces];</code>
237         * @return if Statement
238         * @throws TemplateException
239         */
240        private final Statement ifStatement(ExprData data) throws TemplateException {
241                if(!data.cfml.forwardIfCurrent("if",'(')) return null;
242                
243                
244                Position line = data.cfml.getPosition();
245                
246                Body body=new BodyBase();
247                Condition cont=new Condition(condition(data),body,line,null);
248                
249                if(!data.cfml.forwardIfCurrent(')')) throw new TemplateException(data.cfml,"if statement must end with a [)]");
250                // ex block
251                statement(data,body,CTX_IF);
252                // else if
253                comments(data);
254                while(elseifStatement(data,cont)) {
255                        comments(data);
256                }
257                // else
258                 if(elseStatement(data,cont)) {
259                        comments(data);
260                 }
261
262                cont.setEnd(data.cfml.getPosition());
263                return cont;
264        }
265        
266        /**
267         * Liest ein else if Statement ein.
268         * <br />
269         * EBNF:<br />
270         * <code>spaces condition spaces ")" spaces block;</code>
271         * @return else if Statement
272         * @throws TemplateException
273         */
274        private  final boolean elseifStatement(ExprData data,Condition cont) throws TemplateException {
275                int pos=data.cfml.getPos();
276                if(!data.cfml.forwardIfCurrent("else")) return false;
277                
278                comments(data);
279                if(!data.cfml.forwardIfCurrent("if",'(')) {
280                        data.cfml.setPos(pos);
281                        return false;
282                }
283                        
284                Position line = data.cfml.getPosition();
285                Body body=new BodyBase();
286                Pair pair = cont.addElseIf(condition(data), body, line,null);
287
288                if(!data.cfml.forwardIfCurrent(')'))
289                        throw new TemplateException(data.cfml,"else if statement must end with a [)]");
290                // ex block
291                statement(data,body,CTX_ELSE_IF);
292                pair.end=data.cfml.getPosition();
293                return true;
294        }
295        
296        /**
297         * Liest ein else Statement ein.
298         * <br />
299         * EBNF:<br />
300         * <code>block;</code>
301         * @return else Statement
302         * @throws TemplateException
303         * 
304         */
305        private final boolean elseStatement(ExprData data,Condition cont) throws TemplateException {
306                if(!data.cfml.forwardIfCurrent("else",'{') && !data.cfml.forwardIfCurrent("else ") && !data.cfml.forwardIfCurrent("else",'/')) 
307                        return false;
308
309                // start (
310                data.cfml.previous();
311                // ex block
312                Body body=new BodyBase();
313                Pair p = cont.setElse(body, data.cfml.getPosition(),null);
314                statement(data,body,CTX_ELSE);
315                p.end=data.cfml.getPosition();
316                return true;
317        }
318        
319
320        private final boolean finallyStatement(ExprData data,TryCatchFinally tcf) throws TemplateException {
321                if(!data.cfml.forwardIfCurrent("finally",'{') && !data.cfml.forwardIfCurrent("finally ") && !data.cfml.forwardIfCurrent("finally",'/')) 
322                        return false;
323
324                // start (
325                data.cfml.previous();
326                // ex block
327                Body body=new BodyBase();
328                tcf.setFinally(body, data.cfml.getPosition());
329                statement(data,body,CTX_FINALLY);
330                
331                return true;
332        }
333        
334        /**
335         * Liest ein while Statement ein.
336         * <br />
337         * EBNF:<br />
338         * <code>spaces condition spaces ")" spaces block;</code>
339         * @return while Statement
340         * @throws TemplateException
341         */
342        private final While whileStatement(ExprData data) throws TemplateException {
343                int pos=data.cfml.getPos();
344                
345                // id
346                String id=variableDec(data, false);
347                if(id==null) {
348                        data.cfml.setPos(pos);
349                        return null;
350                }
351                if(id.equalsIgnoreCase("while")){
352                        id=null;
353                        data.cfml.removeSpace();
354                        if(!data.cfml.forwardIfCurrent('(')){
355                                data.cfml.setPos(pos);
356                                return null;
357                        }       
358                }
359                else {
360                        data.cfml.removeSpace();
361                        if(!data.cfml.forwardIfCurrent(':')){
362                                data.cfml.setPos(pos);
363                                return null;
364                        }
365                        data.cfml.removeSpace();
366                        
367                        if(!data.cfml.forwardIfCurrent("while",'(')){
368                                data.cfml.setPos(pos);
369                                return null;
370                        }
371                }
372                
373                Position line = data.cfml.getPosition();
374                Body body=new BodyBase();
375                While whil=new While(condition(data),body,line,null,id);
376                
377                if(!data.cfml.forwardIfCurrent(')'))
378                        throw new TemplateException(data.cfml,"while statement must end with a [)]");
379                
380                statement(data,body,CTX_WHILE);
381                whil.setEnd(data.cfml.getPosition());
382                return whil;
383        }
384        
385        /**
386         * Liest ein switch Statment ein
387         * @return switch Statement
388         * @throws TemplateException
389         */
390        private final Switch switchStatement(ExprData data) throws TemplateException {
391                if(!data.cfml.forwardIfCurrent("switch",'('))
392                        return null;
393                
394                Position line = data.cfml.getPosition();
395                
396                comments(data);
397                Expression expr = super.expression(data);
398                comments(data);
399                // end )
400                if(!data.cfml.forwardIfCurrent(')'))
401                        throw new TemplateException(data.cfml,"switch statement must end with a [)]");
402                comments(data);
403
404                if(!data.cfml.forwardIfCurrent('{'))
405                        throw new TemplateException(data.cfml,"switch statement must have a starting  [{]");
406
407                Switch swit=new Switch(expr,line,null);
408                
409                //      cases
410                 //Node child=null;
411                 comments(data);
412                 while(caseStatement(data,swit)) {
413                         comments(data);
414                 }
415                 // default
416                  if(defaultStatement(data,swit)) {
417                        comments(data);
418                  }
419                  
420                  while(caseStatement(data,swit)) {
421                                 comments(data);
422                         }
423                  
424                  
425                // }
426                if(!data.cfml.forwardIfCurrent('}'))
427                        throw new TemplateException(data.cfml,"invalid construct in switch statement");
428                swit.setEnd(data.cfml.getPosition());
429                return swit;
430        }
431        
432        /**
433         * Liest ein Case Statement ein
434         * @return case Statement
435         * @throws TemplateException
436         */
437        private final boolean caseStatement(ExprData data,Switch swit) throws TemplateException {
438                if(!data.cfml.forwardIfCurrentAndNoWordAfter("case"))
439                        return false;
440                
441                //int line=data.cfml.getLine();         
442                comments(data);
443                Expression expr = super.expression(data);
444                comments(data);
445                
446                if(!data.cfml.forwardIfCurrent(':'))
447                        throw new TemplateException(data.cfml,"case body must start with [:]");
448                
449                Body body=new BodyBase();
450                switchBlock(data,body);
451                swit.addCase(expr, body);
452                return true;
453        }
454        
455        /**
456         * Liest ein default Statement ein
457         * @return default Statement
458         * @throws TemplateException
459         */
460        private final boolean defaultStatement(ExprData data,Switch swit) throws TemplateException {
461                if(!data.cfml.forwardIfCurrent("default",':'))
462                        return false;
463                
464                //int line=data.cfml.getLine();
465                
466                Body body=new BodyBase();
467                swit.setDefaultCase(body);
468                switchBlock(data,body);
469                return true;
470        }
471        
472        /**
473         * Liest ein Switch Block ein
474         * @param block
475         * @throws TemplateException
476         */
477        private final void switchBlock(ExprData data,Body body) throws TemplateException {
478                while(data.cfml.isValidIndex()) {
479                        comments(data);
480                        if(data.cfml.isCurrent("case ") || data.cfml.isCurrent("default",':') || data.cfml.isCurrent('}')) 
481                                return;
482                        statement(data,body,CTX_SWITCH);
483                }
484        }
485        
486        
487        /**
488         * Liest ein do Statement ein.
489         * <br />
490         * EBNF:<br />
491         * <code>block spaces "while" spaces "(" spaces condition spaces ")";</code>
492         * @return do Statement
493         * @throws TemplateException
494         */
495        private final DoWhile doStatement(ExprData data) throws TemplateException {
496                int pos=data.cfml.getPos();
497                
498                // id
499                String id=variableDec(data, false);
500                if(id==null) {
501                        data.cfml.setPos(pos);
502                        return null;
503                }
504                if(id.equalsIgnoreCase("do")){
505                        id=null;
506                        if(!data.cfml.isCurrent('{') && !data.cfml.isCurrent(' ') && !data.cfml.isCurrent('/')) {
507                                data.cfml.setPos(pos);
508                                return null;
509                        }       
510                }
511                else {
512                        data.cfml.removeSpace();
513                        if(!data.cfml.forwardIfCurrent(':')){
514                                data.cfml.setPos(pos);
515                                return null;
516                        }
517                        data.cfml.removeSpace();
518                        
519                        if(!data.cfml.forwardIfCurrent("do",'{') && !data.cfml.forwardIfCurrent("do ") && !data.cfml.forwardIfCurrent("do",'/')) {
520                                data.cfml.setPos(pos);
521                                return null;
522                        }
523                        data.cfml.previous();
524                }
525                
526                Position line = data.cfml.getPosition();
527                Body body=new BodyBase();
528                
529                statement(data,body,CTX_DO_WHILE);
530                
531                
532                comments(data);
533                if(!data.cfml.forwardIfCurrent("while",'('))
534                        throw new TemplateException(data.cfml,"do statement must have a while at the end");
535                
536                DoWhile doWhile=new DoWhile(condition(data),body,line,data.cfml.getPosition(),id);
537                
538                if(!data.cfml.forwardIfCurrent(')'))
539                        throw new TemplateException(data.cfml,"do statement must end with a [)]");
540                
541                
542                return doWhile;
543        }
544        
545        /**
546         * Liest ein for Statement ein.
547         * <br />
548         * EBNF:<br />
549         * <code>expression spaces ";" spaces condition spaces ";" spaces expression spaces ")" spaces block;</code>
550         * @return for Statement
551         * @throws TemplateException
552         */
553        private final Statement forStatement(ExprData data) throws TemplateException {
554                
555int pos=data.cfml.getPos();
556                
557                // id
558                String id=variableDec(data, false);
559                if(id==null) {
560                        data.cfml.setPos(pos);
561                        return null;
562                }
563                if(id.equalsIgnoreCase("for")){
564                        id=null;
565                        data.cfml.removeSpace();
566                        if(!data.cfml.forwardIfCurrent('(')){
567                                data.cfml.setPos(pos);
568                                return null;
569                        }       
570                }
571                else {
572                        data.cfml.removeSpace();
573                        if(!data.cfml.forwardIfCurrent(':')){
574                                data.cfml.setPos(pos);
575                                return null;
576                        }
577                        data.cfml.removeSpace();
578                        
579                        if(!data.cfml.forwardIfCurrent("for",'(')){
580                                data.cfml.setPos(pos);
581                                return null;
582                        }
583                }
584                
585                
586                
587                
588                //if(!data.cfml.forwardIfCurrent("for",'(')) 
589                //      return null;
590                
591                
592                
593                Expression left=null;
594                Body body=new BodyBase();
595                Position line = data.cfml.getPosition();
596                comments(data);
597                if(!data.cfml.isCurrent(';')) {
598                        // left
599                        left=expression(data);
600                        comments(data);
601                }
602                // middle for
603                        if(data.cfml.forwardIfCurrent(';')) {
604
605                                Expression cont=null;
606                                Expression update=null;
607                                // condition
608                                        comments(data);
609                                        if(!data.cfml.isCurrent(';')) {
610                                                cont=condition(data);
611                                                comments(data);
612                                        }
613                                // middle
614                                if(!data.cfml.forwardIfCurrent(';'))
615                                        throw new TemplateException(data.cfml,"invalid syntax in for statement");
616                                // update
617                                        comments(data);
618                                        if(!data.cfml.isCurrent(')')) {
619                                                update=expression(data);
620                                                comments(data);
621                                        }
622                                // start )
623                                if(!data.cfml.forwardIfCurrent(')'))
624                                        throw new TemplateException(data.cfml,"invalid syntax in for statement, for statement must end with a [)]");
625                                // ex block
626                                statement(data,body,CTX_FOR);
627                
628                                return new For(left,cont,update,body,line,data.cfml.getPosition(),id);                                  
629                        }
630                // middle foreach
631                        else if(data.cfml.forwardIfCurrent("in")) {
632                                // condition
633                                        comments(data);
634                                        Expression value = expression(data);
635                                        comments(data);
636                                if(!data.cfml.forwardIfCurrent(')'))
637                                        throw new TemplateException(data.cfml,"invalid syntax in for statement, for statement must end with a [)]");
638                                
639                                // ex block
640                                statement(data,body,CTX_FOR);
641                                if(!(left instanceof Variable))
642                                        throw new TemplateException(data.cfml,"invalid syntax in for statement, left value is invalid");
643                                
644                                //if(!(value instanceof Variable || value instanceof LitString))
645                                //      throw new TemplateException(data.cfml,"invalid syntax in for statement, right value is invalid");
646                                return new ForEach((Variable)left,value,body,line,data.cfml.getPosition(),id);  
647                        }
648                        else 
649                                throw new TemplateException(data.cfml,"invalid syntax in for statement");
650        }
651        
652        /**
653         * Liest ein function Statement ein.
654         * <br />
655         * EBNF:<br />
656         * <code>identifier spaces "(" spaces identifier spaces {"," spaces identifier spaces} ")" spaces block;</code>
657         * @return function Statement
658         * @throws TemplateException
659         */
660        private final Function funcStatement(ExprData data,Body parent) throws TemplateException {
661                int pos=data.cfml.getPos();
662                
663                // access modifier
664                String strAccess=variableDec(data, false);
665                if(strAccess==null) {
666                        data.cfml.setPos(pos);
667                        return null;
668                }
669                
670                String rtnType=null;
671                if(strAccess.equalsIgnoreCase("FUNCTION")){
672                        strAccess=null;
673                        comments(data);
674                        // only happens when return type is function
675                        if(data.cfml.forwardIfCurrent("function ")){
676                                rtnType="function";
677                                comments(data);
678                        }
679                }
680                else{
681                        comments(data);
682                        rtnType=variableDec(data, false);
683                        if(rtnType==null){
684                                data.cfml.setPos(pos);
685                                return null;
686                        }
687                        if(rtnType.equalsIgnoreCase("FUNCTION")){
688                                comments(data);
689                                // only happens when return type is function
690                                if(data.cfml.forwardIfCurrent("function ")){
691                                        comments(data);
692                                }
693                                else rtnType=null;
694                        }
695                        comments(data);
696                        
697                        if(rtnType!=null && !data.cfml.forwardIfCurrent("function ") && !rtnType.equalsIgnoreCase("FUNCTION")){
698                                data.cfml.setPos(pos);
699                                return null;
700                        }
701                        comments(data);
702                }
703
704                // check access returntype
705                int access=Component.ACCESS_PUBLIC;
706                if(strAccess!=null && rtnType!=null){
707                        access = ComponentUtil.toIntAccess(strAccess,-1);
708                        if(access==-1)
709                                throw new TemplateException(data.cfml,"invalid access type ["+strAccess+"], access types are remote, public, package, private");
710                }
711                if(strAccess!=null && rtnType==null){
712                        access = ComponentUtil.toIntAccess(strAccess,-1);
713                        if(access==-1){
714                                rtnType=strAccess;
715                                strAccess=null;
716                                access=Component.ACCESS_PUBLIC;
717                        }
718                }
719                
720                
721                
722                Position line = data.cfml.getPosition();
723                
724                comments(data);
725                
726                // Name
727                        String id=identifier(data,false);
728                        
729                        
730                        if(id==null) {
731                                if(data.cfml.isCurrent('(')) {
732                                        data.cfml.setPos(pos);
733                                        return null;
734                                }
735                                throw new TemplateException(data.cfml,"invalid name for a function");
736                        }
737                        
738                        if(!data.isCFC && !data.isInterface){
739                                FunctionLibFunction flf = getFLF(data,id);
740                                if(flf!=null && flf.getClazz()!=CFFunction.class) {
741                                        PageSource ps = data.cfml.getPageSource();
742                                        String path=null;
743                                        if(ps!=null) {
744                                                path = ps.getDisplayPath();
745                                                path=path.replace('\\', '/');
746                                        }
747                                        if(path==null || path.indexOf("/library/function/")==-1)// TODO make better
748                                                throw new TemplateException(data.cfml,"The name ["+id+"] is already used by a built in Function");
749                                }
750                        }
751                        return closurePart(data, id,access,rtnType,line,false);
752        }
753
754        protected  final Function closurePart(ExprData data, String id, int access, String rtnType, Position line,boolean closure) throws TemplateException {           
755                
756                Body body=new FunctionBody();
757                Function func=closure?
758                                new Closure(data.page,id,access,rtnType,body,line,null)
759                                :new FunctionImpl(data.page,id,access,rtnType,body,line,null);
760                
761                        comments(data);
762                        if(!data.cfml.forwardIfCurrent('('))
763                                throw new TemplateException(data.cfml,"invalid syntax in function head, missing begin [(]");
764                
765                        // arguments
766                        LitBoolean passByRef;
767                        Expression displayName;
768                        Expression hint;
769                        Map<String,Attribute> meta;
770                        String _name;
771                        do      {
772                                comments(data);
773                                // finish
774                                if(data.cfml.isCurrent(')'))break;
775                                
776                                // attribute
777                                
778                                // name
779                                //String idName=identifier(data,false,true);
780                                boolean required=false;
781                                
782                                String idName = variableDec(data, false);
783                                // required
784                                if("required".equalsIgnoreCase(idName)){
785                                        comments(data);
786                                        String idName2=variableDec(data, false);
787                                        if(idName2!=null){
788                                                idName=idName2;
789                                                required=true;
790                                        }
791                                }
792                                
793                                
794                                String typeName="any";
795                                if(idName==null) throw new TemplateException(data.cfml,"invalid argument definition");
796                                comments(data);
797                                if(!data.cfml.isCurrent(')') && !data.cfml.isCurrent('=') && !data.cfml.isCurrent(':') && !data.cfml.isCurrent(',')) {
798                                        typeName=idName.toLowerCase();
799                                        idName=identifier(data,false); // MUST was upper case before, is this a problem?
800                                }
801                                else if(idName.indexOf('.')!=-1 || idName.indexOf('[')!=-1) {
802                                        throw new TemplateException(data.cfml,"invalid argument name ["+idName+"] definition");
803                                }
804                                
805                                comments(data);
806                                Expression defaultValue;
807                                if(data.cfml.isCurrent('=') || data.cfml.isCurrent(':')) {
808                                        data.cfml.next();
809                                        comments(data);
810                                        defaultValue=expression(data);
811                                }
812                                else defaultValue=null;
813                                
814                                // assign meta data defined in doc comment
815                                passByRef = LitBoolean.TRUE;
816                                displayName=LitString.EMPTY;
817                                hint=LitString.EMPTY;
818                                meta=null;
819                                if(data.docComment!=null){
820                                        Map<String, Attribute> params = data.docComment.getParams();
821                                        Attribute[] attrs = params.values().toArray(new Attribute[params.size()]);
822                                        Attribute attr;
823                                        String name;
824                                        
825                                        for(int i=0;i<attrs.length;i++){
826                                                attr=attrs[i];
827                                                name=attr.getName();
828                                                // hint
829                                                if(idName.equalsIgnoreCase(name) || name.equalsIgnoreCase(idName+".hint")) {
830                                                        hint=CastString.toExprString(attr.getValue());
831                                                        params.remove(name);
832                                                }
833                                                //meta
834                                                if(StringUtil.startsWithIgnoreCase(name, idName+".")) {
835                                                        if(name.length()>idName.length()+1){
836                                                                if(meta==null) meta=new HashMap<String, Attribute>();
837                                                                _name=name.substring(idName.length()+1);
838                                                                meta.put(_name, new Attribute(attr.isDynamicType(), _name,attr.getValue(), attr.getType()));
839                                                        }
840                                                        params.remove(name);
841                                                }
842                                        }
843                                        
844                                }
845                                
846                                // argument attributes
847                                Attribute[] _attrs = attributes(null,null,data,COMMA_ENDBRACKED,LitString.EMPTY,Boolean.TRUE,null,false,NO_ATTR_SEP,true);
848                                Attribute _attr;
849                                if(!ArrayUtil.isEmpty(_attrs)){
850                                        if(meta==null) meta=new HashMap<String, Attribute>();
851                                        for(int i=0;i<_attrs.length;i++){
852                                                _attr=_attrs[i];
853                                                meta.put(_attr.getName(), _attr);
854                                        }
855                                }
856                                
857                                func.addArgument(
858                                                LitString.toExprString(idName),
859                                                LitString.toExprString(typeName),
860                                                LitBoolean.toExprBoolean(required),
861                                                defaultValue,passByRef,displayName,hint,meta);
862                                
863                                comments(data);
864                        }
865                        while(data.cfml.forwardIfCurrent(','));
866
867                
868                // end )
869                        comments(data);
870                        if(!data.cfml.forwardIfCurrent(')'))
871                                throw new TemplateException(data.cfml,"invalid syntax in function head, missing ending [)]");
872                
873                //TagLibTag tlt = CFMLTransformer.getTLT(data.cfml,"function");
874                
875                // doc comment
876                if(data.docComment!=null){
877                        func.setHint(data.docComment.getHint());
878                        
879                        
880                        // params
881                        /*Map<String, Attribute> params = data.docComment.getParams();
882                        Iterator<Attribute> it = params.values().iterator();
883                        Attribute attr;
884                        String name;
885                        while(it.hasNext()){
886                                attr=it.next();
887                                name=attr.getName();
888                        }*/
889                        
890                        func.setMetaData(data.docComment.getParams());
891                        data.docComment=null;
892                }
893
894                comments(data);
895                        
896                // attributes
897                Attribute[] attrs = attributes(null,null,data,SEMI_BLOCK,LitString.EMPTY,Boolean.TRUE,null,false,NO_ATTR_SEP,true);
898                for(int i=0;i<attrs.length;i++){
899                        func.addAttribute(attrs[i]);
900                }
901                        
902                // body
903                boolean oldInsideFunction=data.insideFunction;
904                data.insideFunction=true;
905                try {
906                // ex block
907                statement(data,body,CTX_FUNCTION);
908                }
909                finally{
910                        data.insideFunction=oldInsideFunction;
911                }
912                func.setEnd(data.cfml.getPosition());
913                
914                if(closure) comments(data);
915
916                return func;
917        }
918        
919
920        
921        private Statement tagStatement(ExprData data, Body parent) throws TemplateException {
922                Statement child;
923                
924                for(int i=0;i<data.scriptTags.length;i++){
925                        // single
926                        if(data.scriptTags[i].getScript().getType()==TagLibTagScript.TYPE_SINGLE) { 
927                                if((child=_singleAttrStatement(parent,data,data.scriptTags[i]))!=null)return child;
928                        }
929                        // multiple
930                        else {//if(tags[i].getScript().getType()==TagLibTagScript.TYPE_MULTIPLE) { 
931                                if((child=_multiAttrStatement(parent,data,data.scriptTags[i]))!=null)return child;
932                        }
933                }
934                return null;
935        }
936        
937        
938
939        
940        
941        private final Statement _multiAttrStatement(Body parent, ExprData data,TagLibTag tlt) throws TemplateException  {
942                int pos = data.cfml.getPos();
943                try {
944                        return __multiAttrStatement(parent,data,tlt);
945                } 
946                catch (ProcessingDirectiveException e) {
947                        throw e;
948                }
949                catch (TemplateException e) {
950                        try {
951                                data.cfml.setPos(pos);
952                                return expressionStatement(data,parent);
953                        } catch (TemplateException e1) {
954                                if(tlt.getScript().getContext()==CTX_CFC) throw new ComponentTemplateException(e);
955                                throw e;
956                        }
957                }
958        }
959
960        private final Tag __multiAttrStatement(Body parent, ExprData data,TagLibTag tlt) throws TemplateException  {
961                if(data.ep==null) return null;
962                String type=tlt.getName();
963                if(data.cfml.forwardIfCurrent(type)) {
964                        boolean isValid=(data.cfml.isCurrent(' ') || (tlt.getHasBody() && data.cfml.isCurrent('{')));
965                        if(!isValid){
966                                data.cfml.setPos(data.cfml.getPos()-type.length());
967                                return null;
968                        }
969                }
970                else return null;
971                Position line = data.cfml.getPosition();
972                
973                TagLibTagScript script = tlt.getScript();
974                //TagLibTag tlt = CFMLTransformer.getTLT(data.cfml,type);
975                if(script.getContext()==CTX_CFC)data.isCFC=true;
976                else if(script.getContext()==CTX_INTERFACE)data.isInterface=true;
977                //Tag tag=new TagComponent(line);
978                Tag tag=getTag(data,parent,tlt, line,null);
979                tag.setTagLibTag(tlt);
980                tag.setScriptBase(true);
981                
982                // add component meta data
983                if(data.isCFC) {
984                        addMetaData(data,tag,IGNORE_LIST_COMPONENT);
985                }
986                if(data.isInterface) {
987                        addMetaData(data,tag,IGNORE_LIST_INTERFACE);
988                }
989                //EvaluatorPool.getPool();
990                comments(data);
991                
992                // attributes
993                //attributes(func,data);
994                Attribute[] attrs = attributes(tag,tlt,data,SEMI_BLOCK,LitString.EMPTY,script.getRtexpr()?Boolean.TRUE:Boolean.FALSE,null,false,',',false);
995                
996                for(int i=0;i<attrs.length;i++){
997                        tag.addAttribute(attrs[i]);
998                }
999                
1000                comments(data);
1001        
1002                // body
1003                if(tlt.getHasBody()){
1004                        Body body=new BodyBase();
1005                        boolean wasSemiColon=statement(data,body,script.getContext());
1006                        if(!wasSemiColon || !tlt.isBodyFree() || body.hasStatements())
1007                                tag.setBody(body);
1008                        
1009                        
1010                        
1011                }
1012                else checkSemiColonLineFeed(data,true,true,true);
1013                
1014                tag.setEnd(data.cfml.getPosition());
1015                eval(tlt,data,tag);
1016                return tag;
1017        }
1018        
1019        private Statement cftagStatement(ExprData data, Body parent) throws TemplateException {
1020                if(data.ep==null) return null; // that is because cfloop-contition evaluator does not pass this
1021                
1022                int start = data.cfml.getPos();
1023                
1024                // namespace and separator
1025                TagLib tagLib=CFMLTransformer.nameSpace(data);
1026                if(tagLib==null) return null;
1027                
1028                //print.e("namespace:"+tagLib.getNameSpaceAndSeparator());
1029                
1030                // get the name of the tag
1031                String id = CFMLTransformer.identifier(data.cfml, false,true);
1032                
1033                
1034                if(id==null) {
1035                        data.cfml.setPos(start);
1036                        return null;
1037                }
1038                
1039                id=id.toLowerCase();
1040                String appendix=null;
1041                TagLibTag tlt=tagLib.getTag(id);
1042                
1043                //print.e("tlt:"+tlt);
1044                
1045                
1046                // get taglib
1047                if(tlt==null)   {
1048                        tlt=tagLib.getAppendixTag(id);
1049                        //print.e("appendix:"+tlt);
1050                        
1051                         if(tlt==null) {
1052                                 //if(tagLib.getIgnoreUnknowTags()){ if we do this a expression like the following no longer work cfwhatever=1;
1053                                         data.cfml.setPos(start);
1054                                         return null;
1055                                 //} 
1056                                 //throw new TemplateException(data.cfml,"undefined tag ["+tagLib.getNameSpaceAndSeparator()+id+"]");
1057                         }
1058                        appendix=StringUtil.removeStartingIgnoreCase(id,tlt.getName());
1059                }
1060                if(tagLib.isCore() && tlt.getScript()==null) {
1061                        data.cfml.setPos(start);
1062                        return null;
1063                }
1064                
1065                // check for opening bracked or closing semicolon
1066                comments(data);
1067                boolean noAttrs=false;
1068                if(!data.cfml.forwardIfCurrent('(')){
1069                        if(checkSemiColonLineFeed(data, false, false, false)){
1070                                noAttrs=true;
1071                        }
1072                        else {
1073                                data.cfml.setPos(start);
1074                                return null;
1075                        }
1076                }
1077                
1078                Position line = data.cfml.getPosition();
1079                
1080                // script specific behavior
1081                short context=CTX_OTHER;
1082                Boolean allowExpression=Boolean.TRUE;
1083                {
1084                        
1085                        TagLibTagScript script = tlt.getScript();
1086                        if(script!=null) {
1087                                context=script.getContext();
1088                                // always true for this tags allowExpression=script.getRtexpr()?Boolean.TRUE:Boolean.FALSE;
1089                                if(context==CTX_CFC)data.isCFC=true;
1090                                else if(context==CTX_INTERFACE)data.isInterface=true;
1091                        }
1092                }
1093                
1094                Tag tag=getTag(data,parent,tlt, line,null);
1095                if(appendix!=null)      {
1096                        tag.setAppendix(appendix);
1097                        tag.setFullname(tlt.getFullName().concat(appendix));
1098                 }
1099                 else {
1100                         tag.setFullname(tlt.getFullName());
1101                 }
1102                
1103                
1104                tag.setTagLibTag(tlt);
1105                tag.setScriptBase(true);
1106                
1107                // add component meta data
1108                if(data.isCFC) {
1109                        addMetaData(data,tag,IGNORE_LIST_COMPONENT);
1110                }
1111                if(data.isInterface) {
1112                        addMetaData(data,tag,IGNORE_LIST_INTERFACE);
1113                }
1114                comments(data);
1115                
1116                // attributes
1117                Attribute[] attrs = noAttrs?new Attribute[0] : attributes(tag,tlt,data,BRACKED,LitString.EMPTY,allowExpression,null,false,',',true);
1118                data.cfml.forwardIfCurrent(')');
1119                
1120                for(int i=0;i<attrs.length;i++){
1121                        tag.addAttribute(attrs[i]);
1122                }
1123                
1124                comments(data);
1125        
1126                // body
1127                if(tlt.getHasBody()){
1128                        Body body=new BodyBase();
1129                        boolean wasSemiColon=statement(data,body,context);
1130                        if(!wasSemiColon || !tlt.isBodyFree() || body.hasStatements())
1131                                tag.setBody(body);
1132                        
1133                        
1134                        
1135                }
1136                else checkSemiColonLineFeed(data,true,true,true);
1137                
1138                
1139                tag.setEnd(data.cfml.getPosition());
1140                eval(tlt,data,tag);
1141                return tag;
1142        }
1143        
1144        
1145        
1146        private final void addMetaData(ExprData data, Tag tag, String[] ignoreList) {
1147                if(data.docComment==null) return;
1148                
1149
1150                tag.addMetaData(data.docComment.getHintAsAttribute());
1151                
1152                Map<String, Attribute> params = data.docComment.getParams();
1153                Iterator<Attribute> it = params.values().iterator();
1154                Attribute attr;
1155                outer:while(it.hasNext()){
1156                        attr = it.next();
1157                        // ignore list
1158                        if(!ArrayUtil.isEmpty(ignoreList)) {
1159                                for(int i=0;i<ignoreList.length;i++){
1160                                        if(ignoreList[i].equalsIgnoreCase(attr.getName())) continue outer;
1161                                }
1162                        }
1163                        tag.addMetaData(attr);  
1164                }
1165                data.docComment=null;
1166        }
1167        
1168        private final Statement propertyStatement(ExprData data,Body parent) throws TemplateException  {
1169                int pos = data.cfml.getPos();
1170                try {
1171                        return _propertyStatement(data, parent);
1172                } catch (TemplateException e) {
1173                        try {
1174                                data.cfml.setPos(pos);
1175                                return expressionStatement(data,parent);
1176                        } catch (TemplateException e1) {
1177                                throw e;
1178                        }
1179                }
1180        }
1181        
1182        private final Tag _propertyStatement(ExprData data,Body parent) throws TemplateException  {
1183                if(data.context!=CTX_CFC || !data.cfml.forwardIfCurrent("property "))
1184                        return null;
1185                Position line = data.cfml.getPosition();
1186                
1187                TagLibTag tlt = CFMLTransformer.getTLT(data.cfml,"property");
1188                Tag property=new TagOther(line,null);
1189                addMetaData(data, property,IGNORE_LIST_PROPERTY);
1190                
1191
1192                boolean hasName=false,hasType=false;
1193
1194                // TODO allow the following pattern property "a.b.C" d;
1195                //Expression t = string(data);
1196                // print.o("name:"+t.getClass().getName());
1197                
1198                int pos = data.cfml.getPos();
1199                String tmp=variableDec(data, true);
1200                if(!StringUtil.isEmpty(tmp)) {
1201                        if(tmp.indexOf('.')!=-1) {
1202                                property.addAttribute(new Attribute(false,"type",LitString.toExprString(tmp),"string"));
1203                                hasType=true;
1204                        }
1205                        else {
1206                                data.cfml.setPos(pos);
1207                        }
1208                }
1209                else data.cfml.setPos(pos);
1210                
1211                
1212                
1213                // folgend wird tlt extra nicht uebergeben, sonst findet pruefung statt
1214                Attribute[] attrs = attributes(property,tlt,data,SEMI,  NULL,Boolean.FALSE,"name",true,NO_ATTR_SEP,false);
1215                
1216                checkSemiColonLineFeed(data,true,true,false);
1217
1218                property.setTagLibTag(tlt);
1219                property.setScriptBase(true);
1220                
1221                
1222                Attribute attr;
1223                
1224                // first fill all regular attribute -> name="value"
1225                for(int i=attrs.length-1;i>=0;i--){
1226                        attr=attrs[i];
1227                        if(!attr.getValue().equals(NULL)){
1228                                if(attr.getName().equalsIgnoreCase("name")){
1229                                        hasName=true;
1230                                        //attr=new Attribute(attr.isDynamicType(),attr.getName(),CastString.toExprString(attr.getValue()),"string");
1231                                }
1232                                else if(attr.getName().equalsIgnoreCase("type")){
1233                                        hasType=true;
1234                                        //attr=new Attribute(attr.isDynamicType(),attr.getName(),CastString.toExprString(attr.getValue()),"string");
1235                                }
1236                                property.addAttribute(attr);
1237                        }
1238                }
1239                
1240                // now fill name named attributes -> attr1 attr2
1241                
1242                String first=null,second=null;
1243                for(int i=0;i<attrs.length;i++){
1244                        attr=attrs[i];
1245                        
1246                        if(attr.getValue().equals(NULL)){
1247                                // type
1248                                if(first==null && ((!hasName && !hasType) || !hasName)){
1249                                        first=attr.getNameOC();
1250                                }
1251                                // name
1252                                else if(second==null && !hasName && !hasType){
1253                                        second=attr.getNameOC();
1254                                }
1255                                // attr with no value
1256                                else {
1257                                        attr=new Attribute(true,attr.getName(),LitString.EMPTY,"string");
1258                                        property.addAttribute(attr);
1259                                }
1260                        }
1261                }
1262
1263                
1264                
1265                if(first!=null) {
1266                                hasName=true;
1267                        if(second!=null){
1268                                hasType=true;
1269                                property.addAttribute(new Attribute(false,"name",LitString.toExprString(second),"string"));
1270                                property.addAttribute(new Attribute(false,"type",LitString.toExprString(first),"string"));
1271                        }
1272                        else {
1273                                property.addAttribute(new Attribute(false,"name",LitString.toExprString(first),"string"));
1274                        }
1275                }
1276                
1277                if(!hasType)
1278                        property.addAttribute(ANY);
1279                
1280                if(!hasName)
1281                        throw new TemplateException(data.cfml,"missing name declaration for property");
1282
1283                /*Tag property=new TagBase(line);
1284                property.setTagLibTag(tlt);
1285                property.addAttribute(new Attribute(false,"name",LitString.toExprString(name),"string"));
1286                property.addAttribute(new Attribute(false,"type",LitString.toExprString(type),"string"));
1287                */
1288                property.setEnd(data.cfml.getPosition());
1289                
1290                return property;
1291        }
1292        
1293        public Statement paramStatement(ExprData data,Body parent) throws TemplateException  {
1294                int pos = data.cfml.getPos();
1295                try {
1296                        return _paramStatement(data, parent);
1297                } catch (TemplateException e) {
1298                        try {
1299                                data.cfml.setPos(pos);
1300                                return expressionStatement(data,parent);
1301                        } catch (TemplateException e1) {
1302                                throw e;
1303                        }
1304                }
1305        }
1306        
1307        private Tag _paramStatement(ExprData data,Body parent) throws TemplateException  {
1308                if(!data.cfml.forwardIfCurrent("param "))
1309                        return null;
1310                Position line = data.cfml.getPosition();
1311                
1312                TagLibTag tlt = CFMLTransformer.getTLT(data.cfml,"param");
1313                TagParam param=new TagParam(line,null);
1314                
1315                // type
1316                boolean hasType=false;
1317                boolean hasName=false;
1318                int pos = data.cfml.getPos();
1319                
1320                
1321                // first 2 arguments can be type/name directly 
1322                String tmp=variableDec(data, true);
1323                do {
1324                if(!StringUtil.isEmpty(tmp)) {
1325                        TagLibTagAttr attr = tlt.getAttribute(tmp.toLowerCase(),true);
1326                        // name is not a defined attribute 
1327                        
1328                    if(attr==null) {
1329                        comments(data);
1330                        // it could be a name followed by default value
1331                        if(data.cfml.forwardIfCurrent('='))     {
1332                                comments(data);
1333                                Expression v=attributeValue(data,true); 
1334                                param.addAttribute(new Attribute(false,"name",LitString.toExprString(tmp),"string"));
1335                                param.addAttribute(new Attribute(false,"default",v,"string"));
1336                                        hasName=true;
1337                                        break; // if we had a value this was already name
1338                        }
1339                        // can be type or name
1340                        int pos2 = data.cfml.getPos();
1341                                
1342                                // first could be type, followed by name
1343                        comments(data);
1344                                String tmp2=variableDec(data, true);
1345                        
1346                                if(!StringUtil.isEmpty(tmp2)) {
1347                                        attr = tlt.getAttribute(tmp2.toLowerCase(),true);
1348                                        if(attr==null) {
1349                                                param.addAttribute(new Attribute(false,"name",LitString.toExprString(tmp2),"string"));
1350                                                param.addAttribute(new Attribute(false,"type",LitString.toExprString(tmp),"string"));
1351                                                
1352                                                if(data.cfml.forwardIfCurrent('='))     {
1353                                                        Expression v=attributeValue(data,true); 
1354                                                        param.addAttribute(new Attribute(false,"default",v,"string"));
1355                                                }
1356                                                
1357                                                hasName=true;
1358                                                hasType=true;
1359                                                break;
1360                                        }
1361                                }
1362                                param.addAttribute(new Attribute(false,"name",LitString.toExprString(tmp),"string"));
1363                                data.cfml.setPos(pos2);
1364                                hasName=true;
1365                        }
1366                        else data.cfml.setPos(pos);
1367                }
1368                else data.cfml.setPos(pos);
1369                }while(false);
1370                
1371                
1372                // folgend wird tlt extra nicht uebergeben, sonst findet pruefung statt
1373                Attribute[] attrs = attributes(param,tlt,data,SEMI,     NULL,Boolean.TRUE,"name",true,',',false);
1374                checkSemiColonLineFeed(data,true,true,true);
1375
1376                param.setTagLibTag(tlt);
1377                param.setScriptBase(true);
1378                
1379                
1380                Attribute attr;
1381                
1382                // first fill all regular attribute -> name="value"
1383                boolean hasDynamic=false;
1384                for(int i=attrs.length-1;i>=0;i--){
1385                        attr=attrs[i];
1386                        if(!attr.getValue().equals(NULL)){
1387                                if(attr.getName().equalsIgnoreCase("name")){
1388                                        hasName=true;
1389                                        param.addAttribute(attr);
1390                                }
1391                                else if(attr.getName().equalsIgnoreCase("type")){
1392                                        hasType=true;
1393                                        param.addAttribute(attr);
1394                                }
1395                                else if(attr.isDynamicType()){
1396                                        hasName=true;
1397                                        if(hasDynamic) throw attrNotSupported(data.cfml,tlt,attr.getName());
1398                                        hasDynamic=true;
1399                                        param.addAttribute(new Attribute(false,"name",LitString.toExprString(attr.getName()),"string"));
1400                                        param.addAttribute(new Attribute(false,"default",attr.getValue(),"any"));
1401                                }
1402                                else 
1403                                        param.addAttribute(attr);
1404                        }
1405                }
1406                
1407                // now fill name named attributes -> attr1 attr2
1408                String first=null,second=null;
1409                for(int i=0;i<attrs.length;i++){
1410                        attr=attrs[i];
1411                        
1412                        if(attr.getValue().equals(NULL)){
1413                                // type
1414                                if(first==null && (!hasName || !hasType)){
1415                                        first=attr.getName();
1416                                }
1417                                // name
1418                                else if(second==null && !hasName && !hasType){
1419                                        second=attr.getName();
1420                                }
1421                                // attr with no value
1422                                else {
1423                                        attr=new Attribute(true,attr.getName(),LitString.EMPTY,"string");
1424                                        param.addAttribute(attr);
1425                                }
1426                        }
1427                }
1428
1429                
1430                if(first!=null) {
1431                        if(second!=null){
1432                                hasName=true;
1433                                hasType=true;
1434                                if(hasDynamic) throw attrNotSupported(data.cfml,tlt,first);
1435                                hasDynamic=true;
1436                                param.addAttribute(new Attribute(false,"name",LitString.toExprString(second),"string"));
1437                                param.addAttribute(new Attribute(false,"type",LitString.toExprString(first),"string"));
1438                        }
1439                        else {
1440                                param.addAttribute(new Attribute(false,hasName?"type":"name",LitString.toExprString(first),"string"));
1441                                hasName=true;
1442                        }
1443                }
1444                
1445                //if(!hasType)
1446                //      param.addAttribute(ANY);
1447                
1448                if(!hasName) {
1449                        throw new TemplateException(data.cfml,"missing name declaration for param");
1450                }
1451                param.setEnd(data.cfml.getPosition());
1452                return param;
1453        }
1454
1455
1456        private TemplateException attrNotSupported(CFMLString cfml, TagLibTag tag, String id) {
1457                String names=tag.getAttributeNames();
1458                if(StringUtil.isEmpty(names))
1459                        return new TemplateException(cfml,"Attribute "+id+" is not allowed for tag "+tag.getFullName());
1460                
1461                return new TemplateException(cfml,
1462                        "Attribute "+id+" is not allowed for statement "+tag.getName(),
1463                        "valid attribute names are ["+names+"]");
1464        }
1465
1466
1467
1468        private final String variableDec(ExprData data,boolean firstCanBeNumber) {
1469                
1470                String id=identifier(data, firstCanBeNumber);
1471                if(id==null) return null;
1472                
1473                StringBuilder rtn=new StringBuilder(id);
1474                data.cfml.removeSpace();
1475                
1476                while(data.cfml.forwardIfCurrent('.')){
1477                        data.cfml.removeSpace();
1478                        rtn.append('.');
1479                        id=identifier(data, firstCanBeNumber);
1480                        if(id==null)return null;
1481                        rtn.append(id);
1482                        data.cfml.removeSpace();
1483                }
1484                
1485                while(data.cfml.forwardIfCurrent("[]")){
1486                        data.cfml.removeSpace();
1487                        rtn.append("[]");
1488                }
1489                
1490                data.cfml.revertRemoveSpace();
1491                
1492                
1493                return rtn.toString();
1494        }
1495        
1496        /**
1497         * Liest ein return Statement ein.
1498         * <br />
1499         * EBNF:<br />
1500         * <code>spaces expressionStatement spaces;</code>
1501         * @return return Statement
1502         * @throws TemplateException
1503         */
1504        private final Return returnStatement(ExprData data) throws TemplateException {
1505            if(!data.cfml.forwardIfCurrentAndNoVarExt("return")) return null;
1506            
1507            Position line = data.cfml.getPosition();
1508            Return rtn;
1509            
1510            comments(data);
1511            if(checkSemiColonLineFeed(data, false,false,false)) rtn=new Return(line,data.cfml.getPosition());
1512            else {
1513                Expression expr = expression(data);
1514                checkSemiColonLineFeed(data, true,true,false);
1515                rtn=new Return(expr,line,data.cfml.getPosition());
1516            }
1517                comments(data);
1518
1519                return rtn;
1520        }
1521
1522        
1523        private final Statement _singleAttrStatement(Body parent, ExprData data, TagLibTag tlt) throws TemplateException   {
1524                int pos = data.cfml.getPos();
1525                try {
1526                        return __singleAttrStatement(parent,data,tlt, false);
1527                } 
1528                catch (ProcessingDirectiveException e) {
1529                        throw e;
1530                } 
1531                catch (TemplateException e) {
1532                        data.cfml.setPos(pos);
1533                        try {
1534                                return expressionStatement(data,parent);
1535                        } catch (TemplateException e1) {
1536                                throw e;
1537                        }
1538                }
1539        }
1540
1541        private final Statement __singleAttrStatement(Body parent, ExprData data, TagLibTag tlt, boolean allowTwiceAttr) throws TemplateException {
1542                String tagName = tlt.getName();
1543                if(data.cfml.forwardIfCurrent(tagName)){
1544                        if(!data.cfml.isCurrent(' ') && !data.cfml.isCurrent(';')){
1545                                data.cfml.setPos(data.cfml.getPos()-tagName.length());
1546                                return null;
1547                        }
1548                }
1549                else return null;
1550                
1551                
1552                int pos=data.cfml.getPos()-tagName.length();
1553                Position line = data.cfml.getPosition();
1554                //TagLibTag tlt = CFMLTransformer.getTLT(data.cfml,tagName.equals("pageencoding")?"processingdirective":tagName);
1555                
1556                Tag tag=getTag(data,parent,tlt,line,null);
1557                tag.setScriptBase(true);
1558                tag.setTagLibTag(tlt);
1559                
1560                comments(data);
1561                
1562                // attribute
1563                TagLibTagAttr attr = tlt.getScript().getSingleAttr();
1564                String attrName=null;
1565                Expression attrValue=null;
1566                short attrType=ATTR_TYPE_NONE;
1567                if(attr!=null){
1568                        attrType = attr.getScriptSupport();
1569                        char c = data.cfml.getCurrent();
1570                        if(ATTR_TYPE_REQUIRED==attrType || ((!data.cfml.isCurrent(';')) && ATTR_TYPE_OPTIONAL==attrType)) {
1571                                if(data.cfml.isCurrent('{')) {// this can be only a json string
1572                                        int p=data.cfml.getPos();
1573                                        try{
1574                                                attrValue=isSimpleValue(attr.getType())?null:json(data,JSON_STRUCT,'{','}');
1575                                        }
1576                                        catch(Throwable t){
1577                                                ExceptionUtil.rethrowIfNecessary(t);
1578                                                data.cfml.setPos(p);
1579                                        }
1580                                }
1581                                else attrValue =attributeValue(data, tlt.getScript().getRtexpr());
1582                                if(attrValue!=null && isOperator(c)) {
1583                                        data.cfml.setPos(pos);
1584                                        return null;
1585                                }
1586                        }
1587                }
1588                
1589                if(attrValue!=null){
1590                        attrName=attr.getName();
1591                        TagLibTagAttr tlta = tlt.getAttribute(attr.getName(),true);
1592                        tag.addAttribute(new Attribute(false,attrName,CastOther.toExpression(attrValue,tlta.getType()),tlta.getType()));
1593                }
1594                else if(ATTR_TYPE_REQUIRED==attrType){
1595                        data.cfml.setPos(pos);
1596                        return null;
1597                }
1598                
1599                // body
1600                if(tlt.getHasBody()){
1601                        Body body=new BodyBase();
1602                        boolean wasSemiColon=statement(data,body,tlt.getScript().getContext());
1603                        if(!wasSemiColon || !tlt.isBodyFree() || body.hasStatements())
1604                                tag.setBody(body);
1605                }
1606                else checkSemiColonLineFeed(data,true,true,true);
1607                
1608                
1609                if(!StringUtil.isEmpty(tlt.getTteClassName()))data.ep.add(tlt, tag, data.flibs, data.cfml);
1610                
1611                if(!StringUtil.isEmpty(attrName))validateAttributeName(attrName, data.cfml, new ArrayList<String>(), tlt, new RefBooleanImpl(false), new StringBuffer(), allowTwiceAttr);
1612                tag.setEnd(data.cfml.getPosition());
1613                eval(tlt,data,tag);
1614                return tag;
1615        }
1616
1617        private boolean isSimpleValue(String type) {
1618                return type.equalsIgnoreCase("string") || type.equalsIgnoreCase("boolean") || type.equalsIgnoreCase("number") || type.equalsIgnoreCase("numeric");
1619        }
1620
1621        private boolean isOperator(char c) {
1622                return c=='=' || c=='+' || c=='-';
1623        }
1624
1625        /*protected Statement __singleAttrStatement(Body parent, Data data, String tagName,String attrName,int attrType, boolean allowExpression, boolean allowTwiceAttr) throws TemplateException {
1626                
1627                if(data.cfml.forwardIfCurrent(tagName)){
1628                        if(!data.cfml.isCurrent(' ') && !data.cfml.isCurrent(';')){
1629                                data.cfml.setPos(data.cfml.getPos()-tagName.length());
1630                                return null;
1631                        }
1632                }
1633                else return null;
1634                
1635                
1636                int pos=data.cfml.getPos()-tagName.length();
1637                int line=data.cfml.getLine();
1638                TagLibTag tlt = CFMLTransformer.getTLT(data.cfml,tagName.equals("pageencoding")?"processingdirective":tagName);
1639                
1640                Tag tag=getTag(parent,tlt,line);
1641                tag.setScriptBase(true);
1642                tag.setTagLibTag(tlt);
1643                
1644                comments(data);
1645                
1646                // attribute
1647                Expression attrValue=null;
1648                if(ATTR_TYPE_REQUIRED==attrType || (!data.cfml.isCurrent(';') && ATTR_TYPE_OPTIONAL==attrType))
1649                        attrValue =attributeValue(data, allowExpression);
1650                                //allowExpression?super.expression(data):string(data);
1651                
1652                if(attrValue!=null){
1653                        TagLibTagAttr tlta = tlt.getAttribute(attrName);
1654                        tag.addAttribute(new Attribute(false,attrName,Cast.toExpression(attrValue,tlta.getType()),tlta.getType()));
1655                }
1656                else if(ATTR_TYPE_REQUIRED==attrType){
1657                        data.cfml.setPos(pos);
1658                        return null;
1659                }
1660                
1661                checkSemiColonLineFeed(data,true);
1662                if(!StringUtil.isEmpty(tlt.getTteClassName()))data.ep.add(tlt, tag, data.fld, data.cfml);
1663                
1664                if(!StringUtil.isEmpty(attrName))validateAttributeName(attrName, data.cfml, new ArrayList<String>(), tlt, new RefBooleanImpl(false), new StringBuffer(), allowTwiceAttr);
1665                
1666                eval(tlt,data,tag);
1667                return tag;
1668        }*/
1669        
1670        
1671
1672        private final void eval(TagLibTag tlt, lucee.transformer.cfml.expression.CFMLExprTransformer.ExprData data, Tag tag) throws TemplateException {
1673                if(!StringUtil.isEmpty(tlt.getTteClassName())){
1674                        try {
1675                                tlt.getEvaluator().execute(data.config, tag, tlt,data.flibs, data);
1676                        } catch (EvaluatorException e) {
1677                                throw new TemplateException(e.getMessage());
1678                        }
1679                        data.ep.add(tlt, tag, data.flibs, data.cfml);
1680                }
1681        }
1682
1683        private final Tag getTag(ExprData data,Body parent, TagLibTag tlt, Position start,Position end) throws TemplateException {
1684                try {
1685                        Tag tag = tlt.getTag(start, end);
1686                        tag.setParent(parent);
1687                        return tag;
1688                } catch (TagLibException e) {
1689                        throw new TemplateException(data.cfml,e);
1690                }
1691                /*if(StringUtil.isEmpty(tlt.getTttClassName()))tag= new TagBase(line);
1692                else {
1693                        try {
1694                                Class<Tag> clazz = ClassUtil.loadClass(tlt.getTttClassName());
1695                                Constructor<Tag> constr = clazz.getConstructor(new Class[]{Position.class});
1696                                tag = constr.newInstance(new Object[]{line});
1697                                
1698                        } 
1699                        catch (Exception e) {
1700                                e.printStackTrace();
1701                                tag= new TagBase(line);
1702                        }
1703                }*/
1704                
1705        }
1706        
1707        
1708        
1709        /**
1710         * List mithilfe des data.cfmlExprTransformer einen Ausruck ein.
1711         * <br />
1712         * EBNF:<br />
1713         * <code>expression ";";</code>
1714         * @param parent 
1715         * @return Ausdruck
1716         * @throws TemplateException
1717         */
1718        private Statement expressionStatement(ExprData data, Body parent) throws TemplateException {
1719                Expression expr=expression(data);
1720                checkSemiColonLineFeed(data,true,true,false);
1721                if(expr instanceof ClosureAsExpression)
1722                        return ((ClosureAsExpression)expr).getClosure();
1723                        
1724                return new ExpressionAsStatement(expr);
1725        }
1726        
1727        private final boolean checkSemiColonLineFeed(ExprData data,boolean throwError, boolean checkNLBefore,boolean allowEmptyCurlyBracked) throws TemplateException {
1728                comments(data);
1729                if(!data.cfml.forwardIfCurrent(';')){
1730                        
1731                        // curly brackets?
1732                        if(allowEmptyCurlyBracked) {
1733                                int pos = data.cfml.getPos();
1734                                if(data.cfml.forwardIfCurrent('{')) {
1735                                        comments(data);
1736                                        if(data.cfml.forwardIfCurrent('}')) return true;
1737                                        data.cfml.setPos(pos);
1738                                }
1739                        }
1740                        
1741                        
1742                        if((!checkNLBefore || !data.cfml.hasNLBefore()) && !data.cfml.isCurrent("</",data.tagName) && !data.cfml.isCurrent('}')){
1743                                if(!throwError) return false;
1744                                throw new TemplateException(data.cfml,"Missing [;] or [line feed] after expression");
1745                        }
1746                }
1747                return true;
1748        }
1749
1750        
1751        /**
1752         * Ruft die Methode expression der zu vererbenten Klasse auf 
1753         * und prueft ob der Rueckgabewert einen boolschen Wert repraesentiert und castet den Wert allenfalls.
1754         * <br />
1755         * EBNF:<br />
1756         * <code>TemplateException::expression;</code>
1757         * @return condition
1758         * @throws TemplateException
1759         */
1760        private final ExprBoolean condition(ExprData data) throws TemplateException {
1761                ExprBoolean condition=null;
1762                comments(data);
1763                condition=CastBoolean.toExprBoolean(super.expression(data));
1764                comments(data);
1765                return condition;
1766        }
1767        
1768        /**
1769         * Liest eine try Block ein
1770         * <br />
1771         * EBNF:<br />
1772         * <code>;</code>
1773         * @return Try Block
1774         * @throws TemplateException
1775        */
1776        private final TryCatchFinally tryStatement(ExprData data) throws TemplateException {
1777                if(!data.cfml.forwardIfCurrent("try",'{') && !data.cfml.forwardIfCurrent("try ") && !data.cfml.forwardIfCurrent("try",'/'))
1778                        return null;
1779                data.cfml.previous();
1780
1781                Body body=new BodyBase();
1782                TryCatchFinally tryCatchFinally=new TryCatchFinally(body,data.cfml.getPosition(),null);
1783                
1784                statement(data,body,CTX_TRY);
1785                comments(data);
1786                
1787                // catches
1788                short catchCount=0;
1789                while(data.cfml.forwardIfCurrent("catch",'(')) {
1790                        catchCount++;
1791                        comments(data);
1792                        
1793                        // type
1794                        int pos=data.cfml.getPos();
1795                        Position line=data.cfml.getPosition();
1796                        Expression name = null,type = null;
1797                        
1798                        StringBuffer sbType=new StringBuffer();
1799            String id;
1800            while(true) {
1801                id=identifier(data,false);
1802                if(id==null)break;
1803                sbType.append(id);
1804                data.cfml.removeSpace();
1805                if(!data.cfml.forwardIfCurrent('.'))break;
1806                sbType.append('.');
1807                data.cfml.removeSpace();
1808            }
1809                                
1810            
1811                        if(sbType.length()==0) {
1812                            type=string(data);
1813                            if(type==null)                          
1814                                throw new TemplateException(data.cfml,"a catch statement must begin with the throwing type (query, application ...).");
1815                        }
1816                        else {
1817                                type=LitString.toExprString(sbType.toString());
1818                        } 
1819            
1820            
1821                        //name = expression();
1822                        comments(data);
1823                        
1824                        // name
1825                        if(!data.cfml.isCurrent(')')) {
1826                                name=expression(data);
1827                        }
1828                        else {
1829                                data.cfml.setPos(pos);
1830                                name=expression(data);
1831                                type = LitString.toExprString( "any" );
1832                        }
1833                        comments(data);
1834
1835            Body b=new BodyBase();
1836                        try {
1837                                tryCatchFinally.addCatch(type,name,b,line);
1838                        } 
1839                        catch (BytecodeException e) {
1840                                throw new TemplateException(data.cfml,e.getMessage());
1841                        }
1842                        comments(data);
1843                        
1844                        if(!data.cfml.forwardIfCurrent(')')) throw new TemplateException(data.cfml,"invalid catch statement, missing closing )");
1845                        
1846            statement(data,b,CTX_CATCH);
1847                        comments(data); 
1848                }
1849        
1850                
1851// finally
1852                 if(finallyStatement(data,tryCatchFinally)) {
1853                        comments(data);
1854                 }
1855                 else if(catchCount==0)
1856                        throw new TemplateException(data.cfml,"a try statement must have at least one catch statement");
1857                
1858        //if(body.isEmpty()) return null;
1859                tryCatchFinally.setEnd(data.cfml.getPosition());
1860                return tryCatchFinally;
1861        }
1862        
1863        /**
1864         * Prueft ob sich der Zeiger am Ende eines Script Blockes befindet
1865         * @return Ende ScriptBlock?
1866         * @throws TemplateException
1867         */
1868        private final boolean isFinish(ExprData data) throws TemplateException {
1869                comments(data);
1870                return data.cfml.isCurrent("</",data.tagName);               
1871        }
1872        
1873        
1874        /**
1875         * Liest den Block mit Statements ein.
1876         * <br />
1877         * EBNF:<br />
1878         * <code>"{" spaces {statements} "}" | statement;</code>
1879         * @param block
1880         * @return was a block
1881         * @throws TemplateException
1882         */
1883        private final boolean block(ExprData data,Body body) throws TemplateException {
1884                if(!data.cfml.forwardIfCurrent('{'))
1885                        return false;
1886                comments(data);
1887                if(data.cfml.forwardIfCurrent('}')) {
1888                        
1889                        return true;
1890                }
1891                statements(data,body,false);
1892                
1893                if(!data.cfml.forwardIfCurrent('}'))
1894                        throw new TemplateException(data.cfml,"Missing ending [}]");
1895                return true;
1896        }       
1897        
1898        
1899        
1900        
1901        
1902        
1903        
1904        
1905        
1906        
1907        
1908        
1909        
1910        
1911        
1912        private final Attribute[] attributes(Tag tag,TagLibTag tlt, ExprData data, EndCondition endCond,Expression defaultValue,
1913                        Object oAllowExpression, String ignoreAttrReqFor, boolean allowTwiceAttr, char attributeSeparator,boolean allowColonAsNameValueSeparator) throws TemplateException {
1914                ArrayList<Attribute> attrs=new ArrayList<Attribute>();
1915                ArrayList<String> ids=new ArrayList<String>();
1916                while(data.cfml.isValidIndex()) {
1917                        data.cfml.removeSpace();
1918                        // if no more attributes break
1919                        if(endCond.isEnd(data)) break;
1920                        Attribute attr = attribute(tlt,data,ids,defaultValue,oAllowExpression, allowTwiceAttr,allowColonAsNameValueSeparator);
1921                        attrs.add(attr);
1922                        
1923                        // seperator
1924                        if(attributeSeparator>0) {
1925                                data.cfml.removeSpace();
1926                                data.cfml.forwardIfCurrent(attributeSeparator);
1927                        }
1928                        
1929                }
1930                
1931                // not defined attributes
1932                if(tlt!=null){
1933                        boolean hasAttributeCollection=attrs.contains("attributecollection");
1934                        int type=tlt.getAttributeType();
1935                        if(type==TagLibTag.ATTRIBUTE_TYPE_FIXED || type==TagLibTag.ATTRIBUTE_TYPE_MIXED)        {
1936                                Map<String, TagLibTagAttr> hash = tlt.getAttributes();
1937                                Iterator<Entry<String, TagLibTagAttr>> it = hash.entrySet().iterator();
1938                                Entry<String, TagLibTagAttr> e;
1939                                while(it.hasNext())     {
1940                                        e = it.next();
1941                                        TagLibTagAttr att=e.getValue();
1942                                        if(att.isRequired() && !contains(attrs,att) && att.getDefaultValue()==null && !att.getName().equals(ignoreAttrReqFor))  {
1943                                                if(!hasAttributeCollection)throw new TemplateException(data.cfml,"attribute "+att.getName()+" is required for statement "+tlt.getName());
1944                                                if(tag!=null)tag.addMissingAttribute(att);
1945                                        }
1946                                }
1947                        }
1948                }
1949                return attrs.toArray(new Attribute[attrs.size()]);
1950        }
1951        
1952        private final boolean contains(ArrayList<Attribute> attrs, TagLibTagAttr attr) {
1953                
1954                Iterator<Attribute> it = attrs.iterator();
1955                String name;
1956                String[] alias;
1957                while(it.hasNext()){
1958                        name=it.next().getName();
1959                        
1960                        // check name
1961                        if(name.equals(attr.getName())) return true;
1962                        
1963                        // and aliases
1964                        alias = attr.getAlias();
1965                        if(!ArrayUtil.isEmpty(alias)) for(int i=0;i<alias.length;i++){
1966                                if(alias[i].equals(attr.getName())) return true;
1967                        }
1968                }
1969                
1970                return false;
1971        }
1972
1973        private final Attribute attribute(TagLibTag tlt, ExprData data, ArrayList<String> args, Expression defaultValue,Object oAllowExpression, boolean allowTwiceAttr, boolean allowColonSeparator) throws TemplateException {
1974                StringBuffer sbType=new StringBuffer();
1975        RefBoolean dynamic=new RefBooleanImpl(false);
1976        
1977                // Name
1978        String name=attributeName(data.cfml,args,tlt,dynamic,sbType, allowTwiceAttr,!allowColonSeparator);
1979        String nameLC=name==null?null:name.toLowerCase();
1980        boolean allowExpression=false;
1981        if(oAllowExpression instanceof Boolean)allowExpression=((Boolean)oAllowExpression).booleanValue();
1982        else if(oAllowExpression instanceof String)allowExpression=((String)oAllowExpression).equalsIgnoreCase(name);
1983
1984          Expression value=null;
1985        comments(data);
1986        
1987        // value
1988        boolean b=data.cfml.forwardIfCurrent('=') || (allowColonSeparator && data.cfml.forwardIfCurrent(':'));
1989        if(b)   {
1990                comments(data);
1991                value=attributeValue(data,allowExpression);     
1992                
1993        }
1994        else {
1995                value=defaultValue;
1996        }               
1997        comments(data);
1998        
1999        
2000        // Type
2001        TagLibTagAttr tlta=null;
2002                if(tlt!=null){
2003                        tlta = tlt.getAttribute(nameLC,true);
2004                        if(tlta!=null && tlta.getName()!=null)nameLC=tlta.getName();
2005                }
2006                return new Attribute(dynamic.toBooleanValue(),name,tlta!=null?CastOther.toExpression(value, tlta.getType()):value,sbType.toString());
2007    }
2008        
2009        private final String attributeName(CFMLString cfml, ArrayList<String> args,TagLibTag tag, RefBoolean dynamic, StringBuffer sbType, boolean allowTwiceAttr, boolean allowColon) throws TemplateException {
2010                String id=CFMLTransformer.identifier(cfml,true,allowColon);
2011                return validateAttributeName(id, cfml, args, tag, dynamic, sbType,allowTwiceAttr);
2012        }
2013        
2014        
2015        
2016        private final String validateAttributeName(String idOC,CFMLString cfml, ArrayList<String> args,TagLibTag tag, RefBoolean dynamic, StringBuffer sbType, boolean allowTwiceAttr) throws TemplateException {
2017                String idLC=idOC.toLowerCase();
2018                
2019                if(args.contains(idLC) && !allowTwiceAttr) throw new TemplateException(cfml,"you can't use the same attribute ["+idOC+"] twice");
2020                args.add(idLC);
2021                
2022                
2023                if(tag==null) return idOC;
2024                int typeDef=tag.getAttributeType();
2025                if("attributecollection".equals(idLC)){
2026                        dynamic.setValue(tag.getAttribute(idLC,true)==null);
2027                        sbType.append("struct");
2028                }
2029                else if(typeDef==TagLibTag.ATTRIBUTE_TYPE_FIXED || typeDef==TagLibTag.ATTRIBUTE_TYPE_MIXED) {
2030                        TagLibTagAttr attr=tag.getAttribute(idLC,true);
2031                        if(attr==null) {
2032                                if(typeDef==TagLibTag.ATTRIBUTE_TYPE_FIXED) {
2033                                        String names=tag.getAttributeNames();
2034                                        if(StringUtil.isEmpty(names))
2035                                                throw new TemplateException(cfml,"Attribute "+idOC+" is not allowed for tag "+tag.getFullName());
2036                                        
2037                                        throw new TemplateException(cfml,
2038                                                "Attribute "+idOC+" is not allowed for statement "+tag.getName(),
2039                                                "valid attribute names are ["+names+"]");
2040                                }
2041                                dynamic.setValue(true);
2042                                
2043                        }
2044                        else {
2045                                sbType.append(attr.getType());
2046                                //parseExpression[0]=attr.getRtexpr();
2047                        }
2048                }
2049                else if(typeDef==TagLibTag.ATTRIBUTE_TYPE_DYNAMIC){
2050                        dynamic.setValue(true);
2051                }
2052                return idOC;
2053        }
2054        
2055                
2056        private final Expression attributeValue(ExprData data, boolean allowExpression) throws TemplateException {
2057                return allowExpression?super.expression(data):transformAsString(data,new String[]{" ", ";", "{"});
2058        }
2059        
2060        public static interface EndCondition {
2061                public boolean isEnd(ExprData data);
2062        }
2063}