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.List;
023import java.util.ListIterator;
024import java.util.Map;
025
026import lucee.runtime.Component;
027import lucee.runtime.component.Property;
028import lucee.runtime.exp.ApplicationException;
029import lucee.runtime.exp.PageException;
030import lucee.runtime.op.Caster;
031import lucee.runtime.op.Decision;
032import lucee.runtime.type.Query;
033import lucee.runtime.type.QueryColumn;
034import lucee.runtime.type.QueryImpl;
035import lucee.runtime.type.UDF;
036
037import org.openamf.AMFBody;
038import org.openamf.config.OpenAMFConfig;
039import org.openamf.config.PageableRecordsetConfig;
040import org.openamf.recordset.ASRecordSet;
041
042import flashgateway.io.ASObject;
043
044/**
045 * Cast a CFML object to AMF Objects and the other way
046 */
047public final class OpenAMFCaster implements AMFCaster {
048        
049        
050        private static OpenAMFCaster singelton;
051
052
053        private OpenAMFCaster(){}
054        
055        public static AMFCaster getInstance(){
056                if(singelton==null){
057                        singelton= new OpenAMFCaster();
058                }
059                return singelton;
060        }
061    
062    /**
063     * cast cfml Object to AMF Object
064     * @param o
065     * @return
066     * @throws PageException
067     */
068    public Object toAMFObject(Object o) throws PageException {
069        if(o instanceof ASObject) return o;
070        if(Decision.isBinary(o))    return o;
071        if(Decision.isArray(o))     return toAMFObject(Caster.toList(o,true));
072        if(Decision.isBoolean(o))   return Caster.toBoolean(o);
073        if(Decision.isComponent(o)) return toAMFObject((Component)o);
074        if(Decision.isDateSimple(o,false))return Caster.toDate(o,null);
075        if(Decision.isNumeric(o))   return Caster.toDouble(o);
076        if(Decision.isQuery(o))     return toAMFObject(Caster.toQuery(o));
077        if(Decision.isStruct(o))    return toAMFObject(Caster.toMap(o,true));
078        if(Decision.isUserDefinedFunction(o))
079                                    return toAMFObject((UDF)o);
080        
081        
082        
083        return o;
084    }
085    
086    
087    private List toAMFObject(List list) throws PageException {
088        ListIterator it = list.listIterator();
089        while(it.hasNext()) {
090            list.set(it.nextIndex(),toAMFObject(it.next()));
091        }
092        return list;
093    }
094    private Map toAMFObject(Map map) throws PageException {
095        Object[] keys = map.keySet().toArray();
096        for(int i=0;i<keys.length;i++) {
097            Object key=keys[i];
098            Object value=map.get(key);
099            if(key instanceof String) {
100                map.remove(key);
101                key=((String)key).toUpperCase();
102            }
103            map.put(key,toAMFObject(value));
104        }
105        return map;
106    }
107    private Object toAMFObject(Component c) throws PageException {
108        ASObject aso = new ASObject();
109        Property[] prop = c.getProperties(false);
110        if(prop!=null)for(int i=0;i<prop.length;i++) {
111                aso.put(prop[i].getName().toUpperCase(), toAMFObject(c.get(prop[i].getName(),null)));
112        }
113        
114        return aso;
115    }
116    private ASRecordSet toAMFObject(Query qry) throws PageException {
117        PageableRecordsetConfig prsc = new PageableRecordsetConfig();
118        prsc.setInitialDataRowCount(qry.getRowCount());
119        OpenAMFConfig.getInstance().setPageableRecordsetConfig(prsc);
120        
121        
122        ASRecordSet rs=new ASRecordSet();
123        
124        String[] keys=qry.getColumns();
125        QueryColumn[] columns=new QueryColumn[keys.length];
126        for(int i=0;i<columns.length;i++) {
127            columns[i]=qry.getColumn(keys[i]);
128            //rows.add(i,new ArrayList());
129        }
130        
131        
132        int iCol;
133        ArrayList rows = new ArrayList();
134        ArrayList row;
135        int rowCount=qry.getRecordcount();
136        for(int iRow=1;iRow<=rowCount;iRow++) {
137            rows.add(row=new ArrayList());
138            for(iCol=0;iCol<columns.length;iCol++) {
139                row.add(toAMFObject(columns[iCol].get(iRow,null)));
140            }
141        }
142        
143        
144        rs.populate(qry.getColumns(),rows);
145        
146        return rs;
147    }
148    private static Object toAMFObject(UDF udf) throws PageException {
149        throw new ApplicationException("can't send a User Defined Function ("+udf.getFunctionName()+") via flash remoting");
150    }
151    
152    public Object toCFMLObject(Object amf) throws PageException {
153        if(amf instanceof List) return toCFMLObject((List)amf);
154        if(amf instanceof Map) return toCFMLObject((Map)amf);
155        if(amf instanceof ASRecordSet) return toCFMLObject((ASRecordSet)amf);
156        
157        return amf;
158    }
159    private List toCFMLObject(List list) throws PageException {
160        ListIterator it = list.listIterator();
161        while(it.hasNext()) {
162            list.set(it.nextIndex(),toCFMLObject(it.next()));
163        }
164        return list;
165    }
166    
167    private Map toCFMLObject(Map map) throws PageException {
168        Object[] keys = map.keySet().toArray();
169        for(int i=0;i<keys.length;i++) {
170            Object key=keys[i];
171            map.put(key,toCFMLObject(map.get(key)));
172        }
173        return map;
174    }
175    
176    private Query toCFMLObject(ASRecordSet rs) throws PageException {
177        String[] columns = rs.getColumnNames();
178        List rows = rs.rows();
179        int len=0;
180        if(rows.size()>0) len=((List)rows.get(0)).size();
181        Query qry=new QueryImpl(columns,len,"query");
182        
183        List row;
184        for(int iCol=0;iCol<columns.length;iCol++) {
185            row=(List) rows.get(iCol);
186            QueryColumn column = qry.getColumn(columns[iCol]);
187            
188            for(int iRow=0;iRow<row.size();iRow++) {
189                column.set(iRow+1,toCFMLObject(row.get(iRow)));
190            }
191        }
192        return qry;
193    }
194    
195    
196    /**
197     * translate a AMFBody type to a String type
198     * @param type
199     * @return string type
200     */
201    public static String toStringType(byte type) {
202        switch(type) {
203        case AMFBody.DATA_TYPE_ARRAY: return "array";
204        case AMFBody.DATA_TYPE_AS_OBJECT: return "as object";
205        case AMFBody.DATA_TYPE_BOOLEAN: return "boolean";
206        case AMFBody.DATA_TYPE_CUSTOM_CLASS: return "custom";
207        case AMFBody.DATA_TYPE_DATE: return "date";
208        case AMFBody.DATA_TYPE_LONG_STRING: return "long string";
209        case AMFBody.DATA_TYPE_MIXED_ARRAY: return "mixed array";
210        case AMFBody.DATA_TYPE_MOVIE_CLIP: return "movie";
211        case AMFBody.DATA_TYPE_NULL: return "null";
212        case AMFBody.DATA_TYPE_NUMBER: return "number";
213        case AMFBody.DATA_TYPE_OBJECT: return "object";
214        case AMFBody.DATA_TYPE_OBJECT_END: return "object end";
215        case AMFBody.DATA_TYPE_RECORDSET: return "recordset";
216        case AMFBody.DATA_TYPE_REFERENCE_OBJECT: return "ref object";
217        case AMFBody.DATA_TYPE_STRING: return "string";
218        case AMFBody.DATA_TYPE_UNDEFINED: return "undefined";
219        case AMFBody.DATA_TYPE_UNKNOWN: return "unknow";
220        case AMFBody.DATA_TYPE_XML: return "xml";
221        }
222
223        return "";
224    }
225
226        public void init(Map arguments) {
227                // TODO Auto-generated method stub
228                
229        }
230}