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