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.net.amf; 020 021import java.util.ArrayList; 022import java.util.Date; 023import java.util.Iterator; 024import java.util.List; 025import java.util.ListIterator; 026import java.util.Map; 027import java.util.Map.Entry; 028 029import lucee.commons.lang.CFTypes; 030import lucee.commons.lang.ExceptionUtil; 031import lucee.commons.lang.StringUtil; 032import lucee.runtime.Component; 033import lucee.runtime.ComponentSpecificAccess; 034import lucee.runtime.Page; 035import lucee.runtime.PageContext; 036import lucee.runtime.PageContextImpl; 037import lucee.runtime.PageSourceImpl; 038import lucee.runtime.component.ComponentLoader; 039import lucee.runtime.component.Property; 040import lucee.runtime.config.ConfigWeb; 041import lucee.runtime.engine.ThreadLocalPageContext; 042import lucee.runtime.exp.ApplicationException; 043import lucee.runtime.exp.PageException; 044import lucee.runtime.img.Image; 045import lucee.runtime.op.Caster; 046import lucee.runtime.op.Decision; 047import lucee.runtime.op.Duplicator; 048import lucee.runtime.text.xml.XMLCaster; 049import lucee.runtime.type.Array; 050import lucee.runtime.type.ArrayImpl; 051import lucee.runtime.type.Collection; 052import lucee.runtime.type.Collection.Key; 053import lucee.runtime.type.KeyImpl; 054import lucee.runtime.type.Query; 055import lucee.runtime.type.Struct; 056import lucee.runtime.type.StructImpl; 057import lucee.runtime.type.UDF; 058import lucee.runtime.type.dt.DateTimeImpl; 059import lucee.runtime.type.util.ArrayUtil; 060import lucee.runtime.type.util.CollectionUtil; 061import lucee.runtime.type.wrap.ArrayAsList; 062import lucee.runtime.type.wrap.ListAsArray; 063import lucee.runtime.type.wrap.MapAsStruct; 064 065import org.w3c.dom.Node; 066 067import flex.messaging.io.amf.ASObject; 068 069 070/** 071 * Cast a CFML object to AMF Objects and the other way 072 */ 073public class ClassicAMFCaster implements AMFCaster { 074 075 076 077 private static final Collection.Key REMOTING_FETCH = KeyImpl.intern("remotingFetch"); 078 079 //private static ClassicAMFCaster singelton; 080 081 protected boolean forceCFCLower; 082 protected boolean forceStructLower; 083 protected boolean forceQueryLower; 084 085 private int methodAccessLevel; 086 087 @Override 088 public void init(Map arguments){ 089 forceCFCLower=Caster.toBooleanValue(arguments.get("force-cfc-lowercase"),false); 090 forceQueryLower=Caster.toBooleanValue(arguments.get("force-query-lowercase"),false); 091 forceStructLower=Caster.toBooleanValue(arguments.get("force-struct-lowercase"),false); 092 // method access level 093 String str=Caster.toString(arguments.get("method-access-level"),"remote"); 094 if("private".equalsIgnoreCase(str))methodAccessLevel=Component.ACCESS_PRIVATE; 095 else if("package".equalsIgnoreCase(str))methodAccessLevel=Component.ACCESS_PACKAGE; 096 else if("public".equalsIgnoreCase(str))methodAccessLevel=Component.ACCESS_PUBLIC; 097 else methodAccessLevel=Component.ACCESS_REMOTE; 098 099 } 100 101 102 @Override 103 public Object toAMFObject(Object cf) throws PageException { 104 if(cf instanceof Node) return toAMFObject((Node)cf); 105 if(cf instanceof List) return toAMFObject((List)cf); 106 if(cf instanceof Array) return toAMFObject(ArrayAsList.toList((Array)cf)); 107 if(cf instanceof Component) return toAMFObject((Component)cf); 108 if(cf instanceof Query) return toAMFObject((Query)cf); 109 if(cf instanceof Image) return toAMFObject((Image)cf); 110 if(cf instanceof Map) return toAMFObject((Map)cf); 111 if(cf instanceof Object[]) return toAMFObject((Object[])cf); 112 113 return cf; 114 } 115 116 protected Object toAMFObject(Node node) { 117 return XMLCaster.toRawNode(node); 118 } 119 protected Object toAMFObject(Query query) throws PageException { 120 List<ASObject> result = new ArrayList<ASObject>(); 121 int len=query.getRecordcount(); 122 Collection.Key[] columns=CollectionUtil.keys(query); 123 ASObject row; 124 for(int r=1;r<=len;r++) { 125 result.add(row = new ASObject()); 126 for(int c=0;c<columns.length;c++) { 127 row.put(toString(columns[c],forceQueryLower), toAMFObject(query.getAt(columns[c],r)) ); 128 } 129 } 130 return result; 131 } 132 133 protected Object toAMFObject(Image img) throws PageException { 134 try{ 135 return img.getImageBytes(null); 136 } 137 catch(Throwable t){ 138 ExceptionUtil.rethrowIfNecessary(t); 139 return img.getImageBytes("png"); 140 } 141 } 142 143 protected ASObject toAMFObject(Component cfc) throws PageException { 144 ASObject aso = new ASObject(); 145 aso.setType(cfc.getCallName()); 146 147 148 Component c=ComponentSpecificAccess.toComponentSpecificAccess(methodAccessLevel,cfc); 149 150 Property[] prop = cfc.getProperties(false); 151 Object v; UDF udf; 152 if(prop!=null)for(int i=0;i<prop.length;i++) { 153 boolean remotingFetch = Caster.toBooleanValue(prop[i].getDynamicAttributes().get(REMOTING_FETCH,Boolean.TRUE),true); 154 if(!remotingFetch) continue; 155 156 v=cfc.get(prop[i].getName(),null); 157 if(v==null){ 158 v=c.get("get"+prop[i].getName(),null); 159 if(v instanceof UDF){ 160 udf=(UDF) v; 161 if(udf.getReturnType()==CFTypes.TYPE_VOID) continue; 162 if(udf.getFunctionArguments().length>0) continue; 163 164 try { 165 v=c.call(ThreadLocalPageContext.get(), udf.getFunctionName(), ArrayUtil.OBJECT_EMPTY); 166 } catch (PageException e) { 167 continue; 168 } 169 } 170 } 171 172 aso.put(toString(prop[i].getName(),forceCFCLower), toAMFObject(v)); 173 } 174 return aso; 175 } 176 177 protected Object toAMFObject(Map map) throws PageException { 178 if(forceStructLower && map instanceof Struct) toAMFObject((Struct)map); 179 180 map=(Map) Duplicator.duplicate(map,false); 181 Iterator it = map.entrySet().iterator(); 182 Map.Entry entry; 183 while(it.hasNext()) { 184 entry=(Entry) it.next(); 185 entry.setValue(toAMFObject(entry.getValue())); 186 } 187 return MapAsStruct.toStruct(map, false); 188 } 189 190 protected Object toAMFObject(Struct src) throws PageException { 191 Struct trg=new StructImpl(); 192 //Key[] keys = src.keys(); 193 Iterator<Entry<Key, Object>> it = src.entryIterator(); 194 Entry<Key, Object> e; 195 while(it.hasNext()) { 196 e = it.next(); 197 trg.set(KeyImpl.init(toString(e.getKey(),forceStructLower)), toAMFObject(e.getValue())); 198 } 199 return trg; 200 } 201 202 203 204 protected Object toAMFObject(List list) throws PageException { 205 Object[] trg=new Object[list.size()]; 206 ListIterator it = list.listIterator(); 207 208 while(it.hasNext()) { 209 trg[it.nextIndex()]=toAMFObject(it.next()); 210 } 211 return trg; 212 } 213 214 protected Object toAMFObject(Object[] src) throws PageException { 215 Object[] trg=new Object[src.length]; 216 for(int i=0;i<src.length;i++){ 217 trg[i]=toAMFObject(src[i]); 218 } 219 return trg; 220 } 221 222 223 @Override 224 public Object toCFMLObject(Object amf) throws PageException { 225 if(amf instanceof Node) return toCFMLObject((Node)amf); 226 if(amf instanceof List) return toCFMLObject((List)amf); 227 if(Decision.isNativeArray(amf)) { 228 if(amf instanceof byte[]) return amf; 229 if(amf instanceof char[]) return new String((char[])amf); 230 return toCFMLObject(Caster.toNativeArray(amf)); 231 } 232 if(amf instanceof ASObject) return toCFMLObject((ASObject)amf); 233 if(amf instanceof Map) return toCFMLObject((Map)amf); 234 if(amf instanceof Date) return new DateTimeImpl((Date)amf); 235 if(amf == null) return ""; 236 237 return amf; 238 } 239 240 protected Object toCFMLObject(Node node) { 241 return XMLCaster.toXMLStruct(node, true); 242 } 243 protected Object toCFMLObject(Object[] arr) throws PageException { 244 Array trg=new ArrayImpl(); 245 for(int i=0;i<arr.length;i++){ 246 trg.setEL(i+1, toCFMLObject(arr[i])); 247 } 248 return trg; 249 } 250 251 protected Object toCFMLObject(List list) throws PageException { 252 ListIterator it = list.listIterator(); 253 while(it.hasNext()) { 254 //arr.setE(it.nextIndex()+1, toCFMLObject(it.next())); 255 list.set(it.nextIndex(),toCFMLObject(it.next())); 256 } 257 return ListAsArray.toArray(list); 258 } 259 260 protected Object toCFMLObject(Map map) throws PageException { 261 Iterator it = map.entrySet().iterator(); 262 Map.Entry entry; 263 while(it.hasNext()) { 264 entry=(Entry) it.next(); 265 entry.setValue(toCFMLObject(entry.getValue())); 266 } 267 return MapAsStruct.toStruct(map, false); 268 } 269 270 protected Object toCFMLObject(ASObject aso) throws PageException { 271 if(!StringUtil.isEmpty(aso.getType())){ 272 PageContext pc = ThreadLocalPageContext.get(); 273 ConfigWeb config = pc.getConfig(); 274 275 String name="/"+aso.getType().replace('.', '/')+".cfc"; 276 277 Page p = PageSourceImpl.loadPage(pc, ((PageContextImpl)pc).getPageSources(name), null) ; 278 279 if(p==null)throw new ApplicationException("Could not find a Component with name ["+aso.getType()+"]"); 280 281 Component cfc = ComponentLoader.loadComponent(pc, p, p.getPageSource(), aso.getType(), false); 282 ComponentSpecificAccess cw=ComponentSpecificAccess.toComponentSpecificAccess(config.getComponentDataMemberDefaultAccess(),cfc); 283 284 Iterator it = aso.entrySet().iterator(); 285 Map.Entry entry; 286 while(it.hasNext()){ 287 entry = (Entry) it.next(); 288 cw.set(KeyImpl.toKey(entry.getKey()), toCFMLObject(entry.getValue())); 289 } 290 return cfc; 291 292 293 } 294 return toCFMLObject((Map)aso); 295 } 296 297 protected String toString(Object key, boolean forceLower) { 298 if(key instanceof Key) return toString((Key)key, forceLower); 299 return toString(Caster.toString(key,""), forceLower); 300 } 301 302 protected String toString(Key key, boolean forceLower) { 303 if(forceLower) return key.getLowerString(); 304 return key.getString(); 305 } 306 307 protected String toString(String key, boolean forceLower) { 308 if(forceLower) return key.toLowerCase(); 309 return key; 310 } 311}