001 package railo.runtime.converter; 002 003 import java.io.File; 004 import java.io.IOException; 005 import java.io.Writer; 006 import java.lang.reflect.Field; 007 import java.lang.reflect.Method; 008 import java.lang.reflect.Modifier; 009 import java.util.Date; 010 import java.util.HashSet; 011 import java.util.Iterator; 012 import java.util.List; 013 import java.util.ListIterator; 014 import java.util.Map; 015 import java.util.Map.Entry; 016 import java.util.Set; 017 018 import org.w3c.dom.Node; 019 020 import railo.commons.lang.CFTypes; 021 import railo.runtime.Component; 022 import railo.runtime.ComponentScope; 023 import railo.runtime.ComponentWrap; 024 import railo.runtime.PageContext; 025 import railo.runtime.component.Property; 026 import railo.runtime.exp.ExpressionException; 027 import railo.runtime.exp.PageException; 028 import railo.runtime.java.JavaObject; 029 import railo.runtime.op.Caster; 030 import railo.runtime.op.Decision; 031 import railo.runtime.orm.hibernate.HBMCreator; 032 import railo.runtime.reflection.Reflector; 033 import railo.runtime.text.xml.XMLCaster; 034 import railo.runtime.type.Array; 035 import railo.runtime.type.Collection; 036 import railo.runtime.type.Collection.Key; 037 import railo.runtime.type.KeyImpl; 038 import railo.runtime.type.ObjectWrap; 039 import railo.runtime.type.Query; 040 import railo.runtime.type.Struct; 041 import railo.runtime.type.StructImpl; 042 import railo.runtime.type.UDF; 043 import railo.runtime.type.cfc.ComponentAccess; 044 import railo.runtime.type.dt.DateTime; 045 import railo.runtime.type.dt.DateTimeImpl; 046 import railo.runtime.type.dt.TimeSpan; 047 import railo.runtime.type.util.ArrayUtil; 048 import railo.runtime.type.util.CollectionUtil; 049 import railo.runtime.type.util.ComponentUtil; 050 051 /** 052 * class to serialize and desirilize WDDX Packes 053 */ 054 public final class JSONConverter extends ConverterSupport { 055 056 private static final Collection.Key REMOTING_FETCH = KeyImpl.intern("remotingFetch"); 057 058 private static final Key TO_JSON = KeyImpl.intern("_toJson"); 059 private static final Object NULL = new Object(); 060 private static final String NULL_STRING = ""; 061 062 private boolean ignoreRemotingFetch; 063 064 065 /** 066 * constructor of the class 067 */ 068 public JSONConverter(boolean ignoreRemotingFetch) { 069 this.ignoreRemotingFetch=ignoreRemotingFetch; 070 } 071 072 073 /** 074 * serialize Serializable class 075 * @param serializable 076 * @param sb 077 * @param serializeQueryByColumns 078 * @param done 079 * @throws ConverterException 080 */ 081 082 private void _serializeClass(PageContext pc,Set test,Class clazz,Object obj, StringBuffer sb, boolean serializeQueryByColumns, Set<Object> done) throws ConverterException { 083 Struct sct=new StructImpl(Struct.TYPE_LINKED); 084 if(test==null)test=new HashSet(); 085 086 // Fields 087 Field[] fields = clazz.getFields(); 088 Field field; 089 for(int i=0;i<fields.length;i++){ 090 field=fields[i]; 091 if(obj!=null || (field.getModifiers()&Modifier.STATIC)>0) 092 try { 093 sct.setEL(field.getName(), testRecusrion(test,field.get(obj))); 094 } catch (Exception e) { 095 e.printStackTrace(); 096 } 097 } 098 if(obj !=null){ 099 // setters 100 Method[] setters=Reflector.getSetters(clazz); 101 for(int i=0;i<setters.length;i++){ 102 sct.setEL(setters[i].getName().substring(3), NULL); 103 } 104 // getters 105 Method[] getters=Reflector.getGetters(clazz); 106 for(int i=0;i<getters.length;i++){ 107 try { 108 sct.setEL(getters[i].getName().substring(3), testRecusrion(test,getters[i].invoke(obj, ArrayUtil.OBJECT_EMPTY))); 109 110 } 111 catch (Exception e) {} 112 } 113 } 114 test.add(clazz); 115 116 117 _serializeStruct(pc,test,sct, sb, serializeQueryByColumns, true,done); 118 } 119 120 121 private Object testRecusrion(Set test, Object obj) { 122 if(test.contains(obj.getClass())) return obj.getClass().getName(); 123 return obj; 124 } 125 126 127 /** 128 * serialize a Date 129 * @param date Date to serialize 130 * @param sb 131 * @throws ConverterException 132 */ 133 private void _serializeDate(Date date, StringBuffer sb) { 134 _serializeDateTime(new DateTimeImpl(date),sb); 135 } 136 /** 137 * serialize a DateTime 138 * @param dateTime DateTime to serialize 139 * @param sb 140 * @throws ConverterException 141 */ 142 private void _serializeDateTime(DateTime dateTime, StringBuffer sb) { 143 144 sb.append('"'); 145 146 //sb.append(escape(dateTime.toString())); 147 sb.append(escape(JSONDateFormat.format(dateTime,null))); 148 sb.append('"'); 149 150 /*try { 151 sb.append(goIn()); 152 sb.append("createDateTime("); 153 sb.append(DateFormat.call(null,dateTime,"yyyy,m,d")); 154 sb.append(' '); 155 sb.append(TimeFormat.call(null,dateTime,"HH:mm:ss")); 156 sb.append(')'); 157 } 158 catch (PageException e) { 159 throw new ConverterException(e); 160 }*/ 161 //Januar, 01 2000 01:01:01 162 } 163 164 /** 165 * serialize a Array 166 * @param array Array to serialize 167 * @param sb 168 * @param serializeQueryByColumns 169 * @param done 170 * @throws ConverterException 171 */ 172 private void _serializeArray(PageContext pc,Set test,Array array, StringBuffer sb, boolean serializeQueryByColumns, Set<Object> done) throws ConverterException { 173 _serializeList(pc,test,array.toList(),sb,serializeQueryByColumns,done); 174 } 175 176 /** 177 * serialize a List (as Array) 178 * @param list List to serialize 179 * @param sb 180 * @param serializeQueryByColumns 181 * @param done 182 * @throws ConverterException 183 */ 184 private void _serializeList(PageContext pc,Set test,List list, StringBuffer sb, boolean serializeQueryByColumns, Set<Object> done) throws ConverterException { 185 186 sb.append(goIn()); 187 sb.append("["); 188 boolean doIt=false; 189 ListIterator it=list.listIterator(); 190 while(it.hasNext()) { 191 if(doIt)sb.append(','); 192 doIt=true; 193 _serialize(pc,test,it.next(),sb,serializeQueryByColumns,done); 194 } 195 196 sb.append(']'); 197 } 198 private void _serializeArray(PageContext pc,Set test,Object[] arr, StringBuffer sb, boolean serializeQueryByColumns, Set<Object> done) throws ConverterException { 199 200 sb.append(goIn()); 201 sb.append("["); 202 for(int i=0;i<arr.length;i++) { 203 if(i>0)sb.append(','); 204 _serialize(pc,test,arr[i],sb,serializeQueryByColumns,done); 205 } 206 sb.append(']'); 207 } 208 209 /** 210 * serialize a Struct 211 * @param struct Struct to serialize 212 * @param sb 213 * @param serializeQueryByColumns 214 * @param addUDFs 215 * @param done 216 * @throws ConverterException 217 */ 218 public void _serializeStruct(PageContext pc,Set test,Struct struct, StringBuffer sb, boolean serializeQueryByColumns, boolean addUDFs, Set<Object> done) throws ConverterException { 219 // Component 220 if(struct instanceof Component){ 221 String res = castToJson(pc, (Component)struct, NULL_STRING); 222 if(res!=NULL_STRING) { 223 sb.append(res); 224 return; 225 } 226 } 227 228 229 sb.append(goIn()); 230 sb.append("{"); 231 //Key[] keys = struct.keys(); 232 //Key key; 233 Iterator<Entry<Key, Object>> it = struct.entryIterator(); 234 Entry<Key, Object> e; 235 Object value; 236 boolean doIt=false; 237 while(it.hasNext()) { 238 e = it.next(); 239 //key=keys[i]; 240 value=e.getValue(); 241 if(!addUDFs && (value instanceof UDF || value==null))continue; 242 if(doIt)sb.append(','); 243 doIt=true; 244 sb.append('"'); 245 sb.append(escape(e.getKey().getString())); 246 sb.append('"'); 247 sb.append(':'); 248 _serialize(pc,test,value,sb,serializeQueryByColumns,done); 249 } 250 251 if(struct instanceof Component){ 252 Boolean remotingFetch; 253 Component cp = (Component)struct; 254 boolean isPeristent=false; 255 try { 256 ComponentAccess ca = ComponentUtil.toComponentAccess(cp); 257 isPeristent=ca.isPersistent(); 258 } catch (ExpressionException ee) {} 259 260 Property[] props = cp.getProperties(false); 261 ComponentScope scope = cp.getComponentScope(); 262 for(int i=0;i<props.length;i++) { 263 if(!ignoreRemotingFetch) { 264 remotingFetch=Caster.toBoolean(props[i].getDynamicAttributes().get(REMOTING_FETCH,null),null); 265 if(remotingFetch==null){ 266 if(isPeristent && HBMCreator.isRelated(props[i])) continue; 267 } 268 else if(!remotingFetch.booleanValue()) continue; 269 270 } 271 Key key = KeyImpl.getInstance(props[i].getName()); 272 value=scope.get(key,null); 273 if(!addUDFs && (value instanceof UDF || value==null))continue; 274 if(doIt)sb.append(','); 275 doIt=true; 276 sb.append('"'); 277 sb.append(escape(key.getString())); 278 sb.append('"'); 279 sb.append(':'); 280 _serialize(pc,test,value,sb,serializeQueryByColumns,done); 281 } 282 } 283 284 285 sb.append('}'); 286 } 287 288 private static String castToJson(PageContext pc,Component cfc, String defaultValue) throws ConverterException { 289 Object o=cfc.get(TO_JSON,null); 290 if(!(o instanceof UDF)) return defaultValue; 291 UDF udf=(UDF) o; 292 if(udf.getReturnType()!=CFTypes.TYPE_VOID && udf.getFunctionArguments().length==0) { 293 try { 294 return Caster.toString(cfc.call(pc, TO_JSON, new Object[0])); 295 } catch (PageException e) { 296 e.printStackTrace(); 297 throw toConverterException(e); 298 } 299 } 300 return defaultValue; 301 } 302 303 304 305 /** 306 * serialize a Map (as Struct) 307 * @param map Map to serialize 308 * @param sb 309 * @param serializeQueryByColumns 310 * @param done 311 * @throws ConverterException 312 */ 313 private void _serializeMap(PageContext pc,Set test,Map map, StringBuffer sb, boolean serializeQueryByColumns, Set<Object> done) throws ConverterException { 314 sb.append(goIn()); 315 sb.append("{"); 316 317 Iterator it=map.keySet().iterator(); 318 boolean doIt=false; 319 while(it.hasNext()) { 320 Object key=it.next(); 321 if(doIt)sb.append(','); 322 doIt=true; 323 sb.append('"'); 324 sb.append(escape(key.toString())); 325 sb.append('"'); 326 sb.append(':'); 327 _serialize(pc,test,map.get(key),sb,serializeQueryByColumns,done); 328 } 329 330 sb.append('}'); 331 } 332 /** 333 * serialize a Component 334 * @param component Component to serialize 335 * @param sb 336 * @param serializeQueryByColumns 337 * @param done 338 * @throws ConverterException 339 */ 340 private void _serializeComponent(PageContext pc,Set test,Component component, StringBuffer sb, boolean serializeQueryByColumns, Set<Object> done) throws ConverterException { 341 try { 342 ComponentWrap cw = ComponentWrap.toComponentWrap(Component.ACCESS_PRIVATE,component); 343 _serializeStruct(pc,test,cw, sb, serializeQueryByColumns,false,done); 344 } 345 catch (ExpressionException e) { 346 throw toConverterException(e); 347 } 348 } 349 350 351 private void _serializeUDF(PageContext pc,Set test,UDF udf, StringBuffer sb,boolean serializeQueryByColumns, Set<Object> done) throws ConverterException { 352 Struct sct=new StructImpl(); 353 try { 354 // Meta 355 Struct meta = udf.getMetaData(pc); 356 sct.setEL("Metadata", meta); 357 358 // Parameters 359 sct.setEL("MethodAttributes", meta.get("PARAMETERS")); 360 } 361 catch (PageException e) { 362 throw toConverterException(e); 363 } 364 365 sct.setEL("Access", ComponentUtil.toStringAccess(udf.getAccess(),"public")); 366 sct.setEL("Output", Caster.toBoolean(udf.getOutput())); 367 sct.setEL("ReturnType", udf.getReturnTypeAsString()); 368 try{ 369 sct.setEL("PagePath", udf.getPageSource().getResource().getAbsolutePath()); 370 }catch(Throwable t){} 371 372 _serializeStruct(pc,test,sct, sb, serializeQueryByColumns, true,done); 373 // TODO key SuperScope and next? 374 } 375 376 377 378 /** 379 * serialize a Query 380 * @param query Query to serialize 381 * @param sb 382 * @param serializeQueryByColumns 383 * @param done 384 * @throws ConverterException 385 */ 386 private void _serializeQuery(PageContext pc,Set test,Query query, StringBuffer sb, boolean serializeQueryByColumns, Set<Object> done) throws ConverterException { 387 388 Collection.Key[] _keys = CollectionUtil.keys(query); 389 sb.append(goIn()); 390 sb.append("{"); 391 392 /* 393 394 {"DATA":[["a","b"],["c","d"]]} 395 {"DATA":{"aaa":["a","c"],"bbb":["b","d"]}} 396 * */ 397 // Rowcount 398 if(serializeQueryByColumns){ 399 sb.append("\"ROWCOUNT\":"); 400 sb.append(Caster.toString(query.getRecordcount())); 401 sb.append(','); 402 } 403 404 // Columns 405 sb.append("\"COLUMNS\":["); 406 String[] cols = query.getColumns(); 407 for(int i=0;i<cols.length;i++) { 408 if(i>0)sb.append(",\""); 409 else sb.append('"'); 410 sb.append(escape(cols[i].toUpperCase())); 411 sb.append('"'); 412 } 413 sb.append("],"); 414 415 // Data 416 sb.append("\"DATA\":"); 417 if(serializeQueryByColumns) { 418 sb.append('{'); 419 boolean oDoIt=false; 420 int len=query.getRecordcount(); 421 for(int i=0;i<_keys.length;i++) { 422 if(oDoIt)sb.append(','); 423 oDoIt=true; 424 sb.append(goIn()); 425 sb.append('"'); 426 sb.append(escape(_keys[i].getString())); 427 sb.append('"'); 428 sb.append(":["); 429 boolean doIt=false; 430 for(int y=1;y<=len;y++) { 431 if(doIt)sb.append(','); 432 doIt=true; 433 try { 434 _serialize(pc,test,query.getAt(_keys[i],y),sb,serializeQueryByColumns,done); 435 } catch (PageException e) { 436 _serialize(pc,test,e.getMessage(),sb,serializeQueryByColumns,done); 437 } 438 } 439 440 sb.append(']'); 441 } 442 443 sb.append('}'); 444 } 445 else { 446 sb.append('['); 447 boolean oDoIt=false; 448 int len=query.getRecordcount(); 449 for(int row=1;row<=len;row++) { 450 if(oDoIt)sb.append(','); 451 oDoIt=true; 452 453 sb.append("["); 454 boolean doIt=false; 455 for(int col=0;col<_keys.length;col++) { 456 if(doIt)sb.append(','); 457 doIt=true; 458 try { 459 _serialize(pc,test,query.getAt(_keys[col],row),sb,serializeQueryByColumns,done); 460 } catch (PageException e) { 461 _serialize(pc,test,e.getMessage(),sb,serializeQueryByColumns,done); 462 } 463 } 464 sb.append(']'); 465 } 466 sb.append(']'); 467 } 468 sb.append('}'); 469 } 470 471 /** 472 * serialize a Object to his xml Format represenation 473 * @param object Object to serialize 474 * @param sb StringBuffer to write data 475 * @param serializeQueryByColumns 476 * @param done 477 * @throws ConverterException 478 */ 479 private void _serialize(PageContext pc,Set test,Object object, StringBuffer sb, boolean serializeQueryByColumns, Set<Object> done) throws ConverterException { 480 481 // NULL 482 if(object==null || object==NULL) { 483 sb.append(goIn()); 484 sb.append("null"); 485 return; 486 } 487 // String 488 if(object instanceof String || object instanceof StringBuffer) { 489 sb.append(goIn()); 490 sb.append('"'); 491 sb.append(escape(object.toString())); 492 sb.append('"'); 493 return; 494 } 495 // Character 496 if(object instanceof Character) { 497 sb.append(goIn()); 498 sb.append('"'); 499 sb.append(escape(String.valueOf(((Character)object).charValue()))); 500 sb.append('"'); 501 return; 502 } 503 // Number 504 if(object instanceof Number) { 505 sb.append(goIn()); 506 sb.append(Caster.toString(((Number)object).doubleValue())); 507 return; 508 } 509 // Boolean 510 if(object instanceof Boolean) { 511 sb.append(goIn()); 512 sb.append(Caster.toString(((Boolean)object).booleanValue())); 513 return; 514 } 515 // DateTime 516 if(object instanceof DateTime) { 517 _serializeDateTime((DateTime)object,sb); 518 return; 519 } 520 // Date 521 if(object instanceof Date) { 522 _serializeDate((Date)object,sb); 523 return; 524 } 525 // XML 526 if(object instanceof Node) { 527 _serializeXML((Node)object,sb); 528 return; 529 } 530 // Timespan 531 if(object instanceof TimeSpan) { 532 _serializeTimeSpan((TimeSpan) object,sb); 533 return; 534 } 535 // File 536 if(object instanceof File) { 537 _serialize(pc,test, ((File)object).getAbsolutePath(), sb, serializeQueryByColumns,done); 538 return; 539 } 540 // String Converter 541 if(object instanceof ScriptConvertable) { 542 sb.append(((ScriptConvertable)object).serialize()); 543 return; 544 } 545 Object raw = LazyConverter.toRaw(object); 546 if(done.contains(raw)){ 547 sb.append(goIn()); 548 sb.append("null"); 549 return; 550 } 551 552 553 done.add(raw); 554 try{ 555 // Component 556 if(object instanceof Component) { 557 _serializeComponent(pc,test,(Component)object,sb,serializeQueryByColumns,done); 558 return; 559 } 560 // UDF 561 if(object instanceof UDF) { 562 _serializeUDF(pc,test,(UDF)object,sb,serializeQueryByColumns,done); 563 return; 564 } 565 // Struct 566 if(object instanceof Struct) { 567 _serializeStruct(pc,test,(Struct)object,sb,serializeQueryByColumns,true,done); 568 return; 569 } 570 // Map 571 if(object instanceof Map) { 572 _serializeMap(pc,test,(Map)object,sb,serializeQueryByColumns,done); 573 return; 574 } 575 // Array 576 if(object instanceof Array) { 577 _serializeArray(pc,test,(Array)object,sb,serializeQueryByColumns,done); 578 return; 579 } 580 // List 581 if(object instanceof List) { 582 _serializeList(pc,test,(List)object,sb,serializeQueryByColumns,done); 583 return; 584 } 585 // Query 586 if(object instanceof Query) { 587 _serializeQuery(pc,test,(Query)object,sb,serializeQueryByColumns,done); 588 return; 589 } 590 // Native Array 591 if(Decision.isNativeArray(object)){ 592 if(object instanceof char[]) 593 _serialize(pc,test,new String((char[])object), sb, serializeQueryByColumns,done); 594 else { 595 _serializeArray(pc,test,ArrayUtil.toReferenceType(object,ArrayUtil.OBJECT_EMPTY), sb, serializeQueryByColumns,done); 596 } 597 return; 598 599 } 600 // ObjectWrap 601 if(object instanceof ObjectWrap) { 602 try { 603 _serialize(pc,test,((ObjectWrap)object).getEmbededObject(), sb, serializeQueryByColumns,done); 604 } catch (PageException e) { 605 if(object instanceof JavaObject){ 606 _serializeClass(pc,test,((JavaObject)object).getClazz(),null,sb,serializeQueryByColumns,done); 607 } 608 else throw new ConverterException("can't serialize Object of type [ "+Caster.toClassName(object)+" ]"); 609 } 610 return; 611 } 612 613 _serializeClass(pc,test,object.getClass(),object,sb,serializeQueryByColumns,done); 614 } 615 finally{ 616 done.remove(raw); 617 } 618 } 619 620 private void _serializeXML(Node node, StringBuffer sb) { 621 node=XMLCaster.toRawNode(node); 622 sb.append(goIn()); 623 sb.append('"'); 624 sb.append(escape(XMLCaster.toString(node,""))); 625 sb.append('"'); 626 627 } 628 629 630 private void _serializeTimeSpan(TimeSpan span, StringBuffer sb) { 631 632 sb.append(goIn()); 633 sb.append("createTimeSpan("); 634 sb.append(span.getDay()); 635 sb.append(','); 636 sb.append(span.getHour()); 637 sb.append(','); 638 sb.append(span.getMinute()); 639 sb.append(','); 640 sb.append(span.getSecond()); 641 sb.append(')'); 642 643 } 644 645 646 public static String escape(String str) { 647 char[] arr=str.toCharArray(); 648 StringBuffer rtn=new StringBuffer(arr.length); 649 for(int i=0;i<arr.length;i++) { 650 if(arr[i] < 128){ 651 switch(arr[i]) { 652 case '\\': rtn.append("\\\\"); break; 653 case '/': rtn.append("\\/"); break; 654 case '\n': rtn.append("\\n"); break; 655 case '\r': rtn.append("\\r"); break; 656 case '\f': rtn.append("\\f"); break; 657 case '\b': rtn.append("\\b"); break; 658 case '\t': rtn.append("\\t"); break; 659 case '"' : rtn.append("\\\""); break; 660 default : rtn.append(arr[i]); break; 661 } 662 } 663 else { 664 if (arr[i] < 0x10) rtn.append("\\u000"); 665 else if (arr[i] < 0x100) rtn.append( "\\u00"); 666 else if (arr[i] < 0x1000) rtn.append( "\\u0"); 667 else rtn.append( "\\u"); 668 rtn.append(Integer.toHexString(arr[i])); 669 } 670 } 671 return rtn.toString(); 672 } 673 674 /** 675 * serialize a Object to his literal Format 676 * @param object Object to serialize 677 * @param serializeQueryByColumns 678 * @return serialized wddx package 679 * @throws ConverterException 680 */ 681 public String serialize(PageContext pc,Object object, boolean serializeQueryByColumns) throws ConverterException { 682 StringBuffer sb=new StringBuffer(); 683 _serialize(pc,null,object,sb,serializeQueryByColumns,new HashSet<Object>()); 684 return sb.toString(); 685 } 686 687 @Override 688 public void writeOut(PageContext pc, Object source, Writer writer) throws ConverterException, IOException { 689 writer.write(serialize(pc,source,false)); 690 writer.flush(); 691 } 692 693 694 /** 695 * @return return current blockquote 696 */ 697 private String goIn() { 698 return ""; 699 } 700 701 702 }