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    }