1 38 package com.gargoylesoftware.htmlunit.html; 39 40 import java.beans.PropertyChangeListener ; 41 import java.beans.PropertyChangeSupport ; 42 import java.io.PrintWriter ; 43 import java.io.StringWriter ; 44 import java.util.Iterator ; 45 import java.util.NoSuchElementException ; 46 47 import org.apache.commons.logging.Log; 48 import org.apache.commons.logging.LogFactory; 49 50 import com.gargoylesoftware.htmlunit.Assert; 51 import com.gargoylesoftware.htmlunit.javascript.SimpleScriptable; 52 53 66 public abstract class DomNode implements Cloneable { 67 70 public static final short DOCUMENT_NODE = 0; 71 74 public static final short ELEMENT_NODE = 1; 75 78 public static final short TEXT_NODE = 3; 79 80 83 public static final String READY_STATE_UNINITIALIZED = "uninitialized"; 84 85 88 public static final String READY_STATE_LOADING = "loading"; 89 90 93 public static final String READY_STATE_COMPLETE = "complete"; 94 95 96 97 private final HtmlPage htmlPage_; 98 99 100 private DomNode parent_; 101 102 106 private DomNode previousSibling_; 107 108 111 private DomNode nextSibling_; 112 113 114 private DomNode firstChild_; 115 116 123 private Object scriptObject_; 124 125 129 private String readyState_ = READY_STATE_UNINITIALIZED; 131 132 private PropertyChangeSupport propertyChangeSupport_ = null; 135 136 137 public static final String PROPERTY_ELEMENT = "element"; 138 139 144 protected DomNode(final HtmlPage htmlPage) { 145 readyState_ = READY_STATE_LOADING; htmlPage_ = htmlPage; 147 } 148 149 150 155 public HtmlPage getPage() { 156 return htmlPage_; 157 } 158 159 166 public void setScriptObject( final Object scriptObject ) { 167 scriptObject_ = scriptObject; 168 } 169 170 175 public DomNode getLastChild() { 176 if(firstChild_ != null) { 177 return firstChild_.previousSibling_; 179 } 180 else { 181 return null; 182 } 183 } 184 185 189 public DomNode getParentNode() { 190 return parent_; 191 } 192 193 197 protected void setParentNode(DomNode parent) { 198 parent_ = parent; 199 } 200 201 202 206 public DomNode getPreviousSibling() { 207 if(parent_ == null || this == parent_.firstChild_) { 208 return null; 210 } 211 else { 212 return previousSibling_; 213 } 214 } 215 216 219 public DomNode getNextSibling() { 220 return nextSibling_; 221 } 222 223 226 public DomNode getFirstChild() { 227 return firstChild_; 228 } 229 230 231 protected void setPreviousSibling(DomNode previous) { 232 previousSibling_ = previous; 233 } 234 235 236 protected void setNextSibling(DomNode next) { 237 nextSibling_ = next; 238 } 239 240 244 public abstract short getNodeType(); 245 246 250 public abstract String getNodeName(); 251 252 253 261 public String asText() { 262 String text = getChildrenAsText(); 263 264 text = text.replace((char)160,' '); 266 267 text = reduceWhitespace(text); 269 return text; 270 } 271 272 273 280 protected final String getChildrenAsText() { 281 final StringBuffer buffer = new StringBuffer (); 282 final Iterator childIterator = getChildIterator(); 283 284 if(!childIterator.hasNext()) { 285 return ""; 286 } 287 while(childIterator.hasNext()) { 288 final DomNode node = (DomNode)childIterator.next(); 289 buffer.append(node.asText()); 290 } 291 292 return buffer.toString(); 293 } 294 295 296 302 private static String reduceWhitespace( final String text ) { 303 final StringBuffer buffer = new StringBuffer ( text.length() ); 304 final int length = text.length(); 305 boolean whitespace = false; 306 for( int i = 0; i < length; ++i) { 307 final char ch = text.charAt(i); 308 if( whitespace ) { 309 if( Character.isWhitespace(ch) == false ) { 310 buffer.append(ch); 311 whitespace = false; 312 } 313 } 314 else { 315 if( Character.isWhitespace(ch) ) { 316 whitespace = true; 317 buffer.append(' '); 318 } 319 else { 320 buffer.append(ch); 321 } 322 } 323 } 324 return buffer.toString().trim(); 325 } 326 327 331 protected final Log getLog() { 332 return LogFactory.getLog(getClass()); 333 } 334 335 341 public String asXml() { 342 343 final StringWriter stringWriter = new StringWriter (); 344 final PrintWriter printWriter = new PrintWriter (stringWriter); 345 printXml("", printWriter); 346 printWriter.close(); 347 return stringWriter.toString(); 348 } 349 350 356 protected void printXml( final String indent, final PrintWriter printWriter ) { 357 358 printWriter.println(indent+this); 359 printChildrenAsXml( indent, printWriter ); 360 } 361 362 368 protected void printChildrenAsXml( final String indent, final PrintWriter printWriter ) { 369 DomNode child = getFirstChild(); 370 while (child != null) { 371 child.printXml(indent+" ", printWriter); 372 child = child.getNextSibling(); 373 } 374 } 375 376 380 public String getNodeValue() { 381 return null; 382 } 383 384 387 public void setNodeValue(final String x) { 388 } 390 391 399 public DomNode cloneNode(final boolean deep) { 400 401 final DomNode newnode; 402 try { 403 newnode = (DomNode) clone(); 404 } 405 catch( final CloneNotSupportedException e ) { 406 throw new IllegalStateException ("Clone not supported for node ["+this+"]"); 407 } 408 409 newnode.parent_ = null; 410 newnode.nextSibling_ = null; 411 newnode.previousSibling_ = null; 412 newnode.firstChild_ = null; 413 newnode.scriptObject_ = null; 414 415 if (deep) { 417 for (DomNode child = firstChild_; child != null; child = child.nextSibling_) { 418 newnode.appendChild(child.cloneNode(true)); 419 } 420 } 421 return newnode; 422 } 423 424 431 public Object getScriptObject() { 432 if (scriptObject_ == null) { 433 scriptObject_ = ((SimpleScriptable) getPage().getScriptObject()).makeScriptableFor(this); 434 } 435 return scriptObject_; 436 } 437 438 443 public DomNode appendChild(final DomNode node) { 444 445 if(node != this) { 447 node.basicRemove(); 448 } 449 if(firstChild_ == null) { 450 firstChild_ = node; 451 firstChild_.previousSibling_ = node; 452 } 453 else { 454 DomNode last = getLastChild(); 455 456 last.nextSibling_ = node; 457 node.previousSibling_ = last; 458 node.nextSibling_ = null; firstChild_.previousSibling_ = node; } 461 node.parent_ = this; 462 463 getHtmlPage().notifyNodeAdded(node); 464 465 return node; 466 } 467 468 472 private HtmlPage getHtmlPage() { 473 if (this instanceof HtmlPage) { 474 return (HtmlPage) this; 475 } 476 else { 477 return htmlPage_; 478 } 479 } 480 481 488 public void insertBefore(final DomNode newNode) throws IllegalStateException { 489 490 if(previousSibling_ == null) { 491 throw new IllegalStateException (); 492 } 493 494 if(newNode != this) { 496 newNode.basicRemove(); 497 } 498 499 if(parent_.firstChild_ == this) { 500 parent_.firstChild_ = newNode; 501 } 502 else { 503 previousSibling_.nextSibling_ = newNode; 504 } 505 newNode.previousSibling_ = previousSibling_; 506 newNode.nextSibling_ = this; 507 previousSibling_ = newNode; 508 newNode.parent_ = parent_; 509 510 getHtmlPage().notifyNodeAdded(newNode); 511 } 512 513 517 public void remove() throws IllegalStateException { 518 if(previousSibling_ == null) { 519 throw new IllegalStateException (); 520 } 521 basicRemove(); 522 523 getHtmlPage().notifyNodeRemoved(this); 524 } 525 526 529 private void basicRemove() { 530 if(parent_ != null && parent_.firstChild_ == this) { 531 parent_.firstChild_ = nextSibling_; 532 } 533 else if(previousSibling_ != null && previousSibling_.nextSibling_ == this) { 534 previousSibling_.nextSibling_ = nextSibling_; 535 } 536 if(nextSibling_ != null && nextSibling_.previousSibling_ == this) { 537 nextSibling_.previousSibling_ = previousSibling_; 538 } 539 if(parent_ != null && this == parent_.getLastChild()) { 540 parent_.firstChild_.previousSibling_ = previousSibling_; 541 } 542 543 nextSibling_ = null; 544 previousSibling_ = null; 545 parent_ = null; 546 } 547 548 554 public void replace(final DomNode newNode) throws IllegalStateException { 555 insertBefore(newNode); 556 remove(); 557 } 558 559 562 public Iterator getChildIterator() { 563 return new ChildIterator(); 564 } 565 566 570 public final synchronized void addPropertyChangeListener( 571 final PropertyChangeListener listener ) { 572 573 Assert.notNull("listener", listener); 574 if( propertyChangeSupport_ == null ) { 575 propertyChangeSupport_ = new PropertyChangeSupport (this); 576 } 577 propertyChangeSupport_.addPropertyChangeListener(listener); 578 } 579 580 584 public final synchronized void removePropertyChangeListener( 585 final PropertyChangeListener listener ) { 586 587 Assert.notNull("listener", listener); 588 if( propertyChangeSupport_ != null ) { 589 propertyChangeSupport_.removePropertyChangeListener(listener); 590 } 591 } 592 593 599 protected final synchronized void firePropertyChange( 600 final String propertyName, final Object oldValue, final Object newValue ) { 601 602 if( propertyChangeSupport_ != null ) { 603 propertyChangeSupport_.firePropertyChange(propertyName, oldValue, newValue); 604 } 605 } 606 607 610 protected class ChildIterator implements Iterator { 611 612 private DomNode nextNode_ = firstChild_; 613 private DomNode currentNode_ = null; 614 615 616 public boolean hasNext() { 617 return nextNode_ != null; 618 } 619 620 621 public Object next() { 622 if(nextNode_ != null) { 623 currentNode_ = nextNode_; 624 nextNode_ = nextNode_.nextSibling_; 625 return currentNode_; 626 } 627 else { 628 throw new NoSuchElementException (); 629 } 630 } 631 632 633 public void remove() { 634 if(currentNode_ == null) { 635 throw new IllegalStateException (); 636 } 637 currentNode_.remove(); 638 } 639 } 640 641 642 647 public DescendantElementsIterator getAllHtmlChildElements() { 648 return new DescendantElementsIterator(); 649 } 650 651 654 protected class DescendantElementsIterator implements Iterator { 655 656 private HtmlElement nextElement_ = getFirstChildElement(DomNode.this); 657 658 659 public boolean hasNext() { 660 return nextElement_ != null; 661 } 662 663 664 public Object next() { 665 return nextElement(); 666 } 667 668 669 public void remove() { 670 if(nextElement_ == null) { 671 throw new IllegalStateException (); 672 } 673 if(nextElement_.getPreviousSibling() != null) { 674 nextElement_.getPreviousSibling().remove(); 675 } 676 } 677 678 679 public HtmlElement nextElement() { 680 final HtmlElement result = nextElement_; 681 setNextElement(); 682 return result; 683 } 684 685 private void setNextElement() { 686 HtmlElement next = getFirstChildElement(nextElement_); 687 if( next == null ) { 688 next = getNextSibling(nextElement_); 689 } 690 if( next == null ) { 691 next = getNextElementUpwards(nextElement_); 692 } 693 nextElement_ = next; 694 } 695 696 private HtmlElement getNextElementUpwards( final DomNode startingNode ) { 697 if( startingNode == DomNode.this) { 698 return null; 699 } 700 701 final DomNode parent = startingNode.getParentNode(); 702 if( parent == DomNode.this ) { 703 return null; 704 } 705 706 DomNode next = parent.getNextSibling(); 707 while( next != null && next instanceof HtmlElement == false ) { 708 next = next.getNextSibling(); 709 } 710 711 if( next == null ) { 712 return getNextElementUpwards(parent); 713 } 714 else { 715 return (HtmlElement)next; 716 } 717 } 718 719 private HtmlElement getFirstChildElement(final DomNode parent) { 720 DomNode node = parent.getFirstChild(); 721 while( node != null && node instanceof HtmlElement == false ) { 722 node = node.getNextSibling(); 723 } 724 return (HtmlElement)node; 725 } 726 727 private HtmlElement getNextSibling( final HtmlElement element) { 728 DomNode node = element.getNextSibling(); 729 while( node != null && node instanceof HtmlElement == false ) { 730 node = node.getNextSibling(); 731 } 732 return (HtmlElement)node; 733 } 734 } 735 736 740 public String getReadyState() { 741 return readyState_; 742 } 743 744 748 public void setReadyState(final String state) { 749 readyState_ = state; 750 } 751 752 756 public void removeAllChildren() { 757 if (getFirstChild() == null) { 758 return; 759 } 760 final Iterator it = getChildIterator(); 761 DomNode child; 762 while (it.hasNext()) { 763 child = (DomNode) it.next(); 764 child.removeAllChildren(); 765 it.remove(); 766 } 767 } 768 } 769 | Popular Tags |