- /*
- * $Id: SimpleElementFactory.java,v 1.2 2001/04/20 00:52:21 edwingo Exp $
- *
- * The Apache Software License, Version 1.1
- *
- *
- * Copyright (c) 2000 The Apache Software Foundation. All rights
- * reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- *
- * 3. The end-user documentation included with the redistribution,
- * if any, must include the following acknowledgment:
- * "This product includes software developed by the
- * Apache Software Foundation (http://www.apache.org/)."
- * Alternately, this acknowledgment may appear in the software itself,
- * if and wherever such third-party acknowledgments normally appear.
- *
- * 4. The names "Crimson" and "Apache Software Foundation" must
- * not be used to endorse or promote products derived from this
- * software without prior written permission. For written
- * permission, please contact apache@apache.org.
- *
- * 5. Products derived from this software may not be called "Apache",
- * nor may "Apache" appear in their name, without prior written
- * permission of the Apache Software Foundation.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
- * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation and was
- * originally based on software copyright (c) 1999, Sun Microsystems, Inc.,
- * http://www.sun.com. For more information on the Apache Software
- * Foundation, please see <http://www.apache.org/>.
- */
-
- package org.apache.crimson.tree;
-
- import java.util.Dictionary;
- import java.util.Hashtable;
- import java.util.Locale;
-
- import org.w3c.dom.*;
-
-
- /**
- * This is a convenience class for creating application-specific elements
- * associated with specified (or default) XML namespaces. It maintains
- * tables mapping element tag names to classes, and uses them as needed
- * to instantiate classes. The string <em>*Element</em>, which is not a
- * legal XML element name, may be used to map otherwise unrecognized tags
- * to a particular class. If this factory is not configured, then all
- * mappings are to the <a href=ElementNode.html>ElementNode</a> class.
- * Erroneous mappings are fatal errors.
- *
- * <P> A suggested XML syntax for recording these bindings, which may
- * in the future be explicitly supported, is: <PRE>
- * <b><bindings xmlns="..."></b>
- * <!-- first, bindings for the "default" namespace -->
- * <b><binding tag="..." class="..."/></b>
- * <binding <b>tag="*Element"</b> class="..."/>
- * ...
- *
- * <!-- then bindings for other namespaces -->
- * <b><namespace uri="..."></b>
- * <binding tag="..." class="..."/>
- * ...
- * <b></namespace></b>
- *
- * <!-- can specify JAR files for namespaces -->
- * <namespace uri="..." <b>jar="..."</b>>
- * <binding tag="..." class="..."/>
- * ...
- * </namespace>
- * ...
- * <b></bindings></b>
- * </PRE>
- *
- * <P> Note that while most URIs used to identify namespaces will be URLs,
- * such as <em>http://www.example.com/xml/purchasing</em>, some may also
- * be URNs like <em>urn:uuid:221ffe10-ae3c-11d1-b66c-00805f8a2676</em>.
- * You can't assume that the URIs are associated with web-accessible data;
- * they must be treated as being no more than distinguishable strings.
- *
- * <P> Applications classes configuring an element factory will need to
- * provide their own class loader (<code>this.class.getClassLoader</code>)
- * to get the desired behavior in common cases. Classes loaded via some
- * URL will similarly need to use a network class loader.
- *
- * @author David Brownell
- * @version $Revision: 1.2 $
- */
- public class SimpleElementFactory implements ElementFactory
- {
- // in the absense of a mapping tied to namespace URI, use these
- private Dictionary defaultMapping;
- private ClassLoader defaultLoader;
-
- private String defaultNs;
-
- // these hold mappings tied to namespace URIs
- private Dictionary nsMappings;
- private Dictionary nsLoaders;
- private Locale locale = Locale.getDefault ();
-
-
- /**
- * Constructs an unconfigured element factory.
- */
- public SimpleElementFactory () { }
-
- /**
- * Records a default element name to namespace mapping, for use
- * by namespace-unaware DOM construction and when a specific
- * namespace mapping is not available.
- *
- * @param dict Keys are element names, and values are either class
- * names (interpreted with respect to <em>loader</em>) or class
- * objects. This value may not be null, and the dictionary is
- * retained and modified by the factory.
- * @param loader If non-null, this is used instead of the bootstrap
- * class loader when mapping from class names to class objects.
- */
- public void addMapping (Dictionary dict, ClassLoader loader)
- {
- if (dict == null)
- throw new IllegalArgumentException ();
- defaultMapping = dict;
- defaultLoader = loader;
- }
-
- /**
- * Records a namespace-specific mapping between element names and
- * classes.
- *
- * @param namespace A URI identifying the namespace for which the
- * mapping is defined
- * @param dict Keys are element names, and values are either class
- * names (interpreted with respect to <em>loader</em>) or class
- * objects. This value may not be null, and the dictionary is
- * retained and modified by the factory.
- * @param loader If non-null, this is used instead of the bootstrap
- * class loader when mapping from class names to class objects.
- */
- public void addMapping (
- String namespace,
- Dictionary dict,
- ClassLoader loader
- ) {
- if (namespace == null || dict == null)
- throw new IllegalArgumentException ();
- if (nsMappings == null) {
- nsMappings = new Hashtable ();
- nsLoaders = new Hashtable ();
- }
- nsMappings.put (namespace, dict);
- if (loader != null)
- nsLoaders.put (namespace, loader);
- }
-
- /**
- * Defines a URI to be treated as the "default" namespace. This
- * is used only when choosing element classes, and may not be
- * visible when instances are asked for their namespaces.
- */
- public void setDefaultNamespace (String ns)
- { defaultNs = ns; }
-
- private Class map2Class (
- String key,
- Dictionary node2class,
- ClassLoader loader
- )
- {
- Object mapResult = node2class.get (key);
-
- if (mapResult instanceof Class)
- return (Class) mapResult;
- if (mapResult == null)
- return null;
-
- if (mapResult instanceof String) {
- String className = (String) mapResult;
- Class retval;
-
- try {
- if (loader == null) {
- // Find the appropriate ClassLoader to use depending on
- // if we are part of the JDK or not and taking JDK
- // version into account.
- loader = findClassLoader();
- }
- if (loader == null)
- retval = Class.forName (className);
- else
- retval = loader.loadClass (className);
-
- //
- // We really have no option here. DOM requires two
- // bidirectional relationships (parent/child, and
- // between siblings) and one unidirectional one (nodes
- // belong to one document) but doesn't provide APIs that
- // would suffice to maintain them. So those APIs
- // must rely on knowledge of the implementation to
- // which things are connecting.
- //
- if (!ElementNode.class.isAssignableFrom (retval))
- throw new IllegalArgumentException (getMessage ("SEF-000",
- new Object [] { key, className }));
- node2class.put (key, retval);
- return retval;
-
- } catch (ClassNotFoundException e) {
- throw new IllegalArgumentException (getMessage ("SEF-001",
- new Object [] { key, className, e.getMessage ()}));
- }
- }
-
- // another option: clone elements, resetting parent
- // and document associations?
-
- throw new IllegalArgumentException (getMessage ("SEF-002",
- new Object [] { key }));
- }
-
- private ElementNode doMap (
- String tagName,
- Dictionary node2class,
- ClassLoader loader
- ) {
- Class theClass;
- ElementNode retval;
-
- theClass = map2Class (tagName, node2class, loader);
- if (theClass == null)
- theClass = map2Class ("*Element", node2class, loader);
- if (theClass == null)
- retval = new ElementNode (tagName);
- else {
- try {
- retval = (ElementNode) theClass.newInstance ();
- } catch (Exception e) {
- //InstantiationException
- //IllegalAccessException
- throw new IllegalArgumentException (getMessage ("SEF-003",
- new Object [] {tagName, theClass.getName (),
- e.getMessage () }));
- }
- }
- return retval;
- }
-
- /**
- * Creates an element by using the mapping associated with the
- * specified namespace, or the default namespace as appropriate.
- * If no mapping associated with that namespace is defined, then
- * the default mapping is used.
- *
- * @param namespace URI for namespace; null indicates use of
- * the default namespace, if any.
- * @param localName element tag, without any embedded colon
- */
- public ElementEx createElementEx (String namespace, String localName)
- {
- Dictionary mapping = null;
-
- if (namespace == null)
- namespace = defaultNs;
-
- if (nsMappings != null)
- mapping = (Dictionary) nsMappings.get (namespace);
- if (mapping == null)
- return doMap (localName, defaultMapping, defaultLoader);
- else
- return doMap (localName, mapping,
- (ClassLoader)nsLoaders.get (namespace));
- }
-
- /**
- * Creates an element by using the default mapping.
- *
- * @param tag element tag
- */
- public ElementEx createElementEx (String tag)
- {
- return doMap (tag, defaultMapping, defaultLoader);
- }
-
- /*
- * Gets the messages from the resource bundles for the given messageId.
- */
- String getMessage (String messageId) {
- return getMessage (messageId, null);
- }
-
- /*
- * Gets the messages from the resource bundles for the given messageId
- * after formatting it with the parameters passed to it.
- */
- //XXX use the default locale only at this point.
- String getMessage (String messageId, Object[] parameters) {
- return XmlDocument.catalog.getMessage (locale, messageId, parameters);
- }
-
-
- /*
- * The following section of code tries to get our ContextClassLoader,
- * if we are running in Java 2. This code is derived from
- * javax.xml.parsers.FactoryFinder.
- */
-
- /**
- * Figure out which ClassLoader to use. For JDK 1.2 and later use the
- * context ClassLoader if possible. Note: we defer linking the class
- * that calls an API only in JDK 1.2 until runtime so that we can catch
- * LinkageError so that this code will run in older non-Sun JVMs such
- * as the Microsoft JVM in IE.
- */
- private static ClassLoader findClassLoader()
- {
- ClassLoader classLoader;
- try {
- // Construct the name of the concrete class to instantiate
- Class clazz = Class.forName(SimpleElementFactory.class.getName()
- + "$ClassLoaderFinderConcrete");
- ClassLoaderFinder clf = (ClassLoaderFinder) clazz.newInstance();
- classLoader = clf.getContextClassLoader();
- } catch (LinkageError le) {
- // Assume that we are running JDK 1.1, use the current ClassLoader
- classLoader = SimpleElementFactory.class.getClassLoader();
- } catch (ClassNotFoundException x) {
- // This case should not normally happen. MS IE can throw this
- // instead of a LinkageError the second time Class.forName() is
- // called so assume that we are running JDK 1.1 and use the
- // current ClassLoader
- classLoader = SimpleElementFactory.class.getClassLoader();
- } catch (Exception x) {
- // Something abnormal happened so just use current ClassLoader
- classLoader = SimpleElementFactory.class.getClassLoader();
- }
- return classLoader;
- }
-
- /*
- * The following nested classes allow getContextClassLoader() to be
- * called only on JDK 1.2 and yet run in older JDK 1.1 JVMs
- */
-
- private static abstract class ClassLoaderFinder {
- abstract ClassLoader getContextClassLoader();
- }
-
- static class ClassLoaderFinderConcrete extends ClassLoaderFinder {
- ClassLoader getContextClassLoader() {
- return Thread.currentThread().getContextClassLoader();
- }
- }
- }