blob: b76960200fb52b695f44a36650be9319b4532513 [file] [log] [blame]
/*
* Copyright (c) 2001-2004 World Wide Web Consortium,
* (Massachusetts Institute of Technology, Institut National de
* Recherche en Informatique et en Automatique, Keio University). All
* Rights Reserved. This program is distributed under the W3C's Software
* Intellectual Property License. This program 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 W3C License http://www.w3.org/Consortium/Legal/ for more details.
*/
package org.w3c.domts;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
/**
* This class implements the generic parser and configuation
* abstract class for the DOM L3 implementations
*
* @author Curt Arnold
*/
public class LSDocumentBuilderFactory
extends DOMTestDocumentBuilderFactory {
private final Object parser;
private final Method parseURIMethod;
private final DOMImplementation impl;
/**
*
* Abstract class for a strategy to map a DocumentBuilderSetting
* to an action on LSParser.
*/
private static abstract class LSStrategy {
/**
* Constructor.
*/
protected LSStrategy() {
}
/**
* Applies setting to LSParser
*
* @param setting setting
* @param parser parser
* @throws DOMTestIncompatibleException if parser does not support setting
*/
public abstract void applySetting(DocumentBuilderSetting setting,
Object parser) throws
DOMTestIncompatibleException;
/**
* Gets state of setting for parser
*
* @param parser parser
* @return state of setting
*/
public abstract boolean hasSetting(Object parser);
}
/**
* Represents a fixed setting, for example, all Java implementations
* supported signed values.
*
*/
private static class LSFixedStrategy
extends LSStrategy {
private final boolean fixedValue;
/**
* Constructor
*
* @param settingName setting name
* @param fixedValue fixed value
*/
public LSFixedStrategy(boolean fixedValue) {
this.fixedValue = fixedValue;
}
/**
* Apply setting. Throws exception if requested setting
* does not match fixed value.
*/
public void applySetting(DocumentBuilderSetting setting, Object parser) throws
DOMTestIncompatibleException {
if (setting.getValue() != fixedValue) {
throw new DOMTestIncompatibleException(null, setting);
}
}
/**
* Gets fixed value for setting
*/
public boolean hasSetting(Object parser) {
return fixedValue;
}
}
/**
* A strategy for a setting that can be applied by setting a DOMConfiguration
* parameter.
*
*/
private static class LSParameterStrategy
extends LSStrategy {
private final String lsParameter;
private final boolean inverse;
/**
* Constructor
*
* @param lsParameter corresponding DOMConfiguration parameter
* @param inverse if true, DOMConfiguration value is the inverse
* of the setting value
*/
public LSParameterStrategy(String lsParameter, boolean inverse) {
this.lsParameter = lsParameter;
this.inverse = inverse;
}
protected static void setParameter(DocumentBuilderSetting setting,
Object parser,
String parameter,
Object value) throws
DOMTestIncompatibleException {
try {
Method domConfigMethod = parser.getClass().getMethod("getDomConfig",
new Class[0]);
Object domConfig = domConfigMethod.invoke(parser, new Object[0]);
Method setParameterMethod = domConfig.getClass().getMethod(
"setParameter", new Class[] {String.class, Object.class});
setParameterMethod.invoke(domConfig, new Object[] {parameter, value});
}
catch (InvocationTargetException ex) {
throw new DOMTestIncompatibleException(ex.getTargetException(), setting);
}
catch (Exception ex) {
throw new DOMTestIncompatibleException(ex, setting);
}
}
protected static Object getParameter(Object parser,
String parameter) throws Exception {
Method domConfigMethod = parser.getClass().getMethod("getDomConfig",
new Class[0]);
Object domConfig = domConfigMethod.invoke(parser, new Object[0]);
Method getParameterMethod = domConfig.getClass().getMethod("getParameter",
new Class[] {String.class});
return getParameterMethod.invoke(domConfig, new Object[] {parameter});
}
/**
* Apply setting
*/
public void applySetting(DocumentBuilderSetting setting, Object parser) throws
DOMTestIncompatibleException {
if (inverse) {
setParameter(setting, parser, lsParameter,
new Boolean(!setting.getValue()));
}
else {
setParameter(setting, parser, lsParameter, new Boolean(setting.getValue()));
}
}
/**
* Get value of setting
*/
public boolean hasSetting(Object parser) {
try {
if (inverse) {
return! ( (Boolean) getParameter(parser, lsParameter)).booleanValue();
}
else {
return ( (Boolean) getParameter(parser, lsParameter)).booleanValue();
}
}
catch (Exception ex) {
return false;
}
}
}
/**
* A strategy for the validation settings which require
* two DOMConfigurure parameters being set, 'validate' and 'schema-type'
*
*/
private static class LSValidateStrategy
extends LSParameterStrategy {
private final String schemaType;
/**
* Constructor
* @param schemaType schema type
*/
public LSValidateStrategy(String schemaType) {
super("validate", false);
this.schemaType = schemaType;
}
/**
* Apply setting
*/
public void applySetting(DocumentBuilderSetting setting, Object parser) throws
DOMTestIncompatibleException {
super.applySetting(setting, parser);
setParameter(null, parser, "schema-type", schemaType);
}
/**
* Get setting value
*/
public boolean hasSetting(Object parser) {
if (super.hasSetting(parser)) {
try {
String parserSchemaType = (String) getParameter(parser, "schema-type");
if (schemaType == null || schemaType.equals(parserSchemaType)) {
return true;
}
}
catch (Exception ex) {
}
}
return false;
}
}
/**
* Strategies for mapping DocumentBuilderSettings to
* actions on LSParser
*/
private static final Map strategies;
static {
strategies = new HashMap();
strategies.put("coalescing", new LSParameterStrategy("cdata-sections", true));
strategies.put("expandEntityReferences", new LSParameterStrategy("entities", true));
strategies.put("ignoringElementContentWhitespace",
new LSParameterStrategy("element-content-whitespace", true));
strategies.put("namespaceAware", new LSParameterStrategy("namespaces", false));
strategies.put("validating",
new LSValidateStrategy("http://www.w3.org/TR/REC-xml"));
strategies.put("schemaValidating",
new LSValidateStrategy("http://www.w3.org/2001/XMLSchema"));
strategies.put("ignoringComments", new LSParameterStrategy("comments", true));
strategies.put("signed", new LSFixedStrategy(true));
strategies.put("hasNullString", new LSFixedStrategy(true));
}
/**
* Creates a LS implementation of DOMTestDocumentBuilderFactory.
* @param settings array of settings, may be null.
* @throws DOMTestIncompatibleException
* Thrown if implementation does not support the specified settings
*/
public LSDocumentBuilderFactory(DocumentBuilderSetting[] settings) throws
DOMTestIncompatibleException {
super(settings);
try {
Class domImplRegistryClass = Class.forName(
"org.w3c.dom.bootstrap.DOMImplementationRegistry");
Method newInstanceMethod = domImplRegistryClass.getMethod("newInstance", (Class<?>) null);
Object domRegistry = newInstanceMethod.invoke(null, (Class<?>) null);
Method getDOMImplementationMethod = domImplRegistryClass.getMethod(
"getDOMImplementation", new Class[] {String.class});
impl = (DOMImplementation) getDOMImplementationMethod.invoke(domRegistry,
new Object[] {"LS"});
Method createLSParserMethod = impl.getClass().getMethod("createLSParser",
new Class[] {short.class, String.class});
parser = createLSParserMethod.invoke(impl,
new Object[] {new Short( (short) 1), null});
parseURIMethod = parser.getClass().getMethod("parseURI",
new Class[] {String.class});
}
catch (InvocationTargetException ex) {
throw new DOMTestIncompatibleException(ex.getTargetException(), null);
}
catch (Exception ex) {
throw new DOMTestIncompatibleException(ex, null);
}
if (settings != null) {
for (int i = 0; i < settings.length; i++) {
Object strategy = strategies.get(settings[i].getProperty());
if (strategy == null) {
throw new DOMTestIncompatibleException(null, settings[i]);
}
else {
( (LSStrategy) strategy).applySetting(settings[i], parser);
}
}
}
}
/**
* Create new instance of document builder factory
* reflecting specified settings
* @param newSettings new settings
* @return New instance
* @throws DOMTestIncompatibleException
* if settings are not supported by implementation
*/
public DOMTestDocumentBuilderFactory newInstance(
DocumentBuilderSetting[] newSettings) throws DOMTestIncompatibleException {
if (newSettings == null) {
return this;
}
DocumentBuilderSetting[] mergedSettings = mergeSettings(newSettings);
return new LSDocumentBuilderFactory(mergedSettings);
}
/**
* Loads specified URL
* @param url url to load
* @return DOM document
* @throws DOMTestLoadException if unable to load document
*/
public Document load(java.net.URL url) throws DOMTestLoadException {
try {
return (Document) parseURIMethod.invoke(parser,
new Object[] {url.toString()});
}
catch (InvocationTargetException ex) {
throw new DOMTestLoadException(ex.getTargetException());
}
catch (Exception ex) {
throw new DOMTestLoadException(ex);
}
}
/**
* Gets DOMImplementation
* @return DOM implementation, may be null
*/
public DOMImplementation getDOMImplementation() {
return impl;
}
/**
* Determines if the implementation supports the specified feature
* @param feature Feature
* @param version Version
* @return true if implementation supports the feature
*/
public boolean hasFeature(String feature, String version) {
return getDOMImplementation().hasFeature(feature, version);
}
private boolean hasProperty(String parameter) {
try {
return ( (Boolean) LSParameterStrategy.getParameter(parser, parameter)).
booleanValue();
}
catch (Exception ex) {
return true;
}
}
/**
* Indicates whether the implementation combines text and cdata nodes.
* @return true if coalescing
*/
public boolean isCoalescing() {
return!hasProperty("cdata-sections");
}
/**
* Indicates whether the implementation expands entity references.
* @return true if expanding entity references
*/
public boolean isExpandEntityReferences() {
return!hasProperty("entities");
}
/**
* Indicates whether the implementation ignores
* element content whitespace.
* @return true if ignoring element content whitespace
*/
public boolean isIgnoringElementContentWhitespace() {
return!hasProperty("element-content-whitespace");
}
/**
* Indicates whether the implementation is namespace aware.
* @return true if namespace aware
*/
public boolean isNamespaceAware() {
return hasProperty("namespaces");
}
/**
* Indicates whether the implementation is validating.
* @return true if validating
*/
public boolean isValidating() {
return hasProperty("validate");
}
}