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}