1 19 20 package org.netbeans.modules.xml.xdm.nodes; 21 import java.util.ArrayList ; 22 import java.util.Collections ; 23 import java.util.HashMap ; 24 import java.util.LinkedHashMap ; 25 import java.util.List ; 26 import java.util.Map ; 27 import javax.xml.XMLConstants ; 28 import org.netbeans.modules.xml.spi.dom.NodeListImpl; 30 import org.netbeans.modules.xml.xdm.XDMModel; 31 import org.netbeans.modules.xml.xdm.visitor.PathFromRootVisitor; 32 import org.w3c.dom.*; 33 34 38 public abstract class NodeImpl implements Node, Cloneable { 39 40 public static final String XMLNS = "xmlns"; 42 43 private boolean inTree; 44 45 46 private XDMModel model; 47 48 49 private int id; 50 51 52 private List <Token> tokens; 53 54 55 private List <Node> children; 56 57 58 private List <Attribute> attributes = null; 59 60 61 NodeImpl() { 62 model = null; 63 inTree = false; 64 id = -1; 65 } 66 67 71 public final int getId() { 72 return id; 73 } 74 75 79 private void setId(int nodeId) { 80 id = nodeId; 81 } 82 83 @Override 84 public int hashCode() { 85 return (int) getId(); 86 } 87 88 92 public final boolean isInTree() { 93 return inTree && getModel()!=null; 94 } 95 96 99 public void addedToTree(XDMModel model) { 100 if (!isInTree()) { 101 inTree = true; 102 if(getModel() != model) { 103 setModel(model); 104 setId(model.getNextNodeId()); 105 } else { 106 if(getId() == -1) 107 setId(model.getNextNodeId()); 108 } 109 for (Node n: getChildren()) { 110 n.addedToTree(model); 111 } 112 for (Node n: getAttributesForRead()) { 113 n.addedToTree(model); 114 } 115 } 116 } 117 118 private static interface UniqueId { 119 int nextId(); 120 } 121 122 private UniqueId createUniqueId() { 123 return new UniqueId() { 124 private int lastId = -1; 125 public int nextId() { 126 return ++lastId; 127 } 128 }; 129 } 130 131 134 public void assignNodeIdRecursively() { 135 assignNodeId(createUniqueId()); 136 } 137 138 public void assignNodeId(UniqueId id) { 139 assert ! isInTree(); 140 setId(id.nextId()); 141 for (Node n: getChildren()) { 142 ((NodeImpl)n).assignNodeId(id); 143 } 144 for (Node n: getAttributesForRead()) { 145 ((NodeImpl)n).assignNodeId(id); 146 } 147 } 148 149 public void assignNodeId(int id) { 150 assert ! isInTree(); 151 setId(id); 152 for (Node n: getChildren()) { 153 ((NodeImpl)n).assignNodeId(id); 154 } 155 for (Node n: getAttributesForRead()) { 156 ((NodeImpl)n).assignNodeId(id); 157 } 158 } 159 160 protected XDMModel getModel() { 161 return model; 162 } 163 164 private void setModel(XDMModel xdmModel) { 165 assert xdmModel != null; 166 model = xdmModel; 167 } 168 169 173 public boolean isEquivalentNode(Node node){ 174 return (this==node) || getClass().isInstance(node) && 175 getModel()!=null && getModel()==((NodeImpl)node).getModel() && 176 getId() != -1 && getId()==node.getId(); 177 } 178 179 183 final void checkNotInTree() { 184 if (isInTree()) { 185 throw new IllegalStateException ("mutations cannot occur on nodes already added to a tree"); 186 } 187 } 188 189 public boolean isSupported(String feature, String version) { 191 return "1.0".equals(version); 192 } 193 194 203 public Node clone(boolean cloneContent, boolean cloneAttributes, boolean cloneChildren) { 204 try { 205 NodeImpl clone = (NodeImpl)super.clone(); 206 clone.inTree = false; 207 if(cloneContent) { 208 clone.setTokens(new ArrayList <Token>(getTokens())); 209 } else { 210 clone.setTokens(getTokens()); 211 } 212 if(cloneAttributes) { 213 clone.setAttributes(new ArrayList <Attribute>(getAttributesForRead())); 214 } else { 215 clone.setAttributes(getAttributesForRead()); 216 } 217 if(cloneChildren) { 218 clone.setChildren(new ArrayList <Node>(getChildren())); 219 } else { 220 clone.setChildren(getChildren()); 221 } 222 return clone; 223 } catch (CloneNotSupportedException cne) { 224 throw new RuntimeException (cne); 225 } 226 } 227 233 public Node cloneNode(boolean deep) { 234 return cloneNode(deep, true); 235 } 236 237 public Node cloneNode(boolean deep, boolean cloneNamespacePrefix) { 238 Document root = isInTree() ? (Document) getOwnerDocument() : null; 239 Map <Integer ,String > allNamespaces = null; 240 if (cloneNamespacePrefix && root != null) { 241 allNamespaces = root.getNamespaceMap(); 242 } 243 Map <String ,String > clonePrefixes = new HashMap <String ,String >(); 244 return cloneNode(deep, allNamespaces, clonePrefixes); 245 } 246 247 public Node cloneNode(boolean deep, Map <Integer ,String > allNS, Map <String ,String > clonePrefixes) { 248 try { 249 NodeImpl clone = (NodeImpl)super.clone(); 250 clone.inTree = false; 251 clone.model = null; 252 clone.setTokens(new ArrayList <Token>(getTokens())); 253 if(deep) { 254 List <Node> cloneChildren = new ArrayList <Node>(getChildren().size()); 255 for (Node c : getChildren()) { 256 NodeImpl child = (NodeImpl) c; 257 NodeImpl cloneChild = (NodeImpl)child.cloneNode(deep, allNS, clonePrefixes); 258 cloneChildren.add(cloneChild); 259 } 260 clone.setChildren(cloneChildren); 261 } 262 263 List <Attribute> cloneAttributes = new ArrayList <Attribute>(getAttributesForRead().size()); 265 for (Attribute attribute:getAttributesForRead()) { 266 cloneAttributes.add((Attribute)attribute.cloneNode(deep, allNS, clonePrefixes)); 267 } 268 clone.setAttributes(cloneAttributes); 269 270 cloneNamespacePrefixes(allNS, clonePrefixes); 271 return clone; 272 } catch (CloneNotSupportedException cne) { 273 throw new RuntimeException (cne); 274 } 275 } 276 277 protected void cloneNamespacePrefixes(Map <Integer ,String > allNS, Map <String ,String > prefixes) { 278 if (allNS == null) return; 279 280 String namespace = allNS.get(getId()); 281 if (namespace != null) { 282 String prefix = getPrefix(); 283 if (prefix != null) { 284 prefixes.put(prefix, namespace); 285 } else { 286 prefixes.put(XMLConstants.DEFAULT_NS_PREFIX, namespace); 287 } 288 } 289 } 290 291 public Node cloneShallowWithModelContext() { 292 try { 293 NodeImpl clone = (NodeImpl)super.clone(); 294 clone.inTree = false; 295 clone.setTokens(new ArrayList <Token>(getTokens())); 296 if(hasChildNodes()) clone.setChildren(new ArrayList <Node>(getChildren())); 297 if(hasAttributes()) clone.setAttributes(new ArrayList <Attribute>(getAttributesForRead())); 298 return clone; 299 } catch (CloneNotSupportedException cne) { 300 throw new RuntimeException (cne); 301 } 302 } 303 304 308 public boolean hasChildNodes() { 309 return !getChildren().isEmpty(); 310 } 311 312 316 public NodeList getChildNodes() { 317 if (!hasChildNodes()) return NodeListImpl.EMPTY; 318 return new NodeListImpl(getChildren()); 319 } 320 321 325 public Node getFirstChild() { 326 if (!hasChildNodes()) return null; 327 return getChildren().get(0); 328 } 329 330 334 public Node getLastChild() { 335 if (!hasChildNodes()) return null; 336 return getChildren().get(getChildren().size()-1); 337 } 338 339 public int getIndexOfChild(Node n) { 340 if (n == null) return -1; 341 List <Node> childs = getChildren(); 342 for (int i = 0; i < childs.size(); i++) { 343 if (childs.get(i) == n || 344 (childs.get(i).getId() == n.getId() && 345 n.getId() != -1)) { 346 return i; 347 } 348 } 349 return -1; 350 } 351 352 359 public Node appendChild(org.w3c.dom.Node node) { 360 checkNotInTree(); 361 if(node instanceof Node) { 362 NodeImpl nodeImpl = (NodeImpl) node; 363 nodeImpl.checkNotInTree(); 364 getChildrenForWrite().add(nodeImpl); 365 return nodeImpl; 366 } else { 367 throw new DOMException(DOMException.TYPE_MISMATCH_ERR,node.getClass().getName()); 368 } 369 } 370 371 379 public Node replaceChild(org.w3c.dom.Node newNode, org.w3c.dom.Node oldNode) { 380 checkNotInTree(); 381 if(newNode instanceof Node && oldNode instanceof Node) { 382 NodeImpl newNodeImpl = (NodeImpl) newNode; 383 NodeImpl oldNodeImpl = (NodeImpl) oldNode; 384 newNodeImpl.checkNotInTree(); 385 int oldIndex = getIndexOfChild(oldNodeImpl); 386 if(oldIndex!=-1) { 387 return getChildrenForWrite().set(oldIndex, newNodeImpl); 388 } else { 389 throw new DOMException(DOMException.NOT_FOUND_ERR,null); 390 } 391 } else { 392 throw new DOMException(DOMException.TYPE_MISMATCH_ERR,null); 393 } 394 } 395 396 401 public Node reorderChild(org.w3c.dom.Node child, int index) { 402 checkNotInTree(); 403 if (child instanceof Node) { 404 NodeImpl n = (NodeImpl) child; 405 if (! n.isInTree()) { 406 throw new IllegalArgumentException ("Node is not in tree"); 407 } 408 int currentIndex = getIndexOfChild(n); 409 if (index == currentIndex) { 410 return n; 411 } 412 if (! getChildrenForWrite().remove(n)) { 413 throw new IllegalArgumentException ("Node is not in children"); 414 } 415 index = index > currentIndex ? index-1 : index; 416 getChildrenForWrite().add(index, n); 417 return n; 418 } else { 419 throw new DOMException(DOMException.TYPE_MISMATCH_ERR,null); 420 } 421 } 422 423 428 public void reorderChildren(int[] permutation) { 429 checkNotInTree(); 430 431 List <Node> copy = new ArrayList <Node>(getChildren()); 432 if (permutation.length != copy.size()) { 433 throw new IllegalArgumentException ( 434 "Permutation length: "+permutation.length+" " + 435 "is different than children size: "+copy.size()); 436 } 437 List writableChildren = getChildrenForWrite(); 438 for (int i = 0; i < copy.size(); i++ ) { 439 Node child = copy.get(i); 440 writableChildren.set(permutation[i], child); 441 } 442 } 443 444 450 public Node removeChild(org.w3c.dom.Node node) { 451 checkNotInTree(); 452 if(node instanceof Attribute) { 453 if(getAttributesForWrite().remove(node)) { 454 return (Node) node; 455 } 456 } else if(node instanceof Node) { 457 if(getChildrenForWrite().remove(node)) { 458 return (Node)node; 459 } 460 } 461 throw new DOMException(DOMException.TYPE_MISMATCH_ERR,null); 462 } 463 464 473 public Node insertBefore(org.w3c.dom.Node newChild, org.w3c.dom.Node refChild) throws DOMException { 474 if(refChild == null) 475 return appendChild(newChild); 476 checkNotInTree(); 477 if(newChild instanceof Node && refChild instanceof Node) { 478 NodeImpl newChildImpl = (NodeImpl) newChild; 479 newChildImpl.checkNotInTree(); 480 int index = getIndexOfChild((NodeImpl)refChild); 481 if(index <0) 482 throw new DOMException(DOMException.NOT_FOUND_ERR, null); 483 getChildrenForWrite().add(index,newChildImpl); 484 return newChildImpl; 485 } else { 486 throw new DOMException(DOMException.TYPE_MISMATCH_ERR,null); 487 } 488 } 489 490 494 public boolean hasAttributes() { 495 return !getAttributesForRead().isEmpty(); 496 } 497 498 502 public NamedNodeMap getAttributes() { 503 if(attributes == null || attributes.isEmpty()) return NamedNodeMapImpl.EMPTY; 504 return new NamedNodeMapImpl(attributes); 505 } 506 507 511 public org.w3c.dom.Document getOwnerDocument() { 512 return getModel().getDocument(); 513 } 514 515 public Node getParentNode() { 516 if (!isInTree()) return null; 517 PathFromRootVisitor pfrv = new PathFromRootVisitor(); 518 List <Node> path = pfrv.findPath(getModel().getDocument(),this); 519 if(path == null || path.size()<2) return null; 520 return path.get(1); 521 } 522 523 public Node getNextSibling() { 524 if (!isInTree()) return null; 525 PathFromRootVisitor pfrv = new PathFromRootVisitor(); 526 List <Node> path = pfrv.findPath(getModel().getDocument(),this); 527 if(path == null || path.size()<2) return null; 528 NodeImpl parent = (NodeImpl)path.get(1); 529 NodeImpl node = (NodeImpl)path.get(0); 530 int nextIndex = parent.getIndexOfChild(node)+1; 531 if(nextIndex>=parent.getChildren().size()) return null; 532 return parent.getChildren().get(nextIndex); 533 } 534 535 public Node getPreviousSibling() { 536 if (!isInTree()) return null; 537 PathFromRootVisitor pfrv = new PathFromRootVisitor(); 538 List <Node> path = pfrv.findPath(getModel().getDocument(),this); 539 if(path == null || path.size()<2) return null; 540 NodeImpl parent = (NodeImpl)path.get(1); 541 NodeImpl node = (NodeImpl)path.get(0); 542 int prevIndex = parent.getIndexOfChild(node)-1; 543 if(prevIndex<0) return null; 544 return parent.getChildren().get(prevIndex); 545 } 546 547 551 public abstract short getNodeType(); 552 553 557 public abstract String getNodeName(); 558 559 public String getNodeValue() throws DOMException { 560 return null; 561 } 562 563 public void setNodeValue(String str) throws DOMException { 564 } 565 566 public String getLocalName() { 567 return null; 568 } 569 570 public String getNamespaceURI(Document document) { 571 assert document != null; 572 return document.getNamespaceURI(this); 573 } 574 575 public String getNamespaceURI() { 576 String namespace = lookupNamespaceLocally(getPrefix()); 577 if (namespace != null) return namespace; 578 if (isInTree()) { 579 return getModel().getDocument().getNamespaceURI(this); 580 } else { 581 return lookupNamespaceURI(getPrefix()); 582 } 583 } 584 585 public String lookupNamespaceURI(String prefix) { 586 if(prefix == null) prefix = ""; 587 String namespace = lookupNamespaceLocally(prefix); 588 if (namespace == null && isInTree()) { 589 List <Node> pathToRoot = new PathFromRootVisitor().findPath(getModel(). getDocument(), this); 590 namespace = lookupNamespace(prefix, pathToRoot); 591 } 592 return namespace; 593 } 594 595 public static String lookupNamespace(String prefix, List <Node> path) { 596 if (path == null) return null; 597 if(prefix == null) prefix = ""; 598 for (Node node : path) { 599 String namespace = ((NodeImpl)node).lookupNamespaceLocally(prefix); 600 if (namespace != null) { 601 return namespace; 602 } 603 } 604 return null; 605 } 606 607 String lookupNamespaceLocally(String prefix) { 608 if(prefix == null) prefix = ""; 609 if(hasAttributes()) { 610 for (Attribute attribute:getAttributesForRead()) { 611 if(attribute.isXmlnsAttribute()) { 612 String key = attribute.getPrefix() == null ? "" : attribute.getLocalName(); 613 if(key.equals(prefix)) { 614 return attribute.getValue(); 615 } 616 } 617 } 618 } 619 return null; 620 } 621 622 String lookupPrefixLocally(String uri) { 623 if(hasAttributes()) { 624 String defaultNamespace = null; 625 for (Attribute attribute:getAttributesForRead()) { 626 String attrName = attribute.getName(); 627 if (attrName.startsWith(XMLNS)) { 628 if (attrName.length() == 5) { 629 defaultNamespace = attribute.getValue(); 630 } else if (attrName.charAt(5) == ':' && uri.equals(attribute.getValue())) { 631 return attrName.substring(6); 632 } 633 } 634 } 635 if (uri.equals(defaultNamespace)) { 636 return ""; 637 } 638 } 639 return null; 640 } 641 642 public String lookupPrefix(String uri) { 643 if(uri == null) return null; 644 if(isInTree()) { 645 PathFromRootVisitor pfrv = new PathFromRootVisitor(); 646 List <Node> path = pfrv.findPath(getModel().getDocument(),this); 647 if (path != null && ! path.isEmpty()) { 648 return lookupPrefix(uri, path); 649 } 650 } 651 return lookupPrefixLocally(uri); 652 } 653 654 public static String lookupPrefix(String uri, List <Node> path) { 655 if (path == null) return null; 656 for(Node node : path) { 657 NodeImpl n = (NodeImpl) node; 658 String prefix = n.lookupPrefixLocally(uri); 659 if (prefix != null) { 660 return prefix; 661 } 662 } 663 return null; 664 } 665 666 public String getPrefix() { 667 return null; } 669 670 public void setPrefix(String str) throws DOMException { 671 } 672 673 public void normalize() { 674 } 675 public short compareDocumentPosition(org.w3c.dom.Node a) { 677 throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "This read-only implementation supports DOM level 1 Core and XML module."); 678 } 679 680 public String getBaseURI() { 681 throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "This read-only implementation supports DOM level 1 Core and XML module."); 682 } 683 public Object getFeature(String a, String b) { 684 throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "This read-only implementation supports DOM level 1 Core and XML module."); 685 } 686 public String getTextContent() { 687 throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "This read-only implementation supports DOM level 1 Core and XML module."); 688 } 689 public Object getUserData(String a) { 690 throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "This read-only implementation supports DOM level 1 Core and XML module."); 691 } 692 public boolean isDefaultNamespace(String a) { 693 throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "This read-only implementation supports DOM level 1 Core and XML module."); 694 } 695 public boolean isEqualNode(org.w3c.dom.Node a) { 696 throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "This read-only implementation supports DOM level 1 Core and XML module."); 697 } 698 public boolean isSameNode(org.w3c.dom.Node a) { 699 throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "This read-only implementation supports DOM level 1 Core and XML module."); 700 } 701 public void setTextContent(String a) { 702 throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "This read-only implementation supports DOM level 1 Core and XML module."); 703 } 704 public Object setUserData(String a, Object b, UserDataHandler c) { 705 throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "This read-only implementation supports DOM level 1 Core and XML module."); 706 } 707 708 713 public void copyTokens(Node newNode) { 714 checkNotInTree(); 715 setTokens(((NodeImpl)newNode).getTokens()); 716 } 717 718 722 private List <Node> getChildren() { 723 return createUnmodifiableListIfNeeded(children); 724 } 725 726 730 private List <Node> getChildrenForWrite() { 731 checkNotInTree(); 732 if (children == null) { 733 children = new ArrayList <Node>(0); 734 } 735 return children; 736 } 737 738 742 private void setChildren(List <Node> newChildren) { 743 checkNotInTree(); 744 children = newChildren; 745 } 746 747 751 protected List <Attribute> getAttributesForRead() { 752 return createUnmodifiableListIfNeeded(attributes); 753 } 754 755 759 protected List <Attribute> getAttributesForWrite() { 760 checkNotInTree(); 761 if (attributes == null) { 762 attributes = new ArrayList <Attribute>(0); 763 } 764 return attributes; 765 } 766 767 771 private void setAttributes(List <Attribute> newAttributes) { 772 checkNotInTree(); 773 attributes = newAttributes; 774 } 775 776 780 public List <Token> getTokens() { 781 return createUnmodifiableListIfNeeded(tokens); 782 } 783 784 788 List <Token> getTokensForWrite() { 789 checkNotInTree(); 790 if (tokens == null) { 791 tokens = new ArrayList <Token>(0); 792 } 793 return tokens; 794 } 795 796 800 void setTokens(List <Token> newTokens) { 801 tokens = newTokens; 802 } 803 804 809 public Node copy() { 810 NodeImpl clone = (NodeImpl) cloneNode(true); 811 clone.assignNodeId(-1); 812 return clone; 813 } 814 815 826 private <E extends Object > List <E> createUnmodifiableListIfNeeded(List <E> objects) { 827 List <E> unmodifiableObjects = objects; 829 if(objects == null) { 830 unmodifiableObjects = Collections.emptyList(); 832 } else if (objects instanceof ArrayList ) { 833 unmodifiableObjects = Collections.unmodifiableList(objects); 835 } 836 return unmodifiableObjects; 837 } 838 } 839 | Popular Tags |