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