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.Method;
023import java.util.ArrayList;
024import java.util.Date;
025import java.util.List;
026
027import lucee.commons.lang.CFTypes;
028import lucee.commons.lang.ClassUtil;
029import lucee.commons.lang.ExceptionUtil;
030import lucee.commons.lang.Md5;
031import lucee.commons.lang.StringUtil;
032import lucee.runtime.op.Caster;
033import lucee.runtime.type.dt.DateTime;
034import lucee.runtime.type.dt.TimeSpan;
035import lucee.runtime.type.util.ListUtil;
036
037
038/**
039 * Die Klasse TagLibTagAttr repraesentiert ein einzelnes Attribute eines Tag 
040 * und haelt saemtliche Informationen zu diesem Attribut.
041 */
042public final class TagLibTagAttr {
043
044        public static final short SCRIPT_SUPPORT_NONE = 0;
045        public static final short SCRIPT_SUPPORT_OPTIONAL = 1;
046        public static final short SCRIPT_SUPPORT_REQUIRED = 2;
047        
048        private String name="noname";
049        private String[] alias=null;
050        
051
052
053        private String type;
054        private String description="";
055        private boolean required;
056        private boolean rtexpr=true;
057        private Object defaultValue;
058    private TagLibTag tag;
059        private boolean hidden;
060        private boolean _default;
061        private boolean noname;
062        private short status=TagLib.STATUS_IMPLEMENTED;
063        private short scriptSupport=SCRIPT_SUPPORT_NONE;
064        private String valueList;
065        private char delimiter=',';
066        private Object[] values;
067
068        
069        public TagLibTagAttr duplicate(TagLibTag tag) {
070                TagLibTagAttr tlta=new TagLibTagAttr(tag);
071                tlta.name=name;
072                tlta.alias=alias;
073                tlta.type=type;
074                tlta.description=description;
075                tlta.required=required;
076                tlta.rtexpr=rtexpr;
077                tlta.defaultValue=defaultValue;
078                tlta.hidden=hidden;
079                tlta.valueList=valueList;
080                tlta.values=values;
081                tlta.delimiter=delimiter;
082                tlta.noname=noname;
083                tlta._default=_default;
084                tlta.status=status;
085                
086                
087                return tlta;
088        }
089        
090        
091        /**
092         * @return the status (TagLib.,TagLib.STATUS_IMPLEMENTED,TagLib.STATUS_DEPRECATED,TagLib.STATUS_UNIMPLEMENTED)
093         */
094        public short getStatus() {
095                return status;
096        }
097
098
099        /**
100         * @param status the status to set (TagLib.,TagLib.STATUS_IMPLEMENTED,TagLib.STATUS_DEPRECATED,TagLib.STATUS_UNIMPLEMENTED)
101         */
102        public void setStatus(short status) {
103                this.status = status;
104        }
105
106        /**
107         * Geschuetzer Konstruktor ohne Argumente.
108         */
109        public TagLibTagAttr(TagLibTag tag) {
110            this.tag=tag;
111        }
112
113        /**
114         * Gibt den Namen des Attribut zurueck.
115         * @return Name des Attribut.
116         */
117        public String getName() {
118                return name;
119        }
120        
121
122        public TagLibTag getTag() {
123                return tag;
124        }
125        
126        public String[] getAlias() {
127                return alias;
128        }
129
130
131        public void setAlias(String strAlias) {
132                this.alias = lucee.runtime.type.util.ListUtil.trimItems(lucee.runtime.type.util.ListUtil.listToStringArray(strAlias.toLowerCase(),','));
133        }
134
135        /**
136         * Gibt zurueck, ob das Attribut Pflicht ist oder nicht.
137         * @return Ist das Attribut Pflicht.
138         */
139        public boolean isRequired() {
140                return required;
141        }
142
143        /**
144         * Gibt den Typ des Attribut zurueck (query, struct, string usw.)
145         * @return Typ des Attribut
146         */
147        public String getType() {
148            if(this.type==null) {
149                try {
150                String methodName="set"+
151                        (name.length()>0?""+Character.toUpperCase(name.charAt(0)):"")+
152                        (name.length()>1?name.substring(1):"");
153                
154                Class clazz= ClassUtil.loadClass(tag.getTagClassName(),(Class)null);//Class.orName(tag.getTagClassName());
155            if(clazz!=null) {
156                Method[] methods = clazz.getMethods();
157                for(int i=0;i<methods.length;i++) {
158                    Method method = methods[i];
159                    if(method.getName().equalsIgnoreCase(methodName)) {
160                        Class[] types = method.getParameterTypes();
161                        if(types.length==1) {
162                                Class type=types[0];
163                            if(type==String.class)this.type="string";
164                            else if(type==double.class)this.type="number";
165                            else if(type==Date.class)this.type="datetime";
166                            else this.type=type.getName();
167                        }
168                    }
169                }
170            }
171                }
172                catch(Throwable t) {
173                                ExceptionUtil.rethrowIfNecessary(t);
174                        return "string";
175                }
176            }
177                return this.type;
178        }
179
180        /**
181         * Gibt zurueck ob das Attribute eines Tag, mithilfe des ExprTransformer, uebersetzt werden soll oder nicht.
182         * @return Soll das Attribut uebbersetzt werden
183         */
184        public boolean getRtexpr() {
185                return rtexpr;
186        }
187
188        /**
189         * Setzt den Namen des Attribut.
190         * @param name Name des Attribut.
191         */
192        public void setName(String name) {
193                this.name = name.toLowerCase();
194        }
195
196        /**
197         * Setzt, ob das Argument Pflicht ist oder nicht.
198         * @param required Ist das Attribut Pflicht.
199         */
200        public void setRequired(boolean required) {
201                this.required = required;
202        }
203
204        /**
205         * Setzt, ob das Attribute eines Tag, mithilfe des ExprTransformer, uebersetzt werden soll oder nicht.
206         * @param rtexpr Soll das Attribut uebbersetzt werden
207         */
208        public void setRtexpr(boolean rtexpr) {
209                this.rtexpr = rtexpr;
210        }
211
212        /**
213         * Setzt, den Typ des Attribut (query, struct, string usw.)
214         * @param type Typ des Attribut.
215         */
216        public void setType(String type) {
217                this.type = type;
218        }
219
220        /**
221         * @return Returns the description.
222         */
223        public String getDescription() {
224                return description;
225        }
226
227        /**
228         * @param description The description to set.
229         */
230        public void setDescription(String description) {
231                this.description = description;
232        }
233
234    /**
235     * @param defaultValue
236     */
237    public void setDefaultValue(Object defaultValue) {
238        this.defaultValue=defaultValue;
239        tag.setHasDefaultValue(true);
240    }
241
242    /**
243     * @return Returns the defaultValue.
244     */
245    public Object getDefaultValue() {
246        return defaultValue;
247    }
248
249    /**
250     * @return
251     */
252    public boolean hasDefaultValue() {
253        return defaultValue!=null;
254    }
255
256        public void setHidden(boolean hidden) {
257                this.hidden=hidden;
258        }
259        public boolean getHidden() {
260                return hidden;
261        }
262
263        public void setNoname(boolean noname) {
264                this.noname=noname;
265        }
266        public boolean getNoname() {
267                return noname;
268        }
269
270        public String getHash() {
271                StringBuffer sb=new StringBuffer();
272                sb.append(this.getDefaultValue());
273                sb.append(this.getName());
274                sb.append(this.getRtexpr());
275                sb.append(this.getType());
276                
277                try {
278                        return Md5.getDigestAsString(sb.toString());
279                } catch (IOException e) {
280                        return "";
281                }
282        }
283
284        public void isDefault(boolean _default) {
285                if(_default)
286                        tag.setDefaultAttribute(this);
287                this._default=_default;
288        }
289
290        public boolean isDefault() {
291                return _default;
292        }
293
294
295        public void setScriptSupport(String str) {
296                if(!StringUtil.isEmpty(str))  {
297                        str=str.trim().toLowerCase();
298                        if("optional".equals(str)) this.scriptSupport=SCRIPT_SUPPORT_OPTIONAL;
299                        else if("opt".equals(str)) this.scriptSupport=SCRIPT_SUPPORT_OPTIONAL;
300                        else if("required".equals(str)) this.scriptSupport=SCRIPT_SUPPORT_REQUIRED;
301                        else if("req".equals(str)) this.scriptSupport=SCRIPT_SUPPORT_REQUIRED;
302                }
303        }
304
305
306        /**
307         * @return the scriptSupport
308         */
309        public short getScriptSupport() {
310                return scriptSupport;
311        }
312
313
314        public Object getScriptSupportAsString() {
315                if(scriptSupport==SCRIPT_SUPPORT_OPTIONAL) return "optional";
316                if(scriptSupport==SCRIPT_SUPPORT_REQUIRED) return "required";
317                return "none";
318        }
319
320
321        public void setValueDelimiter(String delimiter) {
322                if(StringUtil.isEmpty(delimiter,true)) return;
323                this.delimiter=delimiter.trim().charAt(0);
324        }
325        
326        public void setValues(String valueList) {
327                if(tag.getName().equalsIgnoreCase("pop"))
328                if(StringUtil.isEmpty(valueList,true)) return;
329                this.valueList=valueList;
330        }
331        
332        public Object[] getValues() {
333                if(valueList==null) return null;
334                if(values!=null) return values;
335                String[] res = ListUtil.trimItems(ListUtil.listToStringArray(valueList, delimiter));
336                short type=CFTypes.toShort(getType(), false, CFTypes.TYPE_ANY);
337                // String
338                if(type==CFTypes.TYPE_STRING || type==CFTypes.TYPE_ANY) {
339                        values=res;
340                }
341                // Numeric
342                else if(type==CFTypes.TYPE_NUMERIC) {
343                        List<Double> list=new ArrayList<Double>();
344                        Double d;
345                        for(int i=0;i<res.length;i++){
346                                d=Caster.toDouble(res[i],null);
347                                if(d!=null)list.add(d);
348                        }
349                        values=list.toArray(new Double[list.size()]);
350                }
351                // Boolean
352                else if(type==CFTypes.TYPE_BOOLEAN) {
353                        List<Boolean> list=new ArrayList<Boolean>();
354                        Boolean b;
355                        for(int i=0;i<res.length;i++){
356                                b=Caster.toBoolean(res[i],null);
357                                if(b!=null)list.add(b);
358                        }
359                        values=list.toArray(new Boolean[list.size()]);
360                }
361                // DateTime
362                else if(type==CFTypes.TYPE_DATETIME) {
363                        List<DateTime> list=new ArrayList<DateTime>();
364                        DateTime dt;
365                        for(int i=0;i<res.length;i++){
366                                dt=Caster.toDate(res[i],true,null,null);
367                                if(dt!=null)list.add(dt);
368                        }
369                        values=list.toArray(new DateTime[list.size()]);
370                }
371                // Timespan
372                else if(type==CFTypes.TYPE_TIMESPAN) {
373                        List<TimeSpan> list=new ArrayList<TimeSpan>();
374                        TimeSpan ts;
375                        for(int i=0;i<res.length;i++){
376                                ts=Caster.toTimespan(res[i],null);
377                                if(ts!=null)list.add(ts);
378                        }
379                        values=list.toArray(new TimeSpan[list.size()]);
380                }
381                
382                // TODO add support for other types ?
383                else {
384                        valueList=null; 
385                }
386                return values;
387                
388        }
389        
390
391
392}