001 package org.apache.commons.digester3;
002
003 /*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements. See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership. The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License. You may obtain a copy of the License at
011 *
012 * http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied. See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022 import static java.lang.String.format;
023
024 import java.util.Formatter;
025 import java.util.Stack;
026
027 import org.xml.sax.Attributes;
028
029 /**
030 * <p>
031 * Rule implementation that uses an {@link ObjectCreationFactory} to create a new object which it pushes onto the object
032 * stack. When the element is complete, the object will be popped.
033 * </p>
034 * <p>
035 * This rule is intended in situations where the element's attributes are needed before the object can be created. A
036 * common senario is for the ObjectCreationFactory implementation to use the attributes as parameters in a call to
037 * either a factory method or to a non-empty constructor.
038 */
039 public class FactoryCreateRule
040 extends Rule
041 {
042
043 // ----------------------------------------------------------- Fields
044
045 /** Should exceptions thrown by the factory be ignored? */
046 private boolean ignoreCreateExceptions;
047
048 /** Stock to manage */
049 private Stack<Boolean> exceptionIgnoredStack;
050
051 // ----------------------------------------------------------- Constructors
052
053 /**
054 * <p>
055 * Construct a factory create rule that will use the specified class name to create an {@link ObjectCreationFactory}
056 * which will then be used to create an object and push it on the stack.
057 * </p>
058 * <p>
059 * Exceptions thrown during the object creation process will be propagated.
060 * </p>
061 *
062 * @param className Java class name of the object creation factory class
063 */
064 public FactoryCreateRule( String className )
065 {
066 this( className, false );
067 }
068
069 /**
070 * <p>
071 * Construct a factory create rule that will use the specified class to create an {@link ObjectCreationFactory}
072 * which will then be used to create an object and push it on the stack.
073 * </p>
074 * <p>
075 * Exceptions thrown during the object creation process will be propagated.
076 * </p>
077 *
078 * @param clazz Java class name of the object creation factory class
079 */
080 public FactoryCreateRule( Class<? extends ObjectCreationFactory<?>> clazz )
081 {
082 this( clazz, false );
083 }
084
085 /**
086 * <p>
087 * Construct a factory create rule that will use the specified class name (possibly overridden by the specified
088 * attribute if present) to create an {@link ObjectCreationFactory}, which will then be used to instantiate an
089 * object instance and push it onto the stack.
090 * </p>
091 * <p>
092 * Exceptions thrown during the object creation process will be propagated.
093 * </p>
094 *
095 * @param className Default Java class name of the factory class
096 * @param attributeName Attribute name which, if present, contains an override of the class name of the object
097 * creation factory to create.
098 */
099 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 }