001/* $Id: FactoryCreateRule.java 728882 2008-12-23 06:27:54Z 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
020package org.apache.commons.digester;
021
022import org.xml.sax.Attributes;
023
024import org.apache.commons.collections.ArrayStack;
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
038public 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 ArrayStack 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 ArrayStack();
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 (((Boolean) 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}