View Javadoc
1   /*
2    * Copyright 2011 Jakub Narloch
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5    * in compliance with the License. You may obtain a copy of the License at
6    *
7    * http://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software distributed under the License
10   * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11   * or implied. See the License for the specific language governing permissions and limitations under
12   * the License.
13   */
14  package org.exolab.castor.xml.util;
15  
16  import java.util.Enumeration;
17  
18  import javax.xml.stream.XMLStreamException;
19  import javax.xml.stream.XMLStreamWriter;
20  
21  import org.castor.core.util.Assert;
22  import org.exolab.castor.xml.NamespacesStack;
23  import org.xml.sax.Attributes;
24  import org.xml.sax.SAXException;
25  import org.xml.sax.helpers.DefaultHandler;
26  
27  /**
28   * A document handler that uses internally a instance of {@link XMLStreamWriter} to output the
29   * result xml.
30   *
31   * @author <a herf="mailto:jmnarloch AT gmail DOT com">Jakub Narloch</a>
32   * @version 1.3.3
33   * @since 1.3.3
34   */
35  public class StaxStreamHandler extends DefaultHandler {
36  
37    /**
38     * Represents the instance of {@link XMLStreamWriter} class, that will be used to output the
39     * 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, String qName, Attributes attributes)
97        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, namespacesStack.getNamespaceURI(prefix));
119       }
120     } catch (XMLStreamException e) {
121       convertToSAXException("Error occurred when writing the element start.", e);
122     }
123   }
124 
125   @Override
126   public void endElement(String uri, String localName, String qName) throws SAXException {
127     try {
128       // writes the element end
129       xmlStreamWriter.writeEndElement();
130     } catch (XMLStreamException e) {
131       convertToSAXException("Error occurred when writing the element end.", e);
132     }
133   }
134 
135   @Override
136   public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException {
137     try {
138       // writes the characters to output xml
139       xmlStreamWriter.writeCharacters(ch, start, length);
140     } catch (XMLStreamException e) {
141       convertToSAXException("Error occurred when writing the white spaces.", e);
142     }
143   }
144 
145   @Override
146   public void characters(char[] ch, int start, int length) throws SAXException {
147     try {
148       // writes the characters to output xml
149       xmlStreamWriter.writeCharacters(ch, start, length);
150     } catch (XMLStreamException e) {
151       convertToSAXException("Error occurred when writing the characters.", e);
152     }
153   }
154 
155   @Override
156   public void processingInstruction(String target, String data) throws SAXException {
157     try {
158       // writes the processing instruction
159       xmlStreamWriter.writeProcessingInstruction(target, data);
160     } catch (XMLStreamException e) {
161       convertToSAXException("Error occurred when writing the processing instruction.", e);
162     }
163   }
164 
165   /**
166    * Converts the passed exception into a {@link SAXException} instance with using the provided
167    * error message and exception cause.
168    *
169    * @param msg the error message
170    * @param e the inner cause of newly created exception
171    *
172    * @throws SAXException the newly created exception instance
173    */
174   private void convertToSAXException(String msg, XMLStreamException e) throws SAXException {
175     throw new SAXException(msg, e);
176   }
177 }