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 }