001 /* $Id: DigesterRuleParser.java 471661 2006-11-06 08:09:25Z skitching $
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.StringTokenizer;
031
032 import org.apache.commons.beanutils.ConvertUtils;
033
034 import org.apache.commons.collections.ArrayStack;
035
036 import org.apache.commons.digester.AbstractObjectCreationFactory;
037 import org.apache.commons.digester.BeanPropertySetterRule;
038 import org.apache.commons.digester.CallMethodRule;
039 import org.apache.commons.digester.CallParamRule;
040 import org.apache.commons.digester.Digester;
041 import org.apache.commons.digester.FactoryCreateRule;
042 import org.apache.commons.digester.NodeCreateRule;
043 import org.apache.commons.digester.ObjectCreateRule;
044 import org.apache.commons.digester.Rule;
045 import org.apache.commons.digester.RuleSetBase;
046 import org.apache.commons.digester.Rules;
047 import org.apache.commons.digester.SetNestedPropertiesRule;
048 import org.apache.commons.digester.SetNextRule;
049 import org.apache.commons.digester.SetPropertiesRule;
050 import org.apache.commons.digester.SetPropertyRule;
051 import org.apache.commons.digester.SetRootRule;
052 import org.apache.commons.digester.SetTopRule;
053 import org.apache.commons.digester.ObjectParamRule;
054
055 import org.w3c.dom.Node;
056 import org.xml.sax.Attributes;
057 import org.xml.sax.SAXException;
058
059
060 /**
061 * This is a RuleSet that parses XML into Digester rules, and then
062 * adds those rules to a 'target' Digester.
063 *
064 * @since 1.2
065 */
066
067 public class DigesterRuleParser extends RuleSetBase {
068
069 public static final String DIGESTER_PUBLIC_ID = "-//Jakarta Apache //DTD digester-rules XML V1.0//EN";
070
071 /**
072 * path to the DTD
073 */
074 private String digesterDtdUrl;
075
076 /**
077 * This is the digester to which we are adding the rules that we parse
078 * from the Rules XML document.
079 */
080 protected Digester targetDigester;
081
082 /** See {@link #setBasePath}. */
083 protected String basePath = "";
084
085 /**
086 * A stack whose toString method returns a '/'-separated concatenation
087 * of all the elements in the stack.
088 */
089 protected class PatternStack extends ArrayStack {
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 patternStack;
112
113 /**
114 * Used to detect circular includes
115 */
116 private Set includedFiles = new HashSet();
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();
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();
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 stack, Set 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 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 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 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 paramTypesList = new ArrayList();
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 }