    001    /* $Id: DigesterRuleParser.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.xmlrules;
    021
    022
    023    import java.io.FileNotFoundException;
    024    import java.io.IOException;
    025    import java.net.URL;
    026    import java.util.ArrayList;
    027    import java.util.HashSet;
    028    import java.util.List;
    029    import java.util.Set;
    030    import java.util.Stack;
    031    import java.util.StringTokenizer;
    032
    033    import org.apache.commons.beanutils.ConvertUtils;
    034    import org.apache.commons.digester.AbstractObjectCreationFactory;
    035    import org.apache.commons.digester.BeanPropertySetterRule;
    036    import org.apache.commons.digester.CallMethodRule;
    037    import org.apache.commons.digester.CallParamRule;
    038    import org.apache.commons.digester.Digester;
    039    import org.apache.commons.digester.FactoryCreateRule;
    040    import org.apache.commons.digester.NodeCreateRule;
    041    import org.apache.commons.digester.ObjectCreateRule;
    042    import org.apache.commons.digester.ObjectParamRule;
    043    import org.apache.commons.digester.Rule;
    044    import org.apache.commons.digester.RuleSetBase;
    045    import org.apache.commons.digester.Rules;
    046    import org.apache.commons.digester.SetNestedPropertiesRule;
    047    import org.apache.commons.digester.SetNextRule;
    048    import org.apache.commons.digester.SetPropertiesRule;
    049    import org.apache.commons.digester.SetPropertyRule;
    050    import org.apache.commons.digester.SetRootRule;
    051    import org.apache.commons.digester.SetTopRule;
    052    import org.w3c.dom.Node;
    053    import org.xml.sax.Attributes;
    054    import org.xml.sax.SAXException;
    055
    056
    057    /**
    058     * This is a RuleSet that parses XML into Digester rules, and then
    059     * adds those rules to a 'target' Digester.
    060     *
    061     * @since 1.2
    062     */
    063
    064    public class DigesterRuleParser extends RuleSetBase {
    065
    066        public static final String DIGESTER_PUBLIC_ID = "-//Jakarta Apache //DTD digester-rules XML V1.0//EN";
    067
    068        /**
    069         * path to the DTD
    070         */
    071        private String digesterDtdUrl;
    072
    073        /**
    074         * This is the digester to which we are adding the rules that we parse
    075         * from the Rules XML document.
    076         */
    077        protected Digester targetDigester;
    078
    079        /** See {@link #setBasePath}. */
    080        protected String basePath = "";
    081
    082        /**
    083         * A stack whose toString method returns a '/'-separated concatenation
    084         * of all the elements in the stack.
    085         */
    086        protected class PatternStack<E> extends Stack<E> {
    087
    088            private static final long serialVersionUID = 1L;
    089
    090            public String toString() {
    091                StringBuffer str = new StringBuffer();
    092                for (int i = 0; i < size(); i++) {
    093                    String elem = get(i).toString();
    094                    if (elem.length() > 0) {
    095                        if (str.length() > 0) {
    096                            str.append('/');
    097                        }
    098                        str.append(elem);
    099                    }
    100                }
    101                return str.toString();
    102            }
    103        }
    104
    105        /**
    106         * A stack used to maintain the current pattern. The Rules XML document
    107         * type allows nesting of patterns. If an element defines a matching
    108         * pattern, the resulting pattern is a concatenation of that pattern with
    109         * all the ancestor elements' patterns. Hence the need for a stack.
    110         */
    111        protected PatternStack<String> patternStack;
    112
    113        /**
    114         * Used to detect circular includes
    115         */
    116        private Set<String> includedFiles = new HashSet<String>();
    117
    118        /**
    119         * Constructs a DigesterRuleParser. This object will be inoperable
    120         * until the target digester is set, via <code>setTarget(Digester)</code>
    121         */
    122        public DigesterRuleParser() {
    123            patternStack = new PatternStack<String>();
    124        }
    125
    126        /**
    127         * Constructs a rule set for converting XML digester rule descriptions
    128         * into Rule objects, and adding them to the given Digester
    129         * @param targetDigester the Digester to add the rules to
    130         */
    131        public DigesterRuleParser(Digester targetDigester) {
    132            this.targetDigester = targetDigester;
    133            patternStack = new PatternStack<String>();
    134        }
    135
    136        /**
    137         * Constructs a rule set for parsing an XML digester rule file that
    138         * has been included within an outer XML digester rule file. In this
    139         * case, we must pass the pattern stack and the target digester
    140         * to the rule set, as well as the list of files that have already
    141         * been included, for cycle detection.
    142         * @param targetDigester the Digester to add the rules to
    143         * @param stack Stack containing the prefix pattern string to be prepended
    144         * to any pattern parsed by this rule set.
    145         */
    146        private DigesterRuleParser(Digester targetDigester,
    147                                    PatternStack<String> stack, Set<String> includedFiles) {
    148            this.targetDigester = targetDigester;
    149            patternStack = stack;
    150            this.includedFiles = includedFiles;
    151        }
    152
    153        /**
    154         * Sets the digester into which to add the parsed rules
    155         * @param d the Digester to add the rules to
    156         */
    157        public void setTarget(Digester d) {
    158            targetDigester = d;
    159        }
    160
    161        /**
    162         * Set a base pattern beneath which all the rules loaded by this
    163         * object will be registered. If this string is not empty, and does
    164         * not end in a "/", then one will be added.
    165         *
    166         * @since 1.6
    167         */
    168        public void setBasePath(String path) {
    169            if (path == null) {
    170                basePath = "";
    171            }
    172            else if ((path.length() > 0) && !path.endsWith("/")) {
    173                basePath = path + "/";
    174            } else {
    175                basePath = path;
    176            }
    177        }
    178
    179        /**
    180         * Sets the location of the digester rules DTD. This is the DTD used
    181         * to validate the rules XML file.
    182         */
    183        public void setDigesterRulesDTD(String dtdURL) {
    184            digesterDtdUrl = dtdURL;
    185        }
    186
    187        /**
    188         * Returns the location of the DTD used to validate the digester rules
    189         * XML document.
    190         */
    191        protected String getDigesterRulesDTD() {
    192            //ClassLoader classLoader = getClass().getClassLoader();
    193            //URL url = classLoader.getResource(DIGESTER_DTD_PATH);
    194            //return url.toString();
    195            return digesterDtdUrl;
    196        }
    197
    198        /**
    199         * Adds a rule the the target digester. After a rule has been created by
    200         * parsing the XML, it is added to the digester by calling this method.
    201         * Typically, this method is called via reflection, when executing
    202         * a SetNextRule, from the Digester that is parsing the rules XML.
    203         * @param rule a Rule to add to the target digester.
    204         */
    205        public void add(Rule rule) {
    206            targetDigester.addRule(
    207                basePath + patternStack.toString(), rule);
    208        }
    209
    210
    211        /**
    212         * Add to the given digester the set of Rule instances used to parse an XML
    213         * document defining Digester rules. When the digester parses an XML file,
    214         * it will add the resulting rules & patterns to the 'target digester'
    215         * that was passed in this RuleSet's constructor.<P>
    216         * If you extend this class to support additional rules, your implementation
    217         * should of this method should call this implementation first: i.e.
    218         * <code>super.addRuleInstances(digester);</code>
    219         */
    220        public void addRuleInstances(Digester digester) {
    221            final String ruleClassName = Rule.class.getName();
    222            digester.register(DIGESTER_PUBLIC_ID, getDigesterRulesDTD());
    223
    224            digester.addRule("*/pattern", new PatternRule("value"));
    225
    226            digester.addRule("*/include", new IncludeRule());
    227
    228            digester.addFactoryCreate("*/bean-property-setter-rule", new BeanPropertySetterRuleFactory());
    229            digester.addRule("*/bean-property-setter-rule", new PatternRule("pattern"));
    230            digester.addSetNext("*/bean-property-setter-rule", "add", ruleClassName);
    231
    232            digester.addFactoryCreate("*/call-method-rule", new CallMethodRuleFactory());
    233            digester.addRule("*/call-method-rule", new PatternRule("pattern"));
    234            digester.addSetNext("*/call-method-rule", "add", ruleClassName);
    235
    236            digester.addFactoryCreate("*/object-param-rule", new ObjectParamRuleFactory());
    237            digester.addRule("*/object-param-rule", new PatternRule("pattern"));
    238            digester.addSetNext("*/object-param-rule", "add", ruleClassName);
    239
    240            digester.addFactoryCreate("*/call-param-rule", new CallParamRuleFactory());
    241            digester.addRule("*/call-param-rule", new PatternRule("pattern"));
    242            digester.addSetNext("*/call-param-rule", "add", ruleClassName);
    243
    244            digester.addFactoryCreate("*/factory-create-rule", new FactoryCreateRuleFactory());
    245            digester.addRule("*/factory-create-rule", new PatternRule("pattern"));
    246            digester.addSetNext("*/factory-create-rule", "add", ruleClassName);
    247
    248            digester.addFactoryCreate("*/object-create-rule", new ObjectCreateRuleFactory());
    249            digester.addRule("*/object-create-rule", new PatternRule("pattern"));
    250            digester.addSetNext("*/object-create-rule", "add", ruleClassName);
    251
    252            digester.addFactoryCreate("*/node-create-rule", new NodeCreateRuleFactory());
    253            digester.addRule("*/node-create-rule", new PatternRule("pattern"));
    254            digester.addSetNext("*/node-create-rule", "add", ruleClassName);
    255
    256            digester.addFactoryCreate("*/set-properties-rule", new SetPropertiesRuleFactory());
    257            digester.addRule("*/set-properties-rule", new PatternRule("pattern"));
    258            digester.addSetNext("*/set-properties-rule", "add", ruleClassName);
    259
    260            digester.addRule("*/set-properties-rule/alias", new SetPropertiesAliasRule());
    261
    262            digester.addFactoryCreate("*/set-property-rule", new SetPropertyRuleFactory());
    263            digester.addRule("*/set-property-rule", new PatternRule("pattern"));
    264            digester.addSetNext("*/set-property-rule", "add", ruleClassName);
    265
    266            digester.addFactoryCreate("*/set-nested-properties-rule", new SetNestedPropertiesRuleFactory());
    267            digester.addRule("*/set-nested-properties-rule", new PatternRule("pattern"));
    268            digester.addSetNext("*/set-nested-properties-rule", "add", ruleClassName);
    269
    270            digester.addRule("*/set-nested-properties-rule/alias", new SetNestedPropertiesAliasRule());
    271
    272            digester.addFactoryCreate("*/set-top-rule", new SetTopRuleFactory());
    273            digester.addRule("*/set-top-rule", new PatternRule("pattern"));
    274            digester.addSetNext("*/set-top-rule", "add", ruleClassName);
    275
    276            digester.addFactoryCreate("*/set-next-rule", new SetNextRuleFactory());
    277            digester.addRule("*/set-next-rule", new PatternRule("pattern"));
    278            digester.addSetNext("*/set-next-rule", "add", ruleClassName);
    279            digester.addFactoryCreate("*/set-root-rule", new SetRootRuleFactory());
    280            digester.addRule("*/set-root-rule", new PatternRule("pattern"));
    281            digester.addSetNext("*/set-root-rule", "add", ruleClassName);
    282        }
    283
    284
    285        /**
    286         * A rule for extracting the pattern matching strings from the rules XML.
    287         * In the digester-rules document type, a pattern can either be declared
    288         * in the 'value' attribute of a <pattern> element (in which case the pattern
    289         * applies to all rules elements contained within the <pattern> element),
    290         * or it can be declared in the optional 'pattern' attribute of a rule
    291         * element.
    292         */
    293        private class PatternRule extends Rule {
    294
    295            private String attrName;
    296            private String pattern = null;
    297
    298            /**
    299             * @param attrName The name of the attribute containing the pattern
    300             */
    301            public PatternRule(String attrName) {
    302                super();
    303                this.attrName = attrName;
    304            }
    305
    306            /**
    307             * If a pattern is defined for the attribute, push it onto the
    308             * pattern stack.
    309             */
    310            public void begin(Attributes attributes) {
    311                pattern = attributes.getValue(attrName);
    312                if (pattern != null) {
    313                    patternStack.push(pattern);
    314                }
    315            }
    316
    317            /**
    318             * If there was a pattern for this element, pop it off the pattern
    319             * stack.
    320             */
    321            public void end() {
    322                if (pattern != null) {
    323                    patternStack.pop();
    324                }
    325            }
    326        }
    327
    328        /**
    329         * A rule for including one rules XML file within another. Included files
    330         * behave as if they are 'macro-expanded' within the includer. This means
    331         * that the values of the pattern stack are prefixed to every pattern
    332         * in the included rules. <p>This rule will detect 'circular' includes,
    333         * which would result in infinite recursion. It throws a
    334         * CircularIncludeException when a cycle is detected, which will terminate
    335         * the parse.
    336         */
    337        private class IncludeRule extends Rule {
    338            public IncludeRule() {
    339                super();
    340            }
    341
    342            /**
    343             * To include a rules xml file, we instantiate another Digester, and
    344             * another DigesterRulesRuleSet. We pass the
    345             * pattern stack and the target Digester to the new rule set, and
    346             * tell the Digester to parse the file.
    347             */
    348            public void begin(Attributes attributes) throws Exception {
    349                // The path attribute gives the URI to another digester rules xml file
    350                String fileName = attributes.getValue("path");
    351                if (fileName != null && fileName.length() > 0) {
    352                    includeXMLRules(fileName);
    353                }
    354
    355                // The class attribute gives the name of a class that implements
    356                // the DigesterRulesSource interface
    357                String className = attributes.getValue("class");
    358                if (className != null && className.length() > 0) {
    359                    includeProgrammaticRules(className);
    360                }
    361            }
    362
    363            /**
    364             * Creates another DigesterRuleParser, and uses it to extract the rules
    365             * out of the give XML file. The contents of the current pattern stack
    366             * will be prepended to all of the pattern strings parsed from the file.
    367             */
    368            private void includeXMLRules(String fileName)
    369                            throws IOException, SAXException, CircularIncludeException {
    370                ClassLoader cl = Thread.currentThread().getContextClassLoader();
    371                if (cl == null) {
    372                    cl = DigesterRuleParser.this.getClass().getClassLoader();
    373                }
    374                URL fileURL = cl.getResource(fileName);
    375                if (fileURL == null) {
    376                    throw new FileNotFoundException("File \"" + fileName + "\" not found.");
    377                }
    378                fileName = fileURL.toExternalForm();
    379                if (includedFiles.add(fileName) == false) {
    380                    // circular include detected
    381                    throw new CircularIncludeException(fileName);
    382                }
    383                // parse the included xml file
    384                DigesterRuleParser includedSet =
    385                            new DigesterRuleParser(targetDigester, patternStack, includedFiles);
    386                includedSet.setDigesterRulesDTD(getDigesterRulesDTD());
    387                Digester digester = new Digester();
    388                digester.addRuleSet(includedSet);
    389                digester.push(DigesterRuleParser.this);
    390                digester.parse(fileName);
    391                includedFiles.remove(fileName);
    392            }
    393
    394            /**
    395             * Creates an instance of the indicated class. The class must implement
    396             * the DigesterRulesSource interface. Passes the target digester to
    397             * that instance. The DigesterRulesSource instance is supposed to add
    398             * rules into the digester. The contents of the current pattern stack
    399             * will be automatically prepended to all of the pattern strings added
    400             * by the DigesterRulesSource instance.
    401             */
    402            private void includeProgrammaticRules(String className)
    403                            throws ClassNotFoundException, ClassCastException,
    404                            InstantiationException, IllegalAccessException {
    405
    406                Class<?> cls = Class.forName(className);
    407                DigesterRulesSource rulesSource = (DigesterRulesSource) cls.newInstance();
    408
    409                // wrap the digester's Rules object, to prepend pattern
    410                Rules digesterRules = targetDigester.getRules();
    411                Rules prefixWrapper =
    412                        new RulesPrefixAdapter(patternStack.toString(), digesterRules);
    413
    414                targetDigester.setRules(prefixWrapper);
    415                try {
    416                    rulesSource.getRules(targetDigester);
    417                } finally {
    418                    // Put the unwrapped rules back
    419                    targetDigester.setRules(digesterRules);
    420                }
    421            }
    422        }
    423
    424
    425        /**
    426         * Wraps a Rules object. Delegates all the Rules interface methods
    427         * to the underlying Rules object. Overrides the add method to prepend
    428         * a prefix to the pattern string.
    429         */
    430        private class RulesPrefixAdapter implements Rules {
    431
    432            private Rules delegate;
    433            private String prefix;
    434
    435            /**
    436             * @param patternPrefix the pattern string to prepend to the pattern
    437             * passed to the add method.
    438             * @param rules The wrapped Rules object. All of this class's methods
    439             * pass through to this object.
    440             */
    441            public RulesPrefixAdapter(String patternPrefix, Rules rules) {
    442                prefix = patternPrefix;
    443                delegate = rules;
    444            }
    445
    446            /**
    447             * Register a new Rule instance matching a pattern which is constructed
    448             * by concatenating the pattern prefix with the given pattern.
    449             */
    450            public void add(String pattern, Rule rule) {
    451                StringBuffer buffer = new StringBuffer();
    452                buffer.append(prefix);
    453                if (!pattern.startsWith("/")) {
    454                    buffer.append('/');
    455                }
    456                buffer.append(pattern);
    457                delegate.add(buffer.toString(), rule);
    458            }
    459
    460            /**
    461             * This method passes through to the underlying Rules object.
    462             */
    463            public void clear() {
    464                delegate.clear();
    465            }
    466
    467            /**
    468             * This method passes through to the underlying Rules object.
    469             */
    470            public Digester getDigester() {
    471                return delegate.getDigester();
    472            }
    473
    474            /**
    475             * This method passes through to the underlying Rules object.
    476             */
    477            public String getNamespaceURI() {
    478                return delegate.getNamespaceURI();
    479            }
    480
    481            /**
    482             * @deprecated Call match(namespaceURI,pattern) instead.
    483             */
    484            public List<Rule> match(String pattern) {
    485                return delegate.match(pattern);
    486            }
    487
    488            /**
    489             * This method passes through to the underlying Rules object.
    490             */
    491            public List<Rule> match(String namespaceURI, String pattern) {
    492                return delegate.match(namespaceURI, pattern);
    493            }
    494
    495            /**
    496             * This method passes through to the underlying Rules object.
    497             */
    498            public List<Rule> rules() {
    499                return delegate.rules();
    500            }
    501
    502            /**
    503             * This method passes through to the underlying Rules object.
    504             */
    505            public void setDigester(Digester digester) {
    506                delegate.setDigester(digester);
    507            }
    508
    509            /**
    510             * This method passes through to the underlying Rules object.
    511             */
    512            public void setNamespaceURI(String namespaceURI) {
    513                delegate.setNamespaceURI(namespaceURI);
    514            }
    515        }
    516
    517
    518        ///////////////////////////////////////////////////////////////////////
    519        // Classes beyond this point are ObjectCreationFactory implementations,
    520        // used to create Rule objects and initialize them from SAX attributes.
    521        ///////////////////////////////////////////////////////////////////////
    522
    523        /**
    524         * Factory for creating a BeanPropertySetterRule.
    525         */
    526        private class BeanPropertySetterRuleFactory extends AbstractObjectCreationFactory {
    527            public Object createObject(Attributes attributes) throws Exception {
    528                Rule beanPropertySetterRule = null;
    529                String propertyname = attributes.getValue("propertyname");
    530
    531                if (propertyname == null) {
    532                    // call the setter method corresponding to the element name.
    533                    beanPropertySetterRule = new BeanPropertySetterRule();
    534                } else {
    535                    beanPropertySetterRule = new BeanPropertySetterRule(propertyname);
    536                }
    537
    538                return beanPropertySetterRule;
    539            }
    540
    541        }
    542
    543        /**
    544         * Factory for creating a CallMethodRule.
    545         */
    546        protected class CallMethodRuleFactory extends AbstractObjectCreationFactory {
    547            public Object createObject(Attributes attributes) {
    548                Rule callMethodRule = null;
    549                String methodName = attributes.getValue("methodname");
    550
    551                // Select which element is to be the target. Default to zero,
    552                // ie the top object on the stack.
    553                int targetOffset = 0;
    554                String targetOffsetStr = attributes.getValue("targetoffset");
    555                if (targetOffsetStr != null) {
    556                    targetOffset = Integer.parseInt(targetOffsetStr);
    557                }
    558
    559                if (attributes.getValue("paramcount") == null) {
    560                    // call against empty method
    561                    callMethodRule = new CallMethodRule(targetOffset, methodName);
    562
    563                } else {
    564                    int paramCount = Integer.parseInt(attributes.getValue("paramcount"));
    565
    566                    String paramTypesAttr = attributes.getValue("paramtypes");
    567                    if (paramTypesAttr == null || paramTypesAttr.length() == 0) {
    568                        callMethodRule = new CallMethodRule(targetOffset, methodName, paramCount);
    569                    } else {
    570                        String[] paramTypes = getParamTypes(paramTypesAttr);
    571                        callMethodRule = new CallMethodRule(
    572                            targetOffset, methodName, paramCount, paramTypes);
    573                    }
    574                }
    575                return callMethodRule;
    576            }
    577
    578            /**
    579             * Process the comma separated list of paramTypes
    580             * into an array of String class names
    581             */
    582            private String[] getParamTypes(String paramTypes) {
    583                String[] paramTypesArray;
    584                if( paramTypes != null ) {
    585                    ArrayList<String> paramTypesList = new ArrayList<String>();
    586                    StringTokenizer tokens = new StringTokenizer(
    587                            paramTypes, " \t\n\r,");
    588                    while (tokens.hasMoreTokens()) {
    589                        paramTypesList.add(tokens.nextToken());
    590                    }
    591                    paramTypesArray = (String[])paramTypesList.toArray(new String[0]);
    592                } else {
    593                    paramTypesArray = new String[0];
    594                }
    595                return paramTypesArray;
    596            }
    597        }
    598
    599        /**
    600         * Factory for creating a CallParamRule.
    601         */
    602        protected class CallParamRuleFactory extends AbstractObjectCreationFactory {
    603
    604            public Object createObject(Attributes attributes) {
    605                // create callparamrule
    606                int paramIndex = Integer.parseInt(attributes.getValue("paramnumber"));
    607                String attributeName = attributes.getValue("attrname");
    608                String fromStack = attributes.getValue("from-stack");
    609                String stackIndex = attributes.getValue("stack-index");
    610                Rule callParamRule = null;
    611
    612                if (attributeName == null) {
    613                    if (stackIndex != null) {
    614                        callParamRule = new CallParamRule(
    615                            paramIndex, Integer.parseInt(stackIndex));
    616                    } else if (fromStack != null) {
    617                        callParamRule = new CallParamRule(
    618                            paramIndex, Boolean.valueOf(fromStack).booleanValue());
    619                    } else {
    620                        callParamRule = new CallParamRule(paramIndex);
    621                    }
    622                } else {
    623                    if (fromStack == null) {
    624                        callParamRule = new CallParamRule(paramIndex, attributeName);
    625                    } else {
    626                        // specifying both from-stack and attribute name is not allowed
    627                        throw new RuntimeException(
    628                            "Attributes from-stack and attrname cannot both be present.");
    629                    }
    630                }
    631                return callParamRule;
    632            }
    633        }
    634
    635        /**
    636         * Factory for creating a ObjectParamRule
    637         */
    638        protected class ObjectParamRuleFactory extends AbstractObjectCreationFactory {
    639            public Object createObject(Attributes attributes) throws Exception {
    640                // create callparamrule
    641                int paramIndex = Integer.parseInt(attributes.getValue("paramnumber"));
    642                String attributeName = attributes.getValue("attrname");
    643                String type = attributes.getValue("type");
    644                String value = attributes.getValue("value");
    645
    646                Rule objectParamRule = null;
    647
    648                // type name is requried
    649                if (type == null) {
    650                    throw new RuntimeException("Attribute 'type' is required.");
    651                }
    652
    653                // create object instance
    654                Object param = null;
    655                Class<?> clazz = Class.forName(type);
    656                if (value == null) {
    657                    param = clazz.newInstance();
    658                } else {
    659                    param = ConvertUtils.convert(value, clazz);
    660                }
    661
    662                if (attributeName == null) {
    663                    objectParamRule = new ObjectParamRule(paramIndex, param);
    664                } else {
    665                    objectParamRule = new ObjectParamRule(paramIndex, attributeName, param);
    666                }
    667                return objectParamRule;
    668            }
    669         }
    670
    671            /**
    672             * Factory for creating a NodeCreateRule
    673             */
    674        protected class NodeCreateRuleFactory extends AbstractObjectCreationFactory {
    675
    676            public Object createObject(Attributes attributes) throws Exception {
    677
    678                String nodeType = attributes.getValue("type");
    679                if (nodeType == null || "".equals(nodeType)) {
    680
    681                    // uses Node.ELEMENT_NODE
    682                    return new NodeCreateRule();
    683                } else if ("element".equals(nodeType)) {
    684
    685                    return new NodeCreateRule(Node.ELEMENT_NODE);
    686                } else if ("fragment".equals(nodeType)) {
    687
    688                    return new NodeCreateRule(Node.DOCUMENT_FRAGMENT_NODE);
    689                } else {
    690
    691                    throw new RuntimeException(
    692                            "Unrecognized node type: "
    693                                    + nodeType
    694                                    + ".  This attribute is optional or can have a value of element|fragment.");
    695                }
    696            }
    697        }
    698
    699        /**
    700         * Factory for creating a FactoryCreateRule
    701         */
    702        protected class FactoryCreateRuleFactory extends AbstractObjectCreationFactory {
    703            public Object createObject(Attributes attributes) {
    704                String className = attributes.getValue("classname");
    705                String attrName = attributes.getValue("attrname");
    706                boolean ignoreExceptions =
    707                    "true".equalsIgnoreCase(attributes.getValue("ignore-exceptions"));
    708                return (attrName == null || attrName.length() == 0) ?
    709                    new FactoryCreateRule( className, ignoreExceptions) :
    710                    new FactoryCreateRule( className, attrName, ignoreExceptions);
    711            }
    712        }
    713
    714        /**
    715         * Factory for creating a ObjectCreateRule
    716         */
    717        protected class ObjectCreateRuleFactory extends AbstractObjectCreationFactory {
    718            public Object createObject(Attributes attributes) {
    719                String className = attributes.getValue("classname");
    720                String attrName = attributes.getValue("attrname");
    721                return (attrName == null || attrName.length() == 0) ?
    722                    new ObjectCreateRule( className) :
    723                    new ObjectCreateRule( className, attrName);
    724            }
    725        }
    726
    727        /**
    728         * Factory for creating a SetPropertiesRule
    729         */
    730        protected class SetPropertiesRuleFactory extends AbstractObjectCreationFactory {
    731            public Object createObject(Attributes attributes) {
    732                    return new SetPropertiesRule();
    733            }
    734        }
    735
    736        /**
    737         * Factory for creating a SetPropertyRule
    738         */
    739        protected class SetPropertyRuleFactory extends AbstractObjectCreationFactory {
    740            public Object createObject(Attributes attributes) {
    741                String name = attributes.getValue("name");
    742                String value = attributes.getValue("value");
    743                return new SetPropertyRule( name, value);
    744            }
    745        }
    746
    747        /**
    748         * Factory for creating a SetNestedPropertiesRule
    749         */
    750        protected class SetNestedPropertiesRuleFactory extends AbstractObjectCreationFactory {
    751            public Object createObject(Attributes attributes) {
    752               boolean allowUnknownChildElements =
    753                    "true".equalsIgnoreCase(attributes.getValue("allow-unknown-child-elements"));
    754                    SetNestedPropertiesRule snpr = new SetNestedPropertiesRule();
    755                    snpr.setAllowUnknownChildElements( allowUnknownChildElements );
    756                    return snpr;
    757            }
    758        }
    759
    760        /**
    761         * Factory for creating a SetTopRuleFactory
    762         */
    763        protected class SetTopRuleFactory extends AbstractObjectCreationFactory {
    764            public Object createObject(Attributes attributes) {
    765                String methodName = attributes.getValue("methodname");
    766                String paramType = attributes.getValue("paramtype");
    767                return (paramType == null || paramType.length() == 0) ?
    768                    new SetTopRule( methodName) :
    769                    new SetTopRule( methodName, paramType);
    770            }
    771        }
    772
    773        /**
    774         * Factory for creating a SetNextRuleFactory
    775         */
    776        protected class SetNextRuleFactory extends AbstractObjectCreationFactory {
    777            public Object createObject(Attributes attributes) {
    778                String methodName = attributes.getValue("methodname");
    779                String paramType = attributes.getValue("paramtype");
    780                return (paramType == null || paramType.length() == 0) ?
    781                    new SetNextRule( methodName) :
    782                    new SetNextRule( methodName, paramType);
    783            }
    784        }
    785
    786        /**
    787         * Factory for creating a SetRootRuleFactory
    788         */
    789        protected class SetRootRuleFactory extends AbstractObjectCreationFactory {
    790            public Object createObject(Attributes attributes) {
    791                String methodName = attributes.getValue("methodname");
    792                String paramType = attributes.getValue("paramtype");
    793                return (paramType == null || paramType.length() == 0) ?
    794                    new SetRootRule( methodName) :
    795                    new SetRootRule( methodName, paramType);
    796            }
    797        }
    798
    799        /**
    800         * A rule for adding a attribute-property alias to the custom alias mappings of
    801         * the containing SetPropertiesRule rule.
    802         */
    803        protected class SetPropertiesAliasRule extends Rule {
    804
    805            /**
    806             * <p>Base constructor.</p>
    807             */
    808            public SetPropertiesAliasRule() {
    809                super();
    810            }
    811
    812            /**
    813             * Add the alias to the SetPropertiesRule object created by the
    814             * enclosing <set-properties-rule> tag.
    815             */
    816            public void begin(Attributes attributes) {
    817                String attrName = attributes.getValue("attr-name");
    818                String propName = attributes.getValue("prop-name");
    819
    820                SetPropertiesRule rule = (SetPropertiesRule) digester.peek();
    821                rule.addAlias(attrName, propName);
    822            }
    823        }
    824
    825        /**
    826         * A rule for adding a attribute-property alias to the custom alias mappings of
    827         * the containing SetNestedPropertiesRule rule.
    828         */
    829        protected class SetNestedPropertiesAliasRule extends Rule {
    830
    831            /**
    832             * <p>Base constructor.</p>
    833             */
    834            public SetNestedPropertiesAliasRule() {
    835                super();
    836            }
    837
    838            /**
    839             * Add the alias to the SetNestedPropertiesRule object created by the
    840             * enclosing <set-nested-properties-rule> tag.
    841             */
    842            public void begin(Attributes attributes) {
    843                String attrName = attributes.getValue("attr-name");
    844                String propName = attributes.getValue("prop-name");
    845
    846                SetNestedPropertiesRule rule = (SetNestedPropertiesRule) digester.peek();
    847                rule.addAlias(attrName, propName);
    848            }
    849        }
    850
    851    }