001    package railo.runtime.text.xml.struct;
002    
003    import java.lang.reflect.Method;
004    import java.util.Date;
005    import java.util.Iterator;
006    import java.util.Map;
007    
008    import org.w3c.dom.DOMException;
009    import org.w3c.dom.Document;
010    import org.w3c.dom.Element;
011    import org.w3c.dom.NamedNodeMap;
012    import org.w3c.dom.Node;
013    import org.w3c.dom.NodeList;
014    import org.w3c.dom.UserDataHandler;
015    import org.xml.sax.SAXException;
016    
017    import railo.commons.collections.HashTable;
018    import railo.runtime.PageContext;
019    import railo.runtime.dump.DumpData;
020    import railo.runtime.dump.DumpProperties;
021    import railo.runtime.dump.DumpUtil;
022    import railo.runtime.exp.ExpressionException;
023    import railo.runtime.exp.PageException;
024    import railo.runtime.exp.PageRuntimeException;
025    import railo.runtime.exp.XMLException;
026    import railo.runtime.op.Caster;
027    import railo.runtime.op.Operator;
028    import railo.runtime.text.xml.XMLAttributes;
029    import railo.runtime.text.xml.XMLCaster;
030    import railo.runtime.text.xml.XMLNodeList;
031    import railo.runtime.text.xml.XMLUtil;
032    import railo.runtime.type.Collection;
033    import railo.runtime.type.KeyImpl;
034    import railo.runtime.type.dt.DateTime;
035    import railo.runtime.type.util.ArrayUtil;
036    import railo.runtime.type.util.StructSupport;
037    import railo.runtime.util.ArrayIterator;
038    
039    /**
040     * 
041     */
042    public class XMLNodeStruct extends StructSupport implements XMLStruct {
043            
044            private Node node;
045            protected boolean caseSensitive;
046        
047            /**
048             * constructor of the class 
049             * @param node Node 
050             * @param caseSensitive
051             */
052            protected XMLNodeStruct(Node node, boolean caseSensitive) {
053                    if(node instanceof XMLStruct)node=((XMLStruct)node).toNode();
054                    this.node=node;
055                    this.caseSensitive=caseSensitive;
056            }
057            
058            /**
059             * @see railo.runtime.type.Collection#remove(java.lang.String)
060             */
061            public Object remove(String key) throws PageException {
062                    Object o= XMLUtil.removeProperty(node,KeyImpl.init(key),caseSensitive);
063            if(o!=null)return o;           
064            throw new ExpressionException("node has no child with name ["+key+"]");
065            }
066    
067            /**
068             *
069             * @see railo.runtime.type.Collection#remove(railo.runtime.type.Collection.Key)
070             */
071            public Object remove(Key key) throws PageException {
072            Object o= XMLUtil.removeProperty(node,key,caseSensitive);
073            if(o!=null)return o;           
074            throw new ExpressionException("node has no child with name ["+key+"]");
075            }
076    
077            /**
078             *
079             * @see railo.runtime.type.Collection#removeEL(railo.runtime.type.Collection.Key)
080             */
081            public Object removeEL(Key key) {
082            return  XMLUtil.removeProperty(node,key,caseSensitive);
083            }
084            
085            /**
086             * @see railo.runtime.type.Collection#get(railo.runtime.type.Collection.Key)
087             */
088            public Object get(Collection.Key key) throws PageException {
089                    try {               
090                            return XMLUtil.getProperty(node,key,caseSensitive);
091                    } catch (SAXException e) {
092                            throw new XMLException(e);
093                    }
094            }
095            
096            /**
097             * @see railo.runtime.type.Collection#set(railo.runtime.type.Collection.Key, java.lang.Object)
098             */
099            public Object set(Collection.Key key, Object value) throws PageException {
100                    return XMLUtil.setProperty(node,key,value,caseSensitive);
101            }
102            
103            /**
104             * @return retun the inner map
105             */
106            public Map getMap() {
107                    NodeList elements=XMLUtil.getChildNodes(node,Node.ELEMENT_NODE,false,null);// TODO ist das false hier ok?
108                    Map map=new HashTable();
109                    int len=elements.getLength();
110                    
111                    for(int i=0;i<len;i++) {
112                            Node node=elements.item(i);
113                            map.put(node.getNodeName(),node);
114                    }
115                    return map;
116            }
117            
118            /**
119             *
120             * @see railo.runtime.type.Collection#duplicate(boolean)
121             */
122            public Collection duplicate(boolean deepCopy) {
123                    return new XMLNodeStruct(node.cloneNode(deepCopy),caseSensitive);
124            }
125    
126    
127            
128            
129            
130    
131            /**
132             * @see org.w3c.dom.Node#cloneNode(boolean)
133             */
134            public Node cloneNode(boolean deep) {
135                    return new XMLNodeStruct(node.cloneNode(deep),caseSensitive);
136            }
137            
138            
139            /**
140             * @see org.w3c.dom.Node#getNodeType()
141             */
142            public short getNodeType() {
143                    return node.getNodeType();
144            }
145            
146            /**
147             * @see org.w3c.dom.Node#normalize()
148             */
149            public void normalize() {
150                    node.normalize();
151            }
152            
153            /**
154             * @see org.w3c.dom.Node#hasAttributes()
155             */
156            public boolean hasAttributes() {
157                    return node.hasAttributes();
158            }
159            
160            /**
161             * @see org.w3c.dom.Node#hasChildNodes()
162             */
163            public boolean hasChildNodes() {
164                    return node.hasChildNodes();
165            }
166            
167            /**
168             * @see org.w3c.dom.Node#getLocalName()
169             */
170            public String getLocalName() {
171                    return node.getLocalName();
172            }
173            
174            /**
175             * @see org.w3c.dom.Node#getNamespaceURI()
176             */
177            public String getNamespaceURI() {
178                    return node.getNamespaceURI();
179            }
180            
181            /**
182             * @see org.w3c.dom.Node#getNodeName()
183             */
184            public String getNodeName() {
185                    return node.getNodeName();
186            }
187            
188            /**
189             * @see org.w3c.dom.Node#getNodeValue()
190             */
191            public String getNodeValue() throws DOMException {
192                    return node.getNodeValue();
193            }
194            
195            /**
196             * @see org.w3c.dom.Node#getPrefix()
197             */
198            public String getPrefix() {
199                    return node.getPrefix();
200            }
201            
202            /**
203             * @see org.w3c.dom.Node#setNodeValue(java.lang.String)
204             */
205            public void setNodeValue(String nodeValue) throws DOMException {
206                    node.setNodeValue(nodeValue);
207            }
208            
209            /**
210             * @see org.w3c.dom.Node#setPrefix(java.lang.String)
211             */
212            public void setPrefix(String prefix) throws DOMException {
213                    node.setPrefix(prefix);
214            }
215            
216            /**
217             * @see org.w3c.dom.Node#getOwnerDocument()
218             */
219            public Document getOwnerDocument() {
220                    if(node instanceof Document) return (Document) node;
221                    return node.getOwnerDocument();
222            }
223            
224            /**
225             * @see org.w3c.dom.Node#getAttributes()
226             */
227            public NamedNodeMap getAttributes() {
228                    return new XMLAttributes(node.getAttributes(),caseSensitive);
229            }
230            
231            /**
232             * @see org.w3c.dom.Node#getFirstChild()
233             */
234            public Node getFirstChild() {
235                    return node.getFirstChild();
236            }
237            
238            /**
239             * @see org.w3c.dom.Node#getLastChild()
240             */
241            public Node getLastChild() {
242                    return node.getLastChild();
243            }
244            
245            /**
246             * @see org.w3c.dom.Node#getNextSibling()
247             */
248            public Node getNextSibling() {
249                    return node.getNextSibling();
250            }
251            
252            /**
253             * @see org.w3c.dom.Node#getParentNode()
254             */
255            public Node getParentNode() {
256                    return node.getParentNode();
257            }
258            
259            /**
260             * @see org.w3c.dom.Node#getPreviousSibling()
261             */
262            public Node getPreviousSibling() {
263                    return node.getPreviousSibling();
264            }
265            
266            
267            
268            /**
269             * @see org.w3c.dom.Node#getChildNodes()
270             */
271            public NodeList getChildNodes() {
272                    return node.getChildNodes();
273            }
274            
275            /**
276             * @see org.w3c.dom.Node#isSupported(java.lang.String, java.lang.String)
277             */
278            public boolean isSupported(String feature, String version) {
279                    return node.isSupported(feature, version);
280            }
281            
282            /**
283             * @see org.w3c.dom.Node#appendChild(org.w3c.dom.Node)
284             */
285            public Node appendChild(Node newChild) throws DOMException {
286                    return node.appendChild(newChild);
287            }
288            
289            /**
290             * @see org.w3c.dom.Node#removeChild(org.w3c.dom.Node)
291             */
292            public Node removeChild(Node oldChild) throws DOMException {
293                    return node.removeChild(XMLCaster.toRawNode(oldChild));
294            }
295            
296            /**
297             * @see org.w3c.dom.Node#insertBefore(org.w3c.dom.Node, org.w3c.dom.Node)
298             */
299            public Node insertBefore(Node newChild, Node refChild) throws DOMException {
300                    return node.insertBefore(newChild, refChild);
301            }
302            
303            /**
304             * @see org.w3c.dom.Node#replaceChild(org.w3c.dom.Node, org.w3c.dom.Node)
305             */
306            public Node replaceChild(Node newChild, Node oldChild) throws DOMException {
307                    return node.replaceChild(XMLCaster.toRawNode(newChild), XMLCaster.toRawNode(oldChild));
308            }
309            
310            /**
311             * @see railo.runtime.type.Collection#size()
312             */
313            public int size() {
314                    NodeList list = node.getChildNodes();
315                    int len=list.getLength();
316                    int count=0;
317                    for(int i=0;i<len;i++) {
318                            if(list.item(i) instanceof Element) count++;
319                    }
320                    return count;
321            }
322    
323            public String[] keysAsString() {
324                    NodeList elements=XMLUtil.getChildNodes(node,Node.ELEMENT_NODE,false,null);// TODO ist das false hier OK?
325                    String[] arr=new String[elements.getLength()];
326                    for(int i=0;i<arr.length;i++) {
327                            arr[i]=elements.item(i).getNodeName();
328                    }
329                    return arr;
330            }
331    
332            public Collection.Key[] keys() {
333                    NodeList elements=XMLUtil.getChildNodes(node,Node.ELEMENT_NODE,false,null);// TODO ist das false hie ok
334                    Collection.Key[] arr=new Collection.Key[elements.getLength()];
335                    for(int i=0;i<arr.length;i++) {
336                            arr[i]=KeyImpl.init(elements.item(i).getNodeName());
337                    }
338                    return arr;
339            }
340            
341            /**
342             * @see railo.runtime.type.Collection#clear()
343             */
344            public void clear() {
345                    /*NodeList elements=XMLUtil.getChildNodes(node,Node.ELEMENT_NODE);
346                    int len=elements.getLength();
347                    for(int i=0;i<len;i++) {
348                            node.removeChild(elements.item(i));
349                    }*/
350            }
351            
352            /**
353             *
354             * @see railo.runtime.type.Collection#get(railo.runtime.type.Collection.Key, java.lang.Object)
355             */
356            public Object get(Collection.Key key, Object defaultValue) {
357                    return XMLUtil.getPropertyEL(node,key,caseSensitive);
358            }
359    
360            /**
361             *
362             * @see railo.runtime.type.Collection#setEL(railo.runtime.type.Collection.Key, java.lang.Object)
363             */
364            public Object setEL(Key key, Object value) {
365                    return XMLUtil.setPropertyEL(node,key,value,caseSensitive);
366            }
367            
368            /**
369             * @see railo.runtime.type.Collection#keyIterator()
370             */
371            public Iterator keyIterator() {
372                    return new ArrayIterator(keysAsString());
373            }
374    
375            /**
376             * @see railo.runtime.dump.Dumpable#toDumpData(railo.runtime.PageContext, int)
377             */
378            public DumpData toDumpData(PageContext pageContext, int maxlevel, DumpProperties dp) {
379                    return DumpUtil.toDumpData(node, pageContext,maxlevel,dp);
380            }
381    
382            /**
383             * @see railo.runtime.text.xml.struct.XMLStruct#toNode()
384             */
385            public final Node toNode() {
386                    return node;
387            }
388            
389            /**
390             * @return Returns the caseSensitive.
391             */
392            public boolean getCaseSensitive() {
393                    return caseSensitive;
394            }
395            
396            /**
397             *
398             * @see railo.runtime.type.Collection#containsKey(railo.runtime.type.Collection.Key)
399             */
400            public boolean containsKey(Collection.Key key) {
401            return get(key,null)!=null;
402            }
403    
404        /**
405         * @see railo.runtime.text.xml.struct.XMLStruct#getXMLNodeList()
406         */
407        public XMLNodeList getXMLNodeList() {
408            return new XMLNodeList(node,getCaseSensitive());
409        }   
410    
411        /**
412         * @see railo.runtime.op.Castable#castToString()
413         */
414        public String castToString() throws PageException {
415            return XMLCaster.toString(this.node);
416        }
417        
418        /**
419         * @see railo.runtime.type.util.StructSupport#castToString(java.lang.String)
420         */
421        public String castToString(String defaultValue) {
422            return XMLCaster.toString(this.node,defaultValue);
423        }
424    
425        /**
426         * @see railo.runtime.op.Castable#castToBooleanValue()
427         */
428        public boolean castToBooleanValue() throws ExpressionException {
429            throw new ExpressionException("Can't cast XML Node to a boolean value");
430        }
431        
432        /**
433         * @see railo.runtime.op.Castable#castToBoolean(java.lang.Boolean)
434         */
435        public Boolean castToBoolean(Boolean defaultValue) {
436            return defaultValue;
437        }
438    
439    
440        /**
441         * @see railo.runtime.op.Castable#castToDoubleValue()
442         */
443        public double castToDoubleValue() throws ExpressionException {
444            throw new ExpressionException("Can't cast XML Node to a number value");
445        }
446        
447        /**
448         * @see railo.runtime.op.Castable#castToDoubleValue(double)
449         */
450        public double castToDoubleValue(double defaultValue) {
451            return defaultValue;
452        }
453    
454    
455        /**
456         * @see railo.runtime.op.Castable#castToDateTime()
457         */
458        public DateTime castToDateTime() throws ExpressionException {
459            throw new ExpressionException("Can't cast XML Node to a Date");
460        }
461        
462        /**
463         * @see railo.runtime.op.Castable#castToDateTime(railo.runtime.type.dt.DateTime)
464         */
465        public DateTime castToDateTime(DateTime defaultValue) {
466            return defaultValue;
467        }
468    
469            /**
470             * @see railo.runtime.op.Castable#compare(boolean)
471             */
472            public int compareTo(boolean b) throws PageException {
473                    return Operator.compare(castToString(), b);
474            }
475    
476            /**
477             * @see railo.runtime.op.Castable#compareTo(railo.runtime.type.dt.DateTime)
478             */
479            public int compareTo(DateTime dt) throws PageException {
480                    return Operator.compare(castToString(), (Date)dt);
481            }
482    
483            /**
484             * @see railo.runtime.op.Castable#compareTo(double)
485             */
486            public int compareTo(double d) throws PageException {
487                    return Operator.compare(castToString(), d);
488            }
489    
490            /**
491             * @see railo.runtime.op.Castable#compareTo(java.lang.String)
492             */
493            public int compareTo(String str) throws PageException {
494                    return Operator.compare(castToString(), str);
495            }
496    
497        /**
498         * @see org.w3c.dom.Node#getBaseURI()
499         */
500        public String getBaseURI() {
501            // not supported
502            return null;
503        }
504    
505        /**
506         * @see org.w3c.dom.Node#compareDocumentPosition(org.w3c.dom.Node)
507         */
508        public short compareDocumentPosition(Node other) throws DOMException {
509            // not supported
510            return -1;
511        }
512    
513        /* *
514         * @see org.w3c.dom.Node#getTextContent()
515         * /
516        public String getTextContent() throws DOMException {
517            switch(node.getNodeType()) {
518            case ELEMENT_NODE:
519            case ATTRIBUTE_NODE:
520            case ENTITY_NODE:
521            case ENTITY_REFERENCE_NODE:
522            case DOCUMENT_FRAGMENT_NODE:
523                NodeList list = node.getChildNodes();
524                int len=list.getLength();
525                StringBuffer sb=new StringBuffer();
526                for(int i=0;i<len;i++) {
527                    sb.append(list.item(i).getTextContent());
528                }
529                return sb.toString();
530            case TEXT_NODE:
531            case CDATA_SECTION_NODE:
532            case COMMENT_NODE:
533            case PROCESSING_INSTRUCTION_NODE:
534                return node.getNodeValue();
535            
536            }
537            return null;
538        }*/
539    
540        /**
541         * @see org.w3c.dom.Node#setTextContent(java.lang.String)
542         */
543        public void setTextContent(String textContent) throws DOMException {
544            //TODO  not supported
545            throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR,"this method is not supported");
546        }
547    
548        /**
549         * @see org.w3c.dom.Node#isSameNode(org.w3c.dom.Node)
550         */
551        public boolean isSameNode(Node other) {
552            return this==other;
553        }
554    
555        /**
556         * @see org.w3c.dom.Node#lookupPrefix(java.lang.String)
557         */
558        public String lookupPrefix(String namespaceURI) {
559    //      TODO not supported
560            return null;
561        }
562    
563        /**
564         * @see org.w3c.dom.Node#isDefaultNamespace(java.lang.String)
565         */
566        public boolean isDefaultNamespace(String namespaceURI) {
567    //      TODO not supported
568            return false;
569        }
570    
571        /**
572         * @see org.w3c.dom.Node#lookupNamespaceURI(java.lang.String)
573         */
574        public String lookupNamespaceURI(String prefix) {
575    //      TODO not supported
576            return null;
577        }
578    
579        /**
580         * @see org.w3c.dom.Node#isEqualNode(org.w3c.dom.Node)
581         */
582        public boolean isEqualNode(Node node) {
583    //      TODO not supported
584            return this==node;
585        }
586    
587        /**
588         * @see org.w3c.dom.Node#getFeature(java.lang.String, java.lang.String)
589         */
590        public Object getFeature(String feature, String version) {
591            // TODO not supported
592            return null;
593        }
594    
595        /**
596         * @see org.w3c.dom.Node#getUserData(java.lang.String)
597         */
598        public Object getUserData(String key) {
599            // dynamic load to support jre 1.4 and 1.5
600                    try {
601                            Method m = node.getClass().getMethod("getUserData", new Class[]{key.getClass()});
602                            return m.invoke(node, new Object[]{key});
603                    } 
604                    catch (Exception e) {
605                            throw new PageRuntimeException(Caster.toPageException(e));
606                    }
607        }
608    
609            /**
610             * @see org.w3c.dom.Node#getTextContent()
611             */
612            public String getTextContent() throws DOMException {
613            // dynamic load to support jre 1.4 and 1.5
614                    try {
615                            Method m = node.getClass().getMethod("getTextContent", new Class[]{});
616                            return Caster.toString(m.invoke(node, ArrayUtil.OBJECT_EMPTY));
617                    } 
618                    catch (Exception e) {
619                            throw new PageRuntimeException(Caster.toPageException(e));
620                    }
621            }
622    
623            /**
624             * @see org.w3c.dom.Node#setUserData(java.lang.String, java.lang.Object, org.w3c.dom.UserDataHandler)
625             */
626            public Object setUserData(String key, Object data, UserDataHandler handler) {
627            // dynamic load to support jre 1.4 and 1.5
628                    try {
629                            Method m = node.getClass().getMethod("setUserData", new Class[]{key.getClass(),data.getClass(),handler.getClass()});
630                            return m.invoke(node, new Object[]{key,data,handler});
631                    } 
632                    catch (Exception e) {
633                            throw new PageRuntimeException(Caster.toPageException(e));
634                    }
635            }
636    
637            public boolean isCaseSensitive() {
638                    return caseSensitive;
639            }
640            
641            public boolean equals(Object obj) {
642                    if(!(obj instanceof XMLNodeStruct)) 
643                            return super.equals(obj);
644                    XMLNodeStruct other = ((XMLNodeStruct)obj);
645                    return other.caseSensitive=caseSensitive && other.node.equals(node);
646            }
647            
648            
649    
650    }