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
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 @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 }