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