8159058: SAXParseException when sending soap message jdk8u141-b08

Thu, 12 Jan 2017 00:25:07 +0300

author
aefimov
date
Thu, 12 Jan 2017 00:25:07 +0300
changeset 1377
1d5f442d50df
parent 1376
9153a257b264
child 1378
34f55abdda46

8159058: SAXParseException when sending soap message
Reviewed-by: lancea, coffeys

src/share/jaxws_classes/com/sun/xml/internal/messaging/saaj/soap/impl/DetailImpl.java file | annotate | diff | comparison | revisions
src/share/jaxws_classes/com/sun/xml/internal/messaging/saaj/soap/impl/ElementImpl.java file | annotate | diff | comparison | revisions
src/share/jaxws_classes/com/sun/xml/internal/ws/api/message/saaj/SaajStaxWriter.java file | annotate | diff | comparison | revisions
     1.1 --- a/src/share/jaxws_classes/com/sun/xml/internal/messaging/saaj/soap/impl/DetailImpl.java	Mon May 01 10:54:49 2017 -0700
     1.2 +++ b/src/share/jaxws_classes/com/sun/xml/internal/messaging/saaj/soap/impl/DetailImpl.java	Thu Jan 12 00:25:07 2017 +0300
     1.3 @@ -1,5 +1,5 @@
     1.4  /*
     1.5 - * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
     1.6 + * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
     1.7   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     1.8   *
     1.9   * This code is free software; you can redistribute it and/or modify it
    1.10 @@ -47,13 +47,13 @@
    1.11      public DetailEntry addDetailEntry(Name name) throws SOAPException {
    1.12          DetailEntry entry = createDetailEntry(name);
    1.13          addNode(entry);
    1.14 -        return (DetailEntry) circumventBug5034339(entry);
    1.15 +        return entry;
    1.16      }
    1.17  
    1.18      public DetailEntry addDetailEntry(QName qname) throws SOAPException {
    1.19          DetailEntry entry = createDetailEntry(qname);
    1.20          addNode(entry);
    1.21 -        return (DetailEntry) circumventBug5034339(entry);
    1.22 +        return entry;
    1.23      }
    1.24  
    1.25      protected SOAPElement addElement(Name name) throws SOAPException {
    1.26 @@ -119,28 +119,4 @@
    1.27         return true;
    1.28     }
    1.29  
    1.30 -    //overriding this method since the only two uses of this method
    1.31 -    // are in ElementImpl and DetailImpl
    1.32 -    //whereas the original base impl does the correct job for calls to it inside ElementImpl
    1.33 -    // But it would not work for DetailImpl.
    1.34 -    protected SOAPElement circumventBug5034339(SOAPElement element) {
    1.35 -
    1.36 -        Name elementName = element.getElementName();
    1.37 -        if (!isNamespaceQualified(elementName)) {
    1.38 -            String prefix = elementName.getPrefix();
    1.39 -            String defaultNamespace = getNamespaceURI(prefix);
    1.40 -            if (defaultNamespace != null) {
    1.41 -                Name newElementName =
    1.42 -                    NameImpl.create(
    1.43 -                        elementName.getLocalName(),
    1.44 -                        elementName.getPrefix(),
    1.45 -                        defaultNamespace);
    1.46 -                SOAPElement newElement = createDetailEntry(newElementName);
    1.47 -                replaceChild(newElement, element);
    1.48 -                return newElement;
    1.49 -            }
    1.50 -        }
    1.51 -        return element;
    1.52 -    }
    1.53 -
    1.54  }
     2.1 --- a/src/share/jaxws_classes/com/sun/xml/internal/messaging/saaj/soap/impl/ElementImpl.java	Mon May 01 10:54:49 2017 -0700
     2.2 +++ b/src/share/jaxws_classes/com/sun/xml/internal/messaging/saaj/soap/impl/ElementImpl.java	Thu Jan 12 00:25:07 2017 +0300
     2.3 @@ -1,5 +1,5 @@
     2.4  /*
     2.5 - * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
     2.6 + * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
     2.7   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     2.8   *
     2.9   * This code is free software; you can redistribute it and/or modify it
    2.10 @@ -127,8 +127,11 @@
    2.11      }
    2.12  
    2.13      public SOAPElement addChildElement(String localName) throws SOAPException {
    2.14 -        return (SOAPElement) addChildElement(
    2.15 -            NameImpl.createFromUnqualifiedName(localName));
    2.16 +        String nsUri = getNamespaceURI("");
    2.17 +        Name name = (nsUri == null || nsUri.isEmpty())
    2.18 +                ?  NameImpl.createFromUnqualifiedName(localName)
    2.19 +                :  NameImpl.createFromQualifiedName(localName, nsUri);
    2.20 +        return addChildElement(name);
    2.21      }
    2.22  
    2.23      public SOAPElement addChildElement(String localName, String prefix)
    2.24 @@ -372,13 +375,13 @@
    2.25      protected SOAPElement addElement(Name name) throws SOAPException {
    2.26          SOAPElement newElement = createElement(name);
    2.27          addNode(newElement);
    2.28 -        return circumventBug5034339(newElement);
    2.29 +        return newElement;
    2.30      }
    2.31  
    2.32      protected SOAPElement addElement(QName name) throws SOAPException {
    2.33          SOAPElement newElement = createElement(name);
    2.34          addNode(newElement);
    2.35 -        return circumventBug5034339(newElement);
    2.36 +        return newElement;
    2.37      }
    2.38  
    2.39      protected SOAPElement createElement(Name name) {
    2.40 @@ -1201,26 +1204,6 @@
    2.41          return !"".equals(name.getNamespaceURI());
    2.42      }
    2.43  
    2.44 -    protected SOAPElement circumventBug5034339(SOAPElement element) {
    2.45 -
    2.46 -        Name elementName = element.getElementName();
    2.47 -        if (!isNamespaceQualified(elementName)) {
    2.48 -            String prefix = elementName.getPrefix();
    2.49 -            String defaultNamespace = getNamespaceURI(prefix);
    2.50 -            if (defaultNamespace != null) {
    2.51 -                Name newElementName =
    2.52 -                    NameImpl.create(
    2.53 -                        elementName.getLocalName(),
    2.54 -                        elementName.getPrefix(),
    2.55 -                        defaultNamespace);
    2.56 -                SOAPElement newElement = createElement(newElementName);
    2.57 -                replaceChild(newElement, element);
    2.58 -                return newElement;
    2.59 -            }
    2.60 -        }
    2.61 -        return element;
    2.62 -    }
    2.63 -
    2.64      //TODO: This is a temporary SAAJ workaround for optimizing XWS
    2.65      // should be removed once the corresponding JAXP bug is fixed
    2.66      // It appears the bug will be fixed in JAXP 1.4 (not by Appserver 9 timeframe)
     3.1 --- a/src/share/jaxws_classes/com/sun/xml/internal/ws/api/message/saaj/SaajStaxWriter.java	Mon May 01 10:54:49 2017 -0700
     3.2 +++ b/src/share/jaxws_classes/com/sun/xml/internal/ws/api/message/saaj/SaajStaxWriter.java	Thu Jan 12 00:25:07 2017 +0300
     3.3 @@ -1,5 +1,5 @@
     3.4  /*
     3.5 - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
     3.6 + * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved.
     3.7   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     3.8   *
     3.9   * This code is free software; you can redistribute it and/or modify it
    3.10 @@ -25,8 +25,10 @@
    3.11  
    3.12  package com.sun.xml.internal.ws.api.message.saaj;
    3.13  
    3.14 +import java.util.Iterator;
    3.15  import java.util.Arrays;
    3.16 -import java.util.Iterator;
    3.17 +import java.util.List;
    3.18 +import java.util.LinkedList;
    3.19  
    3.20  import javax.xml.namespace.NamespaceContext;
    3.21  import javax.xml.namespace.QName;
    3.22 @@ -42,6 +44,17 @@
    3.23  /**
    3.24   * SaajStaxWriter builds a SAAJ SOAPMessage by using XMLStreamWriter interface.
    3.25   *
    3.26 + * <p>
    3.27 + * Defers creation of SOAPElement until all the aspects of the name of the element are known.
    3.28 + * In some cases, the namespace uri is indicated only by the {@link #writeNamespace(String, String)} call.
    3.29 + * After opening an element ({@code writeStartElement}, {@code writeEmptyElement} methods), all attributes
    3.30 + * and namespace assignments are retained within {@link DeferredElement} object ({@code deferredElement} field).
    3.31 + * As soon as any other method than {@code writeAttribute}, {@code writeNamespace}, {@code writeDefaultNamespace}
    3.32 + * or {@code setNamespace} is called, the contents of {@code deferredElement} is transformed into new SOAPElement
    3.33 + * (which is appropriately inserted into the SOAPMessage under construction).
    3.34 + * This mechanism is necessary to fix JDK-8159058 issue.
    3.35 + * </p>
    3.36 + *
    3.37   * @author shih-chang.chen@oracle.com
    3.38   */
    3.39  public class SaajStaxWriter implements XMLStreamWriter {
    3.40 @@ -49,6 +62,7 @@
    3.41      protected SOAPMessage soap;
    3.42      protected String envURI;
    3.43      protected SOAPElement currentElement;
    3.44 +    protected DeferredElement deferredElement;
    3.45  
    3.46      static final protected String Envelope = "Envelope";
    3.47      static final protected String Header = "Header";
    3.48 @@ -59,6 +73,7 @@
    3.49          soap = msg;
    3.50          currentElement = soap.getSOAPPart().getEnvelope();
    3.51          envURI = currentElement.getNamespaceURI();
    3.52 +        this.deferredElement = new DeferredElement();
    3.53      }
    3.54  
    3.55      public SOAPMessage getSOAPMessage() {
    3.56 @@ -67,11 +82,8 @@
    3.57  
    3.58      @Override
    3.59      public void writeStartElement(final String localName) throws XMLStreamException {
    3.60 -        try {
    3.61 -            currentElement = currentElement.addChildElement(localName);
    3.62 -        } catch (SOAPException e) {
    3.63 -            throw new XMLStreamException(e);
    3.64 -        }
    3.65 +        currentElement = deferredElement.flushTo(currentElement);
    3.66 +        deferredElement.setLocalName(localName);
    3.67      }
    3.68  
    3.69      @Override
    3.70 @@ -81,8 +93,10 @@
    3.71  
    3.72      @Override
    3.73      public void writeStartElement(final String prefix, final String ln, final String ns) throws XMLStreamException {
    3.74 -        try {
    3.75 -            if (envURI.equals(ns)) {
    3.76 +        currentElement = deferredElement.flushTo(currentElement);
    3.77 +
    3.78 +        if (envURI.equals(ns)) {
    3.79 +            try {
    3.80                  if (Envelope.equals(ln)) {
    3.81                      currentElement = soap.getSOAPPart().getEnvelope();
    3.82                      fixPrefix(prefix);
    3.83 @@ -96,13 +110,16 @@
    3.84                      fixPrefix(prefix);
    3.85                      return;
    3.86                  }
    3.87 +            } catch (SOAPException e) {
    3.88 +                throw new XMLStreamException(e);
    3.89              }
    3.90 -            currentElement = (prefix == null) ?
    3.91 -                    currentElement.addChildElement(new QName(ns, ln)) :
    3.92 -                    currentElement.addChildElement(ln, prefix, ns);
    3.93 -        } catch (SOAPException e) {
    3.94 -            throw new XMLStreamException(e);
    3.95 +
    3.96          }
    3.97 +
    3.98 +        deferredElement.setLocalName(ln);
    3.99 +        deferredElement.setNamespaceUri(ns);
   3.100 +        deferredElement.setPrefix(prefix);
   3.101 +
   3.102      }
   3.103  
   3.104      private void fixPrefix(final String prfx) throws XMLStreamException {
   3.105 @@ -129,11 +146,13 @@
   3.106  
   3.107      @Override
   3.108      public void writeEndElement() throws XMLStreamException {
   3.109 +        currentElement = deferredElement.flushTo(currentElement);
   3.110          if (currentElement != null) currentElement = currentElement.getParentElement();
   3.111      }
   3.112  
   3.113      @Override
   3.114      public void writeEndDocument() throws XMLStreamException {
   3.115 +        currentElement = deferredElement.flushTo(currentElement);
   3.116      }
   3.117  
   3.118      @Override
   3.119 @@ -151,19 +170,14 @@
   3.120  
   3.121      @Override
   3.122      public void writeAttribute(final String prefix, final String ns, final String ln, final String value) throws XMLStreamException {
   3.123 -        try {
   3.124 -            if (ns == null) {
   3.125 -                if (prefix == null && xmlns.equals(ln)) {
   3.126 -                    currentElement.addNamespaceDeclaration("", value);
   3.127 -                } else {
   3.128 -                    currentElement.setAttributeNS("", ln, value);
   3.129 -                }
   3.130 +        if (ns == null && prefix == null && xmlns.equals(ln)) {
   3.131 +            writeNamespace("", value);
   3.132 +        } else {
   3.133 +            if (deferredElement.isInitialized()) {
   3.134 +                deferredElement.addAttribute(prefix, ns, ln, value);
   3.135              } else {
   3.136 -                QName name = (prefix == null) ? new QName(ns, ln) : new QName(ns, ln, prefix);
   3.137 -                currentElement.addAttribute(name, value);
   3.138 +                addAttibuteToElement(currentElement, prefix, ns, ln, value);
   3.139              }
   3.140 -        } catch (SOAPException e) {
   3.141 -            throw new XMLStreamException(e);
   3.142          }
   3.143      }
   3.144  
   3.145 @@ -174,16 +188,16 @@
   3.146  
   3.147      @Override
   3.148      public void writeNamespace(String prefix, final String uri) throws XMLStreamException {
   3.149 -
   3.150          // make prefix default if null or "xmlns" (according to javadoc)
   3.151 -        if (prefix == null || "xmlns".equals(prefix)) {
   3.152 -            prefix = "";
   3.153 -        }
   3.154 -
   3.155 -        try {
   3.156 -            currentElement.addNamespaceDeclaration(prefix, uri);
   3.157 -        } catch (SOAPException e) {
   3.158 -            throw new XMLStreamException(e);
   3.159 +        String thePrefix = prefix == null || "xmlns".equals(prefix) ? "" : prefix;
   3.160 +        if (deferredElement.isInitialized()) {
   3.161 +            deferredElement.addNamespaceDeclaration(thePrefix, uri);
   3.162 +        } else {
   3.163 +            try {
   3.164 +                currentElement.addNamespaceDeclaration(thePrefix, uri);
   3.165 +            } catch (SOAPException e) {
   3.166 +                throw new XMLStreamException(e);
   3.167 +            }
   3.168          }
   3.169      }
   3.170  
   3.171 @@ -194,35 +208,40 @@
   3.172  
   3.173      @Override
   3.174      public void writeComment(final String data) throws XMLStreamException {
   3.175 +        currentElement = deferredElement.flushTo(currentElement);
   3.176          Comment c = soap.getSOAPPart().createComment(data);
   3.177          currentElement.appendChild(c);
   3.178      }
   3.179  
   3.180      @Override
   3.181      public void writeProcessingInstruction(final String target) throws XMLStreamException {
   3.182 +        currentElement = deferredElement.flushTo(currentElement);
   3.183          Node n = soap.getSOAPPart().createProcessingInstruction(target, "");
   3.184          currentElement.appendChild(n);
   3.185      }
   3.186  
   3.187      @Override
   3.188      public void writeProcessingInstruction(final String target, final String data) throws XMLStreamException {
   3.189 +        currentElement = deferredElement.flushTo(currentElement);
   3.190          Node n = soap.getSOAPPart().createProcessingInstruction(target, data);
   3.191          currentElement.appendChild(n);
   3.192      }
   3.193  
   3.194      @Override
   3.195      public void writeCData(final String data) throws XMLStreamException {
   3.196 +        currentElement = deferredElement.flushTo(currentElement);
   3.197          Node n = soap.getSOAPPart().createCDATASection(data);
   3.198          currentElement.appendChild(n);
   3.199      }
   3.200  
   3.201      @Override
   3.202      public void writeDTD(final String dtd) throws XMLStreamException {
   3.203 -        //TODO ... Don't do anything here
   3.204 +        currentElement = deferredElement.flushTo(currentElement);
   3.205      }
   3.206  
   3.207      @Override
   3.208      public void writeEntityRef(final String name) throws XMLStreamException {
   3.209 +        currentElement = deferredElement.flushTo(currentElement);
   3.210          Node n = soap.getSOAPPart().createEntityReference(name);
   3.211          currentElement.appendChild(n);
   3.212      }
   3.213 @@ -250,6 +269,7 @@
   3.214  
   3.215      @Override
   3.216      public void writeCharacters(final String text) throws XMLStreamException {
   3.217 +        currentElement = deferredElement.flushTo(currentElement);
   3.218          try {
   3.219              currentElement.addTextNode(text);
   3.220          } catch (SOAPException e) {
   3.221 @@ -259,6 +279,7 @@
   3.222  
   3.223      @Override
   3.224      public void writeCharacters(final char[] text, final int start, final int len) throws XMLStreamException {
   3.225 +        currentElement = deferredElement.flushTo(currentElement);
   3.226          char[] chr = (start == 0 && len == text.length) ? text : Arrays.copyOfRange(text, start, start + len);
   3.227          try {
   3.228              currentElement.addTextNode(new String(chr));
   3.229 @@ -274,10 +295,16 @@
   3.230  
   3.231      @Override
   3.232      public void setPrefix(final String prefix, final String uri) throws XMLStreamException {
   3.233 -        try {
   3.234 -            this.currentElement.addNamespaceDeclaration(prefix, uri);
   3.235 -        } catch (SOAPException e) {
   3.236 -            throw new XMLStreamException(e);
   3.237 +        // TODO: this in fact is not what would be expected from XMLStreamWriter
   3.238 +        //       (e.g. XMLStreamWriter for writing to output stream does not write anything as result of
   3.239 +        //        this method, it just rememebers that given prefix is associated with the given uri
   3.240 +        //        for the scope; to actually declare the prefix assignment in the resulting XML, one
   3.241 +        //        needs to call writeNamespace(...) method
   3.242 +        // Kept for backwards compatibility reasons - this might be worth of further investigation.
   3.243 +        if (deferredElement.isInitialized()) {
   3.244 +            deferredElement.addNamespaceDeclaration(prefix, uri);
   3.245 +        } else {
   3.246 +            throw new XMLStreamException("Namespace not associated with any element");
   3.247          }
   3.248      }
   3.249  
   3.250 @@ -308,12 +335,12 @@
   3.251                  return currentElement.lookupPrefix(namespaceURI);
   3.252              }
   3.253              public Iterator getPrefixes(final String namespaceURI) {
   3.254 -                return new Iterator() {
   3.255 +                return new Iterator<String>() {
   3.256                      String prefix = getPrefix(namespaceURI);
   3.257                      public boolean hasNext() {
   3.258                          return (prefix != null);
   3.259                      }
   3.260 -                    public Object next() {
   3.261 +                    public String next() {
   3.262                          if (!hasNext()) throw new java.util.NoSuchElementException();
   3.263                          String next = prefix;
   3.264                          prefix = null;
   3.265 @@ -324,4 +351,209 @@
   3.266              }
   3.267          };
   3.268      }
   3.269 +
   3.270 +    static void addAttibuteToElement(SOAPElement element, String prefix, String ns, String ln, String value)
   3.271 +            throws XMLStreamException {
   3.272 +        try {
   3.273 +            if (ns == null) {
   3.274 +                element.setAttributeNS("", ln, value);
   3.275 +            } else {
   3.276 +                QName name = prefix == null ? new QName(ns, ln) : new QName(ns, ln, prefix);
   3.277 +                element.addAttribute(name, value);
   3.278 +            }
   3.279 +        } catch (SOAPException e) {
   3.280 +            throw new XMLStreamException(e);
   3.281 +        }
   3.282 +    }
   3.283 +
   3.284 +    /**
   3.285 +     * Holds details of element that needs to be deferred in order to manage namespace assignments correctly.
   3.286 +     *
   3.287 +     * <p>
   3.288 +     * An instance of can be set with all the aspects of the element name (local name, prefix, namespace uri).
   3.289 +     * Attributes and namespace declarations (special case of attribute) can be added.
   3.290 +     * Namespace declarations are handled so that the element namespace is updated if it is implied by the namespace
   3.291 +     * declaration and the namespace was not set to non-{@code null} value previously.
   3.292 +     * </p>
   3.293 +     *
   3.294 +     * <p>
   3.295 +     * The state of this object can be {@link #flushTo(SOAPElement) flushed} to SOAPElement - new SOAPElement will
   3.296 +     * be added a child element; the new element will have exactly the shape as represented by the state of this
   3.297 +     * object. Note that the {@link #flushTo(SOAPElement)} method does nothing
   3.298 +     * (and returns the argument immediately) if the state of this object is not initialized
   3.299 +     * (i.e. local name is null).
   3.300 +     * </p>
   3.301 +     *
   3.302 +     * @author ondrej.cerny@oracle.com
   3.303 +     */
   3.304 +    static class DeferredElement {
   3.305 +        private String prefix;
   3.306 +        private String localName;
   3.307 +        private String namespaceUri;
   3.308 +        private final List<NamespaceDeclaration> namespaceDeclarations;
   3.309 +        private final List<AttributeDeclaration> attributeDeclarations;
   3.310 +
   3.311 +        DeferredElement() {
   3.312 +            this.namespaceDeclarations = new LinkedList<NamespaceDeclaration>();
   3.313 +            this.attributeDeclarations = new LinkedList<AttributeDeclaration>();
   3.314 +            reset();
   3.315 +        }
   3.316 +
   3.317 +
   3.318 +        /**
   3.319 +         * Set prefix of the element.
   3.320 +         * @param prefix namespace prefix
   3.321 +         */
   3.322 +        public void setPrefix(final String prefix) {
   3.323 +            this.prefix = prefix;
   3.324 +        }
   3.325 +
   3.326 +        /**
   3.327 +         * Set local name of the element.
   3.328 +         *
   3.329 +         * <p>
   3.330 +         *     This method initializes the element.
   3.331 +         * </p>
   3.332 +         *
   3.333 +         * @param localName local name {@code not null}
   3.334 +         */
   3.335 +        public void setLocalName(final String localName) {
   3.336 +            if (localName == null) {
   3.337 +                throw new IllegalArgumentException("localName can not be null");
   3.338 +            }
   3.339 +            this.localName = localName;
   3.340 +        }
   3.341 +
   3.342 +        /**
   3.343 +         * Set namespace uri.
   3.344 +         *
   3.345 +         * @param namespaceUri namespace uri
   3.346 +         */
   3.347 +        public void setNamespaceUri(final String namespaceUri) {
   3.348 +            this.namespaceUri = namespaceUri;
   3.349 +        }
   3.350 +
   3.351 +        /**
   3.352 +         * Adds namespace prefix assignment to the element.
   3.353 +         *
   3.354 +         * @param prefix prefix (not {@code null})
   3.355 +         * @param namespaceUri namespace uri
   3.356 +         */
   3.357 +        public void addNamespaceDeclaration(final String prefix, final String namespaceUri) {
   3.358 +            if (null == this.namespaceUri && null != namespaceUri && prefix.equals(emptyIfNull(this.prefix))) {
   3.359 +                this.namespaceUri = namespaceUri;
   3.360 +            }
   3.361 +            this.namespaceDeclarations.add(new NamespaceDeclaration(prefix, namespaceUri));
   3.362 +        }
   3.363 +
   3.364 +        /**
   3.365 +         * Adds attribute to the element.
   3.366 +         * @param prefix prefix
   3.367 +         * @param ns namespace
   3.368 +         * @param ln local name
   3.369 +         * @param value value
   3.370 +         */
   3.371 +        public void addAttribute(final String prefix, final String ns, final String ln, final String value) {
   3.372 +            if (ns == null && prefix == null && xmlns.equals(ln)) {
   3.373 +                this.addNamespaceDeclaration(prefix, value);
   3.374 +            } else {
   3.375 +                this.attributeDeclarations.add(new AttributeDeclaration(prefix, ns, ln, value));
   3.376 +            }
   3.377 +        }
   3.378 +
   3.379 +        /**
   3.380 +         * Flushes state of this element to the {@code target} element.
   3.381 +         *
   3.382 +         * <p>
   3.383 +         * If this element is initialized then it is added with all the namespace declarations and attributes
   3.384 +         * to the {@code target} element as a child. The state of this element is reset to uninitialized.
   3.385 +         * The newly added element object is returned.
   3.386 +         * </p>
   3.387 +         * <p>
   3.388 +         * If this element is not initialized then the {@code target} is returned immediately, nothing else is done.
   3.389 +         * </p>
   3.390 +         *
   3.391 +         * @param target target element
   3.392 +         * @return {@code target} or new element
   3.393 +         * @throws XMLStreamException on error
   3.394 +         */
   3.395 +        public SOAPElement flushTo(final SOAPElement target) throws XMLStreamException {
   3.396 +            try {
   3.397 +                if (this.localName != null) {
   3.398 +                    // add the element appropriately (based on namespace declaration)
   3.399 +                    final SOAPElement newElement;
   3.400 +                    if (this.namespaceUri == null) {
   3.401 +                        // add element with inherited scope
   3.402 +                        newElement = target.addChildElement(this.localName);
   3.403 +                    } else if (prefix == null) {
   3.404 +                        newElement = target.addChildElement(new QName(this.namespaceUri, this.localName));
   3.405 +                    } else {
   3.406 +                        newElement = target.addChildElement(this.localName, this.prefix, this.namespaceUri);
   3.407 +                    }
   3.408 +                    // add namespace declarations
   3.409 +                    for (NamespaceDeclaration namespace : this.namespaceDeclarations) {
   3.410 +                        target.addNamespaceDeclaration(namespace.prefix, namespace.namespaceUri);
   3.411 +                    }
   3.412 +                    // add attribute declarations
   3.413 +                    for (AttributeDeclaration attribute : this.attributeDeclarations) {
   3.414 +                        addAttibuteToElement(newElement,
   3.415 +                                attribute.prefix, attribute.namespaceUri, attribute.localName, attribute.value);
   3.416 +                    }
   3.417 +                    // reset state
   3.418 +                    this.reset();
   3.419 +
   3.420 +                    return newElement;
   3.421 +                } else {
   3.422 +                    return target;
   3.423 +                }
   3.424 +                // else after reset state -> not initialized
   3.425 +            } catch (SOAPException e) {
   3.426 +                throw new XMLStreamException(e);
   3.427 +            }
   3.428 +        }
   3.429 +
   3.430 +        /**
   3.431 +         * Is the element initialized?
   3.432 +         * @return boolean indicating whether it was initialized after last flush
   3.433 +         */
   3.434 +        public boolean isInitialized() {
   3.435 +            return this.localName != null;
   3.436 +        }
   3.437 +
   3.438 +        private void reset() {
   3.439 +            this.localName = null;
   3.440 +            this.prefix = null;
   3.441 +            this.namespaceUri = null;
   3.442 +            this.namespaceDeclarations.clear();
   3.443 +            this.attributeDeclarations.clear();
   3.444 +        }
   3.445 +
   3.446 +        private static String emptyIfNull(String s) {
   3.447 +            return s == null ? "" : s;
   3.448 +        }
   3.449 +    }
   3.450 +
   3.451 +    static class NamespaceDeclaration {
   3.452 +        final String prefix;
   3.453 +        final String namespaceUri;
   3.454 +
   3.455 +        NamespaceDeclaration(String prefix, String namespaceUri) {
   3.456 +            this.prefix = prefix;
   3.457 +            this.namespaceUri = namespaceUri;
   3.458 +        }
   3.459 +    }
   3.460 +
   3.461 +    static class AttributeDeclaration {
   3.462 +        final String prefix;
   3.463 +        final String namespaceUri;
   3.464 +        final String localName;
   3.465 +        final String value;
   3.466 +
   3.467 +        AttributeDeclaration(String prefix, String namespaceUri, String localName, String value) {
   3.468 +            this.prefix = prefix;
   3.469 +            this.namespaceUri = namespaceUri;
   3.470 +            this.localName = localName;
   3.471 +            this.value = value;
   3.472 +        }
   3.473 +    }
   3.474  }

mercurial