View Javadoc

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