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