View Javadoc
1   /*
2    * Copyright 2011 Jakub Narloch
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  package org.exolab.castor.xml.util;
17  
18  import java.util.Enumeration;
19  
20  import javax.xml.stream.XMLStreamException;
21  import javax.xml.stream.XMLStreamWriter;
22  
23  import org.castor.core.util.Assert;
24  import org.exolab.castor.xml.NamespacesStack;
25  import org.xml.sax.Attributes;
26  import org.xml.sax.SAXException;
27  import org.xml.sax.helpers.DefaultHandler;
28  
29  /**
30   * A document handler that uses internally a instance of {@link XMLStreamWriter} to output the result xml.
31   *
32   * @author <a herf="mailto:jmnarloch AT gmail DOT com">Jakub Narloch</a>
33   * @version 1.3.3
34   * @since 1.3.3
35   */
36  public class StaxStreamHandler extends DefaultHandler {
37  
38      /**
39       * Represents the instance of {@link XMLStreamWriter} class, that will be used to output the marshalled object.
40       */
41      private final XMLStreamWriter xmlStreamWriter;
42  
43      /**
44       * Instance of {@link org.exolab.castor.xml.Namespaces} used for handling the namespace.
45       */
46      private NamespacesStack namespacesStack = new NamespacesStack();
47  
48      /**
49       * Flag indicating whether the new namespace scope is required to create.
50       */
51      private boolean createNamespaceScope = true;
52  
53      /**
54       * Creates new instance of {@link StaxStreamHandler} class.
55       *
56       * @param xmlStreamWriter the instance of {@link XMLStreamWriter} to use
57       *
58       * @throws IllegalArgumentException if xmlStreamWriter is null
59       */
60      public StaxStreamHandler(XMLStreamWriter xmlStreamWriter) {
61          Assert.paramNotNull(xmlStreamWriter, "xmlStreamWriter");
62  
63          this.xmlStreamWriter = xmlStreamWriter;
64      }
65  
66      @Override
67      public void startDocument() throws SAXException {
68          try {
69              xmlStreamWriter.writeStartDocument();
70          } catch (XMLStreamException e) {
71              convertToSAXException("Error occurred when writing the document start.", e);
72          }
73      }
74  
75      @Override
76      public void endDocument() throws SAXException {
77          try {
78              xmlStreamWriter.writeEndDocument();
79              xmlStreamWriter.flush();
80          } catch (XMLStreamException e) {
81              convertToSAXException("Error occurred when writing the document end.", e);
82          }
83      }
84  
85      @Override
86      public void startPrefixMapping(String prefix, String uri) throws SAXException {
87          if (createNamespaceScope) {
88              namespacesStack.addNewNamespaceScope();
89              createNamespaceScope = false;
90          }
91  
92          namespacesStack.addNamespace(prefix, uri);
93      }
94  
95      @Override
96      public void startElement(String uri, String localName,
97                               String qName, Attributes attributes) throws SAXException {
98          try {
99              // writes the element start
100             xmlStreamWriter.writeStartElement(qName);
101 
102             // iterates through all attributes and writes them
103             for (int i = 0; i < attributes.getLength(); i++) {
104                 xmlStreamWriter.writeAttribute(attributes.getQName(i), attributes.getValue(i));
105             }
106 
107             // retrieves the default namespace
108             String defaultNamespace = namespacesStack.getDefaultNamespaceURI();
109             if(defaultNamespace != null && defaultNamespace.length() > 0) {
110                 xmlStreamWriter.setDefaultNamespace(defaultNamespace);
111             }
112 
113             // iterates over all namespaces declared in current scope
114             Enumeration enumeration = namespacesStack.getLocalNamespacePrefixes();
115             String prefix;
116             while(enumeration.hasMoreElements()) {
117                 prefix = (String)enumeration.nextElement();
118                 xmlStreamWriter.writeNamespace(prefix,
119                         namespacesStack.getNamespaceURI(prefix));
120             }
121         } catch (XMLStreamException e) {
122             convertToSAXException("Error occurred when writing the element start.", e);
123         }
124     }
125 
126     @Override
127     public void endElement(String uri, String localName, String qName) throws SAXException {
128         try {
129             // writes the element end
130             xmlStreamWriter.writeEndElement();
131         } catch (XMLStreamException e) {
132             convertToSAXException("Error occurred when writing the element end.", e);
133         }
134     }
135 
136     @Override
137     public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException {
138         try {
139             // writes the characters to output xml
140             xmlStreamWriter.writeCharacters(ch, start, length);
141         } catch (XMLStreamException e) {
142             convertToSAXException("Error occurred when writing the white spaces.", e);
143         }
144     }
145 
146     @Override
147     public void characters(char[] ch, int start, int length) throws SAXException {
148         try {
149             // writes the characters to output xml
150             xmlStreamWriter.writeCharacters(ch, start, length);
151         } catch (XMLStreamException e) {
152             convertToSAXException("Error occurred when writing the characters.", e);
153         }
154     }
155 
156     @Override
157     public void processingInstruction(String target, String data) throws SAXException {
158         try {
159             // writes the processing instruction
160             xmlStreamWriter.writeProcessingInstruction(target, data);
161         } catch (XMLStreamException e) {
162             convertToSAXException("Error occurred when writing the processing instruction.", e);
163         }
164     }
165 
166     /**
167      * Converts the passed exception into a {@link SAXException} instance with using the provided error message and
168      * exception cause.
169      *
170      * @param msg the error message
171      * @param e   the inner cause of newly created exception
172      *
173      * @throws SAXException the newly created exception instance
174      */
175     private void convertToSAXException(String msg, XMLStreamException e) throws SAXException {
176         throw new SAXException(msg, e);
177     }
178 }