Home | History | Annotate | Download | only in domts
      1 /*
      2  * Copyright (c) 2001-2004 World Wide Web Consortium,
      3  * (Massachusetts Institute of Technology, Institut National de
      4  * Recherche en Informatique et en Automatique, Keio University). All
      5  * Rights Reserved. This program is distributed under the W3C's Software
      6  * Intellectual Property License. This program is distributed in the
      7  * hope that it will be useful, but WITHOUT ANY WARRANTY; without even
      8  * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
      9  * PURPOSE.
     10  * See W3C License http://www.w3.org/Consortium/Legal/ for more details.
     11  */
     12 
     13 package org.w3c.domts;
     14 
     15 import java.lang.reflect.InvocationTargetException;
     16 import java.lang.reflect.Method;
     17 import java.util.HashMap;
     18 import java.util.Map;
     19 
     20 import org.w3c.dom.DOMImplementation;
     21 import org.w3c.dom.Document;
     22 
     23 /**
     24  *   This class implements the generic parser and configuation
     25  *   abstract class for the DOM L3 implementations
     26  *
     27  *   @author Curt Arnold
     28  */
     29 public class LSDocumentBuilderFactory
     30     extends DOMTestDocumentBuilderFactory {
     31 
     32   private final Object parser;
     33   private final Method parseURIMethod;
     34   private final DOMImplementation impl;
     35 
     36   /**
     37    *
     38    * Abstract class for a strategy to map a DocumentBuilderSetting
     39    * to an action on LSParser.
     40    */
     41   private static abstract class LSStrategy {
     42 
     43     /**
     44      * Constructor.
     45      */
     46     protected LSStrategy() {
     47     }
     48 
     49     /**
     50      * Applies setting to LSParser
     51      *
     52      * @param setting setting
     53      * @param parser parser
     54      * @throws DOMTestIncompatibleException if parser does not support setting
     55      */
     56     public abstract void applySetting(DocumentBuilderSetting setting,
     57                                       Object parser) throws
     58         DOMTestIncompatibleException;
     59 
     60     /**
     61      * Gets state of setting for parser
     62      *
     63      * @param parser parser
     64      * @return state of setting
     65      */
     66     public abstract boolean hasSetting(Object parser);
     67 
     68   }
     69 
     70   /**
     71    * Represents a fixed setting, for example, all Java implementations
     72    * supported signed values.
     73    *
     74    */
     75   private static class LSFixedStrategy
     76       extends LSStrategy {
     77     private final boolean fixedValue;
     78 
     79     /**
     80      * Constructor
     81      *
     82      * @param settingName setting name
     83      * @param fixedValue fixed value
     84      */
     85     public LSFixedStrategy(boolean fixedValue) {
     86       this.fixedValue = fixedValue;
     87     }
     88 
     89     /**
     90      * Apply setting.  Throws exception if requested setting
     91      * does not match fixed value.
     92      */
     93     public void applySetting(DocumentBuilderSetting setting, Object parser) throws
     94         DOMTestIncompatibleException {
     95       if (setting.getValue() != fixedValue) {
     96         throw new DOMTestIncompatibleException(null, setting);
     97       }
     98     }
     99 
    100     /**
    101      * Gets fixed value for setting
    102      */
    103     public boolean hasSetting(Object parser) {
    104       return fixedValue;
    105     }
    106   }
    107 
    108   /**
    109    * A strategy for a setting that can be applied by setting a DOMConfiguration
    110    * parameter.
    111    *
    112    */
    113   private static class LSParameterStrategy
    114       extends LSStrategy {
    115     private final String lsParameter;
    116     private final boolean inverse;
    117 
    118     /**
    119      * Constructor
    120      *
    121      * @param lsParameter corresponding DOMConfiguration parameter
    122      * @param inverse if true, DOMConfiguration value is the inverse
    123      * of the setting value
    124      */
    125     public LSParameterStrategy(String lsParameter, boolean inverse) {
    126       this.lsParameter = lsParameter;
    127       this.inverse = inverse;
    128     }
    129 
    130     protected static void setParameter(DocumentBuilderSetting setting,
    131                                        Object parser,
    132                                        String parameter,
    133                                        Object value) throws
    134         DOMTestIncompatibleException {
    135       try {
    136         Method domConfigMethod = parser.getClass().getMethod("getDomConfig",
    137             new Class[0]);
    138         Object domConfig = domConfigMethod.invoke(parser, new Object[0]);
    139         Method setParameterMethod = domConfig.getClass().getMethod(
    140             "setParameter", new Class[] {String.class, Object.class});
    141         setParameterMethod.invoke(domConfig, new Object[] {parameter, value});
    142 
    143       }
    144       catch (InvocationTargetException ex) {
    145         throw new DOMTestIncompatibleException(ex.getTargetException(), setting);
    146       }
    147       catch (Exception ex) {
    148         throw new DOMTestIncompatibleException(ex, setting);
    149       }
    150     }
    151 
    152     protected static Object getParameter(Object parser,
    153                                          String parameter) throws Exception {
    154       Method domConfigMethod = parser.getClass().getMethod("getDomConfig",
    155           new Class[0]);
    156       Object domConfig = domConfigMethod.invoke(parser, new Object[0]);
    157       Method getParameterMethod = domConfig.getClass().getMethod("getParameter",
    158           new Class[] {String.class});
    159       return getParameterMethod.invoke(domConfig, new Object[] {parameter});
    160     }
    161 
    162     /**
    163      * Apply setting
    164      */
    165     public void applySetting(DocumentBuilderSetting setting, Object parser) throws
    166         DOMTestIncompatibleException {
    167       if (inverse) {
    168         setParameter(setting, parser, lsParameter,
    169                      new Boolean(!setting.getValue()));
    170       }
    171       else {
    172         setParameter(setting, parser, lsParameter, new Boolean(setting.getValue()));
    173       }
    174     }
    175 
    176     /**
    177      * Get value of setting
    178      */
    179     public boolean hasSetting(Object parser) {
    180       try {
    181         if (inverse) {
    182           return! ( (Boolean) getParameter(parser, lsParameter)).booleanValue();
    183         }
    184         else {
    185           return ( (Boolean) getParameter(parser, lsParameter)).booleanValue();
    186         }
    187       }
    188       catch (Exception ex) {
    189         return false;
    190       }
    191     }
    192   }
    193 
    194   /**
    195    * A strategy for the validation settings which require
    196    * two DOMConfigurure parameters being set, 'validate' and 'schema-type'
    197    *
    198    */
    199   private static class LSValidateStrategy
    200       extends LSParameterStrategy {
    201     private final String schemaType;
    202 
    203     /**
    204      * Constructor
    205      * @param schemaType schema type
    206      */
    207     public LSValidateStrategy(String schemaType) {
    208       super("validate", false);
    209       this.schemaType = schemaType;
    210     }
    211 
    212     /**
    213      * Apply setting
    214      */
    215     public void applySetting(DocumentBuilderSetting setting, Object parser) throws
    216         DOMTestIncompatibleException {
    217       super.applySetting(setting, parser);
    218       setParameter(null, parser, "schema-type", schemaType);
    219     }
    220 
    221     /**
    222      * Get setting value
    223      */
    224     public boolean hasSetting(Object parser) {
    225       if (super.hasSetting(parser)) {
    226         try {
    227           String parserSchemaType = (String) getParameter(parser, "schema-type");
    228           if (schemaType == null || schemaType.equals(parserSchemaType)) {
    229             return true;
    230           }
    231         }
    232         catch (Exception ex) {
    233         }
    234       }
    235       return false;
    236     }
    237 
    238   }
    239 
    240   /**
    241    * Strategies for mapping DocumentBuilderSettings to
    242    * actions on LSParser
    243    */
    244   private static final Map strategies;
    245 
    246   static {
    247     strategies = new HashMap();
    248     strategies.put("coalescing", new LSParameterStrategy("cdata-sections", true));
    249     strategies.put("expandEntityReferences", new LSParameterStrategy("entities", true));
    250     strategies.put("ignoringElementContentWhitespace",
    251                    new LSParameterStrategy("element-content-whitespace", true));
    252     strategies.put("namespaceAware", new LSParameterStrategy("namespaces", false));
    253     strategies.put("validating",
    254                    new LSValidateStrategy("http://www.w3.org/TR/REC-xml"));
    255     strategies.put("schemaValidating",
    256                    new LSValidateStrategy("http://www.w3.org/2001/XMLSchema"));
    257     strategies.put("ignoringComments", new LSParameterStrategy("comments", true));
    258     strategies.put("signed", new LSFixedStrategy(true));
    259     strategies.put("hasNullString", new LSFixedStrategy(true));
    260   }
    261 
    262   /**
    263    * Creates a LS implementation of DOMTestDocumentBuilderFactory.
    264    * @param settings array of settings, may be null.
    265    * @throws DOMTestIncompatibleException
    266    *     Thrown if implementation does not support the specified settings
    267    */
    268   public LSDocumentBuilderFactory(DocumentBuilderSetting[] settings) throws
    269       DOMTestIncompatibleException {
    270     super(settings);
    271 
    272     try {
    273       Class domImplRegistryClass = Class.forName(
    274           "org.w3c.dom.bootstrap.DOMImplementationRegistry");
    275       Method newInstanceMethod = domImplRegistryClass.getMethod("newInstance", (Class<?>) null);
    276       Object domRegistry = newInstanceMethod.invoke(null, (Class<?>) null);
    277       Method getDOMImplementationMethod = domImplRegistryClass.getMethod(
    278           "getDOMImplementation", new Class[] {String.class});
    279       impl = (DOMImplementation) getDOMImplementationMethod.invoke(domRegistry,
    280           new Object[] {"LS"});
    281       Method createLSParserMethod = impl.getClass().getMethod("createLSParser",
    282           new Class[] {short.class, String.class});
    283       parser = createLSParserMethod.invoke(impl,
    284                                            new Object[] {new Short( (short) 1), null});
    285       parseURIMethod = parser.getClass().getMethod("parseURI",
    286           new Class[] {String.class});
    287     }
    288     catch (InvocationTargetException ex) {
    289       throw new DOMTestIncompatibleException(ex.getTargetException(), null);
    290     }
    291     catch (Exception ex) {
    292       throw new DOMTestIncompatibleException(ex, null);
    293     }
    294 
    295     if (settings != null) {
    296       for (int i = 0; i < settings.length; i++) {
    297         Object strategy = strategies.get(settings[i].getProperty());
    298         if (strategy == null) {
    299           throw new DOMTestIncompatibleException(null, settings[i]);
    300         }
    301         else {
    302           ( (LSStrategy) strategy).applySetting(settings[i], parser);
    303         }
    304       }
    305     }
    306   }
    307 
    308   /**
    309    *    Create new instance of document builder factory
    310    *    reflecting specified settings
    311    *    @param newSettings new settings
    312    *    @return New instance
    313    *    @throws DOMTestIncompatibleException
    314    *         if settings are not supported by implementation
    315    */
    316   public DOMTestDocumentBuilderFactory newInstance(
    317       DocumentBuilderSetting[] newSettings) throws DOMTestIncompatibleException {
    318     if (newSettings == null) {
    319       return this;
    320     }
    321     DocumentBuilderSetting[] mergedSettings = mergeSettings(newSettings);
    322     return new LSDocumentBuilderFactory(mergedSettings);
    323   }
    324 
    325   /**
    326    *    Loads specified URL
    327    *    @param url url to load
    328    *    @return DOM document
    329    *    @throws DOMTestLoadException if unable to load document
    330    */
    331   public Document load(java.net.URL url) throws DOMTestLoadException {
    332     try {
    333       return (Document) parseURIMethod.invoke(parser,
    334                                               new Object[] {url.toString()});
    335     }
    336     catch (InvocationTargetException ex) {
    337       throw new DOMTestLoadException(ex.getTargetException());
    338     }
    339     catch (Exception ex) {
    340       throw new DOMTestLoadException(ex);
    341     }
    342   }
    343 
    344   /**
    345    *     Gets DOMImplementation
    346    *     @return DOM implementation, may be null
    347    */
    348   public DOMImplementation getDOMImplementation() {
    349     return impl;
    350   }
    351 
    352   /**
    353    *   Determines if the implementation supports the specified feature
    354    *   @param feature Feature
    355    *   @param version Version
    356    *   @return true if implementation supports the feature
    357    */
    358   public boolean hasFeature(String feature, String version) {
    359     return getDOMImplementation().hasFeature(feature, version);
    360   }
    361 
    362   private boolean hasProperty(String parameter) {
    363     try {
    364       return ( (Boolean) LSParameterStrategy.getParameter(parser, parameter)).
    365           booleanValue();
    366     }
    367     catch (Exception ex) {
    368       return true;
    369     }
    370 
    371   }
    372 
    373   /**
    374    *   Indicates whether the implementation combines text and cdata nodes.
    375    *   @return true if coalescing
    376    */
    377   public boolean isCoalescing() {
    378     return!hasProperty("cdata-sections");
    379   }
    380 
    381   /**
    382    *   Indicates whether the implementation expands entity references.
    383    *   @return true if expanding entity references
    384    */
    385   public boolean isExpandEntityReferences() {
    386     return!hasProperty("entities");
    387   }
    388 
    389   /**
    390    *   Indicates whether the implementation ignores
    391    *       element content whitespace.
    392    *   @return true if ignoring element content whitespace
    393    */
    394   public boolean isIgnoringElementContentWhitespace() {
    395     return!hasProperty("element-content-whitespace");
    396   }
    397 
    398   /**
    399    *   Indicates whether the implementation is namespace aware.
    400    *   @return true if namespace aware
    401    */
    402   public boolean isNamespaceAware() {
    403     return hasProperty("namespaces");
    404   }
    405 
    406   /**
    407    *   Indicates whether the implementation is validating.
    408    *   @return true if validating
    409    */
    410   public boolean isValidating() {
    411     return hasProperty("validate");
    412   }
    413 
    414 }
    415