    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    }