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 from 'old'
25   * 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     * 
51     * @param properties the Properties to read parser features from
52     * @param defaultFeatures any default features to use
53     * @param validation Whether to enable validation or not.
54     * @param namespaces Whether to enable namespace support for not.
55     * @param xmlReader The XMLReader instance to configure.
56     */
57    public static void setFeaturesOnXmlReader(final String parserFeatures,
58        final String parserFeaturesToDisable, final boolean validation, final boolean namespaces,
59        final XMLReader xmlReader) {
60      try {
61        xmlReader.setFeature(VALIDATION, validation);
62        xmlReader.setFeature(NAMESPACES, namespaces);
63        enableFeatures(parserFeatures, xmlReader);
64        disableFeatures(parserFeaturesToDisable, xmlReader);
65      } catch (SAXException except) {
66        LOG.error(Messages.format("conf.configurationError", except));
67      }
68    }
69  
70    /**
71     * Enables selected features on the XMLReader instance.
72     * 
73     * @param features Features to enable
74     * @param xmlReader XMLReader instance to be configured.
75     * @throws SAXNotRecognizedException If the feature is not recognized by the XMLReader.
76     * @throws SAXNotSupportedException If the feature is not supported by the XMLReader.
77     */
78    private static void enableFeatures(final String features, final XMLReader xmlReader)
79        throws SAXNotRecognizedException, SAXNotSupportedException {
80      StringTokenizer token;
81      if (features != null) {
82        token = new StringTokenizer(features, ", ");
83        while (token.hasMoreTokens()) {
84          xmlReader.setFeature(token.nextToken(), true);
85        }
86      }
87    }
88  
89    /**
90     * Disables selected features on the XMLReader instance.
91     * 
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 parser or XMLReader.
110    * 
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
136    * either {@link Parser} or {@link XMLReader}.
137    * 
138    * @return the {@link SAXParser} for further use
139    */
140   public static SAXParser getSAXParser(SAXParserFactory saxParserFactory) {
141     SAXParser saxParser = null;
142 
143     try {
144       saxParser = saxParserFactory.newSAXParser();
145       if (LOG.isDebugEnabled()) {
146         LOG.debug("Successfully instantiated a JAXP SAXParser instance.");
147       }
148     } catch (ParserConfigurationException pcx) {
149       LOG.error(Messages.format("conf.configurationError", pcx));
150     } catch (org.xml.sax.SAXException sx) {
151       LOG.error(Messages.format("conf.configurationError", sx));
152     }
153     return saxParser;
154   }
155 
156   /**
157    * To get a SAXParserFactory instance which is then used to obtain an {@link SAXParser} instance.
158    * 
159    * @param namespaces Whether to provide namespace support.
160    * @param validation Whether to produce a validating SAX parser.
161    * @return the SAXParserFactory for further use
162    */
163   public static SAXParserFactory getSAXParserFactory(final boolean validation,
164       final boolean namespaces) {
165     SAXParserFactory factory = SAXParserFactory.newInstance();
166     factory.setNamespaceAware(namespaces);
167     factory.setValidating(validation);
168     return factory;
169   }
170 
171   /**
172    * Instantiates an {@link XMLReader} instance directly, using {@link Class#forName(String)} to
173    * obtain the {@link Class} instance, and uses {@link Class#newInstance()} to create the actual
174    * instance.
175    * 
176    * @param className The class name of the {@link XMLReader} instance to be instantiated.
177    * @return An {@link XMLReader} instance.
178    */
179   public static XMLReader instantiateXMLReader(final String className) {
180     XMLReader xmlReader;
181     try {
182       Class cls;
183       cls = Class.forName(className);
184       xmlReader = (XMLReader) cls.newInstance();
185       if (LOG.isDebugEnabled()) {
186         LOG.debug("Successfully instantiated " + className);
187       }
188     } catch (Exception except) {
189       throw new RuntimeException(
190           Messages.format("conf.failedInstantiateParser", className, except));
191     }
192     return xmlReader;
193   }
194 
195   /**
196    * Instantiates an {@link Parser} instance directly, using {@link Class#forName(String)} to obtain
197    * the {@link Class} instance, and uses {@link Class#newInstance()} to create the actual instance.
198    * 
199    * @param className The class name of the {@link Parser} instance to be instantiated.
200    * @return An {@link Parser} instance.
201    */
202   public static Parser instantiateParser(final String className) {
203     Parser parser;
204     try {
205       Class cls;
206       cls = Class.forName(className);
207       parser = (Parser) cls.newInstance();
208       if (LOG.isDebugEnabled()) {
209         LOG.debug("Successfully instantiated " + className);
210       }
211     } catch (Exception except) {
212       throw new RuntimeException(
213           Messages.format("conf.failedInstantiateParser", className, except));
214     }
215     return parser;
216   }
217 
218   public static Parser getParser(final AbstractProperties properties, final String features) {
219     Parser parser = null;
220     Boolean validation = properties.getBoolean(XMLProperties.PARSER_VALIDATION);
221     Boolean namespaces = properties.getBoolean(XMLProperties.NAMESPACES);
222     String parserClassName = properties.getString(XMLProperties.PARSER);
223     if ((parserClassName == null) || (parserClassName.length() == 0)) {
224       SAXParser saxParser =
225           XMLParserUtils.getSAXParser(validation.booleanValue(), namespaces.booleanValue());
226       if (saxParser != null) {
227         try {
228           parser = saxParser.getParser();
229         } catch (SAXException e) {
230           LOG.error(Messages.format("conf.configurationError", e));
231         }
232       }
233     }
234 
235     if (parser == null) {
236       if ((parserClassName == null) || (parserClassName.length() == 0)
237           || (parserClassName.equalsIgnoreCase("xerces"))) {
238         parserClassName = "org.apache.xerces.parsers.SAXParser";
239       }
240 
241       // if a parser class was specified, we try to create it
242       parser = XMLParserUtils.instantiateParser(parserClassName);
243 
244       if (parser instanceof XMLReader) {
245         XMLReader xmlReader = (XMLReader) parser;
246         XMLParserUtils.setFeaturesOnXmlReader(
247             properties.getString(XMLProperties.PARSER_FEATURES, features),
248             properties.getString(XMLProperties.PARSER_FEATURES_DISABLED, ""),
249             validation.booleanValue(), namespaces.booleanValue(), 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 =
260         getSerializerFactory(properties.getString(XMLProperties.SERIALIZER_FACTORY))
261             .getSerializer();
262     serializer.setOutputFormat(getOutputFormat(properties));
263     return serializer;
264   }
265 
266   /**
267    * @see org.castor.xml.InternalContext#getOutputFormat()
268    */
269   public static OutputFormat getOutputFormat(final AbstractProperties properties) {
270 
271     boolean indent = properties.getBoolean(XMLProperties.USE_INDENTATION, false);
272 
273     String version = properties.getString(XMLProperties.XML_VERSION, "1.0");
274 
275     OutputFormat format =
276         getSerializerFactory(properties.getString(XMLProperties.SERIALIZER_FACTORY))
277             .getOutputFormat();
278     format.setMethod(OutputFormat.XML);
279     format.setVersion(version);
280     format.setIndenting(indent);
281 
282     // There is a bad interaction between the indentation and the
283     // setPreserveSpace option. The indentated output is strangely indented.
284     if (!indent) {
285       format.setPreserveSpace(true);
286     }
287 
288     return format;
289   } // -- getOutputFormat
290 
291   /**
292    * Returns the currently configured XMLSerializerFactory instance.
293    * 
294    * @param serializerFactoryName the class name of the serializer factory
295    * @return XMLSerializerFactory to use by Castor
296    */
297   public static XMLSerializerFactory getSerializerFactory(final String serializerFactoryName) {
298     XMLSerializerFactory serializerFactory;
299 
300     try {
301       serializerFactory = (XMLSerializerFactory) Class.forName(serializerFactoryName).newInstance();
302     } catch (Exception except) {
303       throw new RuntimeException(Messages.format("conf.failedInstantiateSerializerFactory",
304           serializerFactoryName, except));
305     }
306     return serializerFactory;
307   }
308 
309 
310 }
311