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.net.URI;
023import java.net.URISyntaxException;
024import java.util.ArrayList;
025import java.util.HashMap;
026import java.util.Iterator;
027import java.util.List;
028import java.util.Map;
029import java.util.Map.Entry;
030
031import lucee.commons.lang.ClassException;
032import lucee.commons.lang.ClassUtil;
033import lucee.commons.lang.Md5;
034import lucee.commons.lang.StringUtil;
035import lucee.runtime.exp.ExpressionException;
036import lucee.runtime.exp.PageRuntimeException;
037import lucee.transformer.cfml.ExprTransformer;
038
039
040/**
041 * Die Klasse TagLib repaesentiert eine Komplette TLD, 
042 * mit ihrer Hilfe kann man alle Informationen, zu einer TLD Abfragen. 
043 */
044public class TagLib implements Cloneable {
045
046        public static final short STATUS_IMPLEMENTED=0;
047        public static final short STATUS_DEPRECATED=1;
048        public static final short STATUS_UNIMPLEMENTED=2;
049        public static final short STATUS_HIDDEN=4;
050        
051        
052        /**
053     * Field <code>EXPR_TRANSFORMER</code>
054     */
055    public static String EXPR_TRANSFORMER="lucee.transformer.cfml.expression.CFMLExprTransformer";
056        
057    private String shortName="";
058    private String displayName=null;
059    private String type="cfml";
060    private String nameSpace;
061        private String nameSpaceSeperator=":";
062        private String ELClass=EXPR_TRANSFORMER;
063        private HashMap<String,TagLibTag> tags=new HashMap<String,TagLibTag>();
064        private HashMap<String,TagLibTag> appendixTags=new HashMap<String,TagLibTag>();
065        private ExprTransformer exprTransformer;
066
067    private char[] nameSpaceAndNameSpaceSeperator;
068
069        private boolean isCore;
070
071        private String source;
072
073        private URI uri;
074
075        private String description;
076        private TagLibTag[] scriptTags;
077        private boolean ignoreUnknowTags;
078
079        
080        /**
081         * Geschuetzer Konstruktor ohne Argumente.
082         */
083        protected TagLib(boolean isCore) {
084                this.isCore=isCore;
085        }
086        protected TagLib() {
087                this(false);
088        }
089
090        /**
091         * @param source the source to set
092         */
093        public void setSource(String source) {
094                this.source = source;
095        }
096        
097        /**
098         * Gibt den Name-Space einer TLD als String zurueck.
099         * @return String Name der TLD.
100         */
101        public String getNameSpace() {
102        
103                return nameSpace;
104        }
105
106        /**
107         * Gibt den Trenner zwischen Name-Space und Name einer TLD zurueck.
108         * @return Name zwischen Name-Space und Name.
109         */
110        public String getNameSpaceSeparator() {
111                return nameSpaceSeperator;
112        }
113    
114    /**
115     * Gibt den Name-Space inkl. dem Seperator zurueck.
116     * @return String
117     */
118    public String getNameSpaceAndSeparator() {
119        return nameSpace+nameSpaceSeperator;
120    }
121    
122    /**
123     * Gibt den Name-Space inkl. dem Seperator zurueck.
124     * @return String
125     */
126    public char[] getNameSpaceAndSeperatorAsCharArray() {
127        if(nameSpaceAndNameSpaceSeperator==null) {
128            nameSpaceAndNameSpaceSeperator=getNameSpaceAndSeparator().toCharArray();
129        }
130        return nameSpaceAndNameSpaceSeperator;
131    }
132        
133        /**
134         * Gibt einen Tag (TagLibTag)zurueck, dessen Name mit dem uebergebenen Wert uebereinstimmt,
135         * falls keine uebereinstimmung gefunden wird, wird null zurueck gegeben.
136         * @param name Name des Tag das zurueck gegeben werden soll.
137         * @return TagLibTag Tag das auf den Namen passt.
138         */
139        public TagLibTag getTag(String name)    {
140                return tags.get(name);
141        }
142        
143        public TagLibTag getTag(Class clazz)    {
144                Iterator<TagLibTag> _tags = tags.values().iterator();
145                TagLibTag tlt;
146                while(_tags.hasNext()){
147                        tlt = _tags.next();
148                        if(tlt.getTagClassName().equalsIgnoreCase(clazz.getName())) {
149                                return tlt;
150                        }
151                }
152                return null;
153        }
154        
155        /**
156         * Gibt einen Tag (TagLibTag)zurueck, welches definiert hat, dass es einen Appendix besitzt.
157         * D.h. dass der Name des Tag mit weiteren Buchstaben erweitert sein kann, 
158         * also muss nur der erste Teil des Namen vom Tag mit dem uebergebenen Namen uebereinstimmen.
159         * Wenn keine uebereinstimmung gefunden wird, wird null zurueck gegeben.
160         * @param name Name des Tag inkl. Appendix das zurueck gegeben werden soll.
161         * @return TagLibTag Tag das auf den Namen passt.
162         */
163        public TagLibTag getAppendixTag(String name)    {
164                Iterator<String> it = appendixTags.keySet().iterator();
165                String match="";
166                while(it.hasNext()) {
167                        String tagName=StringUtil.toStringNative(it.next(),"");
168                        if(match.length()<tagName.length() && name.indexOf(tagName)==0) {
169                                match=tagName;
170                        }
171                }
172                return getTag(match);
173        }
174        
175        /**
176         * Gibt alle Tags (TagLibTag) als HashMap zurueck.
177         * @return Alle Tags als HashMap.
178         */
179        public Map<String,TagLibTag> getTags() {
180                return tags;
181        }
182
183        /**
184         * Gibt die Klasse des ExprTransformer als Zeichenkette zurueck.
185         * @return String
186         */
187        public String getELClass() {
188                return ELClass;
189        }
190
191        /**
192         * Laedt den innerhalb der TagLib definierten ExprTransfomer und gibt diesen zurueck.
193         * Load Expression Transfomer defined in the tag library and return it.
194         * @return ExprTransformer
195         * @throws TagLibException
196         */
197        public ExprTransformer getExprTransfomer() throws TagLibException {
198                //Class cls;
199                if(exprTransformer!=null)
200                        return exprTransformer;
201                
202                try {
203                        exprTransformer =  (ExprTransformer)ClassUtil.loadInstance(ELClass);
204                        //exprTransformer = (ExprTransformer) cls.newInstance();
205                } 
206                catch (ClassException e) {
207                        throw new TagLibException(e);
208                }
209                return exprTransformer;
210        }
211        
212        /**
213         * Fuegt der TagLib einen weiteren Tag hinzu.
214         * Diese Methode wird durch die Klasse TagLibFactory verwendet.
215         * @param tag Neuer Tag.
216         */
217        public void setTag(TagLibTag tag)       {
218                tag.setTagLib(this);
219                tags.put(tag.getName(),tag);
220                
221                if(tag.hasAppendix())
222                        appendixTags.put(tag.getName(),tag);
223                else if(appendixTags.containsKey(tag.getName()))
224                        appendixTags.remove(tag.getName());
225        }
226        
227        /**
228         * Fuegt der TagLib die Evaluator Klassendefinition als Zeichenkette hinzu.
229         * Diese Methode wird durch die Klasse TagLibFactory verwendet.
230         * @param eLClass Zeichenkette der Evaluator Klassendefinition.
231         */
232        protected void setELClass(String eLClass) {
233                ELClass = eLClass;
234        }
235
236        /**
237         * Fuegt der TagLib die die Definition des Name-Space hinzu.
238         * Diese Methode wird durch die Klasse TagLibFactory verwendet.
239         * @param nameSpace Name-Space der TagLib.
240         */
241        public void setNameSpace(String nameSpace) {
242                this.nameSpace = nameSpace.toLowerCase();
243        }
244
245        /**
246         * Fuegt der TagLib die die Definition des Name-Space-Seperator hinzu.
247         * Diese Methode wird durch die Klasse TagLibFactory verwendet.
248         * @param nameSpaceSeperator Name-Space-Seperator der TagLib.
249         */
250        public void setNameSpaceSeperator(String nameSpaceSeperator) {
251                this.nameSpaceSeperator = nameSpaceSeperator;
252        }
253
254    /**
255     * @return Returns the displayName.
256     */
257    public String getDisplayName() {
258        if(displayName==null)return shortName;
259        return displayName;
260    }
261    /**
262     * @param displayName The displayName to set.
263     */
264    public void setDisplayName(String displayName) {
265        this.displayName = displayName;
266    }
267    /**
268     * @return Returns the shortName.
269     */
270    public String getShortName() {
271        return shortName;
272    }
273    /**
274     * @param shortName The shortName to set.
275     */
276    public void setShortName(String shortName) {
277        this.shortName = shortName;
278        if(nameSpace==null)nameSpace=shortName.toLowerCase();
279    }
280    
281
282        public void setIgnoreUnknowTags(boolean ignoreUnknowTags) {
283                this.ignoreUnknowTags=ignoreUnknowTags;
284        }
285        public boolean getIgnoreUnknowTags() {
286                return ignoreUnknowTags;
287        }
288    
289    
290    /**
291     * @return Returns the type.
292     */
293    public String getType() {
294        return type;
295    }
296    /**
297     * @param type The type to set.
298     */
299    public void setType(String type) {
300        this.type = type;
301    }
302
303    /**
304     * @see java.lang.Object#toString()
305     */
306    public String toString() {
307        return getDisplayName()+":"+getShortName()+":"+super.toString();
308    }
309    
310    public String getHash() {
311        StringBuffer sb=new StringBuffer();
312        Iterator<String> it = tags.keySet().iterator();
313        while(it.hasNext()) {
314                //"__filename"
315                
316                sb.append((tags.get(it.next())).getHash()+"\n");
317        }
318        try {
319                        return Md5.getDigestAsString(sb.toString());
320                } catch (IOException e) {
321                        return "";
322                }
323    }
324
325        public boolean isCore() {
326                return isCore;
327        }
328        
329        public void setIsCore(boolean isCore) {
330                this.isCore=isCore;
331        }
332        
333
334
335        /**
336         * @see java.lang.Object#clone()
337         */
338        public Object clone(){
339                return duplicate(false);
340        }
341
342        /**
343         * duplicate the taglib, does not 
344         * @param deepCopy duplicate also the children (TagLibTag) of this TagLib
345         * @return clone of this taglib
346         */
347        public TagLib duplicate(boolean deepCopy) {
348                TagLib tl = new TagLib(isCore);
349                tl.appendixTags=duplicate(this.appendixTags,deepCopy);
350                tl.displayName=this.displayName;
351                tl.ELClass=this.ELClass;
352                tl.exprTransformer=this.exprTransformer;
353                tl.isCore=this.isCore;
354                tl.nameSpace=this.nameSpace;
355                tl.nameSpaceAndNameSpaceSeperator=this.nameSpaceAndNameSpaceSeperator;
356                tl.nameSpaceSeperator=this.nameSpaceSeperator;
357                tl.shortName=this.shortName;
358                tl.tags=duplicate(this.tags,deepCopy);
359                tl.type=this.type;
360                tl.source=this.source;
361                tl.ignoreUnknowTags=this.ignoreUnknowTags;
362                
363                return tl;
364        }
365        
366        /**
367         * duplcate a hashmap with TagLibTag's
368         * @param tags
369         * @param deepCopy
370         * @return cloned map
371         */
372        private HashMap<String,TagLibTag> duplicate(HashMap<String,TagLibTag> tags, boolean deepCopy) {
373                if(deepCopy) throw new PageRuntimeException(new ExpressionException("deep copy not supported"));
374                
375                Iterator<Entry<String, TagLibTag>> it = tags.entrySet().iterator();
376                HashMap<String,TagLibTag> cm = new HashMap<String,TagLibTag>();
377                Entry<String, TagLibTag>  entry;
378                while(it.hasNext()){
379                        entry = it.next();
380                        cm.put(
381                                        entry.getKey(), 
382                                        deepCopy?
383                                                        entry.getValue(): // TODO add support for deepcopy ((TagLibTag)entry.getValue()).duplicate(deepCopy):
384                                                        entry.getValue());
385                }
386                return cm;
387        }
388        public String getSource() {
389                return source;
390        }
391        public URI getUri() {
392                // TODO Auto-generated method stub
393                return uri;
394        }
395        public void setUri(String strUri) throws URISyntaxException {
396                this.uri=new URI(strUri);
397        }
398        public void setUri(URI uri) {
399                this.uri=uri;
400        }
401        public void setDescription(String description) {
402                this.description=description;
403        }
404        
405        public String getDescription() {
406                return description;
407        }
408        
409        public TagLibTag[] getScriptTags() {
410                if(scriptTags==null) {
411                        Iterator<TagLibTag> it = getTags().values().iterator();
412                        TagLibTag tag;
413                        TagLibTagScript script;
414                        List<TagLibTag> tags=new ArrayList<TagLibTag>();
415                        while(it.hasNext()){
416                                tag = it.next();
417                                script = tag.getScript();
418                                if(script!=null && script.getType()!=TagLibTagScript.TYPE_NONE) {
419                                        tags.add(tag);
420                                        //print.o(tag.getName()+":"+tag.getScript().getType());
421                                }
422                        }
423                        scriptTags=tags.toArray(new TagLibTag[tags.size()]);
424                }
425                return scriptTags;
426        }
427        
428        
429}