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