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