1 // XMLReaderFactory.java - factory for creating a new reader.
2 // http://www.saxproject.org
3 // Written by David Megginson
4 // and by David Brownell
5 // NO WARRANTY!  This class is in the Public Domain.
6 // $Id: XMLReaderFactory.java,v 1.10 2002/04/22 01:00:13 dbrownell Exp $
7 
8 package org.xml.sax.helpers;
9 
10 import java.io.BufferedReader;
11 import java.io.InputStream;
12 import java.io.InputStreamReader;
13 import java.nio.charset.StandardCharsets;
14 import org.xml.sax.SAXException;
15 import org.xml.sax.XMLReader;
16 
17 
18 /**
19  * Factory for creating an XML reader.
20  *
21  * <blockquote>
22  * <em>This module, both source code and documentation, is in the
23  * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em>
24  * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a>
25  * for further information.
26  * </blockquote>
27  *
28  * <p>This class contains static methods for creating an XML reader
29  * from an explicit class name, or based on runtime defaults:</p>
30  *
31  * <pre>
32  * try {
33  *   XMLReader myReader = XMLReaderFactory.createXMLReader();
34  * } catch (SAXException e) {
35  *   System.err.println(e.getMessage());
36  * }
37  * </pre>
38  *
39  * <p><strong>Note to Distributions bundled with parsers:</strong>
40  * You should modify the implementation of the no-arguments
41  * <em>createXMLReader</em> to handle cases where the external
42  * configuration mechanisms aren't set up.  That method should do its
43  * best to return a parser when one is in the class path, even when
44  * nothing bound its class name to <code>org.xml.sax.driver</code> so
45  * those configuration mechanisms would see it.</p>
46  *
47  * @since SAX 2.0
48  * @author David Megginson, David Brownell
49  * @version 2.0.1 (sax2r2)
50  */
51 final public class XMLReaderFactory
52 {
53     /**
54      * Private constructor.
55      *
56      * <p>This constructor prevents the class from being instantiated.</p>
57      */
XMLReaderFactory()58     private XMLReaderFactory ()
59     {
60     }
61 
62     private static final String property = "org.xml.sax.driver";
63 
64     /**
65      * Attempt to create an XMLReader from system defaults.
66      * In environments which can support it, the name of the XMLReader
67      * class is determined by trying each these options in order, and
68      * using the first one which succeeds:</p> <ul>
69      *
70      * <li>If the system property <code>org.xml.sax.driver</code>
71      * has a value, that is used as an XMLReader class name. </li>
72      *
73      * <li>The JAR "Services API" is used to look for a class name
74      * in the <em>META-INF/services/org.xml.sax.driver</em> file in
75      * jarfiles available to the runtime.</li>
76      *
77      * <li> SAX parser distributions are strongly encouraged to provide
78      * a default XMLReader class name that will take effect only when
79      * previous options (on this list) are not successful.</li>
80      *
81      * <li>Finally, if {@link ParserFactory#makeParser()} can
82      * return a system default SAX1 parser, that parser is wrapped in
83      * a {@link ParserAdapter}.  (This is a migration aid for SAX1
84      * environments, where the <code>org.xml.sax.parser</code> system
85      * property will often be usable.) </li>
86      *
87      * </ul>
88      *
89      * <p> In environments such as small embedded systems, which can not
90      * support that flexibility, other mechanisms to determine the default
91      * may be used. </p>
92      *
93      * <p>Note that many Java environments allow system properties to be
94      * initialized on a command line.  This means that <em>in most cases</em>
95      * setting a good value for that property ensures that calls to this
96      * method will succeed, except when security policies intervene.
97      * This will also maximize application portability to older SAX
98      * environments, with less robust implementations of this method.
99      * </p>
100      *
101      * @return A new XMLReader.
102      * @exception org.xml.sax.SAXException If no default XMLReader class
103      *            can be identified and instantiated.
104      * @see #createXMLReader(java.lang.String)
105      */
createXMLReader()106     public static XMLReader createXMLReader ()
107     throws SAXException
108     {
109     String        className = null;
110     ClassLoader    loader = NewInstance.getClassLoader ();
111 
112     // 1. try the JVM-instance-wide system property
113     try { className = System.getProperty (property); }
114     catch (RuntimeException e) { /* normally fails for applets */ }
115 
116     // 2. if that fails, try META-INF/services/
117     if (className == null) {
118         try {
119         String        service = "META-INF/services/" + property;
120         InputStream    in;
121         BufferedReader    reader;
122 
123         if (loader == null)
124             in = ClassLoader.getSystemResourceAsStream (service);
125         else
126             in = loader.getResourceAsStream (service);
127 
128         if (in != null) {
129             reader = new BufferedReader (new InputStreamReader (in, StandardCharsets.UTF_8));
130             className = reader.readLine ();
131             in.close ();
132         }
133         } catch (Exception e) {
134         }
135     }
136 
137     // 3. Distro-specific fallback
138     if (className == null) {
139 // BEGIN DISTRIBUTION-SPECIFIC
140 
141         // EXAMPLE:
142         // className = "com.example.sax.XmlReader";
143         // or a $JAVA_HOME/jre/lib/*properties setting...
144 
145 // END DISTRIBUTION-SPECIFIC
146     }
147 
148     // do we know the XMLReader implementation class yet?
149     if (className != null)
150         return loadClass (loader, className);
151 
152     // 4. panic -- adapt any SAX1 parser
153     try {
154         return new ParserAdapter (ParserFactory.makeParser ());
155     } catch (Exception e) {
156         throw new SAXException ("Can't create default XMLReader; "
157             + "is system property org.xml.sax.driver set?");
158     }
159     }
160 
161 
162     /**
163      * Attempt to create an XML reader from a class name.
164      *
165      * <p>Given a class name, this method attempts to load
166      * and instantiate the class as an XML reader.</p>
167      *
168      * @param className the name of the class that should be instantiated.
169      *
170      * <p>Note that this method will not be usable in environments where
171      * the caller (perhaps an applet) is not permitted to load classes
172      * dynamically.</p>
173      *
174      * @return A new XML reader.
175      * @exception org.xml.sax.SAXException If the class cannot be
176      *            loaded, instantiated, and cast to XMLReader.
177      * @see #createXMLReader()
178      */
createXMLReader(String className)179     public static XMLReader createXMLReader (String className)
180     throws SAXException
181     {
182     return loadClass (NewInstance.getClassLoader (), className);
183     }
184 
loadClass(ClassLoader loader, String className)185     private static XMLReader loadClass (ClassLoader loader, String className)
186     throws SAXException
187     {
188     try {
189         return (XMLReader) NewInstance.newInstance (loader, className);
190     } catch (ClassNotFoundException e1) {
191         throw new SAXException("SAX2 driver class " + className +
192                    " not found", e1);
193     } catch (IllegalAccessException e2) {
194         throw new SAXException("SAX2 driver class " + className +
195                    " found but cannot be loaded", e2);
196     } catch (InstantiationException e3) {
197         throw new SAXException("SAX2 driver class " + className +
198        " loaded but cannot be instantiated (no empty public constructor?)",
199                    e3);
200     } catch (ClassCastException e4) {
201         throw new SAXException("SAX2 driver class " + className +
202                    " does not implement XMLReader", e4);
203     }
204     }
205 }
206