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