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.runtime.tag;
020
021import java.util.HashMap;
022import java.util.Iterator;
023import java.util.Map;
024import java.util.Map.Entry;
025
026import javax.servlet.http.Cookie;
027import javax.servlet.jsp.tagext.Tag;
028
029import lucee.commons.io.DevNullOutputStream;
030import lucee.commons.lang.ExceptionUtil;
031import lucee.commons.lang.Pair;
032import lucee.commons.lang.StringUtil;
033import lucee.runtime.Component;
034import lucee.runtime.ComponentImpl;
035import lucee.runtime.ComponentSpecificAccess;
036import lucee.runtime.Page;
037import lucee.runtime.PageContext;
038import lucee.runtime.PageContextImpl;
039import lucee.runtime.PageSource;
040import lucee.runtime.component.ComponentLoader;
041import lucee.runtime.config.ConfigWeb;
042import lucee.runtime.config.ConfigWebImpl;
043import lucee.runtime.engine.ThreadLocalPageContext;
044import lucee.runtime.exp.ApplicationException;
045import lucee.runtime.exp.PageException;
046import lucee.runtime.ext.tag.DynamicAttributes;
047import lucee.runtime.op.Caster;
048import lucee.runtime.reflection.Reflector;
049import lucee.runtime.reflection.pairs.MethodInstance;
050import lucee.runtime.thread.ThreadUtil;
051import lucee.runtime.type.Collection;
052import lucee.runtime.type.Collection.Key;
053import lucee.runtime.type.KeyImpl;
054import lucee.runtime.type.Struct;
055import lucee.runtime.type.StructImpl;
056import lucee.runtime.type.util.ArrayUtil;
057import lucee.runtime.type.util.KeyConstants;
058import lucee.transformer.library.tag.TagLib;
059import lucee.transformer.library.tag.TagLibTag;
060import lucee.transformer.library.tag.TagLibTagAttr;
061
062public class TagUtil {
063        
064        public static final short ORIGINAL_CASE = 0;
065        public static final short UPPER_CASE = 1;
066        public static final short LOWER_CASE = 2;
067
068        //private static final String "invalid call of the function ["+tlt.getName()+", you can not mix named on regular arguments]" = "invalid argument for function, only named arguments are allowed like struct(name:\"value\",name2:\"value2\")";
069
070        public static void setAttributeCollection(PageContext pc,Tag tag, MissingAttribute[] missingAttrs, Struct _attrs, int attrType) throws PageException {
071                // check missing tags
072                Map<Key, Object> att=new HashMap<Key, Object>();
073                {
074                        Iterator<Entry<Key, Object>> it = _attrs.entryIterator();
075                        Entry<Key, Object> e;
076                        while(it.hasNext()){
077                                e = it.next();
078                                att.put(e.getKey(), e.getValue());
079                        }
080                }
081                
082                if(!ArrayUtil.isEmpty(missingAttrs)){
083                        Key k;
084                        Object value;
085                        MissingAttribute miss;
086                        for(int i=0;i<missingAttrs.length;i++) {
087                                miss = missingAttrs[i];
088                                value=att.get(miss.getName());
089                                // check alias
090                                if(value==null && !ArrayUtil.isEmpty(miss.getAlias())) {
091                                        String[] alias = miss.getAlias();
092                                        for(int y=0;y<alias.length;y++){
093                                                value=att.get(k=KeyImpl.init(alias[y]));
094                                                if(value!=null) {
095                                                        att.remove(k);
096                                                        break;
097                                                }
098                                        }
099                                }
100                                
101                                
102                                if(value==null)
103                                        throw new ApplicationException("attribute "+missingAttrs[i].getName().getString()+" is required but missing");
104                                        //throw new ApplicationException("attribute "+missingAttrs[i].getName().getString()+" is required for tag "+tag.getFullName());
105                                att.put(
106                                                missingAttrs[i].getName(), 
107                                                Caster.castTo(pc, missingAttrs[i].getType(), value, false));
108                        }
109                }
110                setAttributes(pc,tag,att,attrType);
111        }
112        
113
114        public static void setAttributes(PageContext pc,Tag tag, Map<Key, Object> att, int attrType) throws PageException {
115                Iterator<Entry<Key, Object>> it;
116                Entry<Key, Object> e;
117                //TagLibTag tlt=null;
118                if(TagLibTag.ATTRIBUTE_TYPE_DYNAMIC==attrType) {
119                        DynamicAttributes da=(DynamicAttributes) tag;
120                        it = att.entrySet().iterator();
121                        while(it.hasNext()) {
122                                e = it.next();
123                                da.setDynamicAttribute(null, e.getKey(),e.getValue());
124                        }
125                }
126                else if(TagLibTag.ATTRIBUTE_TYPE_FIXED==attrType) {
127                        Object value;
128                        it = att.entrySet().iterator();
129                        MethodInstance setter;
130                        while(it.hasNext()) {
131                                e = it.next();
132                                value=e.getValue();
133                                if(value!=null){
134                                        setter = Reflector.getSetter(tag, e.getKey().getLowerString(),value,null);
135                                        //if(tlt==null) tlt=getTLT(pc.getConfig(), tag);
136                                        //setter=getSetter(pc,tlt,tag,e.getKey().getLowerString(),value);
137                                        if(setter!=null) {
138                                                try {
139                                                        setter.invoke(tag);
140                                                } 
141                                                catch (Exception _e) {
142                                                        throw Caster.toPageException(_e);
143                                                }
144                                        }
145                                }
146                                //}catch(PageException pe){}
147                        }       
148                }
149                else if(TagLibTag.ATTRIBUTE_TYPE_MIXED==attrType) {
150                        MethodInstance setter;
151                        it = att.entrySet().iterator();
152                        while(it.hasNext()) {
153                                e = it.next();
154                                setter = Reflector.getSetter(tag, e.getKey().getLowerString(),e.getValue(),null);
155                                //if(tlt==null) tlt=getTLT(pc.getConfig(), tag);
156                                //setter=getSetter(pc,tlt,tag,e.getKey().getLowerString(),e.getValue());
157                                if(setter!=null) {
158                                        try {
159                                                setter.invoke(tag);
160                                        } 
161                                        catch (Exception _e) {
162                                                throw Caster.toPageException(_e);
163                                        }
164                                }
165                                else {
166                                        DynamicAttributes da=(DynamicAttributes) tag;
167                                        da.setDynamicAttribute(null, e.getKey(),e.getValue());
168                                }
169                        }
170                }
171        }
172
173        /*private static MethodInstance getSetter(PageContext pc,TagLibTag tlt, Tag tag, String name, Object value) throws PageException { 
174                MethodInstance setter = Reflector.getSetter(tag, name,value,null);
175                if(setter==null && tlt!=null) {
176                        TagLibTagAttr attr = tlt.getAttribute(name);
177                        if(attr==null)
178                                throw new TemplateException(
179                                        "Attribute "+name+" is not allowed for tag "+tlt.getFullName(),
180                                        "valid attribute names are ["+tlt.getAttributeNames()+"]");
181                        value=Caster.castTo(pc, attr.getType(), value, false);
182                        setter = Reflector.getSetter(tag, name,value,null);
183                }
184                return setter;
185        }*/
186
187        private static TagLibTag getTLT(ConfigWeb config, Tag tag) {
188                TagLib[] tlds = ((ConfigWebImpl)config).getTLDs();
189                TagLibTag tlt;
190                for(int i=0;i<tlds.length;i++){
191                        tlt = tlds[i].getTag(tag.getClass());
192                        if(tlt!=null) return tlt;
193                }
194                return null;
195        }
196
197        public static void setDynamicAttribute(StructImpl attributes,Collection.Key name, Object value, short caseType) {
198                if(name.equalsIgnoreCase(KeyConstants._attributecollection)) {
199            if(value instanceof lucee.runtime.type.Collection) {
200                lucee.runtime.type.Collection coll=(lucee.runtime.type.Collection)value;
201                Iterator<Entry<Key, Object>> it = coll.entryIterator();
202                Entry<Key, Object> e;
203                while(it.hasNext()) {
204                        e = it.next();
205                    if(attributes.get(e.getKey(),null)==null)
206                        attributes.setEL(e.getKey(),e.getValue());
207                }
208                return;
209            }
210            else if(value instanceof Map) {
211                
212                Map map=(Map) value;
213                            Iterator it = map.entrySet().iterator();
214                            Map.Entry entry;
215                            Key key;
216                            while(it.hasNext()) {
217                                entry=(Entry) it.next();
218                                key = Caster.toKey(entry.getKey(),null);
219                                if(!attributes.containsKey(key)){
220                                        attributes.setEL(key,entry.getValue());
221                        }
222                    }
223                return;
224            }
225        }
226                if(LOWER_CASE==caseType)name=KeyImpl.init(name.getLowerString());
227                else if(UPPER_CASE==caseType)name=KeyImpl.init(name.getUpperString());
228        attributes.setEL(name, value);
229        }
230
231        /**
232     * load metadata from cfc based custom tags and add the info to the tag
233     * @param cs
234     * @param config
235     */
236    public static void addTagMetaData(ConfigWebImpl cw) {
237        if(true) return;
238        
239        PageContextImpl pc=null;
240        try{
241                pc = ThreadUtil.createPageContext(cw, DevNullOutputStream.DEV_NULL_OUTPUT_STREAM, 
242                                "localhost", "/","", new Cookie[0], new Pair[0], new Pair[0], new StructImpl());
243                
244        }
245        catch(Throwable t){
246                        ExceptionUtil.rethrowIfNecessary(t);
247                return;
248        }
249        PageContext orgPC = ThreadLocalPageContext.get();
250        ThreadLocalPageContext.register(pc);
251        try{
252                TagLibTagAttr attrFileName,attrIsWeb;
253                String filename;
254                Boolean isWeb;
255                TagLibTag tlt;
256                
257                TagLib[] tlds = cw.getTLDs();
258                for(int i=0;i<tlds.length;i++){
259                        Map<String, TagLibTag> tags = tlds[i].getTags();
260                        Iterator<TagLibTag> it = tags.values().iterator();
261                        while(it.hasNext()){
262                                tlt = it.next();
263                                if("lucee.runtime.tag.CFTagCore".equals(tlt.getTagClassName())) {
264                                        attrFileName = tlt.getAttribute("__filename");
265                                        attrIsWeb = tlt.getAttribute("__isweb");
266                                        if(attrFileName!=null && attrIsWeb!=null) {
267                                                filename = Caster.toString(attrFileName.getDefaultValue(),null);
268                                                isWeb=Caster.toBoolean(attrIsWeb.getDefaultValue(),null);
269                                                if(filename!=null && isWeb!=null) {
270                                                        addTagMetaData(pc, tlds[i], tlt, filename,isWeb.booleanValue());
271                                                }
272                                        }
273                                }
274                        }
275                }
276        }
277        catch(Throwable t){
278                        ExceptionUtil.rethrowIfNecessary(t);
279                //t.printStackTrace();
280        }
281        finally{
282                pc.release();
283                ThreadLocalPageContext.register(orgPC);
284        }
285        }
286
287        private static void addTagMetaData(PageContext pc,TagLib tl, TagLibTag tlt, String filename, boolean isWeb) {
288        if(pc==null) return;
289                try{
290                        ConfigWebImpl config=(ConfigWebImpl) pc.getConfig();
291                        PageSource ps = isWeb?
292                                        config.getTagMapping().getPageSource(filename):
293                                        config.getServerTagMapping().getPageSource(filename);
294                        
295                        Page p = ps.loadPage(pc);
296                        ComponentImpl c = ComponentLoader.loadComponent(pc, p, ps, filename, true,true);
297                        ComponentSpecificAccess cw = ComponentSpecificAccess.toComponentSpecificAccess(Component.ACCESS_PRIVATE,c);
298                        Struct meta = Caster.toStruct( cw.get(KeyConstants._metadata,null),null);
299
300                        // TODO handle all metadata here and make checking at runtime useless
301                        if(meta!=null) {
302                                
303                                // parse body
304                                boolean rtexprvalue=Caster.toBooleanValue(meta.get(KeyConstants._parsebody,Boolean.FALSE),false);
305                        tlt.setParseBody(rtexprvalue);
306                        
307                        // hint
308                        String hint=Caster.toString(meta.get(KeyConstants._hint,null),null);
309                        if(!StringUtil.isEmpty(hint))tlt.setDescription(hint);
310                        
311                        }
312                        
313                } 
314                catch (Throwable t) {
315                        ExceptionUtil.rethrowIfNecessary(t);
316                        //t.printStackTrace();
317                }
318    }
319        
320}