| /* |
| * Copyright (c) 2004 World Wide Web Consortium, |
| * |
| * (Massachusetts Institute of Technology, European Research Consortium for |
| * Informatics and Mathematics, Keio University). All Rights Reserved. This |
| * work is distributed under the W3C(r) Software License [1] in the hope that |
| * it will be useful, but WITHOUT ANY WARRANTY; without even the implied |
| * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| * |
| * [1] http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231 |
| */ |
| |
| |
| package org.w3c.dom.bootstrap; |
| |
| import java.util.StringTokenizer; |
| import java.util.Vector; |
| import org.w3c.dom.DOMImplementationSource; |
| import org.w3c.dom.DOMImplementationList; |
| import org.w3c.dom.DOMImplementation; |
| import java.io.InputStream; |
| import java.io.BufferedReader; |
| import java.io.InputStreamReader; |
| import java.security.AccessController; |
| import java.security.PrivilegedAction; |
| |
| /** |
| * A factory that enables applications to obtain instances of |
| * <code>DOMImplementation</code>. |
| * |
| * <p> |
| * Example: |
| * </p> |
| * |
| * <pre class='example'> |
| * // get an instance of the DOMImplementation registry |
| * DOMImplementationRegistry registry = |
| * DOMImplementationRegistry.newInstance(); |
| * // get a DOM implementation the Level 3 XML module |
| * DOMImplementation domImpl = |
| * registry.getDOMImplementation("XML 3.0"); |
| * </pre> |
| * |
| * <p> |
| * This provides an application with an implementation-independent starting |
| * point. DOM implementations may modify this class to meet new security |
| * standards or to provide *additional* fallbacks for the list of |
| * DOMImplementationSources. |
| * </p> |
| * |
| * @see DOMImplementation |
| * @see DOMImplementationSource |
| * @since DOM Level 3 |
| */ |
| public final class DOMImplementationRegistry { |
| /** |
| * The system property to specify the |
| * DOMImplementationSource class names. |
| */ |
| public static final String PROPERTY = |
| "org.w3c.dom.DOMImplementationSourceList"; |
| |
| /** |
| * Default columns per line. |
| */ |
| private static final int DEFAULT_LINE_LENGTH = 80; |
| |
| /** |
| * The list of DOMImplementationSources. |
| */ |
| private Vector sources; |
| |
| /** |
| * Private constructor. |
| * @param srcs Vector List of DOMImplementationSources |
| */ |
| private DOMImplementationRegistry(final Vector srcs) { |
| sources = srcs; |
| } |
| |
| /** |
| * Obtain a new instance of a <code>DOMImplementationRegistry</code>. |
| * |
| |
| * The <code>DOMImplementationRegistry</code> is initialized by the |
| * application or the implementation, depending on the context, by |
| * first checking the value of the Java system property |
| * <code>org.w3c.dom.DOMImplementationSourceList</code> and |
| * the the service provider whose contents are at |
| * "<code>META_INF/services/org.w3c.dom.DOMImplementationSourceList</code>" |
| * The value of this property is a white-space separated list of |
| * names of availables classes implementing the |
| * <code>DOMImplementationSource</code> interface. Each class listed |
| * in the class name list is instantiated and any exceptions |
| * encountered are thrown to the application. |
| * |
| * @return an initialized instance of DOMImplementationRegistry |
| * @throws ClassNotFoundException |
| * If any specified class can not be found |
| * @throws InstantiationException |
| * If any specified class is an interface or abstract class |
| * @throws IllegalAccessException |
| * If the default constructor of a specified class is not accessible |
| * @throws ClassCastException |
| * If any specified class does not implement |
| * <code>DOMImplementationSource</code> |
| */ |
| public static DOMImplementationRegistry newInstance() |
| throws |
| ClassNotFoundException, |
| InstantiationException, |
| IllegalAccessException, |
| ClassCastException { |
| Vector sources = new Vector(); |
| |
| ClassLoader classLoader = getClassLoader(); |
| // fetch system property: |
| String p = getSystemProperty(PROPERTY); |
| |
| // |
| // if property is not specified then use contents of |
| // META_INF/org.w3c.dom.DOMImplementationSourceList from classpath |
| if (p == null) { |
| p = getServiceValue(classLoader); |
| } |
| if (p == null) { |
| // |
| // DOM Implementations can modify here to add *additional* fallback |
| // mechanisms to access a list of default DOMImplementationSources. |
| p = "gnu.xml.dom.ImplementationSource"; |
| } |
| if (p != null) { |
| StringTokenizer st = new StringTokenizer(p); |
| while (st.hasMoreTokens()) { |
| String sourceName = st.nextToken(); |
| // Use context class loader, falling back to Class.forName |
| // if and only if this fails... |
| Class sourceClass = null; |
| if (classLoader != null) { |
| sourceClass = classLoader.loadClass(sourceName); |
| } else { |
| sourceClass = Class.forName(sourceName); |
| } |
| DOMImplementationSource source = |
| (DOMImplementationSource) sourceClass.newInstance(); |
| sources.addElement(source); |
| } |
| } |
| return new DOMImplementationRegistry(sources); |
| } |
| |
| /** |
| * Return the first implementation that has the desired |
| * features, or <code>null</code> if none is found. |
| * |
| * @param features |
| * A string that specifies which features are required. This is |
| * a space separated list in which each feature is specified by |
| * its name optionally followed by a space and a version number. |
| * This is something like: "XML 1.0 Traversal +Events 2.0" |
| * @return An implementation that has the desired features, |
| * or <code>null</code> if none found. |
| */ |
| public DOMImplementation getDOMImplementation(final String features) { |
| int size = sources.size(); |
| String name = null; |
| for (int i = 0; i < size; i++) { |
| DOMImplementationSource source = |
| (DOMImplementationSource) sources.elementAt(i); |
| DOMImplementation impl = source.getDOMImplementation(features); |
| if (impl != null) { |
| return impl; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Return a list of implementations that support the |
| * desired features. |
| * |
| * @param features |
| * A string that specifies which features are required. This is |
| * a space separated list in which each feature is specified by |
| * its name optionally followed by a space and a version number. |
| * This is something like: "XML 1.0 Traversal +Events 2.0" |
| * @return A list of DOMImplementations that support the desired features. |
| */ |
| public DOMImplementationList getDOMImplementationList(final String features) { |
| final Vector implementations = new Vector(); |
| int size = sources.size(); |
| for (int i = 0; i < size; i++) { |
| DOMImplementationSource source = |
| (DOMImplementationSource) sources.elementAt(i); |
| DOMImplementationList impls = |
| source.getDOMImplementationList(features); |
| for (int j = 0; j < impls.getLength(); j++) { |
| DOMImplementation impl = impls.item(j); |
| implementations.addElement(impl); |
| } |
| } |
| return new DOMImplementationList() { |
| public DOMImplementation item(final int index) { |
| if (index >= 0 && index < implementations.size()) { |
| try { |
| return (DOMImplementation) |
| implementations.elementAt(index); |
| } catch (ArrayIndexOutOfBoundsException e) { |
| return null; |
| } |
| } |
| return null; |
| } |
| |
| public int getLength() { |
| return implementations.size(); |
| } |
| }; |
| } |
| |
| /** |
| * Register an implementation. |
| * |
| * @param s The source to be registered, may not be <code>null</code> |
| */ |
| public void addSource(final DOMImplementationSource s) { |
| if (s == null) { |
| throw new NullPointerException(); |
| } |
| if (!sources.contains(s)) { |
| sources.addElement(s); |
| } |
| } |
| |
| /** |
| * |
| * Gets a class loader. |
| * |
| * @return A class loader, possibly <code>null</code> |
| */ |
| private static ClassLoader getClassLoader() { |
| try { |
| ClassLoader contextClassLoader = getContextClassLoader(); |
| |
| if (contextClassLoader != null) { |
| return contextClassLoader; |
| } |
| } catch (Exception e) { |
| // Assume that the DOM application is in a JRE 1.1, use the |
| // current ClassLoader |
| return DOMImplementationRegistry.class.getClassLoader(); |
| } |
| return DOMImplementationRegistry.class.getClassLoader(); |
| } |
| |
| /** |
| * This method attempts to return the first line of the resource |
| * META_INF/services/org.w3c.dom.DOMImplementationSourceList |
| * from the provided ClassLoader. |
| * |
| * @param classLoader classLoader, may not be <code>null</code>. |
| * @return first line of resource, or <code>null</code> |
| */ |
| private static String getServiceValue(final ClassLoader classLoader) { |
| String serviceId = "META-INF/services/" + PROPERTY; |
| // try to find services in CLASSPATH |
| try { |
| InputStream is = getResourceAsStream(classLoader, serviceId); |
| |
| if (is != null) { |
| BufferedReader rd; |
| try { |
| rd = |
| new BufferedReader(new InputStreamReader(is, "UTF-8"), |
| DEFAULT_LINE_LENGTH); |
| } catch (java.io.UnsupportedEncodingException e) { |
| rd = |
| new BufferedReader(new InputStreamReader(is), |
| DEFAULT_LINE_LENGTH); |
| } |
| String serviceValue = rd.readLine(); |
| rd.close(); |
| if (serviceValue != null && serviceValue.length() > 0) { |
| return serviceValue; |
| } |
| } |
| } catch (Exception ex) { |
| return null; |
| } |
| return null; |
| } |
| |
| /** |
| * A simple JRE (Java Runtime Environment) 1.1 test |
| * |
| * @return <code>true</code> if JRE 1.1 |
| */ |
| private static boolean isJRE11() { |
| try { |
| Class c = Class.forName("java.security.AccessController"); |
| // java.security.AccessController existed since 1.2 so, if no |
| // exception was thrown, the DOM application is running in a JRE |
| // 1.2 or higher |
| return false; |
| } catch (Exception ex) { |
| // ignore |
| } |
| return true; |
| } |
| |
| /** |
| * This method returns the ContextClassLoader or <code>null</code> if |
| * running in a JRE 1.1 |
| * |
| * @return The Context Classloader |
| */ |
| private static ClassLoader getContextClassLoader() { |
| return isJRE11() |
| ? null |
| : (ClassLoader) |
| AccessController.doPrivileged(new PrivilegedAction() { |
| public Object run() { |
| ClassLoader classLoader = null; |
| try { |
| classLoader = |
| Thread.currentThread().getContextClassLoader(); |
| } catch (SecurityException ex) { |
| } |
| return classLoader; |
| } |
| }); |
| } |
| |
| /** |
| * This method returns the system property indicated by the specified name |
| * after checking access control privileges. For a JRE 1.1, this check is |
| * not done. |
| * |
| * @param name the name of the system property |
| * @return the system property |
| */ |
| private static String getSystemProperty(final String name) { |
| return isJRE11() |
| ? (String) System.getProperty(name) |
| : (String) AccessController.doPrivileged(new PrivilegedAction() { |
| public Object run() { |
| return System.getProperty(name); |
| } |
| }); |
| } |
| |
| /** |
| * This method returns an Inputstream for the reading resource |
| * META_INF/services/org.w3c.dom.DOMImplementationSourceList after checking |
| * access control privileges. For a JRE 1.1, this check is not done. |
| * |
| * @param classLoader classLoader |
| * @param name the resource |
| * @return an Inputstream for the resource specified |
| */ |
| private static InputStream getResourceAsStream(final ClassLoader classLoader, |
| final String name) { |
| if (isJRE11()) { |
| InputStream ris; |
| if (classLoader == null) { |
| ris = ClassLoader.getSystemResourceAsStream(name); |
| } else { |
| ris = classLoader.getResourceAsStream(name); |
| } |
| return ris; |
| } else { |
| return (InputStream) |
| AccessController.doPrivileged(new PrivilegedAction() { |
| public Object run() { |
| InputStream ris; |
| if (classLoader == null) { |
| ris = |
| ClassLoader.getSystemResourceAsStream(name); |
| } else { |
| ris = classLoader.getResourceAsStream(name); |
| } |
| return ris; |
| } |
| }); |
| } |
| } |
| } |