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 }