package org.codehaus.stax2.validation;
import java.io.*;
import java.net.
URL;
import java.util.*;
import javax.xml.stream.
FactoryConfigurationError;
import javax.xml.stream.
XMLStreamException;
/**
* Defines an abstract factory for constructing {@link XMLValidationSchema}
* instances. This abstract base class has methods for instantiating the
* actual implementation (similar to the way
* {@link javax.xml.stream.XMLInputFactory} works, and defines the API to
* use for configuring these instances, as well as factory methods concrete
* classes implement for actually creating {@link XMLValidationSchema}
* instances.
*<p>
* Note: this class is part of the second major revision of StAX 2 API
* (StAX2, v2), and is optional for StAX2 implementations to support.
*
* @see javax.xml.stream.XMLInputFactory
* @see org.codehaus.stax2.validation.XMLValidationSchema
* @see org.codehaus.stax2.XMLInputFactory2
*/
public abstract class
XMLValidationSchemaFactory
{
// // // Internal ids matching SCHEMA_ID_ constants from XMLValidationSchema
public final static
String INTERNAL_ID_SCHEMA_DTD = "dtd";
public final static
String INTERNAL_ID_SCHEMA_RELAXNG = "relaxng";
public final static
String INTERNAL_ID_SCHEMA_W3C = "w3c";
public final static
String INTERNAL_ID_SCHEMA_TREX = "trex";
final static
HashMap<
String,
String>
sSchemaIds = new
HashMap<
String,
String>();
static {
sSchemaIds.
put(
XMLValidationSchema.
SCHEMA_ID_DTD,
INTERNAL_ID_SCHEMA_DTD);
sSchemaIds.
put(
XMLValidationSchema.
SCHEMA_ID_RELAXNG,
INTERNAL_ID_SCHEMA_RELAXNG);
sSchemaIds.
put(
XMLValidationSchema.
SCHEMA_ID_W3C_SCHEMA,
INTERNAL_ID_SCHEMA_W3C);
sSchemaIds.
put(
XMLValidationSchema.
SCHEMA_ID_TREX,
INTERNAL_ID_SCHEMA_TREX);
}
// // // Properties for locating implementations
/**
* Name of resource file that contains JAXP properties.
*/
final static
String JAXP_PROP_FILENAME = "jaxp.properties";
/**
* Defines the system property that can be set to explicitly specify
* which implementation to use (in case there are multiple StAX2
* implementations; or the one used does not specify other mechanisms
* for the loader to find the implementation class).
*/
public final static
String SYSTEM_PROPERTY_FOR_IMPL = "org.codehaus.stax2.validation.XMLValidationSchemaFactory.";
/**
* Path to resource that should contain implementation class definition.
*/
public final static
String SERVICE_DEFINITION_PATH = "META-INF/services/" +
SYSTEM_PROPERTY_FOR_IMPL;
// // // Names of standard configuration properties
/**
* Property that determines whether schemas constructed are namespace-aware,
* in cases where schema supports both namespace-aware and non-namespace
* aware modes. In general this only applies to DTDs, since namespace
* support for DTDs is both optional, and not well specified.
*<p>
* Default value is TRUE. For schema types for which only one value
* (usually TRUE) is allowed, this property will be ignored.
*/
public static final
String P_IS_NAMESPACE_AWARE = "org.codehaus2.stax2.validation.isNamespaceAware";
/**
* Property that determines whether schema instances created by this
* factory instance can be cached by the factory; if false, no caching
* is allowed to be doe; if true, factory can do caching if it wants to.
* The exact rules used to determine unique id of schema instances is
* factory dependant; it is expected that the implementations use
* implementation based on unified system ids (serialized URLs or such).
*/
public static final
String P_ENABLE_CACHING = "org.codehaus2.stax2.validation.enableCaching";
/**
* Schema type this factory instance supports.
*/
protected final
String mSchemaType;
/**
* @param st Schema type this factory supports; one of
* <code>SCHEMA_ID_xxx</code> constants.
*/
protected
XMLValidationSchemaFactory(
String st)
{
mSchemaType =
st;
}
/*
////////////////////////////////////////////////////////
// Factory methods
////////////////////////////////////////////////////////
*/
// // // First creating the factory instances:
/**
* Creates a new XMLValidationFactory instance, using the default
* instance configuration mechanism.
*/
public static
XMLValidationSchemaFactory newInstance(
String schemaType)
throws
FactoryConfigurationError
{
return
newInstance(
schemaType,
Thread.
currentThread().
getContextClassLoader());
}
public static
XMLValidationSchemaFactory newInstance(
String schemaType,
ClassLoader classLoader)
throws
FactoryConfigurationError
{
/* First, let's check and map schema type to the shorter internal
* id:
*/
String internalId = (
String)
sSchemaIds.
get(
schemaType);
if (
internalId == null) {
throw new
FactoryConfigurationError("Unrecognized schema type (id '"+
schemaType+"')");
}
String propertyId =
SYSTEM_PROPERTY_FOR_IMPL +
internalId;
SecurityException secEx = null;
/* First, let's see if there's a system property (overrides other
* settings)
*/
try {
String clsName =
System.
getProperty(
propertyId);
if (
clsName != null &&
clsName.
length() > 0) {
return
createNewInstance(
classLoader,
clsName);
}
} catch (
SecurityException se) {
// May happen on sandbox envs, like applets?
secEx =
se;
}
/* try to read from $java.home/lib/xml.properties (simulating
* the way XMLInputFactory does this... not sure if this should
* be done, as this is not [yet?] really jaxp specified)
*/
try {
String home =
System.
getProperty("java.home");
File f = new
File(
home);
// Let's not hard-code separators...
f = new
File(
f, "lib");
f = new
File(
f,
JAXP_PROP_FILENAME);
if (
f.
exists()) {
try {
Properties props = new
Properties();
props.
load(new
FileInputStream(
f));
String clsName =
props.
getProperty(
propertyId);
if (
clsName != null &&
clsName.
length() > 0) {
return
createNewInstance(
classLoader,
clsName);
}
} catch (
IOException ioe) {
// can also happen quite easily...
}
}
} catch (
SecurityException se) {
// Fine, as above
secEx =
se;
}
/* Ok, no match; how about a service def from the impl jar?
*/
// try to find services in CLASSPATH
String path =
SERVICE_DEFINITION_PATH +
internalId;
try {
Enumeration<?>
en;
if (
classLoader == null) {
en =
ClassLoader.
getSystemResources(
path);
} else {
en =
classLoader.
getResources(
path);
}
if (
en != null) {
while (
en.
hasMoreElements()) {
URL url = (
URL)
en.
nextElement();
InputStream is =
url.
openStream();
BufferedReader rd =
new
BufferedReader(new
InputStreamReader(
is, "ISO-8859-1"));
String clsName = null;
String line;
try {
while ((
line =
rd.
readLine()) != null) {
line =
line.
trim();
if (
line.
length() > 0 &&
line.
charAt(0) != '#') {
clsName =
line;
break;
}
}
} finally {
rd.
close();
}
if (
clsName != null &&
clsName.
length() > 0) {
return
createNewInstance(
classLoader,
clsName);
}
}
}
} catch (
SecurityException se) {
secEx =
se;
} catch (
IOException ex) {
/* Let's assume these are mostly ok, too (missing jar ie.)
*/
}
String msg = "No XMLValidationSchemaFactory implementation class specified or accessible (via system property '"
+
propertyId+"', or service definition under '"+
path+"')";
if (
secEx != null) {
throw new
FactoryConfigurationError(
msg + " (possibly caused by: "+
secEx+")",
secEx);
}
throw new
FactoryConfigurationError(
msg);
}
// // // And then actual per-instance factory methods
public
XMLValidationSchema createSchema(
InputStream in)
throws
XMLStreamException
{
return
createSchema(
in, null);
}
public
XMLValidationSchema createSchema(
InputStream in,
String encoding)
throws
XMLStreamException
{
return
createSchema(
in,
encoding, null, null);
}
public abstract
XMLValidationSchema createSchema(
InputStream in,
String encoding,
String publicId,
String systemId)
throws
XMLStreamException;
public
XMLValidationSchema createSchema(
Reader r)
throws
XMLStreamException
{
return
createSchema(
r, null, null);
}
public abstract
XMLValidationSchema createSchema(
Reader r,
String publicId,
String systemId)
throws
XMLStreamException;
public abstract
XMLValidationSchema createSchema(
URL url)
throws
XMLStreamException;
public abstract
XMLValidationSchema createSchema(
File f)
throws
XMLStreamException;
/*
////////////////////////////////////////////////////////
// Configuration
////////////////////////////////////////////////////////
*/
public abstract boolean
isPropertySupported(
String propName);
/**
* @param propName Name of property to set
* @param value Value to set property to
*
* @return True if setting succeeded; false if property was recognized
* but could not be changed to specified value, or if it was not
* recognized but the implementation did not throw an exception.
*/
public abstract boolean
setProperty(
String propName,
Object value);
public abstract
Object getProperty(
String propName);
/**
* @return Name of schema type (one of <code>SCHEMA_ID_xxx</code>
* constants) that this factory supports.
*/
public final
String getSchemaType() { return
mSchemaType; }
/*
///////////////////////////////////////////////////////
// Internal methods
///////////////////////////////////////////////////////
*/
private static
XMLValidationSchemaFactory createNewInstance(
ClassLoader cloader,
String clsName)
throws
FactoryConfigurationError
{
try {
Class<?>
factoryClass;
if (
cloader == null) {
factoryClass =
Class.
forName(
clsName);
} else {
factoryClass =
cloader.
loadClass(
clsName);
}
return (
XMLValidationSchemaFactory)
factoryClass.
newInstance();
} catch (
ClassNotFoundException x) {
throw new
FactoryConfigurationError
("XMLValidationSchemaFactory implementation '"+
clsName+"' not found (missing jar in classpath?)",
x);
} catch (
Exception x) {
throw new
FactoryConfigurationError
("XMLValidationSchemaFactory implementation '"+
clsName+"' could not be instantiated: "+
x,
x);
}
}
}