1. /* $Id: FactoryCreateRule.java,v 1.21.2.1 2004/07/30 20:11:01 rdonkin Exp $
  2. *
  3. * Copyright 2001-2004 The Apache Software Foundation.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. package org.apache.commons.digester;
  18. import org.xml.sax.Attributes;
  19. import org.apache.commons.collections.ArrayStack;
  20. /**
  21. * <p>Rule implementation that uses an {@link ObjectCreationFactory} to create
  22. * a new object which it pushes onto the object stack. When the element is
  23. * complete, the object will be popped.</p>
  24. *
  25. * <p>This rule is intended in situations where the element's attributes are
  26. * needed before the object can be created. A common senario is for the
  27. * ObjectCreationFactory implementation to use the attributes as parameters
  28. * in a call to either a factory method or to a non-empty constructor.
  29. */
  30. public class FactoryCreateRule extends Rule {
  31. // ----------------------------------------------------------- Fields
  32. /** Should exceptions thrown by the factory be ignored? */
  33. private boolean ignoreCreateExceptions;
  34. /** Stock to manage */
  35. private ArrayStack exceptionIgnoredStack;
  36. // ----------------------------------------------------------- Constructors
  37. /**
  38. * Construct a factory create rule that will use the specified
  39. * class name to create an {@link ObjectCreationFactory} which will
  40. * then be used to create an object and push it on the stack.
  41. *
  42. * @param digester The associated Digester
  43. * @param className Java class name of the object creation factory class
  44. *
  45. * @deprecated The digester instance is now set in the {@link Digester#addRule} method.
  46. * Use {@link #FactoryCreateRule(String className)} instead.
  47. */
  48. public FactoryCreateRule(Digester digester, String className) {
  49. this(className);
  50. }
  51. /**
  52. * Construct a factory create rule that will use the specified
  53. * class to create an {@link ObjectCreationFactory} which will
  54. * then be used to create an object and push it on the stack.
  55. *
  56. * @param digester The associated Digester
  57. * @param clazz Java class name of the object creation factory class
  58. *
  59. * @deprecated The digester instance is now set in the {@link Digester#addRule} method.
  60. * Use {@link #FactoryCreateRule(Class clazz)} instead.
  61. */
  62. public FactoryCreateRule(Digester digester, Class clazz) {
  63. this(clazz);
  64. }
  65. /**
  66. * Construct a factory create rule that will use the specified
  67. * class name (possibly overridden by the specified attribute if present)
  68. * to create an {@link ObjectCreationFactory}, which will then be used
  69. * to instantiate an object instance and push it onto the stack.
  70. *
  71. * @param digester The associated Digester
  72. * @param className Default Java class name of the factory class
  73. * @param attributeName Attribute name which, if present, contains an
  74. * override of the class name of the object creation factory to create.
  75. *
  76. * @deprecated The digester instance is now set in the {@link Digester#addRule} method.
  77. * Use {@link #FactoryCreateRule(String className, String attributeName)} instead.
  78. */
  79. public FactoryCreateRule(Digester digester,
  80. String className, String attributeName) {
  81. this(className, attributeName);
  82. }
  83. /**
  84. * Construct a factory create rule that will use the specified
  85. * class (possibly overridden by the specified attribute if present)
  86. * to create an {@link ObjectCreationFactory}, which will then be used
  87. * to instantiate an object instance and push it onto the stack.
  88. *
  89. * @param digester The associated Digester
  90. * @param clazz Default Java class name of the factory class
  91. * @param attributeName Attribute name which, if present, contains an
  92. * override of the class name of the object creation factory to create.
  93. *
  94. * @deprecated The digester instance is now set in the {@link Digester#addRule} method.
  95. * Use {@link #FactoryCreateRule(Class clazz, String attributeName)} instead.
  96. */
  97. public FactoryCreateRule(Digester digester,
  98. Class clazz, String attributeName) {
  99. this(clazz, attributeName);
  100. }
  101. /**
  102. * Construct a factory create rule using the given, already instantiated,
  103. * {@link ObjectCreationFactory}.
  104. *
  105. * @param digester The associated Digester
  106. * @param creationFactory called on to create the object.
  107. *
  108. * @deprecated The digester instance is now set in the {@link Digester#addRule} method.
  109. * Use {@link #FactoryCreateRule(ObjectCreationFactory creationFactory)} instead.
  110. */
  111. public FactoryCreateRule(Digester digester,
  112. ObjectCreationFactory creationFactory) {
  113. this(creationFactory);
  114. }
  115. /**
  116. * <p>Construct a factory create rule that will use the specified
  117. * class name to create an {@link ObjectCreationFactory} which will
  118. * then be used to create an object and push it on the stack.</p>
  119. *
  120. * <p>Exceptions thrown during the object creation process will be propagated.</p>
  121. *
  122. * @param className Java class name of the object creation factory class
  123. */
  124. public FactoryCreateRule(String className) {
  125. this(className, false);
  126. }
  127. /**
  128. * <p>Construct a factory create rule that will use the specified
  129. * class to create an {@link ObjectCreationFactory} which will
  130. * then be used to create an object and push it on the stack.</p>
  131. *
  132. * <p>Exceptions thrown during the object creation process will be propagated.</p>
  133. *
  134. * @param clazz Java class name of the object creation factory class
  135. */
  136. public FactoryCreateRule(Class clazz) {
  137. this(clazz, false);
  138. }
  139. /**
  140. * <p>Construct a factory create rule that will use the specified
  141. * class name (possibly overridden by the specified attribute if present)
  142. * to create an {@link ObjectCreationFactory}, which will then be used
  143. * to instantiate an object instance and push it onto the stack.</p>
  144. *
  145. * <p>Exceptions thrown during the object creation process will be propagated.</p>
  146. *
  147. * @param className Default Java class name of the factory class
  148. * @param attributeName Attribute name which, if present, contains an
  149. * override of the class name of the object creation factory to create.
  150. */
  151. public FactoryCreateRule(String className, String attributeName) {
  152. this(className, attributeName, false);
  153. }
  154. /**
  155. * <p>Construct a factory create rule that will use the specified
  156. * class (possibly overridden by the specified attribute if present)
  157. * to create an {@link ObjectCreationFactory}, which will then be used
  158. * to instantiate an object instance and push it onto the stack.</p>
  159. *
  160. * <p>Exceptions thrown during the object creation process will be propagated.</p>
  161. *
  162. * @param clazz Default Java class name of the factory class
  163. * @param attributeName Attribute name which, if present, contains an
  164. * override of the class name of the object creation factory to create.
  165. */
  166. public FactoryCreateRule(Class clazz, String attributeName) {
  167. this(clazz, attributeName, false);
  168. }
  169. /**
  170. * <p>Construct a factory create rule using the given, already instantiated,
  171. * {@link ObjectCreationFactory}.</p>
  172. *
  173. * <p>Exceptions thrown during the object creation process will be propagated.</p>
  174. *
  175. * @param creationFactory called on to create the object.
  176. */
  177. public FactoryCreateRule(ObjectCreationFactory creationFactory) {
  178. this(creationFactory, false);
  179. }
  180. /**
  181. * Construct a factory create rule that will use the specified
  182. * class name to create an {@link ObjectCreationFactory} which will
  183. * then be used to create an object and push it on the stack.
  184. *
  185. * @param className Java class name of the object creation factory class
  186. * @param ignoreCreateExceptions if true, exceptions thrown by the object
  187. * creation factory
  188. * will be ignored.
  189. */
  190. public FactoryCreateRule(String className, boolean ignoreCreateExceptions) {
  191. this(className, null, ignoreCreateExceptions);
  192. }
  193. /**
  194. * Construct a factory create rule that will use the specified
  195. * class to create an {@link ObjectCreationFactory} which will
  196. * then be used to create an object and push it on the stack.
  197. *
  198. * @param clazz Java class name of the object creation factory class
  199. * @param ignoreCreateExceptions if true, exceptions thrown by the
  200. * object creation factory
  201. * will be ignored.
  202. */
  203. public FactoryCreateRule(Class clazz, boolean ignoreCreateExceptions) {
  204. this(clazz, null, ignoreCreateExceptions);
  205. }
  206. /**
  207. * Construct a factory create rule that will use the specified
  208. * class name (possibly overridden by the specified attribute if present)
  209. * to create an {@link ObjectCreationFactory}, which will then be used
  210. * to instantiate an object instance and push it onto the stack.
  211. *
  212. * @param className Default Java class name of the factory class
  213. * @param attributeName Attribute name which, if present, contains an
  214. * override of the class name of the object creation factory to create.
  215. * @param ignoreCreateExceptions if true, exceptions thrown by the object
  216. * creation factory will be ignored.
  217. */
  218. public FactoryCreateRule(
  219. String className,
  220. String attributeName,
  221. boolean ignoreCreateExceptions) {
  222. this.className = className;
  223. this.attributeName = attributeName;
  224. this.ignoreCreateExceptions = ignoreCreateExceptions;
  225. }
  226. /**
  227. * Construct a factory create rule that will use the specified
  228. * class (possibly overridden by the specified attribute if present)
  229. * to create an {@link ObjectCreationFactory}, which will then be used
  230. * to instantiate an object instance and push it onto the stack.
  231. *
  232. * @param clazz Default Java class name of the factory class
  233. * @param attributeName Attribute name which, if present, contains an
  234. * override of the class name of the object creation factory to create.
  235. * @param ignoreCreateExceptions if true, exceptions thrown by the object
  236. * creation factory will be ignored.
  237. */
  238. public FactoryCreateRule(
  239. Class clazz,
  240. String attributeName,
  241. boolean ignoreCreateExceptions) {
  242. this(clazz.getName(), attributeName, ignoreCreateExceptions);
  243. }
  244. /**
  245. * Construct a factory create rule using the given, already instantiated,
  246. * {@link ObjectCreationFactory}.
  247. *
  248. * @param creationFactory called on to create the object.
  249. * @param ignoreCreateExceptions if true, exceptions thrown by the object
  250. * creation factory will be ignored.
  251. */
  252. public FactoryCreateRule(
  253. ObjectCreationFactory creationFactory,
  254. boolean ignoreCreateExceptions) {
  255. this.creationFactory = creationFactory;
  256. this.ignoreCreateExceptions = ignoreCreateExceptions;
  257. }
  258. // ----------------------------------------------------- Instance Variables
  259. /**
  260. * The attribute containing an override class name if it is present.
  261. */
  262. protected String attributeName = null;
  263. /**
  264. * The Java class name of the ObjectCreationFactory to be created.
  265. * This class must have a no-arguments constructor.
  266. */
  267. protected String className = null;
  268. /**
  269. * The object creation factory we will use to instantiate objects
  270. * as required based on the attributes specified in the matched XML
  271. * element.
  272. */
  273. protected ObjectCreationFactory creationFactory = null;
  274. // --------------------------------------------------------- Public Methods
  275. /**
  276. * Process the beginning of this element.
  277. *
  278. * @param attributes The attribute list of this element
  279. */
  280. public void begin(String namespace, String name, Attributes attributes) throws Exception {
  281. if (ignoreCreateExceptions) {
  282. if (exceptionIgnoredStack == null) {
  283. exceptionIgnoredStack = new ArrayStack();
  284. }
  285. try {
  286. Object instance = getFactory(attributes).createObject(attributes);
  287. if (digester.log.isDebugEnabled()) {
  288. digester.log.debug("[FactoryCreateRule]{" + digester.match +
  289. "} New " + instance.getClass().getName());
  290. }
  291. digester.push(instance);
  292. exceptionIgnoredStack.push(Boolean.FALSE);
  293. } catch (Exception e) {
  294. // log message and error
  295. if (digester.log.isInfoEnabled()) {
  296. digester.log.info("[FactoryCreateRule] Create exception ignored: " +
  297. ((e.getMessage() == null) ? e.getClass().getName() : e.getMessage()));
  298. if (digester.log.isDebugEnabled()) {
  299. digester.log.debug("[FactoryCreateRule] Ignored exception:", e);
  300. }
  301. }
  302. exceptionIgnoredStack.push(Boolean.TRUE);
  303. }
  304. } else {
  305. Object instance = getFactory(attributes).createObject(attributes);
  306. if (digester.log.isDebugEnabled()) {
  307. digester.log.debug("[FactoryCreateRule]{" + digester.match +
  308. "} New " + instance.getClass().getName());
  309. }
  310. digester.push(instance);
  311. }
  312. }
  313. /**
  314. * Process the end of this element.
  315. */
  316. public void end(String namespace, String name) throws Exception {
  317. // check if object was created
  318. // this only happens if an exception was thrown and we're ignoring them
  319. if (
  320. ignoreCreateExceptions &&
  321. exceptionIgnoredStack != null &&
  322. !(exceptionIgnoredStack.empty())) {
  323. if (((Boolean) exceptionIgnoredStack.pop()).booleanValue()) {
  324. // creation exception was ignored
  325. // nothing was put onto the stack
  326. if (digester.log.isTraceEnabled()) {
  327. digester.log.trace("[FactoryCreateRule] No creation so no push so no pop");
  328. }
  329. return;
  330. }
  331. }
  332. Object top = digester.pop();
  333. if (digester.log.isDebugEnabled()) {
  334. digester.log.debug("[FactoryCreateRule]{" + digester.match +
  335. "} Pop " + top.getClass().getName());
  336. }
  337. }
  338. /**
  339. * Clean up after parsing is complete.
  340. */
  341. public void finish() throws Exception {
  342. if (attributeName != null) {
  343. creationFactory = null;
  344. }
  345. }
  346. /**
  347. * Render a printable version of this Rule.
  348. */
  349. public String toString() {
  350. StringBuffer sb = new StringBuffer("FactoryCreateRule[");
  351. sb.append("className=");
  352. sb.append(className);
  353. sb.append(", attributeName=");
  354. sb.append(attributeName);
  355. if (creationFactory != null) {
  356. sb.append(", creationFactory=");
  357. sb.append(creationFactory);
  358. }
  359. sb.append("]");
  360. return (sb.toString());
  361. }
  362. // ------------------------------------------------------ Protected Methods
  363. /**
  364. * Return an instance of our associated object creation factory,
  365. * creating one if necessary.
  366. *
  367. * @param attributes Attributes passed to our factory creation element
  368. *
  369. * @exception Exception if any error occurs
  370. */
  371. protected ObjectCreationFactory getFactory(Attributes attributes)
  372. throws Exception {
  373. if (creationFactory == null) {
  374. String realClassName = className;
  375. if (attributeName != null) {
  376. String value = attributes.getValue(attributeName);
  377. if (value != null) {
  378. realClassName = value;
  379. }
  380. }
  381. if (digester.log.isDebugEnabled()) {
  382. digester.log.debug("[FactoryCreateRule]{" + digester.match +
  383. "} New factory " + realClassName);
  384. }
  385. Class clazz = digester.getClassLoader().loadClass(realClassName);
  386. creationFactory = (ObjectCreationFactory)
  387. clazz.newInstance();
  388. creationFactory.setDigester(digester);
  389. }
  390. return (creationFactory);
  391. }
  392. }