001/* $Id: FactoryCreateRule.java 992060 2010-09-02 19:09:47Z simonetripodi $
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 java.util.Stack;
023
024import 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
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 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    @Deprecated
062    public FactoryCreateRule(Digester digester, String className) {
063
064        this(className);
065
066    }
067
068
069    /**
070     * Construct a factory create rule that will use the specified
071     * class to create an {@link ObjectCreationFactory} which will
072     * then be used to create an object and push it on the stack.
073     *
074     * @param digester The associated Digester
075     * @param clazz Java class name of the object creation factory class
076     *
077     * @deprecated The digester instance is now set in the {@link Digester#addRule} method. 
078     * Use {@link #FactoryCreateRule(Class clazz)} instead.
079     */
080    @Deprecated
081    public FactoryCreateRule(Digester digester, Class<?> clazz) {
082
083        this(clazz);
084
085    }
086
087
088    /**
089     * Construct a factory create rule that will use the specified
090     * class name (possibly overridden by the specified attribute if present)
091     * to create an {@link ObjectCreationFactory}, which will then be used
092     * to instantiate an object instance and push it onto the stack.
093     *
094     * @param digester The associated Digester
095     * @param className Default Java class name of the factory class
096     * @param attributeName Attribute name which, if present, contains an
097     *  override of the class name of the object creation factory to create.
098     *
099     * @deprecated The digester instance is now set in the {@link Digester#addRule} method. 
100     * Use {@link #FactoryCreateRule(String className, String attributeName)} instead.
101     */
102    @Deprecated
103    public FactoryCreateRule(Digester digester,
104                             String className, String attributeName) {
105
106        this(className, attributeName);
107
108    }
109
110
111    /**
112     * Construct a factory create rule that will use the specified
113     * class (possibly overridden by the specified attribute if present)
114     * to create an {@link ObjectCreationFactory}, which will then be used
115     * to instantiate an object instance and push it onto the stack.
116     *
117     * @param digester The associated Digester
118     * @param clazz Default Java class name of the factory class
119     * @param attributeName Attribute name which, if present, contains an
120     *  override of the class name of the object creation factory to create.
121     *
122     * @deprecated The digester instance is now set in the {@link Digester#addRule} method. 
123     * Use {@link #FactoryCreateRule(Class clazz, String attributeName)} instead.
124     */
125    @Deprecated
126    public FactoryCreateRule(Digester digester,
127                             Class<?> clazz, String attributeName) {
128
129        this(clazz, attributeName);
130
131    }
132
133
134    /**
135     * Construct a factory create rule using the given, already instantiated,
136     * {@link ObjectCreationFactory}.
137     *
138     * @param digester The associated Digester
139     * @param creationFactory called on to create the object.
140     *
141     * @deprecated The digester instance is now set in the {@link Digester#addRule} method. 
142     * Use {@link #FactoryCreateRule(ObjectCreationFactory creationFactory)} instead.
143     */
144    @Deprecated
145    public FactoryCreateRule(Digester digester,
146                             ObjectCreationFactory creationFactory) {
147
148        this(creationFactory);
149
150    }    
151
152    /**
153     * <p>Construct a factory create rule that will use the specified
154     * class name to create an {@link ObjectCreationFactory} which will
155     * then be used to create an object and push it on the stack.</p>
156     *
157     * <p>Exceptions thrown during the object creation process will be propagated.</p>
158     *
159     * @param className Java class name of the object creation factory class
160     */
161    public FactoryCreateRule(String className) {
162
163        this(className, false);
164
165    }
166
167
168    /**
169     * <p>Construct a factory create rule that will use the specified
170     * class to create an {@link ObjectCreationFactory} which will
171     * then be used to create an object and push it on the stack.</p>
172     *
173     * <p>Exceptions thrown during the object creation process will be propagated.</p>
174     *
175     * @param clazz Java class name of the object creation factory class
176     */
177    public FactoryCreateRule(Class<?> clazz) {
178
179        this(clazz, false);
180
181    }
182
183
184    /**
185     * <p>Construct a factory create rule that will use the specified
186     * class name (possibly overridden by the specified attribute if present)
187     * to create an {@link ObjectCreationFactory}, which will then be used
188     * to instantiate an object instance and push it onto the stack.</p>
189     *
190     * <p>Exceptions thrown during the object creation process will be propagated.</p>
191     *
192     * @param className Default Java class name of the factory class
193     * @param attributeName Attribute name which, if present, contains an
194     *  override of the class name of the object creation factory to create.
195     */
196    public FactoryCreateRule(String className, String attributeName) {
197
198        this(className, attributeName, false);
199
200    }
201
202
203    /**
204     * <p>Construct a factory create rule that will use the specified
205     * class (possibly overridden by the specified attribute if present)
206     * to create an {@link ObjectCreationFactory}, which will then be used
207     * to instantiate an object instance and push it onto the stack.</p>
208     *
209     * <p>Exceptions thrown during the object creation process will be propagated.</p>
210     *
211     * @param clazz Default Java class name of the factory class
212     * @param attributeName Attribute name which, if present, contains an
213     *  override of the class name of the object creation factory to create.
214     */
215    public FactoryCreateRule(Class<?> clazz, String attributeName) {
216
217        this(clazz, attributeName, false);
218
219    }
220
221
222    /**
223     * <p>Construct a factory create rule using the given, already instantiated,
224     * {@link ObjectCreationFactory}.</p>
225     *
226     * <p>Exceptions thrown during the object creation process will be propagated.</p>
227     *
228     * @param creationFactory called on to create the object.
229     */
230    public FactoryCreateRule(ObjectCreationFactory creationFactory) {
231
232        this(creationFactory, false);
233
234    }
235    
236    /**
237     * Construct a factory create rule that will use the specified
238     * class name to create an {@link ObjectCreationFactory} which will
239     * then be used to create an object and push it on the stack.
240     *
241     * @param className Java class name of the object creation factory class
242     * @param ignoreCreateExceptions if true, exceptions thrown by the object
243     *  creation factory
244     * will be ignored.
245     */
246    public FactoryCreateRule(String className, boolean ignoreCreateExceptions) {
247
248        this(className, null, ignoreCreateExceptions);
249
250    }
251
252
253    /**
254     * Construct a factory create rule that will use the specified
255     * class to create an {@link ObjectCreationFactory} which will
256     * then be used to create an object and push it on the stack.
257     *
258     * @param clazz Java class name of the object creation factory class
259     * @param ignoreCreateExceptions if true, exceptions thrown by the
260     *  object creation factory
261     * will be ignored.
262     */
263    public FactoryCreateRule(Class<?> clazz, boolean ignoreCreateExceptions) {
264
265        this(clazz, null, ignoreCreateExceptions);
266
267    }
268
269
270    /**
271     * Construct a factory create rule that will use the specified
272     * class name (possibly overridden by the specified attribute if present)
273     * to create an {@link ObjectCreationFactory}, which will then be used
274     * to instantiate an object instance and push it onto the stack.
275     *
276     * @param className Default Java class name of the factory class
277     * @param attributeName Attribute name which, if present, contains an
278     *  override of the class name of the object creation factory to create.
279     * @param ignoreCreateExceptions if true, exceptions thrown by the object
280     *  creation factory will be ignored.
281     */
282    public FactoryCreateRule(
283                                String className, 
284                                String attributeName,
285                                boolean ignoreCreateExceptions) {
286
287        this.className = className;
288        this.attributeName = attributeName;
289        this.ignoreCreateExceptions = ignoreCreateExceptions;
290
291    }
292
293
294    /**
295     * Construct a factory create rule that will use the specified
296     * class (possibly overridden by the specified attribute if present)
297     * to create an {@link ObjectCreationFactory}, which will then be used
298     * to instantiate an object instance and push it onto the stack.
299     *
300     * @param clazz Default Java class name of the factory class
301     * @param attributeName Attribute name which, if present, contains an
302     *  override of the class name of the object creation factory to create.
303     * @param ignoreCreateExceptions if true, exceptions thrown by the object
304     *  creation factory will be ignored.
305     */
306    public FactoryCreateRule(
307                                Class<?> clazz, 
308                                String attributeName,
309                                boolean ignoreCreateExceptions) {
310
311        this(clazz.getName(), attributeName, ignoreCreateExceptions);
312
313    }
314
315
316    /**
317     * Construct a factory create rule using the given, already instantiated,
318     * {@link ObjectCreationFactory}.
319     *
320     * @param creationFactory called on to create the object.
321     * @param ignoreCreateExceptions if true, exceptions thrown by the object
322     *  creation factory will be ignored.
323     */
324    public FactoryCreateRule(
325                            ObjectCreationFactory creationFactory, 
326                            boolean ignoreCreateExceptions) {
327
328        this.creationFactory = creationFactory;
329        this.ignoreCreateExceptions = ignoreCreateExceptions;
330    }
331
332    // ----------------------------------------------------- Instance Variables
333
334
335    /**
336     * The attribute containing an override class name if it is present.
337     */
338    protected String attributeName = null;
339
340
341    /**
342     * The Java class name of the ObjectCreationFactory to be created.
343     * This class must have a no-arguments constructor.
344     */
345    protected String className = null;
346
347
348    /**
349     * The object creation factory we will use to instantiate objects
350     * as required based on the attributes specified in the matched XML
351     * element.
352     */
353    protected ObjectCreationFactory creationFactory = null;
354
355
356    // --------------------------------------------------------- Public Methods
357
358
359    /**
360     * Process the beginning of this element.
361     *
362     * @param attributes The attribute list of this element
363     */
364    @Override
365    public void begin(String namespace, String name, Attributes attributes) throws Exception {
366        
367        if (ignoreCreateExceptions) {
368        
369            if (exceptionIgnoredStack == null) {
370                exceptionIgnoredStack = new Stack<Boolean>();
371            }
372            
373            try {
374                Object instance = getFactory(attributes).createObject(attributes);
375                
376                if (digester.log.isDebugEnabled()) {
377                    digester.log.debug("[FactoryCreateRule]{" + digester.match +
378                            "} New " + (instance == null ? "null object" :
379                            instance.getClass().getName()));
380                }
381                digester.push(instance);
382                exceptionIgnoredStack.push(Boolean.FALSE);
383                
384            } catch (Exception e) {
385                // log message and error
386                if (digester.log.isInfoEnabled()) {
387                    digester.log.info("[FactoryCreateRule] Create exception ignored: " +
388                        ((e.getMessage() == null) ? e.getClass().getName() : e.getMessage()));
389                    if (digester.log.isDebugEnabled()) {
390                        digester.log.debug("[FactoryCreateRule] Ignored exception:", e);
391                    }
392                }
393                exceptionIgnoredStack.push(Boolean.TRUE);
394            }
395            
396        } else {
397            Object instance = getFactory(attributes).createObject(attributes);
398            
399            if (digester.log.isDebugEnabled()) {
400                digester.log.debug("[FactoryCreateRule]{" + digester.match +
401                        "} New " + (instance == null ? "null object" :
402                        instance.getClass().getName()));
403            }
404            digester.push(instance);
405        }
406    }
407
408
409    /**
410     * Process the end of this element.
411     */
412    @Override
413    public void end(String namespace, String name) throws Exception {
414        
415        // check if object was created 
416        // this only happens if an exception was thrown and we're ignoring them
417        if (
418                ignoreCreateExceptions &&
419                exceptionIgnoredStack != null &&
420                !(exceptionIgnoredStack.empty())) {
421                
422            if (exceptionIgnoredStack.pop().booleanValue()) {
423                // creation exception was ignored
424                // nothing was put onto the stack
425                if (digester.log.isTraceEnabled()) {
426                    digester.log.trace("[FactoryCreateRule] No creation so no push so no pop");
427                }
428                return;
429            }
430        } 
431
432        Object top = digester.pop();
433        if (digester.log.isDebugEnabled()) {
434            digester.log.debug("[FactoryCreateRule]{" + digester.match +
435                    "} Pop " + top.getClass().getName());
436        }
437
438    }
439
440
441    /**
442     * Clean up after parsing is complete.
443     */
444    @Override
445    public void finish() throws Exception {
446
447        if (attributeName != null) {
448            creationFactory = null;
449        }
450
451    }
452
453
454    /**
455     * Render a printable version of this Rule.
456     */
457    @Override
458    public String toString() {
459
460        StringBuffer sb = new StringBuffer("FactoryCreateRule[");
461        sb.append("className=");
462        sb.append(className);
463        sb.append(", attributeName=");
464        sb.append(attributeName);
465        if (creationFactory != null) {
466            sb.append(", creationFactory=");
467            sb.append(creationFactory);
468        }
469        sb.append("]");
470        return (sb.toString());
471
472    }
473
474
475    // ------------------------------------------------------ Protected Methods
476
477
478    /**
479     * Return an instance of our associated object creation factory,
480     * creating one if necessary.
481     *
482     * @param attributes Attributes passed to our factory creation element
483     *
484     * @exception Exception if any error occurs
485     */
486    protected ObjectCreationFactory getFactory(Attributes attributes)
487            throws Exception {
488
489        if (creationFactory == null) {
490            String realClassName = className;
491            if (attributeName != null) {
492                String value = attributes.getValue(attributeName);
493                if (value != null) {
494                    realClassName = value;
495                }
496            }
497            if (digester.log.isDebugEnabled()) {
498                digester.log.debug("[FactoryCreateRule]{" + digester.match +
499                        "} New factory " + realClassName);
500            }
501            Class<?> clazz = digester.getClassLoader().loadClass(realClassName);
502            creationFactory = (ObjectCreationFactory)
503                    clazz.newInstance();
504            creationFactory.setDigester(digester);
505        }
506        return (creationFactory);
507
508    }    
509}