001    /* $Id: FactoryCreateRule.java 729242 2008-12-24 06:10:07Z rahul $
002     *
003     * Licensed to the Apache Software Foundation (ASF) under one or more
004     * contributor license agreements.  See the NOTICE file distributed with
005     * this work for additional information regarding copyright ownership.
006     * The ASF licenses this file to You under the Apache License, Version 2.0
007     * (the "License"); you may not use this file except in compliance with
008     * the License.  You may obtain a copy of the License at
009     * 
010     *      http://www.apache.org/licenses/LICENSE-2.0
011     * 
012     * Unless required by applicable law or agreed to in writing, software
013     * distributed under the License is distributed on an "AS IS" BASIS,
014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */ 
018    
019    
020    package org.apache.commons.digester;
021    
022    import java.util.Stack;
023    
024    import org.xml.sax.Attributes;
025    
026    
027    /**
028     * <p>Rule implementation that uses an {@link ObjectCreationFactory} to create
029     * a new object which it pushes onto the object stack.  When the element is
030     * complete, the object will be popped.</p>
031     *
032     * <p>This rule is intended in situations where the element's attributes are
033     * needed before the object can be created.  A common senario is for the
034     * ObjectCreationFactory implementation to use the attributes  as parameters
035     * in a call to either a factory method or to a non-empty constructor.
036     */
037    
038    public class FactoryCreateRule extends Rule {
039    
040        // ----------------------------------------------------------- Fields
041        
042        /** Should exceptions thrown by the factory be ignored? */
043        private boolean ignoreCreateExceptions;
044        /** Stock to manage */
045        private Stack<Boolean> exceptionIgnoredStack;
046    
047        // ----------------------------------------------------------- Constructors
048    
049    
050        /**
051         * Construct a factory create rule that will use the specified
052         * class name to create an {@link ObjectCreationFactory} which will
053         * then be used to create an object and push it on the stack.
054         *
055         * @param digester The associated Digester
056         * @param className Java class name of the object creation factory class
057         *
058         * @deprecated The digester instance is now set in the {@link Digester#addRule} method. 
059         * Use {@link #FactoryCreateRule(String className)} instead.
060         */
061        public FactoryCreateRule(Digester digester, String className) {
062    
063            this(className);
064    
065        }
066    
067    
068        /**
069         * Construct a factory create rule that will use the specified
070         * class to create an {@link ObjectCreationFactory} which will
071         * then be used to create an object and push it on the stack.
072         *
073         * @param digester The associated Digester
074         * @param clazz Java class name of the object creation factory class
075         *
076         * @deprecated The digester instance is now set in the {@link Digester#addRule} method. 
077         * Use {@link #FactoryCreateRule(Class clazz)} instead.
078         */
079        public FactoryCreateRule(Digester digester, Class<?> clazz) {
080    
081            this(clazz);
082    
083        }
084    
085    
086        /**
087         * Construct a factory create rule that will use the specified
088         * class name (possibly overridden by the specified attribute if present)
089         * to create an {@link ObjectCreationFactory}, which will then be used
090         * to instantiate an object instance and push it onto the stack.
091         *
092         * @param digester The associated Digester
093         * @param className Default Java class name of the factory class
094         * @param attributeName Attribute name which, if present, contains an
095         *  override of the class name of the object creation factory to create.
096         *
097         * @deprecated The digester instance is now set in the {@link Digester#addRule} method. 
098         * Use {@link #FactoryCreateRule(String className, String attributeName)} instead.
099         */
100        public FactoryCreateRule(Digester digester,
101                                 String className, String attributeName) {
102    
103            this(className, attributeName);
104    
105        }
106    
107    
108        /**
109         * Construct a factory create rule that will use the specified
110         * class (possibly overridden by the specified attribute if present)
111         * to create an {@link ObjectCreationFactory}, which will then be used
112         * to instantiate an object instance and push it onto the stack.
113         *
114         * @param digester The associated Digester
115         * @param clazz Default Java class name of the factory class
116         * @param attributeName Attribute name which, if present, contains an
117         *  override of the class name of the object creation factory to create.
118         *
119         * @deprecated The digester instance is now set in the {@link Digester#addRule} method. 
120         * Use {@link #FactoryCreateRule(Class clazz, String attributeName)} instead.
121         */
122        public FactoryCreateRule(Digester digester,
123                                 Class<?> clazz, String attributeName) {
124    
125            this(clazz, attributeName);
126    
127        }
128    
129    
130        /**
131         * Construct a factory create rule using the given, already instantiated,
132         * {@link ObjectCreationFactory}.
133         *
134         * @param digester The associated Digester
135         * @param creationFactory called on to create the object.
136         *
137         * @deprecated The digester instance is now set in the {@link Digester#addRule} method. 
138         * Use {@link #FactoryCreateRule(ObjectCreationFactory creationFactory)} instead.
139         */
140        public FactoryCreateRule(Digester digester,
141                                 ObjectCreationFactory creationFactory) {
142    
143            this(creationFactory);
144    
145        }    
146    
147        /**
148         * <p>Construct a factory create rule that will use the specified
149         * class name to create an {@link ObjectCreationFactory} which will
150         * then be used to create an object and push it on the stack.</p>
151         *
152         * <p>Exceptions thrown during the object creation process will be propagated.</p>
153         *
154         * @param className Java class name of the object creation factory class
155         */
156        public FactoryCreateRule(String className) {
157    
158            this(className, false);
159    
160        }
161    
162    
163        /**
164         * <p>Construct a factory create rule that will use the specified
165         * class to create an {@link ObjectCreationFactory} which will
166         * then be used to create an object and push it on the stack.</p>
167         *
168         * <p>Exceptions thrown during the object creation process will be propagated.</p>
169         *
170         * @param clazz Java class name of the object creation factory class
171         */
172        public FactoryCreateRule(Class<?> clazz) {
173    
174            this(clazz, false);
175    
176        }
177    
178    
179        /**
180         * <p>Construct a factory create rule that will use the specified
181         * class name (possibly overridden by the specified attribute if present)
182         * to create an {@link ObjectCreationFactory}, which will then be used
183         * to instantiate an object instance and push it onto the stack.</p>
184         *
185         * <p>Exceptions thrown during the object creation process will be propagated.</p>
186         *
187         * @param className Default Java class name of the factory class
188         * @param attributeName Attribute name which, if present, contains an
189         *  override of the class name of the object creation factory to create.
190         */
191        public FactoryCreateRule(String className, String attributeName) {
192    
193            this(className, attributeName, false);
194    
195        }
196    
197    
198        /**
199         * <p>Construct a factory create rule that will use the specified
200         * class (possibly overridden by the specified attribute if present)
201         * to create an {@link ObjectCreationFactory}, which will then be used
202         * to instantiate an object instance and push it onto the stack.</p>
203         *
204         * <p>Exceptions thrown during the object creation process will be propagated.</p>
205         *
206         * @param clazz Default Java class name of the factory class
207         * @param attributeName Attribute name which, if present, contains an
208         *  override of the class name of the object creation factory to create.
209         */
210        public FactoryCreateRule(Class<?> clazz, String attributeName) {
211    
212            this(clazz, attributeName, false);
213    
214        }
215    
216    
217        /**
218         * <p>Construct a factory create rule using the given, already instantiated,
219         * {@link ObjectCreationFactory}.</p>
220         *
221         * <p>Exceptions thrown during the object creation process will be propagated.</p>
222         *
223         * @param creationFactory called on to create the object.
224         */
225        public FactoryCreateRule(ObjectCreationFactory creationFactory) {
226    
227            this(creationFactory, false);
228    
229        }
230        
231        /**
232         * Construct a factory create rule that will use the specified
233         * class name to create an {@link ObjectCreationFactory} which will
234         * then be used to create an object and push it on the stack.
235         *
236         * @param className Java class name of the object creation factory class
237         * @param ignoreCreateExceptions if true, exceptions thrown by the object
238         *  creation factory
239         * will be ignored.
240         */
241        public FactoryCreateRule(String className, boolean ignoreCreateExceptions) {
242    
243            this(className, null, ignoreCreateExceptions);
244    
245        }
246    
247    
248        /**
249         * Construct a factory create rule that will use the specified
250         * class to create an {@link ObjectCreationFactory} which will
251         * then be used to create an object and push it on the stack.
252         *
253         * @param clazz Java class name of the object creation factory class
254         * @param ignoreCreateExceptions if true, exceptions thrown by the
255         *  object creation factory
256         * will be ignored.
257         */
258        public FactoryCreateRule(Class<?> clazz, boolean ignoreCreateExceptions) {
259    
260            this(clazz, null, ignoreCreateExceptions);
261    
262        }
263    
264    
265        /**
266         * Construct a factory create rule that will use the specified
267         * class name (possibly overridden by the specified attribute if present)
268         * to create an {@link ObjectCreationFactory}, which will then be used
269         * to instantiate an object instance and push it onto the stack.
270         *
271         * @param className Default Java class name of the factory class
272         * @param attributeName Attribute name which, if present, contains an
273         *  override of the class name of the object creation factory to create.
274         * @param ignoreCreateExceptions if true, exceptions thrown by the object
275         *  creation factory will be ignored.
276         */
277        public FactoryCreateRule(
278                                    String className, 
279                                    String attributeName,
280                                    boolean ignoreCreateExceptions) {
281    
282            this.className = className;
283            this.attributeName = attributeName;
284            this.ignoreCreateExceptions = ignoreCreateExceptions;
285    
286        }
287    
288    
289        /**
290         * Construct a factory create rule that will use the specified
291         * class (possibly overridden by the specified attribute if present)
292         * to create an {@link ObjectCreationFactory}, which will then be used
293         * to instantiate an object instance and push it onto the stack.
294         *
295         * @param clazz Default Java class name of the factory class
296         * @param attributeName Attribute name which, if present, contains an
297         *  override of the class name of the object creation factory to create.
298         * @param ignoreCreateExceptions if true, exceptions thrown by the object
299         *  creation factory will be ignored.
300         */
301        public FactoryCreateRule(
302                                    Class<?> clazz, 
303                                    String attributeName,
304                                    boolean ignoreCreateExceptions) {
305    
306            this(clazz.getName(), attributeName, ignoreCreateExceptions);
307    
308        }
309    
310    
311        /**
312         * Construct a factory create rule using the given, already instantiated,
313         * {@link ObjectCreationFactory}.
314         *
315         * @param creationFactory called on to create the object.
316         * @param ignoreCreateExceptions if true, exceptions thrown by the object
317         *  creation factory will be ignored.
318         */
319        public FactoryCreateRule(
320                                ObjectCreationFactory creationFactory, 
321                                boolean ignoreCreateExceptions) {
322    
323            this.creationFactory = creationFactory;
324            this.ignoreCreateExceptions = ignoreCreateExceptions;
325        }
326    
327        // ----------------------------------------------------- Instance Variables
328    
329    
330        /**
331         * The attribute containing an override class name if it is present.
332         */
333        protected String attributeName = null;
334    
335    
336        /**
337         * The Java class name of the ObjectCreationFactory to be created.
338         * This class must have a no-arguments constructor.
339         */
340        protected String className = null;
341    
342    
343        /**
344         * The object creation factory we will use to instantiate objects
345         * as required based on the attributes specified in the matched XML
346         * element.
347         */
348        protected ObjectCreationFactory creationFactory = null;
349    
350    
351        // --------------------------------------------------------- Public Methods
352    
353    
354        /**
355         * Process the beginning of this element.
356         *
357         * @param attributes The attribute list of this element
358         */
359        public void begin(String namespace, String name, Attributes attributes) throws Exception {
360            
361            if (ignoreCreateExceptions) {
362            
363                if (exceptionIgnoredStack == null) {
364                    exceptionIgnoredStack = new Stack<Boolean>();
365                }
366                
367                try {
368                    Object instance = getFactory(attributes).createObject(attributes);
369                    
370                    if (digester.log.isDebugEnabled()) {
371                        digester.log.debug("[FactoryCreateRule]{" + digester.match +
372                                "} New " + (instance == null ? "null object" :
373                                instance.getClass().getName()));
374                    }
375                    digester.push(instance);
376                    exceptionIgnoredStack.push(Boolean.FALSE);
377                    
378                } catch (Exception e) {
379                    // log message and error
380                    if (digester.log.isInfoEnabled()) {
381                        digester.log.info("[FactoryCreateRule] Create exception ignored: " +
382                            ((e.getMessage() == null) ? e.getClass().getName() : e.getMessage()));
383                        if (digester.log.isDebugEnabled()) {
384                            digester.log.debug("[FactoryCreateRule] Ignored exception:", e);
385                        }
386                    }
387                    exceptionIgnoredStack.push(Boolean.TRUE);
388                }
389                
390            } else {
391                Object instance = getFactory(attributes).createObject(attributes);
392                
393                if (digester.log.isDebugEnabled()) {
394                    digester.log.debug("[FactoryCreateRule]{" + digester.match +
395                            "} New " + (instance == null ? "null object" :
396                            instance.getClass().getName()));
397                }
398                digester.push(instance);
399            }
400        }
401    
402    
403        /**
404         * Process the end of this element.
405         */
406        public void end(String namespace, String name) throws Exception {
407            
408            // check if object was created 
409            // this only happens if an exception was thrown and we're ignoring them
410            if (
411                    ignoreCreateExceptions &&
412                    exceptionIgnoredStack != null &&
413                    !(exceptionIgnoredStack.empty())) {
414                    
415                if (exceptionIgnoredStack.pop().booleanValue()) {
416                    // creation exception was ignored
417                    // nothing was put onto the stack
418                    if (digester.log.isTraceEnabled()) {
419                        digester.log.trace("[FactoryCreateRule] No creation so no push so no pop");
420                    }
421                    return;
422                }
423            } 
424    
425            Object top = digester.pop();
426            if (digester.log.isDebugEnabled()) {
427                digester.log.debug("[FactoryCreateRule]{" + digester.match +
428                        "} Pop " + top.getClass().getName());
429            }
430    
431        }
432    
433    
434        /**
435         * Clean up after parsing is complete.
436         */
437        public void finish() throws Exception {
438    
439            if (attributeName != null) {
440                creationFactory = null;
441            }
442    
443        }
444    
445    
446        /**
447         * Render a printable version of this Rule.
448         */
449        public String toString() {
450    
451            StringBuffer sb = new StringBuffer("FactoryCreateRule[");
452            sb.append("className=");
453            sb.append(className);
454            sb.append(", attributeName=");
455            sb.append(attributeName);
456            if (creationFactory != null) {
457                sb.append(", creationFactory=");
458                sb.append(creationFactory);
459            }
460            sb.append("]");
461            return (sb.toString());
462    
463        }
464    
465    
466        // ------------------------------------------------------ Protected Methods
467    
468    
469        /**
470         * Return an instance of our associated object creation factory,
471         * creating one if necessary.
472         *
473         * @param attributes Attributes passed to our factory creation element
474         *
475         * @exception Exception if any error occurs
476         */
477        protected ObjectCreationFactory getFactory(Attributes attributes)
478                throws Exception {
479    
480            if (creationFactory == null) {
481                String realClassName = className;
482                if (attributeName != null) {
483                    String value = attributes.getValue(attributeName);
484                    if (value != null) {
485                        realClassName = value;
486                    }
487                }
488                if (digester.log.isDebugEnabled()) {
489                    digester.log.debug("[FactoryCreateRule]{" + digester.match +
490                            "} New factory " + realClassName);
491                }
492                Class<?> clazz = digester.getClassLoader().loadClass(realClassName);
493                creationFactory = (ObjectCreationFactory)
494                        clazz.newInstance();
495                creationFactory.setDigester(digester);
496            }
497            return (creationFactory);
498    
499        }    
500    }