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