View Javadoc

1   /*
2    * Copyright 2002-2009 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.xml.stream;
18  
19  import javax.xml.namespace.QName;
20  import javax.xml.stream.XMLStreamConstants;
21  import javax.xml.stream.XMLStreamException;
22  import javax.xml.stream.XMLStreamReader;
23  
24  import org.xml.sax.Attributes;
25  import org.xml.sax.SAXException;
26  import org.xml.sax.helpers.AttributesImpl;
27  
28  import org.springframework.util.StringUtils;
29  import org.springframework.xml.namespace.QNameUtils;
30  import org.springframework.xml.namespace.SimpleNamespaceContext;
31  
32  /**
33   * SAX <code>XMLReader</code> that reads from a StAX <code>XMLStreamReader</code>.  Reads from an
34   * <code>XMLStreamReader</code>, and calls the corresponding methods on the SAX callback interfaces.
35   *
36   * @author Arjen Poutsma
37   * @see XMLStreamReader
38   * @see #setContentHandler(org.xml.sax.ContentHandler)
39   * @see #setDTDHandler(org.xml.sax.DTDHandler)
40   * @see #setEntityResolver(org.xml.sax.EntityResolver)
41   * @see #setErrorHandler(org.xml.sax.ErrorHandler)
42   * @since 1.0.0
43   */
44  public class StaxStreamXmlReader extends AbstractStaxXmlReader {
45  
46      private final XMLStreamReader reader;
47  
48      private final SimpleNamespaceContext namespaceContext = new SimpleNamespaceContext();
49  
50      /**
51       * Constructs a new instance of the <code>StaxStreamXmlReader</code> that reads from the given
52       * <code>XMLStreamReader</code>.  The supplied stream reader must be in <code>XMLStreamConstants.START_DOCUMENT</code>
53       * or <code>XMLStreamConstants.START_ELEMENT</code> state.
54       *
55       * @param reader the <code>XMLEventReader</code> to read from
56       * @throws IllegalStateException if the reader is not at the start of a document or element
57       */
58      public StaxStreamXmlReader(XMLStreamReader reader) {
59          int event = reader.getEventType();
60          if (!(event == XMLStreamConstants.START_DOCUMENT || event == XMLStreamConstants.START_ELEMENT)) {
61              throw new IllegalStateException("XMLEventReader not at start of document or element");
62          }
63          this.reader = reader;
64      }
65  
66      protected void parseInternal() throws SAXException, XMLStreamException {
67          boolean documentStarted = false;
68          boolean documentEnded = false;
69          int elementDepth = 0;
70          int eventType = reader.getEventType();
71          while (true) {
72              if (eventType != XMLStreamConstants.START_DOCUMENT && eventType != XMLStreamConstants.END_DOCUMENT &&
73                      !documentStarted) {
74                  handleStartDocument();
75                  documentStarted = true;
76              }
77              switch (eventType) {
78                  case XMLStreamConstants.START_ELEMENT:
79                      elementDepth++;
80                      handleStartElement();
81                      break;
82                  case XMLStreamConstants.END_ELEMENT:
83                      elementDepth--;
84                      if (elementDepth >= 0) {
85                          handleEndElement();
86                      }
87                      break;
88                  case XMLStreamConstants.PROCESSING_INSTRUCTION:
89                      handleProcessingInstruction();
90                      break;
91                  case XMLStreamConstants.CHARACTERS:
92                  case XMLStreamConstants.SPACE:
93                  case XMLStreamConstants.CDATA:
94                      handleCharacters();
95                      break;
96                  case XMLStreamConstants.START_DOCUMENT:
97                      setLocator(reader.getLocation());
98                      handleStartDocument();
99                      documentStarted = true;
100                     break;
101                 case XMLStreamConstants.END_DOCUMENT:
102                     handleEndDocument();
103                     documentEnded = true;
104                     break;
105                 case XMLStreamConstants.COMMENT:
106                     handleComment();
107                     break;
108                 case XMLStreamConstants.DTD:
109                     handleDtd();
110                     break;
111                 case XMLStreamConstants.ENTITY_REFERENCE:
112                     handleEntityReference();
113                     break;
114             }
115             if (reader.hasNext() && elementDepth >= 0) {
116                 eventType = reader.next();
117             }
118             else {
119                 break;
120             }
121         }
122         if (!documentEnded) {
123             handleEndDocument();
124         }
125     }
126 
127     private void handleStartDocument() throws SAXException {
128         if (getContentHandler() != null) {
129             getContentHandler().startDocument();
130             if (reader.standaloneSet()) {
131                 setStandalone(reader.isStandalone());
132             }
133         }
134     }
135 
136     private void handleStartElement() throws SAXException {
137         if (getContentHandler() != null) {
138             QName qName = reader.getName();
139             if (hasNamespacesFeature()) {
140                 for (int i = 0; i < reader.getNamespaceCount(); i++) {
141                     String prefix = reader.getNamespacePrefix(i);
142                     if (prefix == null) {
143                         prefix = "";
144                     }
145                     startPrefixMapping(prefix, reader.getNamespaceURI(i));
146                 }
147                 for (int i = 0; i < reader.getAttributeCount(); i++) {
148                     String prefix = reader.getAttributePrefix(i);
149                     if (prefix == null) {
150                         prefix = "";
151                     }
152                     String namespace = reader.getAttributeNamespace(i);
153                     if (namespace == null) {
154                         continue;
155                     }
156                     startPrefixMapping(prefix, namespace);
157                 }
158                 getContentHandler()
159                         .startElement(qName.getNamespaceURI(), qName.getLocalPart(), QNameUtils.toQualifiedName(qName),
160                                 getAttributes());
161             }
162             else {
163                 getContentHandler().startElement("", "", QNameUtils.toQualifiedName(qName), getAttributes());
164             }
165         }
166     }
167 
168     private void startPrefixMapping(String prefix, String namespace) throws SAXException {
169         if (!namespaceContext.getNamespaceURI(prefix).equals(namespace)) {
170             getContentHandler().startPrefixMapping(prefix, namespace);
171             namespaceContext.bindNamespaceUri(prefix, namespace);
172         }
173     }
174 
175     private void handleEndElement() throws SAXException {
176         if (getContentHandler() != null) {
177             QName qName = reader.getName();
178             if (hasNamespacesFeature()) {
179                 getContentHandler()
180                         .endElement(qName.getNamespaceURI(), qName.getLocalPart(), QNameUtils.toQualifiedName(qName));
181                 for (int i = 0; i < reader.getNamespaceCount(); i++) {
182                     String prefix = reader.getNamespacePrefix(i);
183                     if (prefix == null) {
184                         prefix = "";
185                     }
186                     endPrefixMapping(prefix);
187                 }
188             }
189             else {
190                 getContentHandler().endElement("", "", QNameUtils.toQualifiedName(qName));
191             }
192         }
193     }
194 
195     private void endPrefixMapping(String prefix) throws SAXException {
196         if (namespaceContext.hasBinding(prefix)) {
197             getContentHandler().endPrefixMapping(prefix);
198             namespaceContext.removeBinding(prefix);
199         }
200     }
201 
202     private void handleCharacters() throws SAXException {
203         if (getContentHandler() != null && reader.isWhiteSpace()) {
204             getContentHandler()
205                     .ignorableWhitespace(reader.getTextCharacters(), reader.getTextStart(), reader.getTextLength());
206             return;
207         }
208         if (XMLStreamConstants.CDATA == reader.getEventType() && getLexicalHandler() != null) {
209             getLexicalHandler().startCDATA();
210         }
211         if (getContentHandler() != null) {
212             getContentHandler().characters(reader.getTextCharacters(), reader.getTextStart(), reader.getTextLength());
213         }
214         if (XMLStreamConstants.CDATA == reader.getEventType() && getLexicalHandler() != null) {
215             getLexicalHandler().endCDATA();
216         }
217     }
218 
219     private void handleComment() throws SAXException {
220         if (getLexicalHandler() != null) {
221             getLexicalHandler().comment(reader.getTextCharacters(), reader.getTextStart(), reader.getTextLength());
222         }
223     }
224 
225     private void handleDtd() throws SAXException {
226         if (getLexicalHandler() != null) {
227             javax.xml.stream.Location location = reader.getLocation();
228             getLexicalHandler().startDTD(null, location.getPublicId(), location.getSystemId());
229         }
230         if (getLexicalHandler() != null) {
231             getLexicalHandler().endDTD();
232         }
233     }
234 
235     private void handleEntityReference() throws SAXException {
236         if (getLexicalHandler() != null) {
237             getLexicalHandler().startEntity(reader.getLocalName());
238         }
239         if (getLexicalHandler() != null) {
240             getLexicalHandler().endEntity(reader.getLocalName());
241         }
242     }
243 
244     private void handleEndDocument() throws SAXException {
245         if (getContentHandler() != null) {
246             getContentHandler().endDocument();
247         }
248     }
249 
250     private void handleProcessingInstruction() throws SAXException {
251         if (getContentHandler() != null) {
252             getContentHandler().processingInstruction(reader.getPITarget(), reader.getPIData());
253         }
254     }
255 
256     private Attributes getAttributes() {
257         AttributesImpl attributes = new AttributesImpl();
258 
259         for (int i = 0; i < reader.getAttributeCount(); i++) {
260             String namespace = reader.getAttributeNamespace(i);
261             if (namespace == null || !hasNamespacesFeature()) {
262                 namespace = "";
263             }
264             String type = reader.getAttributeType(i);
265             if (type == null) {
266                 type = "CDATA";
267             }
268             attributes.addAttribute(namespace, reader.getAttributeLocalName(i),
269                     QNameUtils.toQualifiedName(reader.getAttributeName(i)), type, reader.getAttributeValue(i));
270         }
271         if (hasNamespacePrefixesFeature()) {
272             for (int i = 0; i < reader.getNamespaceCount(); i++) {
273                 String prefix = reader.getNamespacePrefix(i);
274                 String namespaceUri = reader.getNamespaceURI(i);
275                 String qName;
276                 if (StringUtils.hasLength(prefix)) {
277                     qName = "xmlns:" + prefix;
278                 }
279                 else {
280                     qName = "xmlns";
281                 }
282                 attributes.addAttribute("", "", qName, "CDATA", namespaceUri);
283             }
284         }
285 
286         return attributes;
287     }
288 
289 }