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    
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        @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    }