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 }