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