001    
002    package railo.runtime.converter;
003    
004    import java.util.Calendar;
005    import java.util.HashSet;
006    import java.util.Iterator;
007    import java.util.List;
008    import java.util.ListIterator;
009    import java.util.Map;
010    import java.util.Set;
011    
012    import railo.commons.date.JREDateTimeUtil;
013    import railo.commons.lang.StringUtil;
014    import railo.runtime.engine.ThreadLocalPageContext;
015    import railo.runtime.exp.PageException;
016    import railo.runtime.op.Caster;
017    import railo.runtime.op.Decision;
018    import railo.runtime.type.Array;
019    import railo.runtime.type.Collection.Key;
020    import railo.runtime.type.Query;
021    import railo.runtime.type.Struct;
022    import railo.runtime.type.dt.DateTime;
023    
024    
025    /**
026     * class to serialize to Convert Cold Fusion Objects (query,array,struct usw) to a JavaScript representation
027     */
028    public final class JSConverter {
029    
030            private static final String NULL = "null";
031            private boolean useShortcuts=false;
032            private boolean useWDDX=true;
033    
034            /**
035             * serialize a Cold Fusion object to a JavaScript Object
036             * @param object object to serialize
037             * @param clientVariableName name of the variable to create 
038             * @return vonverte Javascript Code as String
039             * @throws ConverterException
040             */
041            public String serialize(Object object, String clientVariableName) throws ConverterException {
042                    StringBuffer sb=new StringBuffer();
043                    _serialize(clientVariableName,object,sb,new HashSet<Object>());
044                    String str = sb.toString().trim();
045                    return clientVariableName+"="+str+(StringUtil.endsWith(str, ';')?"":";");
046                    //return sb.toString();
047            }
048            
049            private void _serialize(String name,Object object,StringBuffer sb,Set<Object> done) throws ConverterException {
050                    // NULL
051                    if(object==null) {
052                            sb.append(goIn());
053                            sb.append(NULL+";");
054                            return;
055                    }
056                    // String
057                    if(object instanceof String || object instanceof StringBuffer) {
058                            sb.append(goIn());
059                            sb.append("\"");
060                            sb.append(StringUtil.escapeJS(object.toString()));
061                            sb.append("\";");
062                            return;
063                    }
064                    // Number
065                    if(object instanceof Number) {
066                            sb.append(goIn());
067                            sb.append("\"");
068                            sb.append(Caster.toString(((Number)object).doubleValue()));
069                            sb.append("\";");
070                            return;
071                    }
072                    // Date
073                    if(Decision.isDateSimple(object,false)) {
074                            _serializeDateTime(Caster.toDate(object,false,null,null),sb);
075                            return;
076                    }
077                    // Boolean
078                    if(object instanceof Boolean) {
079                            sb.append(goIn());
080                            sb.append("\"");
081                            sb.append((((Boolean)object).booleanValue()?"true":"false"));
082                            sb.append("\";");
083                            return;
084                    }
085                    
086                    Object raw = LazyConverter.toRaw(object);
087                    if(done.contains(raw)){
088                            sb.append(NULL+";");
089                            return;
090                    }
091                    done.add(raw);
092                    try {
093                            // Struct
094                            if(object instanceof Struct) {
095                                    _serializeStruct(name,(Struct)object,sb,done);
096                                    return;
097                            }
098                            // Map
099                            if(object instanceof Map) {
100                                    _serializeMap(name,(Map)object,sb,done);
101                                    return;
102                            }
103                            // List
104                            if(object instanceof List) {
105                                    _serializeList(name,(List)object,sb,done);
106                                    return;
107                            }
108                            // Array
109                            if(Decision.isArray(object)) {
110                                    _serializeArray(name,Caster.toArray(object,null),sb,done);
111                                    return;
112                            }
113                            // Query
114                            if(object instanceof Query) {
115                                    _serializeQuery(name,(Query)object,sb,done);
116                                    return;
117                            }
118                    }
119                    finally {
120                            done.remove(raw);
121                    }
122                    
123                    throw new ConverterException("can't serialize Object of type ["+Caster.toClassName(object)+"] to a js representation");
124                    //deep--;
125                    //return rtn;
126            }
127            
128    
129            
130            /**
131             * serialize a Array
132             * @param name 
133             * @param array Array to serialize
134             * @param sb 
135             * @param done 
136             * @return serialized array
137             * @throws ConverterException
138             */
139            private void _serializeArray(String name, Array array, StringBuffer sb, Set<Object> done) throws ConverterException {
140                    _serializeList(name,array.toList(),sb,done);
141            }
142            
143            /**
144             * serialize a List (as Array)
145             * @param name 
146             * @param list List to serialize
147             * @param sb 
148             * @param done 
149             * @return serialized list
150             * @throws ConverterException
151             */
152            private void _serializeList(String name, List list, StringBuffer sb, Set<Object> done) throws ConverterException {
153                    
154                    
155                    if(useShortcuts)sb.append("[];");
156                    else sb.append("new Array();");
157                    
158                    ListIterator it=list.listIterator();
159                    int index=-1;
160                    while(it.hasNext()) {
161                            //if(index!=-1)sb.append(",");
162                            index = it.nextIndex();
163                            sb.append(name+"["+index+"]=");
164                            _serialize(name+"["+index+"]",it.next(),sb,done);
165                            //sb.append(";");
166                    }
167            }
168    
169            /**
170             * serialize a Struct
171             * @param name 
172             * @param struct Struct to serialize
173             * @param done 
174             * @param sb2 
175             * @return serialized struct
176             * @throws ConverterException
177             */
178            private String _serializeStruct(String name, Struct struct, StringBuffer sb, Set<Object> done) throws ConverterException {
179                    if(useShortcuts)sb.append("{};");
180                    else sb.append("new Object();");
181                    
182                    Key[] keys = struct.keys();
183                    for(int i=0;i<keys.length;i++) {
184                            // lower case ist ok!
185                            String key=StringUtil.escapeJS(Caster.toString(keys[i].getLowerString(),""));
186                sb.append(name+"[\""+key+"\"]=");
187                            try {
188                                    _serialize(name+"[\""+key+"\"]",struct.get(keys[i]),sb,done);
189                            } 
190                            catch (PageException e) {
191                                    _serialize(name+"[\""+key+"\"]",e.getMessage(),sb,done);
192                            }
193                    }
194            return sb.toString();
195            }
196    
197            /**
198             * serialize a Map (as Struct)
199             * @param name 
200             * @param map Map to serialize
201             * @param done 
202             * @param sb2 
203             * @return serialized map
204             * @throws ConverterException
205             */
206            private String _serializeMap(String name, Map map, StringBuffer sb, Set<Object> done) throws ConverterException {
207    
208                    if(useShortcuts)sb.append("{}");
209                    else sb.append("new Object();");
210                    Iterator it=map.keySet().iterator();
211                    while(it.hasNext()) {
212                            Object key=it.next();
213                            String skey=StringUtil.toLowerCase(StringUtil.escapeJS(key.toString()));
214                sb.append(name+"[\""+skey+"\"]=");
215                            _serialize(name+"[\""+skey+"\"]",map.get(key),sb,done);
216                            //sb.append(";");
217                    }
218                    return sb.toString();
219            }
220            
221            /**
222             * serialize a Query
223             * @param query Query to serialize
224             * @param done 
225             * @return serialized query
226             * @throws ConverterException
227             */
228            private void _serializeQuery(String name,Query query,StringBuffer sb, Set<Object> done) throws ConverterException {
229                    if(useWDDX)_serializeWDDXQuery(name,query,sb,done);
230                    else _serializeASQuery(name,query,sb,done);
231            }
232    
233            private void _serializeWDDXQuery(String name,Query query,StringBuffer sb, Set<Object> done) throws ConverterException {
234                    
235                    Key[] keys = query.keys();
236                    sb.append("new WddxRecordset();");
237                    
238                    int recordcount=query.getRecordcount();
239                    for(int i=0;i<keys.length;i++) {
240                            if(useShortcuts)sb.append("col"+i+"=[];");
241                            else sb.append("col"+i+"=new Array();");
242                            // lower case ist ok!
243                            String skey = StringUtil.escapeJS(keys[i].getLowerString());
244                            for(int y=0;y<recordcount;y++) {
245                                    
246                                    sb.append("col"+i+"["+y+"]=");
247                                    
248                                    _serialize("col"+i+"["+y+"]",query.getAt(keys[i],y+1,null),sb,done);
249                                    
250                            }
251                            sb.append(name+"[\""+skey+"\"]=col"+i+";col"+i+"=null;");
252                    }
253            }
254    
255            private void _serializeASQuery(String name,Query query,StringBuffer sb, Set<Object> done) throws ConverterException {
256                    
257                    String[] keys = query.keysAsString();
258                    for(int i=0;i<keys.length;i++) {
259                            keys[i] = StringUtil.escapeJS(keys[i]);
260                    }
261                    if(useShortcuts)sb.append("[];");
262                    else sb.append("new Array();");
263                    
264                    int recordcount=query.getRecordcount();
265                    for(int i=0;i<recordcount;i++) {
266                            if(useShortcuts)sb.append(name+"["+i+"]={};");
267                            else sb.append(name+"["+i+"]=new Object();");
268                            
269                            for(int y=0;y<keys.length;y++) {
270                                    sb.append(name+"["+i+"]['"+keys[y]+"']=");
271                                    _serialize(name+"["+i+"]['"+keys[y]+"']",query.getAt(keys[y],i+1,null),sb,done);
272                            }
273                    }
274            }
275            
276            
277    
278            /**
279             * serialize a DateTime
280             * @param dateTime DateTime to serialize
281             * @param sb 
282             * @param sb
283             * @throws ConverterException
284             */
285            private synchronized void _serializeDateTime(DateTime dateTime, StringBuffer sb) {
286               
287                    Calendar c = JREDateTimeUtil.newInstance(ThreadLocalPageContext.getTimeZone());
288                    c.setTime(dateTime);
289                sb.append(goIn());
290                sb.append("new Date(");
291                sb.append(c.get(Calendar.YEAR));
292                sb.append(",");
293                sb.append(c.get(Calendar.MONTH));
294                sb.append(",");
295                sb.append(c.get(Calendar.DAY_OF_MONTH));
296                sb.append(",");
297                sb.append(c.get(Calendar.HOUR_OF_DAY));
298                sb.append(",");
299                sb.append(c.get(Calendar.MINUTE));
300                sb.append(",");
301                sb.append(c.get(Calendar.SECOND));
302                sb.append(");");
303            }
304    
305            private String goIn() {
306                    //StringBuffer rtn=new StringBuffer(deep);
307                    //for(int i=0;i<deep;i++) rtn.append('\t');
308                    return "";//rtn.toString();
309            }
310    
311            public void useShortcuts(boolean useShortcuts) { 
312                    this.useShortcuts=useShortcuts;
313                    
314            }
315    
316            public void useWDDX(boolean useWDDX) {
317                    this.useWDDX=useWDDX;
318            }
319            
320            /*
321             * @param args
322             * @throws Exception
323             
324            public static void main(String[] args) throws Exception {
325                    JSConverter js=new JSConverter();
326                    Query query=QueryNew.call(null,"aaa,bbb,ccc");
327                    QueryAddRow.call(null,query);
328                    QuerySetCell.call(null,query,"aaa","1.1");
329                    QuerySetCell.call(null,query,"bbb","1.2");
330                    QuerySetCell.call(null,query,"ccc","1.3");
331                    QueryAddRow.call(null,query);
332                    QuerySetCell.call(null,query,"aaa","2.1");
333                    QuerySetCell.call(null,query,"bbb","2.2");
334                    QuerySetCell.call(null,query,"ccc","2.3");
335                    QueryAddRow.call(null,query);
336                    QuerySetCell.call(null,query,"aaa","3.1");
337                    QuerySetCell.call(null,query,"bbb","3.2");
338                    QuerySetCell.call(null,query,"ccc","3.3<hello>");
339                    Array arr2=List ToArray.call(null,"111,222");
340                    Array arr=List ToArray.call(null,"aaaa,bbb,ccc,dddd,eee");
341                    
342                    arr.set(10,arr2);
343    
344                    Struct sct= new Struct();
345                    sct.set("aaa","val1");
346                    sct.set("bbb","val2");
347                    sct.set("ccc","val3");
348                    sct.set("ddd",arr2);
349                    
350                    /*
351            }*/
352    }