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*/
30package com.sun.xml.internal.messaging.saaj.soap;
31
32import com.sun.xml.internal.messaging.saaj.soap.impl.CDATAImpl;
33import com.sun.xml.internal.messaging.saaj.soap.impl.ElementFactory;
34import com.sun.xml.internal.messaging.saaj.soap.impl.ElementImpl;
35import com.sun.xml.internal.messaging.saaj.soap.impl.NamedNodeMapImpl;
36import com.sun.xml.internal.messaging.saaj.soap.impl.NodeListImpl;
37import com.sun.xml.internal.messaging.saaj.soap.impl.SOAPCommentImpl;
38import com.sun.xml.internal.messaging.saaj.soap.impl.SOAPTextImpl;
39import com.sun.xml.internal.messaging.saaj.soap.name.NameImpl;
40import com.sun.xml.internal.messaging.saaj.util.LogDomainConstants;
41import com.sun.xml.internal.messaging.saaj.util.SAAJUtil;
42import org.w3c.dom.Attr;
43import org.w3c.dom.CDATASection;
44import org.w3c.dom.CharacterData;
45import org.w3c.dom.Comment;
46import org.w3c.dom.DOMConfiguration;
47import org.w3c.dom.DOMException;
48import org.w3c.dom.DOMImplementation;
49import org.w3c.dom.Document;
50import org.w3c.dom.DocumentFragment;
51import org.w3c.dom.DocumentType;
52import org.w3c.dom.Element;
53import org.w3c.dom.EntityReference;
54import org.w3c.dom.NamedNodeMap;
55import org.w3c.dom.Node;
56import org.w3c.dom.NodeList;
57import org.w3c.dom.ProcessingInstruction;
58import org.w3c.dom.Text;
59import org.w3c.dom.UserDataHandler;
60
61import javax.xml.parsers.DocumentBuilder;
62import javax.xml.parsers.DocumentBuilderFactory;
63import javax.xml.parsers.ParserConfigurationException;
64import javax.xml.soap.SOAPElement;
65import javax.xml.soap.SOAPException;
66import java.lang.reflect.Constructor;
67import java.text.MessageFormat;
68import java.util.logging.Logger;
69
70public 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}
683