001/**
002 *
003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved.
004 *
005 * This library is free software; you can redistribute it and/or
006 * modify it under the terms of the GNU Lesser General Public
007 * License as published by the Free Software Foundation; either 
008 * version 2.1 of the License, or (at your option) any later version.
009 * 
010 * This library is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013 * Lesser General Public License for more details.
014 * 
015 * You should have received a copy of the GNU Lesser General Public 
016 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
017 * 
018 **/
019package lucee.runtime.text.xml.struct;
020
021import java.lang.reflect.Method;
022import java.util.Date;
023import java.util.Iterator;
024import java.util.Map;
025
026import lucee.commons.collection.MapFactory;
027import lucee.runtime.PageContext;
028import lucee.runtime.dump.DumpData;
029import lucee.runtime.dump.DumpProperties;
030import lucee.runtime.dump.DumpUtil;
031import lucee.runtime.exp.ExpressionException;
032import lucee.runtime.exp.PageException;
033import lucee.runtime.exp.PageRuntimeException;
034import lucee.runtime.exp.XMLException;
035import lucee.runtime.op.Caster;
036import lucee.runtime.op.Operator;
037import lucee.runtime.text.xml.XMLAttributes;
038import lucee.runtime.text.xml.XMLCaster;
039import lucee.runtime.text.xml.XMLNodeList;
040import lucee.runtime.text.xml.XMLUtil;
041import lucee.runtime.type.Collection;
042import lucee.runtime.type.KeyImpl;
043import lucee.runtime.type.dt.DateTime;
044import lucee.runtime.type.it.EntryIterator;
045import lucee.runtime.type.it.KeyIterator;
046import lucee.runtime.type.it.StringIterator;
047import lucee.runtime.type.it.ValueIterator;
048import lucee.runtime.type.util.ArrayUtil;
049import lucee.runtime.type.util.StructSupport;
050
051import org.w3c.dom.DOMException;
052import org.w3c.dom.Document;
053import org.w3c.dom.Element;
054import org.w3c.dom.NamedNodeMap;
055import org.w3c.dom.Node;
056import org.w3c.dom.NodeList;
057import org.w3c.dom.UserDataHandler;
058import org.xml.sax.SAXException;
059
060/**
061 * 
062 */
063public class XMLNodeStruct extends StructSupport implements XMLStruct {
064        
065        private Node node;
066        protected boolean caseSensitive;
067    
068        /**
069         * constructor of the class 
070         * @param node Node 
071         * @param caseSensitive
072         */
073        protected XMLNodeStruct(Node node, boolean caseSensitive) {
074                if(node instanceof XMLStruct)node=((XMLStruct)node).toNode();
075                this.node=node;
076                this.caseSensitive=caseSensitive;
077        }
078        
079        @Override
080        public Object remove(Key key) throws PageException {
081        Object o= XMLUtil.removeProperty(node,key,caseSensitive);
082        if(o!=null)return o;           
083        throw new ExpressionException("node has no child with name ["+key+"]");
084        }
085
086        @Override
087        public Object removeEL(Key key) {
088        return  XMLUtil.removeProperty(node,key,caseSensitive);
089        }
090        
091        @Override
092        public Object get(Collection.Key key) throws PageException {
093                try {               
094                        return XMLUtil.getProperty(node,key,caseSensitive);
095                } catch (SAXException e) {
096                        throw new XMLException(e);
097                }
098        }
099        
100        @Override
101        public Object set(Collection.Key key, Object value) throws PageException {
102                return XMLUtil.setProperty(node,key,value,caseSensitive);
103        }
104        
105        /**
106         * @return retun the inner map
107         */
108        public Map<String,Node> getMap() {
109                NodeList elements=XMLUtil.getChildNodes(node,Node.ELEMENT_NODE,false,null);// TODO ist das false hier ok?
110                Map<String,Node> map=MapFactory.<String,Node>getConcurrentMap();
111                int len=elements.getLength();
112                
113                for(int i=0;i<len;i++) {
114                        Node node=elements.item(i);
115                        map.put(node.getNodeName(),node);
116                }
117                return map;
118        }
119        
120        @Override
121        public Collection duplicate(boolean deepCopy) {
122                return new XMLNodeStruct(node.cloneNode(deepCopy),caseSensitive);
123        }
124
125
126        
127        
128        
129
130        @Override
131        public Node cloneNode(boolean deep) {
132                return new XMLNodeStruct(node.cloneNode(deep),caseSensitive);
133        }
134        
135        
136        @Override
137        public short getNodeType() {
138                return node.getNodeType();
139        }
140        
141        @Override
142        public void normalize() {
143                node.normalize();
144        }
145        
146        @Override
147        public boolean hasAttributes() {
148                return node.hasAttributes();
149        }
150        
151        @Override
152        public boolean hasChildNodes() {
153                return node.hasChildNodes();
154        }
155        
156        @Override
157        public String getLocalName() {
158                return node.getLocalName();
159        }
160        
161        @Override
162        public String getNamespaceURI() {
163                return node.getNamespaceURI();
164        }
165        
166        @Override
167        public String getNodeName() {
168                return node.getNodeName();
169        }
170        
171        @Override
172        public String getNodeValue() throws DOMException {
173                return node.getNodeValue();
174        }
175        
176        @Override
177        public String getPrefix() {
178                return node.getPrefix();
179        }
180        
181        @Override
182        public void setNodeValue(String nodeValue) throws DOMException {
183                node.setNodeValue(nodeValue);
184        }
185        
186        @Override
187        public void setPrefix(String prefix) throws DOMException {
188                node.setPrefix(prefix);
189        }
190        
191        @Override
192        public Document getOwnerDocument() {
193                if(node instanceof Document) return (Document) node;
194                return node.getOwnerDocument();
195        }
196        
197        @Override
198        public NamedNodeMap getAttributes() {
199                return new XMLAttributes(node,caseSensitive);
200        }
201        
202        @Override
203        public Node getFirstChild() {
204                return node.getFirstChild();
205        }
206        
207        @Override
208        public Node getLastChild() {
209                return node.getLastChild();
210        }
211        
212        @Override
213        public Node getNextSibling() {
214                return node.getNextSibling();
215        }
216        
217        @Override
218        public Node getParentNode() {
219                return node.getParentNode();
220        }
221        
222        @Override
223        public Node getPreviousSibling() {
224                return node.getPreviousSibling();
225        }
226        
227        
228        
229        @Override
230        public NodeList getChildNodes() {
231                return node.getChildNodes();
232        }
233        
234        @Override
235        public boolean isSupported(String feature, String version) {
236                return node.isSupported(feature, version);
237        }
238        
239        @Override
240        public Node appendChild(Node newChild) throws DOMException {
241                return node.appendChild(newChild);
242        }
243        
244        @Override
245        public Node removeChild(Node oldChild) throws DOMException {
246                return node.removeChild(XMLCaster.toRawNode(oldChild));
247        }
248        
249        @Override
250        public Node insertBefore(Node newChild, Node refChild) throws DOMException {
251                return node.insertBefore(newChild, refChild);
252        }
253        
254        @Override
255        public Node replaceChild(Node newChild, Node oldChild) throws DOMException {
256                return node.replaceChild(XMLCaster.toRawNode(newChild), XMLCaster.toRawNode(oldChild));
257        }
258        
259        @Override
260        public int size() {
261                NodeList list = node.getChildNodes();
262                int len=list.getLength();
263                int count=0;
264                for(int i=0;i<len;i++) {
265                        if(list.item(i) instanceof Element) count++;
266                }
267                return count;
268        }
269
270        public Collection.Key[] keys() {
271                NodeList elements=XMLUtil.getChildNodes(node,Node.ELEMENT_NODE,false,null);// TODO ist das false hie ok
272                Collection.Key[] arr=new Collection.Key[elements.getLength()];
273                for(int i=0;i<arr.length;i++) {
274                        arr[i]=KeyImpl.init(elements.item(i).getNodeName());
275                }
276                return arr;
277        }
278        
279        @Override
280        public void clear() {
281                /*NodeList elements=XMLUtil.getChildNodes(node,Node.ELEMENT_NODE);
282                int len=elements.getLength();
283                for(int i=0;i<len;i++) {
284                        node.removeChild(elements.item(i));
285                }*/
286        }
287        
288        @Override
289        public Object get(Collection.Key key, Object defaultValue) {
290                return XMLUtil.getProperty(node,key,caseSensitive,defaultValue);
291        }
292
293        @Override
294        public Object setEL(Key key, Object value) {
295                return XMLUtil.setProperty(node,key,value,caseSensitive,null);
296        }
297        
298        @Override
299        public Iterator<Collection.Key> keyIterator() {
300                return new KeyIterator(keys());
301        }
302    
303    @Override
304        public Iterator<String> keysAsStringIterator() {
305        return new StringIterator(keys());
306    }
307        
308        @Override
309        public Iterator<Entry<Key, Object>> entryIterator() {
310                return new EntryIterator(this,keys());
311        }
312        
313        @Override
314        public Iterator<Object> valueIterator() {
315                return new ValueIterator(this,keys());
316        }
317
318        @Override
319        public DumpData toDumpData(PageContext pageContext, int maxlevel, DumpProperties dp) {
320                return DumpUtil.toDumpData(node, pageContext,maxlevel,dp);
321        }
322
323        @Override
324        public final Node toNode() {
325                return node;
326        }
327        
328        /**
329         * @return Returns the caseSensitive.
330         */
331        public boolean getCaseSensitive() {
332                return caseSensitive;
333        }
334        
335        @Override
336        public boolean containsKey(Collection.Key key) {
337        return get(key,null)!=null;
338        }
339
340    @Override
341    public XMLNodeList getXMLNodeList() {
342        return new XMLNodeList(node,getCaseSensitive(),Node.ELEMENT_NODE);
343    }   
344
345    @Override
346    public String castToString() throws PageException {
347        return XMLCaster.toString(this.node);
348    }
349    
350    @Override
351    public String castToString(String defaultValue) {
352        return XMLCaster.toString(this.node,defaultValue);
353    }
354
355    @Override
356    public boolean castToBooleanValue() throws ExpressionException {
357        throw new ExpressionException("Can't cast XML Node to a boolean value");
358    }
359    
360    @Override
361    public Boolean castToBoolean(Boolean defaultValue) {
362        return defaultValue;
363    }
364
365
366    @Override
367    public double castToDoubleValue() throws ExpressionException {
368        throw new ExpressionException("Can't cast XML Node to a number value");
369    }
370    
371    @Override
372    public double castToDoubleValue(double defaultValue) {
373        return defaultValue;
374    }
375
376
377    @Override
378    public DateTime castToDateTime() throws ExpressionException {
379        throw new ExpressionException("Can't cast XML Node to a Date");
380    }
381    
382    @Override
383    public DateTime castToDateTime(DateTime defaultValue) {
384        return defaultValue;
385    }
386
387        @Override
388        public int compareTo(boolean b) throws PageException {
389                return Operator.compare(castToString(), b);
390        }
391
392        @Override
393        public int compareTo(DateTime dt) throws PageException {
394                return Operator.compare(castToString(), (Date)dt);
395        }
396
397        @Override
398        public int compareTo(double d) throws PageException {
399                return Operator.compare(castToString(), d);
400        }
401
402        @Override
403        public int compareTo(String str) throws PageException {
404                return Operator.compare(castToString(), str);
405        }
406
407    public String getBaseURI() {
408        // not supported
409        return null;
410    }
411
412    public short compareDocumentPosition(Node other) throws DOMException {
413        // not supported
414        return -1;
415    }
416
417    public void setTextContent(String textContent) throws DOMException {
418        //TODO  not supported
419        throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR,"this method is not supported");
420    }
421
422    public boolean isSameNode(Node other) {
423        return this==other;
424    }
425
426    public String lookupPrefix(String namespaceURI) {
427//      TODO not supported
428        return null;
429    }
430
431    public boolean isDefaultNamespace(String namespaceURI) {
432//      TODO not supported
433        return false;
434    }
435
436    public String lookupNamespaceURI(String prefix) {
437//      TODO not supported
438        return null;
439    }
440
441    public boolean isEqualNode(Node node) {
442//      TODO not supported
443        return this==node;
444    }
445
446    public Object getFeature(String feature, String version) {
447        // TODO not supported
448        return null;
449    }
450
451    public Object getUserData(String key) {
452        // dynamic load to support jre 1.4 and 1.5
453                try {
454                        Method m = node.getClass().getMethod("getUserData", new Class[]{key.getClass()});
455                        return m.invoke(node, new Object[]{key});
456                } 
457                catch (Exception e) {
458                        throw new PageRuntimeException(Caster.toPageException(e));
459                }
460    }
461
462        public String getTextContent() throws DOMException {
463        // dynamic load to support jre 1.4 and 1.5
464                try {
465                        Method m = node.getClass().getMethod("getTextContent", new Class[]{});
466                        return Caster.toString(m.invoke(node, ArrayUtil.OBJECT_EMPTY));
467                } 
468                catch (Exception e) {
469                        throw new PageRuntimeException(Caster.toPageException(e));
470                }
471        }
472
473        public Object setUserData(String key, Object data, UserDataHandler handler) {
474        // dynamic load to support jre 1.4 and 1.5
475                try {
476                        Method m = node.getClass().getMethod("setUserData", new Class[]{key.getClass(),data.getClass(),handler.getClass()});
477                        return m.invoke(node, new Object[]{key,data,handler});
478                } 
479                catch (Exception e) {
480                        throw new PageRuntimeException(Caster.toPageException(e));
481                }
482        }
483
484        public boolean isCaseSensitive() {
485                return caseSensitive;
486        }
487        
488        public boolean equals(Object obj) {
489                if(!(obj instanceof XMLNodeStruct)) 
490                        return super.equals(obj);
491                XMLNodeStruct other = ((XMLNodeStruct)obj);
492                return other.caseSensitive=caseSensitive && other.node.equals(node);
493        }
494        
495        
496
497}