001package 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 022import static java.lang.String.format; 023 024import java.util.Formatter; 025import java.util.Stack; 026 027import 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 */ 039public 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}