001: /*
002: * $Id: JAXPSAXParserFactory.java,v 1.7 2004/07/08 08:03:04 yuvalo Exp $
003: *
004: * (C) Copyright 2002-2004 by Yuval Oren. All rights reserved.
005: *
006: * Licensed under the Apache License, Version 2.0 (the "License");
007: * you may not use this file except in compliance with the License.
008: * You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing, software
013: * distributed under the License is distributed on an "AS IS" BASIS,
014: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: */
018:
019: package com.bluecast.xml;
020:
021: import com.bluecast.util.FactoryServiceFinder;
022: import java.util.*;
023: import javax.xml.parsers.*;
024: import org.xml.sax.*;
025: import java.io.IOException;
026: import java.io.*;
027:
028: /**
029: * JAXP factory class for creating SAX parsers. This factory creates
030: * an instance of Piccolo when a non-validating parser is requested.
031: * <p>
032: * If a validating parser is requested, this class will search for
033: * another <code>SAXParserFactory</code> to create the validating parser.
034: * This class will search for a factory in the following ways:
035: * <ol>
036: * <li>The system property <code>com.bluecast.xml.ValidatingSAXParserFactory</code>
037: * <li>The next listed Service Provider for
038: * <code>javax.xml.parsers.SAXParserFactory</code>
039: * <li>Crimson (<code>org.apache.crimson.jaxp.SAXParserFactoryImpl</code>)
040: * </ol>
041: * If all of the above fail, a <code>ParserConfigurationException</code>
042: * will be thrown.
043: *
044: * @author Yuval Oren, yuval@bluecast.com
045: * @version $Revision: 1.7 $
046: */
047: public class JAXPSAXParserFactory extends SAXParserFactory {
048: private Map featureMap = new HashMap();
049: private static Boolean TRUE = new Boolean(true);
050: private static Boolean FALSE = new Boolean(false);
051: private Piccolo nvParser = new Piccolo();
052: private SAXParserFactory validatingFactory;
053: private static final String VALIDATING_PROPERTY = "com.bluecast.xml.ValidatingSAXParserFactory";
054:
055: private static Class validatingFactoryClass;
056: static {
057: validatingFactoryClass = findValidatingFactory();
058: }
059:
060: // Used to remember the exception we may get from switching
061: // between validating and non-validating
062: private ParserConfigurationException pendingValidatingException = null,
063: pendingNonvalidatingException = null;
064:
065: private boolean validating = false, namespaceAware = false;
066:
067: static public SAXParserFactory newInstance() {
068: return new JAXPSAXParserFactory();
069: }
070:
071: public JAXPSAXParserFactory() {
072: try {
073: if (validatingFactoryClass != null) {
074: validatingFactory = (SAXParserFactory) validatingFactoryClass
075: .newInstance();
076: validatingFactory.setNamespaceAware(false);
077: validatingFactory.setValidating(true);
078: }
079: } catch (Exception e) {
080: validatingFactory = null;
081: }
082:
083: setNamespaceAware(false); // Force false default on Piccolo
084: }
085:
086: public boolean getFeature(String name)
087: throws ParserConfigurationException,
088: SAXNotRecognizedException, SAXNotSupportedException {
089:
090: if (validating && validatingFactory != null)
091: return validatingFactory.getFeature(name);
092: else
093: return nvParser.getFeature(name);
094: }
095:
096: public SAXParser newSAXParser()
097: throws ParserConfigurationException, SAXException {
098: if (validating) {
099: if (validatingFactory == null)
100: throw new ParserConfigurationException(
101: "XML document validation is not supported");
102: else if (pendingValidatingException != null)
103: throw pendingValidatingException;
104: else
105: return validatingFactory.newSAXParser();
106: } else {
107: if (pendingNonvalidatingException != null)
108: throw pendingNonvalidatingException;
109: else
110: return new JAXPSAXParser(new Piccolo(nvParser));
111: }
112: }
113:
114: public void setFeature(String name, boolean enabled)
115: throws ParserConfigurationException,
116: SAXNotRecognizedException, SAXNotSupportedException {
117:
118: // First explicitly set this feature
119: featureMap.put(name, enabled ? TRUE : FALSE);
120:
121: // Configure the validating factory
122: if (validatingFactory != null) {
123: if (pendingValidatingException != null)
124: reconfigureValidating();
125: else {
126: try {
127: validatingFactory.setFeature(name, enabled);
128: } catch (ParserConfigurationException e) {
129: pendingValidatingException = e;
130: }
131: }
132: }
133:
134: // Configure the nonvalidating parser
135: if (pendingNonvalidatingException != null)
136: reconfigureNonvalidating();
137:
138: // Now throw the appropriate exception if there was a problem
139: if (validating && pendingValidatingException != null)
140: throw pendingValidatingException;
141: else if (!validating && pendingNonvalidatingException != null)
142: throw pendingNonvalidatingException;
143: }
144:
145: public void setNamespaceAware(boolean awareness) {
146: super .setNamespaceAware(awareness);
147: namespaceAware = awareness;
148:
149: try {
150: nvParser
151: .setFeature(
152: "http://xml.org/sax/features/namespaces",
153: awareness);
154: nvParser.setFeature(
155: "http://xml.org/sax/features/namespace-prefixes",
156: !awareness);
157: } catch (SAXNotSupportedException e) {
158: pendingNonvalidatingException = new ParserConfigurationException(
159: "Error setting namespace feature: " + e.toString());
160: } catch (SAXNotRecognizedException e) {
161: pendingNonvalidatingException = new ParserConfigurationException(
162: "Error setting namespace feature: " + e.toString());
163: }
164:
165: if (validatingFactory != null)
166: validatingFactory.setNamespaceAware(awareness);
167: }
168:
169: public void setValidating(boolean value) {
170: super .setValidating(value);
171: validating = value;
172: }
173:
174: private static Class findValidatingFactory() {
175: // Find a factory for creating validating parsers
176:
177: // First try the system property
178: try {
179: String validatingClassName = System
180: .getProperty(VALIDATING_PROPERTY);
181: if (validatingClassName != null)
182: return Class.forName(validatingClassName);
183: } catch (Exception e) {
184: }
185:
186: // Next try looking in jaxp.properties
187: try {
188: String javah = System.getProperty("java.home");
189: String configFile = javah + File.separator + "lib"
190: + File.separator + "jaxp.properties";
191: File f = new File(configFile);
192: if (f.exists()) {
193: Properties props = new Properties();
194: props.load(new FileInputStream(f));
195: String validatingClassName = props
196: .getProperty(VALIDATING_PROPERTY);
197: if (validatingClassName != null)
198: return Class.forName(validatingClassName);
199: }
200: } catch (Exception e) {
201: }
202:
203: // Next try the Service Provider Interface
204: try {
205: Enumeration enumValue = FactoryServiceFinder
206: .findServices("javax.xml.parsers.SAXParserFactory");
207: while (enumValue.hasMoreElements()) {
208: try {
209: String factory = (String) enumValue.nextElement();
210: if (!factory.equals("com.bluecast.xml.Piccolo"))
211: return Class.forName(factory);
212: } catch (ClassNotFoundException e) {
213: }
214: }
215: } catch (Exception e) {
216: }
217:
218: // Finally try Crimson
219: try {
220: return Class
221: .forName("org.apache.crimson.jaxp.SAXParserFactoryImpl");
222: } catch (ClassNotFoundException e) {
223: return null;
224: }
225: }
226:
227: private void reconfigureValidating() {
228: if (validatingFactory == null)
229: return;
230:
231: try {
232: Iterator iter = featureMap.entrySet().iterator();
233: while (iter.hasNext()) {
234: Map.Entry entry = (Map.Entry) iter.next();
235: validatingFactory.setFeature((String) entry.getKey(),
236: ((Boolean) entry.getValue()).booleanValue());
237: }
238: } catch (ParserConfigurationException e) {
239: pendingValidatingException = e;
240: } catch (SAXNotRecognizedException e) {
241: pendingValidatingException = new ParserConfigurationException(
242: e.toString());
243: } catch (SAXNotSupportedException e) {
244: pendingValidatingException = new ParserConfigurationException(
245: e.toString());
246: }
247: }
248:
249: private void reconfigureNonvalidating() {
250: try {
251: Iterator iter = featureMap.entrySet().iterator();
252: while (iter.hasNext()) {
253: Map.Entry entry = (Map.Entry) iter.next();
254: nvParser.setFeature((String) entry.getKey(),
255: ((Boolean) entry.getValue()).booleanValue());
256: }
257: } catch (SAXNotRecognizedException e) {
258: pendingNonvalidatingException = new ParserConfigurationException(
259: e.toString());
260: } catch (SAXNotSupportedException e) {
261: pendingNonvalidatingException = new ParserConfigurationException(
262: e.toString());
263: }
264: }
265:
266: static class JAXPSAXParser extends SAXParser {
267: Piccolo parser;
268:
269: JAXPSAXParser(Piccolo parser) {
270: this .parser = parser;
271: }
272:
273: public Parser getParser() {
274: return parser;
275: }
276:
277: public Object getProperty(String name)
278: throws SAXNotRecognizedException,
279: SAXNotSupportedException {
280: return parser.getProperty(name);
281: }
282:
283: public XMLReader getXMLReader() {
284: return parser;
285: }
286:
287: public boolean isNamespaceAware() {
288: return parser.fNamespaces;
289: }
290:
291: public boolean isValidating() {
292: return false;
293: }
294:
295: public void setProperty(String name, Object value)
296: throws SAXNotRecognizedException,
297: SAXNotSupportedException {
298: parser.setProperty(name, value);
299: }
300: }
301: }
|