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