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