org.w3c.dom.bootstrap.DOMImplementationRegistry Maven / Gradle / Ivy
/*
* Copyright (C) 2005 by Quentin Anciaux
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Library General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This library is distributed 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. See the GNU Library General Public License
* for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; if not, write to the Free Software Foundation,
* Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* @author Quentin Anciaux
*/
package org.w3c.dom.bootstrap;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.DOMImplementationList;
import org.w3c.dom.DOMImplementationSource;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.StringTokenizer;
import java.util.Vector;
/**
* A factory that enables applications to obtain instances of
* DOMImplementation
.
*
*
* 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");
*
*
*
*
*
* 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.
*
*
* @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 DOMImplementationRegistry
. The
* DOMImplementationRegistry
is initialized by the
* application or the implementation, depending on the context, by first
* checking the value of the Java system property
* org.w3c.dom.DOMImplementationSourceList
and the the
* service provider whose contents are at "
* META_INF/services/org.w3c.dom.DOMImplementationSourceList
"
* The value of this property is a white-space separated list of names of
* availables classes implementing the
* DOMImplementationSource
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
* DOMImplementationSource
*/
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.
}
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
* null
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
* null
if none found.
*/
public DOMImplementation getDOMImplementation(final String features) {
int size = sources.size();
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 null
*
* @throws NullPointerException DOCUMENT ME!
*/
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 null
*/
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 null
.
*
* @return first line of resource, or null
*/
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 true
if JRE 1.1
*/
private static boolean isJRE11() {
try {
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 null
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;
}
}
);
}
}
}