
com.sun.xml.tree.SimpleElementFactory Maven / Gradle / Ivy
/*
* $Id: SimpleElementFactory.java,v 1.2 1999/04/04 18:49:08 db Exp $
*
* Copyright (c) 1998-1999 Sun Microsystems, Inc. All Rights Reserved.
*
* This software is the confidential and proprietary information of Sun
* Microsystems, Inc. ("Confidential Information"). You shall not
* disclose such Confidential Information and shall use it only in
* accordance with the terms of the license agreement you entered into
* with Sun.
*
* SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
* SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
* SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
* THIS SOFTWARE OR ITS DERIVATIVES.
*/
package com.sun.xml.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 *Element, 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 ElementNode class.
* Erroneous mappings are fatal errors.
*
* A suggested XML syntax for recording these bindings, which may
* in the future be explicitly supported, is:
* <bindings xmlns="...">
* <!-- first, bindings for the "default" namespace -->
* <binding tag="..." class="..."/>
* <binding tag="*Element" class="..."/>
* ...
*
* <!-- then bindings for other namespaces -->
* <namespace uri="...">
* <binding tag="..." class="..."/>
* ...
* </namespace>
*
* <!-- can specify JAR files for namespaces -->
* <namespace uri="..." jar="...">
* <binding tag="..." class="..."/>
* ...
* </namespace>
* ...
* </bindings>
*
*
* Note that while most URIs used to identify namespaces will be URLs,
* such as http://www.example.com/xml/purchasing, some may also
* be URNs like urn:uuid:221ffe10-ae3c-11d1-b66c-00805f8a2676.
* You can't assume that the URIs are associated with web-accessible data;
* they must be treated as being no more than distinguishable strings.
*
*
Applications classes configuring an element factory will need to
* provide their own class loader (this.class.getClassLoader
)
* 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 loader) 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 loader) 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)
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 ();
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 tag element tag, without any embedded colon
*/
public ElementEx createElementEx (String namespace, String tag)
{
Dictionary mapping = null;
if (namespace == null)
namespace = defaultNs;
if (nsMappings != null)
mapping = (Dictionary) nsMappings.get (namespace);
if (mapping == null)
return doMap (tag, defaultMapping, defaultLoader);
else
return doMap (tag, 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);
}
}