View Javadoc
1   /*
2    * Copyright 2005-2010 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.springframework.ws.soap.saaj.support;
18  
19  import java.util.Iterator;
20  import javax.xml.soap.Name;
21  import javax.xml.soap.Node;
22  import javax.xml.soap.SOAPElement;
23  import javax.xml.soap.Text;
24  
25  import org.springframework.util.StringUtils;
26  import org.springframework.xml.sax.AbstractXmlReader;
27  
28  import org.xml.sax.Attributes;
29  import org.xml.sax.InputSource;
30  import org.xml.sax.SAXException;
31  import org.xml.sax.SAXNotRecognizedException;
32  import org.xml.sax.SAXNotSupportedException;
33  import org.xml.sax.helpers.AttributesImpl;
34  
35  /**
36   * SAX <code>XMLReader</code> that reads from a SAAJ <code>Node</code>. Consumes <code>XMLEvents</code> from an
37   * <code>XMLEventReader</code>, and calls the corresponding methods on the SAX callback interfaces.
38   *
39   * @author Arjen Poutsma
40   * @see javax.xml.soap.Node
41   * @see javax.xml.soap.SOAPElement
42   * @since 1.0.0
43   */
44  public class SaajXmlReader extends AbstractXmlReader {
45  
46      private static final String NAMESPACES_FEATURE_NAME = "http://xml.org/sax/features/namespaces";
47  
48      private static final String NAMESPACE_PREFIXES_FEATURE_NAME = "http://xml.org/sax/features/namespace-prefixes";
49  
50      private final Node startNode;
51  
52      private boolean namespacesFeature = true;
53  
54      private boolean namespacePrefixesFeature = false;
55  
56      /**
57       * Constructs a new instance of the <code>SaajXmlReader</code> that reads from the given <code>Node</code>.
58       *
59       * @param startNode the SAAJ <code>Node</code> to read from
60       */
61      public SaajXmlReader(Node startNode) {
62          this.startNode = startNode;
63      }
64  
65      @Override
66      public boolean getFeature(String name) throws SAXNotRecognizedException, SAXNotSupportedException {
67          if (NAMESPACES_FEATURE_NAME.equals(name)) {
68              return namespacesFeature;
69          }
70          else if (NAMESPACE_PREFIXES_FEATURE_NAME.equals(name)) {
71              return namespacePrefixesFeature;
72          }
73          else {
74              return super.getFeature(name);
75          }
76      }
77  
78      @Override
79      public void setFeature(String name, boolean value) throws SAXNotRecognizedException, SAXNotSupportedException {
80          if (NAMESPACES_FEATURE_NAME.equals(name)) {
81              this.namespacesFeature = value;
82          }
83          else if (NAMESPACE_PREFIXES_FEATURE_NAME.equals(name)) {
84              this.namespacePrefixesFeature = value;
85          }
86          else {
87              super.setFeature(name, value);
88          }
89      }
90  
91      /**
92       * Parses the StAX XML reader passed at construction-time.
93       * <p/>
94       * <strong>Note</strong> that the given <code>InputSource</code> is not read, but ignored.
95       *
96       * @param ignored is ignored
97       * @throws org.xml.sax.SAXException A SAX exception, possibly wrapping a <code>XMLStreamException</code>
98       */
99      public final void parse(InputSource ignored) throws SAXException {
100         parse();
101     }
102 
103     /**
104      * Parses the StAX XML reader passed at construction-time.
105      * <p/>
106      * <strong>Note</strong> that the given system identifier is not read, but ignored.
107      *
108      * @param ignored is ignored
109      * @throws SAXException A SAX exception, possibly wrapping a <code>XMLStreamException</code>
110      */
111     public final void parse(String ignored) throws SAXException {
112         parse();
113     }
114 
115     private void parse() throws SAXException {
116         if (getContentHandler() != null) {
117             getContentHandler().startDocument();
118         }
119         handleNode(startNode);
120         if (getContentHandler() != null) {
121             getContentHandler().endDocument();
122         }
123     }
124 
125     private void handleNode(Node node) throws SAXException {
126         if (node instanceof SOAPElement) {
127             handleElement((SOAPElement) node);
128         }
129         else if (node instanceof Text) {
130             Text text = (Text) node;
131             handleText(text);
132         }
133     }
134 
135     private void handleElement(SOAPElement element) throws SAXException {
136         Name elementName = element.getElementName();
137         if (getContentHandler() != null) {
138             if (namespacesFeature) {
139                 for (Iterator<?> iterator = element.getNamespacePrefixes(); iterator.hasNext();) {
140                     String prefix = (String) iterator.next();
141                     String namespaceUri = element.getNamespaceURI(prefix);
142                     getContentHandler().startPrefixMapping(prefix, namespaceUri);
143                 }
144                 getContentHandler()
145                         .startElement(elementName.getURI(), elementName.getLocalName(), elementName.getQualifiedName(),
146                                 getAttributes(element));
147             }
148             else {
149                 getContentHandler().startElement("", "", elementName.getQualifiedName(), getAttributes(element));
150             }
151         }
152         for (Iterator<?> iterator = element.getChildElements(); iterator.hasNext();) {
153             Node child = (Node) iterator.next();
154             handleNode(child);
155         }
156         if (getContentHandler() != null) {
157             if (namespacesFeature) {
158                 getContentHandler()
159                         .endElement(elementName.getURI(), elementName.getLocalName(), elementName.getQualifiedName());
160                 for (Iterator<?> iterator = element.getNamespacePrefixes(); iterator.hasNext();) {
161                     String prefix = (String) iterator.next();
162                     getContentHandler().endPrefixMapping(prefix);
163                 }
164             }
165             else {
166                 getContentHandler().endElement("", "", elementName.getQualifiedName());
167             }
168         }
169     }
170 
171     private void handleText(Text text) throws SAXException {
172         if (getContentHandler() != null) {
173             char[] ch = text.getValue() != null ? text.getValue().toCharArray() : new char[0];
174             getContentHandler().characters(ch, 0, ch.length);
175         }
176     }
177 
178     private Attributes getAttributes(SOAPElement element) {
179         AttributesImpl attributes = new AttributesImpl();
180 
181         for (Iterator<?> iterator = element.getAllAttributes(); iterator.hasNext();) {
182             Name attributeName = (Name) iterator.next();
183             String namespace = attributeName.getURI();
184             if (namespace == null || !namespacesFeature) {
185                 namespace = "";
186             }
187             String attributeValue = element.getAttributeValue(attributeName);
188             attributes.addAttribute(namespace, attributeName.getLocalName(), attributeName.getQualifiedName(), "CDATA",
189                     attributeValue);
190         }
191         if (namespacePrefixesFeature) {
192             for (Iterator<?> iterator = element.getNamespacePrefixes(); iterator.hasNext();) {
193                 String prefix = (String) iterator.next();
194                 String namespaceUri = element.getNamespaceURI(prefix);
195                 String qName;
196                 if (StringUtils.hasLength(prefix)) {
197                     qName = "xmlns:" + prefix;
198                 }
199                 else {
200                     qName = "xmlns";
201                 }
202                 attributes.addAttribute("", "", qName, "CDATA", namespaceUri);
203             }
204         }
205         return attributes;
206     }
207 
208 }