001 package railo.runtime.converter; 002 003 import java.io.IOException; 004 import java.io.StringReader; 005 import java.util.Date; 006 import java.util.HashSet; 007 import java.util.Iterator; 008 import java.util.List; 009 import java.util.ListIterator; 010 import java.util.Locale; 011 import java.util.Map; 012 import java.util.Set; 013 import java.util.TimeZone; 014 015 import javax.xml.parsers.FactoryConfigurationError; 016 017 import org.apache.xerces.parsers.DOMParser; 018 import org.w3c.dom.Document; 019 import org.w3c.dom.Element; 020 import org.w3c.dom.Node; 021 import org.w3c.dom.NodeList; 022 import org.xml.sax.InputSource; 023 024 import railo.commons.date.TimeZoneConstants; 025 import railo.commons.lang.NumberUtil; 026 import railo.runtime.Component; 027 import railo.runtime.ComponentScope; 028 import railo.runtime.ComponentWrap; 029 import railo.runtime.PageContext; 030 import railo.runtime.component.Property; 031 import railo.runtime.engine.ThreadLocalPageContext; 032 import railo.runtime.exp.ExpressionException; 033 import railo.runtime.exp.PageException; 034 import railo.runtime.op.Caster; 035 import railo.runtime.op.date.DateCaster; 036 import railo.runtime.orm.hibernate.HBMCreator; 037 import railo.runtime.text.xml.XMLUtil; 038 import railo.runtime.type.Array; 039 import railo.runtime.type.ArrayImpl; 040 import railo.runtime.type.Collection; 041 import railo.runtime.type.KeyImpl; 042 import railo.runtime.type.Query; 043 import railo.runtime.type.QueryImpl; 044 import railo.runtime.type.Struct; 045 import railo.runtime.type.StructImpl; 046 import railo.runtime.type.UDF; 047 import railo.runtime.type.cfc.ComponentAccess; 048 import railo.runtime.type.dt.DateTime; 049 import railo.runtime.type.dt.DateTimeImpl; 050 import railo.runtime.type.util.ComponentUtil; 051 052 /** 053 * class to serialize and desirilize WDDX Packes 054 */ 055 public final class WDDXConverter { 056 private static final Collection.Key REMOTING_FETCH = KeyImpl.intern("remotingFetch"); 057 058 private int deep=1; 059 private boolean xmlConform; 060 private char _; 061 private TimeZone timeZone; 062 private boolean ignoreRemotingFetch=true; 063 //private PageContext pcx; 064 065 /** 066 * constructor of the class 067 * @param timeZone 068 * @param xmlConform define if generated xml conform output or wddx conform output (wddx is not xml conform) 069 */ 070 public WDDXConverter(TimeZone timeZone, boolean xmlConform,boolean ignoreRemotingFetch) { 071 this.xmlConform=xmlConform; 072 _=(xmlConform)?'"':'\''; 073 this.timeZone=timeZone; 074 this.ignoreRemotingFetch=ignoreRemotingFetch; 075 } 076 077 /** 078 * defines timezone info will 079 * @param timeZone 080 */ 081 public void setTimeZone(TimeZone timeZone) { 082 this.timeZone=timeZone; 083 } 084 085 /** 086 * serialize a Date 087 * @param date Date to serialize 088 * @return serialized date 089 * @throws ConverterException 090 */ 091 private String _serializeDate(Date date) { 092 return _serializeDateTime(new DateTimeImpl(date)); 093 } 094 /** 095 * serialize a DateTime 096 * @param dateTime DateTime to serialize 097 * @return serialized dateTime 098 * @throws ConverterException 099 */ 100 private String _serializeDateTime(DateTime dateTime) { 101 //try { 102 103 104 String strDate = new railo.runtime.format.DateFormat(Locale.US).format(dateTime,"yyyy-m-d",TimeZoneConstants.UTC); 105 String strTime = new railo.runtime.format.TimeFormat(Locale.US).format(dateTime,"H:m:s",TimeZoneConstants.UTC); 106 107 return goIn()+"<dateTime>"+ 108 strDate+ 109 "T"+strTime+ 110 "+0:0"+ 111 "</dateTime>"; 112 113 /*} 114 catch (PageException e) { 115 throw new ConverterException(e); 116 }*/ 117 } 118 119 /** 120 * @param dateTime 121 * @return returns the time zone info 122 */ 123 private String getTimeZoneInfo(DateTime dateTime) { 124 timeZone=ThreadLocalPageContext.getTimeZone(timeZone); 125 //if(timeZone==null) return ""; 126 127 int minutes=timeZone.getOffset(dateTime.getTime())/1000/60; 128 String operator=(minutes>=0)?"+":"-"; 129 if(operator.equals("-"))minutes=minutes-(minutes+minutes); 130 int hours=minutes/60; 131 minutes=minutes%60; 132 133 return operator+hours+":"+minutes; 134 135 136 } 137 138 /** 139 * serialize a Array 140 * @param array Array to serialize 141 * @param done 142 * @return serialized array 143 * @throws ConverterException 144 */ 145 private String _serializeArray(Array array, Set<Object> done) throws ConverterException { 146 return _serializeList(array.toList(),done); 147 } 148 149 /** 150 * serialize a List (as Array) 151 * @param list List to serialize 152 * @param done 153 * @return serialized list 154 * @throws ConverterException 155 */ 156 private String _serializeList(List list, Set<Object> done) throws ConverterException { 157 StringBuffer sb=new StringBuffer(goIn()+"<array length="+_+list.size()+_+">"); 158 159 ListIterator it=list.listIterator(); 160 while(it.hasNext()) { 161 sb.append(_serialize(it.next(),done)); 162 } 163 164 sb.append(goIn()+"</array>"); 165 return sb.toString(); 166 } 167 168 /** 169 * serialize a Component 170 * @param component Component to serialize 171 * @param done 172 * @return serialized component 173 * @throws ConverterException 174 */ 175 private String _serializeComponent(Component component, Set<Object> done) throws ConverterException { 176 StringBuffer sb=new StringBuffer(); 177 ComponentAccess ca; 178 try { 179 component=new ComponentWrap(Component.ACCESS_PRIVATE, ca=ComponentUtil.toComponentAccess(component)); 180 } catch (ExpressionException e1) { 181 throw new ConverterException(e1); 182 } 183 boolean isPeristent=ca.isPersistent(); 184 185 186 deep++; 187 Object member; 188 Iterator it=component.keyIterator(); 189 Collection.Key key; 190 while(it.hasNext()) { 191 key=Caster.toKey(it.next(),null); 192 member = component.get(key,null); 193 if(member instanceof UDF) continue; 194 sb.append(goIn()+"<var scope=\"this\" name="+_+key.toString()+_+">"); 195 sb.append(_serialize(member,done)); 196 sb.append(goIn()+"</var>"); 197 } 198 199 Property p; 200 Boolean remotingFetch; 201 Struct props = ignoreRemotingFetch?null:ComponentUtil.getPropertiesAsStruct(ca,false); 202 ComponentScope scope = ca.getComponentScope(); 203 it=scope.keyIterator(); 204 while(it.hasNext()) { 205 key=Caster.toKey(it.next(),null); 206 if(!ignoreRemotingFetch) { 207 p=(Property) props.get(key,null); 208 if(p!=null) { 209 remotingFetch=Caster.toBoolean(p.getDynamicAttributes().get(REMOTING_FETCH,null),null); 210 if(remotingFetch==null){ 211 if(isPeristent && HBMCreator.isRelated(p)) continue; 212 } 213 else if(!remotingFetch.booleanValue()) continue; 214 } 215 } 216 217 member = scope.get(key,null); 218 if(member instanceof UDF || key.equals(KeyImpl.THIS)) continue; 219 sb.append(goIn()+"<var scope=\"variables\" name="+_+key.toString()+_+">"); 220 sb.append(_serialize(member,done)); 221 sb.append(goIn()+"</var>"); 222 } 223 224 225 deep--; 226 try { 227 //return goIn()+"<struct>"+sb+"</struct>"; 228 return goIn()+"<component md5=\""+ComponentUtil.md5(component)+"\" name=\""+component.getAbsName()+"\">"+sb+"</component>"; 229 } 230 catch (Exception e) { 231 throw new ConverterException(e); 232 } 233 } 234 235 /** 236 * serialize a Struct 237 * @param struct Struct to serialize 238 * @param done 239 * @return serialized struct 240 * @throws ConverterException 241 */ 242 private String _serializeStruct(Struct struct, Set<Object> done) throws ConverterException { 243 StringBuffer sb=new StringBuffer(goIn()+"<struct>"); 244 245 Iterator it=struct.keyIterator(); 246 247 deep++; 248 while(it.hasNext()) { 249 String key=Caster.toString(it.next(),""); 250 sb.append(goIn()+"<var name="+_+key.toString()+_+">"); 251 sb.append(_serialize(struct.get(key,null),done)); 252 sb.append(goIn()+"</var>"); 253 } 254 deep--; 255 256 sb.append(goIn()+"</struct>"); 257 return sb.toString(); 258 } 259 260 /** 261 * serialize a Map (as Struct) 262 * @param map Map to serialize 263 * @param done 264 * @return serialized map 265 * @throws ConverterException 266 */ 267 private String _serializeMap(Map map, Set<Object> done) throws ConverterException { 268 StringBuffer sb=new StringBuffer(goIn()+"<struct>"); 269 270 Iterator it=map.keySet().iterator(); 271 272 deep++; 273 while(it.hasNext()) { 274 Object key=it.next(); 275 sb.append(goIn()+"<var name="+_+key.toString()+_+">"); 276 sb.append(_serialize(map.get(key),done)); 277 sb.append(goIn()+"</var>"); 278 } 279 deep--; 280 281 sb.append(goIn()+"</struct>"); 282 return sb.toString(); 283 } 284 285 /** 286 * serialize a Query 287 * @param query Query to serialize 288 * @param done 289 * @return serialized query 290 * @throws ConverterException 291 */ 292 private String _serializeQuery(Query query, Set<Object> done) throws ConverterException { 293 294 String[] keys = query.keysAsString(); 295 StringBuffer sb=new StringBuffer(goIn()+"<recordset rowCount="+_+query.getRecordcount()+_+" fieldNames="+_+railo.runtime.type.List.arrayToList(keys,",")+_+" type="+_+"coldfusion.sql.QueryTable"+_+">"); 296 297 298 deep++; 299 int len=query.getRecordcount(); 300 for(int i=0;i<keys.length;i++) { 301 sb.append(goIn()+"<field name="+_+keys[i]+_+">"); 302 for(int y=1;y<=len;y++) { 303 try { 304 sb.append(_serialize(query.getAt(keys[i],y),done)); 305 } catch (PageException e) { 306 sb.append(_serialize(e.getMessage(),done)); 307 } 308 } 309 310 sb.append(goIn()+"</field>"); 311 } 312 deep--; 313 314 sb.append(goIn()+"</recordset>"); 315 return sb.toString(); 316 } 317 318 /** 319 * serialize a Object to his xml Format represenation 320 * @param object Object to serialize 321 * @param done 322 * @return serialized Object 323 * @throws ConverterException 324 */ 325 private String _serialize(Object object, Set<Object> done) throws ConverterException { 326 String rtn; 327 deep++; 328 // NULL 329 if(object==null) { 330 rtn= goIn()+"<null/>"; 331 deep--; 332 return rtn; 333 } 334 // String 335 if(object instanceof String) { 336 rtn= goIn()+"<string>"+XMLUtil.escapeXMLString(object.toString())+"</string>"; 337 deep--; 338 return rtn; 339 } 340 // Number 341 if(object instanceof Number) { 342 rtn= goIn()+"<number>"+((Number)object).doubleValue()+"</number>"; 343 deep--; 344 return rtn; 345 } 346 // Boolean 347 if(object instanceof Boolean) { 348 rtn= goIn()+"<boolean value="+_+((Boolean)object).booleanValue()+_+"/>"; 349 deep--; 350 return rtn; 351 } 352 // DateTime 353 if(object instanceof DateTime) { 354 rtn= _serializeDateTime((DateTime)object); 355 deep--; 356 return rtn; 357 } 358 // Date 359 if(object instanceof Date) { 360 rtn= _serializeDate((Date)object); 361 deep--; 362 return rtn; 363 } 364 365 Object raw = LazyConverter.toRaw(object); 366 if(done.contains(raw)){ 367 rtn= goIn()+"<null/>"; 368 deep--; 369 return rtn; 370 } 371 372 done.add(raw); 373 try { 374 // Component 375 if(object instanceof Component) { 376 rtn= _serializeComponent((Component)object,done); 377 deep--; 378 return rtn; 379 } 380 // Struct 381 if(object instanceof Struct) { 382 rtn= _serializeStruct((Struct)object,done); 383 deep--; 384 return rtn; 385 } 386 // Map 387 if(object instanceof Map) { 388 rtn= _serializeMap((Map)object,done); 389 deep--; 390 return rtn; 391 } 392 // Array 393 if(object instanceof Array) { 394 rtn= _serializeArray((Array)object,done); 395 deep--; 396 return rtn; 397 } 398 // List 399 if(object instanceof List) { 400 rtn= _serializeList((List)object,done); 401 deep--; 402 return rtn; 403 } 404 // Query 405 if(object instanceof Query) { 406 rtn= _serializeQuery((Query)object,done); 407 deep--; 408 return rtn; 409 } 410 } 411 finally{ 412 done.remove(raw); 413 } 414 // Others 415 rtn="<struct type="+_+"L"+object.getClass().getName()+";"+_+"></struct>"; 416 deep--; 417 return rtn; 418 } 419 420 /** 421 * serialize a Object to his xml Format represenation and create a valid wddx representation 422 * @param object Object to serialize 423 * @return serialized wddx package 424 * @throws ConverterException 425 */ 426 public String serialize(Object object) throws ConverterException { 427 deep=0; 428 429 StringBuffer sb=new StringBuffer(); 430 if(xmlConform)sb.append("<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>"); 431 sb.append("<wddxPacket version="+_+"1.0"+_+">"); 432 deep++; 433 sb.append(goIn()+"<header/>"); 434 sb.append(goIn()+"<data>"); 435 sb.append(_serialize(object,new HashSet<Object>())); 436 sb.append(goIn()+"</data>"); 437 deep--; 438 sb.append("</wddxPacket>"); 439 return sb.toString(); 440 } 441 442 443 /** 444 * deserialize a WDDX Package (XML String Representation) to a runtime object 445 * @param strWddx 446 * @param validate 447 * @return Object represent WDDX Package 448 * @throws ConverterException 449 * @throws IOException 450 * @throws FactoryConfigurationError 451 */ 452 public Object deserialize(String strWddx, boolean validate) throws ConverterException, IOException, FactoryConfigurationError { 453 try { 454 DOMParser parser = new DOMParser(); 455 if(validate) parser.setEntityResolver(new WDDXEntityResolver()); 456 457 parser.parse(new InputSource(new StringReader(strWddx))); 458 Document doc=parser.getDocument(); 459 460 // WDDX Package 461 NodeList docChldren = doc.getChildNodes(); 462 Node wddxPacket=doc; 463 int len = docChldren.getLength(); 464 for(int i = 0; i < len; i++) { 465 Node node=docChldren.item(i); 466 if(node.getNodeName().equalsIgnoreCase("wddxPacket")) { 467 wddxPacket=node; 468 break; 469 } 470 } 471 472 NodeList nl = wddxPacket.getChildNodes(); 473 int n = nl.getLength(); 474 475 476 for(int i = 0; i < n; i++) { 477 Node data = nl.item(i); 478 if(data.getNodeName().equals("data")) { 479 NodeList list=data.getChildNodes(); 480 len=list.getLength(); 481 for(int y=0;y<len;y++) { 482 Node node=list.item(y); 483 if(node instanceof Element) 484 return _deserialize((Element)node); 485 486 } 487 } 488 } 489 490 throw new IllegalArgumentException("Invalid WDDX Format: node 'data' not found in WDD packet"); 491 492 } 493 catch(org.xml.sax.SAXException sxe) { 494 throw new IllegalArgumentException("XML Error: " + sxe.toString()); 495 } 496 } 497 498 499 500 /** 501 * deserialize a WDDX Package (XML Element) to a runtime object 502 * @param element 503 * @return deserialized Element 504 * @throws ConverterException 505 */ 506 private Object _deserialize(Element element) throws ConverterException { 507 String nodeName=element.getNodeName().toLowerCase(); 508 509 // NULL 510 if(nodeName.equals("null")) { 511 return null; 512 } 513 // String 514 else if(nodeName.equals("string")) { 515 return _deserializeString(element); 516 /*Node data=element.getFirstChild(); 517 if(data==null) return ""; 518 519 String value=data.getNodeValue(); 520 521 if(value==null) return ""; 522 return XMLUtil.unescapeXMLString(value);*/ 523 } 524 // Number 525 else if(nodeName.equals("number")) { 526 try { 527 Node data=element.getFirstChild(); 528 if(data==null) return new Double(0); 529 return Caster.toDouble(data.getNodeValue()); 530 } catch (Exception e) { 531 throw new ConverterException(e); 532 } 533 } 534 // Boolean 535 else if(nodeName.equals("boolean")) { 536 try { 537 return Caster.toBoolean(element.getAttribute("value")); 538 } catch (PageException e) { 539 throw new ConverterException(e); 540 541 } 542 } 543 // Array 544 else if(nodeName.equals("array")) { 545 return _deserializeArray(element); 546 } 547 // Component 548 else if(nodeName.equals("component")) { 549 return _deserializeComponent(element); 550 } 551 // Struct 552 else if(nodeName.equals("struct")) { 553 return _deserializeStruct(element); 554 } 555 // Query 556 else if(nodeName.equals("recordset")) { 557 return _deserializeQuery(element); 558 } 559 // DateTime 560 else if(nodeName.equalsIgnoreCase("dateTime")) { 561 try { 562 return DateCaster.toDateAdvanced(element.getFirstChild().getNodeValue(),timeZone); 563 } 564 catch (Exception e) { 565 throw new ConverterException(e); 566 } 567 } 568 else 569 throw new ConverterException("can't deserialize Element of type ["+nodeName+"] to a Object representation"); 570 571 } 572 573 private Object _deserializeString(Element element) { 574 NodeList childList = element.getChildNodes(); 575 int len = childList.getLength(); 576 StringBuffer sb=new StringBuffer(); 577 Node data; 578 String str; 579 for(int i=0;i<len;i++) { 580 data=childList.item(i); 581 if(data==null)continue; 582 583 //<char code="0a"/> 584 if("char".equals(data.getNodeName())) { 585 str=((Element)data).getAttribute("code"); 586 sb.append((char)NumberUtil.hexToInt(str, 10)); 587 } 588 else { 589 sb.append(str=data.getNodeValue()); 590 } 591 } 592 return sb.toString(); 593 //return XMLUtil.unescapeXMLString(sb.toString()); 594 } 595 596 /** 597 * Desirialize a Query Object 598 * @param recordset Query Object as XML Element 599 * @return Query Object 600 * @throws ConverterException 601 */ 602 private Object _deserializeQuery(Element recordset) throws ConverterException { 603 try { 604 // create Query Object 605 Query query=new QueryImpl( 606 railo.runtime.type.List.listToArray( 607 recordset.getAttribute("fieldNames"),',' 608 ) 609 ,Caster.toIntValue(recordset.getAttribute("rowCount")),"query" 610 ); 611 612 NodeList list = recordset.getChildNodes(); 613 int len=list.getLength(); 614 for(int i=0;i<len;i++) { 615 Node node=list.item(i); 616 if(node instanceof Element) { 617 _deserializeQueryField(query,(Element) node); 618 } 619 } 620 return query; 621 } 622 catch(PageException e) { 623 throw new ConverterException(e); 624 } 625 626 } 627 628 /** 629 * deserilize a single Field of a query WDDX Object 630 * @param query 631 * @param field 632 * @throws ConverterException 633 * @throws PageException 634 */ 635 private void _deserializeQueryField(Query query,Element field) throws PageException, ConverterException { 636 String name=field.getAttribute("name"); 637 NodeList list = field.getChildNodes(); 638 int len=list.getLength(); 639 int count=0; 640 for(int i=0;i<len;i++) { 641 Node node=list.item(i); 642 if(node instanceof Element) { 643 query.setAt(name,++count,_deserialize((Element) node)); 644 } 645 } 646 647 } 648 649 /** 650 * Desirialize a Component Object 651 * @param elComp Component Object as XML Element 652 * @return Component Object 653 * @throws ConverterException 654 * @throws ConverterException 655 */ 656 private Object _deserializeComponent(Element elComp) throws ConverterException { 657 // String type=elStruct.getAttribute("type"); 658 String name=elComp.getAttribute("name"); 659 String md5=elComp.getAttribute("md5"); 660 661 // TLPC 662 PageContext pc = ThreadLocalPageContext.get(); 663 664 // Load comp 665 ComponentAccess comp=null; 666 try { 667 comp = ComponentUtil.toComponentAccess(pc.loadComponent(name)); 668 if(!ComponentUtil.md5(comp).equals(md5)){ 669 throw new ConverterException("component ["+name+"] in this enviroment has not the same interface as the component to load"); 670 } 671 } 672 catch (ConverterException e) { 673 throw e; 674 } 675 catch (Exception e) { 676 throw new ConverterException(e.getMessage()); 677 } 678 679 680 NodeList list=elComp.getChildNodes(); 681 ComponentScope scope = comp.getComponentScope(); 682 int len=list.getLength(); 683 String scopeName; 684 Element var,value; 685 Collection.Key key; 686 for(int i=0;i<len;i++) { 687 Node node=list.item(i); 688 if(node instanceof Element) { 689 var=(Element)node; 690 value=getChildElement((Element)node); 691 scopeName=var.getAttribute("scope"); 692 if(value!=null) { 693 key=Caster.toKey(var.getAttribute("name"),null); 694 if(key==null) continue; 695 if("variables".equalsIgnoreCase(scopeName)) 696 scope.setEL(key,_deserialize(value)); 697 else 698 comp.setEL(key,_deserialize(value)); 699 } 700 } 701 } 702 return comp; 703 } 704 705 /** 706 * Desirialize a Struct Object 707 * @param elStruct Struct Object as XML Element 708 * @return Struct Object 709 * @throws ConverterException 710 */ 711 private Object _deserializeStruct(Element elStruct) throws ConverterException { 712 String type=elStruct.getAttribute("type"); 713 Struct struct=new StructImpl(); 714 715 NodeList list=elStruct.getChildNodes(); 716 int len=list.getLength(); 717 for(int i=0;i<len;i++) { 718 //print.ln(i); 719 720 Node node=list.item(i); 721 if(node instanceof Element) { 722 Element var=(Element)node; 723 Element value=getChildElement((Element)node); 724 if(value!=null) { 725 struct.setEL(var.getAttribute("name"),_deserialize(value)); 726 727 } 728 } 729 } 730 if(struct.size()==0 && type!=null && type.length()>0) { 731 return ""; 732 } 733 return struct; 734 } 735 736 /** 737 * Desirialize a Struct Object 738 * @param el Struct Object as XML Element 739 * @return Struct Object 740 * @throws ConverterException 741 */ 742 private Array _deserializeArray(Element el) throws ConverterException { 743 Array array=new ArrayImpl(); 744 745 NodeList list=el.getChildNodes(); 746 int len=list.getLength(); 747 for(int i=0;i<len;i++) { 748 Node node=list.item(i); 749 if(node instanceof Element) 750 try { 751 array.append(_deserialize((Element)node)); 752 } catch (PageException e) { 753 throw new ConverterException(e); 754 } 755 756 } 757 return array; 758 } 759 760 /** 761 * return fitst child Element of a Element, if there are no child Elements return null 762 * @param parent parent node 763 * @return child Element 764 */ 765 private Element getChildElement(Element parent) { 766 NodeList list=parent.getChildNodes(); 767 int len=list.getLength(); 768 for(int i=0;i<len;i++) { 769 Node node=list.item(i); 770 if(node instanceof Element) { 771 return (Element)node; 772 } 773 } 774 return null; 775 } 776 777 778 /** 779 * @return return current blockquote 780 */ 781 private String goIn() { 782 //StringBuffer rtn=new StringBuffer(deep); 783 //for(int i=0;i<deep;i++) rtn.append('\t'); 784 //return rtn.toString(); 785 return ""; 786 } 787 788 /** 789 * @see java.lang.Object#equals(java.lang.Object) 790 */ 791 public boolean equals(Object obj) { 792 return timeZone.equals(obj); 793 } 794 }