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}