001/**
002 *
003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved.
004 *
005 * This library is free software; you can redistribute it and/or
006 * modify it under the terms of the GNU Lesser General Public
007 * License as published by the Free Software Foundation; either 
008 * version 2.1 of the License, or (at your option) any later version.
009 * 
010 * This library is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013 * Lesser General Public License for more details.
014 * 
015 * You should have received a copy of the GNU Lesser General Public 
016 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
017 * 
018 **/
019package lucee.transformer.library.tag;
020
021import java.io.IOException;
022import java.lang.reflect.Constructor;
023import java.lang.reflect.InvocationTargetException;
024import java.util.HashMap;
025import java.util.Iterator;
026import java.util.LinkedHashMap;
027import java.util.Map;
028import java.util.Map.Entry;
029
030import lucee.commons.lang.ClassException;
031import lucee.commons.lang.ClassUtil;
032import lucee.commons.lang.ExceptionUtil;
033import lucee.commons.lang.Md5;
034import lucee.commons.lang.StringUtil;
035import lucee.runtime.op.Caster;
036import lucee.runtime.type.util.ArrayUtil;
037import lucee.transformer.bytecode.Position;
038import lucee.transformer.bytecode.cast.CastOther;
039import lucee.transformer.bytecode.expression.Expression;
040import lucee.transformer.bytecode.literal.LitBoolean;
041import lucee.transformer.bytecode.literal.LitDouble;
042import lucee.transformer.bytecode.literal.LitString;
043import lucee.transformer.bytecode.statement.tag.Attribute;
044import lucee.transformer.bytecode.statement.tag.Tag;
045import lucee.transformer.bytecode.statement.tag.TagOther;
046import lucee.transformer.cfml.attributes.AttributeEvaluator;
047import lucee.transformer.cfml.attributes.AttributeEvaluatorException;
048import lucee.transformer.cfml.evaluator.Evaluator;
049import lucee.transformer.cfml.evaluator.EvaluatorException;
050import lucee.transformer.cfml.tag.TagDependentBodyTransformer;
051
052import org.objectweb.asm.Type;
053
054/**
055 * Die Klasse TagLibTag repaesentiert ein einzelne Tag Definition einer TagLib, 
056 * beschreibt also alle Informationen die man zum validieren eines Tags braucht. 
057 */
058public final class TagLibTag {
059        
060        public final static int ATTRIBUTE_TYPE_FIXED=0;
061        public final static int ATTRIBUTE_TYPE_DYNAMIC=1;
062        public final static int ATTRIBUTE_TYPE_NONAME=3;
063        public final static int ATTRIBUTE_TYPE_MIXED=4;
064        
065        
066        /**
067         * Definition des Attribut Type 
068         */
069        //public final static int ATTRIBUTE_TYPE_FULLDYNAMIC=2; deprecated
070        /**
071         * Definition des Attribut Type 
072         */
073        
074        private final static Class[] CONSTRUCTOR_PARAMS=new Class[]{Position.class,Position.class};
075        
076        private int attributeType;
077        private String name;
078        private boolean hasBody=true;
079        
080        private boolean isBodyReq=false;
081        private boolean isTagDependent=false;
082        private boolean bodyFree=true;
083        
084        private boolean parseBody;
085        private boolean hasAppendix;
086        private String description="";
087        private String tagClass;
088        private String tteClass;
089        private String tdbtClass;
090        private int min;
091        private int max;
092        private TagLib tagLib;
093        private Evaluator eval;
094        private TagDependentBodyTransformer tdbt;
095
096        private Map<String,TagLibTagAttr> attributes=new LinkedHashMap<String,TagLibTagAttr>();
097        private Map<String,String> setters=new HashMap<String,String>();
098        private TagLibTagAttr attrFirst;
099        private TagLibTagAttr attrLast;
100        
101        private String strAttributeEvaluator;
102        private AttributeEvaluator attributeEvaluator;
103        private boolean handleException;
104    private boolean hasDefaultValue=false;
105        private Type tagType;
106        private String tttClass;
107        private Constructor  tttConstructor;
108        private boolean allowRemovingLiteral;
109        private TagLibTagAttr defaultAttribute;
110        private short status=TagLib.STATUS_IMPLEMENTED;
111        private Class clazz;
112        private TagLibTagScript script;
113        private final static TagLibTagAttr UNDEFINED=new TagLibTagAttr(null);
114        private TagLibTagAttr singleAttr=UNDEFINED;
115        private Expression attributeDefaultValue;
116
117        public TagLibTag duplicate(boolean cloneAttributes) {
118                TagLibTag tlt = new TagLibTag(tagLib);
119
120                tlt.attributeType=attributeType;
121                tlt.name=name;
122                tlt.hasBody=hasBody;
123                tlt.isBodyReq=isBodyReq;
124                tlt.isTagDependent=isTagDependent;
125                tlt.bodyFree=bodyFree;
126                tlt.parseBody=parseBody;
127                tlt.hasAppendix=hasAppendix;
128                tlt.description=description;
129                tlt.tagClass=tagClass;
130                tlt.tteClass=tteClass;
131                tlt.tdbtClass=tdbtClass;
132                tlt.min=min;
133                tlt.max=max;
134                tlt.strAttributeEvaluator=strAttributeEvaluator;
135                tlt.handleException=handleException;
136                tlt.hasDefaultValue=hasDefaultValue;
137                tlt.tagType=tagType;
138                tlt.tttClass=tttClass;
139                tlt.tttConstructor=tttConstructor;
140                tlt.allowRemovingLiteral=allowRemovingLiteral;
141                tlt.status=status;
142                
143                tlt.eval=null;
144                tlt.tdbt=null;
145                tlt.attributeEvaluator=null;
146                
147                
148                Iterator<Entry<String, TagLibTagAttr>> it = attributes.entrySet().iterator();
149                if(cloneAttributes) {
150                        while(it.hasNext()){
151                                tlt.setAttribute(it.next().getValue().duplicate(tlt));
152                        }
153                        if(defaultAttribute!=null)tlt.defaultAttribute=defaultAttribute.duplicate(tlt);
154                }
155                else {
156                        while(it.hasNext()){
157                                tlt.setAttribute(it.next().getValue());
158                                tlt.attrFirst=attrFirst;
159                                tlt.attrLast=attrLast;
160                        }
161                        tlt.defaultAttribute=defaultAttribute;
162                }
163                
164                
165                // setter
166                Iterator<Entry<String, String>> sit = setters.entrySet().iterator();
167                Entry<String, String> se;
168                while(sit.hasNext()){
169                        se = sit.next();
170                        tlt.setters.put(se.getKey(), se.getValue());
171                }
172                
173                
174                
175/*
176                private Map attributes=new HashMap();
177                private TagLibTagAttr attrFirst;
178                private TagLibTagAttr attrLast;
179                
180                private Map setters=new HashMap();
181                private TagLibTagAttr defaultAttribute;
182        */      
183                return tlt;
184        }
185        
186        
187        /**
188         * Geschuetzer Konstruktor ohne Argumente.
189         * @param tagLib
190         */
191        public TagLibTag(TagLib tagLib) {
192            this.tagLib=tagLib;
193        }
194
195        /**
196         * Gibt alle Attribute (TagLibTagAttr) eines Tag als HashMap zurueck.
197         * @return HashMap Attribute als HashMap.
198         */
199        public Map<String,TagLibTagAttr> getAttributes() {
200                return attributes;
201        }
202        
203        /**
204         * Gibt ein bestimmtes Attribut anhand seines Namens zurueck, 
205         * falls dieses Attribut nicht existiert wird null zurueckgegeben.
206         * @param name Name des Attribut das zurueckgegeben werden soll.
207         * @return Attribute das angfragt wurde oder null.
208         */
209        public TagLibTagAttr getAttribute(String name) {
210                return getAttribute(name, false);
211        }
212        
213        public TagLibTagAttr getAttribute(String name, boolean checkAlias) {
214                TagLibTagAttr attr = attributes.get(name);
215                // checking alias
216                if(attr==null && checkAlias) 
217                        return getAttributeByAlias(name);
218                return attr;
219                
220        }
221        
222
223        public TagLibTagAttr getAttributeByAlias(String alias) {
224                Iterator<TagLibTagAttr> it = attributes.values().iterator();
225                TagLibTagAttr attr;
226                String[] aliases;
227                while(it.hasNext()){
228                        attr = it.next();
229                        if(ArrayUtil.isEmpty(attr.getAlias())) continue;
230                        aliases = attr.getAlias();
231                        for(int i=0;i<aliases.length;i++){
232                                if(aliases[i].equalsIgnoreCase(alias)) return attr;
233                        }
234                }
235                return null;
236        }
237
238
239        /**
240         * Gibt das erste Attribut, welches innerhalb des Tag definiert wurde, zurueck.
241         * @return  Attribut das angfragt wurde oder null.
242         */
243        public TagLibTagAttr getFirstAttribute() {
244                return attrFirst;
245        }
246        
247        /**
248          * Gibt das letzte Attribut, welches innerhalb des Tag definiert wurde, zurueck.
249         * @return Attribut das angfragt wurde oder null.
250         */
251        public TagLibTagAttr getLastAttribute() {
252                return attrLast;
253        }
254
255        /**
256         * Gibt den Namen des Tag zurueck.
257         * @return String Name des Tag.
258         */
259        public String getName() {
260                return name;
261        }
262        
263        /**
264         * Gibt den kompletten Namen des Tag zurueck, inkl. Name-Space und Trenner.
265         * @return String Kompletter Name des Tag.
266         */
267        public String getFullName() {
268                String fullName;
269                if(tagLib!=null)        {
270                        fullName=tagLib.getNameSpaceAndSeparator()+name;
271                }
272                else    {
273                        fullName=name;
274                }
275                return fullName;
276        }
277
278        /**
279         * Gibt die Klassendefinition, welche diesen Tag implementiert, als Zeichenkette zurueck.
280         * Achtung: Die implementierende Klasse ist keine Java Klasse.
281         * @return String Zeichenkette der Klassendefinition.
282         */
283        public String getTagClassName() {
284                return tagClass;
285        }
286        
287
288        public Class getClazz() throws ClassException {
289                if(clazz==null) {
290                        clazz=ClassUtil.loadClass(tagClass);
291                }
292                return clazz;
293        }
294        
295        
296        public Type getTagType() throws ClassException {
297                if(tagType==null) {
298                        tagType=Type.getType(getClazz());
299                }
300                return tagType;
301        }
302        /**
303         * @return the status (TagLib.,TagLib.STATUS_IMPLEMENTED,TagLib.STATUS_DEPRECATED,TagLib.STATUS_UNIMPLEMENTED)
304         */
305        public short getStatus() {
306                return status;
307        }
308
309
310        /**
311         * @param status the status to set (TagLib.,TagLib.STATUS_IMPLEMENTED,TagLib.STATUS_DEPRECATED,TagLib.STATUS_UNIMPLEMENTED)
312         */
313        public void setStatus(short status) {
314                this.status = status;
315        }
316        
317        /**
318         * Gibt die Klassendefinition, der Klasse die den Evaluator (Translation Time Evaluator) implementiert, 
319         * als Zeichenkette zurueck.
320         * Falls kein Evaluator definiert ist wird null zurueckgegeben.
321         * @return String Zeichenkette der Klassendefinition.
322         */
323        public String getTteClassName() {
324                return tteClass;
325        }
326        
327        public String getTttClassName() {
328                return tttClass;
329        }
330        
331        /**
332         * Gibt den Evaluator (Translation Time Evaluator) dieser Klasse zurueck.
333         * Falls kein Evaluator definiert ist, wird null zurueckgegeben.
334         * @return Implementation des Evaluator zu dieser Klasse.
335         * @throws EvaluatorException Falls die Evaluator-Klasse nicht geladen werden kann.
336         */
337        public Evaluator getEvaluator() throws EvaluatorException {
338                if(!hasTteClass()) return null;
339                if(eval!=null) return eval;
340                try {
341                        eval = (Evaluator) ClassUtil.loadInstance(tteClass);
342                } 
343                catch (ClassException e) {
344                        throw new EvaluatorException(e.getMessage());
345                } 
346                return eval;
347        }
348        
349        /**
350         * Gibt den TagDependentBodyTransformer dieser Klasse zurueck.
351         * Falls kein TagDependentBodyTransformer definiert ist, wird null zurueckgegeben.
352         * @return Implementation des TagDependentBodyTransformer zu dieser Klasse.
353         * @throws TagLibException Falls die TagDependentBodyTransformer-Klasse nicht geladen werden kann.
354         */
355        public TagDependentBodyTransformer getBodyTransformer() throws TagLibException {
356                if(!hasTdbtClass()) return null;
357                if(tdbt!=null) return tdbt;
358                try {
359                        tdbt = (TagDependentBodyTransformer) ClassUtil.loadInstance(tdbtClass);
360                } catch (ClassException e) {
361                        throw new TagLibException(e);
362                } 
363                return tdbt;
364        }
365        
366        /**
367         * Gibt zurueck ob Exception durch die implementierte Klasse abgehandelt werden oder nicht
368         * @return Wird eine Exception abgehandelt?
369         */
370        public boolean handleException() {
371                return handleException;
372        }
373
374        /**
375         * Gibt zurueck, ob eine Klassendefinition
376         * der Klasse die den Evaluator (Translation Time Evaluator) implementiert existiert.
377         * @return Ob eine Evaluator definiert ist.
378         */
379        public boolean hasTteClass() {
380                return tteClass !=null && tteClass.length()>0;
381        }
382
383        /**
384         * Gibt zurueck, ob eine Klassendefinition
385         * der Klasse die den TagDependentBodyTransformer implementiert existiert.
386         * @return Ob eine Evaluator definiert ist.
387         */
388        public boolean hasTdbtClass() {
389                return tdbtClass !=null && tdbtClass.length()>0;
390        }
391
392        /**
393         * Gibt den Attributetyp der Klasse zurueck.
394         * ( ATTRIBUTE_TYPE_FIX, ATTRIBUTE_TYPE_DYNAMIC, ATTRIBUTE_TYPE_NONAME)
395         * @return int
396         */
397        public int getAttributeType() {
398                return attributeType;
399        }
400
401        /**
402         * Gibt zurueck, ob das Tag einen Body haben kann oder nicht.
403         * @return Kann das Tag einen Body haben.
404         */
405        public boolean getHasBody() {
406                return hasBody;
407        }
408
409        /**
410         * Gibt die maximale Anzahl Attribute zurueck, die das Tag haben kann.
411         * @return Maximale moegliche Anzahl Attribute.
412         */
413        public int getMax() {
414                return max;
415        }
416
417        /**
418         * Gibt die minimale Anzahl Attribute zurueck, die das Tag haben muss.
419         * @return Minimal moegliche Anzahl Attribute.
420         */
421        public int getMin() {
422                return min;
423        }
424
425        /**
426         * Gibt die TagLib zurueck zu der das Tag gehoert.
427         * @return TagLib Zugehoerige TagLib.
428         */
429        public TagLib getTagLib() {
430                return tagLib;
431        }
432        
433        /**
434         * Gibt zurueck ob das Tag seinen Body parsen soll oder nicht.
435         * @return Soll der Body geparst werden.
436         */
437        public boolean getParseBody() {
438                return parseBody;
439        }
440        
441        /**
442         * Gibt zurueck, ob das Tag einen Appendix besitzen kann oder nicht.
443         * @return Kann das Tag einen Appendix besitzen.
444         */
445        public boolean hasAppendix() {
446                return hasAppendix;
447        }
448
449        /**
450         * Fragt ab ob der Body eines Tag freiwillig ist oder nicht.
451         * @return is required
452         */
453        public boolean isBodyReq() {
454                return isBodyReq;
455        }
456
457        /**
458         * Fragt ab ob die verarbeitung des Inhaltes eines Tag mit einem eigenen Transformer 
459         * vorgenommen werden soll.
460         * @return Fragt ab ob die verarbeitung des Inhaltes eines Tag mit einem eigenen Transformer 
461         * vorgenommen werden soll.
462         */
463        public boolean isTagDependent() {
464                return isTagDependent;
465        }
466
467        /**
468         * Setzt die TagLib des Tag.
469         * Diese Methode wird durch die Klasse TagLibFactory verwendet.
470         * @param tagLib TagLib des Tag.
471         */
472        protected void setTagLib(TagLib tagLib) {
473                this.tagLib = tagLib;
474        }
475        
476        /**
477         * Setzt ein einzelnes Attribut (TagLibTagAttr) eines Tag.
478         * Diese Methode wird durch die Klasse TagLibFactory verwendet.
479         * @param attribute Attribute eines Tag.
480         */
481        public void setAttribute(TagLibTagAttr attribute) {
482                attributes.put(attribute.getName(),attribute);
483                if(attrFirst==null)attrFirst=attribute;
484                attrLast=attribute;
485        }
486
487        /**
488         * Setzt den Attributtyp eines Tag.
489         * ( ATTRIBUTE_TYPE_FIX, ATTRIBUTE_TYPE_DYNAMIC, ATTRIBUTE_TYPE_FULLDYNAMIC, ATTRIBUTE_TYPE_NONAME)
490         * Diese Methode wird durch die Klasse TagLibFactory verwendet.
491         * @param attributeType The attributeType to set
492         */
493        public void setAttributeType(int attributeType) {
494                
495                this.attributeType = attributeType;
496        }
497
498        /**
499         * Setzt die Information, was fuer ein BodyContent das Tag haben kann.
500         * Diese Methode wird durch die Klasse TagLibFactory verwendet.
501         * @param value BodyContent Information.
502         */
503        public void setBodyContent(String value) {
504                // empty, free, must, tagdependent
505                value=value.toLowerCase().trim();
506                //if(value.equals("jsp")) value="free";
507                
508                this.hasBody = !value.equals("empty");
509                this.isBodyReq = !value.equals("free");
510                this.isTagDependent = value.equals("tagdependent");
511                bodyFree=value.equals("free");
512        }
513
514        /**
515         * Setzt wieviele Attribute das Tag maximal haben darf.
516         * Diese Methode wird durch die Klasse TagLibFactory verwendet.
517         * @param max The max to set
518         */
519        protected void setMax(int max) {
520                this.max = max;
521        }
522
523        /**
524         * Setzt wieviele Attribute das Tag minimal haben darf.
525         * Diese Methode wird durch die Klasse TagLibFactory verwendet.
526         * @param min The min to set
527         */
528        protected void setMin(int min) {
529                this.min = min;
530        }
531
532        /**
533         * Setzt den Namen des Tag.
534         * Diese Methode wird durch die Klasse TagLibFactory verwendet.
535         * @param name Name des Tag.
536         */
537        public void setName(String name) {
538                this.name = name.toLowerCase();
539        }
540
541        /**
542         * Setzt die implementierende Klassendefinition des Tag.
543         * Diese Methode wird durch die Klasse TagLibFactory verwendet.
544         * @param tagClass Klassendefinition der Tag-Implementation.
545         */
546        public void setTagClass(String tagClass) {
547                this.tagClass = tagClass;
548        }
549
550        /**
551         * Setzt die implementierende Klassendefinition des Evaluator.
552         * Diese Methode wird durch die Klasse TagLibFactory verwendet.
553         * @param  tteClass Klassendefinition der Evaluator-Implementation.
554         */
555        protected void setTteClass(String tteClass) {
556                this.tteClass = tteClass;
557        }
558
559        /**
560         * Setzt die implementierende Klassendefinition des Evaluator.
561         * Diese Methode wird durch die Klasse TagLibFactory verwendet.
562         * @param  tteClass Klassendefinition der Evaluator-Implementation.
563         */
564        public void setTttClass(String tttClass) {
565                this.tttClass = tttClass;
566                this.tttConstructor=null;
567        }
568
569        /**
570         * Setzt die implementierende Klassendefinition des TagDependentBodyTransformer.
571         * Diese Methode wird durch die Klasse TagLibFactory verwendet.
572         * @param  tdbtClass Klassendefinition der TagDependentBodyTransformer-Implementation.
573         */
574        public void setTdbtClass(String tdbtClass) {
575                this.tdbtClass = tdbtClass;
576                this.tdbt = null;
577        }
578
579        /**
580         * Setzt, ob der Body des Tag geparst werden soll oder nicht.
581         * Diese Methode wird durch die Klasse TagLibFactory verwendet.
582         * @param parseBody Soll der Body geparst werden.
583         */
584        public void setParseBody(boolean parseBody) {
585                this.parseBody = parseBody;
586        }
587
588        /**
589         * Setzt ob das Tag einen Appendix besitzen kann oder nicht.
590         * Diese Methode wird durch die Klasse TagLibFactory verwendet.
591         * @param hasAppendix Kann das Tag einen Appendix besitzen.
592         */
593        public void setAppendix(boolean hasAppendix) {
594                this.hasAppendix = hasAppendix;
595        }
596        /**
597         * @return Returns the description.
598         */
599        public String getDescription() {
600                return description;
601        }
602
603        /**
604         * @param description The description to set.
605         */
606        public void setDescription(String description) {
607                this.description = description;
608        }
609
610        /**
611         * @return Returns the bodyIsFree.
612         */
613        public boolean isBodyFree() {
614                return bodyFree;
615        }
616
617        public boolean hasBodyMethodExists() {
618                Class clazz= ClassUtil.loadClass(getTagClassName(),(Class)null);//Class.orName(tag.getTagClassName());
619                if(clazz==null) return false;
620                
621                try {
622                        java.lang.reflect.Method method = clazz.getMethod("hasBody", new Class[]{boolean.class});
623                        if(method==null)return false;
624                        return method.getReturnType()==void.class;
625                } 
626                catch (Exception e) {}
627                return false;
628        }
629
630        /**
631         * @return Gibt zurueck ob ein Attribut Evaluator definiert ist oder nicht.
632         */
633        public boolean hasAttributeEvaluator() {
634                return strAttributeEvaluator!=null;
635        }
636
637        /**
638         * @return Gibt den AttributeEvaluator zum Tag zurueck
639         * @throws AttributeEvaluatorException
640         */
641        public AttributeEvaluator getAttributeEvaluator() throws AttributeEvaluatorException {
642                if(!hasAttributeEvaluator()) return null;
643                if(attributeEvaluator!=null) return attributeEvaluator;
644                try {
645                        return  attributeEvaluator=(AttributeEvaluator) ClassUtil.loadInstance(strAttributeEvaluator);
646                        
647                } catch (ClassException e) {
648                        throw new AttributeEvaluatorException(e.getMessage());
649                } 
650        }
651
652        /**
653         * Setzt den Namen der Klasse welche einen AttributeEvaluator implementiert.
654         * @param value Name der AttributeEvaluator Klassse
655         */
656        public void setAttributeEvaluatorClassName(String value) {
657                strAttributeEvaluator=value;
658                
659        }
660
661        /**
662         * sets if tag handle exception inside his body or not
663         * @param handleException handle it or not
664         */
665        public void setHandleExceptions(boolean handleException) {
666                this.handleException=handleException;
667        }
668
669    /**
670     * @return
671     */
672    public boolean hasDefaultValue() {
673        return hasDefaultValue;
674    }
675
676    /**
677     * @param hasDefaultValue The hasDefaultValue to set.
678     */
679    public void setHasDefaultValue(boolean hasDefaultValue) {
680        this.hasDefaultValue = hasDefaultValue;
681    }
682
683        /**
684         * return ASM Tag for this tag
685         * @param line
686         * @return
687
688         */
689        public Tag getTag(Position start,Position end) throws TagLibException {
690                if(StringUtil.isEmpty(tttClass)) return new TagOther(start,end);
691                try {
692                        return _getTag(start,end);
693                } 
694                catch (ClassException e) {
695                        throw new TagLibException(e.getMessage());
696                } 
697                catch (NoSuchMethodException e) {
698                        throw new TagLibException(e.getMessage());
699                } 
700                catch (Throwable e) {
701                        ExceptionUtil.rethrowIfNecessary(e);
702                        throw new TagLibException(e);
703                }
704        }
705        private Tag _getTag(Position start,Position end) throws ClassException, SecurityException, NoSuchMethodException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException {
706                if(tttConstructor==null) {
707                        Class clazz = ClassUtil.loadClass(tttClass);
708                        tttConstructor = clazz.getConstructor(CONSTRUCTOR_PARAMS);
709                }
710                return (Tag) tttConstructor.newInstance(new Object[]{start,end});
711        }
712
713        public void setAllowRemovingLiteral(boolean allowRemovingLiteral) {
714                this.allowRemovingLiteral=allowRemovingLiteral;
715        }
716
717        /**
718         * @return the allowRemovingLiteral
719         */
720        public boolean isAllowRemovingLiteral() {
721                return allowRemovingLiteral;
722        }
723
724        public String getAttributeNames() {
725                Iterator<String> it = attributes.keySet().iterator();
726                StringBuffer sb=new StringBuffer();
727                while(it.hasNext()) {
728                        if(sb.length()>0)sb.append(",");
729                        sb.append(it.next());
730                }
731                return sb.toString();
732        }
733
734        public String getSetter(Attribute attr, Type type) {
735                if(tagLib.isCore())
736                        return "set"+StringUtil.ucFirst(attr.getName());
737                
738                String setter=setters.get(attr.getName());
739                if(setter!=null)return setter;
740                setter = "set"+StringUtil.ucFirst(attr.getName());
741                Class clazz;
742                try {
743                        if(type==null) type = CastOther.getType(attr.getType());
744                        clazz=ClassUtil.loadClass(getTagClassName());
745                        java.lang.reflect.Method m = ClassUtil.getMethodIgnoreCase(clazz,setter,new Class[]{ClassUtil.loadClass(type.getClassName())});
746                        setter=m.getName();
747                } 
748                catch (Exception e) {
749                        //print.err(setter);
750                        e.printStackTrace();
751                }
752                setters.put(attr.getName(), setter);
753                return setter;
754        }
755
756        public String getHash() {
757                StringBuilder sb=new StringBuilder();
758                sb.append(this.getTagClassName());
759                sb.append(this.getAttributeNames());
760                sb.append(this.getAttributeType());
761                sb.append(this.getMax());
762                sb.append(this.getMin());
763                sb.append(this.getName());
764                sb.append(this.getParseBody());
765                sb.append(this.getTteClassName());
766                sb.append(this.getTttClassName());
767                Iterator<Entry<String, TagLibTagAttr>> it = this.getAttributes().entrySet().iterator();
768                Entry<String, TagLibTagAttr> entry;
769                while(it.hasNext()){
770                        entry = it.next();
771                        sb.append(entry.getKey());
772                        sb.append(entry.getValue().getHash());
773                }
774                
775                try {
776                        return Md5.getDigestAsString(sb.toString());
777                } catch (IOException e) {
778                        return "";
779                }
780        }
781
782        public TagLibTagAttr getDefaultAttribute() {
783                return defaultAttribute;
784        }
785        
786        public void setDefaultAttribute(TagLibTagAttr defaultAttribute) {
787                this.defaultAttribute=defaultAttribute;
788        }
789
790
791        public void setScript(TagLibTagScript script) {
792                this.script=script;
793        }
794
795
796        /**
797         * @return the script
798         */
799        public TagLibTagScript getScript() {
800                return script;
801        }
802
803
804        public TagLibTagAttr getSingleAttr() {
805                
806                if(singleAttr==UNDEFINED) {
807                        singleAttr=null;
808                        Iterator<TagLibTagAttr> it = getAttributes().values().iterator();
809                        TagLibTagAttr attr;
810                        while(it.hasNext()){
811                                attr=it.next();
812                                if(attr.getNoname()){
813                                        singleAttr=attr;
814                                        break;
815                                }       
816                        }
817                }
818                return singleAttr;
819        }
820
821
822        /**
823         * attribute value set, if the attribute has no value defined
824         * @return
825         */
826        public Expression getAttributeDefaultValue() {
827                if(attributeDefaultValue==null) return LitBoolean.TRUE;
828                return attributeDefaultValue;
829        }
830        
831        public void setAttributeDefaultValue(String defaultValue) {
832                defaultValue=defaultValue.trim();
833                // boolean
834                if(StringUtil.startsWithIgnoreCase(defaultValue, "boolean:")) {
835                        String str=defaultValue.substring(8).trim();
836                        Boolean b = Caster.toBoolean(str,null);
837                        if(b!=null){
838                                this.attributeDefaultValue=LitBoolean.toExprBoolean(b.booleanValue());
839                                return;
840                        }
841                        
842                }
843                // number
844                else if(StringUtil.startsWithIgnoreCase(defaultValue, "number:")) {
845                        String str=defaultValue.substring(7).trim();
846                        Double d = Caster.toDouble(str,null);
847                        if(d!=null){
848                                this.attributeDefaultValue=LitDouble.toExprDouble(d.doubleValue());
849                                return;
850                        }
851                        
852                }
853                else this.attributeDefaultValue=LitString.toExprString(defaultValue);
854        }
855}