001    package railo.runtime.text.xml;
002    
003    import java.util.ArrayList;
004    import java.util.Iterator;
005    import java.util.List;
006    
007    import org.w3c.dom.Document;
008    import org.w3c.dom.Node;
009    import org.w3c.dom.NodeList;
010    
011    import railo.runtime.PageContext;
012    import railo.runtime.dump.DumpData;
013    import railo.runtime.dump.DumpProperties;
014    import railo.runtime.dump.DumpTable;
015    import railo.runtime.dump.DumpTablePro;
016    import railo.runtime.dump.DumpUtil;
017    import railo.runtime.dump.SimpleDumpData;
018    import railo.runtime.exp.ExpressionException;
019    import railo.runtime.exp.PageException;
020    import railo.runtime.op.Caster;
021    import railo.runtime.text.xml.struct.XMLObject;
022    import railo.runtime.text.xml.struct.XMLStruct;
023    import railo.runtime.type.Collection;
024    import railo.runtime.type.dt.DateTime;
025    import railo.runtime.type.util.ArraySupport;
026    import railo.runtime.type.util.ArrayUtil;
027    import railo.runtime.type.util.StructUtil;
028    import railo.runtime.util.ArrayIterator;
029    
030    /**
031     * 
032     */
033    public final class XMLNodeList extends ArraySupport implements NodeList, XMLObject{
034    
035            private boolean caseSensitive;
036            private Document doc;
037            private Node parent;
038            private String filter;
039            
040            /**
041             * @param parent Parent Node
042             * @param caseSensitive
043             */
044        public XMLNodeList(Node parent, boolean caseSensitive) {
045            this(parent,caseSensitive,null);
046        }
047        public XMLNodeList(Node parent, boolean caseSensitive, String filter) {
048            if(parent instanceof XMLStruct) {
049                XMLStruct xmlNode = ((XMLStruct)parent);
050                this.parent=xmlNode.toNode();
051                this.caseSensitive=xmlNode.getCaseSensitive();
052            }
053            else {
054                this.parent=parent;
055                this.caseSensitive=caseSensitive;
056            }
057            this.doc=this.parent.getOwnerDocument();
058            this.filter=filter;
059        }
060    
061            /**
062             * @see org.w3c.dom.NodeList#getLength()
063             */
064            public int getLength() {
065                    return XMLUtil.childNodesLength(parent,Node.ELEMENT_NODE,caseSensitive,filter);
066            }
067    
068            /**
069             * @see org.w3c.dom.NodeList#item(int)
070             */
071            public Node item(int index) {
072                    return XMLCaster.toXMLStruct(getChildNode(index),caseSensitive);
073            }
074    
075            /**
076             * @see railo.runtime.type.Collection#size()
077             */
078            public int size() {
079                    return getLength();
080            }
081    
082            /**
083             * @see railo.runtime.type.Collection#keysAsString()
084             */
085            public String[] keysAsString() {
086                    String[] keys=new String[getLength()];
087                    for(int i=1;i<=keys.length;i++) {
088                            keys[i-1]=i+"";
089                    }
090                    return keys;
091            }
092    
093            /**
094             * @see railo.runtime.type.Collection#keys()
095             */
096            public Collection.Key[] keys() {
097                    return StructUtil.toCollectionKeys(keysAsString());
098            }
099            
100            /**
101             * @see railo.runtime.type.Array#intKeys()
102             */
103            public int[] intKeys() {
104                    int[] keys=new int[getLength()];
105                    for(int i=1;i<=keys.length;i++) {
106                            keys[i-1]=i;
107                    }
108                    return keys;
109            }
110            
111            public Object removeEL(Collection.Key key) {
112                    return removeEL(Caster.toIntValue(key.getString(),-1));
113            }
114    
115    
116            /**
117             *
118             * @see railo.runtime.type.Collection#remove(railo.runtime.type.Collection.Key)
119             */
120            public Object remove(Collection.Key key) throws PageException {
121                    return removeE(Caster.toIntValue(key.getString()));
122            }
123    
124            /**
125             * @see railo.runtime.type.Array#removeEL(int)
126             */
127            public Object removeEL(int index) {
128                    int len=size();
129                    if(index<1 || index>len) return null;
130                    try {
131                            return XMLCaster.toXMLStruct(parent.removeChild(XMLCaster.toRawNode(item(index-1))),caseSensitive);
132                    } 
133                    catch (Exception e) {
134                            return null;
135                    }
136            }
137            
138            /**
139             * @see railo.runtime.type.Array#removeE(int)
140             */
141            public Object removeE(int index) throws PageException {
142                    int len=size();
143                    if(index<1 || index>len)
144                            throw new ExpressionException("can't remove value form XML Node List at index "+index+
145                                    ", valid indexes goes from 1 to "+len);
146                    return XMLCaster.toXMLStruct(parent.removeChild(XMLCaster.toRawNode(item(index-1))),caseSensitive);
147            }
148    
149            /**
150             * @see railo.runtime.type.Collection#clear()
151             */
152            public void clear() {
153                    Node[] nodes=getChildNodesAsArray();
154                    for(int i=0;i<nodes.length;i++) {
155                            parent.removeChild(XMLCaster.toRawNode(nodes[i]));
156                    }
157            }
158    
159    
160            /**
161             * @see railo.runtime.type.Collection#get(java.lang.String)
162             */
163            public Object get(String key) throws ExpressionException {
164                    return getE(Caster.toIntValue(key));
165            }
166    
167            /**
168             * @see railo.runtime.type.Collection#get(railo.runtime.type.Collection.Key)
169             */
170            public Object get(Collection.Key key) throws ExpressionException {
171                    return get(key.getString());
172            }
173            
174            /**
175             * @see railo.runtime.type.Array#getE(int)
176             */
177            public Object getE(int key) throws ExpressionException {
178                    Object rtn= item(key-1);
179                    if(rtn==null) throw new ExpressionException("invalid index ["+key+"] for XML Node List , indexes goes from [0-"+size()+"]");
180                    return rtn;
181            }
182    
183            /**
184             * @see railo.runtime.type.Collection#get(java.lang.String, java.lang.Object)
185             */
186            public Object get(String key, Object defaultValue) {
187                    int index=Caster.toIntValue(key,Integer.MIN_VALUE);
188                    if(index==Integer.MIN_VALUE) return defaultValue;
189                return get(index,defaultValue);
190            }
191    
192            /**
193             *
194             * @see railo.runtime.type.Collection#get(railo.runtime.type.Collection.Key, java.lang.Object)
195             */
196            public Object get(Collection.Key key, Object defaultValue) {
197                    return get(key.getString(),defaultValue);
198            }
199    
200            /**
201             *
202             * @see railo.runtime.type.Array#get(int, java.lang.Object)
203             */
204            public Object get(int key, Object defaultValue) {
205                    Object rtn= item(key-1);
206                    if(rtn==null) return defaultValue;
207                    return rtn;
208            }
209    
210            /**
211             * @see railo.runtime.type.Collection#set(java.lang.String, java.lang.Object)
212             */
213            public Object set(String key, Object value) throws PageException {
214                    return setE(Caster.toIntValue(key),value);
215            }
216    
217            /**
218             *
219             * @see railo.runtime.type.Collection#set(railo.runtime.type.Collection.Key, java.lang.Object)
220             */
221            public Object set(Collection.Key key, Object value) throws PageException {
222                    return set(key.getString(), value);
223            }
224            
225            /**
226             * @see railo.runtime.type.Array#setE(int, java.lang.Object)
227             */
228            public Object setE(int index, Object value) throws PageException {
229                    // check min Index
230                            if(index<1)
231                                    throw new ExpressionException("invalid index ["+index+"] to set a child node, valid indexes start at 1");
232                    
233                    Node[] nodes=getChildNodesAsArray();
234                    
235                    // if index Greater len append
236                            if(index>nodes.length) return append(value);
237                    
238                    // remove all children
239                    clear();
240                    
241                    // set all children before new Element
242                    for(int i=1;i<index;i++) {
243                            append(nodes[i-1]);
244                    }
245                    
246                    // set new Element
247                    append(XMLCaster.toNode(doc,value));
248                    
249                    // set all after new Element
250                    for(int i=index;i<nodes.length;i++) {
251                            append(nodes[i]);
252                    }
253                    
254                    return value;
255            }
256    
257            /**
258             * @see railo.runtime.type.Collection#setEL(java.lang.String, java.lang.Object)
259             */
260            public Object setEL(String key, Object value) {
261                    int index=Caster.toIntValue(key,Integer.MIN_VALUE);
262                    if(index==Integer.MIN_VALUE) return null;
263                    return setEL(index,value);
264            }
265    
266            /**
267             *
268             * @see railo.runtime.type.Collection#setEL(railo.runtime.type.Collection.Key, java.lang.Object)
269             */
270            public Object setEL(Collection.Key key, Object value) {
271                    return setEL(key.getString(), value);
272            }
273            
274            /**
275             * @see railo.runtime.type.Array#setEL(int, java.lang.Object)
276             */
277            public Object setEL(int index, Object value) {
278                    try {
279                            return setE(index,value);
280                    } catch (PageException e) {
281                            return null;
282                    }
283            }
284    
285            /**
286             * @see railo.runtime.type.Collection#keyIterator()
287             */
288            public Iterator keyIterator() {
289                    return new ArrayIterator(keysAsString());
290            }
291            
292            public Iterator valueIterator() {
293                    Object[] values=new Object[getLength()];
294                    for(int i=0;i<values.length;i++) {
295                            values[i]=item(i);
296                    }
297                    return new ArrayIterator(values);
298            }
299            
300            /**
301             *
302             * @see railo.runtime.type.Iteratorable#iterator()
303             */
304            public Iterator iterator() {
305                    return valueIterator();
306            }
307    
308            /**
309             * @see railo.runtime.dump.Dumpable#toDumpData(railo.runtime.PageContext, int)
310             */
311            public DumpData toDumpData(PageContext pageContext, int maxlevel, DumpProperties dp) {
312                    maxlevel--;
313                    DumpTable table = new DumpTablePro("xml","#cc9999","#ffffff","#000000");
314                    table.setTitle("Array (XML Node List)");
315                    int len=size();
316                    
317                    for(int i=1;i<=len;i++) {
318                            table.appendRow(1,new SimpleDumpData(i),DumpUtil.toDumpData(item(i-1), pageContext,maxlevel,dp));
319                    }
320                    return table;
321            }
322            
323            /**
324             * @see railo.runtime.type.Array#append(java.lang.Object)
325             */
326            public Object append(Object o) throws PageException {
327                    return parent.appendChild(XMLCaster.toNode(doc,o));
328            }
329            
330            public Object appendEL(Object o) {
331                    try {
332                            return append(o);
333                    } catch (Exception e) {
334                            return null;
335                    }
336            }
337            
338            /**
339             * @see java.lang.Object#clone()
340             */
341            public Object clone() {
342                    return duplicate(true);
343            }
344    
345    
346            /**
347             * @see railo.runtime.type.Collection#duplicate(boolean)
348             */
349            public Collection duplicate(boolean deepCopy) {
350                    return new XMLNodeList(parent.cloneNode(deepCopy),caseSensitive);
351            }
352            
353    
354            /**
355             * @see railo.runtime.type.Array#getDimension()
356             */
357            public int getDimension() {
358                    return 1;
359            }
360            
361            /**
362             * @see railo.runtime.type.Array#insert(int, java.lang.Object)
363             */
364            public boolean insert(int index, Object value) throws PageException {
365                    // check min Index
366                    if(index<1)
367                            throw new ExpressionException("invalid index ["+index+"] to insert a child node, valid indexes start at 1");
368            
369                    Node[] nodes=getChildNodesAsArray();
370                    
371                    // if index Greater len append
372                            if(index>nodes.length) {
373                                    append(value);
374                                    return true;
375                            }
376                    
377                    // remove all children
378                    clear();
379                    
380                    // set all children before new Element
381                    for(int i=1;i<index;i++) {
382                            append(nodes[i-1]);
383                    }
384                    
385                    // set new Element
386                    append(XMLCaster.toNode(doc,value));
387                    
388                    // set all after new Element
389                    for(int i=index;i<=nodes.length;i++) {
390                            append(nodes[i-1]);
391                    }
392                    
393                    return true;
394            }
395            
396            /**
397             * @see railo.runtime.type.Array#prepend(java.lang.Object)
398             */
399            public Object prepend(Object o) throws PageException {
400                    
401                    Node[] nodes=getChildNodesAsArray();
402                    
403                    // remove all children
404                    clear();
405                    
406                    // set new Element
407                    append(XMLCaster.toNode(doc,o));
408                    
409                    // set all after new Element
410                    for(int i=0;i<nodes.length;i++) {
411                            append(nodes[i]);
412                    }
413                    return o;
414            }
415            
416            /**
417             * @see railo.runtime.type.Array#resize(int)
418             */
419            public void resize(int to) throws ExpressionException {
420                    if(to>size())throw new ExpressionException("can't resize a XML Node List Array with empty Elements");
421            }
422            
423            /**
424             * @see railo.runtime.type.Array#sort(java.lang.String, java.lang.String)
425             */
426            public void sort(String sortType, String sortOrder)
427                            throws ExpressionException {
428                    throw new ExpressionException("can't sort a XML Node List Array","sorttype:"+sortType+";sortorder:"+sortOrder);
429            }
430    
431            /**
432             * @see railo.runtime.type.Array#toArray()
433             */
434            public Object[] toArray() {
435                    return getChildNodesAsArray();
436            }
437            
438            /**
439             * @see railo.runtime.type.Array#toArrayList()
440             */
441            public ArrayList toArrayList() {
442                    Object[] arr=toArray();
443                    ArrayList list=new ArrayList();
444                    for(int i=0;i>arr.length;i++) {
445                            list.add(arr[i]);
446                    }
447                    return list;
448            }
449            
450            /**
451             * @return returns a output from the content as plain Text
452             */
453            public String toPlain() {
454                    StringBuffer sb=new StringBuffer();
455                    int length=size();
456                    for(int i=1;i<=length;i++) {
457                            sb.append(i);
458                            sb.append(": ");
459                            sb.append(get(i,null));
460                            sb.append("\n");
461                    }
462                    return sb.toString();
463            }
464    
465        private NodeList getChildNodes() {
466                    return XMLUtil.getChildNodes(parent,Node.ELEMENT_NODE,caseSensitive,filter);
467            }
468        
469        private Node getChildNode(int index) {
470                    return XMLUtil.getChildNode(parent,Node.ELEMENT_NODE,caseSensitive,filter,index);
471            }
472            
473            private Node[] getChildNodesAsArray() {
474                    return XMLUtil.getChildNodesAsArray(parent,Node.ELEMENT_NODE,caseSensitive,filter);
475            }
476    
477        /**
478         * @see railo.runtime.type.Collection#containsKey(java.lang.String)
479         */
480        public boolean containsKey(String key) {
481            return get(key,null)!=null;
482        }   
483    
484            /**
485             *
486             * @see railo.runtime.type.Collection#containsKey(railo.runtime.type.Collection.Key)
487             */
488            public boolean containsKey(Collection.Key key) {
489            return get(key,null)!=null;
490            }
491    
492        /**
493         * @see railo.runtime.type.Array#containsKey(int)
494         */
495        public boolean containsKey(int key) {
496            return get(key,null)!=null;
497        }
498    
499        /**
500         * @see railo.runtime.text.xml.struct.XMLObject#getCaseSensitive()
501         */
502        public boolean getCaseSensitive() {
503            return caseSensitive;
504        }      
505        
506        /**
507         * @see railo.runtime.op.Castable#castToString()
508         */
509        public String castToString() throws ExpressionException {
510            throw new ExpressionException("Can't cast XML NodeList to String");
511        }
512        
513            /**
514             * @see railo.runtime.type.util.StructSupport#castToString(java.lang.String)
515             */
516            public String castToString(String defaultValue) {
517                    return defaultValue;
518            }
519    
520        /**
521         * @see railo.runtime.op.Castable#castToBooleanValue()
522         */
523        public boolean castToBooleanValue() throws ExpressionException {
524            throw new ExpressionException("Can't cast XML NodeList to a boolean value");
525        }
526        
527        /**
528         * @see railo.runtime.op.Castable#castToBoolean(java.lang.Boolean)
529         */
530        public Boolean castToBoolean(Boolean defaultValue) {
531            return defaultValue;
532        }
533    
534    
535        /**
536         * @see railo.runtime.op.Castable#castToDoubleValue()
537         */
538        public double castToDoubleValue() throws ExpressionException {
539            throw new ExpressionException("Can't cast XML NodeList to a number value");
540        }
541        
542        /**
543         * @see railo.runtime.op.Castable#castToDoubleValue(double)
544         */
545        public double castToDoubleValue(double defaultValue) {
546            return defaultValue;
547        }
548    
549    
550        /**
551         * @see railo.runtime.op.Castable#castToDateTime()
552         */
553        public DateTime castToDateTime() throws ExpressionException {
554            throw new ExpressionException("Can't cast XML NodeList to a Date");
555        }
556        
557        /**
558         * @see railo.runtime.op.Castable#castToDateTime(railo.runtime.type.dt.DateTime)
559         */
560        public DateTime castToDateTime(DateTime defaultValue) {
561            return defaultValue;
562        }
563    
564            /**
565             * @see railo.runtime.op.Castable#compare(boolean)
566             */
567            public int compareTo(boolean b) throws ExpressionException {
568                    throw new ExpressionException("can't compare XML NodeList with a boolean value");
569            }
570    
571            /**
572             * @see railo.runtime.op.Castable#compareTo(railo.runtime.type.dt.DateTime)
573             */
574            public int compareTo(DateTime dt) throws PageException {
575                    throw new ExpressionException("can't compare XML NodeList with a DateTime Object");
576            }
577    
578            /**
579             * @see railo.runtime.op.Castable#compareTo(double)
580             */
581            public int compareTo(double d) throws PageException {
582                    throw new ExpressionException("can't compare XML NodeList with a numeric value");
583            }
584    
585            /**
586             * @see railo.runtime.op.Castable#compareTo(java.lang.String)
587             */
588            public int compareTo(String str) throws PageException {
589                    throw new ExpressionException("can't compare XML NodeList with a String");
590            }
591    
592            /**
593             * @see railo.runtime.type.Sizeable#sizeOf()
594             */
595            public long sizeOf() {
596                    return ArrayUtil.sizeOf((List)this);
597            }
598    }