1. /*
  2. * $Id: SimpleElementFactory.java,v 1.2 2001/04/20 00:52:21 edwingo Exp $
  3. *
  4. * The Apache Software License, Version 1.1
  5. *
  6. *
  7. * Copyright (c) 2000 The Apache Software Foundation. All rights
  8. * reserved.
  9. *
  10. * Redistribution and use in source and binary forms, with or without
  11. * modification, are permitted provided that the following conditions
  12. * are met:
  13. *
  14. * 1. Redistributions of source code must retain the above copyright
  15. * notice, this list of conditions and the following disclaimer.
  16. *
  17. * 2. Redistributions in binary form must reproduce the above copyright
  18. * notice, this list of conditions and the following disclaimer in
  19. * the documentation and/or other materials provided with the
  20. * distribution.
  21. *
  22. * 3. The end-user documentation included with the redistribution,
  23. * if any, must include the following acknowledgment:
  24. * "This product includes software developed by the
  25. * Apache Software Foundation (http://www.apache.org/)."
  26. * Alternately, this acknowledgment may appear in the software itself,
  27. * if and wherever such third-party acknowledgments normally appear.
  28. *
  29. * 4. The names "Crimson" and "Apache Software Foundation" must
  30. * not be used to endorse or promote products derived from this
  31. * software without prior written permission. For written
  32. * permission, please contact apache@apache.org.
  33. *
  34. * 5. Products derived from this software may not be called "Apache",
  35. * nor may "Apache" appear in their name, without prior written
  36. * permission of the Apache Software Foundation.
  37. *
  38. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  39. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  40. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  41. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  42. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  43. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  44. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  45. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  46. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  47. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  48. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  49. * SUCH DAMAGE.
  50. * ====================================================================
  51. *
  52. * This software consists of voluntary contributions made by many
  53. * individuals on behalf of the Apache Software Foundation and was
  54. * originally based on software copyright (c) 1999, Sun Microsystems, Inc.,
  55. * http://www.sun.com. For more information on the Apache Software
  56. * Foundation, please see <http://www.apache.org/>.
  57. */
  58. package org.apache.crimson.tree;
  59. import java.util.Dictionary;
  60. import java.util.Hashtable;
  61. import java.util.Locale;
  62. import org.w3c.dom.*;
  63. /**
  64. * This is a convenience class for creating application-specific elements
  65. * associated with specified (or default) XML namespaces. It maintains
  66. * tables mapping element tag names to classes, and uses them as needed
  67. * to instantiate classes. The string <em>*Element</em>, which is not a
  68. * legal XML element name, may be used to map otherwise unrecognized tags
  69. * to a particular class. If this factory is not configured, then all
  70. * mappings are to the <a href=ElementNode.html>ElementNode</a> class.
  71. * Erroneous mappings are fatal errors.
  72. *
  73. * <P> A suggested XML syntax for recording these bindings, which may
  74. * in the future be explicitly supported, is: <PRE>
  75. * <b><bindings xmlns="..."></b>
  76. * <!-- first, bindings for the "default" namespace -->
  77. * <b><binding tag="..." class="..."/></b>
  78. * <binding <b>tag="*Element"</b> class="..."/>
  79. * ...
  80. *
  81. * <!-- then bindings for other namespaces -->
  82. * <b><namespace uri="..."></b>
  83. * <binding tag="..." class="..."/>
  84. * ...
  85. * <b></namespace></b>
  86. *
  87. * <!-- can specify JAR files for namespaces -->
  88. * <namespace uri="..." <b>jar="..."</b>>
  89. * <binding tag="..." class="..."/>
  90. * ...
  91. * </namespace>
  92. * ...
  93. * <b></bindings></b>
  94. * </PRE>
  95. *
  96. * <P> Note that while most URIs used to identify namespaces will be URLs,
  97. * such as <em>http://www.example.com/xml/purchasing</em>, some may also
  98. * be URNs like <em>urn:uuid:221ffe10-ae3c-11d1-b66c-00805f8a2676</em>.
  99. * You can't assume that the URIs are associated with web-accessible data;
  100. * they must be treated as being no more than distinguishable strings.
  101. *
  102. * <P> Applications classes configuring an element factory will need to
  103. * provide their own class loader (<code>this.class.getClassLoader</code>)
  104. * to get the desired behavior in common cases. Classes loaded via some
  105. * URL will similarly need to use a network class loader.
  106. *
  107. * @author David Brownell
  108. * @version $Revision: 1.2 $
  109. */
  110. public class SimpleElementFactory implements ElementFactory
  111. {
  112. // in the absense of a mapping tied to namespace URI, use these
  113. private Dictionary defaultMapping;
  114. private ClassLoader defaultLoader;
  115. private String defaultNs;
  116. // these hold mappings tied to namespace URIs
  117. private Dictionary nsMappings;
  118. private Dictionary nsLoaders;
  119. private Locale locale = Locale.getDefault ();
  120. /**
  121. * Constructs an unconfigured element factory.
  122. */
  123. public SimpleElementFactory () { }
  124. /**
  125. * Records a default element name to namespace mapping, for use
  126. * by namespace-unaware DOM construction and when a specific
  127. * namespace mapping is not available.
  128. *
  129. * @param dict Keys are element names, and values are either class
  130. * names (interpreted with respect to <em>loader</em>) or class
  131. * objects. This value may not be null, and the dictionary is
  132. * retained and modified by the factory.
  133. * @param loader If non-null, this is used instead of the bootstrap
  134. * class loader when mapping from class names to class objects.
  135. */
  136. public void addMapping (Dictionary dict, ClassLoader loader)
  137. {
  138. if (dict == null)
  139. throw new IllegalArgumentException ();
  140. defaultMapping = dict;
  141. defaultLoader = loader;
  142. }
  143. /**
  144. * Records a namespace-specific mapping between element names and
  145. * classes.
  146. *
  147. * @param namespace A URI identifying the namespace for which the
  148. * mapping is defined
  149. * @param dict Keys are element names, and values are either class
  150. * names (interpreted with respect to <em>loader</em>) or class
  151. * objects. This value may not be null, and the dictionary is
  152. * retained and modified by the factory.
  153. * @param loader If non-null, this is used instead of the bootstrap
  154. * class loader when mapping from class names to class objects.
  155. */
  156. public void addMapping (
  157. String namespace,
  158. Dictionary dict,
  159. ClassLoader loader
  160. ) {
  161. if (namespace == null || dict == null)
  162. throw new IllegalArgumentException ();
  163. if (nsMappings == null) {
  164. nsMappings = new Hashtable ();
  165. nsLoaders = new Hashtable ();
  166. }
  167. nsMappings.put (namespace, dict);
  168. if (loader != null)
  169. nsLoaders.put (namespace, loader);
  170. }
  171. /**
  172. * Defines a URI to be treated as the "default" namespace. This
  173. * is used only when choosing element classes, and may not be
  174. * visible when instances are asked for their namespaces.
  175. */
  176. public void setDefaultNamespace (String ns)
  177. { defaultNs = ns; }
  178. private Class map2Class (
  179. String key,
  180. Dictionary node2class,
  181. ClassLoader loader
  182. )
  183. {
  184. Object mapResult = node2class.get (key);
  185. if (mapResult instanceof Class)
  186. return (Class) mapResult;
  187. if (mapResult == null)
  188. return null;
  189. if (mapResult instanceof String) {
  190. String className = (String) mapResult;
  191. Class retval;
  192. try {
  193. if (loader == null) {
  194. // Find the appropriate ClassLoader to use depending on
  195. // if we are part of the JDK or not and taking JDK
  196. // version into account.
  197. loader = findClassLoader();
  198. }
  199. if (loader == null)
  200. retval = Class.forName (className);
  201. else
  202. retval = loader.loadClass (className);
  203. //
  204. // We really have no option here. DOM requires two
  205. // bidirectional relationships (parent/child, and
  206. // between siblings) and one unidirectional one (nodes
  207. // belong to one document) but doesn't provide APIs that
  208. // would suffice to maintain them. So those APIs
  209. // must rely on knowledge of the implementation to
  210. // which things are connecting.
  211. //
  212. if (!ElementNode.class.isAssignableFrom (retval))
  213. throw new IllegalArgumentException (getMessage ("SEF-000",
  214. new Object [] { key, className }));
  215. node2class.put (key, retval);
  216. return retval;
  217. } catch (ClassNotFoundException e) {
  218. throw new IllegalArgumentException (getMessage ("SEF-001",
  219. new Object [] { key, className, e.getMessage ()}));
  220. }
  221. }
  222. // another option: clone elements, resetting parent
  223. // and document associations?
  224. throw new IllegalArgumentException (getMessage ("SEF-002",
  225. new Object [] { key }));
  226. }
  227. private ElementNode doMap (
  228. String tagName,
  229. Dictionary node2class,
  230. ClassLoader loader
  231. ) {
  232. Class theClass;
  233. ElementNode retval;
  234. theClass = map2Class (tagName, node2class, loader);
  235. if (theClass == null)
  236. theClass = map2Class ("*Element", node2class, loader);
  237. if (theClass == null)
  238. retval = new ElementNode (tagName);
  239. else {
  240. try {
  241. retval = (ElementNode) theClass.newInstance ();
  242. } catch (Exception e) {
  243. //InstantiationException
  244. //IllegalAccessException
  245. throw new IllegalArgumentException (getMessage ("SEF-003",
  246. new Object [] {tagName, theClass.getName (),
  247. e.getMessage () }));
  248. }
  249. }
  250. return retval;
  251. }
  252. /**
  253. * Creates an element by using the mapping associated with the
  254. * specified namespace, or the default namespace as appropriate.
  255. * If no mapping associated with that namespace is defined, then
  256. * the default mapping is used.
  257. *
  258. * @param namespace URI for namespace; null indicates use of
  259. * the default namespace, if any.
  260. * @param localName element tag, without any embedded colon
  261. */
  262. public ElementEx createElementEx (String namespace, String localName)
  263. {
  264. Dictionary mapping = null;
  265. if (namespace == null)
  266. namespace = defaultNs;
  267. if (nsMappings != null)
  268. mapping = (Dictionary) nsMappings.get (namespace);
  269. if (mapping == null)
  270. return doMap (localName, defaultMapping, defaultLoader);
  271. else
  272. return doMap (localName, mapping,
  273. (ClassLoader)nsLoaders.get (namespace));
  274. }
  275. /**
  276. * Creates an element by using the default mapping.
  277. *
  278. * @param tag element tag
  279. */
  280. public ElementEx createElementEx (String tag)
  281. {
  282. return doMap (tag, defaultMapping, defaultLoader);
  283. }
  284. /*
  285. * Gets the messages from the resource bundles for the given messageId.
  286. */
  287. String getMessage (String messageId) {
  288. return getMessage (messageId, null);
  289. }
  290. /*
  291. * Gets the messages from the resource bundles for the given messageId
  292. * after formatting it with the parameters passed to it.
  293. */
  294. //XXX use the default locale only at this point.
  295. String getMessage (String messageId, Object[] parameters) {
  296. return XmlDocument.catalog.getMessage (locale, messageId, parameters);
  297. }
  298. /*
  299. * The following section of code tries to get our ContextClassLoader,
  300. * if we are running in Java 2. This code is derived from
  301. * javax.xml.parsers.FactoryFinder.
  302. */
  303. /**
  304. * Figure out which ClassLoader to use. For JDK 1.2 and later use the
  305. * context ClassLoader if possible. Note: we defer linking the class
  306. * that calls an API only in JDK 1.2 until runtime so that we can catch
  307. * LinkageError so that this code will run in older non-Sun JVMs such
  308. * as the Microsoft JVM in IE.
  309. */
  310. private static ClassLoader findClassLoader()
  311. {
  312. ClassLoader classLoader;
  313. try {
  314. // Construct the name of the concrete class to instantiate
  315. Class clazz = Class.forName(SimpleElementFactory.class.getName()
  316. + "$ClassLoaderFinderConcrete");
  317. ClassLoaderFinder clf = (ClassLoaderFinder) clazz.newInstance();
  318. classLoader = clf.getContextClassLoader();
  319. } catch (LinkageError le) {
  320. // Assume that we are running JDK 1.1, use the current ClassLoader
  321. classLoader = SimpleElementFactory.class.getClassLoader();
  322. } catch (ClassNotFoundException x) {
  323. // This case should not normally happen. MS IE can throw this
  324. // instead of a LinkageError the second time Class.forName() is
  325. // called so assume that we are running JDK 1.1 and use the
  326. // current ClassLoader
  327. classLoader = SimpleElementFactory.class.getClassLoader();
  328. } catch (Exception x) {
  329. // Something abnormal happened so just use current ClassLoader
  330. classLoader = SimpleElementFactory.class.getClassLoader();
  331. }
  332. return classLoader;
  333. }
  334. /*
  335. * The following nested classes allow getContextClassLoader() to be
  336. * called only on JDK 1.2 and yet run in older JDK 1.1 JVMs
  337. */
  338. private static abstract class ClassLoaderFinder {
  339. abstract ClassLoader getContextClassLoader();
  340. }
  341. static class ClassLoaderFinderConcrete extends ClassLoaderFinder {
  342. ClassLoader getContextClassLoader() {
  343. return Thread.currentThread().getContextClassLoader();
  344. }
  345. }
  346. }