1 38 package com.gargoylesoftware.htmlunit.javascript.configuration; 39 40 import java.io.File ; 41 import java.io.FileInputStream ; 42 import java.io.IOException ; 43 import java.io.InputStream ; 44 import java.io.InputStreamReader ; 45 import java.io.Reader ; 46 import java.lang.reflect.Method ; 47 import java.util.Collections ; 48 import java.util.HashMap ; 49 import java.util.Iterator ; 50 import java.util.Map ; 51 import java.util.Set ; 52 53 import javax.xml.parsers.DocumentBuilder ; 54 import javax.xml.parsers.DocumentBuilderFactory ; 55 56 import org.apache.commons.logging.Log; 57 import org.apache.commons.logging.LogFactory; 58 import org.w3c.dom.Document ; 59 import org.w3c.dom.Element ; 60 import org.w3c.dom.Node ; 61 import org.xml.sax.InputSource ; 62 import org.xml.sax.SAXParseException ; 63 64 import com.gargoylesoftware.htmlunit.Assert; 65 import com.gargoylesoftware.htmlunit.BrowserVersion; 66 import com.gargoylesoftware.htmlunit.javascript.StrictErrorHandler; 67 68 75 public final class JavaScriptConfiguration { 76 private static Document XmlDocument_; 77 78 79 public static final int ENABLED = 1; 80 81 82 public static final int DISABLED = 2; 83 84 85 public static final int NOT_FOUND = 3; 86 87 private static Map ConfigurationMap_ = new HashMap (11); 88 private static HashMap ClassnameMap_ = new HashMap (); 89 private static Map HtmlJavaScriptMap_; 90 91 private final Map configuration_; 92 private final BrowserVersion browser_; 93 94 95 99 private JavaScriptConfiguration(final BrowserVersion browser) { 100 browser_ = browser; 101 if( XmlDocument_ == null ) { 102 loadConfiguration(); 103 } 104 105 if( XmlDocument_ == null ) { 106 throw new IllegalStateException ("Configuration was not initialized - see log for details"); 107 } 108 configuration_ = buildUsageMap(browser); 109 } 110 111 116 protected static boolean isDocumentLoaded() { 117 return XmlDocument_ != null; 118 } 119 120 125 protected static void resetClassForTesting() { 126 XmlDocument_ = null; 127 ConfigurationMap_ = new HashMap (11); 128 } 129 130 134 protected static void setXmlDocument(final Document document) { 135 XmlDocument_ = document; 136 } 137 138 141 protected static void loadConfiguration() { 142 try { 143 final Reader reader = getConfigurationFileAsReader(); 144 if( reader == null ) { 145 getLog().error("Unable to load JavaScriptConfiguration.xml"); 146 } 147 loadConfiguration(reader); 148 reader.close(); 149 } 150 catch( final Exception e ) { 151 getLog().error("Error when loading JavascriptConfiguration.xml", e); 152 e.printStackTrace(); 153 } 154 } 155 156 157 162 protected static void loadConfiguration(final Reader configurationReader) { 163 final InputSource inputSource = new InputSource (configurationReader); 164 165 try { 166 final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 167 factory.setNamespaceAware( true ); 168 factory.setValidating( false ); 169 170 final DocumentBuilder documentBuilder = factory.newDocumentBuilder(); 171 documentBuilder.setErrorHandler( new StrictErrorHandler() ); 172 173 XmlDocument_ = documentBuilder.parse( inputSource ); 174 } 175 catch( final SAXParseException parseException ) { 176 getLog().error( "line=[" + parseException.getLineNumber() 177 + "] columnNumber=[" + parseException.getColumnNumber() 178 + "] systemId=[" + parseException.getSystemId() 179 + "] publicId=[" + parseException.getPublicId() + "]", parseException ); 180 } 181 catch( final Exception e ) { 182 getLog().error("Error when loading JavascriptConfiguration.xml", e); 183 } 184 } 185 186 187 193 public static synchronized JavaScriptConfiguration getInstance( final BrowserVersion browserVersion ) { 194 if (browserVersion == null) { 195 throw new IllegalStateException ("BrowserVersion must be defined"); 196 } 197 JavaScriptConfiguration configuration = 198 (JavaScriptConfiguration)ConfigurationMap_.get(browserVersion); 199 200 if( configuration == null ) { 201 configuration = new JavaScriptConfiguration(browserVersion); 202 ConfigurationMap_.put( browserVersion, configuration ); 203 } 204 return configuration; 205 } 206 207 208 214 static JavaScriptConfiguration getAllEntries() { 215 JavaScriptConfiguration configuration = new JavaScriptConfiguration(null); 216 return configuration; 217 } 218 219 220 221 private static Log getLog() { 222 return LogFactory.getLog(JavaScriptConfiguration.class); 223 } 224 225 226 private static Reader getConfigurationFileAsReader() { 227 final String fileName = "/com/gargoylesoftware/htmlunit/javascript/configuration/JavaScriptConfiguration.xml"; 228 return new InputStreamReader (getResourceAsStream(fileName)); 229 } 230 231 232 private static InputStream getResourceAsStream( final String name ) { 233 Assert.notNull("name", name); 234 InputStream inputStream = JavaScriptConfiguration.class.getResourceAsStream(name); 235 if( inputStream == null ) { 236 try { 237 final String localizedName = name.replace( '/', File.separatorChar ); 238 inputStream = new FileInputStream ( localizedName ); 239 } 240 catch( final IOException e ) { 241 } 243 } 244 245 if( inputStream == null ) { 247 try { 248 final String localizedName = ("./src/java"+name).replace( '/', File.separatorChar ); 249 inputStream = new FileInputStream ( localizedName ); 250 } 251 catch( final IOException e ) { 252 } 254 } 255 return inputStream; 256 } 257 258 262 public Set keySet() { 263 return configuration_.keySet(); 264 } 265 266 private Map buildUsageMap(final BrowserVersion browser) { 267 final Map classMap = new HashMap (30); 268 Node node = XmlDocument_.getDocumentElement().getFirstChild(); 269 while( node != null ) { 270 if( node instanceof Element ) { 271 final Element element = (Element )node; 272 if( element.getTagName().equals("class")) { 273 final String className = element.getAttribute("name"); 274 if (!testToExcludeElement(element)) { 275 try { 276 final ClassConfiguration classConfiguration = parseClassElement(className, element); 277 if (classConfiguration != null) { 278 classMap.put(className, classConfiguration); 279 } 280 } 281 catch (final ClassNotFoundException e) { 282 throw new IllegalStateException ("The class was not found for '" + className + "'"); 283 } 284 } 285 } 286 } 287 node = node.getNextSibling(); 288 } 289 return Collections.unmodifiableMap(classMap); 290 } 291 292 299 private ClassConfiguration parseClassElement(final String className, final Element element) 300 throws ClassNotFoundException { 301 final String notImplemented = element.getAttribute("notImplemented"); 302 if ("true".equalsIgnoreCase(notImplemented)) { 303 return null; 304 } 305 final String linkedClassname = element.getAttribute("classname"); 306 final String superclassName = element.getAttribute("extends"); 307 final String htmlClassname = element.getAttribute("htmlClass"); 308 boolean jsObjectFlag = false; 309 final String jsObjectStr = element.getAttribute("JSObject"); 310 if ("true".equalsIgnoreCase(jsObjectStr)) { 311 jsObjectFlag = true; 312 } 313 final ClassConfiguration classConfiguration = 314 new ClassConfiguration(className, linkedClassname, superclassName, htmlClassname, jsObjectFlag); 315 ClassnameMap_.put(linkedClassname, className); 316 Node node = element.getFirstChild(); 317 while( node != null ) { 318 if( node instanceof Element ) { 319 final Element childElement = (Element )node; 320 final String tagName = childElement.getTagName(); 321 if( tagName.equals("property")) { 322 parsePropertyElement(classConfiguration, childElement); 323 } 324 else if (tagName.equals("function")) { 325 parseFunctionElement(classConfiguration, childElement); 326 } 327 else if (tagName.equals("javascript")) { 328 getLog().debug("javascript tag not yet handled for class " + linkedClassname); 329 } 330 else if (tagName.equals("browser")) { 331 getLog().debug("browser tag not yet handled for class " + linkedClassname); 332 } 333 else if (tagName.equals("doclink")) { 334 } 336 else { 337 throw new IllegalStateException ("Do not understand element type '" 338 + tagName + "' in '" + linkedClassname + "'"); 339 } 340 } 341 node = node.getNextSibling(); 342 } 343 return classConfiguration; 344 } 345 346 352 private void parsePropertyElement(final ClassConfiguration classConfiguration, final Element element) { 353 final String notImplemented = element.getAttribute("notImplemented"); 354 if ("true".equalsIgnoreCase(notImplemented)) { 355 return; 356 } 357 if (testToExcludeElement(element)) { 358 return; 359 } 360 final String propertyName = element.getAttribute("name"); 361 boolean readable = false; 362 boolean writeable = false; 363 final String readFlag = element.getAttribute("readable"); 364 if ("true".equalsIgnoreCase(readFlag)) { 365 readable = true; 366 } 367 final String writeFlag = element.getAttribute("writable"); 368 if ("true".equalsIgnoreCase(writeFlag)) { 369 writeable = true; 370 } 371 classConfiguration.addProperty(propertyName, readable, writeable); 372 } 373 374 380 private void parseFunctionElement(final ClassConfiguration classConfiguration, final Element element) { 381 final String notImplemented = element.getAttribute("notImplemented"); 382 if ("true".equalsIgnoreCase(notImplemented)) { 383 return; 384 } 385 final String propertyName = element.getAttribute("name"); 386 if (testToExcludeElement(element)) { 387 return; 388 } 389 classConfiguration.addFunction(propertyName); 390 } 391 392 398 private boolean testToExcludeElement(final Element element) { 399 if (browser_ == null) { 400 return false; 401 } 402 Node node = element.getFirstChild(); 403 boolean browserConstraint = false; 404 boolean allowBrowser = false; 405 boolean javascriptConstraint = false; 406 boolean allowJavascriptConstraint = false; 407 while( node != null ) { 408 if( node instanceof Element ) { 409 final Element childElement = (Element )node; 410 if( childElement.getTagName().equals("browser")) { 411 browserConstraint = true; 412 if (testToIncludeForBrowserConstraint(childElement, browser_)) { 413 allowBrowser = true; 414 } 415 } 416 else if( childElement.getTagName().equals("javascript")) { 417 javascriptConstraint = true; 418 if (testToIncludeForJSConstraint(childElement, browser_)) { 419 allowJavascriptConstraint = true; 420 } 421 } 422 } 423 node = node.getNextSibling(); 424 } 425 if (browserConstraint && !allowBrowser) { 426 return true; 427 } 428 if (javascriptConstraint && !allowJavascriptConstraint) { 429 return true; 430 } 431 return false; 432 } 433 434 442 protected boolean classConfigEquals(final String classname, final ClassConfiguration config) { 443 final ClassConfiguration myConfig = (ClassConfiguration) configuration_.get(classname); 444 return config.equals(myConfig); 445 } 446 449 public BrowserVersion getBrowser() { 450 return browser_; 451 } 452 453 458 public ClassConfiguration getClassConfiguration(final String classname) { 459 return (ClassConfiguration) configuration_.get(classname); 460 } 461 462 private boolean testToIncludeForBrowserConstraint(final Element element, final BrowserVersion browser) { 463 if (!browser.getApplicationName().equals(element.getAttribute("name"))) { 464 return false; 465 } 466 final String max = element.getAttribute("max-version"); 467 float maxVersion; 468 if (max.length() == 0) { 469 maxVersion = 0; 470 } 471 else { 472 maxVersion = Float.parseFloat(max); 473 } 474 if ((maxVersion > 0) && (browser.getBrowserVersionNumeric() > maxVersion)) { 475 return false; 476 } 477 478 float minVersion; 479 final String min = element.getAttribute("min-version"); 480 if (min.length() == 0) { 481 minVersion = 0; 482 } 483 else { 484 minVersion = Float.parseFloat(min); 485 } 486 if ((minVersion > 0) && (browser.getBrowserVersionNumeric() < minVersion)) { 487 return false; 488 } 489 return true; 490 } 491 492 493 private boolean testToIncludeForJSConstraint(final Element element, final BrowserVersion browser) { 494 final String max = element.getAttribute("max-version"); 495 float maxVersion; 496 if (max.length() == 0) { 497 maxVersion = 0; 498 } 499 else { 500 maxVersion = Float.parseFloat(max); 501 } 502 if ((maxVersion > 0) && (browser.getJavaScriptVersionNumeric() > maxVersion)) { 503 return false; 504 } 505 506 float minVersion; 507 final String min = element.getAttribute("min-version"); 508 if (min.length() == 0) { 509 minVersion = 0; 510 } 511 else { 512 minVersion = Float.parseFloat(min); 513 } 514 if ((minVersion > 0) && (browser.getJavaScriptVersionNumeric() < minVersion)) { 515 return false; 516 } 517 return true; 518 } 519 520 524 protected Iterator keyIterator() { 525 return configuration_.keySet().iterator(); 526 } 527 528 533 protected Class getClassObject(final String classname) { 534 final ClassConfiguration config = (ClassConfiguration) configuration_.get(classname); 535 return config.getLinkedClass(); 536 } 537 538 544 public Method getPropertyReadMethod(final Class clazz, final String propertyName) { 545 final String classname = getClassnameForClass(clazz); 546 return getPropertyReadMethod(classname, propertyName); 547 } 548 549 556 public Method getPropertyReadMethod(String classname, final String propertyName) { 557 ClassConfiguration config; 558 Method theMethod; 559 while (classname.length() > 0) { 560 config = (ClassConfiguration) configuration_.get(classname); 561 if (config == null) { 562 return null; 563 } 564 theMethod = config.getPropertyReadMethod(propertyName); 565 if (theMethod != null) { 566 return theMethod; 567 } 568 classname = config.getExtendedClass(); 569 } 570 return null; 571 } 572 573 574 private ClassConfiguration.PropertyInfo findPropertyInChain(final String classname, final String propertyName) { 575 String workname = classname; 576 ClassConfiguration config; 577 while (workname.length() > 0) { 578 config = (ClassConfiguration) configuration_.get(workname); 579 final ClassConfiguration.PropertyInfo info = config.getPropertyInfo(propertyName); 580 if (info != null) { 581 return info; 582 } 583 workname = config.getExtendedClass(); 584 } 585 return null; 586 } 587 593 public Method getPropertyWriteMethod(final Class clazz, final String propertyName) { 594 final String classname = getClassnameForClass(clazz); 595 return getPropertyWriteMethod(classname, propertyName); 596 } 597 598 605 public Method getPropertyWriteMethod(String classname, final String propertyName) { 606 ClassConfiguration config; 607 Method theMethod; 608 while (classname.length() > 0) { 609 config = (ClassConfiguration) configuration_.get(classname); 610 theMethod = config.getPropertyWriteMethod(propertyName); 611 if (theMethod != null) { 612 return theMethod; 613 } 614 classname = config.getExtendedClass(); 615 } 616 return null; 617 } 618 619 620 626 public Method getFunctionMethod(final Class clazz, final String functionName) { 627 final String classname = getClassnameForClass(clazz); 628 return getFunctionMethod(classname, functionName); 629 } 630 631 638 public Method getFunctionMethod(String classname, final String functionName) { 639 ClassConfiguration config; 640 Method theMethod; 641 while (classname.length() > 0) { 642 config = (ClassConfiguration) configuration_.get(classname); 643 theMethod = config.getFunctionMethod(functionName); 644 if (theMethod != null) { 645 return theMethod; 646 } 647 classname = config.getExtendedClass(); 648 } 649 return null; 650 } 651 652 658 public boolean propertyExists(final Class clazz, final String propertyName) { 659 final String classname = getClassnameForClass(clazz); 660 return propertyExists(classname, propertyName); 661 } 662 663 664 670 public boolean propertyExists(final String classname, final String propertyName) { 671 final ClassConfiguration.PropertyInfo info = findPropertyInChain(classname, propertyName); 672 if (info == null) { 673 return false; 674 } 675 return true; 676 } 677 678 686 private String getClassnameForClass(final Class clazz) { 687 String name = (String ) ClassnameMap_.get(clazz.getName()); 688 if (name == null) { 689 throw new IllegalStateException ("Did not find the mapping of the class to the classname for " + 690 clazz.getName()); 691 } 692 return name; 693 } 694 695 701 public static Map getHtmlJavaScriptMapping() { 702 if( HtmlJavaScriptMap_ != null ) { 703 return HtmlJavaScriptMap_; 704 } 705 final JavaScriptConfiguration configuration = JavaScriptConfiguration.getAllEntries(); 706 707 final Iterator it = configuration.keyIterator(); 708 String jsClassname; 709 String htmlClassname; 710 ClassConfiguration classConfig; 711 Class htmlClass; 712 final Map map = new HashMap (); 713 714 while (it.hasNext()) { 715 jsClassname = (String ) it.next(); 716 classConfig = configuration.getClassConfiguration(jsClassname); 717 htmlClassname = classConfig.getHtmlClassname(); 718 if (htmlClassname != null) { 719 try { 720 htmlClass = Class.forName(htmlClassname); 721 getLog().debug("Mapping " + htmlClass.getName() + " to " + jsClassname); 723 while (!classConfig.isJsObject()) { 724 jsClassname = classConfig.getExtendedClass(); 725 classConfig = configuration.getClassConfiguration(jsClassname); 726 getLog().debug(" testing to use " + jsClassname); 727 } 728 729 map.put( htmlClass, jsClassname ); 730 } 731 catch( final ClassNotFoundException e ) { 732 throw new NoClassDefFoundError (e.getMessage()); 733 } 734 } 735 } 736 HtmlJavaScriptMap_ = Collections.unmodifiableMap(map); 737 return HtmlJavaScriptMap_; 738 } 739 } 740 | Popular Tags |