001: /*
002: * Copyright (c) 2002-2008 Gargoyle Software Inc. All rights reserved.
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * 1. Redistributions of source code must retain the above copyright notice,
008: * this list of conditions and the following disclaimer.
009: * 2. Redistributions in binary form must reproduce the above copyright notice,
010: * this list of conditions and the following disclaimer in the documentation
011: * and/or other materials provided with the distribution.
012: * 3. The end-user documentation included with the redistribution, if any, must
013: * include the following acknowledgment:
014: *
015: * "This product includes software developed by Gargoyle Software Inc.
016: * (http://www.GargoyleSoftware.com/)."
017: *
018: * Alternately, this acknowledgment may appear in the software itself, if
019: * and wherever such third-party acknowledgments normally appear.
020: * 4. The name "Gargoyle Software" must not be used to endorse or promote
021: * products derived from this software without prior written permission.
022: * For written permission, please contact info@GargoyleSoftware.com.
023: * 5. Products derived from this software may not be called "HtmlUnit", nor may
024: * "HtmlUnit" appear in their name, without prior written permission of
025: * Gargoyle Software Inc.
026: *
027: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
028: * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
029: * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GARGOYLE
030: * SOFTWARE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
031: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
032: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
033: * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
034: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
035: * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
036: * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
037: */
038: package com.gargoylesoftware.htmlunit.javascript.configuration;
039:
040: import java.io.File;
041: import java.io.FileInputStream;
042: import java.io.IOException;
043: import java.io.InputStream;
044: import java.io.InputStreamReader;
045: import java.io.Reader;
046: import java.lang.reflect.Method;
047: import java.util.Collections;
048: import java.util.HashMap;
049: import java.util.Iterator;
050: import java.util.Map;
051: import java.util.Set;
052:
053: import javax.xml.parsers.DocumentBuilder;
054: import javax.xml.parsers.DocumentBuilderFactory;
055:
056: import org.apache.commons.logging.Log;
057: import org.apache.commons.logging.LogFactory;
058: import org.w3c.dom.Document;
059: import org.w3c.dom.Element;
060: import org.w3c.dom.Node;
061: import org.xml.sax.InputSource;
062: import org.xml.sax.SAXParseException;
063:
064: import com.gargoylesoftware.htmlunit.BrowserVersion;
065: import com.gargoylesoftware.htmlunit.javascript.StrictErrorHandler;
066:
067: /**
068: * A container for all the javascript configuration information.
069: * TODO - Need to add the logic to support the browser and javascript conditionals in the Class elements.
070: *
071: * @version $Revision: 2139 $
072: * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
073: * @author Chris Erskine
074: * @author Ahmed Ashour
075: */
076: public final class JavaScriptConfiguration {
077: private static Document XmlDocument_;
078:
079: /** Constant indicating that this function/property is used by the specified browser version */
080: public static final int ENABLED = 1;
081:
082: /** Constant indicating that this function/property is not used by the specified browser version */
083: public static final int DISABLED = 2;
084:
085: /** Constant indicating that this function/property is not defined in the configuration file */
086: public static final int NOT_FOUND = 3;
087:
088: private static Map ConfigurationMap_ = new HashMap(11);
089: private static HashMap ClassnameMap_ = new HashMap();
090: private static Map HtmlJavaScriptMap_;
091:
092: private final Map configuration_;
093: private final BrowserVersion browser_;
094:
095: /**
096: * C'tor is only called from {@link #getInstance(BrowserVersion)} which is synchronized.
097: * @param browser the browser version to use
098: */
099: private JavaScriptConfiguration(final BrowserVersion browser) {
100: browser_ = browser;
101: if (XmlDocument_ == null) {
102: loadConfiguration();
103: }
104:
105: if (XmlDocument_ == null) {
106: throw new IllegalStateException(
107: "Configuration was not initialized - see log for details");
108: }
109: configuration_ = buildUsageMap();
110: }
111:
112: /**
113: * Test for a configuration having been loaded for testing
114: *
115: * @return boolean - true if the XmlDocument has been loaded;
116: */
117: protected static boolean isDocumentLoaded() {
118: return XmlDocument_ != null;
119: }
120:
121: /**
122: * Reset the this class to it's initial state. This method is
123: * used for testing only.
124: *
125: */
126: protected static void resetClassForTesting() {
127: XmlDocument_ = null;
128: ConfigurationMap_ = new HashMap(11);
129: }
130:
131: /**
132: * Set the document configuration for testing
133: * @param document - The configuration document
134: */
135: protected static void setXmlDocument(final Document document) {
136: XmlDocument_ = document;
137: }
138:
139: /**
140: * Get the configuration file and make it an input reader and then pass to the method to read the file.
141: */
142: protected static void loadConfiguration() {
143: try {
144: final Reader reader = getConfigurationFileAsReader();
145: if (reader == null) {
146: getLog().error(
147: "Unable to load JavaScriptConfiguration.xml");
148: }
149: loadConfiguration(reader);
150: reader.close();
151: } catch (final Exception e) {
152: getLog()
153: .error(
154: "Error when loading JavascriptConfiguration.xml",
155: e);
156: e.printStackTrace();
157: }
158: }
159:
160: /**
161: * Load the configuration from a supplied Reader
162: *
163: * @param configurationReader - A reader pointing to the configuration
164: */
165: protected static void loadConfiguration(
166: final Reader configurationReader) {
167: final InputSource inputSource = new InputSource(
168: configurationReader);
169:
170: try {
171: final DocumentBuilderFactory factory = DocumentBuilderFactory
172: .newInstance();
173: factory.setNamespaceAware(true);
174: factory.setValidating(false);
175:
176: final DocumentBuilder documentBuilder = factory
177: .newDocumentBuilder();
178: documentBuilder.setErrorHandler(new StrictErrorHandler());
179:
180: XmlDocument_ = documentBuilder.parse(inputSource);
181: } catch (final SAXParseException parseException) {
182: getLog().error(
183: "line=[" + parseException.getLineNumber()
184: + "] columnNumber=["
185: + parseException.getColumnNumber()
186: + "] systemId=["
187: + parseException.getSystemId()
188: + "] publicId=["
189: + parseException.getPublicId() + "]",
190: parseException);
191: } catch (final Exception e) {
192: getLog()
193: .error(
194: "Error when loading JavascriptConfiguration.xml",
195: e);
196: }
197: }
198:
199: /**
200: * Return the instance that represents the configuration for the specified {@link BrowserVersion}.
201: * This method is synchronized to allow multithreaded access to the Javascript configuration.
202: * @param browserVersion The {@link BrowserVersion}
203: * @return The instance for the specified {@link BrowserVersion}
204: */
205: public static synchronized JavaScriptConfiguration getInstance(
206: final BrowserVersion browserVersion) {
207: if (browserVersion == null) {
208: throw new IllegalStateException(
209: "BrowserVersion must be defined");
210: }
211: JavaScriptConfiguration configuration = (JavaScriptConfiguration) ConfigurationMap_
212: .get(browserVersion);
213:
214: if (configuration == null) {
215: configuration = new JavaScriptConfiguration(browserVersion);
216: ConfigurationMap_.put(browserVersion, configuration);
217: }
218: return configuration;
219: }
220:
221: /**
222: * Return the configuration that has all entries. No constraints are put on the returned
223: * entries.
224: *
225: * @return The instance containing all entries from the configuration file.
226: */
227: static JavaScriptConfiguration getAllEntries() {
228: final JavaScriptConfiguration configuration = new JavaScriptConfiguration(
229: null);
230: return configuration;
231: }
232:
233: private static Log getLog() {
234: return LogFactory.getLog(JavaScriptConfiguration.class);
235: }
236:
237: private static Reader getConfigurationFileAsReader() {
238: final Class clazz = JavaScriptConfiguration.class;
239: final String name = clazz.getPackage().getName().replace('.',
240: '/')
241: + '/' + "JavaScriptConfiguration.xml";
242: InputStream inputStream = clazz.getClassLoader()
243: .getResourceAsStream(name);
244: if (inputStream == null) {
245: try {
246: final String localizedName = name.replace('/',
247: File.separatorChar);
248: inputStream = new FileInputStream(localizedName);
249: } catch (final IOException e) {
250: // Fall through
251: }
252: }
253:
254: // If we are running maven tests then the path will be slightly different
255: if (inputStream == null) {
256: try {
257: final String localizedName = ("./src/java" + name)
258: .replace('/', File.separatorChar);
259: inputStream = new FileInputStream(localizedName);
260: } catch (final IOException e) {
261: // Fall through
262: }
263: }
264: return new InputStreamReader(inputStream);
265: }
266:
267: /**
268: * Get the set of keys for the class configurations.
269: * @return Set
270: */
271: public Set keySet() {
272: return configuration_.keySet();
273: }
274:
275: private Map buildUsageMap() {
276: final Map classMap = new HashMap(30);
277: Node node = XmlDocument_.getDocumentElement().getFirstChild();
278: while (node != null) {
279: if (node instanceof Element) {
280: final Element element = (Element) node;
281: if (element.getTagName().equals("class")) {
282: final String className = element
283: .getAttribute("name");
284: if (!testToExcludeElement(element)) {
285: try {
286: final ClassConfiguration classConfiguration = parseClassElement(
287: className, element);
288: if (classConfiguration != null) {
289: classMap.put(className,
290: classConfiguration);
291: }
292: } catch (final ClassNotFoundException e) {
293: throw new IllegalStateException(
294: "The class was not found for '"
295: + className + "'");
296: }
297: }
298: }
299: }
300: node = node.getNextSibling();
301: }
302: return Collections.unmodifiableMap(classMap);
303: }
304:
305: /**
306: * Parse the class element to build the class configuration
307: * @param className The name of the class element
308: * @param element The element to parse
309: * @return ClassConfiguration
310: * @throws ClassNotFoundException If the specified class could not be found
311: */
312: private ClassConfiguration parseClassElement(
313: final String className, final Element element)
314: throws ClassNotFoundException {
315: final String notImplemented = element
316: .getAttribute("notImplemented");
317: if ("true".equalsIgnoreCase(notImplemented)) {
318: return null;
319: }
320: final String linkedClassname = element
321: .getAttribute("classname");
322: final String jsConstructor = element
323: .getAttribute("jsConstructor");
324: final String super className = element.getAttribute("extends");
325: final String htmlClassname = element.getAttribute("htmlClass");
326: boolean jsObjectFlag = false;
327: final String jsObjectStr = element.getAttribute("JSObject");
328: if ("true".equalsIgnoreCase(jsObjectStr)) {
329: jsObjectFlag = true;
330: }
331: final ClassConfiguration classConfiguration = new ClassConfiguration(
332: className, linkedClassname, jsConstructor,
333: super className, htmlClassname, jsObjectFlag);
334: ClassnameMap_.put(linkedClassname, className);
335: Node node = element.getFirstChild();
336: while (node != null) {
337: if (node instanceof Element) {
338: final Element childElement = (Element) node;
339: final String tagName = childElement.getTagName();
340: if (tagName.equals("property")) {
341: parsePropertyElement(classConfiguration,
342: childElement);
343: } else if (tagName.equals("function")) {
344: parseFunctionElement(classConfiguration,
345: childElement);
346: } else if (tagName.equals("constant")) {
347: parseConstantElement(classConfiguration,
348: childElement);
349: } else if (tagName.equals("javascript")) {
350: getLog().debug(
351: "javascript tag not yet handled for class "
352: + linkedClassname);
353: } else if (tagName.equals("browser")) {
354: getLog().debug(
355: "browser tag not yet handled for class "
356: + linkedClassname);
357: } else if (tagName.equals("doclink")) {
358: // ignore this link
359: } else {
360: throw new IllegalStateException(
361: "Do not understand element type '"
362: + tagName + "' in '"
363: + linkedClassname + "'");
364: }
365: }
366: node = node.getNextSibling();
367: }
368: return classConfiguration;
369: }
370:
371: /**
372: * Parse out the values for the property.
373: *
374: * @param classConfiguration The configuration that is being built
375: * @param element The property element
376: */
377: private void parsePropertyElement(
378: final ClassConfiguration classConfiguration,
379: final Element element) {
380: final String notImplemented = element
381: .getAttribute("notImplemented");
382: if ("true".equalsIgnoreCase(notImplemented)) {
383: return;
384: }
385: if (testToExcludeElement(element)) {
386: return;
387: }
388: final String propertyName = element.getAttribute("name");
389: boolean readable = false;
390: boolean writeable = false;
391: final String readFlag = element.getAttribute("readable");
392: if ("true".equalsIgnoreCase(readFlag)) {
393: readable = true;
394: }
395: final String writeFlag = element.getAttribute("writable");
396: if ("true".equalsIgnoreCase(writeFlag)) {
397: writeable = true;
398: }
399: classConfiguration.addProperty(propertyName, readable,
400: writeable);
401: }
402:
403: /**
404: * Parse out the values from the function element.
405: *
406: * @param classConfiguration The configuration that is being built
407: * @param element The function element
408: */
409: private void parseFunctionElement(
410: final ClassConfiguration classConfiguration,
411: final Element element) {
412: final String notImplemented = element
413: .getAttribute("notImplemented");
414: if ("true".equalsIgnoreCase(notImplemented)) {
415: return;
416: }
417: final String propertyName = element.getAttribute("name");
418: if (testToExcludeElement(element)) {
419: return;
420: }
421: classConfiguration.addFunction(propertyName);
422: }
423:
424: /**
425: * Parse out the values for the property.
426: *
427: * @param classConfiguration The configuration that is being built
428: * @param element The property element
429: */
430: private void parseConstantElement(
431: final ClassConfiguration classConfiguration,
432: final Element element) {
433: if (testToExcludeElement(element)) {
434: return;
435: }
436: final String constantName = element.getAttribute("name");
437: classConfiguration.addConstant(constantName);
438: }
439:
440: /**
441: * Test for the browser and javascript constraints. Returns true if any constraints are present
442: * and the browser does not meet the constraints.
443: * @param element The element to scan the children of
444: * @return true to exclude this element
445: */
446: private boolean testToExcludeElement(final Element element) {
447: if (browser_ == null) {
448: return false;
449: }
450: Node node = element.getFirstChild();
451: boolean browserConstraint = false;
452: boolean allowBrowser = false;
453: boolean javascriptConstraint = false;
454: boolean allowJavascriptConstraint = false;
455: while (node != null) {
456: if (node instanceof Element) {
457: final Element childElement = (Element) node;
458: if (childElement.getTagName().equals("browser")) {
459: browserConstraint = true;
460: if (testToIncludeForBrowserConstraint(childElement,
461: browser_)) {
462: allowBrowser = true;
463: }
464: } else if (childElement.getTagName().equals(
465: "javascript")) {
466: javascriptConstraint = true;
467: if (testToIncludeForJSConstraint(childElement,
468: browser_)) {
469: allowJavascriptConstraint = true;
470: }
471: }
472: }
473: node = node.getNextSibling();
474: }
475: if (browserConstraint && !allowBrowser) {
476: return true;
477: }
478: if (javascriptConstraint && !allowJavascriptConstraint) {
479: return true;
480: }
481: return false;
482: }
483:
484: /**
485: * Test to see if the supplied configuration matches for the parsed configuration for the named class
486: * This is a method for testing.
487: *
488: * @param classname - the parsed classname to test
489: * @param config - the expected configuration
490: * @return true if they match
491: */
492: protected boolean classConfigEquals(final String classname,
493: final ClassConfiguration config) {
494: final ClassConfiguration myConfig = (ClassConfiguration) configuration_
495: .get(classname);
496: return config.equals(myConfig);
497: }
498:
499: /**
500: * @return Returns the browser.
501: */
502: public BrowserVersion getBrowser() {
503: return browser_;
504: }
505:
506: /**
507: * Get the class configuration for the supplied javascript class name
508: * @param classname The js class name
509: * @return ClassConfiguration
510: */
511: public ClassConfiguration getClassConfiguration(
512: final String classname) {
513: return (ClassConfiguration) configuration_.get(classname);
514: }
515:
516: private boolean testToIncludeForBrowserConstraint(
517: final Element element, final BrowserVersion browser) {
518: if (!browser.getApplicationName().equals(
519: element.getAttribute("name"))
520: && (!browser.isNetscape() || !"Firefox".equals(element
521: .getAttribute("name")))) {
522: return false;
523: }
524: final String max = element.getAttribute("max-version");
525: float maxVersion;
526: if (max.length() == 0) {
527: maxVersion = 0;
528: } else {
529: maxVersion = Float.parseFloat(max);
530: }
531: if ((maxVersion > 0)
532: && (browser.getBrowserVersionNumeric() > maxVersion)) {
533: return false;
534: }
535:
536: float minVersion;
537: final String min = element.getAttribute("min-version");
538: if (min.length() == 0) {
539: minVersion = 0;
540: } else {
541: minVersion = Float.parseFloat(min);
542: }
543: if ((minVersion > 0)
544: && (browser.getBrowserVersionNumeric() < minVersion)) {
545: return false;
546: }
547: return true;
548: }
549:
550: private boolean testToIncludeForJSConstraint(final Element element,
551: final BrowserVersion browser) {
552: final String max = element.getAttribute("max-version");
553: float maxVersion;
554: if (max.length() == 0) {
555: maxVersion = 0;
556: } else {
557: maxVersion = Float.parseFloat(max);
558: }
559: if ((maxVersion > 0)
560: && (browser.getJavaScriptVersionNumeric() > maxVersion)) {
561: return false;
562: }
563:
564: float minVersion;
565: final String min = element.getAttribute("min-version");
566: if (min.length() == 0) {
567: minVersion = 0;
568: } else {
569: minVersion = Float.parseFloat(min);
570: }
571: if ((minVersion > 0)
572: && (browser.getJavaScriptVersionNumeric() < minVersion)) {
573: return false;
574: }
575: return true;
576: }
577:
578: /**
579: * Get an iterator over the keys in the configuration - For testing only
580: * @return Iterator
581: */
582: protected Iterator keyIterator() {
583: return configuration_.keySet().iterator();
584: }
585:
586: /**
587: * Return the class for the given class name
588: * @param classname The classname that you want the implementing class for. For testing only.
589: * @return Class
590: */
591: protected Class getClassObject(final String classname) {
592: final ClassConfiguration config = (ClassConfiguration) configuration_
593: .get(classname);
594: return config.getLinkedClass();
595: }
596:
597: /**
598: * Get the method that implements the getter for the given property based upon the class object.
599: * @param clazz The actual class to use as reference
600: * @param propertyName The property to find the getter for
601: * @return Method
602: */
603: public Method getPropertyReadMethod(final Class clazz,
604: final String propertyName) {
605: final String classname = getClassnameForClass(clazz);
606: return getPropertyReadMethod(classname, propertyName);
607: }
608:
609: /**
610: * Return the method that implements the get function for in the class for the given class
611: *
612: * @param classname The name of the class to work with
613: * @param propertyName The property to find the getter for
614: * @return Method
615: */
616: public Method getPropertyReadMethod(String classname,
617: final String propertyName) {
618: ClassConfiguration config;
619: Method theMethod;
620: while (classname.length() > 0) {
621: config = (ClassConfiguration) configuration_.get(classname);
622: if (config == null) {
623: return null;
624: }
625: theMethod = config.getPropertyReadMethod(propertyName);
626: if (theMethod != null) {
627: return theMethod;
628: }
629: classname = config.getExtendedClass();
630: }
631: return null;
632: }
633:
634: private ClassConfiguration.PropertyInfo findPropertyInChain(
635: final String classname, final String propertyName) {
636: String workname = classname;
637: ClassConfiguration config;
638: while (workname.length() > 0) {
639: config = (ClassConfiguration) configuration_.get(workname);
640: final ClassConfiguration.PropertyInfo info = config
641: .getPropertyInfo(propertyName);
642: if (info != null) {
643: return info;
644: }
645: workname = config.getExtendedClass();
646: }
647: return null;
648: }
649:
650: /**
651: * Get the method that implements the setter for the given property based upon the class object.
652: * @param clazz The actual class to use as reference
653: * @param propertyName The property to find the getter for
654: * @return Method
655: */
656: public Method getPropertyWriteMethod(final Class clazz,
657: final String propertyName) {
658: final String classname = getClassnameForClass(clazz);
659: return getPropertyWriteMethod(classname, propertyName);
660: }
661:
662: /**
663: * Return the method that implements the set function in the class for the given class
664: *
665: * @param classname The name of the class to work with
666: * @param propertyName The property to find the setter for
667: * @return Method
668: */
669: public Method getPropertyWriteMethod(String classname,
670: final String propertyName) {
671: ClassConfiguration config;
672: Method theMethod;
673: while (classname.length() > 0) {
674: config = (ClassConfiguration) configuration_.get(classname);
675: theMethod = config.getPropertyWriteMethod(propertyName);
676: if (theMethod != null) {
677: return theMethod;
678: }
679: classname = config.getExtendedClass();
680: }
681: return null;
682: }
683:
684: /**
685: * Get the method that implements the setter for the given property based upon the class object.
686: * @param clazz The actual class to use as reference
687: * @param functionName The function to find the method for
688: * @return Method
689: */
690: public Method getFunctionMethod(final Class clazz,
691: final String functionName) {
692: final String classname = getClassnameForClass(clazz);
693: return getFunctionMethod(classname, functionName);
694: }
695:
696: /**
697: * Return the method that implements the given function in the class for the given class
698: *
699: * @param classname The name of the class to work with
700: * @param functionName The function to find the method for
701: * @return Method
702: */
703: public Method getFunctionMethod(String classname,
704: final String functionName) {
705: ClassConfiguration config;
706: Method theMethod;
707: while (classname.length() > 0) {
708: config = (ClassConfiguration) configuration_.get(classname);
709: theMethod = config.getFunctionMethod(functionName);
710: if (theMethod != null) {
711: return theMethod;
712: }
713: classname = config.getExtendedClass();
714: }
715: return null;
716: }
717:
718: /**
719: * Check to see if there is an entry for the given property.
720: * @param clazz The class the property is for
721: * @param propertyName The name of the property
722: * @return boolean True if the property exists
723: */
724: public boolean propertyExists(final Class clazz,
725: final String propertyName) {
726: final String classname = getClassnameForClass(clazz);
727: return propertyExists(classname, propertyName);
728: }
729:
730: /**
731: * Check to see if there is an entry for the given property.
732: * @param classname The class the property is for
733: * @param propertyName The name of the property
734: * @return boolean True if the property exists
735: */
736: public boolean propertyExists(final String classname,
737: final String propertyName) {
738: final ClassConfiguration.PropertyInfo info = findPropertyInChain(
739: classname, propertyName);
740: if (info == null) {
741: return false;
742: }
743: return true;
744: }
745:
746: /**
747: * Return the classname that the given class implements. If the class is
748: * the input class, then the name is extracted from the type that the Input class
749: * is masquerading as.
750: * FIXME - Implement the Input class processing
751: * @param clazz
752: * @return the classname
753: */
754: private String getClassnameForClass(final Class clazz) {
755: final String name = (String) ClassnameMap_.get(clazz.getName());
756: if (name == null) {
757: throw new IllegalStateException(
758: "Did not find the mapping of the class to the classname for "
759: + clazz.getName());
760: }
761: return name;
762: }
763:
764: /**
765: * Return an immutable map containing the html to javascript mappings. Keys are
766: * java classes for the various html classes (ie HtmlInput.class) and the values
767: * are the javascript class names (ie "Anchor").
768: * @return the mappings
769: */
770: public static synchronized Map getHtmlJavaScriptMapping() {
771: if (HtmlJavaScriptMap_ != null) {
772: return HtmlJavaScriptMap_;
773: }
774: final JavaScriptConfiguration configuration = JavaScriptConfiguration
775: .getAllEntries();
776:
777: final Iterator it = configuration.keyIterator();
778: final Map map = new HashMap();
779:
780: while (it.hasNext()) {
781: String jsClassname = (String) it.next();
782: ClassConfiguration classConfig = configuration
783: .getClassConfiguration(jsClassname);
784: final String htmlClassname = classConfig.getHtmlClassname();
785: if (htmlClassname != null) {
786: try {
787: final Class htmlClass = Class
788: .forName(htmlClassname);
789: // preload and validate that the class exists
790: getLog().debug(
791: "Mapping " + htmlClass.getName() + " to "
792: + jsClassname);
793: while (!classConfig.isJsObject()) {
794: jsClassname = classConfig.getExtendedClass();
795: classConfig = configuration
796: .getClassConfiguration(jsClassname);
797: }
798: map.put(htmlClass, classConfig.getLinkedClass());
799: } catch (final ClassNotFoundException e) {
800: throw new NoClassDefFoundError(e.getMessage());
801: }
802: }
803: }
804: HtmlJavaScriptMap_ = Collections.unmodifiableMap(map);
805: return HtmlJavaScriptMap_;
806: }
807: }
|