1 /* 2 * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 /** 27 * 28 * @author SAAJ RI Development Team 29 */ 30 package com.sun.xml.internal.messaging.saaj.soap; 31 32 import com.sun.xml.internal.messaging.saaj.soap.impl.CDATAImpl; 33 import com.sun.xml.internal.messaging.saaj.soap.impl.ElementFactory; 34 import com.sun.xml.internal.messaging.saaj.soap.impl.ElementImpl; 35 import com.sun.xml.internal.messaging.saaj.soap.impl.NamedNodeMapImpl; 36 import com.sun.xml.internal.messaging.saaj.soap.impl.NodeListImpl; 37 import com.sun.xml.internal.messaging.saaj.soap.impl.SOAPCommentImpl; 38 import com.sun.xml.internal.messaging.saaj.soap.impl.SOAPTextImpl; 39 import com.sun.xml.internal.messaging.saaj.soap.name.NameImpl; 40 import com.sun.xml.internal.messaging.saaj.util.LogDomainConstants; 41 import com.sun.xml.internal.messaging.saaj.util.SAAJUtil; 42 import org.w3c.dom.Attr; 43 import org.w3c.dom.CDATASection; 44 import org.w3c.dom.CharacterData; 45 import org.w3c.dom.Comment; 46 import org.w3c.dom.DOMConfiguration; 47 import org.w3c.dom.DOMException; 48 import org.w3c.dom.DOMImplementation; 49 import org.w3c.dom.Document; 50 import org.w3c.dom.DocumentFragment; 51 import org.w3c.dom.DocumentType; 52 import org.w3c.dom.Element; 53 import org.w3c.dom.EntityReference; 54 import org.w3c.dom.NamedNodeMap; 55 import org.w3c.dom.Node; 56 import org.w3c.dom.NodeList; 57 import org.w3c.dom.ProcessingInstruction; 58 import org.w3c.dom.Text; 59 import org.w3c.dom.UserDataHandler; 60 61 import javax.xml.parsers.DocumentBuilder; 62 import javax.xml.parsers.DocumentBuilderFactory; 63 import javax.xml.parsers.ParserConfigurationException; 64 import javax.xml.soap.SOAPElement; 65 import javax.xml.soap.SOAPException; 66 import java.lang.reflect.Constructor; 67 import java.text.MessageFormat; 68 import java.util.logging.Logger; 69 70 public class SOAPDocumentImpl implements SOAPDocument, javax.xml.soap.Node, Document { 71 72 public static final String SAAJ_NODE = "javax.xml.soap.Node"; 73 74 private static final String XMLNS = "xmlns".intern(); 75 protected static final Logger log = 76 Logger.getLogger(LogDomainConstants.SOAP_DOMAIN, 77 "com.sun.xml.internal.messaging.saaj.soap.LocalStrings"); 78 79 SOAPPartImpl enclosingSOAPPart; 80 81 private Document document; 82 83 public SOAPDocumentImpl(SOAPPartImpl enclosingDocument) { 84 document = createDocument(); 85 this.enclosingSOAPPart = enclosingDocument; 86 register(this); 87 } 88 89 private Document createDocument() { 90 DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance("com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl", SAAJUtil.getSystemClassLoader()); 91 try { 92 final DocumentBuilder documentBuilder = docFactory.newDocumentBuilder(); 93 return documentBuilder.newDocument(); 94 } catch (ParserConfigurationException e) { 95 throw new RuntimeException("Error creating xml document", e); 96 } 97 } 98 99 // public SOAPDocumentImpl(boolean grammarAccess) { 100 // super(grammarAccess); 101 // } 102 // 103 // public SOAPDocumentImpl(DocumentType doctype) { 104 // super(doctype); 105 // } 106 // 107 // public SOAPDocumentImpl(DocumentType doctype, boolean grammarAccess) { 108 // super(doctype, grammarAccess); 109 // } 110 111 @Override 112 public SOAPPartImpl getSOAPPart() { 113 if (enclosingSOAPPart == null) { 114 log.severe("SAAJ0541.soap.fragment.not.bound.to.part"); 115 throw new RuntimeException("Could not complete operation. Fragment not bound to SOAP part."); 116 } 117 return enclosingSOAPPart; 118 } 119 120 @Override 121 public SOAPDocumentImpl getDocument() { 122 return this; 123 } 124 125 @Override 126 public DocumentType getDoctype() { 127 // SOAP means no DTD, No DTD means no doctype (SOAP 1.2 only?) 128 return null; 129 } 130 131 @Override 132 public DOMImplementation getImplementation() { 133 return document.getImplementation(); 134 } 135 136 @Override 137 public Element getDocumentElement() { 138 // This had better be an Envelope! 139 getSOAPPart().doGetDocumentElement(); 140 return doGetDocumentElement(); 141 } 142 143 protected Element doGetDocumentElement() { 144 return document.getDocumentElement(); 145 } 146 147 @Override 148 public Element createElement(String tagName) throws DOMException { 149 return ElementFactory.createElement( 150 this, 151 NameImpl.getLocalNameFromTagName(tagName), 152 NameImpl.getPrefixFromTagName(tagName), 153 null); 154 } 155 156 @Override 157 public DocumentFragment createDocumentFragment() { 158 return new SOAPDocumentFragment(this); 159 } 160 161 @Override 162 public org.w3c.dom.Text createTextNode(String data) { 163 return new SOAPTextImpl(this, data); 164 } 165 166 @Override 167 public Comment createComment(String data) { 168 return new SOAPCommentImpl(this, data); 169 } 170 171 @Override 172 public CDATASection createCDATASection(String data) throws DOMException { 173 return new CDATAImpl(this, data); 174 } 175 176 @Override 177 public ProcessingInstruction createProcessingInstruction( 178 String target, 179 String data) 180 throws DOMException { 181 log.severe("SAAJ0542.soap.proc.instructions.not.allowed.in.docs"); 182 throw new UnsupportedOperationException("Processing Instructions are not allowed in SOAP documents"); 183 } 184 185 @Override 186 public Attr createAttribute(String name) throws DOMException { 187 boolean isQualifiedName = (name.indexOf(":") > 0); 188 if (isQualifiedName) { 189 String nsUri = null; 190 String prefix = name.substring(0, name.indexOf(":")); 191 //cannot do anything to resolve the URI if prefix is not 192 //XMLNS. 193 if (XMLNS.equals(prefix)) { 194 nsUri = ElementImpl.XMLNS_URI; 195 return createAttributeNS(nsUri, name); 196 } 197 } 198 199 return document.createAttribute(name); 200 } 201 202 @Override 203 public EntityReference createEntityReference(String name) 204 throws DOMException { 205 log.severe("SAAJ0543.soap.entity.refs.not.allowed.in.docs"); 206 throw new UnsupportedOperationException("Entity References are not allowed in SOAP documents"); 207 } 208 209 @Override 210 public NodeList getElementsByTagName(String tagname) { 211 return new NodeListImpl(this, document.getElementsByTagName(tagname)); 212 } 213 214 @Override 215 public org.w3c.dom.Node importNode(Node importedNode, boolean deep) 216 throws DOMException { 217 Node domNode = getDomNode(importedNode); 218 final Node newNode = document.importNode(domNode, deep); 219 220 if (importedNode instanceof javax.xml.soap.Node) { 221 Node newSoapNode = createSoapNode(importedNode.getClass(), newNode); 222 newNode.setUserData(SAAJ_NODE, newSoapNode, null); 223 if (deep && newSoapNode.hasChildNodes()) { 224 NodeList childNodes = newSoapNode.getChildNodes(); 225 for (int i = 0; i < childNodes.getLength(); i++) { 226 registerChildNodes(childNodes.item(i), deep); 227 } 228 } 229 return newSoapNode; 230 } 231 232 registerChildNodes(newNode, deep); 233 return findIfPresent(newNode); 234 } 235 236 /** 237 * If the parentNode is not registered to domToSoap, create soap wapper for parentNode and register it to domToSoap 238 * If deep = true, also register all children transitively of parentNode to domToSoap map. 239 * @param parentNode node to wrap 240 * @param deep wrap child nodes transitively 241 */ 242 public void registerChildNodes(Node parentNode, boolean deep) { 243 if (parentNode.getUserData(SAAJ_NODE) == null) { 244 if (parentNode instanceof Element) { 245 ElementFactory.createElement(this, (Element) parentNode); 246 } else if (parentNode instanceof CharacterData) { 247 switch (parentNode.getNodeType()) { 248 case CDATA_SECTION_NODE: 249 new CDATAImpl(this, (CharacterData) parentNode); 250 break; 251 case COMMENT_NODE: 252 new SOAPCommentImpl(this, (CharacterData) parentNode); 253 break; 254 case TEXT_NODE: 255 new SOAPTextImpl(this, (CharacterData) parentNode); 256 break; 257 } 258 } else if (parentNode instanceof DocumentFragment) { 259 new SOAPDocumentFragment(this, (DocumentFragment) parentNode); 260 } 261 } 262 if (deep) { 263 NodeList nodeList = parentNode.getChildNodes(); 264 for (int i = 0; i < nodeList.getLength(); i++) { 265 Node nextChild = nodeList.item(i); 266 registerChildNodes(nextChild, true); 267 } 268 } 269 } 270 271 @Override 272 public Element createElementNS(String namespaceURI, String qualifiedName) 273 throws DOMException { 274 return ElementFactory.createElement( 275 this, 276 NameImpl.getLocalNameFromTagName(qualifiedName), 277 NameImpl.getPrefixFromTagName(qualifiedName), 278 namespaceURI); 279 } 280 281 @Override 282 public Attr createAttributeNS(String namespaceURI, String qualifiedName) 283 throws DOMException { 284 return document.createAttributeNS(namespaceURI, qualifiedName); 285 } 286 287 @Override 288 public NodeList getElementsByTagNameNS( 289 String namespaceURI, 290 String localName) { 291 return new NodeListImpl(this, document.getElementsByTagNameNS(namespaceURI, localName)); 292 } 293 294 @Override 295 public Element getElementById(String elementId) { 296 return (Element) findIfPresent(document.getElementById(elementId)); 297 } 298 299 @Override 300 public String getInputEncoding() { 301 return document.getInputEncoding(); 302 } 303 304 @Override 305 public String getXmlEncoding() { 306 return document.getXmlEncoding(); 307 } 308 309 @Override 310 public boolean getXmlStandalone() { 311 return document.getXmlStandalone(); 312 } 313 314 @Override 315 public void setXmlStandalone(boolean xmlStandalone) throws DOMException { 316 document.setXmlStandalone(xmlStandalone); 317 } 318 319 @Override 320 public String getXmlVersion() { 321 return document.getXmlVersion(); 322 } 323 324 @Override 325 public void setXmlVersion(String xmlVersion) throws DOMException { 326 document.setXmlVersion(xmlVersion); 327 } 328 329 @Override 330 public boolean getStrictErrorChecking() { 331 return document.getStrictErrorChecking(); 332 } 333 334 @Override 335 public void setStrictErrorChecking(boolean strictErrorChecking) { 336 document.setStrictErrorChecking(strictErrorChecking); 337 } 338 339 @Override 340 public String getDocumentURI() { 341 return document.getDocumentURI(); 342 } 343 344 @Override 345 public void setDocumentURI(String documentURI) { 346 document.setDocumentURI(documentURI); 347 } 348 349 @Override 350 public Node adoptNode(Node source) throws DOMException { 351 return document.adoptNode(source); 352 } 353 354 @Override 355 public DOMConfiguration getDomConfig() { 356 return document.getDomConfig(); 357 } 358 359 @Override 360 public void normalizeDocument() { 361 document.normalizeDocument(); 362 } 363 364 @Override 365 public Node renameNode(Node n, String namespaceURI, String qualifiedName) throws DOMException { 366 return findIfPresent(document.renameNode(n, namespaceURI, qualifiedName)); 367 } 368 369 @Override 370 public String getNodeName() { 371 return document.getNodeName(); 372 } 373 374 @Override 375 public String getNodeValue() throws DOMException { 376 return document.getNodeValue(); 377 } 378 379 @Override 380 public void setNodeValue(String nodeValue) throws DOMException { 381 document.setNodeValue(nodeValue); 382 } 383 384 @Override 385 public short getNodeType() { 386 return document.getNodeType(); 387 } 388 389 @Override 390 public Node getParentNode() { 391 return findIfPresent(document.getParentNode()); 392 } 393 394 @Override 395 public NodeList getChildNodes() { 396 return new NodeListImpl(this, document.getChildNodes()); 397 } 398 399 @Override 400 public Node getFirstChild() { 401 return findIfPresent(document.getFirstChild()); 402 } 403 404 @Override 405 public Node getLastChild() { 406 return findIfPresent(document.getLastChild()); 407 } 408 409 @Override 410 public Node getPreviousSibling() { 411 return findIfPresent(document.getPreviousSibling()); 412 } 413 414 @Override 415 public Node getNextSibling() { 416 return findIfPresent(document.getNextSibling()); 417 } 418 419 @Override 420 public NamedNodeMap getAttributes() { 421 NamedNodeMap attributes = document.getAttributes(); 422 if (attributes == null) { 423 return null; 424 } 425 return new NamedNodeMapImpl(attributes, this); 426 } 427 428 @Override 429 public Document getOwnerDocument() { 430 return document.getOwnerDocument(); 431 } 432 433 @Override 434 public Node insertBefore(Node newChild, Node refChild) throws DOMException { 435 return document.insertBefore(getDomNode(newChild), getDomNode(refChild)); 436 } 437 438 @Override 439 public Node replaceChild(Node newChild, Node oldChild) throws DOMException { 440 return document.replaceChild(getDomNode(newChild), getDomNode(oldChild)); 441 } 442 443 @Override 444 public Node removeChild(Node oldChild) throws DOMException { 445 return document.removeChild(getDomNode(oldChild)); 446 } 447 448 @Override 449 public Node appendChild(Node newChild) throws DOMException { 450 return document.appendChild(getDomNode(newChild)); 451 } 452 453 @Override 454 public boolean hasChildNodes() { 455 return document.hasChildNodes(); 456 } 457 458 @Override 459 public Node cloneNode(boolean deep) { 460 Node node = document.cloneNode(deep); 461 registerChildNodes(node, deep); 462 return findIfPresent(node); 463 } 464 465 @Override 466 public void normalize() { 467 document.normalize(); 468 } 469 470 @Override 471 public boolean isSupported(String feature, String version) { 472 return document.isSupported(feature, version); 473 } 474 475 @Override 476 public String getNamespaceURI() { 477 return document.getNamespaceURI(); 478 } 479 480 @Override 481 public String getPrefix() { 482 return document.getPrefix(); 483 } 484 485 @Override 486 public void setPrefix(String prefix) throws DOMException { 487 document.setPrefix(prefix); 488 } 489 490 @Override 491 public String getLocalName() { 492 return document.getLocalName(); 493 } 494 495 @Override 496 public boolean hasAttributes() { 497 return document.hasAttributes(); 498 } 499 500 @Override 501 public String getBaseURI() { 502 return document.getBaseURI(); 503 } 504 505 @Override 506 public short compareDocumentPosition(Node other) throws DOMException { 507 return document.compareDocumentPosition(getDomNode(other)); 508 } 509 510 @Override 511 public String getTextContent() throws DOMException { 512 return document.getTextContent(); 513 } 514 515 @Override 516 public void setTextContent(String textContent) throws DOMException { 517 document.setTextContent(textContent); 518 } 519 520 @Override 521 public boolean isSameNode(Node other) { 522 return document.isSameNode(getDomNode(other)); 523 } 524 525 @Override 526 public String lookupPrefix(String namespaceURI) { 527 return document.lookupPrefix(namespaceURI); 528 } 529 530 @Override 531 public boolean isDefaultNamespace(String namespaceURI) { 532 return document.isDefaultNamespace(namespaceURI); 533 } 534 535 @Override 536 public String lookupNamespaceURI(String prefix) { 537 return document.lookupNamespaceURI(prefix); 538 } 539 540 @Override 541 public boolean isEqualNode(Node arg) { 542 return document.isEqualNode(getDomNode(arg)); 543 } 544 545 @Override 546 public Object getFeature(String feature, String version) { 547 return document.getFeature(feature, version); 548 } 549 550 @Override 551 public Object setUserData(String key, Object data, UserDataHandler handler) { 552 return document.setUserData(key, data, handler); 553 } 554 555 @Override 556 public Object getUserData(String key) { 557 return document.getUserData(key); 558 } 559 560 public Document getDomDocument() { 561 return document; 562 } 563 564 /** 565 * Insert a mapping information for {@link org.w3c.dom.Node} - {@link javax.xml.soap.Node}. 566 * 567 * In SAAJ, elements in DOM are expected to be interfaces of SAAJ, on the other hand in JDKs Xerces, 568 * they are casted to internal impl classes. After removal of SAAJ dependency 569 * to JDKs internal classes elements in DOM can never be both of them. 570 * 571 * @param node SAAJ wrapper node for w3c DOM node 572 */ 573 public void register(javax.xml.soap.Node node) { 574 final Node domElement = getDomNode(node); 575 if (domElement.getUserData(SAAJ_NODE) != null) { 576 throw new IllegalStateException("Element " + domElement.getNodeName() 577 + " is already registered"); 578 } 579 domElement.setUserData(SAAJ_NODE, node, null); 580 } 581 582 /** 583 * Find a soap wrapper for w3c dom node. 584 * 585 * @param node w3c dom node nullable 586 * @return soap wrapper for w3c dom node 587 * 588 * @throws 589 */ 590 public javax.xml.soap.Node find(Node node) { 591 return find(node, true); 592 } 593 594 private javax.xml.soap.Node find(Node node, boolean required) { 595 if (node == null) { 596 return null; 597 } 598 if (node instanceof javax.xml.soap.Node) { 599 return (javax.xml.soap.Node) node; 600 } 601 final javax.xml.soap.Node found = (javax.xml.soap.Node) node.getUserData(SAAJ_NODE); 602 if (found == null && required) { 603 throw new IllegalArgumentException(MessageFormat.format("Cannot find SOAP wrapper for element {0}", node)); 604 } 605 return found; 606 } 607 608 /** 609 * If corresponding soap wrapper exists for w3c dom node it is returned, 610 * if not passed dom element is returned. 611 * 612 * @param node w3c dom node 613 * @return soap wrapper or passed w3c dom node if not found 614 */ 615 public Node findIfPresent(Node node) { 616 final javax.xml.soap.Node found = find(node, false); 617 return found != null ? found : node; 618 } 619 620 /** 621 * Extracts w3c dom node from corresponding soap wrapper. 622 * 623 * @param node soap or dom nullable 624 * @return dom node 625 */ 626 public Node getDomNode(Node node) { 627 if (node instanceof SOAPDocumentImpl) { 628 return ((SOAPDocumentImpl)node).getDomElement(); 629 } else if (node instanceof ElementImpl) { 630 return ((ElementImpl) node).getDomElement(); 631 } else if (node instanceof SOAPTextImpl) { 632 return ((SOAPTextImpl)node).getDomElement(); 633 } else if (node instanceof SOAPCommentImpl) { 634 return ((SOAPCommentImpl)node).getDomElement(); 635 } else if (node instanceof CDATAImpl) { 636 return ((CDATAImpl) node).getDomElement(); 637 } else if (node instanceof SOAPDocumentFragment) { 638 return ((SOAPDocumentFragment)node).getDomNode(); 639 } 640 return node; 641 } 642 643 644 private Node createSoapNode(Class nodeType, Node node) { 645 if (SOAPTextImpl.class.isAssignableFrom(nodeType)) { 646 return new SOAPTextImpl(this, (Text) node); 647 } else if (SOAPCommentImpl.class.isAssignableFrom(nodeType)) { 648 return new SOAPCommentImpl(this, (Comment) node); 649 } else if (CDATAImpl.class.isAssignableFrom(nodeType)) { 650 return new CDATAImpl(this, (CDATASection) node); 651 } else if (SOAPDocumentFragment.class.isAssignableFrom(nodeType)) { 652 return new SOAPDocumentFragment(this, (DocumentFragment) node); 653 } 654 try { 655 Constructor<Node> constructor = nodeType.getConstructor(SOAPDocumentImpl.class, Element.class); 656 return constructor.newInstance(this, node); 657 } catch (Exception e) { 658 throw new IllegalStateException(e); 659 } 660 } 661 662 663 public Document getDomElement() { 664 return document; 665 } 666 667 @Override 668 public String getValue() { 669 throw new UnsupportedOperationException(); 670 } 671 672 @Override 673 public void setValue(String value) { 674 throw new UnsupportedOperationException(); 675 } 676 677 @Override 678 public void setParentElement(SOAPElement parent) throws SOAPException { 679 throw new UnsupportedOperationException(); 680 } 681 682 @Override 683 public SOAPElement getParentElement() { 684 throw new UnsupportedOperationException(); 685 } 686 687 @Override 688 public void detachNode() { 689 throw new UnsupportedOperationException(); 690 } 691 692 @Override 693 public void recycleNode() { 694 throw new UnsupportedOperationException(); 695 } 696 }