View Javadoc
1   package org.exolab.castor.xml.util;
2   
3   import java.util.StringTokenizer;
4   
5   import javax.xml.parsers.ParserConfigurationException;
6   import javax.xml.parsers.SAXParser;
7   import javax.xml.parsers.SAXParserFactory;
8   
9   import org.apache.commons.logging.Log;
10  import org.apache.commons.logging.LogFactory;
11  import org.castor.core.util.AbstractProperties;
12  import org.castor.core.util.Messages;
13  import org.castor.xml.XMLProperties;
14  import org.exolab.castor.xml.OutputFormat;
15  import org.exolab.castor.xml.Serializer;
16  import org.exolab.castor.xml.XMLSerializerFactory;
17  import org.xml.sax.Parser;
18  import org.xml.sax.SAXException;
19  import org.xml.sax.SAXNotRecognizedException;
20  import org.xml.sax.SAXNotSupportedException;
21  import org.xml.sax.XMLReader;
22  
23  /**
24   * A couple of routines to manipulate XMLParser instances. Mostly extracted
25   * from 'old' LocalConfiguration class.
26   * 
27   * @author Joachim Grueneis, jgrueneis_at_gmail_dot_com
28   * @version $Id$
29   * @since 1.1.3
30   */
31  public class XMLParserUtils {
32  
33      /**
34       * Logger to be used.
35       */
36      static final Log LOG = LogFactory.getFactory().getInstance(XMLParserUtils.class);
37      
38      /** 
39       * To set validation feature of XMLReader.
40       */
41      private static final String VALIDATION = "http://xml.org/sax/features/validation";
42      
43      /** 
44       * To set namespaces feature of XMLReader. 
45       */
46      private static final String NAMESPACES = "http://xml.org/sax/features/namespaces";
47  
48      /**
49       * Sets features on XML reader instance.
50       * @param properties the Properties to read parser features from
51       * @param defaultFeatures any default features to use
52       * @param validation Whether to enable validation or not.
53       * @param namespaces Whether to enable namespace support for not.
54       * @param xmlReader The XMLReader instance to configure.
55       */
56      public static void setFeaturesOnXmlReader(
57              final String parserFeatures,
58              final String parserFeaturesToDisable,
59              final boolean validation, 
60              final boolean namespaces, 
61              final XMLReader xmlReader) {
62          try {
63              xmlReader.setFeature(VALIDATION, validation);
64              xmlReader.setFeature(NAMESPACES, namespaces);
65              enableFeatures(parserFeatures, xmlReader);
66              disableFeatures(parserFeaturesToDisable, xmlReader);
67          } catch (SAXException except) {
68              LOG.error(Messages.format("conf.configurationError", except));
69          }
70      }
71  
72      /**
73       * Enables selected features on the XMLReader instance.
74       * @param features Features to enable
75       * @param xmlReader XMLReader instance to be configured.
76       * @throws SAXNotRecognizedException If the feature is not recognized by the XMLReader.
77       * @throws SAXNotSupportedException If the feature is not supported by the XMLReader.
78       */
79      private static void enableFeatures(final String features, final XMLReader xmlReader) 
80          throws SAXNotRecognizedException, SAXNotSupportedException {
81          StringTokenizer token;
82          if (features != null) {
83              token = new StringTokenizer(features, ", ");
84              while (token.hasMoreTokens()) {
85                  xmlReader.setFeature(token.nextToken(), true);
86              }
87          }
88      }
89  
90      /**
91       * Disables selected features on the XMLReader instance.
92       * @param features Features to disable
93       * @param xmlReader XMLReader instance to be configured.
94       * @throws SAXNotRecognizedException If the feature is not recognized by the XMLReader.
95       * @throws SAXNotSupportedException If the feature is not supported by the XMLReader.
96       */
97      private static void disableFeatures(final String features, final XMLReader xmlReader) 
98          throws SAXNotRecognizedException, SAXNotSupportedException {
99          StringTokenizer token;
100         if (features != null) {
101             token = new StringTokenizer(features, ", ");
102             while (token.hasMoreTokens()) {
103                 xmlReader.setFeature(token.nextToken(), false);
104             }
105         }
106     }
107     
108     /**
109      * To get a SAXParser instance which is then used to get either
110      * parser or XMLReader.
111      * @param validation validation flag to set into parser factory
112      * @param namespaces namespace flag to set into parser factory
113      * @return the SAXParser for further use
114      */
115     public static SAXParser getSAXParser(final boolean validation, final boolean namespaces) {
116         SAXParser saxParser = null;
117 
118         SAXParserFactory factory = SAXParserFactory.newInstance();
119         factory.setNamespaceAware(namespaces);
120         factory.setValidating(validation);
121         try {
122             saxParser = factory.newSAXParser();
123             if (LOG.isDebugEnabled()) {
124                 LOG.debug("Successfully instantiated a JAXP SAXParser instance.");
125             }
126         } catch (ParserConfigurationException pcx) {
127             LOG.error(Messages.format("conf.configurationError", pcx));
128         } catch (org.xml.sax.SAXException sx) {
129             LOG.error(Messages.format("conf.configurationError", sx));
130         }
131         return saxParser;
132     }
133 
134     /**
135      * To get a SAXParser instance from a given {@link SAXParserFactory}, which is then used to obtain either
136      * {@link Parser} or {@link XMLReader}.
137      * @return the {@link SAXParser} for further use
138      */
139     public static SAXParser getSAXParser(SAXParserFactory saxParserFactory) {
140         SAXParser saxParser = null;
141 
142         try {
143             saxParser = saxParserFactory.newSAXParser();
144             if (LOG.isDebugEnabled()) {
145                 LOG.debug("Successfully instantiated a JAXP SAXParser instance.");
146             }
147         } catch (ParserConfigurationException pcx) {
148             LOG.error(Messages.format("conf.configurationError", pcx));
149         } catch (org.xml.sax.SAXException sx) {
150             LOG.error(Messages.format("conf.configurationError", sx));
151         }
152         return saxParser;
153     }
154 
155     /**
156      * To get a SAXParserFactory instance which is then used to obtain an
157      * {@link SAXParser} instance.
158      * @param namespaces Whether to provide namespace support.
159      * @param validation Whether to produce a validating SAX parser.
160      * @return the SAXParserFactory for further use
161      */
162     public static SAXParserFactory getSAXParserFactory(final boolean validation, final boolean namespaces) {
163         SAXParserFactory factory = SAXParserFactory.newInstance();
164         factory.setNamespaceAware(namespaces);
165         factory.setValidating(validation);
166         return factory;
167     }
168 
169     /**
170      * Instantiates an {@link XMLReader} instance directly, using {@link Class#forName(String)}
171      * to obtain the {@link Class} instance, and uses {@link Class#newInstance()}
172      * to create the actual instance.
173      * @param className The class name of the {@link XMLReader} instance to be instantiated.
174      * @return An {@link XMLReader} instance.
175      */
176     public static XMLReader instantiateXMLReader(final String className) {
177         XMLReader xmlReader;
178         try {
179             Class cls;
180             cls = Class.forName(className);
181             xmlReader = (XMLReader) cls.newInstance();
182             if (LOG.isDebugEnabled()) {
183                 LOG.debug("Successfully instantiated " + className);
184             }
185         } catch (Exception except) {
186             throw new RuntimeException(Messages.format(
187                     "conf.failedInstantiateParser", className, except));
188         }
189         return xmlReader;
190     }
191 
192     /**
193      * Instantiates an {@link Parser} instance directly, using {@link Class#forName(String)}
194      * to obtain the {@link Class} instance, and uses {@link Class#newInstance()}
195      * to create the actual instance.
196      * @param className The class name of the {@link Parser} instance to be instantiated.
197      * @return An {@link Parser} instance.
198      */
199     public static Parser instantiateParser (final String className) {
200         Parser parser;
201         try {
202             Class cls;
203             cls = Class.forName(className);
204             parser = (Parser) cls.newInstance();
205             if (LOG.isDebugEnabled()) {
206                 LOG.debug("Successfully instantiated " + className);
207             }
208         } catch (Exception except) {
209             throw new RuntimeException(Messages.format(
210                     "conf.failedInstantiateParser", className, except));
211         }
212         return parser;
213     }
214     
215     public static Parser getParser(final AbstractProperties properties, final String features) {
216         Parser parser = null;
217         Boolean validation = properties.getBoolean(XMLProperties.PARSER_VALIDATION);
218         Boolean namespaces = properties.getBoolean(XMLProperties.NAMESPACES);
219         String parserClassName = properties.getString(XMLProperties.PARSER);
220         if ((parserClassName == null) || (parserClassName.length() == 0)) {
221             SAXParser saxParser = XMLParserUtils.getSAXParser(
222                     validation.booleanValue(), namespaces.booleanValue());
223             if (saxParser != null) {
224                 try {
225                     parser = saxParser.getParser();
226                 } catch (SAXException e) {
227                     LOG.error(Messages.format("conf.configurationError", e));
228                 }
229             }
230         }
231         
232         if (parser == null) {
233             if ((parserClassName == null) 
234                     || (parserClassName.length() == 0) 
235                     || (parserClassName.equalsIgnoreCase("xerces"))) {
236                 parserClassName = "org.apache.xerces.parsers.SAXParser";
237             }
238             
239             // if a parser class was specified, we try to create it
240             parser = XMLParserUtils.instantiateParser(parserClassName);
241 
242             if (parser instanceof XMLReader) {
243                 XMLReader xmlReader = (XMLReader) parser;
244                 XMLParserUtils.setFeaturesOnXmlReader(
245                         properties.getString(XMLProperties.PARSER_FEATURES, features),
246                         properties.getString(XMLProperties.PARSER_FEATURES_DISABLED, ""),
247                         validation.booleanValue(),
248                         namespaces.booleanValue(),
249                         xmlReader);
250             }
251         }
252         return parser;
253     }
254     
255     /**
256      * @see org.castor.xml.InternalContext#getSerializer()
257      */
258     public static Serializer getSerializer(final AbstractProperties properties) {
259         Serializer serializer = getSerializerFactory(
260                 properties.getString(XMLProperties.SERIALIZER_FACTORY)).getSerializer();
261         serializer.setOutputFormat(getOutputFormat(properties));
262         return serializer;
263     }
264     
265     /**
266      * @see org.castor.xml.InternalContext#getOutputFormat()
267      */
268     public static OutputFormat getOutputFormat(final AbstractProperties properties) {
269 
270         boolean indent = properties.getBoolean(XMLProperties.USE_INDENTATION, false);
271         
272         String version = properties.getString(XMLProperties.XML_VERSION, "1.0");
273 
274         OutputFormat format = getSerializerFactory(
275                 properties.getString(XMLProperties.SERIALIZER_FACTORY)).getOutputFormat();
276         format.setMethod(OutputFormat.XML);
277         format.setVersion(version);
278         format.setIndenting(indent);
279         
280         // There is a bad interaction between the indentation and the
281         // setPreserveSpace option. The indentated output is strangely indented.
282         if (!indent) {
283             format.setPreserveSpace(true);
284         } 
285 
286         return format;
287     } //-- getOutputFormat
288     
289     /**
290      * Returns the currently configured XMLSerializerFactory instance.
291      * @param serializerFactoryName the class name of the serializer factory
292      * @return XMLSerializerFactory to use by Castor
293      */
294     public static XMLSerializerFactory getSerializerFactory(final String serializerFactoryName) {
295         XMLSerializerFactory serializerFactory;
296         
297         try {
298             serializerFactory = (XMLSerializerFactory) 
299             Class.forName(serializerFactoryName).newInstance();
300         } catch (Exception except) {
301             throw new RuntimeException(
302                     Messages.format("conf.failedInstantiateSerializerFactory", 
303                             serializerFactoryName, except));
304         }
305         return serializerFactory;
306     }
307     
308     
309 }
310