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 }