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