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 && importedNode.hasChildNodes()) {
 224                 NodeList childNodes = importedNode.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     //If the parentNode is not registered to domToSoap, create soap wapper for parentNode and register it to domToSoap
 237     //If deep = true, also register all children of parentNode to domToSoap map.
 238     public void registerChildNodes(Node parentNode, boolean deep) {
 239         if (parentNode.getUserData(SAAJ_NODE) == null) {
 240             if (parentNode instanceof Element) {
 241                 ElementFactory.createElement(this, (Element) parentNode);
 242             } else if (parentNode instanceof CharacterData) {
 243                 switch (parentNode.getNodeType()) {
 244                     case CDATA_SECTION_NODE:
 245                         new CDATAImpl(this, (CharacterData) parentNode);
 246                         break;
 247                     case COMMENT_NODE:
 248                         new SOAPCommentImpl(this, (CharacterData) parentNode);
 249                         break;
 250                     case TEXT_NODE:
 251                         new SOAPTextImpl(this, (CharacterData) parentNode);
 252                         break;
 253                 }
 254             }
 255         }
 256         if (deep) {
 257             NodeList nodeList = parentNode.getChildNodes();
 258             for (int i = 0; i < nodeList.getLength(); i++) {
 259                 Node nextChild = nodeList.item(i);
 260                 registerChildNodes(nextChild, true);
 261             }
 262         }
 263     }
 264 
 265     @Override
 266     public Element createElementNS(String namespaceURI, String qualifiedName)
 267         throws DOMException {
 268         return ElementFactory.createElement(
 269             this,
 270             NameImpl.getLocalNameFromTagName(qualifiedName),
 271             NameImpl.getPrefixFromTagName(qualifiedName),
 272             namespaceURI);
 273     }
 274 
 275     @Override
 276     public Attr createAttributeNS(String namespaceURI, String qualifiedName)
 277         throws DOMException {
 278         return document.createAttributeNS(namespaceURI, qualifiedName);
 279     }
 280 
 281     @Override
 282     public NodeList getElementsByTagNameNS(
 283         String namespaceURI,
 284         String localName) {
 285         return new NodeListImpl(this, document.getElementsByTagNameNS(namespaceURI, localName));
 286     }
 287 
 288     @Override
 289     public Element getElementById(String elementId) {
 290         return (Element) findIfPresent(document.getElementById(elementId));
 291     }
 292 
 293     @Override
 294     public String getInputEncoding() {
 295         return document.getInputEncoding();
 296     }
 297 
 298     @Override
 299     public String getXmlEncoding() {
 300         return document.getXmlEncoding();
 301     }
 302 
 303     @Override
 304     public boolean getXmlStandalone() {
 305         return document.getXmlStandalone();
 306     }
 307 
 308     @Override
 309     public void setXmlStandalone(boolean xmlStandalone) throws DOMException {
 310         document.setXmlStandalone(xmlStandalone);
 311     }
 312 
 313     @Override
 314     public String getXmlVersion() {
 315         return document.getXmlVersion();
 316     }
 317 
 318     @Override
 319     public void setXmlVersion(String xmlVersion) throws DOMException {
 320         document.setXmlVersion(xmlVersion);
 321     }
 322 
 323     @Override
 324     public boolean getStrictErrorChecking() {
 325         return document.getStrictErrorChecking();
 326     }
 327 
 328     @Override
 329     public void setStrictErrorChecking(boolean strictErrorChecking) {
 330         document.setStrictErrorChecking(strictErrorChecking);
 331     }
 332 
 333     @Override
 334     public String getDocumentURI() {
 335         return document.getDocumentURI();
 336     }
 337 
 338     @Override
 339     public void setDocumentURI(String documentURI) {
 340         document.setDocumentURI(documentURI);
 341     }
 342 
 343     @Override
 344     public Node adoptNode(Node source) throws DOMException {
 345         return document.adoptNode(source);
 346     }
 347 
 348     @Override
 349     public DOMConfiguration getDomConfig() {
 350         return document.getDomConfig();
 351     }
 352 
 353     @Override
 354     public void normalizeDocument() {
 355         document.normalizeDocument();
 356     }
 357 
 358     @Override
 359     public Node renameNode(Node n, String namespaceURI, String qualifiedName) throws DOMException {
 360         return findIfPresent(document.renameNode(n, namespaceURI, qualifiedName));
 361     }
 362 
 363     @Override
 364     public String getNodeName() {
 365         return document.getNodeName();
 366     }
 367 
 368     @Override
 369     public String getNodeValue() throws DOMException {
 370         return document.getNodeValue();
 371     }
 372 
 373     @Override
 374     public void setNodeValue(String nodeValue) throws DOMException {
 375         document.setNodeValue(nodeValue);
 376     }
 377 
 378     @Override
 379     public short getNodeType() {
 380         return document.getNodeType();
 381     }
 382 
 383     @Override
 384     public Node getParentNode() {
 385         return findIfPresent(document.getParentNode());
 386     }
 387 
 388     @Override
 389     public NodeList getChildNodes() {
 390         return new NodeListImpl(this, document.getChildNodes());
 391     }
 392 
 393     @Override
 394     public Node getFirstChild() {
 395         return findIfPresent(document.getFirstChild());
 396     }
 397 
 398     @Override
 399     public Node getLastChild() {
 400         return findIfPresent(document.getLastChild());
 401     }
 402 
 403     @Override
 404     public Node getPreviousSibling() {
 405         return findIfPresent(document.getPreviousSibling());
 406     }
 407 
 408     @Override
 409     public Node getNextSibling() {
 410         return findIfPresent(document.getNextSibling());
 411     }
 412 
 413     @Override
 414     public NamedNodeMap getAttributes() {
 415         return new NamedNodeMapImpl(document.getAttributes(), this);
 416     }
 417 
 418     @Override
 419     public Document getOwnerDocument() {
 420         return document.getOwnerDocument();
 421     }
 422 
 423     @Override
 424     public Node insertBefore(Node newChild, Node refChild) throws DOMException {
 425         return document.insertBefore(getDomNode(newChild), getDomNode(refChild));
 426     }
 427 
 428     @Override
 429     public Node replaceChild(Node newChild, Node oldChild) throws DOMException {
 430         return document.replaceChild(getDomNode(newChild), getDomNode(oldChild));
 431     }
 432 
 433     @Override
 434     public Node removeChild(Node oldChild) throws DOMException {
 435         return document.removeChild(getDomNode(oldChild));
 436     }
 437 
 438     @Override
 439     public Node appendChild(Node newChild) throws DOMException {
 440         return document.appendChild(getDomNode(newChild));
 441     }
 442 
 443     @Override
 444     public boolean hasChildNodes() {
 445         return document.hasChildNodes();
 446     }
 447 
 448     @Override
 449     public Node cloneNode(boolean deep) {
 450         Node node = document.cloneNode(deep);
 451         registerChildNodes(node, deep);
 452         return findIfPresent(node);
 453     }
 454 
 455     @Override
 456     public void normalize() {
 457         document.normalize();
 458     }
 459 
 460     @Override
 461     public boolean isSupported(String feature, String version) {
 462         return document.isSupported(feature, version);
 463     }
 464 
 465     @Override
 466     public String getNamespaceURI() {
 467         return document.getNamespaceURI();
 468     }
 469 
 470     @Override
 471     public String getPrefix() {
 472         return document.getPrefix();
 473     }
 474 
 475     @Override
 476     public void setPrefix(String prefix) throws DOMException {
 477         document.setPrefix(prefix);
 478     }
 479 
 480     @Override
 481     public String getLocalName() {
 482         return document.getLocalName();
 483     }
 484 
 485     @Override
 486     public boolean hasAttributes() {
 487         return document.hasAttributes();
 488     }
 489 
 490     @Override
 491     public String getBaseURI() {
 492         return document.getBaseURI();
 493     }
 494 
 495     @Override
 496     public short compareDocumentPosition(Node other) throws DOMException {
 497         return document.compareDocumentPosition(getDomNode(other));
 498     }
 499 
 500     @Override
 501     public String getTextContent() throws DOMException {
 502         return document.getTextContent();
 503     }
 504 
 505     @Override
 506     public void setTextContent(String textContent) throws DOMException {
 507         document.setTextContent(textContent);
 508     }
 509 
 510     @Override
 511     public boolean isSameNode(Node other) {
 512         return document.isSameNode(getDomNode(other));
 513     }
 514 
 515     @Override
 516     public String lookupPrefix(String namespaceURI) {
 517         return document.lookupPrefix(namespaceURI);
 518     }
 519 
 520     @Override
 521     public boolean isDefaultNamespace(String namespaceURI) {
 522         return document.isDefaultNamespace(namespaceURI);
 523     }
 524 
 525     @Override
 526     public String lookupNamespaceURI(String prefix) {
 527         return document.lookupNamespaceURI(prefix);
 528     }
 529 
 530     @Override
 531     public boolean isEqualNode(Node arg) {
 532         return document.isEqualNode(getDomNode(arg));
 533     }
 534 
 535     @Override
 536     public Object getFeature(String feature, String version) {
 537         return document.getFeature(feature, version);
 538     }
 539 
 540     @Override
 541     public Object setUserData(String key, Object data, UserDataHandler handler) {
 542         return document.setUserData(key, data, handler);
 543     }
 544 
 545     @Override
 546     public Object getUserData(String key) {
 547         return document.getUserData(key);
 548     }
 549 
 550     public Document getDomDocument() {
 551         return document;
 552     }
 553 
 554     /**
 555      * Insert a mapping information for {@link org.w3c.dom.Node} - {@link javax.xml.soap.Node}.
 556      *
 557      * In SAAJ, elements in DOM are expected to be interfaces of SAAJ, on the other hand in JDKs Xerces,
 558      * they are casted to internal impl classes. After removal of SAAJ dependency
 559      * to JDKs internal classes elements in DOM can never be both of them.
 560      *
 561      * @param node SAAJ wrapper node for w3c DOM node
 562      */
 563     public void register(javax.xml.soap.Node node) {
 564         final Node domElement = getDomNode(node);
 565         if (domElement.getUserData(SAAJ_NODE) != null) {
 566             throw new IllegalStateException("Element " + domElement.getNodeName()
 567                     + " is already registered");
 568         }
 569         domElement.setUserData(SAAJ_NODE, node, null);
 570     }
 571 
 572     /**
 573      * Find a soap wrapper for w3c dom node.
 574      *
 575      * @param node w3c dom node nullable
 576      * @return soap wrapper for w3c dom node
 577      *
 578      * @throws
 579      */
 580     public javax.xml.soap.Node find(Node node) {
 581         return find(node, true);
 582     }
 583 
 584     private javax.xml.soap.Node find(Node node, boolean required) {
 585         if (node == null) {
 586             return null;
 587         }
 588         if (node instanceof javax.xml.soap.Node) {
 589             return (javax.xml.soap.Node) node;
 590         }
 591         final javax.xml.soap.Node found = (javax.xml.soap.Node) node.getUserData(SAAJ_NODE);
 592         if (found == null && required) {
 593             throw new IllegalArgumentException(MessageFormat.format("Cannot find SOAP wrapper for element {0}", node));
 594         }
 595         return found;
 596     }
 597 
 598     /**
 599      * If corresponding soap wrapper exists for w3c dom node it is returned,
 600      * if not passed dom element is returned.
 601      *
 602      * @param node w3c dom node
 603      * @return soap wrapper or passed w3c dom node if not found
 604      */
 605     public Node findIfPresent(Node node) {
 606         final javax.xml.soap.Node found = find(node, false);
 607         return found != null ? found : node;
 608     }
 609 
 610     /**
 611      * Extracts w3c dom node from corresponding soap wrapper.
 612      *
 613      * @param node soap or dom nullable
 614      * @return dom node
 615      */
 616     public Node getDomNode(Node node) {
 617         if (node instanceof SOAPDocumentImpl) {
 618             return ((SOAPDocumentImpl)node).getDomElement();
 619         } else if (node instanceof ElementImpl) {
 620             return ((ElementImpl) node).getDomElement();
 621         } else if (node instanceof SOAPTextImpl) {
 622             return ((SOAPTextImpl)node).getDomElement();
 623         } else if (node instanceof SOAPCommentImpl) {
 624             return ((SOAPCommentImpl)node).getDomElement();
 625         } else if (node instanceof CDATAImpl) {
 626             return ((CDATAImpl) node).getDomElement();
 627         }
 628         return node;
 629     }
 630 
 631 
 632     private Node createSoapNode(Class nodeType, Node node) {
 633         if (SOAPTextImpl.class.isAssignableFrom(nodeType)) {
 634             return new SOAPTextImpl(this, (Text) node);
 635         } else if (SOAPCommentImpl.class.isAssignableFrom(nodeType)) {
 636             return new SOAPCommentImpl(this, (Comment) node);
 637         } else if (CDATAImpl.class.isAssignableFrom(nodeType)) {
 638             return new CDATAImpl(this, (CDATASection) node);
 639         }
 640         try {
 641             Constructor<Node> constructor = nodeType.getConstructor(SOAPDocumentImpl.class, Element.class);
 642             return constructor.newInstance(this, node);
 643         } catch (Exception e) {
 644             throw new IllegalStateException(e);
 645         }
 646     }
 647 
 648 
 649     public Document getDomElement() {
 650         return document;
 651     }
 652 
 653     @Override
 654     public String getValue() {
 655         throw new UnsupportedOperationException();
 656     }
 657 
 658     @Override
 659     public void setValue(String value) {
 660         throw new UnsupportedOperationException();
 661     }
 662 
 663     @Override
 664     public void setParentElement(SOAPElement parent) throws SOAPException {
 665         throw new UnsupportedOperationException();
 666     }
 667 
 668     @Override
 669     public SOAPElement getParentElement() {
 670         throw new UnsupportedOperationException();
 671     }
 672 
 673     @Override
 674     public void detachNode() {
 675         throw new UnsupportedOperationException();
 676     }
 677 
 678     @Override
 679     public void recycleNode() {
 680         throw new UnsupportedOperationException();
 681     }
 682 }