View Javadoc

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