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