/* * 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"); } }