From 8e2b7968359ce156f7c412a3697053244e3d538f Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Fri, 3 Nov 2023 10:09:40 -0400 Subject: [PATCH] Sort members --- .../commons/digester3/AbstractMethodRule.java | 146 +- .../commons/digester3/AbstractRulesImpl.java | 58 +- .../digester3/BeanPropertySetterRule.java | 92 +- .../commons/digester3/CallMethodRule.java | 286 +- .../commons/digester3/CallParamRule.java | 90 +- .../DefaultThrowingErrorHandler.java | 4 +- .../apache/commons/digester3/Digester.java | 3828 ++++++++--------- .../commons/digester3/ExtendedBaseRules.java | 76 +- .../commons/digester3/FactoryCreateRule.java | 180 +- .../commons/digester3/NodeCreateRule.java | 76 +- .../commons/digester3/ObjectCreateRule.java | 198 +- .../commons/digester3/ObjectParamRule.java | 34 +- .../commons/digester3/PathCallParamRule.java | 14 +- .../commons/digester3/PatternRuleMatcher.java | 72 +- .../commons/digester3/RecordedInvocation.java | 90 +- .../apache/commons/digester3/RegexRules.java | 88 +- .../org/apache/commons/digester3/Rule.java | 86 +- .../org/apache/commons/digester3/RuleSet.java | 14 +- .../org/apache/commons/digester3/Rules.java | 54 +- .../apache/commons/digester3/RulesBase.java | 118 +- .../digester3/SetNestedPropertiesRule.java | 552 +-- .../apache/commons/digester3/SetNextRule.java | 16 +- .../commons/digester3/SetPropertiesRule.java | 80 +- .../commons/digester3/SetPropertyRule.java | 24 +- .../apache/commons/digester3/SetRootRule.java | 16 +- .../apache/commons/digester3/SetTopRule.java | 16 +- .../commons/digester3/SimpleRegexMatcher.java | 26 +- .../apache/commons/digester3/StackAction.java | 26 +- .../digester3/WithDefaultsRulesWrapper.java | 96 +- .../digester3/annotations/DigesterRule.java | 10 +- .../FromAnnotationsRuleModule.java | 194 +- .../annotations/WithMemoryRulesBinder.java | 32 +- .../handlers/AbstractMethodHandler.java | 124 +- .../annotations/reflect/MethodArgument.java | 48 +- .../annotations/rules/BeanPropertySetter.java | 24 +- .../annotations/rules/CallMethod.java | 30 +- .../annotations/rules/CallParam.java | 40 +- .../annotations/rules/FactoryCreate.java | 76 +- .../annotations/rules/ObjectCreate.java | 34 +- .../annotations/rules/PathCallParam.java | 24 +- .../digester3/annotations/rules/SetNext.java | 8 +- .../annotations/rules/SetProperty.java | 34 +- .../digester3/annotations/rules/SetRoot.java | 8 +- .../digester3/annotations/rules/SetTop.java | 26 +- .../annotations/utils/AnnotationUtils.java | 74 +- .../AbstractBackToLinkedRuleBuilder.java | 44 +- .../binder/AbstractParamTypeBuilder.java | 88 +- .../digester3/binder/AbstractRulesModule.java | 66 +- .../binder/BeanPropertySetterBuilder.java | 26 +- .../digester3/binder/BinderClassLoader.java | 18 +- .../digester3/binder/CallMethodBuilder.java | 148 +- .../digester3/binder/CallParamBuilder.java | 62 +- .../digester3/binder/DefaultRulesBinder.java | 76 +- .../digester3/binder/DigesterLoader.java | 626 +-- .../binder/DigesterLoadingException.java | 14 +- .../digester3/binder/ErrorMessage.java | 16 +- .../binder/FactoryCreateBuilder.java | 102 +- .../digester3/binder/FromBinderRuleSet.java | 254 +- .../digester3/binder/LinkedRuleBuilder.java | 206 +- .../binder/NestedPropertiesBuilder.java | 74 +- .../binder/NodeCreateRuleProvider.java | 130 +- .../digester3/binder/ObjectCreateBuilder.java | 112 +- .../digester3/binder/ObjectParamBuilder.java | 32 +- .../binder/PathCallParamBuilder.java | 18 +- .../binder/PluginCreateRuleBuilder.java | 136 +- .../commons/digester3/binder/RulesBinder.java | 28 +- .../binder/SetPropertiesBuilder.java | 44 +- .../digester3/binder/SetPropertyBuilder.java | 18 +- .../digester3/plugins/Declaration.java | 124 +- .../plugins/PluginAssertionFailure.java | 20 +- .../plugins/PluginConfigurationException.java | 20 +- .../digester3/plugins/PluginContext.java | 116 +- .../digester3/plugins/PluginCreateRule.java | 384 +- .../plugins/PluginDeclarationRule.java | 82 +- .../digester3/plugins/PluginException.java | 18 +- .../plugins/PluginInvalidInputException.java | 20 +- .../digester3/plugins/PluginManager.java | 72 +- .../digester3/plugins/PluginRules.java | 336 +- .../strategies/FinderFromResource.java | 72 +- .../plugins/strategies/LoaderFromClass.java | 36 +- .../substitution/VariableAttributes.java | 154 +- .../xmlrules/FromXmlRulesModule.java | 64 +- .../xmlrules/NameSpaceURIRulesBinder.java | 38 +- .../xmlrules/PrefixedRulesBinder.java | 28 +- .../xmlrules/WithMemoryRulesBinder.java | 30 +- .../org/apache/commons/digester3/Address.java | 60 +- .../apache/commons/digester3/AlphaBean.java | 24 +- .../digester3/AsyncReaderTestCase.java | 20 +- .../BeanPropertySetterRuleTestCase.java | 152 +- .../apache/commons/digester3/BetaBean.java | 24 +- .../org/apache/commons/digester3/Box.java | 48 +- .../digester3/CallMethodRuleTestCase.java | 854 ++-- .../digester3/DTDValidationTestCase.java | 38 +- .../digester3/Digester153TestCase.java | 92 +- .../digester3/Digester171TestCase.java | 14 +- .../commons/digester3/DigesterTestCase.java | 674 +-- .../apache/commons/digester3/Employee.java | 62 +- .../commons/digester3/ErrorHandlerTest.java | 84 +- .../digester3/ExtendedBaseRulesTestCase.java | 256 +- .../digester3/LocationTrackerTestCase.java | 12 +- .../digester3/NamespaceSnapshotTestCase.java | 54 +- .../digester3/NodeCreateRuleTestCase.java | 488 +-- .../apache/commons/digester3/ParamBean.java | 16 +- .../commons/digester3/RegexRulesTestCase.java | 84 +- .../commons/digester3/RuleTestCase.java | 210 +- .../commons/digester3/RulesBaseTestCase.java | 182 +- .../SetNestedPropertiesRuleTestCase.java | 156 +- .../digester3/SetPropertiesRuleTestCase.java | 62 +- .../digester3/SetPropertyRuleTestCase.java | 46 +- .../commons/digester3/SimpleTestBean.java | 32 +- .../apache/commons/digester3/TestBean.java | 300 +- .../digester3/TestEntityResolution.java | 18 +- .../commons/digester3/TestFactoryCreate.java | 94 +- .../apache/commons/digester3/TestRule.java | 86 +- .../apache/commons/digester3/TestRuleSet.java | 14 +- .../apache/commons/digester3/URLTestCase.java | 52 +- .../WithDefaultsRulesWrapperTestCase.java | 34 +- .../commons/digester3/XIncludeTestCase.java | 32 +- .../commons/digester3/XMLSchemaTestCase.java | 122 +- .../AbstractAnnotatedPojoTestCase.java | 8 +- .../annotations/addressbook/Address.java | 120 +- .../annotations/addressbook/Person.java | 86 +- .../annotations/catalog/AudioVisual.java | 100 +- .../digester3/annotations/catalog/Book.java | 76 +- .../annotations/catalog/Catalog.java | 16 +- .../annotations/catalog/CatalogTestCase.java | 34 +- .../annotations/employee/Address.java | 112 +- .../annotations/employee/Employee.java | 50 +- .../employee/EmployeeTestCase.java | 34 +- .../failingtests/BeanWithFakeHandler.java | 8 +- .../digester3/annotations/person/Person.java | 70 +- .../digester3/annotations/rss/Channel.java | 112 +- .../digester3/annotations/rss/Image.java | 120 +- .../digester3/annotations/rss/Item.java | 60 +- .../annotations/servletbean/ServletBean.java | 50 +- .../binder/BinderClassLoaderTestCase.java | 356 +- .../digester3/binder/Digester163TestCase.java | 22 +- .../binder/DigesterLoaderTestCase.java | 70 +- .../digester3/plugins/ObjectTestImpl.java | 8 +- .../commons/digester3/plugins/Slider.java | 44 +- .../TestConfigurablePluginAttributes.java | 66 +- .../digester3/plugins/TestDelegate.java | 16 +- .../digester3/plugins/TestRecursion.java | 30 +- .../digester3/plugins/TestRuleInfo.java | 30 +- .../digester3/plugins/TestXmlRuleInfo.java | 28 +- .../commons/digester3/plugins/TextLabel.java | 8 +- .../commons/digester3/plugins/TextLabel2.java | 8 +- .../digester3/plugins/TextLabel2RuleInfo.java | 14 +- .../CompoundSubstitutorTestCase.java | 72 +- .../VariableExpansionTestCase.java | 214 +- .../xmlrules/CallParamTestObject.java | 30 +- .../commons/digester3/xmlrules/Entry.java | 32 +- .../commons/digester3/xmlrules/Feed.java | 48 +- .../xmlrules/FromXmlRuleSetTest.java | 308 +- .../digester3/xmlrules/IncludeTest.java | 40 +- .../digester3/xmlrules/ObjectTestImpl.java | 54 +- .../digester3/annotations/atom/Entry.java | 32 +- .../digester3/annotations/atom/Feed.java | 52 +- .../examples/api/addressbook/Address.java | 142 +- .../examples/api/addressbook/Main.java | 100 +- .../examples/api/addressbook/Person.java | 40 +- .../examples/api/catalog/AudioVisual.java | 32 +- .../digester3/examples/api/catalog/Book.java | 18 +- .../digester3/examples/api/catalog/Main.java | 98 +- .../digester3/examples/api/dbinsert/Main.java | 102 +- .../examples/api/dbinsert/Table.java | 8 +- .../examples/api/documentmarkup/Main.java | 48 +- .../api/documentmarkup/MarkupDigester.java | 68 +- .../documentmarkup/SetTextSegmentRule.java | 14 +- .../commons/digester3/edsl/atom/Entry.java | 32 +- .../commons/digester3/edsl/atom/Feed.java | 48 +- .../plugins/pipeline/CompoundTransform.java | 14 +- .../examples/plugins/pipeline/Pipeline.java | 36 +- .../plugins/pipeline/SubstituteTransform.java | 12 +- .../apache/commons/digester3/rss/Channel.java | 366 +- .../apache/commons/digester3/rss/Image.java | 106 +- .../apache/commons/digester3/rss/Item.java | 50 +- .../commons/digester3/rss/RSSDigester.java | 244 +- .../commons/digester3/rss/TextInput.java | 70 +- .../xmlrules/addressbook/Address.java | 142 +- .../examples/xmlrules/addressbook/Person.java | 40 +- 181 files changed, 10079 insertions(+), 10079 deletions(-) diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/AbstractMethodRule.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/AbstractMethodRule.java index ad5948bed..6e9b44beb 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/AbstractMethodRule.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/AbstractMethodRule.java @@ -100,73 +100,6 @@ public AbstractMethodRule( final String methodName, final String paramTypeName ) this.paramTypeName = paramTypeName; } - /** - *

- * Is exact matching being used. - *

- *

- * This rule uses {@code org.apache.commons.beanutils.MethodUtils} to introspect the relevent objects so that - * the right method can be called. Originally, {@code MethodUtils.invokeExactMethod} was used. This matches - * methods very strictly and so may not find a matching method when one exists. This is still the behavior when - * exact matching is enabled. - *

- *

- * When exact matching is disabled, {@code MethodUtils.invokeMethod} is used. This method finds more methods - * but is less precise when there are several methods with correct signatures. So, if you want to choose an exact - * signature you might need to enable this property. - *

- *

- * The default setting is to disable exact matches. - *

- * - * @return true if exact matching is enabled - * @since 1.1.1 - */ - public boolean isExactMatch() - { - return useExactMatch; - } - - /** - * Sets this rule be invoked when {@link #begin(String, String, Attributes)} (true) - * or {@link #end(String, String)} (false) methods are invoked, false by default. - * - * @param fireOnBegin flag to mark this rule be invoked when {@link #begin(String, String, Attributes)} (true) - * or {@link #end(String, String)} (false) methods are invoked, false by default. - */ - public void setFireOnBegin( final boolean fireOnBegin ) - { - this.fireOnBegin = fireOnBegin; - } - - /** - * Returns the flag this rule be invoked when {@link #begin(String, String, Attributes)} (true) - * or {@link #end(String, String)} (false) methods are invoked, false by default. - * - * @return the flag this rule be invoked when {@link #begin(String, String, Attributes)} (true) - * or {@link #end(String, String)} (false) methods are invoked, false by default. - */ - public boolean isFireOnBegin() - { - return fireOnBegin; - } - - /** - *

- * Sets whether exact matching is enabled. - *

- *

- * See {@link #isExactMatch()}. - *

- * - * @param useExactMatch should this rule use exact method matching - * @since 1.1.1 - */ - public void setExactMatch( final boolean useExactMatch ) - { - this.useExactMatch = useExactMatch; - } - /** * {@inheritDoc} */ @@ -193,6 +126,20 @@ public void end( final String namespace, final String name ) } } + /** + * Returns the argument object of method has to be invoked. + * + * @return the argument object of method has to be invoked. + */ + protected abstract Object getChild(); + + /** + * Returns the target object of method has to be invoked. + * + * @return the target object of method has to be invoked. + */ + protected abstract Object getParent(); + /** * Just performs the method execution. * @@ -247,18 +194,71 @@ private void invoke() } /** - * Returns the argument object of method has to be invoked. + *

+ * Is exact matching being used. + *

+ *

+ * This rule uses {@code org.apache.commons.beanutils.MethodUtils} to introspect the relevent objects so that + * the right method can be called. Originally, {@code MethodUtils.invokeExactMethod} was used. This matches + * methods very strictly and so may not find a matching method when one exists. This is still the behavior when + * exact matching is enabled. + *

+ *

+ * When exact matching is disabled, {@code MethodUtils.invokeMethod} is used. This method finds more methods + * but is less precise when there are several methods with correct signatures. So, if you want to choose an exact + * signature you might need to enable this property. + *

+ *

+ * The default setting is to disable exact matches. + *

* - * @return the argument object of method has to be invoked. + * @return true if exact matching is enabled + * @since 1.1.1 */ - protected abstract Object getChild(); + public boolean isExactMatch() + { + return useExactMatch; + } /** - * Returns the target object of method has to be invoked. + * Returns the flag this rule be invoked when {@link #begin(String, String, Attributes)} (true) + * or {@link #end(String, String)} (false) methods are invoked, false by default. * - * @return the target object of method has to be invoked. + * @return the flag this rule be invoked when {@link #begin(String, String, Attributes)} (true) + * or {@link #end(String, String)} (false) methods are invoked, false by default. */ - protected abstract Object getParent(); + public boolean isFireOnBegin() + { + return fireOnBegin; + } + + /** + *

+ * Sets whether exact matching is enabled. + *

+ *

+ * See {@link #isExactMatch()}. + *

+ * + * @param useExactMatch should this rule use exact method matching + * @since 1.1.1 + */ + public void setExactMatch( final boolean useExactMatch ) + { + this.useExactMatch = useExactMatch; + } + + /** + * Sets this rule be invoked when {@link #begin(String, String, Attributes)} (true) + * or {@link #end(String, String)} (false) methods are invoked, false by default. + * + * @param fireOnBegin flag to mark this rule be invoked when {@link #begin(String, String, Attributes)} (true) + * or {@link #end(String, String)} (false) methods are invoked, false by default. + */ + public void setFireOnBegin( final boolean fireOnBegin ) + { + this.fireOnBegin = fireOnBegin; + } /** * {@inheritDoc} diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/AbstractRulesImpl.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/AbstractRulesImpl.java index 543115e7c..7a702ef7c 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/AbstractRulesImpl.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/AbstractRulesImpl.java @@ -52,18 +52,29 @@ public abstract class AbstractRulesImpl * {@inheritDoc} */ @Override - public Digester getDigester() + public final void add( final String pattern, final Rule rule ) { - return digester; + // set up rule + if ( this.digester != null ) + { + rule.setDigester( this.digester ); + } + + if ( this.namespaceURI != null ) + { + rule.setNamespaceURI( this.namespaceURI ); + } + + registerRule( pattern, rule ); } /** * {@inheritDoc} */ @Override - public void setDigester( final Digester digester ) + public Digester getDigester() { - this.digester = digester; + return digester; } /** @@ -76,13 +87,13 @@ public String getNamespaceURI() } /** - * {@inheritDoc} + * Register rule at given pattern. The the Digester and namespaceURI properties of the given {@code Rule} can + * be assumed to have been set properly before this method is called. + * + * @param pattern Nesting pattern to be matched for this Rule + * @param rule Rule instance to be registered */ - @Override - public void setNamespaceURI( final String namespaceURI ) - { - this.namespaceURI = namespaceURI; - } + protected abstract void registerRule( String pattern, Rule rule ); // --------------------------------------------------------- Public Methods @@ -90,29 +101,18 @@ public void setNamespaceURI( final String namespaceURI ) * {@inheritDoc} */ @Override - public final void add( final String pattern, final Rule rule ) + public void setDigester( final Digester digester ) { - // set up rule - if ( this.digester != null ) - { - rule.setDigester( this.digester ); - } - - if ( this.namespaceURI != null ) - { - rule.setNamespaceURI( this.namespaceURI ); - } - - registerRule( pattern, rule ); + this.digester = digester; } /** - * Register rule at given pattern. The the Digester and namespaceURI properties of the given {@code Rule} can - * be assumed to have been set properly before this method is called. - * - * @param pattern Nesting pattern to be matched for this Rule - * @param rule Rule instance to be registered + * {@inheritDoc} */ - protected abstract void registerRule( String pattern, Rule rule ); + @Override + public void setNamespaceURI( final String namespaceURI ) + { + this.namespaceURI = namespaceURI; + } } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/BeanPropertySetterRule.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/BeanPropertySetterRule.java index 8f060a5f9..cec632576 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/BeanPropertySetterRule.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/BeanPropertySetterRule.java @@ -51,31 +51,6 @@ public class BeanPropertySetterRule // ----------------------------------------------------------- Constructors - /** - *

- * Construct rule that sets the given property from the body text. - *

- * - * @param propertyName name of property to set - */ - public BeanPropertySetterRule( final String propertyName ) - { - this.propertyName = propertyName; - } - - /** - *

- * Construct rule that automatically sets a property from the body text. - *

- * This construct creates a rule that sets the property on the top object named the same as the current element. - */ - public BeanPropertySetterRule() - { - this( null ); - } - - // ----------------------------------------------------- Instance Variables - /** * Sets this property on the top object. */ @@ -86,43 +61,37 @@ public BeanPropertySetterRule() */ private String propertyNameFromAttribute; + // ----------------------------------------------------- Instance Variables + /** * The body text used to set the property. */ private String bodyText; - // --------------------------------------------------------- Public Methods - /** - * Returns the property name associated to this setter rule. - * - * @return The property name associated to this setter rule + *

+ * Construct rule that automatically sets a property from the body text. + *

+ * This construct creates a rule that sets the property on the top object named the same as the current element. */ - public String getPropertyName() + public BeanPropertySetterRule() { - return propertyName; + this( null ); } /** - * Sets the attribute name from which the property name has to be extracted. + *

+ * Construct rule that sets the given property from the body text. + *

* - * @param propertyNameFromAttribute the attribute name from which the property name has to be extracted. - * @since 3.0 + * @param propertyName name of property to set */ - public void setPropertyNameFromAttribute( final String propertyNameFromAttribute ) + public BeanPropertySetterRule( final String propertyName ) { - this.propertyNameFromAttribute = propertyNameFromAttribute; + this.propertyName = propertyName; } - /** - * Returns the body text used to set the property. - * - * @return The body text used to set the property - */ - protected String getBodyText() - { - return bodyText; - } + // --------------------------------------------------------- Public Methods /** * {@inheritDoc} @@ -221,6 +190,37 @@ public void finish() bodyText = null; } + /** + * Returns the body text used to set the property. + * + * @return The body text used to set the property + */ + protected String getBodyText() + { + return bodyText; + } + + /** + * Returns the property name associated to this setter rule. + * + * @return The property name associated to this setter rule + */ + public String getPropertyName() + { + return propertyName; + } + + /** + * Sets the attribute name from which the property name has to be extracted. + * + * @param propertyNameFromAttribute the attribute name from which the property name has to be extracted. + * @since 3.0 + */ + public void setPropertyNameFromAttribute( final String propertyNameFromAttribute ) + { + this.propertyNameFromAttribute = propertyNameFromAttribute; + } + /** * {@inheritDoc} */ diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/CallMethodRule.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/CallMethodRule.java index 138440bb0..606f7083a 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/CallMethodRule.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/CallMethodRule.java @@ -89,18 +89,58 @@ public class CallMethodRule // ----------------------------------------------------------- Constructors /** - * Construct a "call method" rule with the specified method name. The parameter types (if any) default to - * java.lang.String. + * The body text collected from this element. + */ + protected String bodyText; + + /** + * location of the target object for the call, relative to the top of the digester object stack. The default value + * of zero means the target object is the one on top of the stack. + */ + protected int targetOffset; + + /** + * The method name to call on the parent object. + */ + protected String methodName; + + /** + * The number of parameters to collect from {@code MethodParam} rules. If this value is zero, a single + * parameter will be collected from the body of this element. + */ + protected int paramCount; + + /** + * The parameter types of the parameters to be collected. + */ + protected Class[] paramTypes = null; + + /** + * The names of the classes of the parameters to be collected. This attribute allows creation of the classes to be + * postponed until the digester is set. + */ + private String[] paramClassNames = null; + + /** + * Should {@code MethodUtils.invokeExactMethod} be used for reflection. + */ + private boolean useExactMatch; + + /** + * Construct a "call method" rule with the specified method name. The method should accept no parameters. * + * @param targetOffset location of the target object. Positive numbers are relative to the top of the digester + * object stack. Negative numbers are relative to the bottom of the stack. Zero implies the top object on + * the stack. * @param methodName Method name of the parent method to call - * @param paramCount The number of parameters to collect, or zero for a single argument from the body of this - * element. */ - public CallMethodRule( final String methodName, final int paramCount ) + public CallMethodRule( final int targetOffset, final String methodName ) { - this( 0, methodName, paramCount ); + this( targetOffset, methodName, 0, (Class[]) null ); } + // ----------------------------------------------------- Instance Variables + /** * Construct a "call method" rule with the specified method name. The parameter types (if any) default to * java.lang.String. @@ -128,43 +168,34 @@ public CallMethodRule( final int targetOffset, final String methodName, final in } } - /** - * Construct a "call method" rule with the specified method name. The method should accept no parameters. - * - * @param methodName Method name of the parent method to call - */ - public CallMethodRule( final String methodName ) - { - this( 0, methodName, 0, (Class[]) null ); - } - - /** - * Construct a "call method" rule with the specified method name. The method should accept no parameters. - * - * @param targetOffset location of the target object. Positive numbers are relative to the top of the digester - * object stack. Negative numbers are relative to the bottom of the stack. Zero implies the top object on - * the stack. - * @param methodName Method name of the parent method to call - */ - public CallMethodRule( final int targetOffset, final String methodName ) - { - this( targetOffset, methodName, 0, (Class[]) null ); - } - /** * Construct a "call method" rule with the specified method name and parameter types. If {@code paramCount} is * set to zero the rule will use the body of this element as the single argument of the method, unless * {@code paramTypes} is null or empty, in this case the rule will call the specified method with no arguments. * + * @param targetOffset location of the target object. Positive numbers are relative to the top of the digester + * object stack. Negative numbers are relative to the bottom of the stack. Zero implies the top object on + * the stack. * @param methodName Method name of the parent method to call - * @param paramCount The number of parameters to collect, or zero for a single argument from the body of ths element - * @param paramTypes The Java class names of the arguments (if you wish to use a primitive type, specify the - * corresonding Java wrapper class instead, such as {@code java.lang.Boolean} for a - * {@code boolean} parameter) + * @param paramCount The number of parameters to collect, or zero for a single argument from the body of the element + * @param paramTypes The Java classes that represent the parameter types of the method arguments (if you wish to use + * a primitive type, specify the corresponding Java wrapper class instead, such as + * {@code java.lang.Boolean.TYPE} for a {@code boolean} parameter) */ - public CallMethodRule( final String methodName, final int paramCount, final String[] paramTypes ) + public CallMethodRule( final int targetOffset, final String methodName, final int paramCount, final Class[] paramTypes ) { - this( 0, methodName, paramCount, paramTypes ); + this.targetOffset = targetOffset; + this.methodName = methodName; + this.paramCount = paramCount; + if ( paramTypes == null ) + { + this.paramTypes = new Class[paramCount]; + fill( this.paramTypes, String.class ); + } + else + { + this.paramTypes = paramTypes.clone(); + } } /** @@ -200,19 +231,26 @@ public CallMethodRule( final int targetOffset, final String methodName, final in } /** - * Construct a "call method" rule with the specified method name and parameter types. If {@code paramCount} is - * set to zero the rule will use the body of this element as the single argument of the method, unless - * {@code paramTypes} is null or empty, in this case the rule will call the specified method with no arguments. + * Construct a "call method" rule with the specified method name. The method should accept no parameters. * * @param methodName Method name of the parent method to call - * @param paramCount The number of parameters to collect, or zero for a single argument from the body of the element - * @param paramTypes The Java classes that represent the parameter types of the method arguments (if you wish to use - * a primitive type, specify the corresponding Java wrapper class instead, such as - * {@code java.lang.Boolean.TYPE} for a {@code boolean} parameter) */ - public CallMethodRule( final String methodName, final int paramCount, final Class paramTypes[] ) + public CallMethodRule( final String methodName ) { - this( 0, methodName, paramCount, paramTypes ); + this( 0, methodName, 0, (Class[]) null ); + } + + /** + * Construct a "call method" rule with the specified method name. The parameter types (if any) default to + * java.lang.String. + * + * @param methodName Method name of the parent method to call + * @param paramCount The number of parameters to collect, or zero for a single argument from the body of this + * element. + */ + public CallMethodRule( final String methodName, final int paramCount ) + { + this( 0, methodName, paramCount ); } /** @@ -220,120 +258,34 @@ public CallMethodRule( final String methodName, final int paramCount, final Clas * set to zero the rule will use the body of this element as the single argument of the method, unless * {@code paramTypes} is null or empty, in this case the rule will call the specified method with no arguments. * - * @param targetOffset location of the target object. Positive numbers are relative to the top of the digester - * object stack. Negative numbers are relative to the bottom of the stack. Zero implies the top object on - * the stack. * @param methodName Method name of the parent method to call * @param paramCount The number of parameters to collect, or zero for a single argument from the body of the element * @param paramTypes The Java classes that represent the parameter types of the method arguments (if you wish to use * a primitive type, specify the corresponding Java wrapper class instead, such as * {@code java.lang.Boolean.TYPE} for a {@code boolean} parameter) */ - public CallMethodRule( final int targetOffset, final String methodName, final int paramCount, final Class[] paramTypes ) - { - this.targetOffset = targetOffset; - this.methodName = methodName; - this.paramCount = paramCount; - if ( paramTypes == null ) - { - this.paramTypes = new Class[paramCount]; - fill( this.paramTypes, String.class ); - } - else - { - this.paramTypes = paramTypes.clone(); - } - } - - // ----------------------------------------------------- Instance Variables - - /** - * The body text collected from this element. - */ - protected String bodyText; - - /** - * location of the target object for the call, relative to the top of the digester object stack. The default value - * of zero means the target object is the one on top of the stack. - */ - protected int targetOffset; - - /** - * The method name to call on the parent object. - */ - protected String methodName; - - /** - * The number of parameters to collect from {@code MethodParam} rules. If this value is zero, a single - * parameter will be collected from the body of this element. - */ - protected int paramCount; - - /** - * The parameter types of the parameters to be collected. - */ - protected Class[] paramTypes = null; - - /** - * The names of the classes of the parameters to be collected. This attribute allows creation of the classes to be - * postponed until the digester is set. - */ - private String[] paramClassNames = null; - - /** - * Should {@code MethodUtils.invokeExactMethod} be used for reflection. - */ - private boolean useExactMatch; - - // --------------------------------------------------------- Public Methods - - /** - * Should {@code MethodUtils.invokeExactMethod} be used for the reflection. - * - * @return true, if {@code MethodUtils.invokeExactMethod} Should be used for the reflection, - * false otherwise - */ - public boolean getUseExactMatch() + public CallMethodRule( final String methodName, final int paramCount, final Class paramTypes[] ) { - return useExactMatch; + this( 0, methodName, paramCount, paramTypes ); } /** - * Sets whether {@code MethodUtils.invokeExactMethod} should be used for the reflection. + * Construct a "call method" rule with the specified method name and parameter types. If {@code paramCount} is + * set to zero the rule will use the body of this element as the single argument of the method, unless + * {@code paramTypes} is null or empty, in this case the rule will call the specified method with no arguments. * - * @param useExactMatch The {@code MethodUtils.invokeExactMethod} flag + * @param methodName Method name of the parent method to call + * @param paramCount The number of parameters to collect, or zero for a single argument from the body of ths element + * @param paramTypes The Java class names of the arguments (if you wish to use a primitive type, specify the + * corresonding Java wrapper class instead, such as {@code java.lang.Boolean} for a + * {@code boolean} parameter) */ - public void setUseExactMatch( final boolean useExactMatch ) + public CallMethodRule( final String methodName, final int paramCount, final String[] paramTypes ) { - this.useExactMatch = useExactMatch; + this( 0, methodName, paramCount, paramTypes ); } - /** - * {@inheritDoc} - */ - @Override - public void setDigester( final Digester digester ) - { - // call superclass - super.setDigester( digester ); - // if necessary, load parameter classes - if ( this.paramClassNames != null ) - { - this.paramTypes = new Class[paramClassNames.length]; - for ( int i = 0; i < this.paramClassNames.length; i++ ) - { - try - { - this.paramTypes[i] = digester.getClassLoader().loadClass( this.paramClassNames[i] ); - } - catch ( final ClassNotFoundException e ) - { - throw new IllegalArgumentException( format( "[CallMethodRule] Cannot load class %s at position %s", - this.paramClassNames[i], i ), e ); - } - } - } - } + // --------------------------------------------------------- Public Methods /** * {@inheritDoc} @@ -510,6 +462,17 @@ public void finish() bodyText = null; } + /** + * Should {@code MethodUtils.invokeExactMethod} be used for the reflection. + * + * @return true, if {@code MethodUtils.invokeExactMethod} Should be used for the reflection, + * false otherwise + */ + public boolean getUseExactMatch() + { + return useExactMatch; + } + /** * Subclasses may override this method to perform additional processing of the invoked method's result. * @@ -520,6 +483,43 @@ protected void processMethodCallResult( final Object result ) // do nothing } + /** + * {@inheritDoc} + */ + @Override + public void setDigester( final Digester digester ) + { + // call superclass + super.setDigester( digester ); + // if necessary, load parameter classes + if ( this.paramClassNames != null ) + { + this.paramTypes = new Class[paramClassNames.length]; + for ( int i = 0; i < this.paramClassNames.length; i++ ) + { + try + { + this.paramTypes[i] = digester.getClassLoader().loadClass( this.paramClassNames[i] ); + } + catch ( final ClassNotFoundException e ) + { + throw new IllegalArgumentException( format( "[CallMethodRule] Cannot load class %s at position %s", + this.paramClassNames[i], i ), e ); + } + } + } + } + + /** + * Sets whether {@code MethodUtils.invokeExactMethod} should be used for the reflection. + * + * @param useExactMatch The {@code MethodUtils.invokeExactMethod} flag + */ + public void setUseExactMatch( final boolean useExactMatch ) + { + this.useExactMatch = useExactMatch; + } + /** * {@inheritDoc} */ diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/CallParamRule.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/CallParamRule.java index 0314d9383..b3f3b1fd8 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/CallParamRule.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/CallParamRule.java @@ -44,6 +44,33 @@ public class CallParamRule // ----------------------------------------------------------- Constructors + /** + * The attribute from which to save the parameter value + */ + protected String attributeName; + + /** + * The zero-relative index of the parameter we are saving. + */ + protected int paramIndex; + + /** + * Is the parameter to be set from the stack? + */ + protected boolean fromStack; + + /** + * The position of the object from the top of the stack + */ + protected int stackIndex; + + // ----------------------------------------------------- Instance Variables + + /** + * Stack is used to allow nested body text to be processed. Lazy creation. + */ + protected Stack bodyTextStack; + /** * Construct a "call parameter" rule that will save the body text of this element as the parameter value. *

@@ -59,18 +86,6 @@ public CallParamRule( final int paramIndex ) this( paramIndex, null ); } - /** - * Construct a "call parameter" rule that will save the value of the specified attribute as the parameter value. - * - * @param paramIndex The zero-relative parameter number - * @param attributeName The name of the attribute to save - */ - public CallParamRule( final int paramIndex, final String attributeName ) - { - this.paramIndex = paramIndex; - this.attributeName = attributeName; - } - /** * Construct a "call parameter" rule. * @@ -98,46 +113,20 @@ public CallParamRule( final int paramIndex, final int stackIndex ) this.stackIndex = stackIndex; } - // ----------------------------------------------------- Instance Variables - - /** - * The attribute from which to save the parameter value - */ - protected String attributeName; - - /** - * The zero-relative index of the parameter we are saving. - */ - protected int paramIndex; - - /** - * Is the parameter to be set from the stack? - */ - protected boolean fromStack; - - /** - * The position of the object from the top of the stack - */ - protected int stackIndex; - - /** - * Stack is used to allow nested body text to be processed. Lazy creation. - */ - protected Stack bodyTextStack; - - // --------------------------------------------------------- Public Methods - /** - * Sets the attribute from which to save the parameter value. + * Construct a "call parameter" rule that will save the value of the specified attribute as the parameter value. * - * @param attributeName The attribute from which to save the parameter value - * @since 3.0 + * @param paramIndex The zero-relative parameter number + * @param attributeName The name of the attribute to save */ - public void setAttributeName( final String attributeName ) + public CallParamRule( final int paramIndex, final String attributeName ) { + this.paramIndex = paramIndex; this.attributeName = attributeName; } + // --------------------------------------------------------- Public Methods + /** * {@inheritDoc} */ @@ -213,6 +202,17 @@ public void end( final String namespace, final String name ) } } + /** + * Sets the attribute from which to save the parameter value. + * + * @param attributeName The attribute from which to save the parameter value + * @since 3.0 + */ + public void setAttributeName( final String attributeName ) + { + this.attributeName = attributeName; + } + /** * {@inheritDoc} */ diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/DefaultThrowingErrorHandler.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/DefaultThrowingErrorHandler.java index 17f78a8de..ff73be5c8 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/DefaultThrowingErrorHandler.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/DefaultThrowingErrorHandler.java @@ -34,7 +34,7 @@ public class DefaultThrowingErrorHandler * {@inheritDoc} */ @Override - public void warning( final SAXParseException e ) + public void error( final SAXParseException e ) throws SAXException { throw e; @@ -54,7 +54,7 @@ public void fatalError( final SAXParseException e ) * {@inheritDoc} */ @Override - public void error( final SAXParseException e ) + public void warning( final SAXParseException e ) throws SAXException { throw e; diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/Digester.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/Digester.java index 2fc141178..03a4e1cfb 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/Digester.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/Digester.java @@ -99,37 +99,9 @@ public class Digester // --------------------------------------------------------- Constructors /** - * Construct a new Digester with default properties. - */ - public Digester() - { - } - - /** - * Construct a new Digester, allowing a SAXParser to be passed in. This allows Digester to be used in environments - * which are unfriendly to JAXP1.1 (such as WebLogic 6.0). This may help in places where you are able to load JAXP - * 1.1 classes yourself. - * - * @param parser The SAXParser used to parse XML streams - */ - public Digester( final SAXParser parser ) - { - this.parser = parser; - } - - /** - * Construct a new Digester, allowing an XMLReader to be passed in. This allows Digester to be used in environments - * which are unfriendly to JAXP1.1 (such as WebLogic 6.0). Note that if you use this option you have to configure - * namespace and validation support yourself, as these properties only affect the SAXParser and emtpy constructor. - * - * @param reader The XMLReader used to parse XML streams + * The schema language supported. By default, we use this one. */ - public Digester( final XMLReader reader ) - { - this.reader = reader; - } - - // --------------------------------------------------- Instance Variables + protected static final String W3C_XML_SCHEMA = "http://www.w3.org/2001/XMLSchema"; /** * The body text of the current element. @@ -141,6 +113,8 @@ public Digester( final XMLReader reader ) */ private final Stack bodyTexts = new Stack(); + // --------------------------------------------------- Instance Variables + /** * Stack whose elements are List objects, each containing a list of Rule objects as returned from Rules.getMatch(). * As each xml element in the input is entered, the matching rules are pushed onto this stack. After the end tag is @@ -283,11 +257,6 @@ public Digester( final XMLReader reader ) */ private Log saxLog = LogFactory.getLog( "org.apache.commons.digester3.Digester.sax" ); - /** - * The schema language supported. By default, we use this one. - */ - protected static final String W3C_XML_SCHEMA = "http://www.w3.org/2001/XMLSchema"; - /** * An optional class that substitutes values in attributes and body text. This may be null and so a null check is * always required before use. @@ -311,1009 +280,1087 @@ public Digester( final XMLReader reader ) */ private StackAction stackAction; - // ------------------------------------------------------------- Properties - /** - * Return the currently mapped namespace URI for the specified prefix, if any; otherwise return {@code null}. - * These mappings come and go dynamically as the document is parsed. - * - * @param prefix Prefix to look up - * @return the currently mapped namespace URI for the specified prefix + *

+ * {@code List} of {@code InputSource} instances created by a {@code createInputSourceFromURL()} + * method call. These represent open input streams that need to be closed to avoid resource leaks, as well as + * potentially locked JAR files on Windows. + *

*/ - public String findNamespaceURI( final String prefix ) - { - final Stack nsStack = namespaces.get( prefix ); - if ( nsStack == null ) - { - return null; - } - try - { - return ( nsStack.peek() ); - } - catch ( final EmptyStackException e ) - { - return null; - } - } + protected List inputSources = new ArrayList( 5 ); /** - * Return the class loader to be used for instantiating application objects when required. This is determined based - * upon the following rules: - *
    - *
  • The class loader set by {@code setClassLoader()}, if any
  • - *
  • The thread context class loader, if it exists and the {@code useContextClassLoader} property is set to - * true
  • - *
  • The class loader used to load the Digester class itself. - *
- * - * @return the class loader to be used for instantiating application objects. + * Construct a new Digester with default properties. */ - public ClassLoader getClassLoader() + public Digester() { - if ( this.classLoader != null ) - { - return ( this.classLoader ); - } - if ( this.useContextClassLoader ) - { - final ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); - if ( classLoader != null ) - { - return ( classLoader ); - } - } - return ( this.getClass().getClassLoader() ); } /** - * Sets the class loader to be used for instantiating application objects when required. + * Construct a new Digester, allowing a SAXParser to be passed in. This allows Digester to be used in environments + * which are unfriendly to JAXP1.1 (such as WebLogic 6.0). This may help in places where you are able to load JAXP + * 1.1 classes yourself. * - * @param classLoader The new class loader to use, or {@code null} to revert to the standard rules + * @param parser The SAXParser used to parse XML streams */ - public void setClassLoader( final ClassLoader classLoader ) + public Digester( final SAXParser parser ) { - this.classLoader = classLoader; + this.parser = parser; } + // ------------------------------------------------------------- Properties + /** - * Return the current depth of the element stack. + * Construct a new Digester, allowing an XMLReader to be passed in. This allows Digester to be used in environments + * which are unfriendly to JAXP1.1 (such as WebLogic 6.0). Note that if you use this option you have to configure + * namespace and validation support yourself, as these properties only affect the SAXParser and emtpy constructor. * - * @return the current depth of the element stack. + * @param reader The XMLReader used to parse XML streams */ - public int getCount() + public Digester( final XMLReader reader ) { - return ( stack.size() ); + this.reader = reader; } /** - * Return the name of the XML element that is currently being processed. + * Add a "bean property setter" rule for the specified parameters. * - * @return the name of the XML element that is currently being processed. + * @param pattern Element matching pattern + * @see BeanPropertySetterRule */ - public String getCurrentElementName() + public void addBeanPropertySetter( final String pattern ) { - String elementName = match; - final int lastSlash = elementName.lastIndexOf( '/' ); - if ( lastSlash >= 0 ) - { - elementName = elementName.substring( lastSlash + 1 ); - } - return ( elementName ); + addRule( pattern, new BeanPropertySetterRule() ); } /** - * Return the error handler for this Digester. + * Add a "bean property setter" rule for the specified parameters. * - * @return the error handler for this Digester. + * @param pattern Element matching pattern + * @param propertyName Name of property to set + * @see BeanPropertySetterRule */ - public ErrorHandler getErrorHandler() + public void addBeanPropertySetter( final String pattern, final String propertyName ) { - return ( this.errorHandler ); + addRule( pattern, new BeanPropertySetterRule( propertyName ) ); } /** - * Sets the error handler for this Digester. + * Add an "call method" rule for a method which accepts no arguments. * - * @param errorHandler The new error handler + * @param pattern Element matching pattern + * @param methodName Method name to be called + * @see CallMethodRule */ - public void setErrorHandler( final ErrorHandler errorHandler ) + public void addCallMethod( final String pattern, final String methodName ) { - this.errorHandler = errorHandler; + addRule( pattern, new CallMethodRule( methodName ) ); } /** - * Return the SAXParserFactory we will use, creating one if necessary. + * Add an "call method" rule for the specified parameters. * - * @return the SAXParserFactory we will use, creating one if necessary. + * @param pattern Element matching pattern + * @param methodName Method name to be called + * @param paramCount Number of expected parameters (or zero for a single parameter from the body of this element) + * @see CallMethodRule */ - public SAXParserFactory getFactory() + public void addCallMethod( final String pattern, final String methodName, final int paramCount ) { - if ( factory == null ) - { - factory = SAXParserFactory.newInstance(); - factory.setNamespaceAware( namespaceAware ); - factory.setXIncludeAware( xincludeAware ); - factory.setValidating( validating ); - factory.setSchema( schema ); - } - return ( factory ); + addRule( pattern, new CallMethodRule( methodName, paramCount ) ); } /** - * Returns a flag indicating whether the requested feature is supported by the underlying implementation of - * {@code org.xml.sax.XMLReader}. See the saxproject website for - * information about the standard SAX2 feature flags. + * Add an "call method" rule for the specified parameters. If {@code paramCount} is set to zero the rule will + * use the body of the matched element as the single argument of the method, unless {@code paramTypes} is null + * or empty, in this case the rule will call the specified method with no arguments. * - * @param feature Name of the feature to inquire about - * @return true, if the requested feature is supported by the underlying implementation of - * {@code org.xml.sax.XMLReader}, false otherwise - * @throws ParserConfigurationException if a parser configuration error occurs - * @throws SAXNotRecognizedException if the property name is not recognized - * @throws SAXNotSupportedException if the property name is recognized but not supported + * @param pattern Element matching pattern + * @param methodName Method name to be called + * @param paramCount Number of expected parameters (or zero for a single parameter from the body of this element) + * @param paramTypes The Java class names of the arguments (if you wish to use a primitive type, specify the + * corresonding Java wrapper class instead, such as {@code java.lang.Boolean} for a + * {@code boolean} parameter) + * @see CallMethodRule */ - public boolean getFeature( final String feature ) - throws ParserConfigurationException, SAXNotRecognizedException, SAXNotSupportedException + public void addCallMethod( final String pattern, final String methodName, final int paramCount, final Class paramTypes[] ) { - return ( getFactory().getFeature( feature ) ); + addRule( pattern, new CallMethodRule( methodName, paramCount, paramTypes ) ); } /** - * Sets a flag indicating whether the requested feature is supported by the underlying implementation of - * {@code org.xml.sax.XMLReader}. See the saxproject website for - * information about the standard SAX2 feature flags. In order to be effective, this method must be called - * before the {@code getParser()} method is called for the first time, either directly or - * indirectly. + * Add an "call method" rule for the specified parameters. If {@code paramCount} is set to zero the rule will + * use the body of the matched element as the single argument of the method, unless {@code paramTypes} is null + * or empty, in this case the rule will call the specified method with no arguments. * - * @param feature Name of the feature to set the status for - * @param value The new value for this feature - * @throws ParserConfigurationException if a parser configuration error occurs - * @throws SAXNotRecognizedException if the property name is not recognized - * @throws SAXNotSupportedException if the property name is recognized but not supported + * @param pattern Element matching pattern + * @param methodName Method name to be called + * @param paramCount Number of expected parameters (or zero for a single parameter from the body of this element) + * @param paramTypes Set of Java class names for the types of the expected parameters (if you wish to use a + * primitive type, specify the corresonding Java wrapper class instead, such as + * {@code java.lang.Boolean} for a {@code boolean} parameter) + * @see CallMethodRule */ - public void setFeature( final String feature, final boolean value ) - throws ParserConfigurationException, SAXNotRecognizedException, SAXNotSupportedException + public void addCallMethod( final String pattern, final String methodName, final int paramCount, final String paramTypes[] ) { - getFactory().setFeature( feature, value ); + addRule( pattern, new CallMethodRule( methodName, paramCount, paramTypes ) ); } /** - * Return the current Logger associated with this instance of the Digester + * Add a "call parameter" rule for the specified parameters. * - * @return the current Logger associated with this instance of the Digester + * @param pattern Element matching pattern + * @param paramIndex Zero-relative parameter index to set (from the body of this element) + * @see CallParamRule */ - public Log getLogger() + public void addCallParam( final String pattern, final int paramIndex ) { - return log; + addRule( pattern, new CallParamRule( paramIndex ) ); } /** - * Sets the current logger for this Digester. + * Add a "call parameter" rule. This will either take a parameter from the stack or from the current element body + * text. * - * @param log the current logger for this Digester. + * @param pattern Element matching pattern + * @param paramIndex The zero-relative parameter number + * @param fromStack Should the call parameter be taken from the top of the stack? + * @see CallParamRule */ - public void setLogger( final Log log ) + public void addCallParam( final String pattern, final int paramIndex, final boolean fromStack ) { - this.log = log; + addRule( pattern, new CallParamRule( paramIndex, fromStack ) ); } /** - * Gets the logger used for logging SAX-related information. Note the output is finely grained. + * Add a "call parameter" rule that sets a parameter from the stack. This takes a parameter from the given position + * on the stack. * - * @return the logger used for logging SAX-related information - * @since 1.6 + * @param pattern Element matching pattern + * @param paramIndex The zero-relative parameter number + * @param stackIndex set the call parameter to the stackIndex'th object down the stack, where 0 is the top of the + * stack, 1 the next element down and so on + * @see CallMethodRule */ - public Log getSAXLogger() + public void addCallParam( final String pattern, final int paramIndex, final int stackIndex ) { - return saxLog; + addRule( pattern, new CallParamRule( paramIndex, stackIndex ) ); } /** - * Sets the logger used for logging SAX-related information. Note the output is finely grained. + * Add a "call parameter" rule for the specified parameters. * - * @param saxLog the logger used for logging SAX-related information, not null - * @since 1.6 + * @param pattern Element matching pattern + * @param paramIndex Zero-relative parameter index to set (from the specified attribute) + * @param attributeName Attribute whose value is used as the parameter value + * @see CallParamRule */ - public void setSAXLogger( final Log saxLog ) + public void addCallParam( final String pattern, final int paramIndex, final String attributeName ) { - this.saxLog = saxLog; + addRule( pattern, new CallParamRule( paramIndex, attributeName ) ); } /** - * Return the current rule match path + * Add a "call parameter" rule that sets a parameter from the current {@code Digester} matching path. This is + * sometimes useful when using rules that support wildcards. * - * @return the current rule match path + * @param pattern the pattern that this rule should match + * @param paramIndex The zero-relative parameter number + * @see CallMethodRule */ - public String getMatch() + public void addCallParamPath( final String pattern, final int paramIndex ) { - return match; + addRule( pattern, new PathCallParamRule( paramIndex ) ); } /** - * Return a Stack whose elements are List objects, each containing a list of - * Rule objects as returned from Rules.getMatch(). + * Add a "factory create" rule for the specified parameters. Exceptions thrown during the object creation process + * will be propagated. * - * @return a Stack whose elements are List objects, each containing a list of - * Rule objects as returned from Rules.getMatch(). - * @since 3.0 + * @param pattern Element matching pattern + * @param clazz Java class of the object creation factory class + * @see FactoryCreateRule */ - public Stack> getMatches() + public void addFactoryCreate( final String pattern, final Class> clazz ) { - return matches; + addFactoryCreate( pattern, clazz, false ); } /** - * Return the "namespace aware" flag for parsers we create. + * Add a "factory create" rule for the specified parameters. * - * @return the "namespace aware" flag for parsers we create. + * @param pattern Element matching pattern + * @param clazz Java class of the object creation factory class + * @param ignoreCreateExceptions when {@code true} any exceptions thrown during object creation will be + * ignored. + * @see FactoryCreateRule */ - public boolean getNamespaceAware() + public void addFactoryCreate( final String pattern, final Class> clazz, + final boolean ignoreCreateExceptions ) { - return ( this.namespaceAware ); + addRule( pattern, new FactoryCreateRule( clazz, ignoreCreateExceptions ) ); } /** - * Sets the "namespace aware" flag for parsers we create. + * Add a "factory create" rule for the specified parameters. Exceptions thrown during the object creation process + * will be propagated. * - * @param namespaceAware The new "namespace aware" flag + * @param pattern Element matching pattern + * @param clazz Java class of the object creation factory class + * @param attributeName Attribute name which, if present, overrides the value specified by {@code className} + * @see FactoryCreateRule */ - public void setNamespaceAware( final boolean namespaceAware ) + public void addFactoryCreate( final String pattern, final Class> clazz, + final String attributeName ) { - this.namespaceAware = namespaceAware; + addFactoryCreate( pattern, clazz, attributeName, false ); } /** - * Return the XInclude-aware flag for parsers we create. XInclude functionality additionally requires - * namespace-awareness. + * Add a "factory create" rule for the specified parameters. * - * @return The XInclude-aware flag - * @see #getNamespaceAware() - * @since 2.0 + * @param pattern Element matching pattern + * @param clazz Java class of the object creation factory class + * @param attributeName Attribute name which, if present, overrides the value specified by {@code className} + * @param ignoreCreateExceptions when {@code true} any exceptions thrown during object creation will be + * ignored. + * @see FactoryCreateRule */ - public boolean getXIncludeAware() + public void addFactoryCreate( final String pattern, final Class> clazz, + final String attributeName, final boolean ignoreCreateExceptions ) { - return ( this.xincludeAware ); + addRule( pattern, new FactoryCreateRule( clazz, attributeName, ignoreCreateExceptions ) ); } /** - * Sets the XInclude-aware flag for parsers we create. This additionally requires namespace-awareness. + * Add a "factory create" rule for the specified parameters. Exceptions thrown during the object creation process + * will be propagated. * - * @param xincludeAware The new XInclude-aware flag - * @see #setNamespaceAware(boolean) - * @since 2.0 + * @param pattern Element matching pattern + * @param creationFactory Previously instantiated ObjectCreationFactory to be utilized + * @see FactoryCreateRule */ - public void setXIncludeAware( final boolean xincludeAware ) + public void addFactoryCreate( final String pattern, final ObjectCreationFactory creationFactory ) { - this.xincludeAware = xincludeAware; + addFactoryCreate( pattern, creationFactory, false ); } /** - * Sets the public id of the current file being parse. + * Add a "factory create" rule for the specified parameters. * - * @param publicId the DTD/Schema public's id. + * @param pattern Element matching pattern + * @param creationFactory Previously instantiated ObjectCreationFactory to be utilized + * @param ignoreCreateExceptions when {@code true} any exceptions thrown during object creation will be + * ignored. + * @see FactoryCreateRule */ - public void setPublicId( final String publicId ) + public void addFactoryCreate( final String pattern, final ObjectCreationFactory creationFactory, + final boolean ignoreCreateExceptions ) { - this.publicId = publicId; + creationFactory.setDigester( this ); + addRule( pattern, new FactoryCreateRule( creationFactory, ignoreCreateExceptions ) ); } /** - * Return the public identifier of the DTD we are currently parsing under, if any. + * Add a "factory create" rule for the specified parameters. Exceptions thrown during the object creation process + * will be propagated. * - * @return the public identifier of the DTD we are currently parsing under, if any. + * @param pattern Element matching pattern + * @param className Java class name of the object creation factory class + * @see FactoryCreateRule */ - public String getPublicId() + public void addFactoryCreate( final String pattern, final String className ) { - return ( this.publicId ); + addFactoryCreate( pattern, className, false ); } /** - * Return the namespace URI that will be applied to all subsequently added {@code Rule} objects. + * Add a "factory create" rule for the specified parameters. * - * @return the namespace URI that will be applied to all subsequently added {@code Rule} objects. + * @param pattern Element matching pattern + * @param className Java class name of the object creation factory class + * @param ignoreCreateExceptions when {@code true} any exceptions thrown during object creation will be + * ignored. + * @see FactoryCreateRule */ - public String getRuleNamespaceURI() + public void addFactoryCreate( final String pattern, final String className, final boolean ignoreCreateExceptions ) { - return ( getRules().getNamespaceURI() ); + addRule( pattern, new FactoryCreateRule( className, ignoreCreateExceptions ) ); } /** - * Sets the namespace URI that will be applied to all subsequently added {@code Rule} objects. + * Add a "factory create" rule for the specified parameters. Exceptions thrown during the object creation process + * will be propagated. * - * @param ruleNamespaceURI Namespace URI that must match on all subsequently added rules, or {@code null} for - * matching regardless of the current namespace URI + * @param pattern Element matching pattern + * @param className Java class name of the object creation factory class + * @param attributeName Attribute name which, if present, overrides the value specified by {@code className} + * @see FactoryCreateRule */ - public void setRuleNamespaceURI( final String ruleNamespaceURI ) + public void addFactoryCreate( final String pattern, final String className, final String attributeName ) { - getRules().setNamespaceURI( ruleNamespaceURI ); + addFactoryCreate( pattern, className, attributeName, false ); } /** - * Return the SAXParser we will use to parse the input stream. - * - * If there is a problem creating the parser, return {@code null}. + * Add a "factory create" rule for the specified parameters. * - * @return the SAXParser we will use to parse the input stream + * @param pattern Element matching pattern + * @param className Java class name of the object creation factory class + * @param attributeName Attribute name which, if present, overrides the value specified by {@code className} + * @param ignoreCreateExceptions when {@code true} any exceptions thrown during object creation will be + * ignored. + * @see FactoryCreateRule */ - public SAXParser getParser() + public void addFactoryCreate( final String pattern, final String className, final String attributeName, + final boolean ignoreCreateExceptions ) { - // Return the parser we already created (if any) - if ( parser != null ) - { - return ( parser ); - } - - // Create a new parser - try - { - parser = getFactory().newSAXParser(); - } - catch ( final Exception e ) - { - log.error( "Digester.getParser: ", e ); - return ( null ); - } - - return ( parser ); + addRule( pattern, new FactoryCreateRule( className, attributeName, ignoreCreateExceptions ) ); } /** - * Return the current value of the specified property for the underlying {@code XMLReader} implementation. - * - * See the saxproject website for information about the standard SAX2 - * properties. + * Add an "object create" rule for the specified parameters. * - * @param property Property name to be retrieved - * @return the current value of the specified property for the underlying {@code XMLReader} implementation. - * @throws SAXNotRecognizedException if the property name is not recognized - * @throws SAXNotSupportedException if the property name is recognized but not supported + * @param pattern Element matching pattern + * @param clazz Java class to be created + * @see ObjectCreateRule */ - public Object getProperty( final String property ) - throws SAXNotRecognizedException, SAXNotSupportedException + public void addObjectCreate( final String pattern, final Class clazz ) { - return ( getParser().getProperty( property ) ); + addRule( pattern, new ObjectCreateRule( clazz ) ); } /** - * Sets the current value of the specified property for the underlying {@code XMLReader} implementation. See the saxproject website for information about the standard SAX2 properties. + * Add an "object create" rule for the specified parameters. * - * @param property Property name to be set - * @param value Property value to be set - * @throws SAXNotRecognizedException if the property name is not recognized - * @throws SAXNotSupportedException if the property name is recognized but not supported + * @param pattern Element matching pattern + * @param className Java class name to be created + * @see ObjectCreateRule */ - public void setProperty( final String property, final Object value ) - throws SAXNotRecognizedException, SAXNotSupportedException + public void addObjectCreate( final String pattern, final String className ) { - getParser().setProperty( property, value ); + addRule( pattern, new ObjectCreateRule( className ) ); } /** - * Return the {@code Rules} implementation object containing our rules collection and associated matching - * policy. If none has been established, a default implementation will be created and returned. + * Add an "object create" rule for the specified parameters. * - * @return the {@code Rules} implementation object. + * @param pattern Element matching pattern + * @param attributeName Attribute name that optionally overrides + * @param clazz Default Java class to be created the default Java class name to be created + * @see ObjectCreateRule */ - public Rules getRules() + public void addObjectCreate( final String pattern, final String attributeName, final Class clazz ) { - if ( this.rules == null ) - { - this.rules = new RulesBase(); - this.rules.setDigester( this ); - } - return ( this.rules ); + addRule( pattern, new ObjectCreateRule( attributeName, clazz ) ); } /** - * Sets the {@code Rules} implementation object containing our rules collection and associated matching policy. + * Add an "object create" rule for the specified parameters. * - * @param rules New Rules implementation + * @param pattern Element matching pattern + * @param className Default Java class name to be created + * @param attributeName Attribute name that optionally overrides the default Java class name to be created + * @see ObjectCreateRule */ - public void setRules( final Rules rules ) + public void addObjectCreate( final String pattern, final String className, final String attributeName ) { - this.rules = rules; - this.rules.setDigester( this ); + addRule( pattern, new ObjectCreateRule( className, attributeName ) ); } /** - * Return the XML Schema used when parsing. + * Add a "call parameter" rule that sets a parameter from a caller-provided object. This can be used to pass + * constants such as strings to methods; it can also be used to pass mutable objects, providing ways for objects to + * do things like "register" themselves with some shared object. + *

+ * Note that when attempting to locate a matching method to invoke, the true type of the paramObj is used, so that + * despite the paramObj being passed in here as type Object, the target method can declare its parameters as being + * the true type of the object (or some ancestor type, according to the usual type-conversion rules). * - * @return The {@link Schema} instance in use. - * @since 2.0 + * @param pattern Element matching pattern + * @param paramIndex The zero-relative parameter number + * @param paramObj Any arbitrary object to be passed to the target method. + * @see CallMethodRule + * @since 1.6 */ - public Schema getXMLSchema() + public void addObjectParam( final String pattern, final int paramIndex, final Object paramObj ) { - return ( this.schema ); + addRule( pattern, new ObjectParamRule( paramIndex, paramObj ) ); } /** - * Sets the XML Schema to be used when parsing. + *

+ * Register a new Rule matching the specified pattern. This method sets the {@code Digester} property on the + * rule. + *

* - * @param schema The {@link Schema} instance to use. - * @since 2.0 + * @param pattern Element matching pattern + * @param rule Rule to be registered */ - public void setXMLSchema( final Schema schema ) + public void addRule( final String pattern, final Rule rule ) { - this.schema = schema; + rule.setDigester( this ); + getRules().add( pattern, rule ); } /** - * Return the boolean as to whether the context ClassLoader should be used. + * Register a set of Rule instances defined in a RuleSet. * - * @return true, if the context ClassLoader should be used, false otherwise. + * @param ruleSet The RuleSet instance to configure from */ - public boolean getUseContextClassLoader() + public void addRuleSet( final RuleSet ruleSet ) { - return useContextClassLoader; + final String oldNamespaceURI = getRuleNamespaceURI(); + final String newNamespaceURI = ruleSet.getNamespaceURI(); + if ( log.isDebugEnabled() ) + { + if ( newNamespaceURI == null ) + { + log.debug( "addRuleSet() with no namespace URI" ); + } + else + { + log.debug( "addRuleSet() with namespace URI " + newNamespaceURI ); + } + } + setRuleNamespaceURI( newNamespaceURI ); + ruleSet.addRuleInstances( this ); + setRuleNamespaceURI( oldNamespaceURI ); } /** - * Determine whether to use the Context ClassLoader (the one found by calling - * {@code Thread.currentThread().getContextClassLoader()}) to resolve/load classes that are defined in various - * rules. If not using Context ClassLoader, then the class-loading defaults to using the calling-class' ClassLoader. + * Adds an {@link SetNestedPropertiesRule}. * - * @param use determines whether to use Context ClassLoader. + * @param pattern register the rule with this pattern + * @since 1.6 */ - public void setUseContextClassLoader( final boolean use ) + public void addSetNestedProperties( final String pattern ) { - useContextClassLoader = use; + addRule( pattern, new SetNestedPropertiesRule() ); } /** - * Return the validating parser flag. + * Adds an {@link SetNestedPropertiesRule}. * - * @return the validating parser flag. + * @param pattern register the rule with this pattern + * @param elementName elment name that a property maps to + * @param propertyName property name of the element mapped from + * @since 1.6 */ - public boolean getValidating() + public void addSetNestedProperties( final String pattern, final String elementName, final String propertyName ) { - return ( this.validating ); + addRule( pattern, new SetNestedPropertiesRule( elementName, propertyName ) ); } /** - * Sets the validating parser flag. This must be called before {@code parse()} is called the first time. - * By default the value of this is set to false. - * - * It essentially just controls the DTD validation. To use modern schema languages use the - * {@link #setXMLSchema(Schema)} method to associate a schema to a parser. + * Adds an {@link SetNestedPropertiesRule}. * - * @param validating The new validating parser flag. - * @see javax.xml.parsers.SAXParserFactory#setValidating(boolean) for more detail. + * @param pattern register the rule with this pattern + * @param elementNames elment names that (in order) map to properties + * @param propertyNames property names that (in order) elements are mapped to + * @since 1.6 */ - public void setValidating( final boolean validating ) + public void addSetNestedProperties( final String pattern, final String[] elementNames, final String[] propertyNames ) { - this.validating = validating; + addRule( pattern, new SetNestedPropertiesRule( elementNames, propertyNames ) ); } /** - * Return the XMLReader to be used for parsing the input document. - * - * FIXME: there is a bug in JAXP/XERCES that prevent the use of a parser that contains a schema with a DTD. + * Add a "set next" rule for the specified parameters. * - * @return the XMLReader to be used for parsing the input document. - * @throws SAXException if no XMLReader can be instantiated + * @param pattern Element matching pattern + * @param methodName Method name to call on the parent element + * @see SetNextRule */ - public XMLReader getXMLReader() - throws SAXException + public void addSetNext( final String pattern, final String methodName ) { - if ( reader == null ) - { - reader = getParser().getXMLReader(); - } - - reader.setDTDHandler( this ); - reader.setContentHandler( this ); - - if ( entityResolver == null ) - { - reader.setEntityResolver( this ); - } - else - { - reader.setEntityResolver( entityResolver ); - } - - if ( this.errorHandler != null ) - { - reader.setErrorHandler( this.errorHandler ); - } - else - { - reader.setErrorHandler( this ); - } - - return reader; + addRule( pattern, new SetNextRule( methodName ) ); } /** - * Gets the {@code Substitutor} used to convert attributes and body text. + * Add a "set next" rule for the specified parameters. * - * @return the {@code Substitutor} used to convert attributes and body text, - * null if not substitutions are to be performed. + * @param pattern Element matching pattern + * @param methodName Method name to call on the parent element + * @param paramType Java class name of the expected parameter type (if you wish to use a primitive type, specify the + * corresonding Java wrapper class instead, such as {@code java.lang.Boolean} for a + * {@code boolean} parameter) + * @see SetNextRule */ - public Substitutor getSubstitutor() + public void addSetNext( final String pattern, final String methodName, final String paramType ) { - return substitutor; + addRule( pattern, new SetNextRule( methodName, paramType ) ); } /** - * Sets the {@code Substitutor} to be used to convert attributes and body text. + * Add a "set properties" rule for the specified parameters. * - * @param substitutor the Substitutor to be used to convert attributes and body text or null if not substitution of - * these values is to be performed. + * @param pattern Element matching pattern + * @see SetPropertiesRule */ - public void setSubstitutor( final Substitutor substitutor ) + public void addSetProperties( final String pattern ) { - this.substitutor = substitutor; + addRule( pattern, new SetPropertiesRule() ); } /** - * returns the custom SAX ContentHandler where events are redirected. + * Add a "set properties" rule with a single overridden parameter. See + * {@link SetPropertiesRule#SetPropertiesRule(String attributeName, String propertyName)} * - * @return the custom SAX ContentHandler where events are redirected. - * @see #setCustomContentHandler(ContentHandler) - * @since 1.7 + * @param pattern Element matching pattern + * @param attributeName map this attribute + * @param propertyName to this property + * @see SetPropertiesRule */ - public ContentHandler getCustomContentHandler() + public void addSetProperties( final String pattern, final String attributeName, final String propertyName ) { - return customContentHandler; + addRule( pattern, new SetPropertiesRule( attributeName, propertyName ) ); } /** - * Redirects (or cancels redirecting) of SAX ContentHandler events to an external object. - *

- * When this object's customContentHandler is non-null, any SAX events received from the parser will simply be - * passed on to the specified object instead of this object handling them. This allows Rule classes to take control - * of the SAX event stream for a while in order to do custom processing. Such a rule should save the old value - * before setting a new one, and restore the old value in order to resume normal digester processing. - *

- * An example of a Rule which needs this feature is NodeCreateRule. - *

- * Note that saving the old value is probably not needed as it should always be null; a custom rule that wants to - * take control could only have been called when there was no custom content handler. But it seems cleaner to - * properly save/restore the value and maybe some day this will come in useful. - *

- * Note also that this is not quite equivalent to - * - *

-     * digester.getXMLReader().setContentHandler( handler )
-     * 
- * - * for these reasons: - *
    - *
  • Some xml parsers don't like having setContentHandler called after parsing has started. The Aelfred parser is - * one example.
  • - *
  • Directing the events via the Digester object potentially allows us to log information about those SAX events - * at the digester level.
  • - *
+ * Add a "set properties" rule with overridden parameters. See + * {@link SetPropertiesRule#SetPropertiesRule(String [] attributeNames, String [] propertyNames)} * - * @param handler the custom SAX ContentHandler where events are redirected. - * @since 1.7 + * @param pattern Element matching pattern + * @param attributeNames names of attributes with custom mappings + * @param propertyNames property names these attributes map to + * @see SetPropertiesRule */ - public void setCustomContentHandler( final ContentHandler handler ) + public void addSetProperties( final String pattern, final String[] attributeNames, final String[] propertyNames ) { - customContentHandler = handler; + addRule( pattern, new SetPropertiesRule( attributeNames, propertyNames ) ); } /** - * Define a callback object which is invoked whenever an object is pushed onto a digester object stack, - * or popped off one. + * Add a "set property" rule for the specified parameters. * - * @param stackAction the callback object which is invoked whenever an object is pushed onto a digester - * object stack, or popped off one. - * @since 1.8 + * @param pattern Element matching pattern + * @param name Attribute name containing the property name to be set + * @param value Attribute name containing the property value to set + * @see SetPropertyRule */ - public void setStackAction( final StackAction stackAction ) + public void addSetProperty( final String pattern, final String name, final String value ) { - this.stackAction = stackAction; + addRule( pattern, new SetPropertyRule( name, value ) ); } /** - * Return the callback object which is invoked whenever an object is pushed onto a digester object stack, - * or popped off one. + * Add {@link SetRootRule} with the specified parameters. * - * @return the callback object which is invoked whenever an object is pushed onto a digester object stack, - * or popped off one. - * @see #setStackAction(StackAction) - * @since 1.8 + * @param pattern Element matching pattern + * @param methodName Method name to call on the root object + * @see SetRootRule */ - public StackAction getStackAction() + public void addSetRoot( final String pattern, final String methodName ) { - return stackAction; + addRule( pattern, new SetRootRule( methodName ) ); } /** - * Gets the most current namespaces for all prefixes. + * Add {@link SetRootRule} with the specified parameters. * - * @return Map A map with namespace prefixes as keys and most current namespace URIs for the corresponding prefixes - * as values - * @since 1.8 + * @param pattern Element matching pattern + * @param methodName Method name to call on the root object + * @param paramType Java class name of the expected parameter type + * @see SetRootRule */ - public Map getCurrentNamespaces() + public void addSetRoot( final String pattern, final String methodName, final String paramType ) { - if ( !namespaceAware ) - { - log.warn( "Digester is not namespace aware" ); - } - final Map currentNamespaces = new HashMap(); - for ( final Map.Entry> nsEntry : namespaces.entrySet() ) - { - try - { - currentNamespaces.put( nsEntry.getKey(), nsEntry.getValue().peek() ); - } - catch ( final RuntimeException e ) - { - // rethrow, after logging - log.error( e.getMessage(), e ); - throw e; - } - } - return currentNamespaces; + addRule( pattern, new SetRootRule( methodName, paramType ) ); } /** - * Returns the executor service used to run asynchronous parse method. + * Add a "set top" rule for the specified parameters. * - * @return the executor service used to run asynchronous parse method - * @since 3.1 + * @param pattern Element matching pattern + * @param methodName Method name to call on the parent element + * @see SetTopRule */ - public ExecutorService getExecutorService() + public void addSetTop( final String pattern, final String methodName ) { - return executorService; + addRule( pattern, new SetTopRule( methodName ) ); } /** - * Sets the executor service to run asynchronous parse method. + * Add a "set top" rule for the specified parameters. * - * @param executorService the executor service to run asynchronous parse method - * @since 3.1 + * @param pattern Element matching pattern + * @param methodName Method name to call on the parent element + * @param paramType Java class name of the expected parameter type (if you wish to use a primitive type, specify the + * corresonding Java wrapper class instead, such as {@code java.lang.Boolean} for a + * {@code boolean} parameter) + * @see SetTopRule */ - public void setExecutorService( final ExecutorService executorService ) + public void addSetTop( final String pattern, final String methodName, final String paramType ) { - this.executorService = executorService; + addRule( pattern, new SetTopRule( methodName, paramType ) ); } - // ------------------------------------------------- ContentHandler Methods - /** - * {@inheritDoc} + * Execute the parse in async mode. + * + * @param the type used to auto-cast the returned object to the assigned variable type + * @param callable + * @return a Future that can be used to track when the parse has been fully processed. + * @since 3.1 */ - @Override - public void characters( final char buffer[], final int start, final int length ) - throws SAXException + private Future asyncParse( final Callable callable ) { - if ( customContentHandler != null ) + if ( executorService == null ) { - // forward calls instead of handling them here - customContentHandler.characters( buffer, start, length ); - return; + throw new IllegalStateException( "ExecutorService not set" ); } - if ( saxLog.isDebugEnabled() ) + return executorService.submit( callable ); + } + + /** + * Creates a Callable instance that parse the content of the specified reader using this Digester. + * + * @param The result type returned by the returned Future's {@code get} method + * @param file File containing the XML data to be parsed + * @return a Future that can be used to track when the parse has been fully processed. + * @see Digester#parse(File) + * @since 3.1 + */ + public Future asyncParse( final File file ) + { + return asyncParse( new Callable() { - saxLog.debug( "characters(" + new String( buffer, start, length ) + ")" ); - } - bodyText.append( buffer, start, length ); + @Override + public T call() + throws Exception + { + return Digester.this. parse( file ); + } + + } ); } /** - * {@inheritDoc} + * Creates a Callable instance that parse the content of the specified reader using this Digester. + * + * @param The result type returned by the returned Future's {@code get} method + * @param input Input source containing the XML data to be parsed + * @return a Future that can be used to track when the parse has been fully processed. + * @see Digester#parse(InputSource) + * @since 3.1 */ - @Override - public void endDocument() - throws SAXException + public Future asyncParse( final InputSource input ) { - if ( saxLog.isDebugEnabled() ) + return asyncParse( new Callable() { - if ( getCount() > 1 ) + + @Override + public T call() + throws Exception { - saxLog.debug( "endDocument(): " + getCount() + " elements left" ); + return Digester.this. parse( input ); } - else + + } ); + } + + // ------------------------------------------------- ContentHandler Methods + + /** + * Creates a Callable instance that parse the content of the specified reader using this Digester. + * + * @param The result type returned by the returned Future's {@code get} method + * @param input Input stream containing the XML data to be parsed + * @return a Future that can be used to track when the parse has been fully processed. + * @see Digester#parse(InputStream) + * @since 3.1 + */ + public Future asyncParse( final InputStream input ) + { + return asyncParse( new Callable() + { + + @Override + public T call() + throws Exception { - saxLog.debug( "endDocument()" ); + return Digester.this. parse( input ); } - } - // Fire "finish" events for all defined rules - for ( final Rule rule : getRules().rules() ) + } ); + } + + /** + * Creates a Callable instance that parse the content of the specified reader using this Digester. + * + * @param The result type returned by the returned Future's {@code get} method + * @param reader Reader containing the XML data to be parsed + * @return a Future that can be used to track when the parse has been fully processed. + * @see Digester#parse(Reader) + * @since 3.1 + */ + public Future asyncParse( final Reader reader ) + { + return asyncParse( new Callable() { - try + + @Override + public T call() + throws Exception { - rule.finish(); + return Digester.this. parse( reader ); } - catch ( final Exception e ) + + } ); + } + + /** + * Creates a Callable instance that parse the content of the specified reader using this Digester. + * + * @param The result type returned by the returned Future's {@code get} method + * @param uri URI containing the XML data to be parsed + * @return a Future that can be used to track when the parse has been fully processed. + * @see Digester#parse(String) + * @since 3.1 + */ + public Future asyncParse( final String uri ) + { + return asyncParse( new Callable() + { + + @Override + public T call() + throws Exception { - log.error( "Finish event threw exception", e ); - throw createSAXException( e ); + return Digester.this. parse( uri ); } - catch ( final Error e ) + + } ); + } + + /** + * Creates a Callable instance that parse the content of the specified reader using this Digester. + * + * @param The result type returned by the returned Future's {@code get} method + * @param url URL containing the XML data to be parsed + * @return a Future that can be used to track when the parse has been fully processed. + * @see Digester#parse(URL) + * @since 3.1 + */ + public Future asyncParse( final URL url ) + { + return asyncParse( new Callable() + { + + @Override + public T call() + throws Exception { - log.error( "Finish event threw error", e ); - throw e; + return Digester.this. parse( url ); } - } - // Perform final cleanup - clear(); + } ); } /** * {@inheritDoc} */ @Override - public void endElement( final String namespaceURI, final String localName, final String qName ) + public void characters( final char buffer[], final int start, final int length ) throws SAXException { if ( customContentHandler != null ) { // forward calls instead of handling them here - customContentHandler.endElement( namespaceURI, localName, qName ); + customContentHandler.characters( buffer, start, length ); return; } - final boolean debug = log.isDebugEnabled(); - - if ( debug ) + if ( saxLog.isDebugEnabled() ) { - if ( saxLog.isDebugEnabled() ) - { - saxLog.debug( "endElement(" + namespaceURI + "," + localName + "," + qName + ")" ); - } - log.debug( " match='" + match + "'" ); - log.debug( " bodyText='" + bodyText + "'" ); + saxLog.debug( "characters(" + new String( buffer, start, length ) + ")" ); } - // the actual element name is either in localName or qName, depending - // on whether the parser is namespace aware - String name = localName; - if ( ( name == null ) || ( name.length() < 1 ) ) - { - name = qName; - } + bodyText.append( buffer, start, length ); + } - // Fire "body" events for all relevant rules - final List rules = matches.pop(); - if ( ( rules != null ) && ( !rules.isEmpty() ) ) + /** + *

+ * Clean up allocated resources after parsing is complete. The default method closes input streams that have been + * created by Digester itself. If you override this method in a subclass, be sure to call + * {@code super.cleanup()} to invoke this logic. + *

+ * + * @since 1.8 + */ + protected void cleanup() + { + // If we created any InputSource objects in this instance, + // they each have an input stream that should be closed + for ( final InputSource source : inputSources ) { - String bodyText = this.bodyText.toString(); - final Substitutor substitutor = getSubstitutor(); - if ( substitutor != null ) + try { - bodyText = substitutor.substitute( bodyText ); + source.getByteStream().close(); } - for (final Rule rule : rules) { - try - { - if ( debug ) - { - log.debug( " Fire body() for " + rule ); - } - rule.body( namespaceURI, name, bodyText ); - } - catch ( final Exception e ) + catch ( final IOException e ) + { + // Fall through so we get them all + if ( log.isWarnEnabled() ) { - log.error( "Body event threw exception", e ); - throw createSAXException( e ); + log.warn( format( "An error occurred while closing resource %s (%s)", + source.getPublicId(), + source.getSystemId() ), e ); } - catch ( final Error e ) - { - log.error( "Body event threw error", e ); - throw e; - } - } - } - else - { - if ( debug ) - { - log.debug( " No rules found matching '" + match + "'." ); } } - - // Recover the body text from the surrounding element - bodyText = bodyTexts.pop(); - if ( debug ) - { - log.debug( " Popping body text '" + bodyText.toString() + "'" ); - } - - // Fire "end" events for all relevant rules in reverse order - if ( rules != null ) - { - for ( int i = 0; i < rules.size(); i++ ) - { - final int j = ( rules.size() - i ) - 1; - try - { - final Rule rule = rules.get( j ); - if ( debug ) - { - log.debug( " Fire end() for " + rule ); - } - rule.end( namespaceURI, name ); - } - catch ( final Exception e ) - { - log.error( "End event threw exception", e ); - throw createSAXException( e ); - } - catch ( final Error e ) - { - log.error( "End event threw error", e ); - throw e; - } - } - } - - // Recover the previous match expression - final int slash = match.lastIndexOf( '/' ); - if ( slash >= 0 ) - { - match = match.substring( 0, slash ); - } - else - { - match = ""; - } + inputSources.clear(); } /** - * {@inheritDoc} + * Clear the current contents of the default object stack, the param stack, all named stacks, and other internal + * variables. + *

+ * Calling this method might allow another document of the same type to be correctly parsed. However this + * method was not intended for this purpose (just to tidy up memory usage). In general, a separate Digester object + * should be created for each document to be parsed. + *

+ * Note that this method is called automatically after a document has been successfully parsed by a Digester + * instance. However it is not invoked automatically when a parse fails, so when reusing a Digester instance (which + * is not recommended) this method must be called manually after a parse failure. */ - @Override - public void endPrefixMapping( final String prefix ) - throws SAXException + public void clear() { - if ( saxLog.isDebugEnabled() ) - { - saxLog.debug( "endPrefixMapping(" + prefix + ")" ); - } - - // Deregister this prefix mapping - final Stack stack = namespaces.get( prefix ); - if ( stack == null ) - { - return; - } - try - { - stack.pop(); - if ( stack.empty() ) - { - namespaces.remove( prefix ); - } - } - catch ( final EmptyStackException e ) - { - throw createSAXException( "endPrefixMapping popped too many times" ); - } + match = ""; + bodyTexts.clear(); + params.clear(); + publicId = null; + stack.clear(); + stacksByName.clear(); + customContentHandler = null; } /** - * {@inheritDoc} + *

+ * Provide a hook for lazy configuration of this {@code Digester} instance. The default implementation does + * nothing, but subclasses can override as needed. + *

+ *

+ * Note This method may be called more than once. Once only initialization code should be placed in + * {@link #initialize} or the code should take responsibility by checking and setting the {@link #configured} flag. + *

*/ - @Override - public void ignorableWhitespace( final char buffer[], final int start, final int len ) - throws SAXException + protected void configure() { - if ( saxLog.isDebugEnabled() ) + // Do not configure more than once + if ( configured ) { - saxLog.debug( "ignorableWhitespace(" + new String( buffer, start, len ) + ")" ); + return; } - // No processing required + // Perform lazy configuration as needed + initialize(); // call hook method for subclasses that want to be initialized once only + // Nothing else required by default + + // Set the configuration flag to avoid repeating + configured = true; } /** - * {@inheritDoc} + *

+ * Convenience method that creates an {@code InputSource} from the string version of a URL. + *

+ * + * @param url URL for which to create an {@code InputSource} + * @return The InputSource that reads from the input URL + * @throws IOException if any error occurs while reading the input URL + * @since 1.8 */ - @Override - public void processingInstruction( final String target, final String data ) - throws SAXException + public InputSource createInputSourceFromURL( final String url ) + throws IOException { - if ( customContentHandler != null ) - { - // forward calls instead of handling them here - customContentHandler.processingInstruction( target, data ); - return; - } - - if ( saxLog.isDebugEnabled() ) - { - saxLog.debug( "processingInstruction('" + target + "','" + data + "')" ); - } - - // No processing is required + return createInputSourceFromURL( new URL( url ) ); } /** - * Gets the document locator associated with our parser. + * Given a URL, return an InputSource that reads from that URL. + *

+ * Ideally this function would not be needed and code could just use {@code new InputSource(entityURL)}. + * Unfortunately it appears that when the entityURL points to a file within a jar archive a caching mechanism inside + * the InputSource implementation causes a file-handle to the jar file to remain open. On Windows systems this then + * causes the jar archive file to be locked on disk ("in use") which makes it impossible to delete the jar file - + * and that really stuffs up "undeploy" in webapps in particular. + *

+ * In JDK1.4 and later, Apache XercesJ is used as the xml parser. The InputSource object provided is converted into + * an XMLInputSource, and eventually passed to an instance of XMLDocumentScannerImpl to specify the source data to + * be converted into tokens for the rest of the XMLReader code to handle. XMLDocumentScannerImpl calls + * fEntityManager.startDocumentEntity(source), where fEntityManager is declared in ancestor class XMLScanner to be + * an XMLEntityManager. In that class, if the input source stream is null, then: * - * @return the Locator supplied by the document parser + *

+     * URL location = new URL( expandedSystemId );
+     * URLConnection connect = location.openConnection();
+     * if ( connect instanceof HttpURLConnection )
+     * {
+     *     setHttpProperties( connect, xmlInputSource );
+     * }
+     * stream = connect.getInputStream();
+     * 
+ * + * This method pretty much duplicates the standard behavior, except that it calls URLConnection.setUseCaches(false) + * before opening the connection. + * + * @param url The URL has to be read + * @return The InputSource that reads from the input URL + * @throws IOException if any error occurs while reading the input URL + * @since 1.8 */ - public Locator getDocumentLocator() + public InputSource createInputSourceFromURL( final URL url ) + throws IOException { - return locator; + final URLConnection connection = url.openConnection(); + connection.setUseCaches( false ); + final InputStream stream = connection.getInputStream(); + final InputSource source = new InputSource( stream ); + source.setSystemId( url.toExternalForm() ); + inputSources.add( source ); + return source; } /** - * {@inheritDoc} + * Create a SAX exception which also understands about the location in the digester file where the exception occurs + * + * @param e the exception cause + * @return the new SAX exception */ - @Override - public void setDocumentLocator( final Locator locator ) + public SAXException createSAXException( Exception e ) { - if ( saxLog.isDebugEnabled() ) + if ( e instanceof InvocationTargetException ) { - saxLog.debug( "setDocumentLocator(" + locator + ")" ); + final Throwable t = ( (InvocationTargetException) e ).getTargetException(); + if ( ( t != null ) && ( t instanceof Exception ) ) + { + e = (Exception) t; + } } + return createSAXException( e.getMessage(), e ); + } - this.locator = locator; + /** + * Create a SAX exception which also understands about the location in the digester file where the exception occurs + * + * @param message the custom SAX exception message + * @return the new SAX exception + */ + public SAXException createSAXException( final String message ) + { + return createSAXException( message, null ); } + // ----------------------------------------------------- DTDHandler Methods + /** - * {@inheritDoc} + * Create a SAX exception which also understands about the location in the digester file where the exception occurs + * + * @param message the custom SAX exception message + * @param e the exception cause + * @return the new SAX exception */ - @Override - public void skippedEntity( final String name ) - throws SAXException + public SAXException createSAXException( final String message, Exception e ) { - if ( saxLog.isDebugEnabled() ) + if ( ( e != null ) && ( e instanceof InvocationTargetException ) ) { - saxLog.debug( "skippedEntity(" + name + ")" ); + final Throwable t = ( (InvocationTargetException) e ).getTargetException(); + if ( ( t != null ) && ( t instanceof Exception ) ) + { + e = (Exception) t; + } } - - // No processing required + if ( locator != null ) + { + final String error = + "Error at line " + locator.getLineNumber() + " char " + locator.getColumnNumber() + ": " + message; + if ( e != null ) + { + return new SAXParseException( error, locator, e ); + } + return new SAXParseException( error, locator ); + } + log.error( "No Locator!" ); + if ( e != null ) + { + return new SAXException( message, e ); + } + return new SAXException( message ); } /** * {@inheritDoc} */ @Override - public void startDocument() + public void endDocument() throws SAXException { if ( saxLog.isDebugEnabled() ) { - saxLog.debug( "startDocument()" ); + if ( getCount() > 1 ) + { + saxLog.debug( "endDocument(): " + getCount() + " elements left" ); + } + else + { + saxLog.debug( "endDocument()" ); + } } - // ensure that the digester is properly configured, as - // the digester could be used as a SAX ContentHandler - // rather than via the parse() methods. - configure(); + // Fire "finish" events for all defined rules + for ( final Rule rule : getRules().rules() ) + { + try + { + rule.finish(); + } + catch ( final Exception e ) + { + log.error( "Finish event threw exception", e ); + throw createSAXException( e ); + } + catch ( final Error e ) + { + log.error( "Finish event threw error", e ); + throw e; + } + } + + // Perform final cleanup + clear(); } + // ----------------------------------------------- EntityResolver Methods + /** * {@inheritDoc} */ @Override - public void startElement( final String namespaceURI, final String localName, final String qName, Attributes list ) + public void endElement( final String namespaceURI, final String localName, final String qName ) throws SAXException { - final boolean debug = log.isDebugEnabled(); - if ( customContentHandler != null ) { // forward calls instead of handling them here - customContentHandler.startElement( namespaceURI, localName, qName, list ); + customContentHandler.endElement( namespaceURI, localName, qName ); return; } - if ( saxLog.isDebugEnabled() ) - { - saxLog.debug( "startElement(" + namespaceURI + "," + localName + "," + qName + ")" ); - } + final boolean debug = log.isDebugEnabled(); - // Save the body text accumulated for our surrounding element - bodyTexts.push( bodyText ); if ( debug ) { - log.debug( " Pushing body text '" + bodyText.toString() + "'" ); + if ( saxLog.isDebugEnabled() ) + { + saxLog.debug( "endElement(" + namespaceURI + "," + localName + "," + qName + ")" ); + } + log.debug( " match='" + match + "'" ); + log.debug( " bodyText='" + bodyText + "'" ); } - bodyText = new StringBuilder(); // the actual element name is either in localName or qName, depending // on whether the parser is namespace aware @@ -1323,46 +1370,33 @@ public void startElement( final String namespaceURI, final String localName, fin name = qName; } - // Compute the current matching rule - final StringBuilder sb = new StringBuilder( match ); - if ( !match.isEmpty() ) - { - sb.append( '/' ); - } - sb.append( name ); - match = sb.toString(); - if ( debug ) - { - log.debug( " New match='" + match + "'" ); - } - - // Fire "begin" events for all relevant rules - final List rules = getRules().match( namespaceURI, match, localName, list ); - matches.push( rules ); + // Fire "body" events for all relevant rules + final List rules = matches.pop(); if ( ( rules != null ) && ( !rules.isEmpty() ) ) { + String bodyText = this.bodyText.toString(); final Substitutor substitutor = getSubstitutor(); if ( substitutor != null ) { - list = substitutor.substitute( list ); + bodyText = substitutor.substitute( bodyText ); } for (final Rule rule : rules) { try { if ( debug ) { - log.debug( " Fire begin() for " + rule ); + log.debug( " Fire body() for " + rule ); } - rule.begin( namespaceURI, name, list ); + rule.body( namespaceURI, name, bodyText ); } catch ( final Exception e ) { - log.error( "Begin event threw exception", e ); + log.error( "Body event threw exception", e ); throw createSAXException( e ); } catch ( final Error e ) { - log.error( "Begin event threw error", e ); + log.error( "Body event threw error", e ); throw e; } } @@ -1374,154 +1408,86 @@ public void startElement( final String namespaceURI, final String localName, fin log.debug( " No rules found matching '" + match + "'." ); } } - } - - /** - * {@inheritDoc} - */ - @Override - public void startPrefixMapping( final String prefix, final String namespaceURI ) - throws SAXException - { - if ( saxLog.isDebugEnabled() ) - { - saxLog.debug( "startPrefixMapping(" + prefix + "," + namespaceURI + ")" ); - } - - // Register this prefix mapping - Stack stack = namespaces.get( prefix ); - if ( stack == null ) - { - stack = new Stack(); - namespaces.put( prefix, stack ); - } - stack.push( namespaceURI ); - } - - // ----------------------------------------------------- DTDHandler Methods - - /** - * {@inheritDoc} - */ - @Override - public void notationDecl( final String name, final String publicId, final String systemId ) - { - if ( saxLog.isDebugEnabled() ) - { - saxLog.debug( "notationDecl(" + name + "," + publicId + "," + systemId + ")" ); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void unparsedEntityDecl( final String name, final String publicId, final String systemId, final String notation ) - { - if ( saxLog.isDebugEnabled() ) - { - saxLog.debug( "unparsedEntityDecl(" + name + "," + publicId + "," + systemId + "," + notation + ")" ); - } - } - - // ----------------------------------------------- EntityResolver Methods - - /** - * Sets the {@code EntityResolver} used by SAX when resolving public id and system id. This must be called - * before the first call to {@code parse()}. - * - * @param entityResolver a class that implement the {@code EntityResolver} interface. - */ - public void setEntityResolver( final EntityResolver entityResolver ) - { - this.entityResolver = entityResolver; - } - - /** - * Return the Entity Resolver used by the SAX parser. - * - * @return the Entity Resolver used by the SAX parser. - */ - public EntityResolver getEntityResolver() - { - return entityResolver; - } - - /** - * {@inheritDoc} - */ - @Override - public InputSource resolveEntity( final String publicId, final String systemId ) - throws SAXException - { - if ( saxLog.isDebugEnabled() ) - { - saxLog.debug( "resolveEntity('" + publicId + "', '" + systemId + "')" ); - } - if ( publicId != null ) - { - this.publicId = publicId; - } - - // Has this system identifier been registered? - URL entityURL = null; - if ( publicId != null ) - { - entityURL = entityValidator.get( publicId ); - } - - // Redirect the schema location to a local destination - if ( entityURL == null && systemId != null ) + // Recover the body text from the surrounding element + bodyText = bodyTexts.pop(); + if ( debug ) { - entityURL = entityValidator.get( systemId ); + log.debug( " Popping body text '" + bodyText.toString() + "'" ); } - if ( entityURL == null ) + // Fire "end" events for all relevant rules in reverse order + if ( rules != null ) { - if ( systemId == null ) + for ( int i = 0; i < rules.size(); i++ ) { - // cannot resolve - if ( log.isDebugEnabled() ) + final int j = ( rules.size() - i ) - 1; + try { - log.debug( " Cannot resolve null entity, returning null InputSource" ); + final Rule rule = rules.get( j ); + if ( debug ) + { + log.debug( " Fire end() for " + rule ); + } + rule.end( namespaceURI, name ); + } + catch ( final Exception e ) + { + log.error( "End event threw exception", e ); + throw createSAXException( e ); + } + catch ( final Error e ) + { + log.error( "End event threw error", e ); + throw e; } - return ( null ); - - } - // try to resolve using system ID - if ( log.isDebugEnabled() ) - { - log.debug( " Trying to resolve using system ID '" + systemId + "'" ); - } - try - { - entityURL = new URL( systemId ); - } - catch ( final MalformedURLException e ) - { - throw new IllegalArgumentException( "Malformed URL '" + systemId + "' : " + e.getMessage() ); } } - // Return an input source to our alternative URL - if ( log.isDebugEnabled() ) + // Recover the previous match expression + final int slash = match.lastIndexOf( '/' ); + if ( slash >= 0 ) + { + match = match.substring( 0, slash ); + } + else + { + match = ""; + } + } + + /** + * {@inheritDoc} + */ + @Override + public void endPrefixMapping( final String prefix ) + throws SAXException + { + if ( saxLog.isDebugEnabled() ) { - log.debug( " Resolving to alternate DTD '" + entityURL + "'" ); + saxLog.debug( "endPrefixMapping(" + prefix + ")" ); } + // Deregister this prefix mapping + final Stack stack = namespaces.get( prefix ); + if ( stack == null ) + { + return; + } try { - return createInputSourceFromURL( entityURL ); + stack.pop(); + if ( stack.empty() ) + { + namespaces.remove( prefix ); + } } - catch ( final Exception e ) + catch ( final EmptyStackException e ) { - throw createSAXException( e ); + throw createSAXException( "endPrefixMapping popped too many times" ); } } - // ------------------------------------------------- ErrorHandler Methods - /** * {@inheritDoc} */ @@ -1533,6 +1499,8 @@ public void error( final SAXParseException exception ) + exception.getMessage(), exception ); } + // ------------------------------------------------- ErrorHandler Methods + /** * {@inheritDoc} */ @@ -1545,1336 +1513,1402 @@ public void fatalError( final SAXParseException exception ) } /** - * {@inheritDoc} - */ - @Override - public void warning( final SAXParseException exception ) - throws SAXException - { - log.warn( "Parse Warning Error at line " + exception.getLineNumber() + " column " - + exception.getColumnNumber() + ": " + exception.getMessage(), exception ); - } - - // ------------------------------------------------------- Public Methods - - /** - * Parse the content of the specified file using this Digester. Returns the root element from the object stack (if - * any). - * - * @param the type used to auto-cast the returned object to the assigned variable type - * @param file File containing the XML data to be parsed - * @return the root element from the object stack (if any) - * @throws IOException if an input/output error occurs - * @throws SAXException if a parsing exception occurs - */ - public T parse( final File file ) - throws IOException, SAXException - { - if ( file == null ) - { - throw new IllegalArgumentException( "File to parse is null" ); - } - - final InputSource input = new InputSource( new FileInputStream( file ) ); - input.setSystemId( file.toURI().toURL().toString() ); - - return ( this. parse( input ) ); - } - - /** - * Creates a Callable instance that parse the content of the specified reader using this Digester. - * - * @param The result type returned by the returned Future's {@code get} method - * @param file File containing the XML data to be parsed - * @return a Future that can be used to track when the parse has been fully processed. - * @see Digester#parse(File) - * @since 3.1 - */ - public Future asyncParse( final File file ) - { - return asyncParse( new Callable() - { - - @Override - public T call() - throws Exception - { - return Digester.this. parse( file ); - } - - } ); - } - - /** - * Parse the content of the specified input source using this Digester. Returns the root element from the object - * stack (if any). + * Return the currently mapped namespace URI for the specified prefix, if any; otherwise return {@code null}. + * These mappings come and go dynamically as the document is parsed. * - * @param the type used to auto-cast the returned object to the assigned variable type - * @param input Input source containing the XML data to be parsed - * @return the root element from the object stack (if any) - * @throws IOException if an input/output error occurs - * @throws SAXException if a parsing exception occurs + * @param prefix Prefix to look up + * @return the currently mapped namespace URI for the specified prefix */ - public T parse( final InputSource input ) - throws IOException, SAXException + public String findNamespaceURI( final String prefix ) { - if ( input == null ) - { - throw new IllegalArgumentException( "InputSource to parse is null" ); - } - - configure(); - - String systemId = input.getSystemId(); - if ( systemId == null ) + final Stack nsStack = namespaces.get( prefix ); + if ( nsStack == null ) { - systemId = "(already loaded from stream)"; + return null; } - try { - getXMLReader().parse( input ); - } - catch ( final IOException e ) - { - log.error( format( "An error occurred while reading stream from '%s', see nested exceptions", systemId ), - e ); - throw e; + return ( nsStack.peek() ); } - cleanup(); - return this. getRoot(); - } - - /** - * Creates a Callable instance that parse the content of the specified reader using this Digester. - * - * @param The result type returned by the returned Future's {@code get} method - * @param input Input source containing the XML data to be parsed - * @return a Future that can be used to track when the parse has been fully processed. - * @see Digester#parse(InputSource) - * @since 3.1 - */ - public Future asyncParse( final InputSource input ) - { - return asyncParse( new Callable() - { - - @Override - public T call() - throws Exception - { - return Digester.this. parse( input ); - } - - } ); - } - - /** - * Parse the content of the specified input stream using this Digester. Returns the root element from the object - * stack (if any). - * - * @param the type used to auto-cast the returned object to the assigned variable type - * @param input Input stream containing the XML data to be parsed - * @return the root element from the object stack (if any) - * @throws IOException if an input/output error occurs - * @throws SAXException if a parsing exception occurs - */ - public T parse( final InputStream input ) - throws IOException, SAXException - { - if ( input == null ) + catch ( final EmptyStackException e ) { - throw new IllegalArgumentException( "InputStream to parse is null" ); + return null; } - - return ( this. parse( new InputSource( input ) ) ); - } - - /** - * Creates a Callable instance that parse the content of the specified reader using this Digester. - * - * @param The result type returned by the returned Future's {@code get} method - * @param input Input stream containing the XML data to be parsed - * @return a Future that can be used to track when the parse has been fully processed. - * @see Digester#parse(InputStream) - * @since 3.1 - */ - public Future asyncParse( final InputStream input ) - { - return asyncParse( new Callable() - { - - @Override - public T call() - throws Exception - { - return Digester.this. parse( input ); - } - - } ); } /** - * Parse the content of the specified reader using this Digester. Returns the root element from the object stack (if - * any). + * Return the class loader to be used for instantiating application objects when required. This is determined based + * upon the following rules: + *
    + *
  • The class loader set by {@code setClassLoader()}, if any
  • + *
  • The thread context class loader, if it exists and the {@code useContextClassLoader} property is set to + * true
  • + *
  • The class loader used to load the Digester class itself. + *
* - * @param the type used to auto-cast the returned object to the assigned variable type - * @param reader Reader containing the XML data to be parsed - * @return the root element from the object stack (if any) - * @throws IOException if an input/output error occurs - * @throws SAXException if a parsing exception occurs + * @return the class loader to be used for instantiating application objects. */ - public T parse( final Reader reader ) - throws IOException, SAXException + public ClassLoader getClassLoader() { - if ( reader == null ) + if ( this.classLoader != null ) { - throw new IllegalArgumentException( "Reader to parse is null" ); + return ( this.classLoader ); } - - return ( this. parse( new InputSource( reader ) ) ); - } - - /** - * Creates a Callable instance that parse the content of the specified reader using this Digester. - * - * @param The result type returned by the returned Future's {@code get} method - * @param reader Reader containing the XML data to be parsed - * @return a Future that can be used to track when the parse has been fully processed. - * @see Digester#parse(Reader) - * @since 3.1 - */ - public Future asyncParse( final Reader reader ) - { - return asyncParse( new Callable() + if ( this.useContextClassLoader ) { - - @Override - public T call() - throws Exception + final ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + if ( classLoader != null ) { - return Digester.this. parse( reader ); + return ( classLoader ); } - - } ); + } + return ( this.getClass().getClassLoader() ); } + // ------------------------------------------------------- Public Methods + /** - * Parse the content of the specified URI using this Digester. Returns the root element from the object stack (if - * any). + * Return the current depth of the element stack. * - * @param the type used to auto-cast the returned object to the assigned variable type - * @param uri URI containing the XML data to be parsed - * @return the root element from the object stack (if any) - * @throws IOException if an input/output error occurs - * @throws SAXException if a parsing exception occurs + * @return the current depth of the element stack. */ - public T parse( final String uri ) - throws IOException, SAXException + public int getCount() { - if ( uri == null ) - { - throw new IllegalArgumentException( "String URI to parse is null" ); - } - - return ( this. parse( createInputSourceFromURL( uri ) ) ); + return ( stack.size() ); } /** - * Creates a Callable instance that parse the content of the specified reader using this Digester. + * Return the name of the XML element that is currently being processed. * - * @param The result type returned by the returned Future's {@code get} method - * @param uri URI containing the XML data to be parsed - * @return a Future that can be used to track when the parse has been fully processed. - * @see Digester#parse(String) - * @since 3.1 + * @return the name of the XML element that is currently being processed. */ - public Future asyncParse( final String uri ) + public String getCurrentElementName() { - return asyncParse( new Callable() + String elementName = match; + final int lastSlash = elementName.lastIndexOf( '/' ); + if ( lastSlash >= 0 ) { - - @Override - public T call() - throws Exception - { - return Digester.this. parse( uri ); - } - - } ); + elementName = elementName.substring( lastSlash + 1 ); + } + return ( elementName ); } /** - * Parse the content of the specified URL using this Digester. Returns the root element from the object stack (if - * any). + * Gets the most current namespaces for all prefixes. * - * @param the type used to auto-cast the returned object to the assigned variable type - * @param url URL containing the XML data to be parsed - * @return the root element from the object stack (if any) - * @throws IOException if an input/output error occurs - * @throws SAXException if a parsing exception occurs + * @return Map A map with namespace prefixes as keys and most current namespace URIs for the corresponding prefixes + * as values * @since 1.8 */ - public T parse( final URL url ) - throws IOException, SAXException + public Map getCurrentNamespaces() { - if ( url == null ) + if ( !namespaceAware ) { - throw new IllegalArgumentException( "URL to parse is null" ); + log.warn( "Digester is not namespace aware" ); } - - return ( this. parse( createInputSourceFromURL( url ) ) ); + final Map currentNamespaces = new HashMap(); + for ( final Map.Entry> nsEntry : namespaces.entrySet() ) + { + try + { + currentNamespaces.put( nsEntry.getKey(), nsEntry.getValue().peek() ); + } + catch ( final RuntimeException e ) + { + // rethrow, after logging + log.error( e.getMessage(), e ); + throw e; + } + } + return currentNamespaces; } /** - * Creates a Callable instance that parse the content of the specified reader using this Digester. + * returns the custom SAX ContentHandler where events are redirected. * - * @param The result type returned by the returned Future's {@code get} method - * @param url URL containing the XML data to be parsed - * @return a Future that can be used to track when the parse has been fully processed. - * @see Digester#parse(URL) - * @since 3.1 + * @return the custom SAX ContentHandler where events are redirected. + * @see #setCustomContentHandler(ContentHandler) + * @since 1.7 */ - public Future asyncParse( final URL url ) + public ContentHandler getCustomContentHandler() { - return asyncParse( new Callable() - { - - @Override - public T call() - throws Exception - { - return Digester.this. parse( url ); - } - - } ); + return customContentHandler; } /** - * Execute the parse in async mode. + * Gets the document locator associated with our parser. * - * @param the type used to auto-cast the returned object to the assigned variable type - * @param callable - * @return a Future that can be used to track when the parse has been fully processed. - * @since 3.1 + * @return the Locator supplied by the document parser */ - private Future asyncParse( final Callable callable ) + public Locator getDocumentLocator() { - if ( executorService == null ) - { - throw new IllegalStateException( "ExecutorService not set" ); - } - - return executorService.submit( callable ); + return locator; } /** - *

- * Register the specified DTD URL for the specified public identifier. This must be called before the first call to - * {@code parse()}. - *

- *

- * {@code Digester} contains an internal {@code EntityResolver} implementation. This maps - * {@code PUBLICID}'s to URLs (from which the resource will be loaded). A common use case for this method is to - * register local URLs (possibly computed at runtime by a classloader) for DTDs. This allows the performance - * advantage of using a local version without having to ensure every {@code SYSTEM} URI on every processed xml - * document is local. This implementation provides only basic functionality. If more sophisticated features are - * required, using {@link #setEntityResolver} to set a custom resolver is recommended. - *

- *

- * Note: This method will have no effect when a custom {@code EntityResolver} has been set. - * (Setting a custom {@code EntityResolver} overrides the internal implementation.) - *

+ * Return the Entity Resolver used by the SAX parser. * - * @param publicId Public identifier of the DTD to be resolved - * @param entityURL The URL to use for reading this DTD - * @since 1.8 + * @return the Entity Resolver used by the SAX parser. */ - public void register( final String publicId, final URL entityURL ) + public EntityResolver getEntityResolver() { - if ( log.isDebugEnabled() ) - { - log.debug( "register('" + publicId + "', '" + entityURL + "'" ); - } - entityValidator.put( publicId, entityURL ); + return entityResolver; } /** - *

- * Convenience method that registers the string version of an entity URL instead of a URL version. - *

+ * Return the error handler for this Digester. * - * @param publicId Public identifier of the entity to be resolved - * @param entityURL The URL to use for reading this entity + * @return the error handler for this Digester. */ - public void register( final String publicId, final String entityURL ) + public ErrorHandler getErrorHandler() { - if ( log.isDebugEnabled() ) - { - log.debug( "register('" + publicId + "', '" + entityURL + "'" ); - } - try - { - entityValidator.put( publicId, new URL( entityURL ) ); - } - catch ( final MalformedURLException e ) - { - throw new IllegalArgumentException( "Malformed URL '" + entityURL + "' : " + e.getMessage() ); - } + return ( this.errorHandler ); } /** - * Convenience method that registers DTD URLs for the specified public identifiers. + * Returns the executor service used to run asynchronous parse method. * - * @param entityValidator The URLs of entityValidator that have been registered, keyed by the public - * identifier that corresponds. - * @since 3.0 + * @return the executor service used to run asynchronous parse method + * @since 3.1 */ - public void registerAll( final Map entityValidator ) + public ExecutorService getExecutorService() { - this.entityValidator.putAll( entityValidator ); + return executorService; } /** - *

- * {@code List} of {@code InputSource} instances created by a {@code createInputSourceFromURL()} - * method call. These represent open input streams that need to be closed to avoid resource leaks, as well as - * potentially locked JAR files on Windows. - *

+ * Return the SAXParserFactory we will use, creating one if necessary. + * + * @return the SAXParserFactory we will use, creating one if necessary. */ - protected List inputSources = new ArrayList( 5 ); + public SAXParserFactory getFactory() + { + if ( factory == null ) + { + factory = SAXParserFactory.newInstance(); + factory.setNamespaceAware( namespaceAware ); + factory.setXIncludeAware( xincludeAware ); + factory.setValidating( validating ); + factory.setSchema( schema ); + } + return ( factory ); + } /** - * Given a URL, return an InputSource that reads from that URL. - *

- * Ideally this function would not be needed and code could just use {@code new InputSource(entityURL)}. - * Unfortunately it appears that when the entityURL points to a file within a jar archive a caching mechanism inside - * the InputSource implementation causes a file-handle to the jar file to remain open. On Windows systems this then - * causes the jar archive file to be locked on disk ("in use") which makes it impossible to delete the jar file - - * and that really stuffs up "undeploy" in webapps in particular. - *

- * In JDK1.4 and later, Apache XercesJ is used as the xml parser. The InputSource object provided is converted into - * an XMLInputSource, and eventually passed to an instance of XMLDocumentScannerImpl to specify the source data to - * be converted into tokens for the rest of the XMLReader code to handle. XMLDocumentScannerImpl calls - * fEntityManager.startDocumentEntity(source), where fEntityManager is declared in ancestor class XMLScanner to be - * an XMLEntityManager. In that class, if the input source stream is null, then: - * - *

-     * URL location = new URL( expandedSystemId );
-     * URLConnection connect = location.openConnection();
-     * if ( connect instanceof HttpURLConnection )
-     * {
-     *     setHttpProperties( connect, xmlInputSource );
-     * }
-     * stream = connect.getInputStream();
-     * 
- * - * This method pretty much duplicates the standard behavior, except that it calls URLConnection.setUseCaches(false) - * before opening the connection. + * Returns a flag indicating whether the requested feature is supported by the underlying implementation of + * {@code org.xml.sax.XMLReader}. See the saxproject website for + * information about the standard SAX2 feature flags. * - * @param url The URL has to be read - * @return The InputSource that reads from the input URL - * @throws IOException if any error occurs while reading the input URL - * @since 1.8 + * @param feature Name of the feature to inquire about + * @return true, if the requested feature is supported by the underlying implementation of + * {@code org.xml.sax.XMLReader}, false otherwise + * @throws ParserConfigurationException if a parser configuration error occurs + * @throws SAXNotRecognizedException if the property name is not recognized + * @throws SAXNotSupportedException if the property name is recognized but not supported + */ + public boolean getFeature( final String feature ) + throws ParserConfigurationException, SAXNotRecognizedException, SAXNotSupportedException + { + return ( getFactory().getFeature( feature ) ); + } + + /** + * Return the current Logger associated with this instance of the Digester + * + * @return the current Logger associated with this instance of the Digester */ - public InputSource createInputSourceFromURL( final URL url ) - throws IOException + public Log getLogger() { - final URLConnection connection = url.openConnection(); - connection.setUseCaches( false ); - final InputStream stream = connection.getInputStream(); - final InputSource source = new InputSource( stream ); - source.setSystemId( url.toExternalForm() ); - inputSources.add( source ); - return source; + return log; } /** - *

- * Convenience method that creates an {@code InputSource} from the string version of a URL. - *

+ * Return the current rule match path * - * @param url URL for which to create an {@code InputSource} - * @return The InputSource that reads from the input URL - * @throws IOException if any error occurs while reading the input URL - * @since 1.8 + * @return the current rule match path */ - public InputSource createInputSourceFromURL( final String url ) - throws IOException + public String getMatch() { - return createInputSourceFromURL( new URL( url ) ); + return match; } - // --------------------------------------------------------- Rule Methods + /** + * Return a Stack whose elements are List objects, each containing a list of + * Rule objects as returned from Rules.getMatch(). + * + * @return a Stack whose elements are List objects, each containing a list of + * Rule objects as returned from Rules.getMatch(). + * @since 3.0 + */ + public Stack> getMatches() + { + return matches; + } /** - *

- * Register a new Rule matching the specified pattern. This method sets the {@code Digester} property on the - * rule. - *

+ * Return the "namespace aware" flag for parsers we create. * - * @param pattern Element matching pattern - * @param rule Rule to be registered + * @return the "namespace aware" flag for parsers we create. */ - public void addRule( final String pattern, final Rule rule ) + public boolean getNamespaceAware() { - rule.setDigester( this ); - getRules().add( pattern, rule ); + return ( this.namespaceAware ); } /** - * Register a set of Rule instances defined in a RuleSet. + * Return the SAXParser we will use to parse the input stream. * - * @param ruleSet The RuleSet instance to configure from + * If there is a problem creating the parser, return {@code null}. + * + * @return the SAXParser we will use to parse the input stream */ - public void addRuleSet( final RuleSet ruleSet ) + public SAXParser getParser() { - final String oldNamespaceURI = getRuleNamespaceURI(); - final String newNamespaceURI = ruleSet.getNamespaceURI(); - if ( log.isDebugEnabled() ) + // Return the parser we already created (if any) + if ( parser != null ) { - if ( newNamespaceURI == null ) - { - log.debug( "addRuleSet() with no namespace URI" ); - } - else - { - log.debug( "addRuleSet() with namespace URI " + newNamespaceURI ); - } + return ( parser ); } - setRuleNamespaceURI( newNamespaceURI ); - ruleSet.addRuleInstances( this ); - setRuleNamespaceURI( oldNamespaceURI ); + + // Create a new parser + try + { + parser = getFactory().newSAXParser(); + } + catch ( final Exception e ) + { + log.error( "Digester.getParser: ", e ); + return ( null ); + } + + return ( parser ); } /** - * Add a "bean property setter" rule for the specified parameters. + * Return the current value of the specified property for the underlying {@code XMLReader} implementation. * - * @param pattern Element matching pattern - * @see BeanPropertySetterRule + * See the saxproject website for information about the standard SAX2 + * properties. + * + * @param property Property name to be retrieved + * @return the current value of the specified property for the underlying {@code XMLReader} implementation. + * @throws SAXNotRecognizedException if the property name is not recognized + * @throws SAXNotSupportedException if the property name is recognized but not supported */ - public void addBeanPropertySetter( final String pattern ) + public Object getProperty( final String property ) + throws SAXNotRecognizedException, SAXNotSupportedException { - addRule( pattern, new BeanPropertySetterRule() ); + return ( getParser().getProperty( property ) ); } /** - * Add a "bean property setter" rule for the specified parameters. + * Return the public identifier of the DTD we are currently parsing under, if any. * - * @param pattern Element matching pattern - * @param propertyName Name of property to set - * @see BeanPropertySetterRule + * @return the public identifier of the DTD we are currently parsing under, if any. */ - public void addBeanPropertySetter( final String pattern, final String propertyName ) + public String getPublicId() { - addRule( pattern, new BeanPropertySetterRule( propertyName ) ); + return ( this.publicId ); } /** - * Add an "call method" rule for a method which accepts no arguments. + * Return the set of DTD URL registrations, keyed by public identifier. NOTE: the returned map is in read-only mode. * - * @param pattern Element matching pattern - * @param methodName Method name to be called - * @see CallMethodRule + * @return the read-only Map of DTD URL registrations. */ - public void addCallMethod( final String pattern, final String methodName ) + Map getRegistrations() { - addRule( pattern, new CallMethodRule( methodName ) ); + return Collections.unmodifiableMap( entityValidator ); } /** - * Add an "call method" rule for the specified parameters. + * Returns the root element of the tree of objects created as a result of applying the rule objects to the input + * XML. + *

+ * If the digester stack was "primed" by explicitly pushing a root object onto the stack before parsing started, + * then that root object is returned here. + *

+ * Alternatively, if a Rule which creates an object (eg ObjectCreateRule) matched the root element of the xml, then + * the object created will be returned here. + *

+ * In other cases, the object most recently pushed onto an empty digester stack is returned. This would be a most + * unusual use of digester, however; one of the previous configurations is much more likely. + *

+ * Note that when using one of the Digester.parse methods, the return value from the parse method is exactly the + * same as the return value from this method. However when the Digester is being used as a SAXContentHandler, no + * such return value is available; in this case, this method allows you to access the root object that has been + * created after parsing has completed. * - * @param pattern Element matching pattern - * @param methodName Method name to be called - * @param paramCount Number of expected parameters (or zero for a single parameter from the body of this element) - * @see CallMethodRule + * @param the type used to auto-cast the returned object to the assigned variable type + * @return the root object that has been created after parsing or null if the digester has not parsed any XML yet. */ - public void addCallMethod( final String pattern, final String methodName, final int paramCount ) + public T getRoot() { - addRule( pattern, new CallMethodRule( methodName, paramCount ) ); + return this. npeSafeCast( root ); } + // --------------------------------------------------------- Rule Methods + /** - * Add an "call method" rule for the specified parameters. If {@code paramCount} is set to zero the rule will - * use the body of the matched element as the single argument of the method, unless {@code paramTypes} is null - * or empty, in this case the rule will call the specified method with no arguments. + * Return the namespace URI that will be applied to all subsequently added {@code Rule} objects. * - * @param pattern Element matching pattern - * @param methodName Method name to be called - * @param paramCount Number of expected parameters (or zero for a single parameter from the body of this element) - * @param paramTypes Set of Java class names for the types of the expected parameters (if you wish to use a - * primitive type, specify the corresonding Java wrapper class instead, such as - * {@code java.lang.Boolean} for a {@code boolean} parameter) - * @see CallMethodRule + * @return the namespace URI that will be applied to all subsequently added {@code Rule} objects. */ - public void addCallMethod( final String pattern, final String methodName, final int paramCount, final String paramTypes[] ) + public String getRuleNamespaceURI() { - addRule( pattern, new CallMethodRule( methodName, paramCount, paramTypes ) ); + return ( getRules().getNamespaceURI() ); } /** - * Add an "call method" rule for the specified parameters. If {@code paramCount} is set to zero the rule will - * use the body of the matched element as the single argument of the method, unless {@code paramTypes} is null - * or empty, in this case the rule will call the specified method with no arguments. + * Return the {@code Rules} implementation object containing our rules collection and associated matching + * policy. If none has been established, a default implementation will be created and returned. * - * @param pattern Element matching pattern - * @param methodName Method name to be called - * @param paramCount Number of expected parameters (or zero for a single parameter from the body of this element) - * @param paramTypes The Java class names of the arguments (if you wish to use a primitive type, specify the - * corresonding Java wrapper class instead, such as {@code java.lang.Boolean} for a - * {@code boolean} parameter) - * @see CallMethodRule + * @return the {@code Rules} implementation object. */ - public void addCallMethod( final String pattern, final String methodName, final int paramCount, final Class paramTypes[] ) + public Rules getRules() { - addRule( pattern, new CallMethodRule( methodName, paramCount, paramTypes ) ); + if ( this.rules == null ) + { + this.rules = new RulesBase(); + this.rules.setDigester( this ); + } + return ( this.rules ); } /** - * Add a "call parameter" rule for the specified parameters. + * Gets the logger used for logging SAX-related information. Note the output is finely grained. * - * @param pattern Element matching pattern - * @param paramIndex Zero-relative parameter index to set (from the body of this element) - * @see CallParamRule + * @return the logger used for logging SAX-related information + * @since 1.6 */ - public void addCallParam( final String pattern, final int paramIndex ) + public Log getSAXLogger() { - addRule( pattern, new CallParamRule( paramIndex ) ); + return saxLog; } /** - * Add a "call parameter" rule for the specified parameters. + * Return the callback object which is invoked whenever an object is pushed onto a digester object stack, + * or popped off one. * - * @param pattern Element matching pattern - * @param paramIndex Zero-relative parameter index to set (from the specified attribute) - * @param attributeName Attribute whose value is used as the parameter value - * @see CallParamRule + * @return the callback object which is invoked whenever an object is pushed onto a digester object stack, + * or popped off one. + * @see #setStackAction(StackAction) + * @since 1.8 */ - public void addCallParam( final String pattern, final int paramIndex, final String attributeName ) + public StackAction getStackAction() { - addRule( pattern, new CallParamRule( paramIndex, attributeName ) ); + return stackAction; } /** - * Add a "call parameter" rule. This will either take a parameter from the stack or from the current element body - * text. + * Gets the {@code Substitutor} used to convert attributes and body text. * - * @param pattern Element matching pattern - * @param paramIndex The zero-relative parameter number - * @param fromStack Should the call parameter be taken from the top of the stack? - * @see CallParamRule + * @return the {@code Substitutor} used to convert attributes and body text, + * null if not substitutions are to be performed. */ - public void addCallParam( final String pattern, final int paramIndex, final boolean fromStack ) + public Substitutor getSubstitutor() { - addRule( pattern, new CallParamRule( paramIndex, fromStack ) ); + return substitutor; } /** - * Add a "call parameter" rule that sets a parameter from the stack. This takes a parameter from the given position - * on the stack. + * Return the boolean as to whether the context ClassLoader should be used. * - * @param pattern Element matching pattern - * @param paramIndex The zero-relative parameter number - * @param stackIndex set the call parameter to the stackIndex'th object down the stack, where 0 is the top of the - * stack, 1 the next element down and so on - * @see CallMethodRule + * @return true, if the context ClassLoader should be used, false otherwise. */ - public void addCallParam( final String pattern, final int paramIndex, final int stackIndex ) + public boolean getUseContextClassLoader() + { + return useContextClassLoader; + } + + /** + * Return the validating parser flag. + * + * @return the validating parser flag. + */ + public boolean getValidating() { - addRule( pattern, new CallParamRule( paramIndex, stackIndex ) ); + return ( this.validating ); } /** - * Add a "call parameter" rule that sets a parameter from the current {@code Digester} matching path. This is - * sometimes useful when using rules that support wildcards. + * Return the XInclude-aware flag for parsers we create. XInclude functionality additionally requires + * namespace-awareness. * - * @param pattern the pattern that this rule should match - * @param paramIndex The zero-relative parameter number - * @see CallMethodRule + * @return The XInclude-aware flag + * @see #getNamespaceAware() + * @since 2.0 */ - public void addCallParamPath( final String pattern, final int paramIndex ) + public boolean getXIncludeAware() { - addRule( pattern, new PathCallParamRule( paramIndex ) ); + return ( this.xincludeAware ); } /** - * Add a "call parameter" rule that sets a parameter from a caller-provided object. This can be used to pass - * constants such as strings to methods; it can also be used to pass mutable objects, providing ways for objects to - * do things like "register" themselves with some shared object. - *

- * Note that when attempting to locate a matching method to invoke, the true type of the paramObj is used, so that - * despite the paramObj being passed in here as type Object, the target method can declare its parameters as being - * the true type of the object (or some ancestor type, according to the usual type-conversion rules). + * Return the XMLReader to be used for parsing the input document. * - * @param pattern Element matching pattern - * @param paramIndex The zero-relative parameter number - * @param paramObj Any arbitrary object to be passed to the target method. - * @see CallMethodRule - * @since 1.6 + * FIXME: there is a bug in JAXP/XERCES that prevent the use of a parser that contains a schema with a DTD. + * + * @return the XMLReader to be used for parsing the input document. + * @throws SAXException if no XMLReader can be instantiated */ - public void addObjectParam( final String pattern, final int paramIndex, final Object paramObj ) + public XMLReader getXMLReader() + throws SAXException { - addRule( pattern, new ObjectParamRule( paramIndex, paramObj ) ); + if ( reader == null ) + { + reader = getParser().getXMLReader(); + } + + reader.setDTDHandler( this ); + reader.setContentHandler( this ); + + if ( entityResolver == null ) + { + reader.setEntityResolver( this ); + } + else + { + reader.setEntityResolver( entityResolver ); + } + + if ( this.errorHandler != null ) + { + reader.setErrorHandler( this.errorHandler ); + } + else + { + reader.setErrorHandler( this ); + } + + return reader; } /** - * Add a "factory create" rule for the specified parameters. Exceptions thrown during the object creation process - * will be propagated. + * Return the XML Schema used when parsing. * - * @param pattern Element matching pattern - * @param className Java class name of the object creation factory class - * @see FactoryCreateRule + * @return The {@link Schema} instance in use. + * @since 2.0 */ - public void addFactoryCreate( final String pattern, final String className ) + public Schema getXMLSchema() { - addFactoryCreate( pattern, className, false ); + return ( this.schema ); } /** - * Add a "factory create" rule for the specified parameters. Exceptions thrown during the object creation process - * will be propagated. - * - * @param pattern Element matching pattern - * @param clazz Java class of the object creation factory class - * @see FactoryCreateRule + * {@inheritDoc} */ - public void addFactoryCreate( final String pattern, final Class> clazz ) + @Override + public void ignorableWhitespace( final char buffer[], final int start, final int len ) + throws SAXException { - addFactoryCreate( pattern, clazz, false ); + if ( saxLog.isDebugEnabled() ) + { + saxLog.debug( "ignorableWhitespace(" + new String( buffer, start, len ) + ")" ); + } + + // No processing required } /** - * Add a "factory create" rule for the specified parameters. Exceptions thrown during the object creation process - * will be propagated. + *

+ * Provides a hook for lazy initialization of this {@code Digester} instance. The default implementation does + * nothing, but subclasses can override as needed. Digester (by default) only calls this method once. + *

+ *

+ * Note This method will be called by {@link #configure} only when the {@link #configured} flag is + * false. Subclasses that override {@code configure} or who set {@code configured} may find that this + * method may be called more than once. + *

* - * @param pattern Element matching pattern - * @param className Java class name of the object creation factory class - * @param attributeName Attribute name which, if present, overrides the value specified by {@code className} - * @see FactoryCreateRule + * @since 1.6 */ - public void addFactoryCreate( final String pattern, final String className, final String attributeName ) + protected void initialize() { - addFactoryCreate( pattern, className, attributeName, false ); + // Perform lazy initialization as needed + // Nothing required by default } /** - * Add a "factory create" rule for the specified parameters. Exceptions thrown during the object creation process - * will be propagated. + * Checks the Digester instance has been configured. * - * @param pattern Element matching pattern - * @param clazz Java class of the object creation factory class - * @param attributeName Attribute name which, if present, overrides the value specified by {@code className} - * @see FactoryCreateRule + * @return true, if the Digester instance has been configured, false otherwise + * @since 3.0 */ - public void addFactoryCreate( final String pattern, final Class> clazz, - final String attributeName ) + public boolean isConfigured() { - addFactoryCreate( pattern, clazz, attributeName, false ); + return configured; } /** - * Add a "factory create" rule for the specified parameters. Exceptions thrown during the object creation process - * will be propagated. + *

+ * Is the stack with the given name empty? + *

+ *

+ * Note: a stack is considered empty if no objects have been pushed onto it yet. + *

* - * @param pattern Element matching pattern - * @param creationFactory Previously instantiated ObjectCreationFactory to be utilized - * @see FactoryCreateRule + * @param stackName the name of the stack whose emptiness should be evaluated + * @return true if the given stack if empty + * @since 1.6 */ - public void addFactoryCreate( final String pattern, final ObjectCreationFactory creationFactory ) + public boolean isEmpty( final String stackName ) { - addFactoryCreate( pattern, creationFactory, false ); + boolean result = true; + final Stack namedStack = stacksByName.get( stackName ); + if ( namedStack != null ) + { + result = namedStack.isEmpty(); + } + return result; } /** - * Add a "factory create" rule for the specified parameters. - * - * @param pattern Element matching pattern - * @param className Java class name of the object creation factory class - * @param ignoreCreateExceptions when {@code true} any exceptions thrown during object creation will be - * ignored. - * @see FactoryCreateRule + * {@inheritDoc} */ - public void addFactoryCreate( final String pattern, final String className, final boolean ignoreCreateExceptions ) + @Override + public void notationDecl( final String name, final String publicId, final String systemId ) { - addRule( pattern, new FactoryCreateRule( className, ignoreCreateExceptions ) ); + if ( saxLog.isDebugEnabled() ) + { + saxLog.debug( "notationDecl(" + name + "," + publicId + "," + systemId + ")" ); + } } /** - * Add a "factory create" rule for the specified parameters. + * Helps casting the input object to given type, avoiding NPEs. * - * @param pattern Element matching pattern - * @param clazz Java class of the object creation factory class - * @param ignoreCreateExceptions when {@code true} any exceptions thrown during object creation will be - * ignored. - * @see FactoryCreateRule + * @since 3.0 + * @param the type the input object has to be cast. + * @param obj the object has to be cast. + * @return the casted object, if input object is not null, null otherwise. */ - public void addFactoryCreate( final String pattern, final Class> clazz, - final boolean ignoreCreateExceptions ) + private T npeSafeCast( final Object obj ) { - addRule( pattern, new FactoryCreateRule( clazz, ignoreCreateExceptions ) ); + if ( obj == null ) + { + return null; + } + + @SuppressWarnings( "unchecked" ) + final + T result = (T) obj; + return result; } /** - * Add a "factory create" rule for the specified parameters. + * Parse the content of the specified file using this Digester. Returns the root element from the object stack (if + * any). * - * @param pattern Element matching pattern - * @param className Java class name of the object creation factory class - * @param attributeName Attribute name which, if present, overrides the value specified by {@code className} - * @param ignoreCreateExceptions when {@code true} any exceptions thrown during object creation will be - * ignored. - * @see FactoryCreateRule + * @param the type used to auto-cast the returned object to the assigned variable type + * @param file File containing the XML data to be parsed + * @return the root element from the object stack (if any) + * @throws IOException if an input/output error occurs + * @throws SAXException if a parsing exception occurs */ - public void addFactoryCreate( final String pattern, final String className, final String attributeName, - final boolean ignoreCreateExceptions ) + public T parse( final File file ) + throws IOException, SAXException { - addRule( pattern, new FactoryCreateRule( className, attributeName, ignoreCreateExceptions ) ); + if ( file == null ) + { + throw new IllegalArgumentException( "File to parse is null" ); + } + + final InputSource input = new InputSource( new FileInputStream( file ) ); + input.setSystemId( file.toURI().toURL().toString() ); + + return ( this. parse( input ) ); } /** - * Add a "factory create" rule for the specified parameters. + * Parse the content of the specified input source using this Digester. Returns the root element from the object + * stack (if any). * - * @param pattern Element matching pattern - * @param clazz Java class of the object creation factory class - * @param attributeName Attribute name which, if present, overrides the value specified by {@code className} - * @param ignoreCreateExceptions when {@code true} any exceptions thrown during object creation will be - * ignored. - * @see FactoryCreateRule + * @param the type used to auto-cast the returned object to the assigned variable type + * @param input Input source containing the XML data to be parsed + * @return the root element from the object stack (if any) + * @throws IOException if an input/output error occurs + * @throws SAXException if a parsing exception occurs */ - public void addFactoryCreate( final String pattern, final Class> clazz, - final String attributeName, final boolean ignoreCreateExceptions ) + public T parse( final InputSource input ) + throws IOException, SAXException { - addRule( pattern, new FactoryCreateRule( clazz, attributeName, ignoreCreateExceptions ) ); + if ( input == null ) + { + throw new IllegalArgumentException( "InputSource to parse is null" ); + } + + configure(); + + String systemId = input.getSystemId(); + if ( systemId == null ) + { + systemId = "(already loaded from stream)"; + } + + try + { + getXMLReader().parse( input ); + } + catch ( final IOException e ) + { + log.error( format( "An error occurred while reading stream from '%s', see nested exceptions", systemId ), + e ); + throw e; + } + cleanup(); + return this. getRoot(); } /** - * Add a "factory create" rule for the specified parameters. + * Parse the content of the specified input stream using this Digester. Returns the root element from the object + * stack (if any). * - * @param pattern Element matching pattern - * @param creationFactory Previously instantiated ObjectCreationFactory to be utilized - * @param ignoreCreateExceptions when {@code true} any exceptions thrown during object creation will be - * ignored. - * @see FactoryCreateRule + * @param the type used to auto-cast the returned object to the assigned variable type + * @param input Input stream containing the XML data to be parsed + * @return the root element from the object stack (if any) + * @throws IOException if an input/output error occurs + * @throws SAXException if a parsing exception occurs */ - public void addFactoryCreate( final String pattern, final ObjectCreationFactory creationFactory, - final boolean ignoreCreateExceptions ) + public T parse( final InputStream input ) + throws IOException, SAXException { - creationFactory.setDigester( this ); - addRule( pattern, new FactoryCreateRule( creationFactory, ignoreCreateExceptions ) ); + if ( input == null ) + { + throw new IllegalArgumentException( "InputStream to parse is null" ); + } + + return ( this. parse( new InputSource( input ) ) ); } /** - * Add an "object create" rule for the specified parameters. + * Parse the content of the specified reader using this Digester. Returns the root element from the object stack (if + * any). * - * @param pattern Element matching pattern - * @param className Java class name to be created - * @see ObjectCreateRule + * @param the type used to auto-cast the returned object to the assigned variable type + * @param reader Reader containing the XML data to be parsed + * @return the root element from the object stack (if any) + * @throws IOException if an input/output error occurs + * @throws SAXException if a parsing exception occurs */ - public void addObjectCreate( final String pattern, final String className ) + public T parse( final Reader reader ) + throws IOException, SAXException { - addRule( pattern, new ObjectCreateRule( className ) ); + if ( reader == null ) + { + throw new IllegalArgumentException( "Reader to parse is null" ); + } + + return ( this. parse( new InputSource( reader ) ) ); } /** - * Add an "object create" rule for the specified parameters. + * Parse the content of the specified URI using this Digester. Returns the root element from the object stack (if + * any). * - * @param pattern Element matching pattern - * @param clazz Java class to be created - * @see ObjectCreateRule + * @param the type used to auto-cast the returned object to the assigned variable type + * @param uri URI containing the XML data to be parsed + * @return the root element from the object stack (if any) + * @throws IOException if an input/output error occurs + * @throws SAXException if a parsing exception occurs */ - public void addObjectCreate( final String pattern, final Class clazz ) + public T parse( final String uri ) + throws IOException, SAXException { - addRule( pattern, new ObjectCreateRule( clazz ) ); + if ( uri == null ) + { + throw new IllegalArgumentException( "String URI to parse is null" ); + } + + return ( this. parse( createInputSourceFromURL( uri ) ) ); } /** - * Add an "object create" rule for the specified parameters. + * Parse the content of the specified URL using this Digester. Returns the root element from the object stack (if + * any). * - * @param pattern Element matching pattern - * @param className Default Java class name to be created - * @param attributeName Attribute name that optionally overrides the default Java class name to be created - * @see ObjectCreateRule + * @param the type used to auto-cast the returned object to the assigned variable type + * @param url URL containing the XML data to be parsed + * @return the root element from the object stack (if any) + * @throws IOException if an input/output error occurs + * @throws SAXException if a parsing exception occurs + * @since 1.8 */ - public void addObjectCreate( final String pattern, final String className, final String attributeName ) + public T parse( final URL url ) + throws IOException, SAXException { - addRule( pattern, new ObjectCreateRule( className, attributeName ) ); + if ( url == null ) + { + throw new IllegalArgumentException( "URL to parse is null" ); + } + + return ( this. parse( createInputSourceFromURL( url ) ) ); } /** - * Add an "object create" rule for the specified parameters. + * Return the top object on the stack without removing it. * - * @param pattern Element matching pattern - * @param attributeName Attribute name that optionally overrides - * @param clazz Default Java class to be created the default Java class name to be created - * @see ObjectCreateRule + * If there are no objects on the stack, return {@code null}. + * + * @param the type used to auto-cast the returned object to the assigned variable type + * @return the top object on the stack without removing it. */ - public void addObjectCreate( final String pattern, final String attributeName, final Class clazz ) + public T peek() { - addRule( pattern, new ObjectCreateRule( attributeName, clazz ) ); + try + { + return this. npeSafeCast( stack.peek() ); + } + catch ( final EmptyStackException e ) + { + log.warn( "Empty stack (returning null)" ); + return ( null ); + } } /** - * Adds an {@link SetNestedPropertiesRule}. + * Return the n'th object down the stack, where 0 is the top element and [getCount()-1] is the bottom element. If + * the specified index is out of range, return {@code null}. * - * @param pattern register the rule with this pattern - * @since 1.6 + * @param the type used to auto-cast the returned object to the assigned variable type + * @param n Index of the desired element, where 0 is the top of the stack, 1 is the next element down, and so on. + * @return the n'th object down the stack */ - public void addSetNestedProperties( final String pattern ) + public T peek( final int n ) { - addRule( pattern, new SetNestedPropertiesRule() ); + final int index = ( stack.size() - 1 ) - n; + if ( index < 0 ) + { + log.warn( "Empty stack (returning null)" ); + return ( null ); + } + try + { + return this. npeSafeCast( stack.get( index ) ); + } + catch ( final EmptyStackException e ) + { + log.warn( "Empty stack (returning null)" ); + return ( null ); + } } /** - * Adds an {@link SetNestedPropertiesRule}. + *

+ * Gets the top object from the stack with the given name. This method does not remove the object from the stack. + *

+ *

+ * Note: a stack is considered empty if no objects have been pushed onto it yet. + *

* - * @param pattern register the rule with this pattern - * @param elementName elment name that a property maps to - * @param propertyName property name of the element mapped from + * @param the type used to auto-cast the returned object to the assigned variable type + * @param stackName the name of the stack to be peeked + * @return the top {@code Object} on the stack or null if the stack is either empty or has not been created yet * @since 1.6 */ - public void addSetNestedProperties( final String pattern, final String elementName, final String propertyName ) + public T peek( final String stackName ) { - addRule( pattern, new SetNestedPropertiesRule( elementName, propertyName ) ); + return this. npeSafeCast( peek( stackName, 0 ) ); } /** - * Adds an {@link SetNestedPropertiesRule}. + *

+ * Gets the top object from the stack with the given name. This method does not remove the object from the stack. + *

+ *

+ * Note: a stack is considered empty if no objects have been pushed onto it yet. + *

* - * @param pattern register the rule with this pattern - * @param elementNames elment names that (in order) map to properties - * @param propertyNames property names that (in order) elements are mapped to + * @param the type used to auto-cast the returned object to the assigned variable type + * @param stackName the name of the stack to be peeked + * @param n Index of the desired element, where 0 is the top of the stack, 1 is the next element down, and so on. + * @return the specified {@code Object} on the stack. * @since 1.6 */ - public void addSetNestedProperties( final String pattern, final String[] elementNames, final String[] propertyNames ) + public T peek( final String stackName, final int n ) { - addRule( pattern, new SetNestedPropertiesRule( elementNames, propertyNames ) ); + T result; + final Stack namedStack = stacksByName.get( stackName ); + if ( namedStack == null ) + { + if ( log.isDebugEnabled() ) + { + log.debug( "Stack '" + stackName + "' is empty" ); + } + throw new EmptyStackException(); + } + + final int index = ( namedStack.size() - 1 ) - n; + if ( index < 0 ) + { + throw new EmptyStackException(); + } + result = this. npeSafeCast( namedStack.get( index ) ); + + return result; } /** - * Add a "set next" rule for the specified parameters. + *

+ * Return the top object on the parameters stack without removing it. If there are no objects on the stack, return + * {@code null}. + *

+ *

+ * The parameters stack is used to store {@code CallMethodRule} parameters. See {@link #params}. + *

* - * @param pattern Element matching pattern - * @param methodName Method name to call on the parent element - * @see SetNextRule + * @return the top object on the parameters stack without removing it. */ - public void addSetNext( final String pattern, final String methodName ) + public Object[] peekParams() { - addRule( pattern, new SetNextRule( methodName ) ); + try + { + return ( params.peek() ); + } + catch ( final EmptyStackException e ) + { + log.warn( "Empty stack (returning null)" ); + return ( null ); + } } /** - * Add a "set next" rule for the specified parameters. + *

+ * Return the n'th object down the parameters stack, where 0 is the top element and [getCount()-1] is the bottom + * element. If the specified index is out of range, return {@code null}. + *

+ *

+ * The parameters stack is used to store {@code CallMethodRule} parameters. See {@link #params}. + *

* - * @param pattern Element matching pattern - * @param methodName Method name to call on the parent element - * @param paramType Java class name of the expected parameter type (if you wish to use a primitive type, specify the - * corresonding Java wrapper class instead, such as {@code java.lang.Boolean} for a - * {@code boolean} parameter) - * @see SetNextRule + * @param n Index of the desired element, where 0 is the top of the stack, 1 is the next element down, and so on. + * @return the n'th object down the parameters stack */ - public void addSetNext( final String pattern, final String methodName, final String paramType ) + public Object[] peekParams( final int n ) { - addRule( pattern, new SetNextRule( methodName, paramType ) ); + final int index = ( params.size() - 1 ) - n; + if ( index < 0 ) + { + log.warn( "Empty stack (returning null)" ); + return ( null ); + } + try + { + return ( params.get( index ) ); + } + catch ( final EmptyStackException e ) + { + log.warn( "Empty stack (returning null)" ); + return ( null ); + } } /** - * Add {@link SetRootRule} with the specified parameters. + * Pop the top object off of the stack, and return it. If there are no objects on the stack, return + * {@code null}. * - * @param pattern Element matching pattern - * @param methodName Method name to call on the root object - * @see SetRootRule + * @param the type used to auto-cast the returned object to the assigned variable type + * @return the top object popped off of the stack */ - public void addSetRoot( final String pattern, final String methodName ) + public T pop() { - addRule( pattern, new SetRootRule( methodName ) ); + try + { + T popped = this. npeSafeCast( stack.pop() ); + if ( stackAction != null ) + { + popped = stackAction.onPop( this, null, popped ); + } + return popped; + } + catch ( final EmptyStackException e ) + { + log.warn( "Empty stack (returning null)" ); + return ( null ); + } } /** - * Add {@link SetRootRule} with the specified parameters. + *

+ * Pops (gets and removes) the top object from the stack with the given name. + *

+ *

+ * Note: a stack is considered empty if no objects have been pushed onto it yet. + *

* - * @param pattern Element matching pattern - * @param methodName Method name to call on the root object - * @param paramType Java class name of the expected parameter type - * @see SetRootRule + * @param the type used to auto-cast the returned object to the assigned variable type + * @param stackName the name of the stack from which the top value is to be popped. + * @return the top {@code Object} on the stack or throws {@code EmptyStackException} + * if the stack is either empty or has not been created yet + * @since 1.6 */ - public void addSetRoot( final String pattern, final String methodName, final String paramType ) + public T pop( final String stackName ) { - addRule( pattern, new SetRootRule( methodName, paramType ) ); + final Stack namedStack = stacksByName.get( stackName ); + if ( namedStack == null ) + { + if ( log.isDebugEnabled() ) + { + log.debug( "Stack '" + stackName + "' is empty" ); + } + throw new EmptyStackException(); + } + + T result = this. npeSafeCast( namedStack.pop() ); + + if ( stackAction != null ) + { + result = stackAction.onPop( this, stackName, result ); + } + + return result; } /** - * Add a "set properties" rule for the specified parameters. + *

+ * Pop the top object off of the parameters stack, and return it. If there are no objects on the stack, return + * {@code null}. + *

+ *

+ * The parameters stack is used to store {@code CallMethodRule} parameters. See {@link #params}. + *

* - * @param pattern Element matching pattern - * @see SetPropertiesRule + * @return the top object popped off of the parameters stack */ - public void addSetProperties( final String pattern ) + public Object[] popParams() { - addRule( pattern, new SetPropertiesRule() ); + try + { + if ( log.isTraceEnabled() ) + { + log.trace( "Popping params" ); + } + return ( params.pop() ); + } + catch ( final EmptyStackException e ) + { + log.warn( "Empty stack (returning null)" ); + return ( null ); + } } /** - * Add a "set properties" rule with a single overridden parameter. See - * {@link SetPropertiesRule#SetPropertiesRule(String attributeName, String propertyName)} - * - * @param pattern Element matching pattern - * @param attributeName map this attribute - * @param propertyName to this property - * @see SetPropertiesRule + * {@inheritDoc} */ - public void addSetProperties( final String pattern, final String attributeName, final String propertyName ) + @Override + public void processingInstruction( final String target, final String data ) + throws SAXException { - addRule( pattern, new SetPropertiesRule( attributeName, propertyName ) ); + if ( customContentHandler != null ) + { + // forward calls instead of handling them here + customContentHandler.processingInstruction( target, data ); + return; + } + + if ( saxLog.isDebugEnabled() ) + { + saxLog.debug( "processingInstruction('" + target + "','" + data + "')" ); + } + + // No processing is required } /** - * Add a "set properties" rule with overridden parameters. See - * {@link SetPropertiesRule#SetPropertiesRule(String [] attributeNames, String [] propertyNames)} + * Pushes the given object onto the stack with the given name. If no stack already exists with the given name then + * one will be created. * - * @param pattern Element matching pattern - * @param attributeNames names of attributes with custom mappings - * @param propertyNames property names these attributes map to - * @see SetPropertiesRule + * @param any type of the pushed object + * @param stackName the name of the stack onto which the object should be pushed + * @param value the Object to be pushed onto the named stack. + * @since 1.6 */ - public void addSetProperties( final String pattern, final String[] attributeNames, final String[] propertyNames ) + public void push( final String stackName, T value ) { - addRule( pattern, new SetPropertiesRule( attributeNames, propertyNames ) ); + if ( stackAction != null ) + { + value = stackAction.onPush( this, stackName, value ); + } + + Stack namedStack = stacksByName.get( stackName ); + if ( namedStack == null ) + { + namedStack = new Stack(); + stacksByName.put( stackName, namedStack ); + } + namedStack.push( value ); } /** - * Add a "set property" rule for the specified parameters. + * Push a new object onto the top of the object stack. * - * @param pattern Element matching pattern - * @param name Attribute name containing the property name to be set - * @param value Attribute name containing the property value to set - * @see SetPropertyRule + * @param any type of the pushed object + * @param object The new object */ - public void addSetProperty( final String pattern, final String name, final String value ) + public void push( T object ) { - addRule( pattern, new SetPropertyRule( name, value ) ); + if ( stackAction != null ) + { + object = stackAction.onPush( this, null, object ); + } + + if ( stack.isEmpty() ) + { + root = object; + } + stack.push( object ); } /** - * Add a "set top" rule for the specified parameters. + *

+ * Push a new object onto the top of the parameters stack. + *

+ *

+ * The parameters stack is used to store {@code CallMethodRule} parameters. See {@link #params}. + *

* - * @param pattern Element matching pattern - * @param methodName Method name to call on the parent element - * @see SetTopRule + * @param object The new object */ - public void addSetTop( final String pattern, final String methodName ) + public void pushParams( final Object... object ) { - addRule( pattern, new SetTopRule( methodName ) ); + if ( log.isTraceEnabled() ) + { + log.trace( "Pushing params" ); + } + params.push( object ); } /** - * Add a "set top" rule for the specified parameters. + *

+ * Convenience method that registers the string version of an entity URL instead of a URL version. + *

* - * @param pattern Element matching pattern - * @param methodName Method name to call on the parent element - * @param paramType Java class name of the expected parameter type (if you wish to use a primitive type, specify the - * corresonding Java wrapper class instead, such as {@code java.lang.Boolean} for a - * {@code boolean} parameter) - * @see SetTopRule + * @param publicId Public identifier of the entity to be resolved + * @param entityURL The URL to use for reading this entity */ - public void addSetTop( final String pattern, final String methodName, final String paramType ) + public void register( final String publicId, final String entityURL ) { - addRule( pattern, new SetTopRule( methodName, paramType ) ); + if ( log.isDebugEnabled() ) + { + log.debug( "register('" + publicId + "', '" + entityURL + "'" ); + } + try + { + entityValidator.put( publicId, new URL( entityURL ) ); + } + catch ( final MalformedURLException e ) + { + throw new IllegalArgumentException( "Malformed URL '" + entityURL + "' : " + e.getMessage() ); + } } - // --------------------------------------------------- Object Stack Methods - /** - * Clear the current contents of the default object stack, the param stack, all named stacks, and other internal - * variables. *

- * Calling this method might allow another document of the same type to be correctly parsed. However this - * method was not intended for this purpose (just to tidy up memory usage). In general, a separate Digester object - * should be created for each document to be parsed. + * Register the specified DTD URL for the specified public identifier. This must be called before the first call to + * {@code parse()}. + *

*

- * Note that this method is called automatically after a document has been successfully parsed by a Digester - * instance. However it is not invoked automatically when a parse fails, so when reusing a Digester instance (which - * is not recommended) this method must be called manually after a parse failure. + * {@code Digester} contains an internal {@code EntityResolver} implementation. This maps + * {@code PUBLICID}'s to URLs (from which the resource will be loaded). A common use case for this method is to + * register local URLs (possibly computed at runtime by a classloader) for DTDs. This allows the performance + * advantage of using a local version without having to ensure every {@code SYSTEM} URI on every processed xml + * document is local. This implementation provides only basic functionality. If more sophisticated features are + * required, using {@link #setEntityResolver} to set a custom resolver is recommended. + *

+ *

+ * Note: This method will have no effect when a custom {@code EntityResolver} has been set. + * (Setting a custom {@code EntityResolver} overrides the internal implementation.) + *

+ * + * @param publicId Public identifier of the DTD to be resolved + * @param entityURL The URL to use for reading this DTD + * @since 1.8 */ - public void clear() + public void register( final String publicId, final URL entityURL ) + { + if ( log.isDebugEnabled() ) + { + log.debug( "register('" + publicId + "', '" + entityURL + "'" ); + } + entityValidator.put( publicId, entityURL ); + } + + /** + * Convenience method that registers DTD URLs for the specified public identifiers. + * + * @param entityValidator The URLs of entityValidator that have been registered, keyed by the public + * identifier that corresponds. + * @since 3.0 + */ + public void registerAll( final Map entityValidator ) { - match = ""; - bodyTexts.clear(); - params.clear(); - publicId = null; - stack.clear(); - stacksByName.clear(); - customContentHandler = null; + this.entityValidator.putAll( entityValidator ); } /** - * Return the top object on the stack without removing it. - * - * If there are no objects on the stack, return {@code null}. + * This method allows the "root" variable to be reset to null. + *

+ * It is not considered safe for a digester instance to be reused to parse multiple xml documents. However if you + * are determined to do so, then you should call both clear() and resetRoot() before each parse. * - * @param the type used to auto-cast the returned object to the assigned variable type - * @return the top object on the stack without removing it. + * @since 1.7 */ - public T peek() + public void resetRoot() { - try - { - return this. npeSafeCast( stack.peek() ); - } - catch ( final EmptyStackException e ) - { - log.warn( "Empty stack (returning null)" ); - return ( null ); - } + root = null; } /** - * Return the n'th object down the stack, where 0 is the top element and [getCount()-1] is the bottom element. If - * the specified index is out of range, return {@code null}. - * - * @param the type used to auto-cast the returned object to the assigned variable type - * @param n Index of the desired element, where 0 is the top of the stack, 1 is the next element down, and so on. - * @return the n'th object down the stack + * {@inheritDoc} */ - public T peek( final int n ) + @Override + public InputSource resolveEntity( final String publicId, final String systemId ) + throws SAXException { - final int index = ( stack.size() - 1 ) - n; - if ( index < 0 ) + if ( saxLog.isDebugEnabled() ) { - log.warn( "Empty stack (returning null)" ); - return ( null ); + saxLog.debug( "resolveEntity('" + publicId + "', '" + systemId + "')" ); } - try + + if ( publicId != null ) { - return this. npeSafeCast( stack.get( index ) ); + this.publicId = publicId; } - catch ( final EmptyStackException e ) + + // Has this system identifier been registered? + URL entityURL = null; + if ( publicId != null ) { - log.warn( "Empty stack (returning null)" ); - return ( null ); + entityURL = entityValidator.get( publicId ); } - } - /** - * Pop the top object off of the stack, and return it. If there are no objects on the stack, return - * {@code null}. - * - * @param the type used to auto-cast the returned object to the assigned variable type - * @return the top object popped off of the stack - */ - public T pop() - { - try + // Redirect the schema location to a local destination + if ( entityURL == null && systemId != null ) { - T popped = this. npeSafeCast( stack.pop() ); - if ( stackAction != null ) + entityURL = entityValidator.get( systemId ); + } + + if ( entityURL == null ) + { + if ( systemId == null ) { - popped = stackAction.onPop( this, null, popped ); + // cannot resolve + if ( log.isDebugEnabled() ) + { + log.debug( " Cannot resolve null entity, returning null InputSource" ); + } + return ( null ); + + } + // try to resolve using system ID + if ( log.isDebugEnabled() ) + { + log.debug( " Trying to resolve using system ID '" + systemId + "'" ); + } + try + { + entityURL = new URL( systemId ); + } + catch ( final MalformedURLException e ) + { + throw new IllegalArgumentException( "Malformed URL '" + systemId + "' : " + e.getMessage() ); } - return popped; } - catch ( final EmptyStackException e ) + + // Return an input source to our alternative URL + if ( log.isDebugEnabled() ) { - log.warn( "Empty stack (returning null)" ); - return ( null ); + log.debug( " Resolving to alternate DTD '" + entityURL + "'" ); } - } - /** - * Push a new object onto the top of the object stack. - * - * @param any type of the pushed object - * @param object The new object - */ - public void push( T object ) - { - if ( stackAction != null ) + try { - object = stackAction.onPush( this, null, object ); + return createInputSourceFromURL( entityURL ); } - - if ( stack.isEmpty() ) + catch ( final Exception e ) { - root = object; + throw createSAXException( e ); } - stack.push( object ); } /** - * Pushes the given object onto the stack with the given name. If no stack already exists with the given name then - * one will be created. + * Sets the class loader to be used for instantiating application objects when required. * - * @param any type of the pushed object - * @param stackName the name of the stack onto which the object should be pushed - * @param value the Object to be pushed onto the named stack. - * @since 1.6 + * @param classLoader The new class loader to use, or {@code null} to revert to the standard rules */ - public void push( final String stackName, T value ) + public void setClassLoader( final ClassLoader classLoader ) { - if ( stackAction != null ) - { - value = stackAction.onPush( this, stackName, value ); - } - - Stack namedStack = stacksByName.get( stackName ); - if ( namedStack == null ) - { - namedStack = new Stack(); - stacksByName.put( stackName, namedStack ); - } - namedStack.push( value ); + this.classLoader = classLoader; } + // --------------------------------------------------- Object Stack Methods + /** + * Redirects (or cancels redirecting) of SAX ContentHandler events to an external object. *

- * Pops (gets and removes) the top object from the stack with the given name. - *

+ * When this object's customContentHandler is non-null, any SAX events received from the parser will simply be + * passed on to the specified object instead of this object handling them. This allows Rule classes to take control + * of the SAX event stream for a while in order to do custom processing. Such a rule should save the old value + * before setting a new one, and restore the old value in order to resume normal digester processing. *

- * Note: a stack is considered empty if no objects have been pushed onto it yet. - *

+ * An example of a Rule which needs this feature is NodeCreateRule. + *

+ * Note that saving the old value is probably not needed as it should always be null; a custom rule that wants to + * take control could only have been called when there was no custom content handler. But it seems cleaner to + * properly save/restore the value and maybe some day this will come in useful. + *

+ * Note also that this is not quite equivalent to * - * @param the type used to auto-cast the returned object to the assigned variable type - * @param stackName the name of the stack from which the top value is to be popped. - * @return the top {@code Object} on the stack or throws {@code EmptyStackException} - * if the stack is either empty or has not been created yet - * @since 1.6 + *

+     * digester.getXMLReader().setContentHandler( handler )
+     * 
+ * + * for these reasons: + *
    + *
  • Some xml parsers don't like having setContentHandler called after parsing has started. The Aelfred parser is + * one example.
  • + *
  • Directing the events via the Digester object potentially allows us to log information about those SAX events + * at the digester level.
  • + *
+ * + * @param handler the custom SAX ContentHandler where events are redirected. + * @since 1.7 */ - public T pop( final String stackName ) + public void setCustomContentHandler( final ContentHandler handler ) { - final Stack namedStack = stacksByName.get( stackName ); - if ( namedStack == null ) + customContentHandler = handler; + } + + /** + * {@inheritDoc} + */ + @Override + public void setDocumentLocator( final Locator locator ) + { + if ( saxLog.isDebugEnabled() ) { - if ( log.isDebugEnabled() ) - { - log.debug( "Stack '" + stackName + "' is empty" ); - } - throw new EmptyStackException(); + saxLog.debug( "setDocumentLocator(" + locator + ")" ); } - T result = this. npeSafeCast( namedStack.pop() ); + this.locator = locator; + } - if ( stackAction != null ) - { - result = stackAction.onPop( this, stackName, result ); - } + /** + * Sets the {@code EntityResolver} used by SAX when resolving public id and system id. This must be called + * before the first call to {@code parse()}. + * + * @param entityResolver a class that implement the {@code EntityResolver} interface. + */ + public void setEntityResolver( final EntityResolver entityResolver ) + { + this.entityResolver = entityResolver; + } - return result; + /** + * Sets the error handler for this Digester. + * + * @param errorHandler The new error handler + */ + public void setErrorHandler( final ErrorHandler errorHandler ) + { + this.errorHandler = errorHandler; + } + + /** + * Sets the executor service to run asynchronous parse method. + * + * @param executorService the executor service to run asynchronous parse method + * @since 3.1 + */ + public void setExecutorService( final ExecutorService executorService ) + { + this.executorService = executorService; + } + + /** + * Sets a flag indicating whether the requested feature is supported by the underlying implementation of + * {@code org.xml.sax.XMLReader}. See the saxproject website for + * information about the standard SAX2 feature flags. In order to be effective, this method must be called + * before the {@code getParser()} method is called for the first time, either directly or + * indirectly. + * + * @param feature Name of the feature to set the status for + * @param value The new value for this feature + * @throws ParserConfigurationException if a parser configuration error occurs + * @throws SAXNotRecognizedException if the property name is not recognized + * @throws SAXNotSupportedException if the property name is recognized but not supported + */ + public void setFeature( final String feature, final boolean value ) + throws ParserConfigurationException, SAXNotRecognizedException, SAXNotSupportedException + { + getFactory().setFeature( feature, value ); } /** - *

- * Gets the top object from the stack with the given name. This method does not remove the object from the stack. - *

- *

- * Note: a stack is considered empty if no objects have been pushed onto it yet. - *

+ * Sets the current logger for this Digester. * - * @param the type used to auto-cast the returned object to the assigned variable type - * @param stackName the name of the stack to be peeked - * @return the top {@code Object} on the stack or null if the stack is either empty or has not been created yet - * @since 1.6 + * @param log the current logger for this Digester. */ - public T peek( final String stackName ) + public void setLogger( final Log log ) { - return this. npeSafeCast( peek( stackName, 0 ) ); + this.log = log; } /** - *

- * Gets the top object from the stack with the given name. This method does not remove the object from the stack. - *

- *

- * Note: a stack is considered empty if no objects have been pushed onto it yet. - *

+ * Sets the "namespace aware" flag for parsers we create. * - * @param the type used to auto-cast the returned object to the assigned variable type - * @param stackName the name of the stack to be peeked - * @param n Index of the desired element, where 0 is the top of the stack, 1 is the next element down, and so on. - * @return the specified {@code Object} on the stack. - * @since 1.6 + * @param namespaceAware The new "namespace aware" flag */ - public T peek( final String stackName, final int n ) + public void setNamespaceAware( final boolean namespaceAware ) { - T result; - final Stack namedStack = stacksByName.get( stackName ); - if ( namedStack == null ) - { - if ( log.isDebugEnabled() ) - { - log.debug( "Stack '" + stackName + "' is empty" ); - } - throw new EmptyStackException(); - } - - final int index = ( namedStack.size() - 1 ) - n; - if ( index < 0 ) - { - throw new EmptyStackException(); - } - result = this. npeSafeCast( namedStack.get( index ) ); + this.namespaceAware = namespaceAware; + } - return result; + /** + * Sets the current value of the specified property for the underlying {@code XMLReader} implementation. See the saxproject website for information about the standard SAX2 properties. + * + * @param property Property name to be set + * @param value Property value to be set + * @throws SAXNotRecognizedException if the property name is not recognized + * @throws SAXNotSupportedException if the property name is recognized but not supported + */ + public void setProperty( final String property, final Object value ) + throws SAXNotRecognizedException, SAXNotSupportedException + { + getParser().setProperty( property, value ); } /** - *

- * Is the stack with the given name empty? - *

- *

- * Note: a stack is considered empty if no objects have been pushed onto it yet. - *

+ * Sets the public id of the current file being parse. * - * @param stackName the name of the stack whose emptiness should be evaluated - * @return true if the given stack if empty - * @since 1.6 + * @param publicId the DTD/Schema public's id. */ - public boolean isEmpty( final String stackName ) + public void setPublicId( final String publicId ) { - boolean result = true; - final Stack namedStack = stacksByName.get( stackName ); - if ( namedStack != null ) - { - result = namedStack.isEmpty(); - } - return result; + this.publicId = publicId; } /** - * Returns the root element of the tree of objects created as a result of applying the rule objects to the input - * XML. - *

- * If the digester stack was "primed" by explicitly pushing a root object onto the stack before parsing started, - * then that root object is returned here. - *

- * Alternatively, if a Rule which creates an object (eg ObjectCreateRule) matched the root element of the xml, then - * the object created will be returned here. - *

- * In other cases, the object most recently pushed onto an empty digester stack is returned. This would be a most - * unusual use of digester, however; one of the previous configurations is much more likely. - *

- * Note that when using one of the Digester.parse methods, the return value from the parse method is exactly the - * same as the return value from this method. However when the Digester is being used as a SAXContentHandler, no - * such return value is available; in this case, this method allows you to access the root object that has been - * created after parsing has completed. + * Sets the namespace URI that will be applied to all subsequently added {@code Rule} objects. * - * @param the type used to auto-cast the returned object to the assigned variable type - * @return the root object that has been created after parsing or null if the digester has not parsed any XML yet. + * @param ruleNamespaceURI Namespace URI that must match on all subsequently added rules, or {@code null} for + * matching regardless of the current namespace URI */ - public T getRoot() + public void setRuleNamespaceURI( final String ruleNamespaceURI ) { - return this. npeSafeCast( root ); + getRules().setNamespaceURI( ruleNamespaceURI ); } /** - * This method allows the "root" variable to be reset to null. - *

- * It is not considered safe for a digester instance to be reused to parse multiple xml documents. However if you - * are determined to do so, then you should call both clear() and resetRoot() before each parse. + * Sets the {@code Rules} implementation object containing our rules collection and associated matching policy. * - * @since 1.7 + * @param rules New Rules implementation */ - public void resetRoot() + public void setRules( final Rules rules ) { - root = null; + this.rules = rules; + this.rules.setDigester( this ); } // ------------------------------------------------ Parameter Stack Methods @@ -2882,292 +2916,258 @@ public void resetRoot() // ------------------------------------------------------ Protected Methods /** - *

- * Clean up allocated resources after parsing is complete. The default method closes input streams that have been - * created by Digester itself. If you override this method in a subclass, be sure to call - * {@code super.cleanup()} to invoke this logic. - *

+ * Sets the logger used for logging SAX-related information. Note the output is finely grained. + * + * @param saxLog the logger used for logging SAX-related information, not null + * @since 1.6 + */ + public void setSAXLogger( final Log saxLog ) + { + this.saxLog = saxLog; + } + + /** + * Define a callback object which is invoked whenever an object is pushed onto a digester object stack, + * or popped off one. * + * @param stackAction the callback object which is invoked whenever an object is pushed onto a digester + * object stack, or popped off one. * @since 1.8 */ - protected void cleanup() + public void setStackAction( final StackAction stackAction ) { - // If we created any InputSource objects in this instance, - // they each have an input stream that should be closed - for ( final InputSource source : inputSources ) - { - try - { - source.getByteStream().close(); - } - catch ( final IOException e ) - { - // Fall through so we get them all - if ( log.isWarnEnabled() ) - { - log.warn( format( "An error occurred while closing resource %s (%s)", - source.getPublicId(), - source.getSystemId() ), e ); - } - } - } - inputSources.clear(); + this.stackAction = stackAction; } /** - *

- * Provide a hook for lazy configuration of this {@code Digester} instance. The default implementation does - * nothing, but subclasses can override as needed. - *

- *

- * Note This method may be called more than once. Once only initialization code should be placed in - * {@link #initialize} or the code should take responsibility by checking and setting the {@link #configured} flag. - *

+ * Sets the {@code Substitutor} to be used to convert attributes and body text. + * + * @param substitutor the Substitutor to be used to convert attributes and body text or null if not substitution of + * these values is to be performed. */ - protected void configure() + public void setSubstitutor( final Substitutor substitutor ) { - // Do not configure more than once - if ( configured ) - { - return; - } + this.substitutor = substitutor; + } - // Perform lazy configuration as needed - initialize(); // call hook method for subclasses that want to be initialized once only - // Nothing else required by default + /** + * Determine whether to use the Context ClassLoader (the one found by calling + * {@code Thread.currentThread().getContextClassLoader()}) to resolve/load classes that are defined in various + * rules. If not using Context ClassLoader, then the class-loading defaults to using the calling-class' ClassLoader. + * + * @param use determines whether to use Context ClassLoader. + */ + public void setUseContextClassLoader( final boolean use ) + { + useContextClassLoader = use; + } - // Set the configuration flag to avoid repeating - configured = true; + // -------------------------------------------------------- Package Methods + + /** + * Sets the validating parser flag. This must be called before {@code parse()} is called the first time. + * By default the value of this is set to false. + * + * It essentially just controls the DTD validation. To use modern schema languages use the + * {@link #setXMLSchema(Schema)} method to associate a schema to a parser. + * + * @param validating The new validating parser flag. + * @see javax.xml.parsers.SAXParserFactory#setValidating(boolean) for more detail. + */ + public void setValidating( final boolean validating ) + { + this.validating = validating; } /** - * Checks the Digester instance has been configured. + * Sets the XInclude-aware flag for parsers we create. This additionally requires namespace-awareness. * - * @return true, if the Digester instance has been configured, false otherwise - * @since 3.0 + * @param xincludeAware The new XInclude-aware flag + * @see #setNamespaceAware(boolean) + * @since 2.0 */ - public boolean isConfigured() + public void setXIncludeAware( final boolean xincludeAware ) { - return configured; + this.xincludeAware = xincludeAware; } /** - *

- * Provides a hook for lazy initialization of this {@code Digester} instance. The default implementation does - * nothing, but subclasses can override as needed. Digester (by default) only calls this method once. - *

- *

- * Note This method will be called by {@link #configure} only when the {@link #configured} flag is - * false. Subclasses that override {@code configure} or who set {@code configured} may find that this - * method may be called more than once. - *

+ * Sets the XML Schema to be used when parsing. * - * @since 1.6 + * @param schema The {@link Schema} instance to use. + * @since 2.0 */ - protected void initialize() + public void setXMLSchema( final Schema schema ) { - // Perform lazy initialization as needed - // Nothing required by default + this.schema = schema; } - // -------------------------------------------------------- Package Methods - /** - * Return the set of DTD URL registrations, keyed by public identifier. NOTE: the returned map is in read-only mode. - * - * @return the read-only Map of DTD URL registrations. + * {@inheritDoc} */ - Map getRegistrations() + @Override + public void skippedEntity( final String name ) + throws SAXException { - return Collections.unmodifiableMap( entityValidator ); + if ( saxLog.isDebugEnabled() ) + { + saxLog.debug( "skippedEntity(" + name + ")" ); + } + + // No processing required } /** - *

- * Return the top object on the parameters stack without removing it. If there are no objects on the stack, return - * {@code null}. - *

- *

- * The parameters stack is used to store {@code CallMethodRule} parameters. See {@link #params}. - *

- * - * @return the top object on the parameters stack without removing it. + * {@inheritDoc} */ - public Object[] peekParams() + @Override + public void startDocument() + throws SAXException { - try - { - return ( params.peek() ); - } - catch ( final EmptyStackException e ) + if ( saxLog.isDebugEnabled() ) { - log.warn( "Empty stack (returning null)" ); - return ( null ); + saxLog.debug( "startDocument()" ); } + + // ensure that the digester is properly configured, as + // the digester could be used as a SAX ContentHandler + // rather than via the parse() methods. + configure(); } /** - *

- * Return the n'th object down the parameters stack, where 0 is the top element and [getCount()-1] is the bottom - * element. If the specified index is out of range, return {@code null}. - *

- *

- * The parameters stack is used to store {@code CallMethodRule} parameters. See {@link #params}. - *

- * - * @param n Index of the desired element, where 0 is the top of the stack, 1 is the next element down, and so on. - * @return the n'th object down the parameters stack + * {@inheritDoc} */ - public Object[] peekParams( final int n ) + @Override + public void startElement( final String namespaceURI, final String localName, final String qName, Attributes list ) + throws SAXException { - final int index = ( params.size() - 1 ) - n; - if ( index < 0 ) + final boolean debug = log.isDebugEnabled(); + + if ( customContentHandler != null ) { - log.warn( "Empty stack (returning null)" ); - return ( null ); + // forward calls instead of handling them here + customContentHandler.startElement( namespaceURI, localName, qName, list ); + return; } - try + + if ( saxLog.isDebugEnabled() ) { - return ( params.get( index ) ); + saxLog.debug( "startElement(" + namespaceURI + "," + localName + "," + qName + ")" ); } - catch ( final EmptyStackException e ) + + // Save the body text accumulated for our surrounding element + bodyTexts.push( bodyText ); + if ( debug ) { - log.warn( "Empty stack (returning null)" ); - return ( null ); + log.debug( " Pushing body text '" + bodyText.toString() + "'" ); } - } + bodyText = new StringBuilder(); - /** - *

- * Pop the top object off of the parameters stack, and return it. If there are no objects on the stack, return - * {@code null}. - *

- *

- * The parameters stack is used to store {@code CallMethodRule} parameters. See {@link #params}. - *

- * - * @return the top object popped off of the parameters stack - */ - public Object[] popParams() - { - try + // the actual element name is either in localName or qName, depending + // on whether the parser is namespace aware + String name = localName; + if ( ( name == null ) || ( name.length() < 1 ) ) { - if ( log.isTraceEnabled() ) - { - log.trace( "Popping params" ); - } - return ( params.pop() ); + name = qName; } - catch ( final EmptyStackException e ) + + // Compute the current matching rule + final StringBuilder sb = new StringBuilder( match ); + if ( !match.isEmpty() ) { - log.warn( "Empty stack (returning null)" ); - return ( null ); + sb.append( '/' ); } - } - - /** - *

- * Push a new object onto the top of the parameters stack. - *

- *

- * The parameters stack is used to store {@code CallMethodRule} parameters. See {@link #params}. - *

- * - * @param object The new object - */ - public void pushParams( final Object... object ) - { - if ( log.isTraceEnabled() ) + sb.append( name ); + match = sb.toString(); + if ( debug ) { - log.trace( "Pushing params" ); + log.debug( " New match='" + match + "'" ); } - params.push( object ); - } - /** - * Create a SAX exception which also understands about the location in the digester file where the exception occurs - * - * @param message the custom SAX exception message - * @param e the exception cause - * @return the new SAX exception - */ - public SAXException createSAXException( final String message, Exception e ) - { - if ( ( e != null ) && ( e instanceof InvocationTargetException ) ) + // Fire "begin" events for all relevant rules + final List rules = getRules().match( namespaceURI, match, localName, list ); + matches.push( rules ); + if ( ( rules != null ) && ( !rules.isEmpty() ) ) { - final Throwable t = ( (InvocationTargetException) e ).getTargetException(); - if ( ( t != null ) && ( t instanceof Exception ) ) + final Substitutor substitutor = getSubstitutor(); + if ( substitutor != null ) { - e = (Exception) t; + list = substitutor.substitute( list ); + } + for (final Rule rule : rules) { + try + { + if ( debug ) + { + log.debug( " Fire begin() for " + rule ); + } + rule.begin( namespaceURI, name, list ); + } + catch ( final Exception e ) + { + log.error( "Begin event threw exception", e ); + throw createSAXException( e ); + } + catch ( final Error e ) + { + log.error( "Begin event threw error", e ); + throw e; + } } } - if ( locator != null ) + else { - final String error = - "Error at line " + locator.getLineNumber() + " char " + locator.getColumnNumber() + ": " + message; - if ( e != null ) + if ( debug ) { - return new SAXParseException( error, locator, e ); + log.debug( " No rules found matching '" + match + "'." ); } - return new SAXParseException( error, locator ); - } - log.error( "No Locator!" ); - if ( e != null ) - { - return new SAXException( message, e ); } - return new SAXException( message ); } /** - * Create a SAX exception which also understands about the location in the digester file where the exception occurs - * - * @param e the exception cause - * @return the new SAX exception + * {@inheritDoc} */ - public SAXException createSAXException( Exception e ) + @Override + public void startPrefixMapping( final String prefix, final String namespaceURI ) + throws SAXException { - if ( e instanceof InvocationTargetException ) + if ( saxLog.isDebugEnabled() ) { - final Throwable t = ( (InvocationTargetException) e ).getTargetException(); - if ( ( t != null ) && ( t instanceof Exception ) ) - { - e = (Exception) t; - } + saxLog.debug( "startPrefixMapping(" + prefix + "," + namespaceURI + ")" ); } - return createSAXException( e.getMessage(), e ); + + // Register this prefix mapping + Stack stack = namespaces.get( prefix ); + if ( stack == null ) + { + stack = new Stack(); + namespaces.put( prefix, stack ); + } + stack.push( namespaceURI ); } /** - * Create a SAX exception which also understands about the location in the digester file where the exception occurs - * - * @param message the custom SAX exception message - * @return the new SAX exception + * {@inheritDoc} */ - public SAXException createSAXException( final String message ) + @Override + public void unparsedEntityDecl( final String name, final String publicId, final String systemId, final String notation ) { - return createSAXException( message, null ); + if ( saxLog.isDebugEnabled() ) + { + saxLog.debug( "unparsedEntityDecl(" + name + "," + publicId + "," + systemId + "," + notation + ")" ); + } } /** - * Helps casting the input object to given type, avoiding NPEs. - * - * @since 3.0 - * @param the type the input object has to be cast. - * @param obj the object has to be cast. - * @return the casted object, if input object is not null, null otherwise. + * {@inheritDoc} */ - private T npeSafeCast( final Object obj ) + @Override + public void warning( final SAXParseException exception ) + throws SAXException { - if ( obj == null ) - { - return null; - } - - @SuppressWarnings( "unchecked" ) - final - T result = (T) obj; - return result; + log.warn( "Parse Warning Error at line " + exception.getLineNumber() + " column " + + exception.getColumnNumber() + ": " + exception.getMessage(), exception ); } } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/ExtendedBaseRules.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/ExtendedBaseRules.java index 806127290..5a0378279 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/ExtendedBaseRules.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/ExtendedBaseRules.java @@ -161,14 +161,40 @@ public class ExtendedBaseRules // --------------------------------------------------------- Public Methods /** - * {@inheritDoc} + * Standard match. Matches the end of the pattern to the key. + * + * @param key The key to be found + * @param pattern The pattern where looking for the key + * @return true, if {@code key} is found inside {@code pattern}, false otherwise */ - @Override - protected void registerRule( final String pattern, final Rule rule ) + private boolean basicMatch( final String key, final String pattern ) { - super.registerRule( pattern, rule ); - counter++; - order.put( rule, counter ); + return ( pattern.equals( key.substring( 2 ) ) || pattern.endsWith( key.substring( 1 ) ) ); + } + + /** + * Finds an exact ancester match for given pattern + * + * @param parentPattern The input pattern + * @return A list of {@code Rule} related to the input pattern + */ + private List findExactAncesterMatch( final String parentPattern ) + { + List matchingRules = null; + int lastIndex = parentPattern.length(); + while ( lastIndex-- > 0 ) + { + lastIndex = parentPattern.lastIndexOf( '/', lastIndex ); + if ( lastIndex > 0 ) + { + matchingRules = this.cache.get( parentPattern.substring( 0, lastIndex ) + "/*" ); + if ( matchingRules != null ) + { + return matchingRules; + } + } + } + return null; } /** @@ -490,40 +516,14 @@ private boolean parentMatch( final String key, final String parentPattern ) } /** - * Standard match. Matches the end of the pattern to the key. - * - * @param key The key to be found - * @param pattern The pattern where looking for the key - * @return true, if {@code key} is found inside {@code pattern}, false otherwise - */ - private boolean basicMatch( final String key, final String pattern ) - { - return ( pattern.equals( key.substring( 2 ) ) || pattern.endsWith( key.substring( 1 ) ) ); - } - - /** - * Finds an exact ancester match for given pattern - * - * @param parentPattern The input pattern - * @return A list of {@code Rule} related to the input pattern + * {@inheritDoc} */ - private List findExactAncesterMatch( final String parentPattern ) + @Override + protected void registerRule( final String pattern, final Rule rule ) { - List matchingRules = null; - int lastIndex = parentPattern.length(); - while ( lastIndex-- > 0 ) - { - lastIndex = parentPattern.lastIndexOf( '/', lastIndex ); - if ( lastIndex > 0 ) - { - matchingRules = this.cache.get( parentPattern.substring( 0, lastIndex ) + "/*" ); - if ( matchingRules != null ) - { - return matchingRules; - } - } - } - return null; + super.registerRule( pattern, rule ); + counter++; + order.put( rule, counter ); } } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/FactoryCreateRule.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/FactoryCreateRule.java index 04623beff..e00afb5fd 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/FactoryCreateRule.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/FactoryCreateRule.java @@ -50,74 +50,83 @@ public class FactoryCreateRule // ----------------------------------------------------------- Constructors + /** + * The attribute containing an override class name if it is present. + */ + protected String attributeName; + + /** + * The Java class name of the ObjectCreationFactory to be created. This class must have a no-arguments constructor. + */ + protected String className; + + /** + * The object creation factory we will use to instantiate objects as required based on the attributes specified in + * the matched XML element. + */ + protected ObjectCreationFactory creationFactory; + /** *

- * Construct a factory create rule that will use the specified class name to create an {@link ObjectCreationFactory} + * Construct a factory create rule that will use the specified class to create an {@link ObjectCreationFactory} * which will then be used to create an object and push it on the stack. *

*

* Exceptions thrown during the object creation process will be propagated. *

* - * @param className Java class name of the object creation factory class + * @param clazz Java class name of the object creation factory class */ - public FactoryCreateRule( final String className ) + public FactoryCreateRule( final Class> clazz ) { - this( className, false ); + this( clazz, false ); } /** - *

* Construct a factory create rule that will use the specified class to create an {@link ObjectCreationFactory} * which will then be used to create an object and push it on the stack. - *

- *

- * Exceptions thrown during the object creation process will be propagated. - *

* * @param clazz Java class name of the object creation factory class + * @param ignoreCreateExceptions if true, exceptions thrown by the object creation factory will be ignored. */ - public FactoryCreateRule( final Class> clazz ) + public FactoryCreateRule( final Class> clazz, final boolean ignoreCreateExceptions ) { - this( clazz, false ); + this( clazz, null, ignoreCreateExceptions ); } /** *

- * Construct a factory create rule that will use the specified class name (possibly overridden by the specified - * attribute if present) to create an {@link ObjectCreationFactory}, which will then be used to instantiate an - * object instance and push it onto the stack. + * Construct a factory create rule that will use the specified class (possibly overridden by the specified attribute + * if present) to create an {@link ObjectCreationFactory}, which will then be used to instantiate an object instance + * and push it onto the stack. *

*

* Exceptions thrown during the object creation process will be propagated. *

* - * @param className Default Java class name of the factory class + * @param clazz Default Java class name of the factory class * @param attributeName Attribute name which, if present, contains an override of the class name of the object * creation factory to create. */ - public FactoryCreateRule( final String className, final String attributeName ) + public FactoryCreateRule( final Class> clazz, final String attributeName ) { - this( className, attributeName, false ); + this( clazz, attributeName, false ); } /** - *

* Construct a factory create rule that will use the specified class (possibly overridden by the specified attribute * if present) to create an {@link ObjectCreationFactory}, which will then be used to instantiate an object instance * and push it onto the stack. - *

- *

- * Exceptions thrown during the object creation process will be propagated. - *

* * @param clazz Default Java class name of the factory class * @param attributeName Attribute name which, if present, contains an override of the class name of the object * creation factory to create. + * @param ignoreCreateExceptions if true, exceptions thrown by the object creation factory will be ignored. */ - public FactoryCreateRule( final Class> clazz, final String attributeName ) + public FactoryCreateRule( final Class> clazz, final String attributeName, + final boolean ignoreCreateExceptions ) { - this( clazz, attributeName, false ); + this( clazz.getName(), attributeName, ignoreCreateExceptions ); } /** @@ -136,92 +145,83 @@ public FactoryCreateRule( final ObjectCreationFactory creationFactory ) } /** + * Construct a factory create rule using the given, already instantiated, {@link ObjectCreationFactory}. + * + * @param creationFactory called on to create the object. + * @param ignoreCreateExceptions if true, exceptions thrown by the object creation factory will be ignored. + */ + public FactoryCreateRule( final ObjectCreationFactory creationFactory, final boolean ignoreCreateExceptions ) + { + this.creationFactory = creationFactory; + this.ignoreCreateExceptions = ignoreCreateExceptions; + } + + /** + *

* Construct a factory create rule that will use the specified class name to create an {@link ObjectCreationFactory} * which will then be used to create an object and push it on the stack. + *

+ *

+ * Exceptions thrown during the object creation process will be propagated. + *

* * @param className Java class name of the object creation factory class - * @param ignoreCreateExceptions if true, exceptions thrown by the object creation factory will be ignored. */ - public FactoryCreateRule( final String className, final boolean ignoreCreateExceptions ) + public FactoryCreateRule( final String className ) { - this( className, null, ignoreCreateExceptions ); + this( className, false ); } + // ----------------------------------------------------- Instance Variables + /** - * Construct a factory create rule that will use the specified class to create an {@link ObjectCreationFactory} + * Construct a factory create rule that will use the specified class name to create an {@link ObjectCreationFactory} * which will then be used to create an object and push it on the stack. * - * @param clazz Java class name of the object creation factory class + * @param className Java class name of the object creation factory class * @param ignoreCreateExceptions if true, exceptions thrown by the object creation factory will be ignored. */ - public FactoryCreateRule( final Class> clazz, final boolean ignoreCreateExceptions ) + public FactoryCreateRule( final String className, final boolean ignoreCreateExceptions ) { - this( clazz, null, ignoreCreateExceptions ); + this( className, null, ignoreCreateExceptions ); } /** + *

* Construct a factory create rule that will use the specified class name (possibly overridden by the specified * attribute if present) to create an {@link ObjectCreationFactory}, which will then be used to instantiate an * object instance and push it onto the stack. + *

+ *

+ * Exceptions thrown during the object creation process will be propagated. + *

* * @param className Default Java class name of the factory class * @param attributeName Attribute name which, if present, contains an override of the class name of the object * creation factory to create. - * @param ignoreCreateExceptions if true, exceptions thrown by the object creation factory will be ignored. */ - public FactoryCreateRule( final String className, final String attributeName, final boolean ignoreCreateExceptions ) + public FactoryCreateRule( final String className, final String attributeName ) { - this.className = className; - this.attributeName = attributeName; - this.ignoreCreateExceptions = ignoreCreateExceptions; + this( className, attributeName, false ); } /** - * Construct a factory create rule that will use the specified class (possibly overridden by the specified attribute - * if present) to create an {@link ObjectCreationFactory}, which will then be used to instantiate an object instance - * and push it onto the stack. + * Construct a factory create rule that will use the specified class name (possibly overridden by the specified + * attribute if present) to create an {@link ObjectCreationFactory}, which will then be used to instantiate an + * object instance and push it onto the stack. * - * @param clazz Default Java class name of the factory class + * @param className Default Java class name of the factory class * @param attributeName Attribute name which, if present, contains an override of the class name of the object * creation factory to create. * @param ignoreCreateExceptions if true, exceptions thrown by the object creation factory will be ignored. */ - public FactoryCreateRule( final Class> clazz, final String attributeName, - final boolean ignoreCreateExceptions ) - { - this( clazz.getName(), attributeName, ignoreCreateExceptions ); - } - - /** - * Construct a factory create rule using the given, already instantiated, {@link ObjectCreationFactory}. - * - * @param creationFactory called on to create the object. - * @param ignoreCreateExceptions if true, exceptions thrown by the object creation factory will be ignored. - */ - public FactoryCreateRule( final ObjectCreationFactory creationFactory, final boolean ignoreCreateExceptions ) + public FactoryCreateRule( final String className, final String attributeName, final boolean ignoreCreateExceptions ) { - this.creationFactory = creationFactory; + this.className = className; + this.attributeName = attributeName; this.ignoreCreateExceptions = ignoreCreateExceptions; } - // ----------------------------------------------------- Instance Variables - - /** - * The attribute containing an override class name if it is present. - */ - protected String attributeName; - - /** - * The Java class name of the ObjectCreationFactory to be created. This class must have a no-arguments constructor. - */ - protected String className; - - /** - * The object creation factory we will use to instantiate objects as required based on the attributes specified in - * the matched XML element. - */ - protected ObjectCreationFactory creationFactory; - // --------------------------------------------------------- Public Methods /** @@ -332,24 +332,6 @@ public void finish() } } - /** - * {@inheritDoc} - */ - @Override - public String toString() - { - final Formatter formatter = new Formatter().format( "FactoryCreateRule[className=%s, attributeName=%s", - className, attributeName ); - if ( creationFactory != null ) - { - formatter.format( ", creationFactory=%s", creationFactory ); - } - formatter.format( "]" ); - return ( formatter.toString() ); - } - - // ------------------------------------------------------ Protected Methods - /** * Return an instance of our associated object creation factory, creating one if necessary. * @@ -383,4 +365,22 @@ protected ObjectCreationFactory getFactory( final Attributes attributes ) return ( creationFactory ); } + // ------------------------------------------------------ Protected Methods + + /** + * {@inheritDoc} + */ + @Override + public String toString() + { + final Formatter formatter = new Formatter().format( "FactoryCreateRule[className=%s, attributeName=%s", + className, attributeName ); + if ( creationFactory != null ) + { + formatter.format( ", creationFactory=%s", creationFactory ); + } + formatter.format( "]" ); + return ( formatter.toString() ); + } + } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/NodeCreateRule.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/NodeCreateRule.java index 1750c5ba5..911ec482c 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/NodeCreateRule.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/NodeCreateRule.java @@ -76,35 +76,13 @@ private class NodeBuilder // ------------------------------------------------------- Constructors - /** - * Constructor. - *

- * Stores the content handler currently used by Digester so it can be reset when done, and initializes the DOM - * objects needed to build the node. - *

- * - * @param doc the document to use to create nodes - * @param root the root node - * @throws ParserConfigurationException if the DocumentBuilderFactory could not be instantiated - * @throws SAXException if the XMLReader could not be instantiated by Digester (should not happen) - */ - public NodeBuilder( final Document doc, final Node root ) - throws ParserConfigurationException, SAXException - { - this.doc = doc; - this.root = root; - this.top = root; - - oldContentHandler = getDigester().getCustomContentHandler(); - } - - // ------------------------------------------------- Instance Variables - /** * The content handler used by Digester before it was set to this content handler. */ protected ContentHandler oldContentHandler; + // ------------------------------------------------- Instance Variables + /** * Depth of the current node, relative to the element where the content handler was put into action. */ @@ -130,6 +108,28 @@ public NodeBuilder( final Document doc, final Node root ) */ protected StringBuilder topText = new StringBuilder(); + /** + * Constructor. + *

+ * Stores the content handler currently used by Digester so it can be reset when done, and initializes the DOM + * objects needed to build the node. + *

+ * + * @param doc the document to use to create nodes + * @param root the root node + * @throws ParserConfigurationException if the DocumentBuilderFactory could not be instantiated + * @throws SAXException if the XMLReader could not be instantiated by Digester (should not happen) + */ + public NodeBuilder( final Document doc, final Node root ) + throws ParserConfigurationException, SAXException + { + this.doc = doc; + this.root = root; + this.top = root; + + oldContentHandler = getDigester().getCustomContentHandler(); + } + // --------------------------------------------- Helper Methods /** @@ -297,6 +297,18 @@ public void startElement( final String namespaceURI, final String localName, fin // ----------------------------------------------------------- Constructors + /** + * The JAXP {@code DocumentBuilder} to use. + */ + private DocumentBuilder documentBuilder; + + /** + * The type of the node that should be created. Must be one of the constants defined in {@link org.w3c.dom.Node + * Node}, but currently only {@link org.w3c.dom.Node#ELEMENT_NODE Node.ELEMENT_NODE} and + * {@link org.w3c.dom.Node#DOCUMENT_FRAGMENT_NODE Node.DOCUMENT_FRAGMENT_NODE} are allowed values. + */ + private int nodeType = Node.ELEMENT_NODE; + /** * Default constructor. Creates an instance of this rule that will create a DOM {@link org.w3c.dom.Element Element}. * @@ -321,6 +333,8 @@ public NodeCreateRule( final DocumentBuilder documentBuilder ) this( Node.ELEMENT_NODE, documentBuilder ); } + // ----------------------------------------------------- Instance Variables + /** * Constructor. Creates an instance of this rule that will create either a DOM {@link org.w3c.dom.Element Element} * or a DOM {@link org.w3c.dom.DocumentFragment DocumentFragment}, depending on the value of the @@ -358,20 +372,6 @@ public NodeCreateRule( final int nodeType, final DocumentBuilder documentBuilder this.documentBuilder = documentBuilder; } - // ----------------------------------------------------- Instance Variables - - /** - * The JAXP {@code DocumentBuilder} to use. - */ - private DocumentBuilder documentBuilder; - - /** - * The type of the node that should be created. Must be one of the constants defined in {@link org.w3c.dom.Node - * Node}, but currently only {@link org.w3c.dom.Node#ELEMENT_NODE Node.ELEMENT_NODE} and - * {@link org.w3c.dom.Node#DOCUMENT_FRAGMENT_NODE Node.DOCUMENT_FRAGMENT_NODE} are allowed values. - */ - private int nodeType = Node.ELEMENT_NODE; - // ----------------------------------------------------------- Rule Methods /** diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/ObjectCreateRule.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/ObjectCreateRule.java index 833b9a6c2..e3ff25ea5 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/ObjectCreateRule.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/ObjectCreateRule.java @@ -58,6 +58,20 @@ private static final class DeferredConstructionCallback implements MethodInterce this.constructorArgs = constructorArgs; } + void establishDelegate() + throws Exception + { + convertTo( constructor.getParameterTypes(), constructorArgs ); + delegate = constructor.newInstance( constructorArgs ); + for ( final RecordedInvocation invocation : invocations ) + { + invocation.getInvokedMethod().invoke( delegate, invocation.getArguments() ); + } + constructor = null; + constructorArgs = null; + invocations = null; + } + @Override public Object intercept( final Object obj, final Method method, final Object[] args, final MethodProxy proxy ) throws Throwable @@ -73,20 +87,6 @@ public Object intercept( final Object obj, final Method method, final Object[] a } return proxy.invokeSuper( obj, args ); } - - void establishDelegate() - throws Exception - { - convertTo( constructor.getParameterTypes(), constructorArgs ); - delegate = constructor.newInstance( constructorArgs ); - for ( final RecordedInvocation invocation : invocations ) - { - invocation.getInvokedMethod().invoke( delegate, invocation.getArguments() ); - } - constructor = null; - constructorArgs = null; - invocations = null; - } } private static final class ProxyManager @@ -187,54 +187,24 @@ void finalize( final Object proxy ) // ----------------------------------------------------------- Constructors - /** - * Construct an object create rule with the specified class name. - * - * @param className Java class name of the object to be created - */ - public ObjectCreateRule( final String className ) - { - this( className, (String) null ); - } - - /** - * Construct an object create rule with the specified class. - * - * @param clazz Java class name of the object to be created - */ - public ObjectCreateRule( final Class clazz ) - { - this( clazz.getName(), (String) null ); - this.clazz = clazz; - } - - /** - * Construct an object create rule with the specified class name and an optional attribute name containing an - * override. - * - * @param className Java class name of the object to be created - * @param attributeName Attribute name which, if present, contains an override of the class name to create - */ - public ObjectCreateRule( final String className, final String attributeName ) - { - this.className = className; - this.attributeName = attributeName; - } - - /** - * Construct an object create rule with the specified class and an optional attribute name containing an override. - * - * @param attributeName Attribute name which, if present, contains an - * @param clazz Java class name of the object to be created override of the class name to create - */ - public ObjectCreateRule( final String attributeName, final Class clazz ) + private static void convertTo( final Class[] types, final Object[] array ) { - this( clazz != null ? clazz.getName() : null, attributeName ); - this.clazz = clazz; + if ( array.length != types.length ) + { + throw new IllegalArgumentException(); + } + // this piece of code is adapted from CallMethodRule + for ( int i = 0; i < array.length; i++ ) + { + // convert nulls and convert stringy parameters for non-stringy param types + if ( array[i] == null + || ( array[i] instanceof String && !String.class.isAssignableFrom( types[i] ) ) ) + { + array[i] = convert( (String) array[i], types[i] ); + } + } } - // ----------------------------------------------------- Instance Variables - /** * The attribute containing an override class name if it is present. */ @@ -250,6 +220,8 @@ public ObjectCreateRule( final String attributeName, final Class clazz ) */ protected String className; + // ----------------------------------------------------- Instance Variables + /** * The constructor argument types. * @@ -271,41 +243,52 @@ public ObjectCreateRule( final String attributeName, final Class clazz ) */ private ProxyManager proxyManager; - // --------------------------------------------------------- Public Methods - /** - * Allows users to specify constructor argument types. + * Construct an object create rule with the specified class. * - * @param constructorArgumentTypes the constructor argument types - * @since 3.2 + * @param clazz Java class name of the object to be created */ - public void setConstructorArgumentTypes( final Class... constructorArgumentTypes ) + public ObjectCreateRule( final Class clazz ) { - if ( constructorArgumentTypes == null ) - { - throw new IllegalArgumentException( "Parameter 'constructorArgumentTypes' must not be null" ); - } + this( clazz.getName(), (String) null ); + this.clazz = clazz; + } - this.constructorArgumentTypes = constructorArgumentTypes; + /** + * Construct an object create rule with the specified class name. + * + * @param className Java class name of the object to be created + */ + public ObjectCreateRule( final String className ) + { + this( className, (String) null ); } /** - * Allows users to specify default constructor arguments. If a default/no-arg constructor is not available - * for the target class, these arguments will be used to create the proxy object. For any argument - * not supplied by a {@link CallParamRule}, the corresponding item from this array will be used - * to construct the final object as well. + * Construct an object create rule with the specified class and an optional attribute name containing an override. * - * @param constructorArguments the default constructor arguments. - * @since 3.2 + * @param attributeName Attribute name which, if present, contains an + * @param clazz Java class name of the object to be created override of the class name to create */ - public void setDefaultConstructorArguments( final Object... constructorArguments ) + public ObjectCreateRule( final String attributeName, final Class clazz ) { - if ( constructorArguments == null ) - { - throw new IllegalArgumentException( "Parameter 'constructorArguments' must not be null" ); - } + this( clazz != null ? clazz.getName() : null, attributeName ); + this.clazz = clazz; + } - this.defaultConstructorArguments = constructorArguments; + // --------------------------------------------------------- Public Methods + + /** + * Construct an object create rule with the specified class name and an optional attribute name containing an + * override. + * + * @param className Java class name of the object to be created + * @param attributeName Attribute name which, if present, contains an override of the class name to create + */ + public ObjectCreateRule( final String className, final String attributeName ) + { + this.className = className; + this.attributeName = attributeName; } /** @@ -397,30 +380,47 @@ public void end( final String namespace, final String name ) } /** - * {@inheritDoc} + * Allows users to specify constructor argument types. + * + * @param constructorArgumentTypes the constructor argument types + * @since 3.2 */ - @Override - public String toString() + public void setConstructorArgumentTypes( final Class... constructorArgumentTypes ) { - return format( "ObjectCreateRule[className=%s, attributeName=%s]", className, attributeName ); + if ( constructorArgumentTypes == null ) + { + throw new IllegalArgumentException( "Parameter 'constructorArgumentTypes' must not be null" ); + } + + this.constructorArgumentTypes = constructorArgumentTypes; } - private static void convertTo( final Class[] types, final Object[] array ) + /** + * Allows users to specify default constructor arguments. If a default/no-arg constructor is not available + * for the target class, these arguments will be used to create the proxy object. For any argument + * not supplied by a {@link CallParamRule}, the corresponding item from this array will be used + * to construct the final object as well. + * + * @param constructorArguments the default constructor arguments. + * @since 3.2 + */ + public void setDefaultConstructorArguments( final Object... constructorArguments ) { - if ( array.length != types.length ) - { - throw new IllegalArgumentException(); - } - // this piece of code is adapted from CallMethodRule - for ( int i = 0; i < array.length; i++ ) + if ( constructorArguments == null ) { - // convert nulls and convert stringy parameters for non-stringy param types - if ( array[i] == null - || ( array[i] instanceof String && !String.class.isAssignableFrom( types[i] ) ) ) - { - array[i] = convert( (String) array[i], types[i] ); - } + throw new IllegalArgumentException( "Parameter 'constructorArguments' must not be null" ); } + + this.defaultConstructorArguments = constructorArguments; + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() + { + return format( "ObjectCreateRule[className=%s, attributeName=%s]", className, attributeName ); } } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/ObjectParamRule.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/ObjectParamRule.java index e92928878..c2ee1239b 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/ObjectParamRule.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/ObjectParamRule.java @@ -42,6 +42,23 @@ public class ObjectParamRule extends Rule { + /** + * The attribute which we are attempting to match + */ + protected String attributeName; + + /** + * The zero-relative index of the parameter we are saving. + */ + protected int paramIndex; + + // ----------------------------------------------------- Instance Variables + + /** + * The parameter we wish to pass to the method call + */ + protected Object param; + // ----------------------------------------------------------- Constructors /** * Construct a "call parameter" rule that will save the given Object as the parameter value. @@ -69,23 +86,6 @@ public ObjectParamRule( final int paramIndex, final String attributeName, final this.param = param; } - // ----------------------------------------------------- Instance Variables - - /** - * The attribute which we are attempting to match - */ - protected String attributeName; - - /** - * The zero-relative index of the parameter we are saving. - */ - protected int paramIndex; - - /** - * The parameter we wish to pass to the method call - */ - protected Object param; - // --------------------------------------------------------- Public Methods /** diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/PathCallParamRule.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/PathCallParamRule.java index db2472bd9..b17fac0da 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/PathCallParamRule.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/PathCallParamRule.java @@ -38,6 +38,13 @@ public class PathCallParamRule // ----------------------------------------------------------- Constructors + /** + * The zero-relative index of the parameter we are saving. + */ + protected int paramIndex; + + // ----------------------------------------------------- Instance Variables + /** * Construct a "call parameter" rule that will save the body text of this element as the parameter value. * @@ -48,13 +55,6 @@ public PathCallParamRule( final int paramIndex ) this.paramIndex = paramIndex; } - // ----------------------------------------------------- Instance Variables - - /** - * The zero-relative index of the parameter we are saving. - */ - protected int paramIndex; - // --------------------------------------------------------- Public Methods /** diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/PatternRuleMatcher.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/PatternRuleMatcher.java index 88364793e..6e9b25ea2 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/PatternRuleMatcher.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/PatternRuleMatcher.java @@ -50,42 +50,6 @@ public PatternRuleMatcher( final String pattern, /* @Nullable */final String nam this.namespaceURI = namespaceURI; } - /** - * {@inheritDoc} - */ - @Override - public boolean match( final String namespace, final String pattern, final String name, final Attributes attributes ) - { - if ( namespaceURI != null && !namespace.equals( namespaceURI ) ) - { - return false; - } - return this.pattern.equals( pattern ); - } - - public String getPattern() - { - return pattern; - } - - public String getNamespaceURI() - { - return namespaceURI; - } - - /** - * {@inheritDoc} - */ - @Override - public int hashCode() - { - final int prime = 31; - int result = 1; - result = prime * result + ( ( namespaceURI == null ) ? 0 : namespaceURI.hashCode() ); - result = prime * result + pattern.hashCode(); - return result; - } - @Override public boolean equals( final Object obj ) { @@ -125,6 +89,42 @@ else if ( !namespaceURI.equals( other.getNamespaceURI() ) ) return true; } + public String getNamespaceURI() + { + return namespaceURI; + } + + public String getPattern() + { + return pattern; + } + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = prime * result + ( ( namespaceURI == null ) ? 0 : namespaceURI.hashCode() ); + result = prime * result + pattern.hashCode(); + return result; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean match( final String namespace, final String pattern, final String name, final Attributes attributes ) + { + if ( namespaceURI != null && !namespace.equals( namespaceURI ) ) + { + return false; + } + return this.pattern.equals( pattern ); + } + /** * {@inheritDoc} */ diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/RecordedInvocation.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/RecordedInvocation.java index 7fecb584c..a1d9c232f 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/RecordedInvocation.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/RecordedInvocation.java @@ -58,51 +58,6 @@ public RecordedInvocation( final Method invokedMethod, final Object[] arguments // Canonical Methods //****************************************************************************************************************** - /** - * Gets the invokedMethod. - * - * @return Method - */ - public Method getInvokedMethod() - { - return invokedMethod; - } - - /** - * Gets the arguments. - * - * @return Object[] - */ - public Object[] getArguments() - { - return arguments; - } - - /** - * {@inheritDoc} - */ - @Override - public String toString() - { - final StringBuilder buffer = new StringBuilder(); - buffer.append( invokedMethod.getDeclaringClass().getName() ); - buffer.append( "." ); - buffer.append( invokedMethod.getName() ); - buffer.append( "(" ); - final int count = arguments.length; - for ( int i = 0; i < count; i++ ) - { - final Object arg = arguments[i]; - if ( i > 0 ) - { - buffer.append( ", " ); - } - convert( buffer, arg ); - } - buffer.append( ")" ); - return buffer.toString(); - } - /** * Add a string representation of {@code input} to {@code buffer}. * @@ -144,4 +99,49 @@ protected void convert( final StringBuilder buffer, final Object input ) } } + /** + * Gets the arguments. + * + * @return Object[] + */ + public Object[] getArguments() + { + return arguments; + } + + /** + * Gets the invokedMethod. + * + * @return Method + */ + public Method getInvokedMethod() + { + return invokedMethod; + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() + { + final StringBuilder buffer = new StringBuilder(); + buffer.append( invokedMethod.getDeclaringClass().getName() ); + buffer.append( "." ); + buffer.append( invokedMethod.getName() ); + buffer.append( "(" ); + final int count = arguments.length; + for ( int i = 0; i < count; i++ ) + { + final Object arg = arguments[i]; + if ( i > 0 ) + { + buffer.append( ", " ); + } + convert( buffer, arg ); + } + buffer.append( ")" ); + return buffer.toString(); + } + } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/RegexRules.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/RegexRules.java index 364c203f8..f808303a9 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/RegexRules.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/RegexRules.java @@ -42,13 +42,29 @@ public class RegexRules // --------------------------------------------------------- Fields + /** Used to associate rules with paths in the rules list */ + private static final class RegisteredRule + { + String pattern; + + Rule rule; + + RegisteredRule( final String pattern, final Rule rule ) + { + this.pattern = pattern; + this.rule = rule; + } + } + /** All registered {@code Rule}'s */ private final ArrayList registeredRules = new ArrayList(); + // --------------------------------------------------------- Constructor + /** The regex strategy used by this RegexRules */ private RegexMatcher matcher; - // --------------------------------------------------------- Constructor + // --------------------------------------------------------- Properties /** * Construct sets the Regex matching strategy. @@ -60,50 +76,25 @@ public RegexRules( final RegexMatcher matcher ) setRegexMatcher( matcher ); } - // --------------------------------------------------------- Properties - - /** - * Gets the current regex matching strategy. - * - * @return the current regex matching strategy. - */ - public RegexMatcher getRegexMatcher() - { - return matcher; - } - - /** - * Sets the current regex matching strategy. - * - * @param matcher use this RegexMatcher, not null - */ - public void setRegexMatcher( final RegexMatcher matcher ) - { - if ( matcher == null ) - { - throw new IllegalArgumentException( "RegexMatcher must not be null." ); - } - this.matcher = matcher; - } - - // --------------------------------------------------------- Public Methods - /** * {@inheritDoc} */ @Override - protected void registerRule( final String pattern, final Rule rule ) + public void clear() { - registeredRules.add( new RegisteredRule( pattern, rule ) ); + registeredRules.clear(); } + // --------------------------------------------------------- Public Methods + /** - * {@inheritDoc} + * Gets the current regex matching strategy. + * + * @return the current regex matching strategy. */ - @Override - public void clear() + public RegexMatcher getRegexMatcher() { - registeredRules.clear(); + return matcher; } /** @@ -131,6 +122,15 @@ public List match( final String namespaceURI, final String pattern, final return rules; } + /** + * {@inheritDoc} + */ + @Override + protected void registerRule( final String pattern, final Rule rule ) + { + registeredRules.add( new RegisteredRule( pattern, rule ) ); + } + /** * {@inheritDoc} */ @@ -145,18 +145,18 @@ public List rules() return rules; } - /** Used to associate rules with paths in the rules list */ - private static final class RegisteredRule + /** + * Sets the current regex matching strategy. + * + * @param matcher use this RegexMatcher, not null + */ + public void setRegexMatcher( final RegexMatcher matcher ) { - String pattern; - - Rule rule; - - RegisteredRule( final String pattern, final Rule rule ) + if ( matcher == null ) { - this.pattern = pattern; - this.rule = rule; + throw new IllegalArgumentException( "RegexMatcher must not be null." ); } + this.matcher = matcher; } } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/Rule.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/Rule.java index 05c96d69d..5bb61377c 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/Rule.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/Rule.java @@ -66,49 +66,6 @@ // ------------------------------------------------------------- Properties - /** - * Return the Digester with which this Rule is associated. - * - * @return the Digester with which this Rule is associated - */ - public Digester getDigester() - { - return ( this.digester ); - } - - /** - * Sets the {@code Digester} with which this {@code Rule} is associated. - * - * @param digester the {@code Digester} with which this {@code Rule} is associated - */ - public void setDigester( final Digester digester ) - { - this.digester = digester; - } - - /** - * Return the namespace URI for which this Rule is relevant, if any. - * - * @return the namespace URI for which this Rule is relevant, if any - */ - public String getNamespaceURI() - { - return ( this.namespaceURI ); - } - - /** - * Sets the namespace URI for which this Rule is relevant, if any. - * - * @param namespaceURI Namespace URI for which this Rule is relevant, or {@code null} to match independent of - * namespace. - */ - public void setNamespaceURI( final String namespaceURI ) - { - this.namespaceURI = namespaceURI; - } - - // --------------------------------------------------------- Public Methods - /** * This method is called when the beginning of a matching XML element is encountered. * @@ -168,4 +125,47 @@ public void finish() // The default implementation does nothing } + // --------------------------------------------------------- Public Methods + + /** + * Return the Digester with which this Rule is associated. + * + * @return the Digester with which this Rule is associated + */ + public Digester getDigester() + { + return ( this.digester ); + } + + /** + * Return the namespace URI for which this Rule is relevant, if any. + * + * @return the namespace URI for which this Rule is relevant, if any + */ + public String getNamespaceURI() + { + return ( this.namespaceURI ); + } + + /** + * Sets the {@code Digester} with which this {@code Rule} is associated. + * + * @param digester the {@code Digester} with which this {@code Rule} is associated + */ + public void setDigester( final Digester digester ) + { + this.digester = digester; + } + + /** + * Sets the namespace URI for which this Rule is relevant, if any. + * + * @param namespaceURI Namespace URI for which this Rule is relevant, or {@code null} to match independent of + * namespace. + */ + public void setNamespaceURI( final String namespaceURI ) + { + this.namespaceURI = namespaceURI; + } + } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/RuleSet.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/RuleSet.java index dd7b2e675..a0288288c 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/RuleSet.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/RuleSet.java @@ -41,20 +41,20 @@ public interface RuleSet // ------------------------------------------------------------- Properties /** - * Return the namespace URI that will be applied to all Rule instances created from this RuleSet. + * Add the set of Rule instances defined in this RuleSet to the specified {@code Digester} instance, + * associating them with our namespace URI (if any). This method should only be called by a Digester instance. * - * @return the namespace URI that will be applied to all Rule instances created from this RuleSet + * @param digester Digester instance to which the new Rule instances should be added. */ - String getNamespaceURI(); + void addRuleInstances( Digester digester ); // --------------------------------------------------------- Public Methods /** - * Add the set of Rule instances defined in this RuleSet to the specified {@code Digester} instance, - * associating them with our namespace URI (if any). This method should only be called by a Digester instance. + * Return the namespace URI that will be applied to all Rule instances created from this RuleSet. * - * @param digester Digester instance to which the new Rule instances should be added. + * @return the namespace URI that will be applied to all Rule instances created from this RuleSet */ - void addRuleInstances( Digester digester ); + String getNamespaceURI(); } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/Rules.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/Rules.java index e6827a7dc..db06aaaf4 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/Rules.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/Rules.java @@ -34,49 +34,34 @@ public interface Rules // ------------------------------------------------------------- Properties /** - * Return the Digester instance with which this Rules instance is associated. + * Register a new Rule instance matching the specified pattern. * - * @return the Digester instance with which this Rules instance is associated + * @param pattern Nesting pattern to be matched for this Rule + * @param rule Rule instance to be registered */ - Digester getDigester(); + void add( String pattern, Rule rule ); /** - * Sets the Digester instance with which this Rules instance is associated. - * - * @param digester The newly associated Digester instance + * Clear all existing Rule instance registrations. */ - void setDigester( Digester digester ); + void clear(); /** - * Return the namespace URI that will be applied to all subsequently added {@code Rule} objects. + * Return the Digester instance with which this Rules instance is associated. * - * @return the namespace URI that will be applied to all subsequently added {@code Rule} objects. + * @return the Digester instance with which this Rules instance is associated */ - String getNamespaceURI(); + Digester getDigester(); /** - * Sets the namespace URI that will be applied to all subsequently added {@code Rule} objects. + * Return the namespace URI that will be applied to all subsequently added {@code Rule} objects. * - * @param namespaceURI Namespace URI that must match on all subsequently added rules, or {@code null} for - * matching regardless of the current namespace URI + * @return the namespace URI that will be applied to all subsequently added {@code Rule} objects. */ - void setNamespaceURI( String namespaceURI ); + String getNamespaceURI(); // --------------------------------------------------------- Public Methods - /** - * Register a new Rule instance matching the specified pattern. - * - * @param pattern Nesting pattern to be matched for this Rule - * @param rule Rule instance to be registered - */ - void add( String pattern, Rule rule ); - - /** - * Clear all existing Rule instance registrations. - */ - void clear(); - /** * Return a List of all registered Rule instances that match the specified nesting pattern, or a zero-length List if * there are no matches. If more than one Rule instance matches, they must be returned in the order @@ -100,4 +85,19 @@ public interface Rules */ List rules(); + /** + * Sets the Digester instance with which this Rules instance is associated. + * + * @param digester The newly associated Digester instance + */ + void setDigester( Digester digester ); + + /** + * Sets the namespace URI that will be applied to all subsequently added {@code Rule} objects. + * + * @param namespaceURI Namespace URI that must match on all subsequently added rules, or {@code null} for + * matching regardless of the current namespace URI + */ + void setNamespaceURI( String namespaceURI ); + } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/RulesBase.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/RulesBase.java index cd19829a3..ace1a1c7d 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/RulesBase.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/RulesBase.java @@ -77,53 +77,47 @@ public class RulesBase * {@inheritDoc} */ @Override - public void setDigester( final Digester digester ) + public void clear() { - super.setDigester( digester ); - for ( final Rule rule : rules ) - { - rule.setDigester( digester ); - } + wildcardCache.clear(); + cache.clear(); + rules.clear(); } // --------------------------------------------------------- Public Methods /** - * {@inheritDoc} + * Return a List of Rule instances for the specified pattern that also match the specified namespace URI (if any). + * If there are no such rules, return {@code null}. + * + * @param namespaceURI Namespace URI to match, or {@code null} to select matching rules regardless of namespace + * URI + * @param pattern Pattern to be matched + * @return a List of Rule instances for the specified pattern that also match the specified namespace URI (if any) */ - @Override - protected void registerRule( String pattern, final Rule rule ) + protected List lookup( final String namespaceURI, final String pattern ) { - // to help users who accidently add '/' to the end of their patterns - final int patternLength = pattern.length(); - if ( patternLength > 1 && pattern.endsWith( "/" ) ) + // Optimize when no namespace URI is specified + final List list = this.cache.get( pattern ); + if ( list == null ) { - pattern = pattern.substring( 0, patternLength - 1 ); + return ( null ); + } + if ( ( namespaceURI == null ) || ( namespaceURI.isEmpty() ) ) + { + return ( list ); } - List list = cache.get( pattern ); - if ( list == null ) + // Select only Rules that match on the specified namespace URI + final ArrayList results = new ArrayList(); + for ( final Rule item : list ) { - list = new ArrayList(); - if ( pattern.startsWith( "*/" ) ) + if ( ( namespaceURI.equals( item.getNamespaceURI() ) ) || ( item.getNamespaceURI() == null ) ) { - wildcardCache.add( pattern.substring( 1 ) ); + results.add( item ); } - cache.put( pattern, list ); } - list.add( rule ); - rules.add( rule ); - } - - /** - * {@inheritDoc} - */ - @Override - public void clear() - { - wildcardCache.clear(); - cache.clear(); - rules.clear(); + return ( results ); } /** @@ -158,6 +152,33 @@ public List match( final String namespaceURI, final String pattern, final return ( rulesList ); } + /** + * {@inheritDoc} + */ + @Override + protected void registerRule( String pattern, final Rule rule ) + { + // to help users who accidently add '/' to the end of their patterns + final int patternLength = pattern.length(); + if ( patternLength > 1 && pattern.endsWith( "/" ) ) + { + pattern = pattern.substring( 0, patternLength - 1 ); + } + + List list = cache.get( pattern ); + if ( list == null ) + { + list = new ArrayList(); + if ( pattern.startsWith( "*/" ) ) + { + wildcardCache.add( pattern.substring( 1 ) ); + } + cache.put( pattern, list ); + } + list.add( rule ); + rules.add( rule ); + } + /** * {@inheritDoc} */ @@ -170,37 +191,16 @@ public List rules() // ------------------------------------------------------ Protected Methods /** - * Return a List of Rule instances for the specified pattern that also match the specified namespace URI (if any). - * If there are no such rules, return {@code null}. - * - * @param namespaceURI Namespace URI to match, or {@code null} to select matching rules regardless of namespace - * URI - * @param pattern Pattern to be matched - * @return a List of Rule instances for the specified pattern that also match the specified namespace URI (if any) + * {@inheritDoc} */ - protected List lookup( final String namespaceURI, final String pattern ) + @Override + public void setDigester( final Digester digester ) { - // Optimize when no namespace URI is specified - final List list = this.cache.get( pattern ); - if ( list == null ) - { - return ( null ); - } - if ( ( namespaceURI == null ) || ( namespaceURI.isEmpty() ) ) - { - return ( list ); - } - - // Select only Rules that match on the specified namespace URI - final ArrayList results = new ArrayList(); - for ( final Rule item : list ) + super.setDigester( digester ); + for ( final Rule rule : rules ) { - if ( ( namespaceURI.equals( item.getNamespaceURI() ) ) || ( item.getNamespaceURI() == null ) ) - { - results.add( item ); - } + rule.setDigester( digester ); } - return ( results ); } } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/SetNestedPropertiesRule.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/SetNestedPropertiesRule.java index 6723fa216..ea75f4878 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/SetNestedPropertiesRule.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/SetNestedPropertiesRule.java @@ -102,16 +102,232 @@ public class SetNestedPropertiesRule extends Rule { + private class AnyChildRule + extends Rule + { + + private String currChildElementName; + + @Override + public void begin( final String namespaceURI, final String name, final Attributes attributes ) + throws Exception + { + currChildElementName = name; + } + + @Override + public void body( final String namespace, final String name, String text ) + throws Exception + { + String propName = currChildElementName; + if ( elementNames.containsKey( currChildElementName ) ) + { + // overide propName + propName = elementNames.get( currChildElementName ); + if ( propName == null ) + { + // user wants us to ignore this element + return; + } + } + + final boolean debug = log.isDebugEnabled(); + + if ( debug ) + { + log.debug( "[SetNestedPropertiesRule]{" + getDigester().getMatch() + "} Setting property '" + propName + + "' to '" + text + "'" ); + } + + // Populate the corresponding properties of the top object + final Object top = getDigester().peek(); + if ( debug ) + { + if ( top != null ) + { + log.debug( "[SetNestedPropertiesRule]{" + getDigester().getMatch() + "} Set " + + top.getClass().getName() + " properties" ); + } + else + { + log.debug( "[SetPropertiesRule]{" + getDigester().getMatch() + "} Set NULL properties" ); + } + } + + if ( trimData ) + { + text = text.trim(); + } + + if ( !allowUnknownChildElements ) + { + // Force an exception if the property does not exist + // (BeanUtils.setProperty() silently returns in this case) + if ( top instanceof DynaBean ) + { + final DynaProperty desc = ( (DynaBean) top ).getDynaClass().getDynaProperty( propName ); + if ( desc == null ) + { + throw new NoSuchMethodException( "Bean has no property named " + propName ); + } + } + else + /* this is a standard JavaBean */ + { + final PropertyDescriptor desc = getPropertyDescriptor( top, propName ); + if ( desc == null ) + { + throw new NoSuchMethodException( "Bean has no property named " + propName ); + } + } + } + + try + { + setProperty( top, propName, text ); + } + catch ( final NullPointerException e ) + { + log.error( "NullPointerException: " + "top=" + top + ",propName=" + propName + ",value=" + text + "!" ); + throw e; + } + } + + @Override + public void end( final String namespace, final String name ) + throws Exception + { + currChildElementName = null; + } + } + + /** Private Rules implementation */ + private class AnyChildRules + implements Rules + { + private String matchPrefix; + + private Rules decoratedRules; + + private final ArrayList rules = new ArrayList( 1 ); + + private final AnyChildRule rule; + + public AnyChildRules( final AnyChildRule rule ) + { + this.rule = rule; + rules.add( rule ); + } + + @Override + public void add( final String pattern, final Rule rule ) + { + } + + @Override + public void clear() + { + } + + @Override + public Digester getDigester() + { + return null; + } + + @Override + public String getNamespaceURI() + { + return null; + } + + public Rules getOldRules() + { + return decoratedRules; + } + + public void init( final String prefix, final Rules rules ) + { + matchPrefix = prefix; + decoratedRules = rules; + } + + @Override + public List match( final String namespaceURI, final String matchPath, final String name, final Attributes attributes ) + { + final List match = decoratedRules.match( namespaceURI, matchPath, name, attributes ); + + if ( ( matchPath.startsWith( matchPrefix ) ) && ( matchPath.indexOf( '/', matchPrefix.length() ) == -1 ) ) + { + + // The current element is a direct child of the element + // specified in the init method, so we want to ensure that + // the rule passed to this object's constructor is included + // in the returned list of matching rules. + + if ( ( match == null || match.isEmpty() ) ) + { + // The "real" rules class doesn't have any matches for + // the specified path, so we return a list containing + // just one rule: the one passed to this object's + // constructor. + return rules; + } + // The "real" rules class has rules that match the current + // node, so we return this list *plus* the rule passed to + // this object's constructor. + // + // It might not be safe to modify the returned list, + // so clone it first. + final LinkedList newMatch = new LinkedList( match ); + newMatch.addLast( rule ); + return newMatch; + } + return match; + } + + @Override + public List rules() + { + // This is not actually expected to be called during normal + // processing. + // + // There is only one known case where this is called; when a rule + // returned from AnyChildRules.match is invoked and throws a + // SAXException then method Digester.endDocument will be called + // without having "uninstalled" the AnyChildRules ionstance. That + // method attempts to invoke the "finish" method for every Rule + // instance - and thus needs to call rules() on its Rules object, + // which is this one. Actually, java 1.5 and 1.6beta2 have a + // bug in their xml implementation such that endDocument is not + // called after a SAXException, but other parsers (eg Aelfred) + // do call endDocument. Here, we therefore need to return the + // rules registered with the underlying Rules object. + log.debug( "AnyChildRules.rules invoked." ); + return decoratedRules.rules(); + } + + @Override + public void setDigester( final Digester digester ) + { + } + + @Override + public void setNamespaceURI( final String namespaceURI ) + { + } + } + private Log log; private boolean trimData = true; + // ----------------------------------------------------------- Constructors + private boolean allowUnknownChildElements; private final HashMap elementNames = new HashMap(); - // ----------------------------------------------------------- Constructors - /** * Base constructor, which maps every child element into a bean property with the same name as the xml element. *

@@ -124,6 +340,22 @@ public SetNestedPropertiesRule() // nothing to set up } + /** + * Constructor which allows element->property mapping to be overridden. + * + * @param elementNames names of elements->properties to map + * @since 3.0 + */ + public SetNestedPropertiesRule( final Map elementNames ) + { + if ( elementNames != null && !elementNames.isEmpty() ) + { + this.elementNames.putAll( elementNames ); + } + } + + // --------------------------------------------------------- Public Methods + /** *

* Convenience constructor which overrides the default mappings for just one property. @@ -194,40 +426,55 @@ public SetNestedPropertiesRule( final String[] elementNames, final String[] prop } /** - * Constructor which allows element->property mapping to be overridden. + * Add an additional custom xml-element -> property mapping. + *

+ * This is primarily intended to be used from the xml rules module (as it is not possible there to pass the + * necessary parameters to the constructor for this class). However it is valid to use this method directly if + * desired. * - * @param elementNames names of elements->properties to map - * @since 3.0 + * @param elementName the xml-element has to be mapped + * @param propertyName the property name target */ - public SetNestedPropertiesRule( final Map elementNames ) + public void addAlias( final String elementName, final String propertyName ) { - if ( elementNames != null && !elementNames.isEmpty() ) - { - this.elementNames.putAll( elementNames ); - } + elementNames.put( elementName, propertyName ); } - // --------------------------------------------------------- Public Methods - /** * {@inheritDoc} */ @Override - public void setDigester( final Digester digester ) + public void begin( final String namespace, final String name, final Attributes attributes ) + throws Exception { - super.setDigester( digester ); - log = digester.getLogger(); + final Rules oldRules = getDigester().getRules(); + final AnyChildRule anyChildRule = new AnyChildRule(); + anyChildRule.setDigester( getDigester() ); + final AnyChildRules newRules = new AnyChildRules( anyChildRule ); + newRules.init( getDigester().getMatch() + "/", oldRules ); + getDigester().setRules( newRules ); } /** - * When set to true, any text within child elements will have leading and trailing whitespace removed before - * assignment to the target object. The default value for this attribute is true. + * {@inheritDoc} + */ + @Override + public void body( final String namespace, final String name, final String text ) + throws Exception + { + final AnyChildRules newRules = (AnyChildRules) getDigester().getRules(); + getDigester().setRules( newRules.getOldRules() ); + } + + /** + * Return the flag to ignore any child element for which there is no corresponding object property * - * @param trimData flag to have leading and trailing whitespace removed + * @return flag to ignore any child element for which there is no corresponding object property + * @see #setAllowUnknownChildElements(boolean) */ - public void setTrimData( final boolean trimData ) + public boolean getAllowUnknownChildElements() { - this.trimData = trimData; + return allowUnknownChildElements; } /** @@ -260,56 +507,27 @@ public void setAllowUnknownChildElements( final boolean allowUnknownChildElement this.allowUnknownChildElements = allowUnknownChildElements; } - /** - * Return the flag to ignore any child element for which there is no corresponding object property - * - * @return flag to ignore any child element for which there is no corresponding object property - * @see #setAllowUnknownChildElements(boolean) - */ - public boolean getAllowUnknownChildElements() - { - return allowUnknownChildElements; - } - /** * {@inheritDoc} */ @Override - public void begin( final String namespace, final String name, final Attributes attributes ) - throws Exception + public void setDigester( final Digester digester ) { - final Rules oldRules = getDigester().getRules(); - final AnyChildRule anyChildRule = new AnyChildRule(); - anyChildRule.setDigester( getDigester() ); - final AnyChildRules newRules = new AnyChildRules( anyChildRule ); - newRules.init( getDigester().getMatch() + "/", oldRules ); - getDigester().setRules( newRules ); + super.setDigester( digester ); + log = digester.getLogger(); } - /** - * {@inheritDoc} - */ - @Override - public void body( final String namespace, final String name, final String text ) - throws Exception - { - final AnyChildRules newRules = (AnyChildRules) getDigester().getRules(); - getDigester().setRules( newRules.getOldRules() ); - } + // ----------------------------------------- local classes /** - * Add an additional custom xml-element -> property mapping. - *

- * This is primarily intended to be used from the xml rules module (as it is not possible there to pass the - * necessary parameters to the constructor for this class). However it is valid to use this method directly if - * desired. + * When set to true, any text within child elements will have leading and trailing whitespace removed before + * assignment to the target object. The default value for this attribute is true. * - * @param elementName the xml-element has to be mapped - * @param propertyName the property name target + * @param trimData flag to have leading and trailing whitespace removed */ - public void addAlias( final String elementName, final String propertyName ) + public void setTrimData( final boolean trimData ) { - elementNames.put( elementName, propertyName ); + this.trimData = trimData; } /** @@ -324,222 +542,4 @@ public String toString() elementNames ); } - // ----------------------------------------- local classes - - /** Private Rules implementation */ - private class AnyChildRules - implements Rules - { - private String matchPrefix; - - private Rules decoratedRules; - - private final ArrayList rules = new ArrayList( 1 ); - - private final AnyChildRule rule; - - public AnyChildRules( final AnyChildRule rule ) - { - this.rule = rule; - rules.add( rule ); - } - - @Override - public Digester getDigester() - { - return null; - } - - @Override - public void setDigester( final Digester digester ) - { - } - - @Override - public String getNamespaceURI() - { - return null; - } - - @Override - public void setNamespaceURI( final String namespaceURI ) - { - } - - @Override - public void add( final String pattern, final Rule rule ) - { - } - - @Override - public void clear() - { - } - - @Override - public List match( final String namespaceURI, final String matchPath, final String name, final Attributes attributes ) - { - final List match = decoratedRules.match( namespaceURI, matchPath, name, attributes ); - - if ( ( matchPath.startsWith( matchPrefix ) ) && ( matchPath.indexOf( '/', matchPrefix.length() ) == -1 ) ) - { - - // The current element is a direct child of the element - // specified in the init method, so we want to ensure that - // the rule passed to this object's constructor is included - // in the returned list of matching rules. - - if ( ( match == null || match.isEmpty() ) ) - { - // The "real" rules class doesn't have any matches for - // the specified path, so we return a list containing - // just one rule: the one passed to this object's - // constructor. - return rules; - } - // The "real" rules class has rules that match the current - // node, so we return this list *plus* the rule passed to - // this object's constructor. - // - // It might not be safe to modify the returned list, - // so clone it first. - final LinkedList newMatch = new LinkedList( match ); - newMatch.addLast( rule ); - return newMatch; - } - return match; - } - - @Override - public List rules() - { - // This is not actually expected to be called during normal - // processing. - // - // There is only one known case where this is called; when a rule - // returned from AnyChildRules.match is invoked and throws a - // SAXException then method Digester.endDocument will be called - // without having "uninstalled" the AnyChildRules ionstance. That - // method attempts to invoke the "finish" method for every Rule - // instance - and thus needs to call rules() on its Rules object, - // which is this one. Actually, java 1.5 and 1.6beta2 have a - // bug in their xml implementation such that endDocument is not - // called after a SAXException, but other parsers (eg Aelfred) - // do call endDocument. Here, we therefore need to return the - // rules registered with the underlying Rules object. - log.debug( "AnyChildRules.rules invoked." ); - return decoratedRules.rules(); - } - - public void init( final String prefix, final Rules rules ) - { - matchPrefix = prefix; - decoratedRules = rules; - } - - public Rules getOldRules() - { - return decoratedRules; - } - } - - private class AnyChildRule - extends Rule - { - - private String currChildElementName; - - @Override - public void begin( final String namespaceURI, final String name, final Attributes attributes ) - throws Exception - { - currChildElementName = name; - } - - @Override - public void body( final String namespace, final String name, String text ) - throws Exception - { - String propName = currChildElementName; - if ( elementNames.containsKey( currChildElementName ) ) - { - // overide propName - propName = elementNames.get( currChildElementName ); - if ( propName == null ) - { - // user wants us to ignore this element - return; - } - } - - final boolean debug = log.isDebugEnabled(); - - if ( debug ) - { - log.debug( "[SetNestedPropertiesRule]{" + getDigester().getMatch() + "} Setting property '" + propName - + "' to '" + text + "'" ); - } - - // Populate the corresponding properties of the top object - final Object top = getDigester().peek(); - if ( debug ) - { - if ( top != null ) - { - log.debug( "[SetNestedPropertiesRule]{" + getDigester().getMatch() + "} Set " - + top.getClass().getName() + " properties" ); - } - else - { - log.debug( "[SetPropertiesRule]{" + getDigester().getMatch() + "} Set NULL properties" ); - } - } - - if ( trimData ) - { - text = text.trim(); - } - - if ( !allowUnknownChildElements ) - { - // Force an exception if the property does not exist - // (BeanUtils.setProperty() silently returns in this case) - if ( top instanceof DynaBean ) - { - final DynaProperty desc = ( (DynaBean) top ).getDynaClass().getDynaProperty( propName ); - if ( desc == null ) - { - throw new NoSuchMethodException( "Bean has no property named " + propName ); - } - } - else - /* this is a standard JavaBean */ - { - final PropertyDescriptor desc = getPropertyDescriptor( top, propName ); - if ( desc == null ) - { - throw new NoSuchMethodException( "Bean has no property named " + propName ); - } - } - } - - try - { - setProperty( top, propName, text ); - } - catch ( final NullPointerException e ) - { - log.error( "NullPointerException: " + "top=" + top + ",propName=" + propName + ",value=" + text + "!" ); - throw e; - } - } - - @Override - public void end( final String namespace, final String name ) - throws Exception - { - currChildElementName = null; - } - } - } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/SetNextRule.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/SetNextRule.java index 2d5d2fbd6..adc6e3f13 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/SetNextRule.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/SetNextRule.java @@ -57,11 +57,11 @@ public SetNextRule( final String methodName ) * Construct a "set next" rule with the specified method name. * * @param methodName Method name of the parent method to call - * @param paramType Java class name of the parent method's argument (if you wish to use a primitive type, - * specify the corresonding Java wrapper class instead, such as {@code java.lang.Boolean} - * for a {@code boolean} parameter) + * @param paramType Java class of the parent method's argument (if you wish to use a primitive type, specify the + * corresonding Java wrapper class instead, such as {@code java.lang.Boolean} for a + * {@code boolean} parameter) */ - public SetNextRule( final String methodName, final String paramType ) + public SetNextRule( final String methodName, final Class paramType ) { super( methodName, paramType ); } @@ -70,11 +70,11 @@ public SetNextRule( final String methodName, final String paramType ) * Construct a "set next" rule with the specified method name. * * @param methodName Method name of the parent method to call - * @param paramType Java class of the parent method's argument (if you wish to use a primitive type, specify the - * corresonding Java wrapper class instead, such as {@code java.lang.Boolean} for a - * {@code boolean} parameter) + * @param paramType Java class name of the parent method's argument (if you wish to use a primitive type, + * specify the corresonding Java wrapper class instead, such as {@code java.lang.Boolean} + * for a {@code boolean} parameter) */ - public SetNextRule( final String methodName, final Class paramType ) + public SetNextRule( final String methodName, final String paramType ) { super( methodName, paramType ); } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/SetPropertiesRule.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/SetPropertiesRule.java index 2748d3dde..476a03ecc 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/SetPropertiesRule.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/SetPropertiesRule.java @@ -45,6 +45,14 @@ public class SetPropertiesRule // ----------------------------------------------------------- Constructors + private final Map aliases = new HashMap(); + + /** + * Used to determine whether the parsing should fail if an property specified in the XML is missing from the bean. + * Default is true for backward compatibility. + */ + private boolean ignoreMissingProperty = true; + /** * Base constructor. */ @@ -53,6 +61,22 @@ public SetPropertiesRule() // nothing to set up } + /** + * Constructor allows attribute->property mapping to be overriden. + * + * @param aliases attribute->property mapping + * @since 3.0 + */ + public SetPropertiesRule( final Map aliases ) + { + if ( aliases != null && !aliases.isEmpty() ) + { + this.aliases.putAll( aliases ); + } + } + + // ----------------------------------------------------- Instance Variables + /** *

* Convenience constructor overrides the mapping for just one property. @@ -119,32 +143,19 @@ public SetPropertiesRule( final String[] attributeNames, final String[] property } } + // --------------------------------------------------------- Public Methods + /** - * Constructor allows attribute->property mapping to be overriden. + * Add an additional attribute name to property name mapping. This is intended to be used from the xml rules. * - * @param aliases attribute->property mapping - * @since 3.0 + * @param attributeName the attribute name has to be mapped + * @param propertyName the target property name */ - public SetPropertiesRule( final Map aliases ) + public void addAlias( final String attributeName, final String propertyName ) { - if ( aliases != null && !aliases.isEmpty() ) - { - this.aliases.putAll( aliases ); - } + aliases.put( attributeName, propertyName ); } - // ----------------------------------------------------- Instance Variables - - private final Map aliases = new HashMap(); - - /** - * Used to determine whether the parsing should fail if an property specified in the XML is missing from the bean. - * Default is true for backward compatibility. - */ - private boolean ignoreMissingProperty = true; - - // --------------------------------------------------------- Public Methods - /** * {@inheritDoc} */ @@ -232,26 +243,6 @@ public void begin( final String namespace, final String name, final Attributes a populate( top, values ); } - /** - * Add an additional attribute name to property name mapping. This is intended to be used from the xml rules. - * - * @param attributeName the attribute name has to be mapped - * @param propertyName the target property name - */ - public void addAlias( final String attributeName, final String propertyName ) - { - aliases.put( attributeName, propertyName ); - } - - /** - * {@inheritDoc} - */ - @Override - public String toString() - { - return format( "SetPropertiesRule[aliases=%s, ignoreMissingProperty=%s]", aliases, ignoreMissingProperty ); - } - /** *

* Are attributes found in the xml without matching properties to be ignored? @@ -280,4 +271,13 @@ public void setIgnoreMissingProperty( final boolean ignoreMissingProperty ) this.ignoreMissingProperty = ignoreMissingProperty; } + /** + * {@inheritDoc} + */ + @Override + public String toString() + { + return format( "SetPropertiesRule[aliases=%s, ignoreMissingProperty=%s]", aliases, ignoreMissingProperty ); + } + } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/SetPropertyRule.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/SetPropertyRule.java index 36203d421..fe08630fe 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/SetPropertyRule.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/SetPropertyRule.java @@ -39,6 +39,18 @@ public class SetPropertyRule // ----------------------------------------------------------- Constructors + /** + * The attribute that will contain the property name. + */ + protected String name; + + // ----------------------------------------------------- Instance Variables + + /** + * The attribute that will contain the property value. + */ + protected String value; + /** * Construct a "set property" rule with the specified name and value attributes. * @@ -51,18 +63,6 @@ public SetPropertyRule( final String name, final String value ) this.value = value; } - // ----------------------------------------------------- Instance Variables - - /** - * The attribute that will contain the property name. - */ - protected String name; - - /** - * The attribute that will contain the property value. - */ - protected String value; - // --------------------------------------------------------- Public Methods /** diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/SetRootRule.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/SetRootRule.java index b17226b83..779113827 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/SetRootRule.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/SetRootRule.java @@ -51,11 +51,11 @@ public SetRootRule( final String methodName ) * Construct a "set root" rule with the specified method name. * * @param methodName Method name of the parent method to call - * @param paramType Java class name of the parent method's argument (if you wish to use a primitive type, - * specify the corresonding Java wrapper class instead, such as {@code java.lang.Boolean} - * for a {@code boolean} parameter) + * @param paramType Java class of the parent method's argument (if you wish to use a primitive type, specify the + * corresonding Java wrapper class instead, such as {@code java.lang.Boolean} for a + * {@code boolean} parameter) */ - public SetRootRule( final String methodName, final String paramType ) + public SetRootRule( final String methodName, final Class paramType ) { super( methodName, paramType ); } @@ -64,11 +64,11 @@ public SetRootRule( final String methodName, final String paramType ) * Construct a "set root" rule with the specified method name. * * @param methodName Method name of the parent method to call - * @param paramType Java class of the parent method's argument (if you wish to use a primitive type, specify the - * corresonding Java wrapper class instead, such as {@code java.lang.Boolean} for a - * {@code boolean} parameter) + * @param paramType Java class name of the parent method's argument (if you wish to use a primitive type, + * specify the corresonding Java wrapper class instead, such as {@code java.lang.Boolean} + * for a {@code boolean} parameter) */ - public SetRootRule( final String methodName, final Class paramType ) + public SetRootRule( final String methodName, final String paramType ) { super( methodName, paramType ); } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/SetTopRule.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/SetTopRule.java index 169659339..962e7c7c1 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/SetTopRule.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/SetTopRule.java @@ -51,11 +51,11 @@ public SetTopRule( final String methodName ) * Construct a "set top" rule with the specified method name. * * @param methodName Method name of the parent method to call - * @param paramType Java class name of the parent method's argument (if you wish to use a primitive type, - * specify the corresonding Java wrapper class instead, such as {@code java.lang.Boolean} - * for a {@code boolean} parameter) + * @param paramType Java class of the parent method's argument (if you wish to use a primitive type, specify the + * corresonding Java wrapper class instead, such as {@code java.lang.Boolean} for a + * {@code boolean} parameter) */ - public SetTopRule( final String methodName, final String paramType ) + public SetTopRule( final String methodName, final Class paramType ) { super( methodName, paramType ); } @@ -64,11 +64,11 @@ public SetTopRule( final String methodName, final String paramType ) * Construct a "set top" rule with the specified method name. * * @param methodName Method name of the parent method to call - * @param paramType Java class of the parent method's argument (if you wish to use a primitive type, specify the - * corresonding Java wrapper class instead, such as {@code java.lang.Boolean} for a - * {@code boolean} parameter) + * @param paramType Java class name of the parent method's argument (if you wish to use a primitive type, + * specify the corresonding Java wrapper class instead, such as {@code java.lang.Boolean} + * for a {@code boolean} parameter) */ - public SetTopRule( final String methodName, final Class paramType ) + public SetTopRule( final String methodName, final String paramType ) { super( methodName, paramType ); } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/SimpleRegexMatcher.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/SimpleRegexMatcher.java index 524009c4a..2ec5d5fc1 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/SimpleRegexMatcher.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/SimpleRegexMatcher.java @@ -61,18 +61,6 @@ public Log getLog() return log; } - /** - * Sets the current {@code Log} implementation used by this class. - * - * @param log the current {@code Log} implementation used by this class. - */ - public void setLog( final Log log ) - { - this.log = log; - } - - // --------------------------------------------------------- Public Methods - /** * {@inheritDoc} */ @@ -87,7 +75,7 @@ public boolean match( final String basePattern, final String regexPattern ) return match( basePattern, regexPattern, 0, 0 ); } - // --------------------------------------------------------- Implementations Methods + // --------------------------------------------------------- Public Methods /** * Implementation of regex matching algorithm. This calls itself recursively. @@ -179,4 +167,16 @@ private boolean match( final String basePattern, final String regexPattern, int } } + // --------------------------------------------------------- Implementations Methods + + /** + * Sets the current {@code Log} implementation used by this class. + * + * @param log the current {@code Log} implementation used by this class. + */ + public void setLog( final Log log ) + { + this.log = log; + } + } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/StackAction.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/StackAction.java index ea68b5ae3..0303c1a12 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/StackAction.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/StackAction.java @@ -43,30 +43,30 @@ public interface StackAction { /** - * Invoked just before an object is to be pushed onto a digester stack. + * Invoked just after an object has been popped from a digester stack. * * @param whatever type is accepted * @param d is the digester instance. - * @param stackName is the name of the stack onto which the object has been pushed. Null is passed to indicate the + * @param stackName is the name of the stack from which the object has been popped. Null is passed to indicate the * default stack. - * @param o is the object that has just been pushed. Calling peek on the specified stack will return - * the same object. - * @return the object to be pushed. Normally, parameter o is returned but this method could return an alternate - * object to be pushed instead (eg a proxy for the provided object). + * @param o is the object that has just been popped. + * @return the object to be returned to the called. Normally, parameter o is returned but this method could return + * an alternate object. */ - T onPush( Digester d, String stackName, T o ); + T onPop( Digester d, String stackName, T o ); /** - * Invoked just after an object has been popped from a digester stack. + * Invoked just before an object is to be pushed onto a digester stack. * * @param whatever type is accepted * @param d is the digester instance. - * @param stackName is the name of the stack from which the object has been popped. Null is passed to indicate the + * @param stackName is the name of the stack onto which the object has been pushed. Null is passed to indicate the * default stack. - * @param o is the object that has just been popped. - * @return the object to be returned to the called. Normally, parameter o is returned but this method could return - * an alternate object. + * @param o is the object that has just been pushed. Calling peek on the specified stack will return + * the same object. + * @return the object to be pushed. Normally, parameter o is returned but this method could return an alternate + * object to be pushed instead (eg a proxy for the provided object). */ - T onPop( Digester d, String stackName, T o ); + T onPush( Digester d, String stackName, T o ); } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/WithDefaultsRulesWrapper.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/WithDefaultsRulesWrapper.java index f43306845..46f531015 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/WithDefaultsRulesWrapper.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/WithDefaultsRulesWrapper.java @@ -92,40 +92,43 @@ public WithDefaultsRulesWrapper( final Rules wrappedRules ) * {@inheritDoc} */ @Override - public Digester getDigester() + public void add( final String pattern, final Rule rule ) { - return wrappedRules.getDigester(); + wrappedRules.add( pattern, rule ); + allRules.add( rule ); } /** - * {@inheritDoc} - */ - @Override - public void setDigester( final Digester digester ) + * Adds a rule to be fired when wrapped implementation returns no matches + * + * @param rule a Rule to be fired when wrapped implementation returns no matches + **/ + public void addDefault( final Rule rule ) { - wrappedRules.setDigester( digester ); - for ( final Rule rule : defaultRules ) + // set up rule + if ( wrappedRules.getDigester() != null ) { - rule.setDigester( digester ); + rule.setDigester( wrappedRules.getDigester() ); } - } - /** - * {@inheritDoc} - */ - @Override - public String getNamespaceURI() - { - return wrappedRules.getNamespaceURI(); + if ( wrappedRules.getNamespaceURI() != null ) + { + rule.setNamespaceURI( wrappedRules.getNamespaceURI() ); + } + + defaultRules.add( rule ); + allRules.add( rule ); } /** * {@inheritDoc} */ @Override - public void setNamespaceURI( final String namespaceURI ) + public void clear() { - wrappedRules.setNamespaceURI( namespaceURI ); + wrappedRules.clear(); + allRules.clear(); + defaultRules.clear(); } /** @@ -138,8 +141,26 @@ public List getDefaults() return defaultRules; } + /** + * {@inheritDoc} + */ + @Override + public Digester getDigester() + { + return wrappedRules.getDigester(); + } + // --------------------------------------------------------- Public Methods + /** + * {@inheritDoc} + */ + @Override + public String getNamespaceURI() + { + return wrappedRules.getNamespaceURI(); + } + /** * {@inheritDoc} */ @@ -156,28 +177,6 @@ public List match( final String namespaceURI, final String pattern, final return matches; } - /** - * Adds a rule to be fired when wrapped implementation returns no matches - * - * @param rule a Rule to be fired when wrapped implementation returns no matches - **/ - public void addDefault( final Rule rule ) - { - // set up rule - if ( wrappedRules.getDigester() != null ) - { - rule.setDigester( wrappedRules.getDigester() ); - } - - if ( wrappedRules.getNamespaceURI() != null ) - { - rule.setNamespaceURI( wrappedRules.getNamespaceURI() ); - } - - defaultRules.add( rule ); - allRules.add( rule ); - } - /** * {@inheritDoc} */ @@ -191,21 +190,22 @@ public List rules() * {@inheritDoc} */ @Override - public void clear() + public void setDigester( final Digester digester ) { - wrappedRules.clear(); - allRules.clear(); - defaultRules.clear(); + wrappedRules.setDigester( digester ); + for ( final Rule rule : defaultRules ) + { + rule.setDigester( digester ); + } } /** * {@inheritDoc} */ @Override - public void add( final String pattern, final Rule rule ) + public void setNamespaceURI( final String namespaceURI ) { - wrappedRules.add( pattern, rule ); - allRules.add( rule ); + wrappedRules.setNamespaceURI( namespaceURI ); } } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/DigesterRule.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/DigesterRule.java index 242709a3d..3fe52ca23 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/DigesterRule.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/DigesterRule.java @@ -40,15 +40,15 @@ public @interface DigesterRule { - /** - * The reflected commons-digester rule. - */ - Class reflectsRule(); - /** * The handler that takes care on converting this annotation in the related * {@link AnnotationHandler} and adds it to the {@link FromAnnotationsRuleModule} */ Class> handledBy(); + /** + * The reflected commons-digester rule. + */ + Class reflectsRule(); + } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/FromAnnotationsRuleModule.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/FromAnnotationsRuleModule.java index 91fe94b72..d6ad6bb40 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/FromAnnotationsRuleModule.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/FromAnnotationsRuleModule.java @@ -51,56 +51,6 @@ public abstract class FromAnnotationsRuleModule private WithMemoryRulesBinder rulesBinder; - /** - * {@inheritDoc} - */ - @Override - protected final void configure() - { - if ( rulesBinder == null ) - { - rulesBinder = new WithMemoryRulesBinder( rulesBinder() ); - } - - try - { - configureRules(); - } - finally - { - rulesBinder = null; - } - } - - /** - * Configures a {@link org.apache.commons.digester3.binder.RulesBinder} via the exposed methods. - */ - protected abstract void configureRules(); - - /** - * Allows users plug a different {@link AnnotationHandlerFactory} to create {@link AnnotationHandler} instances. - * - * @param annotationHandlerFactory A custom {@link AnnotationHandlerFactory} to create - * {@link AnnotationHandler} instances - */ - protected final void useAnnotationHandlerFactory( final AnnotationHandlerFactory annotationHandlerFactory ) - { - if ( annotationHandlerFactory == null ) - { - throw new IllegalArgumentException( "Argument 'annotationHandlerFactory' must be not null" ); - } - - this.annotationHandlerFactory = annotationHandlerFactory; - } - - /** - * Allows users to switch back to the default {@link AnnotationHandlerFactory} implementation. - */ - protected final void useDefaultAnnotationHandlerFactory() - { - useAnnotationHandlerFactory( DEFAULT_HANDLER_FACTORY ); - } - /** * Scan the input Class, looking for Digester rules expressed via annotations, and binds them. * @@ -156,23 +106,100 @@ public Method[] run() } /** + * {@inheritDoc} + */ + @Override + protected final void configure() + { + if ( rulesBinder == null ) + { + rulesBinder = new WithMemoryRulesBinder( rulesBinder() ); + } + + try + { + configureRules(); + } + finally + { + rulesBinder = null; + } + } + + /** + * Configures a {@link org.apache.commons.digester3.binder.RulesBinder} via the exposed methods. + */ + protected abstract void configureRules(); + + /** + * Handles the current visited element and related annotation, invoking the + * right handler putting the rule provider in the rule set. * - * - * @param - * @param action + * @param annotation the current visited annotation. + * @param element the current visited element. */ - private void visitElements( final PrivilegedAction action ) + @SuppressWarnings( "unchecked" ) + private void handle( final A annotation, final E element ) { - AE[] annotatedElements = null; - if ( System.getSecurityManager() != null ) + final Class annotationType = annotation.annotationType(); + + // check if it is one of the @*.List annotation + if ( annotationType.isAnnotationPresent( DigesterRuleList.class ) ) { - annotatedElements = AccessController.doPrivileged( action ); + final Annotation[] annotations = getAnnotationsArrayValue( annotation ); + if ( annotations != null && annotations.length > 0 ) + { + // if it is an annotations array, process them + for ( final Annotation ptr : annotations ) + { + handle( ptr, element ); + } + } } - else + else if ( annotationType.isAnnotationPresent( DigesterRule.class ) ) { - annotatedElements = action.run(); + final DigesterRule digesterRule = annotationType.getAnnotation( DigesterRule.class ); + + // the default behavior if the handler is not specified + final Class> handlerType = + (Class>) digesterRule.handledBy(); + try + { + final AnnotationHandler handler = + annotationHandlerFactory.newInstance( handlerType ); + + // run! + handler.handle( annotation, element, this.rulesBinder ); + } + catch ( final Exception e ) + { + rulesBinder.addError( e ); + } } - visitElements( annotatedElements ); + } + + /** + * Allows users plug a different {@link AnnotationHandlerFactory} to create {@link AnnotationHandler} instances. + * + * @param annotationHandlerFactory A custom {@link AnnotationHandlerFactory} to create + * {@link AnnotationHandler} instances + */ + protected final void useAnnotationHandlerFactory( final AnnotationHandlerFactory annotationHandlerFactory ) + { + if ( annotationHandlerFactory == null ) + { + throw new IllegalArgumentException( "Argument 'annotationHandlerFactory' must be not null" ); + } + + this.annotationHandlerFactory = annotationHandlerFactory; + } + + /** + * Allows users to switch back to the default {@link AnnotationHandlerFactory} implementation. + */ + protected final void useDefaultAnnotationHandlerFactory() + { + useAnnotationHandlerFactory( DEFAULT_HANDLER_FACTORY ); } /** @@ -218,50 +245,23 @@ private void visitElements( final AnnotatedElement... annotatedElements ) } /** - * Handles the current visited element and related annotation, invoking the - * right handler putting the rule provider in the rule set. * - * @param annotation the current visited annotation. - * @param element the current visited element. + * + * @param + * @param action */ - @SuppressWarnings( "unchecked" ) - private void handle( final A annotation, final E element ) + private void visitElements( final PrivilegedAction action ) { - final Class annotationType = annotation.annotationType(); - - // check if it is one of the @*.List annotation - if ( annotationType.isAnnotationPresent( DigesterRuleList.class ) ) + AE[] annotatedElements = null; + if ( System.getSecurityManager() != null ) { - final Annotation[] annotations = getAnnotationsArrayValue( annotation ); - if ( annotations != null && annotations.length > 0 ) - { - // if it is an annotations array, process them - for ( final Annotation ptr : annotations ) - { - handle( ptr, element ); - } - } + annotatedElements = AccessController.doPrivileged( action ); } - else if ( annotationType.isAnnotationPresent( DigesterRule.class ) ) + else { - final DigesterRule digesterRule = annotationType.getAnnotation( DigesterRule.class ); - - // the default behavior if the handler is not specified - final Class> handlerType = - (Class>) digesterRule.handledBy(); - try - { - final AnnotationHandler handler = - annotationHandlerFactory.newInstance( handlerType ); - - // run! - handler.handle( annotation, element, this.rulesBinder ); - } - catch ( final Exception e ) - { - rulesBinder.addError( e ); - } + annotatedElements = action.run(); } + visitElements( annotatedElements ); } } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/WithMemoryRulesBinder.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/WithMemoryRulesBinder.java index 28ef4f37d..2a4eeef6f 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/WithMemoryRulesBinder.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/WithMemoryRulesBinder.java @@ -52,67 +52,67 @@ public WithMemoryRulesBinder( final RulesBinder wrappedRulesBinder ) * {@inheritDoc} */ @Override - public ClassLoader getContextClassLoader() + public void addError( final String messagePattern, final Object... arguments ) { - return wrappedRulesBinder.getContextClassLoader(); + wrappedRulesBinder.addError( messagePattern, arguments ); } /** * {@inheritDoc} */ @Override - public void addError( final String messagePattern, final Object... arguments ) + public void addError( final Throwable t ) { - wrappedRulesBinder.addError( messagePattern, arguments ); + wrappedRulesBinder.addError( t ); } /** * {@inheritDoc} */ @Override - public void addError( final Throwable t ) + public LinkedRuleBuilder forPattern( final String pattern ) { - wrappedRulesBinder.addError( t ); + return wrappedRulesBinder.forPattern( pattern ); } /** * {@inheritDoc} */ @Override - public void install( final RulesModule rulesModule ) + public ClassLoader getContextClassLoader() { - wrappedRulesBinder.install( rulesModule ); + return wrappedRulesBinder.getContextClassLoader(); } /** * {@inheritDoc} */ @Override - public LinkedRuleBuilder forPattern( final String pattern ) + public void install( final RulesModule rulesModule ) { - return wrappedRulesBinder.forPattern( pattern ); + wrappedRulesBinder.install( rulesModule ); } /** * * * @param bindingClass - * @return true if the specified element has not yet been marked + * @return true if the specified element has been marked */ - public boolean markAsBound( final Class bindingClass ) + public boolean isAlreadyBound( final Class bindingClass ) { - return boundClasses.add( bindingClass ); + return boundClasses.contains( bindingClass ); } /** * * * @param bindingClass - * @return true if the specified element has been marked + * @return true if the specified element has not yet been marked */ - public boolean isAlreadyBound( final Class bindingClass ) + public boolean markAsBound( final Class bindingClass ) { - return boundClasses.contains( bindingClass ); + return boundClasses.add( bindingClass ); } } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/handlers/AbstractMethodHandler.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/handlers/AbstractMethodHandler.java index d7c9b817e..33ee34877 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/handlers/AbstractMethodHandler.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/handlers/AbstractMethodHandler.java @@ -53,53 +53,43 @@ abstract class AbstractMethodHandler implements Annotation */ private static final int SUPPORTED_ARGS = 1; - /** - * {@inheritDoc} - */ - @Override - public void handle( final A annotation, final Method element, final RulesBinder rulesBinder ) + protected abstract void doBind( String pattern, String namespaceURI, Method method, Class type, + boolean fireOnBegin, RulesBinder rulesBinder ); + + private void doHandle( final A methodAnnotation, final Annotation annotation, final Method method, final Class type, + final boolean fireOnBegin, final RulesBinder rulesBinder ) { - if ( SUPPORTED_ARGS != element.getParameterTypes().length ) + if ( annotation.annotationType().isAnnotationPresent( DigesterRule.class ) + && annotation.annotationType().isAnnotationPresent( CreationRule.class ) ) { - final DigesterRule rule = annotation.annotationType().getAnnotation( DigesterRule.class ); - - rulesBinder.addError( "Methods annotated with digester annotation rule @%s must have just one argument", - rule.reflectsRule().getName() ); - return; - } + rulesBinder.install( new FromAnnotationsRuleModule() + { - final Object explicitTypesObject = getAnnotationValue( annotation ); - if ( explicitTypesObject == null || !explicitTypesObject.getClass().isArray() - || Class.class != explicitTypesObject.getClass().getComponentType() ) - { - rulesBinder.addError( "Impossible to apply this handler, @%s.value() has to be of type 'Class[]'", - annotation.getClass().getName() ); - return; - } + @Override + protected void configureRules() + { + bindRulesFrom( type ); + } - final Class[] explicitTypes = (Class[]) explicitTypesObject; - final Class paramType = element.getParameterTypes()[0]; - final boolean fireOnBegin = getFireOnBegin( annotation ); + } ); - if ( explicitTypes.length > 0 ) + final String pattern = getAnnotationPattern( annotation ); + final String namespaceURI = getAnnotationNamespaceURI( annotation ); + doBind( pattern, namespaceURI, method, type, fireOnBegin, rulesBinder ); + } + else if ( annotation.annotationType().isAnnotationPresent( DigesterRuleList.class ) ) { - for ( final Class explicitType : explicitTypes ) + // check if it is one of the *.List annotation + final Annotation[] annotations = getAnnotationsArrayValue( annotation ); + if ( annotations != null ) { - if ( !paramType.isAssignableFrom( explicitType ) ) + // if it is an annotations array, process them + for ( final Annotation ptr : annotations ) { - rulesBinder.addError( "Impossible to handle annotation %s on method, %s has to be a %s", - annotation, element.toGenericString(), explicitType.getName(), - paramType.getName() ); - return; + doHandle( methodAnnotation, ptr, method, type, fireOnBegin, rulesBinder ); } - - doHandle( annotation, element, explicitType, fireOnBegin, rulesBinder ); } } - else - { - doHandle( annotation, element, paramType, fireOnBegin, rulesBinder ); - } } private void doHandle( final A methodAnnotation, final Method method, final Class type, final boolean fireOnBegin, @@ -126,43 +116,53 @@ private void doHandle( final A methodAnnotation, final Method method, final Clas } } - private void doHandle( final A methodAnnotation, final Annotation annotation, final Method method, final Class type, - final boolean fireOnBegin, final RulesBinder rulesBinder ) + /** + * {@inheritDoc} + */ + @Override + public void handle( final A annotation, final Method element, final RulesBinder rulesBinder ) { - if ( annotation.annotationType().isAnnotationPresent( DigesterRule.class ) - && annotation.annotationType().isAnnotationPresent( CreationRule.class ) ) + if ( SUPPORTED_ARGS != element.getParameterTypes().length ) { - rulesBinder.install( new FromAnnotationsRuleModule() - { - - @Override - protected void configureRules() - { - bindRulesFrom( type ); - } + final DigesterRule rule = annotation.annotationType().getAnnotation( DigesterRule.class ); - } ); + rulesBinder.addError( "Methods annotated with digester annotation rule @%s must have just one argument", + rule.reflectsRule().getName() ); + return; + } - final String pattern = getAnnotationPattern( annotation ); - final String namespaceURI = getAnnotationNamespaceURI( annotation ); - doBind( pattern, namespaceURI, method, type, fireOnBegin, rulesBinder ); + final Object explicitTypesObject = getAnnotationValue( annotation ); + if ( explicitTypesObject == null || !explicitTypesObject.getClass().isArray() + || Class.class != explicitTypesObject.getClass().getComponentType() ) + { + rulesBinder.addError( "Impossible to apply this handler, @%s.value() has to be of type 'Class[]'", + annotation.getClass().getName() ); + return; } - else if ( annotation.annotationType().isAnnotationPresent( DigesterRuleList.class ) ) + + final Class[] explicitTypes = (Class[]) explicitTypesObject; + final Class paramType = element.getParameterTypes()[0]; + final boolean fireOnBegin = getFireOnBegin( annotation ); + + if ( explicitTypes.length > 0 ) { - // check if it is one of the *.List annotation - final Annotation[] annotations = getAnnotationsArrayValue( annotation ); - if ( annotations != null ) + for ( final Class explicitType : explicitTypes ) { - // if it is an annotations array, process them - for ( final Annotation ptr : annotations ) + if ( !paramType.isAssignableFrom( explicitType ) ) { - doHandle( methodAnnotation, ptr, method, type, fireOnBegin, rulesBinder ); + rulesBinder.addError( "Impossible to handle annotation %s on method, %s has to be a %s", + annotation, element.toGenericString(), explicitType.getName(), + paramType.getName() ); + return; } + + doHandle( annotation, element, explicitType, fireOnBegin, rulesBinder ); } } + else + { + doHandle( annotation, element, paramType, fireOnBegin, rulesBinder ); + } } - protected abstract void doBind( String pattern, String namespaceURI, Method method, Class type, - boolean fireOnBegin, RulesBinder rulesBinder ); - } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/reflect/MethodArgument.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/reflect/MethodArgument.java index ea21f5aca..210edd829 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/reflect/MethodArgument.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/reflect/MethodArgument.java @@ -69,26 +69,6 @@ public MethodArgument( final int index, final Class parameterType, final Anno this.annotations = annotations.clone(); } - /** - * Returns the method argument index. - * - * @return the method argument index. - */ - public int getIndex() - { - return this.index; - } - - /** - * Returns the method argument type. - * - * @return the method argument type. - */ - public Class getParameterType() - { - return this.parameterType; - } - /** * {@inheritDoc} */ @@ -114,6 +94,16 @@ public Annotation[] getAnnotations() return this.getAnnotationsArrayCopy(); } + /** + * Returns an annotations array, copy of the declared annotations in this method argument. + * + * @return an annotations array, copy of the declared annotations in this method argument. + */ + private Annotation[] getAnnotationsArrayCopy() + { + return this.annotations.clone(); + } + /** * {@inheritDoc} */ @@ -124,13 +114,23 @@ public Annotation[] getDeclaredAnnotations() } /** - * Returns an annotations array, copy of the declared annotations in this method argument. + * Returns the method argument index. * - * @return an annotations array, copy of the declared annotations in this method argument. + * @return the method argument index. */ - private Annotation[] getAnnotationsArrayCopy() + public int getIndex() { - return this.annotations.clone(); + return this.index; + } + + /** + * Returns the method argument type. + * + * @return the method argument type. + */ + public Class getParameterType() + { + return this.parameterType; } /** diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/rules/BeanPropertySetter.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/rules/BeanPropertySetter.java index 9d1e7dfca..322c838c8 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/rules/BeanPropertySetter.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/rules/BeanPropertySetter.java @@ -43,18 +43,6 @@ public @interface BeanPropertySetter { - /** - * The element matching pattern. - */ - String pattern(); - - /** - * The namespace URI for which this Rule is relevant, if any. - * - * @since 3.0 - */ - String namespaceURI() default ""; - /** * Defines several {@code @BeanPropertySetter} annotations on the same element. * @@ -69,4 +57,16 @@ BeanPropertySetter[] value(); } + /** + * The namespace URI for which this Rule is relevant, if any. + * + * @since 3.0 + */ + String namespaceURI() default ""; + + /** + * The element matching pattern. + */ + String pattern(); + } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/rules/CallMethod.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/rules/CallMethod.java index de7e0d547..a9766ffac 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/rules/CallMethod.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/rules/CallMethod.java @@ -44,9 +44,18 @@ { /** - * The element matching pattern. + * Defines several {@code @CallMethod} annotations on the same element. + * + * @see CallMethod */ - String pattern(); + @Documented + @Retention( RetentionPolicy.RUNTIME ) + @Target( ElementType.METHOD ) + @DigesterRuleList + @interface List + { + CallMethod[] value(); + } /** * The namespace URI for which this Rule is relevant, if any. @@ -56,22 +65,13 @@ String namespaceURI() default ""; /** - * Marks the {@link CallMethodRule} to be invoked using the matching element body as argument. + * The element matching pattern. */ - boolean usingElementBodyAsArgument() default false; + String pattern(); /** - * Defines several {@code @CallMethod} annotations on the same element. - * - * @see CallMethod + * Marks the {@link CallMethodRule} to be invoked using the matching element body as argument. */ - @Documented - @Retention( RetentionPolicy.RUNTIME ) - @Target( ElementType.METHOD ) - @DigesterRuleList - @interface List - { - CallMethod[] value(); - } + boolean usingElementBodyAsArgument() default false; } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/rules/CallParam.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/rules/CallParam.java index 2b6eb9088..c75785abd 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/rules/CallParam.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/rules/CallParam.java @@ -44,16 +44,18 @@ { /** - * The element matching pattern. - */ - String pattern(); - - /** - * The namespace URI for which this Rule is relevant, if any. + * Defines several {@code @CallParam} annotations on the same element. * - * @since 3.0 + * @see CallParam */ - String namespaceURI() default ""; + @Documented + @Retention( RetentionPolicy.RUNTIME ) + @Target( ElementType.PARAMETER ) + @DigesterRuleList + @interface List + { + CallParam[] value(); + } /** * The attribute from which to save the parameter value. @@ -70,24 +72,22 @@ boolean fromStack() default false; /** - * Sets the position of the object from the top of the stack. + * The namespace URI for which this Rule is relevant, if any. * * @since 3.0 */ - int stackIndex() default 0; + String namespaceURI() default ""; /** - * Defines several {@code @CallParam} annotations on the same element. + * The element matching pattern. + */ + String pattern(); + + /** + * Sets the position of the object from the top of the stack. * - * @see CallParam + * @since 3.0 */ - @Documented - @Retention( RetentionPolicy.RUNTIME ) - @Target( ElementType.PARAMETER ) - @DigesterRuleList - @interface List - { - CallParam[] value(); - } + int stackIndex() default 0; } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/rules/FactoryCreate.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/rules/FactoryCreate.java index 79c9ac131..97f7b1d9a 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/rules/FactoryCreate.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/rules/FactoryCreate.java @@ -47,28 +47,50 @@ { /** - * The Java class of the object creation factory class. + * Dummy ObjectCreationFactory type - only for annotation value type purposes. */ - Class> factoryClass() default DefaultObjectCreationFactory.class; + public static final class DefaultObjectCreationFactory + extends AbstractObjectCreationFactory + { + + /** + * {@inheritDoc} + */ + @Override + public Object createObject( final Attributes attributes ) + throws Exception + { + // do nothing + return null; + } + + } /** - * Allows specify the attribute containing an override class name if it is present. + * Defines several {@code @FactoryCreate} annotations on the same element. * - * @since 3.0 + * @see FactoryCreate */ - String attributeName() default ""; + @Documented + @Retention( RetentionPolicy.RUNTIME ) + @Target( ElementType.TYPE ) + @DigesterRuleList + @interface List + { + FactoryCreate[] value(); + } /** - * The element matching pattern. + * Allows specify the attribute containing an override class name if it is present. + * + * @since 3.0 */ - String pattern(); + String attributeName() default ""; /** - * The namespace URI for which this Rule is relevant, if any. - * - * @since 3.0 + * The Java class of the object creation factory class. */ - String namespaceURI() default ""; + Class> factoryClass() default DefaultObjectCreationFactory.class; /** * When true any exceptions thrown during object creation will be ignored. @@ -76,37 +98,15 @@ boolean ignoreCreateExceptions() default false; /** - * Defines several {@code @FactoryCreate} annotations on the same element. + * The namespace URI for which this Rule is relevant, if any. * - * @see FactoryCreate + * @since 3.0 */ - @Documented - @Retention( RetentionPolicy.RUNTIME ) - @Target( ElementType.TYPE ) - @DigesterRuleList - @interface List - { - FactoryCreate[] value(); - } + String namespaceURI() default ""; /** - * Dummy ObjectCreationFactory type - only for annotation value type purposes. + * The element matching pattern. */ - public static final class DefaultObjectCreationFactory - extends AbstractObjectCreationFactory - { - - /** - * {@inheritDoc} - */ - @Override - public Object createObject( final Attributes attributes ) - throws Exception - { - // do nothing - return null; - } - - } + String pattern(); } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/rules/ObjectCreate.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/rules/ObjectCreate.java index cc80ca263..33ab33ba2 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/rules/ObjectCreate.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/rules/ObjectCreate.java @@ -45,36 +45,36 @@ { /** - * The element matching pattern. + * Defines several {@code @ObjectCreate} annotations on the same element. + * + * @see ObjectCreate */ - String pattern(); + @Documented + @Retention( RetentionPolicy.RUNTIME ) + @Target( ElementType.TYPE ) + @DigesterRuleList + @interface List + { + ObjectCreate[] value(); + } /** - * The namespace URI for which this Rule is relevant, if any. + * Allows specify the attribute containing an override class name if it is present. * * @since 3.0 */ - String namespaceURI() default ""; + String attributeName() default ""; /** - * Allows specify the attribute containing an override class name if it is present. + * The namespace URI for which this Rule is relevant, if any. * * @since 3.0 */ - String attributeName() default ""; + String namespaceURI() default ""; /** - * Defines several {@code @ObjectCreate} annotations on the same element. - * - * @see ObjectCreate + * The element matching pattern. */ - @Documented - @Retention( RetentionPolicy.RUNTIME ) - @Target( ElementType.TYPE ) - @DigesterRuleList - @interface List - { - ObjectCreate[] value(); - } + String pattern(); } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/rules/PathCallParam.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/rules/PathCallParam.java index da308628a..6315a129e 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/rules/PathCallParam.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/rules/PathCallParam.java @@ -43,18 +43,6 @@ public @interface PathCallParam { - /** - * The element matching pattern. - */ - String pattern(); - - /** - * The namespace URI for which this Rule is relevant, if any. - * - * @since 3.0 - */ - String namespaceURI() default ""; - /** * Defines several {@code @PathCallParam} annotations on the same element. * @@ -69,4 +57,16 @@ PathCallParam[] value(); } + /** + * The namespace URI for which this Rule is relevant, if any. + * + * @since 3.0 + */ + String namespaceURI() default ""; + + /** + * The element matching pattern. + */ + String pattern(); + } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/rules/SetNext.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/rules/SetNext.java index 2a307d739..788c71640 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/rules/SetNext.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/rules/SetNext.java @@ -43,13 +43,13 @@ { /** - * Defines the concrete implementation(s) of @SetNext annotated method argument. + * Marks the rule be invoked when {@code begin} or {@code end} events match. */ - Class[] value() default { }; + boolean fireOnBegin() default false; /** - * Marks the rule be invoked when {@code begin} or {@code end} events match. + * Defines the concrete implementation(s) of @SetNext annotated method argument. */ - boolean fireOnBegin() default false; + Class[] value() default { }; } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/rules/SetProperty.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/rules/SetProperty.java index 505fa5455..771249858 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/rules/SetProperty.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/rules/SetProperty.java @@ -43,23 +43,6 @@ public @interface SetProperty { - /** - * The element matching pattern. - */ - String pattern(); - - /** - * The namespace URI for which this Rule is relevant, if any. - * - * @since 3.0 - */ - String namespaceURI() default ""; - - /** - * The overridden parameter. - */ - String attributeName() default ""; - /** * Defines several {@code @SetProperty} annotations on the same element. * @@ -74,4 +57,21 @@ SetProperty[] value(); } + /** + * The overridden parameter. + */ + String attributeName() default ""; + + /** + * The namespace URI for which this Rule is relevant, if any. + * + * @since 3.0 + */ + String namespaceURI() default ""; + + /** + * The element matching pattern. + */ + String pattern(); + } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/rules/SetRoot.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/rules/SetRoot.java index a1e25f507..977da3671 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/rules/SetRoot.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/rules/SetRoot.java @@ -43,13 +43,13 @@ { /** - * Defines the concrete implementation(s) of @SetRoot annotated method argument. + * Marks the rule be invoked when {@code begin} or {@code end} events match. */ - Class[] value() default { }; + boolean fireOnBegin() default false; /** - * Marks the rule be invoked when {@code begin} or {@code end} events match. + * Defines the concrete implementation(s) of @SetRoot annotated method argument. */ - boolean fireOnBegin() default false; + Class[] value() default { }; } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/rules/SetTop.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/rules/SetTop.java index 37835a627..42bc1ccb9 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/rules/SetTop.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/rules/SetTop.java @@ -44,9 +44,18 @@ { /** - * The element matching pattern. + * Defines several {@code @SetTop} annotations on the same element + * + * @see SetTop */ - String pattern(); + @Documented + @Retention( RetentionPolicy.RUNTIME ) + @Target( ElementType.METHOD ) + @DigesterRuleList + @interface List + { + SetTop[] value(); + } /** * Marks the rule be invoked when {@code begin} or {@code end} events match. @@ -61,17 +70,8 @@ String namespaceURI() default ""; /** - * Defines several {@code @SetTop} annotations on the same element - * - * @see SetTop + * The element matching pattern. */ - @Documented - @Retention( RetentionPolicy.RUNTIME ) - @Target( ElementType.METHOD ) - @DigesterRuleList - @interface List - { - SetTop[] value(); - } + String pattern(); } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/utils/AnnotationUtils.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/utils/AnnotationUtils.java index bed473175..b7a760da6 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/utils/AnnotationUtils.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/annotations/utils/AnnotationUtils.java @@ -52,22 +52,19 @@ public class AnnotationUtils private static final String FIRE_ON_BEGIN = "fireOnBegin"; /** - * This class can't be instantiated. - */ - private AnnotationUtils() - { - // this class can' be instantiated - } - - /** - * Extract the {@code value()} from annotation. + * Extract the {@code namespaceURI()} from annotation. * - * @param annotation the annotation has to be introspected. - * @return the annotation {@code value()}. + * @param annotation The annotation has to be introspected + * @return The annotation {@code namespaceURI()} */ - public static Object getAnnotationValue( final Annotation annotation ) + public static String getAnnotationNamespaceURI( final Annotation annotation ) { - return invokeAnnotationMethod( annotation, VALUE ); + final Object ret = invokeAnnotationMethod( annotation, NAMESPACE_URI ); + if ( ret != null ) + { + return (String) ret; + } + return null; } /** @@ -87,21 +84,33 @@ public static String getAnnotationPattern( final Annotation annotation ) } /** - * Extract the {@code namespaceURI()} from annotation. + * Extract the Annotations array {@code value()} from annotation if present, nul otherwise. * - * @param annotation The annotation has to be introspected - * @return The annotation {@code namespaceURI()} + * @param annotation the annotation has to be introspected. + * @return the annotation {@code value()} as Annotations array. */ - public static String getAnnotationNamespaceURI( final Annotation annotation ) + public static Annotation[] getAnnotationsArrayValue( final Annotation annotation ) { - final Object ret = invokeAnnotationMethod( annotation, NAMESPACE_URI ); - if ( ret != null ) + final Object value = getAnnotationValue( annotation ); + if ( value != null && value.getClass().isArray() + && Annotation.class.isAssignableFrom( value.getClass().getComponentType() ) ) { - return (String) ret; + return (Annotation[]) value; } return null; } + /** + * Extract the {@code value()} from annotation. + * + * @param annotation the annotation has to be introspected. + * @return the annotation {@code value()}. + */ + public static Object getAnnotationValue( final Annotation annotation ) + { + return invokeAnnotationMethod( annotation, VALUE ); + } + /** * Extract the {@code fireOnBegin()} from annotation. * @@ -118,23 +127,6 @@ public static boolean getFireOnBegin( final Annotation annotation ) return false; } - /** - * Extract the Annotations array {@code value()} from annotation if present, nul otherwise. - * - * @param annotation the annotation has to be introspected. - * @return the annotation {@code value()} as Annotations array. - */ - public static Annotation[] getAnnotationsArrayValue( final Annotation annotation ) - { - final Object value = getAnnotationValue( annotation ); - if ( value != null && value.getClass().isArray() - && Annotation.class.isAssignableFrom( value.getClass().getComponentType() ) ) - { - return (Annotation[]) value; - } - return null; - } - /** * Invokes an annotation method. * @@ -154,4 +146,12 @@ private static Object invokeAnnotationMethod( final Annotation annotation, final } } + /** + * This class can't be instantiated. + */ + private AnnotationUtils() + { + // this class can' be instantiated + } + } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/AbstractBackToLinkedRuleBuilder.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/AbstractBackToLinkedRuleBuilder.java index c3011abca..f7d557b49 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/AbstractBackToLinkedRuleBuilder.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/AbstractBackToLinkedRuleBuilder.java @@ -48,24 +48,12 @@ abstract class AbstractBackToLinkedRuleBuilder } /** - * Come back to the main {@link LinkedRuleBuilder}. - * - * @return the main {@link LinkedRuleBuilder} - */ - public final LinkedRuleBuilder then() - { - return this.mainBuilder; - } - - /** - * Returns the namespace URI for which this Rule is relevant, if any. + * Provides an instance of {@link Rule}. Must never return null. * - * @return The namespace URI for which this Rule is relevant, if any + * @return an instance of {@link Rule}. + * @see #get() */ - public final String getNamespaceURI() - { - return this.namespaceURI; - } + protected abstract R createRule(); /** * {@inheritDoc} @@ -81,9 +69,14 @@ public final R get() return rule; } - protected final void reportError( final String methodChain, final String message ) + /** + * Returns the namespace URI for which this Rule is relevant, if any. + * + * @return The namespace URI for which this Rule is relevant, if any + */ + public final String getNamespaceURI() { - this.mainBinder.addError( "{ forPattern( \"%s\" ).%s } %s", this.keyPattern, methodChain, message ); + return this.namespaceURI; } /** @@ -96,12 +89,19 @@ public final String getPattern() return keyPattern; } + protected final void reportError( final String methodChain, final String message ) + { + this.mainBinder.addError( "{ forPattern( \"%s\" ).%s } %s", this.keyPattern, methodChain, message ); + } + /** - * Provides an instance of {@link Rule}. Must never return null. + * Come back to the main {@link LinkedRuleBuilder}. * - * @return an instance of {@link Rule}. - * @see #get() + * @return the main {@link LinkedRuleBuilder} */ - protected abstract R createRule(); + public final LinkedRuleBuilder then() + { + return this.mainBuilder; + } } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/AbstractParamTypeBuilder.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/AbstractParamTypeBuilder.java index 43b29d019..133a6ed00 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/AbstractParamTypeBuilder.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/AbstractParamTypeBuilder.java @@ -54,6 +54,50 @@ public abstract class AbstractParamTypeBuilder this.classLoader = classLoader; } + /** + * Marks the rule be invoked when {@code begin} or {@code end} events match. + * + * @param fireOnBegin true, to invoke the rule at {@code begin}, false for {@code end} + * @return this builder instance + */ + public final AbstractParamTypeBuilder fireOnBegin( final boolean fireOnBegin ) + { + this.fireOnBegin = fireOnBegin; + return this; + } + + final String getMethodName() + { + return methodName; + } + + final Class getParamType() + { + return paramType; + } + + final boolean isFireOnBegin() + { + return fireOnBegin; + } + + final boolean isUseExactMatch() + { + return useExactMatch; + } + + /** + * Sets exact matching being used. + * + * @param useExactMatch The exact matching being used + * @return this builder instance + */ + public final AbstractParamTypeBuilder useExactMatch( final boolean useExactMatch ) + { + this.useExactMatch = useExactMatch; + return this; + } + /** * Sets the Java class of the method's argument. * @@ -108,48 +152,4 @@ public final AbstractParamTypeBuilder withParameterType( final String paramTy return this; } - /** - * Sets exact matching being used. - * - * @param useExactMatch The exact matching being used - * @return this builder instance - */ - public final AbstractParamTypeBuilder useExactMatch( final boolean useExactMatch ) - { - this.useExactMatch = useExactMatch; - return this; - } - - /** - * Marks the rule be invoked when {@code begin} or {@code end} events match. - * - * @param fireOnBegin true, to invoke the rule at {@code begin}, false for {@code end} - * @return this builder instance - */ - public final AbstractParamTypeBuilder fireOnBegin( final boolean fireOnBegin ) - { - this.fireOnBegin = fireOnBegin; - return this; - } - - final String getMethodName() - { - return methodName; - } - - final Class getParamType() - { - return paramType; - } - - final boolean isUseExactMatch() - { - return useExactMatch; - } - - final boolean isFireOnBegin() - { - return fireOnBegin; - } - } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/AbstractRulesModule.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/AbstractRulesModule.java index 05698474c..1e8b46cf9 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/AbstractRulesModule.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/AbstractRulesModule.java @@ -30,33 +30,6 @@ public abstract class AbstractRulesModule private RulesBinder rulesBinder; - /** - * {@inheritDoc} - */ - @Override - public final void configure( final RulesBinder rulesBinder ) - { - if ( this.rulesBinder != null ) - { - throw new IllegalStateException( "Re-entry is not allowed." ); - } - - this.rulesBinder = rulesBinder; - try - { - configure(); - } - finally - { - this.rulesBinder = null; - } - } - - /** - * Configures a {@link RulesBinder} via the exposed methods. - */ - protected abstract void configure(); - /** * Records an error message which will be presented to the user at a later time. * @@ -85,14 +58,30 @@ protected void addError( final Throwable t ) } /** - * Uses the given module to configure more bindings. - * - * @param rulesModule The module used to configure more bindings - * @see RulesBinder#install(RulesModule) + * Configures a {@link RulesBinder} via the exposed methods. */ - protected void install( final RulesModule rulesModule ) + protected abstract void configure(); + + /** + * {@inheritDoc} + */ + @Override + public final void configure( final RulesBinder rulesBinder ) { - rulesBinder.install( rulesModule ); + if ( this.rulesBinder != null ) + { + throw new IllegalStateException( "Re-entry is not allowed." ); + } + + this.rulesBinder = rulesBinder; + try + { + configure(); + } + finally + { + this.rulesBinder = null; + } } /** @@ -107,6 +96,17 @@ protected LinkedRuleBuilder forPattern( final String pattern ) return rulesBinder.forPattern( pattern ); } + /** + * Uses the given module to configure more bindings. + * + * @param rulesModule The module used to configure more bindings + * @see RulesBinder#install(RulesModule) + */ + protected void install( final RulesModule rulesModule ) + { + rulesBinder.install( rulesModule ); + } + /** * Return the wrapped {@link RulesBinder}. * diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/BeanPropertySetterBuilder.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/BeanPropertySetterBuilder.java index a9ab1b335..05b07deb2 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/BeanPropertySetterBuilder.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/BeanPropertySetterBuilder.java @@ -39,15 +39,14 @@ public final class BeanPropertySetterBuilder } /** - * Sets the name of property to set. - * - * @param propertyName The name of property to set - * @return this builder instance + * {@inheritDoc} */ - public BeanPropertySetterBuilder withName( /* @Nullable */final String propertyName ) + @Override + protected BeanPropertySetterRule createRule() { - this.propertyName = propertyName; - return this; + final BeanPropertySetterRule rule = new BeanPropertySetterRule( propertyName ); + rule.setPropertyNameFromAttribute( attribute ); + return rule; } /** @@ -68,14 +67,15 @@ public BeanPropertySetterBuilder extractPropertyNameFromAttribute( final String } /** - * {@inheritDoc} + * Sets the name of property to set. + * + * @param propertyName The name of property to set + * @return this builder instance */ - @Override - protected BeanPropertySetterRule createRule() + public BeanPropertySetterBuilder withName( /* @Nullable */final String propertyName ) { - final BeanPropertySetterRule rule = new BeanPropertySetterRule( propertyName ); - rule.setPropertyNameFromAttribute( attribute ); - return rule; + this.propertyName = propertyName; + return this; } } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/BinderClassLoader.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/BinderClassLoader.java index 2dae538a5..cbaedd3c0 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/BinderClassLoader.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/BinderClassLoader.java @@ -81,23 +81,23 @@ public ClassLoader getAdaptedClassLoader() * {@inheritDoc} */ @Override - protected synchronized Class loadClass( final String name, final boolean resolve ) - throws ClassNotFoundException + public URL getResource( final String name ) { - if ( PRIMITIVE_TYPES.containsKey( name ) ) - { - return PRIMITIVE_TYPES.get( name ); - } - return getParent().loadClass( name ); + return getAdaptedClassLoader().getResource( name ); } /** * {@inheritDoc} */ @Override - public URL getResource( final String name ) + protected synchronized Class loadClass( final String name, final boolean resolve ) + throws ClassNotFoundException { - return getAdaptedClassLoader().getResource( name ); + if ( PRIMITIVE_TYPES.containsKey( name ) ) + { + return PRIMITIVE_TYPES.get( name ); + } + return getParent().loadClass( name ); } } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/CallMethodBuilder.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/CallMethodBuilder.java index b1fe0c941..676b00c49 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/CallMethodBuilder.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/CallMethodBuilder.java @@ -55,88 +55,36 @@ public final class CallMethodBuilder } /** - * Sets the location of the target object. - * - * Positive numbers are relative to the top of the digester object stack. - * Negative numbers are relative to the bottom of the stack. Zero implies the top object on the stack. - * - * @param targetOffset location of the target object. - * @return this builder instance - */ - public CallMethodBuilder withTargetOffset( final int targetOffset ) - { - this.targetOffset = targetOffset; - return this; - } - - /** - * Sets the Java class names that represent the parameter types of the method arguments. - * - * If you wish to use a primitive type, specify the corresonding Java wrapper class instead, - * such as {@code java.lang.Boolean.TYPE} for a {@code boolean} parameter. - * - * @param paramTypeNames The Java classes names that represent the parameter types of the method arguments - * @return this builder instance + * {@inheritDoc} */ - public CallMethodBuilder withParamTypes( final String... paramTypeNames ) + @Override + protected CallMethodRule createRule() { - Class[] paramTypes = null; - if ( paramTypeNames != null ) - { - paramTypes = new Class[paramTypeNames.length]; - for ( int i = 0; i < paramTypeNames.length; i++ ) - { - try - { - paramTypes[i] = classLoader.loadClass( paramTypeNames[i] ); - } - catch ( final ClassNotFoundException e ) - { - this.reportError( format( "callMethod( \"%s\" ).withParamTypes( %s )", this.methodName, - Arrays.toString( paramTypeNames ) ), - format( "class '%s' cannot be load", paramTypeNames[i] ) ); - } - } - } - - return withParamTypes( paramTypes ); + final CallMethodRule callMethodRule = new CallMethodRule( targetOffset, methodName, paramCount, paramTypes ); + callMethodRule.setUseExactMatch( useExactMatch ); + return callMethodRule; } /** - * Sets the Java classes that represent the parameter types of the method arguments. - * - * If you wish to use a primitive type, specify the corresonding Java wrapper class instead, - * such as {@code java.lang.Boolean.TYPE} for a {@code boolean} parameter. + * Should {@code MethodUtils.invokeExactMethod} be used for the reflection. * - * @param paramTypes The Java classes that represent the parameter types of the method arguments + * @param useExactMatch Flag to mark exact matching or not * @return this builder instance */ - public CallMethodBuilder withParamTypes( final Class... paramTypes ) + public CallMethodBuilder useExactMatch( final boolean useExactMatch ) { - this.paramTypes = paramTypes; - - if ( paramTypes != null ) - { - this.paramCount = paramTypes.length; - } - else - { - paramCount = 0; - } - + this.useExactMatch = useExactMatch; return this; } /** - * Should {@code MethodUtils.invokeExactMethod} be used for the reflection. + * Prepare the {@link CallMethodRule} to be invoked using the matching element body as argument. * - * @param useExactMatch Flag to mark exact matching or not * @return this builder instance */ - public CallMethodBuilder useExactMatch( final boolean useExactMatch ) + public CallMethodBuilder usingElementBodyAsArgument() { - this.useExactMatch = useExactMatch; - return this; + return withParamCount( 0 ); } /** @@ -175,24 +123,76 @@ public CallMethodBuilder withParamCount( final int paramCount ) } /** - * Prepare the {@link CallMethodRule} to be invoked using the matching element body as argument. + * Sets the Java classes that represent the parameter types of the method arguments. * + * If you wish to use a primitive type, specify the corresonding Java wrapper class instead, + * such as {@code java.lang.Boolean.TYPE} for a {@code boolean} parameter. + * + * @param paramTypes The Java classes that represent the parameter types of the method arguments * @return this builder instance */ - public CallMethodBuilder usingElementBodyAsArgument() + public CallMethodBuilder withParamTypes( final Class... paramTypes ) { - return withParamCount( 0 ); + this.paramTypes = paramTypes; + + if ( paramTypes != null ) + { + this.paramCount = paramTypes.length; + } + else + { + paramCount = 0; + } + + return this; } /** - * {@inheritDoc} + * Sets the Java class names that represent the parameter types of the method arguments. + * + * If you wish to use a primitive type, specify the corresonding Java wrapper class instead, + * such as {@code java.lang.Boolean.TYPE} for a {@code boolean} parameter. + * + * @param paramTypeNames The Java classes names that represent the parameter types of the method arguments + * @return this builder instance */ - @Override - protected CallMethodRule createRule() + public CallMethodBuilder withParamTypes( final String... paramTypeNames ) { - final CallMethodRule callMethodRule = new CallMethodRule( targetOffset, methodName, paramCount, paramTypes ); - callMethodRule.setUseExactMatch( useExactMatch ); - return callMethodRule; + Class[] paramTypes = null; + if ( paramTypeNames != null ) + { + paramTypes = new Class[paramTypeNames.length]; + for ( int i = 0; i < paramTypeNames.length; i++ ) + { + try + { + paramTypes[i] = classLoader.loadClass( paramTypeNames[i] ); + } + catch ( final ClassNotFoundException e ) + { + this.reportError( format( "callMethod( \"%s\" ).withParamTypes( %s )", this.methodName, + Arrays.toString( paramTypeNames ) ), + format( "class '%s' cannot be load", paramTypeNames[i] ) ); + } + } + } + + return withParamTypes( paramTypes ); + } + + /** + * Sets the location of the target object. + * + * Positive numbers are relative to the top of the digester object stack. + * Negative numbers are relative to the bottom of the stack. Zero implies the top object on the stack. + * + * @param targetOffset location of the target object. + * @return this builder instance + */ + public CallMethodBuilder withTargetOffset( final int targetOffset ) + { + this.targetOffset = targetOffset; + return this; } } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/CallParamBuilder.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/CallParamBuilder.java index 6a177d2bb..c966926b5 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/CallParamBuilder.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/CallParamBuilder.java @@ -44,20 +44,25 @@ public final class CallParamBuilder } /** - * Sets the zero-relative parameter number. - * - * @param paramIndex The zero-relative parameter number - * @return this builder instance + * {@inheritDoc} */ - public CallParamBuilder ofIndex( final int paramIndex ) + @Override + protected CallParamRule createRule() { - if ( paramIndex < 0 ) + CallParamRule rule; + + if ( fromStack ) { - reportError( "callParam().ofIndex( int )", "negative index argument not allowed" ); + rule = new CallParamRule( paramIndex, stackIndex ); + } + else + { + rule = new CallParamRule( paramIndex ); } - this.paramIndex = paramIndex; - return this; + rule.setAttributeName( attributeName ); + + return rule; } /** @@ -85,38 +90,33 @@ public CallParamBuilder fromStack( final boolean fromStack ) } /** - * Sets the position of the object from the top of the stack. + * Sets the zero-relative parameter number. * - * @param stackIndex The position of the object from the top of the stack + * @param paramIndex The zero-relative parameter number * @return this builder instance */ - public CallParamBuilder withStackIndex( final int stackIndex ) + public CallParamBuilder ofIndex( final int paramIndex ) { - this.stackIndex = stackIndex; - this.fromStack = true; + if ( paramIndex < 0 ) + { + reportError( "callParam().ofIndex( int )", "negative index argument not allowed" ); + } + + this.paramIndex = paramIndex; return this; } /** - * {@inheritDoc} + * Sets the position of the object from the top of the stack. + * + * @param stackIndex The position of the object from the top of the stack + * @return this builder instance */ - @Override - protected CallParamRule createRule() + public CallParamBuilder withStackIndex( final int stackIndex ) { - CallParamRule rule; - - if ( fromStack ) - { - rule = new CallParamRule( paramIndex, stackIndex ); - } - else - { - rule = new CallParamRule( paramIndex ); - } - - rule.setAttributeName( attributeName ); - - return rule; + this.stackIndex = stackIndex; + this.fromStack = true; + return this; } } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/DefaultRulesBinder.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/DefaultRulesBinder.java index 9b2ff55ae..062138619 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/DefaultRulesBinder.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/DefaultRulesBinder.java @@ -51,24 +51,14 @@ final class DefaultRulesBinder private ClassLoader classLoader; /** + * Records an error, the full details of which will be logged, and the message of which will be presented to the + * user at a later time. * - * - * @param classLoader - */ - void initialize( final ClassLoader classLoader ) - { - this.classLoader = classLoader; - fromBinderRuleSet.clear(); - errors.clear(); - } - - /** - * {@inheritDoc} + * @param errorMessage The error to record. */ - @Override - public ClassLoader getContextClassLoader() + private void addError( final ErrorMessage errorMessage ) { - return this.classLoader; + this.errors.add( errorMessage ); } /** @@ -131,23 +121,13 @@ public void addError( final Throwable t ) } /** - * Records an error, the full details of which will be logged, and the message of which will be presented to the - * user at a later time. * - * @param errorMessage The error to record. - */ - private void addError( final ErrorMessage errorMessage ) - { - this.errors.add( errorMessage ); - } - - /** - * {@inheritDoc} + * + * @return */ - @Override - public void install( final RulesModule rulesModule ) + int errorsSize() { - rulesModule.configure( this ); + return errors.size(); } /** @@ -179,14 +159,23 @@ public LinkedRuleBuilder forPattern( final String pattern ) return new LinkedRuleBuilder( this, fromBinderRuleSet, classLoader, keyPattern ); } + /** + * {@inheritDoc} + */ + @Override + public ClassLoader getContextClassLoader() + { + return this.classLoader; + } + /** * * * @return */ - boolean hasError() + Iterable getErrors() { - return !errors.isEmpty(); + return errors; } /** @@ -194,9 +183,9 @@ boolean hasError() * * @return */ - int errorsSize() + RuleSet getFromBinderRuleSet() { - return errors.size(); + return fromBinderRuleSet; } /** @@ -204,19 +193,30 @@ int errorsSize() * * @return */ - Iterable getErrors() + boolean hasError() { - return errors; + return !errors.isEmpty(); } /** * * - * @return + * @param classLoader */ - RuleSet getFromBinderRuleSet() + void initialize( final ClassLoader classLoader ) { - return fromBinderRuleSet; + this.classLoader = classLoader; + fromBinderRuleSet.clear(); + errors.clear(); + } + + /** + * {@inheritDoc} + */ + @Override + public void install( final RulesModule rulesModule ) + { + rulesModule.configure( this ); } } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/DigesterLoader.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/DigesterLoader.java index e10670d6a..152584f8b 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/DigesterLoader.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/DigesterLoader.java @@ -63,34 +63,34 @@ public final class DigesterLoader private static final String HEADING = "Digester creation errors:%n%n"; /** - * Creates a new {@link DigesterLoader} instance given one or more {@link RulesModule} instance. + * Creates a new {@link DigesterLoader} instance given a collection of {@link RulesModule} instance. * * @param rulesModules The modules containing the {@code Rule} binding * @return A new {@link DigesterLoader} instance */ - public static DigesterLoader newLoader( final RulesModule... rulesModules ) + public static DigesterLoader newLoader( final Iterable rulesModules ) { - if ( rulesModules == null || rulesModules.length == 0 ) + if ( rulesModules == null ) { - throw new DigesterLoadingException( "At least one RulesModule has to be specified" ); + throw new DigesterLoadingException( "RulesModule has to be specified" ); } - return newLoader( Arrays.asList( rulesModules ) ); + + return new DigesterLoader( rulesModules ); } /** - * Creates a new {@link DigesterLoader} instance given a collection of {@link RulesModule} instance. + * Creates a new {@link DigesterLoader} instance given one or more {@link RulesModule} instance. * * @param rulesModules The modules containing the {@code Rule} binding * @return A new {@link DigesterLoader} instance */ - public static DigesterLoader newLoader( final Iterable rulesModules ) + public static DigesterLoader newLoader( final RulesModule... rulesModules ) { - if ( rulesModules == null ) + if ( rulesModules == null || rulesModules.length == 0 ) { - throw new DigesterLoadingException( "RulesModule has to be specified" ); + throw new DigesterLoadingException( "At least one RulesModule has to be specified" ); } - - return new DigesterLoader( rulesModules ); + return newLoader( Arrays.asList( rulesModules ) ); } /** @@ -165,123 +165,109 @@ private DigesterLoader( final Iterable rulesModules ) } /** - * Determine whether to use the Context ClassLoader (the one found by - * calling {@code Thread.currentThread().getContextClassLoader()}) - * to resolve/load classes that are defined in various rules. If not - * using Context ClassLoader, then the class-loading defaults to - * using the calling-class' ClassLoader. + * Add rules to an already created Digester instance, analyzing the digester annotations in the target class. * - * @param useContextClassLoader determines whether to use Context ClassLoader. - * @return This loader instance, useful to chain methods. + * @param digester the Digester instance reference. */ - public DigesterLoader setUseContextClassLoader( final boolean useContextClassLoader ) + public void addRules( final Digester digester ) { - if ( useContextClassLoader ) - { - setClassLoader( Thread.currentThread().getContextClassLoader() ); - } - else - { - setClassLoader( getClass().getClassLoader() ); - } - return this; + final RuleSet ruleSet = createRuleSet(); + ruleSet.addRuleInstances( digester ); } /** - * Sets the class loader to be used for instantiating application objects when required. + * Creates a new {@link RuleSet} instance based on the current configuration. * - * @param classLoader the class loader to be used for instantiating application objects when required. - * @return This loader instance, useful to chain methods. + * @return A new {@link RuleSet} instance based on the current configuration. */ - public DigesterLoader setClassLoader( final ClassLoader classLoader ) + public RuleSet createRuleSet() { - if ( classLoader == null ) + if ( rulesBinder.hasError() ) { - throw new IllegalArgumentException( "Parameter 'classLoader' cannot be null" ); - } + final Formatter fmt = new Formatter().format( HEADING ); + int index = 1; - this.classLoader = createBinderClassLoader( classLoader ); + for ( final ErrorMessage errorMessage : rulesBinder.getErrors() ) + { + fmt.format( "%s) %s%n", index++, errorMessage.getMessage() ); - rulesBinder.initialize( this.classLoader ); - for ( final RulesModule rulesModule : rulesModules ) - { - rulesModule.configure( rulesBinder ); - } + final Throwable cause = errorMessage.getCause(); + if ( cause != null ) + { + final StringWriter writer = new StringWriter(); + cause.printStackTrace( new PrintWriter( writer ) ); + fmt.format( "Caused by: %s", writer.getBuffer() ); + } - return this; - } + fmt.format( "%n" ); + } - /** - * Sets the {@code Substitutor} to be used to convert attributes and body text. - * - * @param substitutor the Substitutor to be used to convert attributes and body text - * or null if not substitution of these values is to be performed. - * @return This loader instance, useful to chain methods. - */ - public DigesterLoader setSubstitutor( final Substitutor substitutor ) - { - this.substitutor = substitutor; - return this; + if ( rulesBinder.errorsSize() == 1 ) + { + fmt.format( "1 error" ); + } + else + { + fmt.format( "%s errors", rulesBinder.errorsSize() ); + } + + throw new DigesterLoadingException( fmt.toString() ); + } + + return rulesBinder.getFromBinderRuleSet(); } /** - * Sets the "namespace aware" flag for parsers we create. + * Gets the document locator associated with our parser. * - * @param namespaceAware The new "namespace aware" flag - * @return This loader instance, useful to chain methods. + * @return the Locator supplied by the document parser + * @since 3.2 */ - public DigesterLoader setNamespaceAware( final boolean namespaceAware ) + public Locator getDocumentLocator() { - factory.setNamespaceAware( namespaceAware ); - return this; + return locator; } /** - * Return the "namespace aware" flag for parsers we create. + * Return the error handler for this Digester. * - * @return true, if the "namespace aware" flag for parsers we create, false otherwise. + * @return the error handler for this Digester. + * @since 3.2 */ - public boolean isNamespaceAware() + public ErrorHandler getErrorHandler() { - return factory.isNamespaceAware(); + return ( this.errorHandler ); } /** - * Sets the XInclude-aware flag for parsers we create. This additionally - * requires namespace-awareness. + * Returns the executor service used to run asynchronous parse method. * - * @param xIncludeAware The new XInclude-aware flag - * @return This loader instance, useful to chain methods. - * @see #setNamespaceAware(boolean) + * @return the executor service used to run asynchronous parse method + * @since 3.1 */ - public DigesterLoader setXIncludeAware( final boolean xIncludeAware ) + public ExecutorService getExecutorService() { - factory.setXIncludeAware( xIncludeAware ); - return this; + return executorService; } /** - * Return the XInclude-aware flag for parsers we create; + * Return the set of DTD URL registrations, keyed by public identifier. * - * @return true, if the XInclude-aware flag for parsers we create is set, - * false otherwise + * @return the set of DTD URL registrations. */ - public boolean isXIncludeAware() + public Map getRegistrations() { - return factory.isXIncludeAware(); + return Collections.unmodifiableMap( this.entityValidator ); } /** - * Sets the {@code DOCTYPE} validation parser flag and should not be used when using schemas. + * Return the "namespace aware" flag for parsers we create. * - * @param validating The new validating parser flag. - * @return This loader instance, useful to chain methods. - * @see javax.xml.parsers.SAXParserFactory#setValidating(boolean) + * @return true, if the "namespace aware" flag for parsers we create, false otherwise. */ - public DigesterLoader setValidating( final boolean validating ) + public boolean isNamespaceAware() { - factory.setValidating( validating ); - return this; + return factory.isNamespaceAware(); } /** @@ -295,368 +281,382 @@ public boolean isValidating() } /** - * Sets the XML Schema to be used when parsing. + * Return the XInclude-aware flag for parsers we create; * - * @param schema The {@link Schema} instance to use. - * @return This loader instance, useful to chain methods. + * @return true, if the XInclude-aware flag for parsers we create is set, + * false otherwise */ - public DigesterLoader setSchema( final Schema schema ) + public boolean isXIncludeAware() { - factory.setSchema( schema ); - return this; + return factory.isXIncludeAware(); } /** - * Sets a flag indicating whether the requested feature is supported by the underlying implementation of - * {@code org.xml.sax.XMLReader}. + * Creates a new {@link Digester} instance that relies on the default {@link Rules} implementation. * - * @see org.apache.commons.digester3.Digester#setFeature(String, boolean) - * @param feature Name of the feature to set the status for - * @param value The new value for this feature - * @return This loader instance, useful to chain methods. - * @throws ParserConfigurationException if a parser configuration error occurs - * @throws SAXNotRecognizedException if the property name is not recognized - * @throws SAXNotSupportedException if the property name is recognized but not supported - * @since 3.3 - */ - public DigesterLoader setFeature( final String feature, final boolean value ) - throws SAXNotRecognizedException, SAXNotSupportedException, ParserConfigurationException - { - factory.setFeature(feature, value); - return this; - } - - /** - *

Register the specified DTD URL for the specified public identifier. - * This must be called before the first call to {@code parse()}. - *

- * {@code Digester} contains an internal {@code EntityResolver} - * implementation. This maps {@code PUBLICID}'s to URLs - * (from which the resource will be loaded). A common use case for this - * method is to register local URLs (possibly computed at runtime by a - * classloader) for DTDs. This allows the performance advantage of using - * a local version without having to ensure every {@code SYSTEM} - * URI on every processed xml document is local. This implementation provides - * only basic functionality. If more sophisticated features are required, - * using {@link #setEntityResolver(EntityResolver)} to set a custom resolver is recommended. - *

- * Note: This method will have no effect when a custom - * {@code EntityResolver} has been set. (Setting a custom - * {@code EntityResolver} overrides the internal implementation.) - *

- * @param publicId Public identifier of the DTD to be resolved - * @param entityURL The URL to use for reading this DTD - * @return This loader instance, useful to chain methods. + * @return a new {@link Digester} instance */ - public DigesterLoader register( final String publicId, final URL entityURL ) + public Digester newDigester() { - entityValidator.put( publicId, entityURL ); - return this; + return this.newDigester( new RulesBase() ); } /** - *

Convenience method that registers the string version of an entity URL - * instead of a URL version.

+ * Creates a new {@link Digester} instance that relies on the custom user define {@link Rules} implementation * - * @param publicId Public identifier of the entity to be resolved - * @param entityURL The URL to use for reading this entity - * @return This loader instance, useful to chain methods. + * @param rules The custom user define {@link Rules} implementation + * @return a new {@link Digester} instance */ - public DigesterLoader register( final String publicId, final String entityURL ) + public Digester newDigester( final Rules rules ) { try { - return register( publicId, new URL( entityURL ) ); + return this.newDigester( this.factory.newSAXParser(), rules ); } - catch ( final MalformedURLException e ) + catch ( final ParserConfigurationException e ) { - throw new IllegalArgumentException( "Malformed URL '" + entityURL + "' : " + e.getMessage() ); + throw new DigesterLoadingException( "SAX Parser misconfigured", e ); + } + catch ( final SAXException e ) + { + throw new DigesterLoadingException( "An error occurred while initializing the SAX Parser", e ); } } /** - * Return the set of DTD URL registrations, keyed by public identifier. + * Creates a new {@link Digester} instance that relies on the given {@code SAXParser} + * and the default {@link Rules} implementation. * - * @return the set of DTD URL registrations. + * @param parser the user-defined {@code SAXParser} + * @return a new {@link Digester} instance */ - public Map getRegistrations() + public Digester newDigester( final SAXParser parser ) { - return Collections.unmodifiableMap( this.entityValidator ); + return newDigester( parser, new RulesBase() ); } /** - * Sets the {@code EntityResolver} used by SAX when resolving public id and system id. This must be called - * before the first call to {@code parse()}. + * Creates a new {@link Digester} instance that relies on the given {@code SAXParser} + * and custom user define {@link Rules} implementation. * - * @param entityResolver a class that implement the {@code EntityResolver} interface. - * @return This loader instance, useful to chain methods. + * @param parser The user-defined {@code SAXParser} + * @param rules The custom user define {@link Rules} implementation + * @return a new {@link Digester} instance */ - public DigesterLoader setEntityResolver( final EntityResolver entityResolver ) + public Digester newDigester( final SAXParser parser, final Rules rules ) { - this.entityResolver = entityResolver; - return this; - } - + if ( parser == null ) + { + throw new DigesterLoadingException( "SAXParser must be not null" ); + } + + try + { + return this.newDigester( parser.getXMLReader(), rules ); + } + catch ( final SAXException e ) + { + throw new DigesterLoadingException( "An error occurred while creating the XML Reader", e ); + } + } + /** - * Sets the Object which will receive callbacks for every pop/push action on the default stack or named stacks. + * Creates a new {@link XMLReader} instance that relies on the given {@code XMLReader} + * and the default {@link Rules} implementation. * - * @param stackAction the Object which will receive callbacks for every pop/push action on the default stack - * or named stacks. - * @return This loader instance, useful to chain methods. + * WARNING Input {@link XMLReader} will be linked to built Digester instance so it is recommended + * to NOT share same {@link XMLReader} instance to produce the Digester. + * + * @param reader The user-defined {@code XMLReader} + * @return a new {@link Digester} instance */ - public DigesterLoader setStackAction( final StackAction stackAction ) + public Digester newDigester( final XMLReader reader ) { - this.stackAction = stackAction; - return this; + return this.newDigester( reader, new RulesBase() ); } /** - * Returns the executor service used to run asynchronous parse method. + * Creates a new {@link XMLReader} instance that relies on the given {@code XMLReader} + * and custom user define {@link Rules} implementation. * - * @return the executor service used to run asynchronous parse method - * @since 3.1 + * WARNING Input {@link XMLReader} and {@link Rules} will be linked to built Digester instance, + * so it is recommended to NOT share same {@link XMLReader} and {@link Rules} instance to produce the Digester. + * + * @param reader The user-defined {@code XMLReader} + * @param rules The custom user define {@link Rules} implementation + * @return a new {@link Digester} instance */ - public ExecutorService getExecutorService() + public Digester newDigester( final XMLReader reader, final Rules rules ) { - return executorService; + if ( reader == null ) + { + throw new DigesterLoadingException( "XMLReader must be not null" ); + } + if ( rules == null ) + { + throw new DigesterLoadingException( "Impossible to create a new Digester with null Rules" ); + } + + final Digester digester = new Digester( reader ); + // the ClassLoader adapter is no needed anymore + digester.setClassLoader( classLoader.getAdaptedClassLoader() ); + digester.setRules( rules ); + digester.setSubstitutor( substitutor ); + digester.registerAll( entityValidator ); + digester.setEntityResolver( entityResolver ); + digester.setStackAction( stackAction ); + digester.setNamespaceAware( isNamespaceAware() ); + digester.setExecutorService( executorService ); + digester.setErrorHandler( errorHandler ); + digester.setDocumentLocator( locator ); + + addRules( digester ); + + return digester; } /** - * Sets the executor service to run asynchronous parse method. + *

Convenience method that registers the string version of an entity URL + * instead of a URL version.

* - * @param executorService the executor service to run asynchronous parse method + * @param publicId Public identifier of the entity to be resolved + * @param entityURL The URL to use for reading this entity * @return This loader instance, useful to chain methods. - * @since 3.1 */ - public DigesterLoader setExecutorService( final ExecutorService executorService ) + public DigesterLoader register( final String publicId, final String entityURL ) { - this.executorService = executorService; + try + { + return register( publicId, new URL( entityURL ) ); + } + catch ( final MalformedURLException e ) + { + throw new IllegalArgumentException( "Malformed URL '" + entityURL + "' : " + e.getMessage() ); + } + } + + /** + *

Register the specified DTD URL for the specified public identifier. + * This must be called before the first call to {@code parse()}. + *

+ * {@code Digester} contains an internal {@code EntityResolver} + * implementation. This maps {@code PUBLICID}'s to URLs + * (from which the resource will be loaded). A common use case for this + * method is to register local URLs (possibly computed at runtime by a + * classloader) for DTDs. This allows the performance advantage of using + * a local version without having to ensure every {@code SYSTEM} + * URI on every processed xml document is local. This implementation provides + * only basic functionality. If more sophisticated features are required, + * using {@link #setEntityResolver(EntityResolver)} to set a custom resolver is recommended. + *

+ * Note: This method will have no effect when a custom + * {@code EntityResolver} has been set. (Setting a custom + * {@code EntityResolver} overrides the internal implementation.) + *

+ * @param publicId Public identifier of the DTD to be resolved + * @param entityURL The URL to use for reading this DTD + * @return This loader instance, useful to chain methods. + */ + public DigesterLoader register( final String publicId, final URL entityURL ) + { + entityValidator.put( publicId, entityURL ); return this; } /** - * Return the error handler for this Digester. + * Sets the class loader to be used for instantiating application objects when required. * - * @return the error handler for this Digester. - * @since 3.2 + * @param classLoader the class loader to be used for instantiating application objects when required. + * @return This loader instance, useful to chain methods. */ - public ErrorHandler getErrorHandler() + public DigesterLoader setClassLoader( final ClassLoader classLoader ) { - return ( this.errorHandler ); + if ( classLoader == null ) + { + throw new IllegalArgumentException( "Parameter 'classLoader' cannot be null" ); + } + + this.classLoader = createBinderClassLoader( classLoader ); + + rulesBinder.initialize( this.classLoader ); + for ( final RulesModule rulesModule : rulesModules ) + { + rulesModule.configure( rulesBinder ); + } + + return this; } /** - * Sets the error handler for this Digester. + * Sets the document locator associated with our parser. * - * @param errorHandler The new error handler + * @param locator the document locator associated with our parser. * @return This loader instance, useful to chain methods. * @since 3.2 */ - public DigesterLoader setErrorHandler( final ErrorHandler errorHandler ) + public DigesterLoader setDocumentLocator( final Locator locator ) { - this.errorHandler = errorHandler; + this.locator = locator; return this; } /** - * Gets the document locator associated with our parser. + * Sets the {@code EntityResolver} used by SAX when resolving public id and system id. This must be called + * before the first call to {@code parse()}. * - * @return the Locator supplied by the document parser - * @since 3.2 + * @param entityResolver a class that implement the {@code EntityResolver} interface. + * @return This loader instance, useful to chain methods. */ - public Locator getDocumentLocator() + public DigesterLoader setEntityResolver( final EntityResolver entityResolver ) { - return locator; + this.entityResolver = entityResolver; + return this; } /** - * Sets the document locator associated with our parser. + * Sets the error handler for this Digester. * - * @param locator the document locator associated with our parser. + * @param errorHandler The new error handler * @return This loader instance, useful to chain methods. * @since 3.2 */ - public DigesterLoader setDocumentLocator( final Locator locator ) + public DigesterLoader setErrorHandler( final ErrorHandler errorHandler ) { - this.locator = locator; + this.errorHandler = errorHandler; return this; } /** - * Creates a new {@link Digester} instance that relies on the default {@link Rules} implementation. + * Sets the executor service to run asynchronous parse method. * - * @return a new {@link Digester} instance + * @param executorService the executor service to run asynchronous parse method + * @return This loader instance, useful to chain methods. + * @since 3.1 */ - public Digester newDigester() + public DigesterLoader setExecutorService( final ExecutorService executorService ) { - return this.newDigester( new RulesBase() ); + this.executorService = executorService; + return this; } /** - * Creates a new {@link Digester} instance that relies on the custom user define {@link Rules} implementation + * Sets a flag indicating whether the requested feature is supported by the underlying implementation of + * {@code org.xml.sax.XMLReader}. * - * @param rules The custom user define {@link Rules} implementation - * @return a new {@link Digester} instance + * @see org.apache.commons.digester3.Digester#setFeature(String, boolean) + * @param feature Name of the feature to set the status for + * @param value The new value for this feature + * @return This loader instance, useful to chain methods. + * @throws ParserConfigurationException if a parser configuration error occurs + * @throws SAXNotRecognizedException if the property name is not recognized + * @throws SAXNotSupportedException if the property name is recognized but not supported + * @since 3.3 */ - public Digester newDigester( final Rules rules ) + public DigesterLoader setFeature( final String feature, final boolean value ) + throws SAXNotRecognizedException, SAXNotSupportedException, ParserConfigurationException { - try - { - return this.newDigester( this.factory.newSAXParser(), rules ); - } - catch ( final ParserConfigurationException e ) - { - throw new DigesterLoadingException( "SAX Parser misconfigured", e ); - } - catch ( final SAXException e ) - { - throw new DigesterLoadingException( "An error occurred while initializing the SAX Parser", e ); - } + factory.setFeature(feature, value); + return this; } /** - * Creates a new {@link Digester} instance that relies on the given {@code SAXParser} - * and the default {@link Rules} implementation. + * Sets the "namespace aware" flag for parsers we create. * - * @param parser the user-defined {@code SAXParser} - * @return a new {@link Digester} instance + * @param namespaceAware The new "namespace aware" flag + * @return This loader instance, useful to chain methods. */ - public Digester newDigester( final SAXParser parser ) + public DigesterLoader setNamespaceAware( final boolean namespaceAware ) { - return newDigester( parser, new RulesBase() ); + factory.setNamespaceAware( namespaceAware ); + return this; } /** - * Creates a new {@link Digester} instance that relies on the given {@code SAXParser} - * and custom user define {@link Rules} implementation. + * Sets the XML Schema to be used when parsing. * - * @param parser The user-defined {@code SAXParser} - * @param rules The custom user define {@link Rules} implementation - * @return a new {@link Digester} instance + * @param schema The {@link Schema} instance to use. + * @return This loader instance, useful to chain methods. */ - public Digester newDigester( final SAXParser parser, final Rules rules ) + public DigesterLoader setSchema( final Schema schema ) { - if ( parser == null ) - { - throw new DigesterLoadingException( "SAXParser must be not null" ); - } - - try - { - return this.newDigester( parser.getXMLReader(), rules ); - } - catch ( final SAXException e ) - { - throw new DigesterLoadingException( "An error occurred while creating the XML Reader", e ); - } + factory.setSchema( schema ); + return this; } /** - * Creates a new {@link XMLReader} instance that relies on the given {@code XMLReader} - * and the default {@link Rules} implementation. - * - * WARNING Input {@link XMLReader} will be linked to built Digester instance so it is recommended - * to NOT share same {@link XMLReader} instance to produce the Digester. + * Sets the Object which will receive callbacks for every pop/push action on the default stack or named stacks. * - * @param reader The user-defined {@code XMLReader} - * @return a new {@link Digester} instance + * @param stackAction the Object which will receive callbacks for every pop/push action on the default stack + * or named stacks. + * @return This loader instance, useful to chain methods. */ - public Digester newDigester( final XMLReader reader ) + public DigesterLoader setStackAction( final StackAction stackAction ) { - return this.newDigester( reader, new RulesBase() ); + this.stackAction = stackAction; + return this; } /** - * Creates a new {@link XMLReader} instance that relies on the given {@code XMLReader} - * and custom user define {@link Rules} implementation. + * Sets the {@code Substitutor} to be used to convert attributes and body text. * - * WARNING Input {@link XMLReader} and {@link Rules} will be linked to built Digester instance, - * so it is recommended to NOT share same {@link XMLReader} and {@link Rules} instance to produce the Digester. + * @param substitutor the Substitutor to be used to convert attributes and body text + * or null if not substitution of these values is to be performed. + * @return This loader instance, useful to chain methods. + */ + public DigesterLoader setSubstitutor( final Substitutor substitutor ) + { + this.substitutor = substitutor; + return this; + } + + /** + * Determine whether to use the Context ClassLoader (the one found by + * calling {@code Thread.currentThread().getContextClassLoader()}) + * to resolve/load classes that are defined in various rules. If not + * using Context ClassLoader, then the class-loading defaults to + * using the calling-class' ClassLoader. * - * @param reader The user-defined {@code XMLReader} - * @param rules The custom user define {@link Rules} implementation - * @return a new {@link Digester} instance + * @param useContextClassLoader determines whether to use Context ClassLoader. + * @return This loader instance, useful to chain methods. */ - public Digester newDigester( final XMLReader reader, final Rules rules ) + public DigesterLoader setUseContextClassLoader( final boolean useContextClassLoader ) { - if ( reader == null ) + if ( useContextClassLoader ) { - throw new DigesterLoadingException( "XMLReader must be not null" ); + setClassLoader( Thread.currentThread().getContextClassLoader() ); } - if ( rules == null ) + else { - throw new DigesterLoadingException( "Impossible to create a new Digester with null Rules" ); + setClassLoader( getClass().getClassLoader() ); } - - final Digester digester = new Digester( reader ); - // the ClassLoader adapter is no needed anymore - digester.setClassLoader( classLoader.getAdaptedClassLoader() ); - digester.setRules( rules ); - digester.setSubstitutor( substitutor ); - digester.registerAll( entityValidator ); - digester.setEntityResolver( entityResolver ); - digester.setStackAction( stackAction ); - digester.setNamespaceAware( isNamespaceAware() ); - digester.setExecutorService( executorService ); - digester.setErrorHandler( errorHandler ); - digester.setDocumentLocator( locator ); - - addRules( digester ); - - return digester; + return this; } /** - * Add rules to an already created Digester instance, analyzing the digester annotations in the target class. + * Sets the {@code DOCTYPE} validation parser flag and should not be used when using schemas. * - * @param digester the Digester instance reference. + * @param validating The new validating parser flag. + * @return This loader instance, useful to chain methods. + * @see javax.xml.parsers.SAXParserFactory#setValidating(boolean) */ - public void addRules( final Digester digester ) + public DigesterLoader setValidating( final boolean validating ) { - final RuleSet ruleSet = createRuleSet(); - ruleSet.addRuleInstances( digester ); + factory.setValidating( validating ); + return this; } /** - * Creates a new {@link RuleSet} instance based on the current configuration. + * Sets the XInclude-aware flag for parsers we create. This additionally + * requires namespace-awareness. * - * @return A new {@link RuleSet} instance based on the current configuration. + * @param xIncludeAware The new XInclude-aware flag + * @return This loader instance, useful to chain methods. + * @see #setNamespaceAware(boolean) */ - public RuleSet createRuleSet() + public DigesterLoader setXIncludeAware( final boolean xIncludeAware ) { - if ( rulesBinder.hasError() ) - { - final Formatter fmt = new Formatter().format( HEADING ); - int index = 1; - - for ( final ErrorMessage errorMessage : rulesBinder.getErrors() ) - { - fmt.format( "%s) %s%n", index++, errorMessage.getMessage() ); - - final Throwable cause = errorMessage.getCause(); - if ( cause != null ) - { - final StringWriter writer = new StringWriter(); - cause.printStackTrace( new PrintWriter( writer ) ); - fmt.format( "Caused by: %s", writer.getBuffer() ); - } - - fmt.format( "%n" ); - } - - if ( rulesBinder.errorsSize() == 1 ) - { - fmt.format( "1 error" ); - } - else - { - fmt.format( "%s errors", rulesBinder.errorsSize() ); - } - - throw new DigesterLoadingException( fmt.toString() ); - } - - return rulesBinder.getFromBinderRuleSet(); + factory.setXIncludeAware( xIncludeAware ); + return this; } } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/DigesterLoadingException.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/DigesterLoadingException.java index fffc267b7..7e5e37b5b 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/DigesterLoadingException.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/DigesterLoadingException.java @@ -34,24 +34,24 @@ public final class DigesterLoadingException private static final long serialVersionUID = 1L; /** - * Constructs a new exception with the specified detail message and cause. + * Constructs a new Digester exception with the specified detail message. * * @param message the detail message. - * @param cause the cause. */ - public DigesterLoadingException( final String message, final Throwable cause ) + public DigesterLoadingException( final String message ) { - super( message, cause ); + super( message ); } /** - * Constructs a new Digester exception with the specified detail message. + * Constructs a new exception with the specified detail message and cause. * * @param message the detail message. + * @param cause the cause. */ - public DigesterLoadingException( final String message ) + public DigesterLoadingException( final String message, final Throwable cause ) { - super( message ); + super( message, cause ); } /** diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/ErrorMessage.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/ErrorMessage.java index 7159572cb..209e27954 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/ErrorMessage.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/ErrorMessage.java @@ -69,23 +69,23 @@ public ErrorMessage( final String message, final Throwable cause ) } /** - * Gets the error message text. + * Returns the Throwable that caused this message, or {@code null} if this message was not caused by a Throwable. * - * @return The error message text + * @return The Throwable that caused this message, or {@code null} if this message was not caused by a Throwable */ - public String getMessage() + public Throwable getCause() { - return message; + return cause; } /** - * Returns the Throwable that caused this message, or {@code null} if this message was not caused by a Throwable. + * Gets the error message text. * - * @return The Throwable that caused this message, or {@code null} if this message was not caused by a Throwable + * @return The error message text */ - public Throwable getCause() + public String getMessage() { - return cause; + return message; } /** diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/FactoryCreateBuilder.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/FactoryCreateBuilder.java index 87bc79acb..863cdcc2b 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/FactoryCreateBuilder.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/FactoryCreateBuilder.java @@ -49,36 +49,34 @@ public final class FactoryCreateBuilder } /** - * Construct a factory create rule that will use the specified class name to create an {@link ObjectCreationFactory} - * which will then be used to create an object and push it on the stack. - * - * @param className Java class name of the object creation factory class - * @return this builder instance + * {@inheritDoc} */ - @SuppressWarnings( "unchecked" ) // if class not assignable, will be notified via exception - public FactoryCreateBuilder ofType( final String className ) + @Override + protected FactoryCreateRule createRule() { - if ( className == null ) + if ( type == null && attributeName == null && creationFactory == null ) { - reportError( "factoryCreate().ofType( String )", "NULL Java type not allowed" ); + reportError( "factoryCreate()", + "at least one between 'className', 'attributeName' or 'creationFactory' has to be specified" ); } - try - { - final Class type = this.classLoader.loadClass( className ); - if ( !ObjectCreationFactory.class.isAssignableFrom( type ) ) - { - reportError( "factoryCreate().ofType( String )", "NULL Java type not allowed" ); - return this; - } - - this.type = (Class>) type; - } - catch ( final ClassNotFoundException e ) + if ( type != null || attributeName != null ) { - reportError( "factoryCreate().ofType( String )", String.format( "class '%s' cannot be load", className ) ); + return new FactoryCreateRule( type, attributeName, ignoreCreateExceptions ); } + return new FactoryCreateRule( creationFactory, ignoreCreateExceptions ); + } + + /** + * Exceptions thrown by the object creation factory will be ignored or not. + * + * @param ignoreCreateExceptions if true, exceptions thrown by the object creation factory will be ignored + * @return this builder instance + */ + public FactoryCreateBuilder ignoreCreateExceptions( final boolean ignoreCreateExceptions ) + { + this.ignoreCreateExceptions = ignoreCreateExceptions; return this; } @@ -103,15 +101,36 @@ public FactoryCreateBuilder ofType( final Class the type of created object by the given factory - * @param creationFactory called on to create the object + * @param className Java class name of the object creation factory class * @return this builder instance */ - public FactoryCreateBuilder usingFactory( /* @Nullable */final ObjectCreationFactory creationFactory ) + @SuppressWarnings( "unchecked" ) // if class not assignable, will be notified via exception + public FactoryCreateBuilder ofType( final String className ) { - this.creationFactory = creationFactory; + if ( className == null ) + { + reportError( "factoryCreate().ofType( String )", "NULL Java type not allowed" ); + } + + try + { + final Class type = this.classLoader.loadClass( className ); + if ( !ObjectCreationFactory.class.isAssignableFrom( type ) ) + { + reportError( "factoryCreate().ofType( String )", "NULL Java type not allowed" ); + return this; + } + + this.type = (Class>) type; + } + catch ( final ClassNotFoundException e ) + { + reportError( "factoryCreate().ofType( String )", String.format( "class '%s' cannot be load", className ) ); + } + return this; } @@ -128,35 +147,16 @@ public FactoryCreateBuilder overriddenByAttribute( /* @Nullable */final String a } /** - * Exceptions thrown by the object creation factory will be ignored or not. + * Construct a factory create rule using the given, already instantiated, {@link ObjectCreationFactory}. * - * @param ignoreCreateExceptions if true, exceptions thrown by the object creation factory will be ignored + * @param the type of created object by the given factory + * @param creationFactory called on to create the object * @return this builder instance */ - public FactoryCreateBuilder ignoreCreateExceptions( final boolean ignoreCreateExceptions ) + public FactoryCreateBuilder usingFactory( /* @Nullable */final ObjectCreationFactory creationFactory ) { - this.ignoreCreateExceptions = ignoreCreateExceptions; + this.creationFactory = creationFactory; return this; } - /** - * {@inheritDoc} - */ - @Override - protected FactoryCreateRule createRule() - { - if ( type == null && attributeName == null && creationFactory == null ) - { - reportError( "factoryCreate()", - "at least one between 'className', 'attributeName' or 'creationFactory' has to be specified" ); - } - - if ( type != null || attributeName != null ) - { - return new FactoryCreateRule( type, attributeName, ignoreCreateExceptions ); - } - - return new FactoryCreateRule( creationFactory, ignoreCreateExceptions ); - } - } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/FromBinderRuleSet.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/FromBinderRuleSet.java index e03131905..99de38748 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/FromBinderRuleSet.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/FromBinderRuleSet.java @@ -39,110 +39,6 @@ final class FromBinderRuleSet implements RuleSet { - /** - * The data structure where storing the providers binding. - */ - private final Collection> providers = - new LinkedList>(); - - /** - * Index for quick-retrieve provider. - */ - private final Map>> providersIndex = - new HashMap>>(); - - /** - * Register the given rule builder and returns it. - * - * @param The Digester rule type - * @param The Digester rule builder type - * @param ruleBuilder The input rule builder instance. - */ - public > void registerProvider( final RB ruleBuilder ) - { - this.providers.add( ruleBuilder ); - - final Key key = new Key( ruleBuilder.getPattern(), ruleBuilder.getNamespaceURI() ); - - // O(1) - Collection> indexedProviders = this.providersIndex.get( key ); - if ( indexedProviders == null ) - { - indexedProviders = new ArrayList>(); - this.providersIndex.put( key, indexedProviders ); // O(1) - } - indexedProviders.add( ruleBuilder ); - } - - /** - * Returns the first instance of {@link RuleProvider} assignable to the input type. - * - * This method is useful for rules that requires be unique in the pattern, - * like {@link org.apache.commons.digester3.SetPropertiesRule} - * and {@link org.apache.commons.digester3.SetNestedPropertiesRule}. - * - * @param The Digester rule type - * @param The Digester rule builder type - * @param keyPattern the rule pattern - * @param namespaceURI the namespace URI (can be null) - * @param type the rule builder type the client is looking for - * @return the rule builder of input type, if any - */ - public > RB getProvider( final String keyPattern, - /* @Nullable */final String namespaceURI, final Class type ) - { - final Key key = new Key( keyPattern, namespaceURI ); - - // O(1) - final Collection> indexedProviders = this.providersIndex.get( key ); - - if ( indexedProviders == null || indexedProviders.isEmpty() ) - { - return null; - } - - // FIXME O(n) not so good - for ( final AbstractBackToLinkedRuleBuilder ruleProvider : indexedProviders ) - { - if ( type.isInstance( ruleProvider ) ) - { - return type.cast( ruleProvider ); - } - } - - return null; - } - - /** - * Clean the provider index. - */ - public void clear() - { - providers.clear(); - providersIndex.clear(); - } - - /** - * {@inheritDoc} - */ - @Override - public void addRuleInstances( final Digester digester ) - { - for ( final AbstractBackToLinkedRuleBuilder provider : providers ) - { - digester.addRule( provider.getPattern(), provider.get() ); - } - } - - /** - * {@inheritDoc} - */ - @Override - public String getNamespaceURI() - { - return null; - } - /** * Used to associate pattern/namespaceURI */ @@ -159,29 +55,6 @@ public Key( final String pattern, final String namespaceURI ) this.namespaceURI = namespaceURI; } - public String getPattern() - { - return pattern; - } - - public String getNamespaceURI() - { - return namespaceURI; - } - - /** - * {@inheritDoc} - */ - @Override - public int hashCode() - { - final int prime = 31; - int result = 1; - result = prime * result + ( ( namespaceURI == null ) ? 0 : namespaceURI.hashCode() ); - result = prime * result + ( ( pattern == null ) ? 0 : pattern.hashCode() ); - return result; - } - /** * {@inheritDoc} */ @@ -231,6 +104,29 @@ else if ( !pattern.equals( other.getPattern() ) ) return true; } + public String getNamespaceURI() + { + return namespaceURI; + } + + public String getPattern() + { + return pattern; + } + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = prime * result + ( ( namespaceURI == null ) ? 0 : namespaceURI.hashCode() ); + result = prime * result + ( ( pattern == null ) ? 0 : pattern.hashCode() ); + return result; + } + /** * {@inheritDoc} */ @@ -242,4 +138,108 @@ public String toString() } + /** + * The data structure where storing the providers binding. + */ + private final Collection> providers = + new LinkedList>(); + + /** + * Index for quick-retrieve provider. + */ + private final Map>> providersIndex = + new HashMap>>(); + + /** + * {@inheritDoc} + */ + @Override + public void addRuleInstances( final Digester digester ) + { + for ( final AbstractBackToLinkedRuleBuilder provider : providers ) + { + digester.addRule( provider.getPattern(), provider.get() ); + } + } + + /** + * Clean the provider index. + */ + public void clear() + { + providers.clear(); + providersIndex.clear(); + } + + /** + * {@inheritDoc} + */ + @Override + public String getNamespaceURI() + { + return null; + } + + /** + * Returns the first instance of {@link RuleProvider} assignable to the input type. + * + * This method is useful for rules that requires be unique in the pattern, + * like {@link org.apache.commons.digester3.SetPropertiesRule} + * and {@link org.apache.commons.digester3.SetNestedPropertiesRule}. + * + * @param The Digester rule type + * @param The Digester rule builder type + * @param keyPattern the rule pattern + * @param namespaceURI the namespace URI (can be null) + * @param type the rule builder type the client is looking for + * @return the rule builder of input type, if any + */ + public > RB getProvider( final String keyPattern, + /* @Nullable */final String namespaceURI, final Class type ) + { + final Key key = new Key( keyPattern, namespaceURI ); + + // O(1) + final Collection> indexedProviders = this.providersIndex.get( key ); + + if ( indexedProviders == null || indexedProviders.isEmpty() ) + { + return null; + } + + // FIXME O(n) not so good + for ( final AbstractBackToLinkedRuleBuilder ruleProvider : indexedProviders ) + { + if ( type.isInstance( ruleProvider ) ) + { + return type.cast( ruleProvider ); + } + } + + return null; + } + + /** + * Register the given rule builder and returns it. + * + * @param The Digester rule type + * @param The Digester rule builder type + * @param ruleBuilder The input rule builder instance. + */ + public > void registerProvider( final RB ruleBuilder ) + { + this.providers.add( ruleBuilder ); + + final Key key = new Key( ruleBuilder.getPattern(), ruleBuilder.getNamespaceURI() ); + + // O(1) + Collection> indexedProviders = this.providersIndex.get( key ); + if ( indexedProviders == null ) + { + indexedProviders = new ArrayList>(); + this.providersIndex.put( key, indexedProviders ); // O(1) + } + indexedProviders.add( ruleBuilder ); + } + } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/LinkedRuleBuilder.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/LinkedRuleBuilder.java index 7ef3ee92e..d75341810 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/LinkedRuleBuilder.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/LinkedRuleBuilder.java @@ -49,14 +49,57 @@ public final class LinkedRuleBuilder } /** - * Construct rule that automatically sets a property from the body text, taking the property - * name the same as the current element. + * Add a provider in the data structure where storing the providers binding. * - * @return a new {@link BeanPropertySetterBuilder} instance. + * @param The rule will be created by the given provider + * @param provider The provider has to be stored in the data structure + * @return The provider itself has to be stored in the data structure */ - public BeanPropertySetterBuilder setBeanProperty() + private > RB addProvider( final RB provider ) { - return addProvider( new BeanPropertySetterBuilder( keyPattern, namespaceURI, mainBinder, this ) ); + fromBinderRuleSet.registerProvider( provider ); + return provider; + } + + /** + * Add a custom user rule in the specified pattern. + * + * WARNING keep away from this method as much as you can, since there's the risk + * same input {@link Rule} instance is plugged to more than one Digester; + * use {@link #addRuleCreatedBy(RuleProvider)} instead!!! + * + * @see #addRuleCreatedBy(RuleProvider) + * @see Rule#setDigester(org.apache.commons.digester3.Digester) + * @param The rule type + * @param rule The custom user rule + * @return a new {@link ByRuleBuilder} instance. + */ + public ByRuleBuilder addRule( final R rule ) + { + if ( rule == null ) + { + mainBinder.addError( "{ forPattern( \"%s\" ).addRule( R ) } NULL rule not valid", keyPattern ); + } + + return this.addProvider( new ByRuleBuilder( keyPattern, namespaceURI, mainBinder, this, rule ) ); + } + + /** + * Add a custom user rule in the specified pattern built by the given provider. + * + * @param The rule type + * @param provider The rule provider + * @return a new {@link ByRuleProviderBuilder} instance. + */ + public ByRuleProviderBuilder addRuleCreatedBy( final RuleProvider provider ) + { + if ( provider == null ) + { + mainBinder.addError( "{ forPattern( \"%s\" ).addRuleCreatedBy() } null rule provider not valid", + keyPattern ); + } + + return addProvider( new ByRuleProviderBuilder( keyPattern, namespaceURI, mainBinder, this, provider ) ); } /** @@ -99,16 +142,13 @@ public PathCallParamBuilder callParamPath() } /** - * Uses an {@link org.apache.commons.digester3.ObjectCreationFactory} to create a new object which it - * pushes onto the object stack. - * - * When the element is complete, the object will be popped. + * A rule implementation that creates a DOM Node containing the XML at the element that matched the rule. * - * @return a new {@link FactoryCreateBuilder} instance. + * @return a new {@link NodeCreateRuleProvider} instance. */ - public FactoryCreateBuilder factoryCreate() + public NodeCreateRuleProvider createNode() { - return addProvider( new FactoryCreateBuilder( keyPattern, namespaceURI, mainBinder, this, classLoader ) ); + return addProvider( new NodeCreateRuleProvider( keyPattern, namespaceURI, mainBinder, this ) ); } /** @@ -121,6 +161,46 @@ public ObjectCreateBuilder createObject() return addProvider( new ObjectCreateBuilder( keyPattern, namespaceURI, mainBinder, this, classLoader ) ); } + /** + * A Digester rule which allows the user to declare a plugin. + * + * NOTE: when using this rule, make sure {@link org.apache.commons.digester3.Digester} instances + * will be created using {@link org.apache.commons.digester3.plugins.PluginRules} rules strategy. + * + * @return a new {@link PluginDeclarationRuleBuilder} instance. + */ + public PluginCreateRuleBuilder createPlugin() + { + return addProvider( new PluginCreateRuleBuilder( keyPattern, namespaceURI, mainBinder, this ) ); + } + + /** + * A Digester rule which allows the user to pre-declare a class which is to + * be referenced later at a plugin point by a PluginCreateRule. + * + * NOTE: when using this rule, make sure {@link org.apache.commons.digester3.Digester} instances + * will be created using {@link org.apache.commons.digester3.plugins.PluginRules} rules strategy. + * + * @return a new {@link PluginDeclarationRuleBuilder} instance. + */ + public PluginDeclarationRuleBuilder declarePlugin() + { + return addProvider( new PluginDeclarationRuleBuilder( keyPattern, namespaceURI, mainBinder, this ) ); + } + + /** + * Uses an {@link org.apache.commons.digester3.ObjectCreationFactory} to create a new object which it + * pushes onto the object stack. + * + * When the element is complete, the object will be popped. + * + * @return a new {@link FactoryCreateBuilder} instance. + */ + public FactoryCreateBuilder factoryCreate() + { + return addProvider( new FactoryCreateBuilder( keyPattern, namespaceURI, mainBinder, this, classLoader ) ); + } + /** * Saves a parameter for use by a surrounding {@link #callMethod(String)}. * @@ -133,6 +213,17 @@ public ObjectParamBuilder objectParam( /* @Nullable */final T paramObj ) return addProvider( new ObjectParamBuilder( keyPattern, namespaceURI, mainBinder, this, paramObj ) ); } + /** + * Construct rule that automatically sets a property from the body text, taking the property + * name the same as the current element. + * + * @return a new {@link BeanPropertySetterBuilder} instance. + */ + public BeanPropertySetterBuilder setBeanProperty() + { + return addProvider( new BeanPropertySetterBuilder( keyPattern, namespaceURI, mainBinder, this ) ); + } + /** * Sets properties on the object at the top of the stack, * based on child elements with names matching properties on that object. @@ -244,84 +335,6 @@ public SetTopBuilder setTop( final String methodName ) return addProvider( new SetTopBuilder( keyPattern, namespaceURI, mainBinder, this, methodName, classLoader ) ); } - /** - * A Digester rule which allows the user to pre-declare a class which is to - * be referenced later at a plugin point by a PluginCreateRule. - * - * NOTE: when using this rule, make sure {@link org.apache.commons.digester3.Digester} instances - * will be created using {@link org.apache.commons.digester3.plugins.PluginRules} rules strategy. - * - * @return a new {@link PluginDeclarationRuleBuilder} instance. - */ - public PluginDeclarationRuleBuilder declarePlugin() - { - return addProvider( new PluginDeclarationRuleBuilder( keyPattern, namespaceURI, mainBinder, this ) ); - } - - /** - * A Digester rule which allows the user to declare a plugin. - * - * NOTE: when using this rule, make sure {@link org.apache.commons.digester3.Digester} instances - * will be created using {@link org.apache.commons.digester3.plugins.PluginRules} rules strategy. - * - * @return a new {@link PluginDeclarationRuleBuilder} instance. - */ - public PluginCreateRuleBuilder createPlugin() - { - return addProvider( new PluginCreateRuleBuilder( keyPattern, namespaceURI, mainBinder, this ) ); - } - - /** - * A rule implementation that creates a DOM Node containing the XML at the element that matched the rule. - * - * @return a new {@link NodeCreateRuleProvider} instance. - */ - public NodeCreateRuleProvider createNode() - { - return addProvider( new NodeCreateRuleProvider( keyPattern, namespaceURI, mainBinder, this ) ); - } - - /** - * Add a custom user rule in the specified pattern. - * - * WARNING keep away from this method as much as you can, since there's the risk - * same input {@link Rule} instance is plugged to more than one Digester; - * use {@link #addRuleCreatedBy(RuleProvider)} instead!!! - * - * @see #addRuleCreatedBy(RuleProvider) - * @see Rule#setDigester(org.apache.commons.digester3.Digester) - * @param The rule type - * @param rule The custom user rule - * @return a new {@link ByRuleBuilder} instance. - */ - public ByRuleBuilder addRule( final R rule ) - { - if ( rule == null ) - { - mainBinder.addError( "{ forPattern( \"%s\" ).addRule( R ) } NULL rule not valid", keyPattern ); - } - - return this.addProvider( new ByRuleBuilder( keyPattern, namespaceURI, mainBinder, this, rule ) ); - } - - /** - * Add a custom user rule in the specified pattern built by the given provider. - * - * @param The rule type - * @param provider The rule provider - * @return a new {@link ByRuleProviderBuilder} instance. - */ - public ByRuleProviderBuilder addRuleCreatedBy( final RuleProvider provider ) - { - if ( provider == null ) - { - mainBinder.addError( "{ forPattern( \"%s\" ).addRuleCreatedBy() } null rule provider not valid", - keyPattern ); - } - - return addProvider( new ByRuleProviderBuilder( keyPattern, namespaceURI, mainBinder, this, provider ) ); - } - /** * Sets the namespace URI for the current rule pattern. * @@ -343,17 +356,4 @@ public LinkedRuleBuilder withNamespaceURI( /* @Nullable */final String namespace return this; } - /** - * Add a provider in the data structure where storing the providers binding. - * - * @param The rule will be created by the given provider - * @param provider The provider has to be stored in the data structure - * @return The provider itself has to be stored in the data structure - */ - private > RB addProvider( final RB provider ) - { - fromBinderRuleSet.registerProvider( provider ); - return provider; - } - } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/NestedPropertiesBuilder.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/NestedPropertiesBuilder.java index f01242abf..ab12445f3 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/NestedPropertiesBuilder.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/NestedPropertiesBuilder.java @@ -45,35 +45,6 @@ public final class NestedPropertiesBuilder super( keyPattern, namespaceURI, mainBinder, mainBuilder ); } - /** - * Allows ignore a matching element. - * - * @param elementName The child xml element to be ignored - * @return this builder instance - */ - public NestedPropertiesBuilder ignoreElement( final String elementName ) - { - if ( elementName == null ) - { - reportError( "setNestedProperties().ignoreElement( String )", "empty 'elementName' not allowed" ); - } - return addAlias( elementName ).forProperty( null ); - } - - /** - * Allows element2property mapping to be overridden. - * - * @param elementName The child xml element to match - * @param propertyName The java bean property to be assigned the value - * @return this builder instance - * @deprecated - */ - @Deprecated - public NestedPropertiesBuilder addAlias( final String elementName, final String propertyName ) - { - return addAlias( elementName ).forProperty( propertyName ); - } - /** * Allows element2property mapping to be overridden. * @@ -91,18 +62,17 @@ public AddAliasBuilder addAlias( final String elementNa } /** - * When set to true, any text within child elements will have leading - * and trailing whitespace removed before assignment to the target - * object. + * Allows element2property mapping to be overridden. * - * @param trimData Flag to set any text within child elements will have leading - * and trailing whitespace removed + * @param elementName The child xml element to match + * @param propertyName The java bean property to be assigned the value * @return this builder instance + * @deprecated */ - public NestedPropertiesBuilder trimData( final boolean trimData ) + @Deprecated + public NestedPropertiesBuilder addAlias( final String elementName, final String propertyName ) { - this.trimData = trimData; - return this; + return addAlias( elementName ).forProperty( propertyName ); } /** @@ -131,4 +101,34 @@ protected SetNestedPropertiesRule createRule() return rule; } + /** + * Allows ignore a matching element. + * + * @param elementName The child xml element to be ignored + * @return this builder instance + */ + public NestedPropertiesBuilder ignoreElement( final String elementName ) + { + if ( elementName == null ) + { + reportError( "setNestedProperties().ignoreElement( String )", "empty 'elementName' not allowed" ); + } + return addAlias( elementName ).forProperty( null ); + } + + /** + * When set to true, any text within child elements will have leading + * and trailing whitespace removed before assignment to the target + * object. + * + * @param trimData Flag to set any text within child elements will have leading + * and trailing whitespace removed + * @return this builder instance + */ + public NestedPropertiesBuilder trimData( final boolean trimData ) + { + this.trimData = trimData; + return this; + } + } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/NodeCreateRuleProvider.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/NodeCreateRuleProvider.java index ae7a1fa15..ce2c485dc 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/NodeCreateRuleProvider.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/NodeCreateRuleProvider.java @@ -46,71 +46,6 @@ public final class NodeCreateRuleProvider extends AbstractBackToLinkedRuleBuilder { - private NodeType nodeType = NodeType.ELEMENT; - - private DocumentBuilder documentBuilder; - - NodeCreateRuleProvider( final String keyPattern, final String namespaceURI, final RulesBinder mainBinder, - final LinkedRuleBuilder mainBuilder ) - { - super( keyPattern, namespaceURI, mainBinder, mainBuilder ); - } - - /** - * {@link NodeCreateRule} instance will be created either a DOM {@link org.w3c.dom.Element Element} - * or a DOM {@link org.w3c.dom.DocumentFragment DocumentFragment}, depending on the value of the - * {@code nodeType} parameter. - * - * @param nodeType the type of node to create, which can be either - * {@link org.w3c.dom.Node#ELEMENT_NODE Node.ELEMENT_NODE} or - * {@link org.w3c.dom.Node#DOCUMENT_FRAGMENT_NODE Node.DOCUMENT_FRAGMENT_NODE} - * @return this builder instance - */ - public NodeCreateRuleProvider ofType( final NodeType nodeType ) - { - if ( nodeType == null ) - { - reportError( "createNode().ofType( NodeType )", "Null NodeType not allowed" ); - } - - this.nodeType = nodeType; - return this; - } - - /** - * {@link NodeCreateRule} instance will be created a DOM {@link org.w3c.dom.Element Element}, but - * lets users specify the JAXP {@code DocumentBuilder} that should be used when constructing the node tree. - * - * @param documentBuilder the JAXP {@code DocumentBuilder} to use - * @return this builder instance - */ - public NodeCreateRuleProvider usingDocumentBuilder( final DocumentBuilder documentBuilder ) - { - this.documentBuilder = documentBuilder; - return this; - } - - /** - * {@inheritDoc} - */ - @Override - protected NodeCreateRule createRule() - { - if ( documentBuilder == null ) - { - try - { - return new NodeCreateRule( nodeType.getDocumentType() ); - } - catch ( final ParserConfigurationException e ) - { - throw new IllegalStateException( e ); - } - } - - return new NodeCreateRule( nodeType.getDocumentType(), documentBuilder ); - } - /** * Enumeration that wraps admitted {@link org.w3c.dom.Node} node constants. */ @@ -180,4 +115,69 @@ private int getDocumentType() } + private NodeType nodeType = NodeType.ELEMENT; + + private DocumentBuilder documentBuilder; + + NodeCreateRuleProvider( final String keyPattern, final String namespaceURI, final RulesBinder mainBinder, + final LinkedRuleBuilder mainBuilder ) + { + super( keyPattern, namespaceURI, mainBinder, mainBuilder ); + } + + /** + * {@inheritDoc} + */ + @Override + protected NodeCreateRule createRule() + { + if ( documentBuilder == null ) + { + try + { + return new NodeCreateRule( nodeType.getDocumentType() ); + } + catch ( final ParserConfigurationException e ) + { + throw new IllegalStateException( e ); + } + } + + return new NodeCreateRule( nodeType.getDocumentType(), documentBuilder ); + } + + /** + * {@link NodeCreateRule} instance will be created either a DOM {@link org.w3c.dom.Element Element} + * or a DOM {@link org.w3c.dom.DocumentFragment DocumentFragment}, depending on the value of the + * {@code nodeType} parameter. + * + * @param nodeType the type of node to create, which can be either + * {@link org.w3c.dom.Node#ELEMENT_NODE Node.ELEMENT_NODE} or + * {@link org.w3c.dom.Node#DOCUMENT_FRAGMENT_NODE Node.DOCUMENT_FRAGMENT_NODE} + * @return this builder instance + */ + public NodeCreateRuleProvider ofType( final NodeType nodeType ) + { + if ( nodeType == null ) + { + reportError( "createNode().ofType( NodeType )", "Null NodeType not allowed" ); + } + + this.nodeType = nodeType; + return this; + } + + /** + * {@link NodeCreateRule} instance will be created a DOM {@link org.w3c.dom.Element Element}, but + * lets users specify the JAXP {@code DocumentBuilder} that should be used when constructing the node tree. + * + * @param documentBuilder the JAXP {@code DocumentBuilder} to use + * @return this builder instance + */ + public NodeCreateRuleProvider usingDocumentBuilder( final DocumentBuilder documentBuilder ) + { + this.documentBuilder = documentBuilder; + return this; + } + } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/ObjectCreateBuilder.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/ObjectCreateBuilder.java index 7756c58b7..a26ab73de 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/ObjectCreateBuilder.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/ObjectCreateBuilder.java @@ -62,28 +62,23 @@ public final class ObjectCreateBuilder } /** - * Construct an object with the specified class name. - * - * @param className Java class name of the object to be created - * @return this builder instance + * {@inheritDoc} */ - public ObjectCreateBuilder ofType( final String className ) + @Override + protected ObjectCreateRule createRule() { - if ( className == null ) - { - reportError( "createObject().ofType( String )", "NULL Java type not allowed" ); - return this; - } + final ObjectCreateRule objectCreateRule = new ObjectCreateRule( attributeName, type ); - try + if ( constructorArgumentsType != null ) { - return ofType( this.classLoader.loadClass( className ) ); + objectCreateRule.setConstructorArgumentTypes( constructorArgumentsType ); } - catch ( final ClassNotFoundException e ) + if ( defaultConstructorArguments != null ) { - reportError( "createObject().ofType( String )", String.format( "class '%s' cannot be load", className ) ); - return this; + objectCreateRule.setDefaultConstructorArguments( defaultConstructorArguments ); } + + return objectCreateRule; } /** @@ -106,6 +101,31 @@ public ObjectCreateBuilder ofType( final Class type ) return this; } + /** + * Construct an object with the specified class name. + * + * @param className Java class name of the object to be created + * @return this builder instance + */ + public ObjectCreateBuilder ofType( final String className ) + { + if ( className == null ) + { + reportError( "createObject().ofType( String )", "NULL Java type not allowed" ); + return this; + } + + try + { + return ofType( this.classLoader.loadClass( className ) ); + } + catch ( final ClassNotFoundException e ) + { + reportError( "createObject().ofType( String )", String.format( "class '%s' cannot be load", className ) ); + return this; + } + } + /** * Allows specify the attribute containing an override class name if it is present. * @@ -118,6 +138,27 @@ public ObjectCreateBuilder ofTypeSpecifiedByAttribute( /* @Nullable */final Stri return this; } + /** + * Allows users to specify constructor argument types. + * + * @param constructorArgumentTypes the constructor argument types + * @return this builder instance + * @since 3.2 + */ + public ObjectCreateBuilder usingConstructor( final Class... constructorArgumentTypes ) + { + if ( constructorArgumentTypes == null ) + { + reportError( "createObject().usingConstructor( Class[] )", + "NULL constructorArgumentTypes not allowed" ); + return this; + } + + this.constructorArgumentsType = constructorArgumentTypes; + + return this; + } + /** * Allows users to specify constructor argument type names. * @@ -151,27 +192,6 @@ public ObjectCreateBuilder usingConstructor( final String...paramTypeNames ) return usingConstructor( paramTypes ); } - /** - * Allows users to specify constructor argument types. - * - * @param constructorArgumentTypes the constructor argument types - * @return this builder instance - * @since 3.2 - */ - public ObjectCreateBuilder usingConstructor( final Class... constructorArgumentTypes ) - { - if ( constructorArgumentTypes == null ) - { - reportError( "createObject().usingConstructor( Class[] )", - "NULL constructorArgumentTypes not allowed" ); - return this; - } - - this.constructorArgumentsType = constructorArgumentTypes; - - return this; - } - /** * Allows users to specify default constructor arguments. * @@ -194,24 +214,4 @@ public ObjectCreateBuilder usingDefaultConstructorArguments( final Object... def } - /** - * {@inheritDoc} - */ - @Override - protected ObjectCreateRule createRule() - { - final ObjectCreateRule objectCreateRule = new ObjectCreateRule( attributeName, type ); - - if ( constructorArgumentsType != null ) - { - objectCreateRule.setConstructorArgumentTypes( constructorArgumentsType ); - } - if ( defaultConstructorArguments != null ) - { - objectCreateRule.setDefaultConstructorArguments( defaultConstructorArguments ); - } - - return objectCreateRule; - } - } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/ObjectParamBuilder.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/ObjectParamBuilder.java index 321c961c2..9383bb438 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/ObjectParamBuilder.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/ObjectParamBuilder.java @@ -45,20 +45,12 @@ public final class ObjectParamBuilder } /** - * The zero-relative index of the parameter we are saving. - * - * @param paramIndex The zero-relative index of the parameter we are saving - * @return this builder instance + * {@inheritDoc} */ - public ObjectParamBuilder ofIndex( final int paramIndex ) + @Override + protected ObjectParamRule createRule() { - if ( paramIndex < 0 ) - { - this.reportError( "objectParam( %s ).ofIndex( int )", "negative index argument not allowed" ); - } - - this.paramIndex = paramIndex; - return this; + return new ObjectParamRule( paramIndex, attributeName, paramObj ); } /** @@ -74,12 +66,20 @@ public ObjectParamBuilder matchingAttribute( /* @Nullable */final String attr } /** - * {@inheritDoc} + * The zero-relative index of the parameter we are saving. + * + * @param paramIndex The zero-relative index of the parameter we are saving + * @return this builder instance */ - @Override - protected ObjectParamRule createRule() + public ObjectParamBuilder ofIndex( final int paramIndex ) { - return new ObjectParamRule( paramIndex, attributeName, paramObj ); + if ( paramIndex < 0 ) + { + this.reportError( "objectParam( %s ).ofIndex( int )", "negative index argument not allowed" ); + } + + this.paramIndex = paramIndex; + return this; } } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/PathCallParamBuilder.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/PathCallParamBuilder.java index 16848697b..98bd3b507 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/PathCallParamBuilder.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/PathCallParamBuilder.java @@ -38,6 +38,15 @@ public final class PathCallParamBuilder super( keyPattern, namespaceURI, mainBinder, mainBuilder ); } + /** + * {@inheritDoc} + */ + @Override + protected PathCallParamRule createRule() + { + return new PathCallParamRule( paramIndex ); + } + /** * Sets the zero-relative parameter number. * @@ -55,13 +64,4 @@ public PathCallParamBuilder ofIndex( final int paramIndex ) return this; } - /** - * {@inheritDoc} - */ - @Override - protected PathCallParamRule createRule() - { - return new PathCallParamRule( paramIndex ); - } - } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/PluginCreateRuleBuilder.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/PluginCreateRuleBuilder.java index 74b2f82d8..ece337cab 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/PluginCreateRuleBuilder.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/PluginCreateRuleBuilder.java @@ -52,49 +52,77 @@ public final class PluginCreateRuleBuilder } /** - * Sets the class which any specified plugin must be descended from. + * Private internal method to set values to a {@link Map} instance and return the current builder. * - * @param Any Java type - * @param type the class which any specified plugin must be descended from + * @param map The target {@link Map} + * @param namespaceUri The attribute NameSpace + * @param attrName The attribute name * @return this builder instance */ - public PluginCreateRuleBuilder ofType( final Class type ) + private PluginCreateRuleBuilder addToMap( final Map map, final String namespaceUri, final String attrName ) { - if ( type == null ) - { - reportError( "createPlugin().ofType( Class )", "NULL Java type not allowed" ); - return this; - } - - this.baseClass = type; - + map.put( namespaceUri, attrName ); return this; } /** - * Sets the class which will be used if the user doesn't specify any plugin-class or plugin-id. - * - * @param Any Java type - * @param type the class which will be used if the user doesn't specify any plugin-class or plugin-id. - * @return this builder instance + * {@inheritDoc} */ - public PluginCreateRuleBuilder usingDefaultPluginClass( /* @Nullable */final Class type ) + @Override + protected PluginCreateRule createRule() { - this.dfltPluginClass = type; - return this; + if ( baseClass == null ) + { + reportError( "createPlugin()", "'baseClass' has to be specified" ); + } + + PluginCreateRule rule; + if ( dfltPluginClass != null ) + { + if ( dfltPluginRuleLoader != null ) + { + rule = new PluginCreateRule( baseClass, dfltPluginClass, dfltPluginRuleLoader ); + } + else + { + rule = new PluginCreateRule( baseClass, dfltPluginClass ); + } + } + else + { + rule = new PluginCreateRule( baseClass ); + } + + for ( final Entry entry : pluginClassAttributes.entrySet() ) + { + rule.setPluginClassAttribute( entry.getKey(), entry.getValue() ); + } + + for ( final Entry entry : pluginIdAttributes.entrySet() ) + { + rule.setPluginIdAttribute( entry.getKey(), entry.getValue() ); + } + + return rule; } /** - * Sets RuleLoader instance which knows how to load the custom rules associated with the default plugin. + * Sets the class which any specified plugin must be descended from. * - * @param Any {@link RuleLoader} extension. - * @param ruleLoader the RuleLoader instance which knows how to load the custom rules associated with - * the default plugin. + * @param Any Java type + * @param type the class which any specified plugin must be descended from * @return this builder instance */ - public PluginCreateRuleBuilder usingRuleLoader( /* @Nullable */final RL ruleLoader ) + public PluginCreateRuleBuilder ofType( final Class type ) { - this.dfltPluginRuleLoader = ruleLoader; + if ( type == null ) + { + reportError( "createPlugin().ofType( Class )", "NULL Java type not allowed" ); + return this; + } + + this.baseClass = type; + return this; } @@ -175,58 +203,30 @@ public PluginCreateRuleBuilder setPluginIdAttribute( /* @Nullable */final String } /** - * Private internal method to set values to a {@link Map} instance and return the current builder. + * Sets the class which will be used if the user doesn't specify any plugin-class or plugin-id. * - * @param map The target {@link Map} - * @param namespaceUri The attribute NameSpace - * @param attrName The attribute name + * @param Any Java type + * @param type the class which will be used if the user doesn't specify any plugin-class or plugin-id. * @return this builder instance */ - private PluginCreateRuleBuilder addToMap( final Map map, final String namespaceUri, final String attrName ) + public PluginCreateRuleBuilder usingDefaultPluginClass( /* @Nullable */final Class type ) { - map.put( namespaceUri, attrName ); + this.dfltPluginClass = type; return this; } /** - * {@inheritDoc} + * Sets RuleLoader instance which knows how to load the custom rules associated with the default plugin. + * + * @param Any {@link RuleLoader} extension. + * @param ruleLoader the RuleLoader instance which knows how to load the custom rules associated with + * the default plugin. + * @return this builder instance */ - @Override - protected PluginCreateRule createRule() + public PluginCreateRuleBuilder usingRuleLoader( /* @Nullable */final RL ruleLoader ) { - if ( baseClass == null ) - { - reportError( "createPlugin()", "'baseClass' has to be specified" ); - } - - PluginCreateRule rule; - if ( dfltPluginClass != null ) - { - if ( dfltPluginRuleLoader != null ) - { - rule = new PluginCreateRule( baseClass, dfltPluginClass, dfltPluginRuleLoader ); - } - else - { - rule = new PluginCreateRule( baseClass, dfltPluginClass ); - } - } - else - { - rule = new PluginCreateRule( baseClass ); - } - - for ( final Entry entry : pluginClassAttributes.entrySet() ) - { - rule.setPluginClassAttribute( entry.getKey(), entry.getValue() ); - } - - for ( final Entry entry : pluginIdAttributes.entrySet() ) - { - rule.setPluginIdAttribute( entry.getKey(), entry.getValue() ); - } - - return rule; + this.dfltPluginRuleLoader = ruleLoader; + return this; } } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/RulesBinder.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/RulesBinder.java index ae4d74faa..b292bf42a 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/RulesBinder.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/RulesBinder.java @@ -27,13 +27,6 @@ public interface RulesBinder { - /** - * Returns the context {@code ClassLoader}. - * - * @return The context {@code ClassLoader} - */ - ClassLoader getContextClassLoader(); - /** * Records an error message which will be presented to the user at a later time. Unlike throwing an exception, this * enable us to continue configuring the Digester and discover more errors. Uses @@ -53,13 +46,6 @@ public interface RulesBinder */ void addError( Throwable t ); - /** - * Allows sub-modules inclusion while binding rules. - * - * @param rulesModule the sub-module has to be included. - */ - void install( RulesModule rulesModule ); - /** * Allows to associate the given pattern to one or more Digester rules. * @@ -68,4 +54,18 @@ public interface RulesBinder */ LinkedRuleBuilder forPattern( String pattern ); + /** + * Returns the context {@code ClassLoader}. + * + * @return The context {@code ClassLoader} + */ + ClassLoader getContextClassLoader(); + + /** + * Allows sub-modules inclusion while binding rules. + * + * @param rulesModule the sub-module has to be included. + */ + void install( RulesModule rulesModule ); + } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/SetPropertiesBuilder.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/SetPropertiesBuilder.java index e9da2de25..c4db94aca 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/SetPropertiesBuilder.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/SetPropertiesBuilder.java @@ -41,6 +41,22 @@ public final class SetPropertiesBuilder super( keyPattern, namespaceURI, mainBinder, mainBuilder ); } + /** + * Add an additional attribute name to property name mapping. + * + * @param attributeName The attribute to match + * @return the property alias builder + * @since 3.2 + */ + public AddAliasBuilder addAlias( final String attributeName ) + { + if ( attributeName == null ) + { + reportError( "setProperties().addAlias( String )", "empty 'attributeName' not allowed" ); + } + return new AddAliasBuilder( this, aliases, attributeName ); + } + /** * Add an additional attribute name to property name mapping. * @@ -56,19 +72,14 @@ public SetPropertiesBuilder addAlias( final String attributeName, final String p } /** - * Add an additional attribute name to property name mapping. - * - * @param attributeName The attribute to match - * @return the property alias builder - * @since 3.2 + * {@inheritDoc} */ - public AddAliasBuilder addAlias( final String attributeName ) + @Override + protected SetPropertiesRule createRule() { - if ( attributeName == null ) - { - reportError( "setProperties().addAlias( String )", "empty 'attributeName' not allowed" ); - } - return new AddAliasBuilder( this, aliases, attributeName ); + final SetPropertiesRule rule = new SetPropertiesRule( aliases ); + rule.setIgnoreMissingProperty( ignoreMissingProperty ); + return rule; } /** @@ -102,15 +113,4 @@ public SetPropertiesBuilder ignoreMissingProperty( final boolean ignoreMissingPr return this; } - /** - * {@inheritDoc} - */ - @Override - protected SetPropertiesRule createRule() - { - final SetPropertiesRule rule = new SetPropertiesRule( aliases ); - rule.setIgnoreMissingProperty( ignoreMissingProperty ); - return rule; - } - } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/SetPropertyBuilder.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/SetPropertyBuilder.java index f7cab06bd..1d26b8b1b 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/SetPropertyBuilder.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/binder/SetPropertyBuilder.java @@ -43,6 +43,15 @@ public final class SetPropertyBuilder this.attributePropertyName = attributePropertyName; } + /** + * {@inheritDoc} + */ + @Override + protected SetPropertyRule createRule() + { + return new SetPropertyRule( attributePropertyName, valueAttributeName ); + } + /** * Sets the name of the attribute that will contain the value to which the property should be set. * @@ -61,13 +70,4 @@ public SetPropertyBuilder extractingValueFromAttribute( final String valueAttrib return this; } - /** - * {@inheritDoc} - */ - @Override - protected SetPropertyRule createRule() - { - return new SetPropertyRule( attributePropertyName, valueAttributeName ); - } - } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/plugins/Declaration.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/plugins/Declaration.java index b004df2b8..2601a2d47 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/plugins/Declaration.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/plugins/Declaration.java @@ -55,20 +55,6 @@ public class Declaration // ---------------------- constructors ---------------------------------- - /** - * Constructor. - * - * @param pluginClassName The name of the class of the object to be instantiated (will be load in the init method) - */ - public Declaration( final String pluginClassName ) - { - // We can't load the pluginClass at this time, because we don't - // have a digester instance yet to load it through. So just - // save the name away, and we'll load the Class object in the - // init method. - this.pluginClassName = pluginClassName; - } - /** * Constructor. * @@ -94,19 +80,51 @@ public Declaration( final Class pluginClass, final RuleLoader ruleLoader ) this.ruleLoader = ruleLoader; } + /** + * Constructor. + * + * @param pluginClassName The name of the class of the object to be instantiated (will be load in the init method) + */ + public Declaration( final String pluginClassName ) + { + // We can't load the pluginClass at this time, because we don't + // have a digester instance yet to load it through. So just + // save the name away, and we'll load the Class object in the + // init method. + this.pluginClassName = pluginClassName; + } + // ---------------------- properties ----------------------------------- /** - * The id that the user associated with a particular plugin declaration in the input xml. This id is later used in - * the input xml to refer back to the original declaration. + * Attempt to load custom rules for the target class at the specified pattern. *

- * For plugins declared "in-line", the id is null. + * On return, any custom rules associated with the plugin class have been loaded into the Rules object currently + * associated with the specified digester object. * - * @param id The id that the user associated with a particular plugin declaration in the input xml + * @param digester The Digester instance where plugin has to be plugged + * @param pattern The pattern the custom rules have to be bound + * @throws PluginException if any error occurs */ - public void setId( final String id ) + public void configure( final Digester digester, final String pattern ) + throws PluginException { - this.id = id; + final Log log = digester.getLogger(); + final boolean debug = log.isDebugEnabled(); + if ( debug ) + { + log.debug( "configure being called!" ); + } + + if ( !initialized ) + { + throw new PluginAssertionFailure( "Not initialized." ); + } + + if ( ruleLoader != null ) + { + ruleLoader.addRules( digester, pattern ); + } } /** @@ -119,23 +137,6 @@ public String getId() return id; } - /** - * Copy all (key,value) pairs in the param into the properties member of this object. - *

- * The declaration properties cannot be explicit member variables, because the set of useful properties a user can - * provide on a declaration depends on what RuleFinder classes are available - and extra RuleFinders can be added by - * the user. So here we keep a map of the settings, and let the RuleFinder objects look for whatever properties they - * consider significant. - *

- * The "id" and "class" properties are treated differently. - * - * @param p The properties have to be copied into the properties member of this object - */ - public void setProperties( final Properties p ) - { - properties.putAll( p ); - } - /** * Return plugin class associated with this declaration. * @@ -146,8 +147,6 @@ public Class getPluginClass() return pluginClass; } - // ---------------------- methods ----------------------------------- - /** * Must be called exactly once, and must be called before any call to the configure method. * @@ -213,35 +212,36 @@ public void init( final Digester digester, final PluginManager pm ) initialized = true; } + // ---------------------- methods ----------------------------------- + /** - * Attempt to load custom rules for the target class at the specified pattern. + * The id that the user associated with a particular plugin declaration in the input xml. This id is later used in + * the input xml to refer back to the original declaration. *

- * On return, any custom rules associated with the plugin class have been loaded into the Rules object currently - * associated with the specified digester object. + * For plugins declared "in-line", the id is null. * - * @param digester The Digester instance where plugin has to be plugged - * @param pattern The pattern the custom rules have to be bound - * @throws PluginException if any error occurs + * @param id The id that the user associated with a particular plugin declaration in the input xml */ - public void configure( final Digester digester, final String pattern ) - throws PluginException + public void setId( final String id ) { - final Log log = digester.getLogger(); - final boolean debug = log.isDebugEnabled(); - if ( debug ) - { - log.debug( "configure being called!" ); - } - - if ( !initialized ) - { - throw new PluginAssertionFailure( "Not initialized." ); - } + this.id = id; + } - if ( ruleLoader != null ) - { - ruleLoader.addRules( digester, pattern ); - } + /** + * Copy all (key,value) pairs in the param into the properties member of this object. + *

+ * The declaration properties cannot be explicit member variables, because the set of useful properties a user can + * provide on a declaration depends on what RuleFinder classes are available - and extra RuleFinders can be added by + * the user. So here we keep a map of the settings, and let the RuleFinder objects look for whatever properties they + * consider significant. + *

+ * The "id" and "class" properties are treated differently. + * + * @param p The properties have to be copied into the properties member of this object + */ + public void setProperties( final Properties p ) + { + properties.putAll( p ); } } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/plugins/PluginAssertionFailure.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/plugins/PluginAssertionFailure.java index 7d84237c7..86a6afd97 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/plugins/PluginAssertionFailure.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/plugins/PluginAssertionFailure.java @@ -49,16 +49,6 @@ public class PluginAssertionFailure private static final long serialVersionUID = 1L; - /** - * Constructs a new exception with the specified cause. - * - * @param cause underlying exception that caused this to be thrown - */ - public PluginAssertionFailure( final Throwable cause ) - { - super( cause ); - } - /** * Constructs a new exception with the specified detail message. * @@ -80,4 +70,14 @@ public PluginAssertionFailure( final String msg, final Throwable cause ) super( msg, cause ); } + /** + * Constructs a new exception with the specified cause. + * + * @param cause underlying exception that caused this to be thrown + */ + public PluginAssertionFailure( final Throwable cause ) + { + super( cause ); + } + } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/plugins/PluginConfigurationException.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/plugins/PluginConfigurationException.java index 02cb1d5b9..8fc330679 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/plugins/PluginConfigurationException.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/plugins/PluginConfigurationException.java @@ -32,16 +32,6 @@ public class PluginConfigurationException private static final long serialVersionUID = 1L; - /** - * Constructs a new exception with the specified cause. - * - * @param cause underlying exception that caused this to be thrown - */ - public PluginConfigurationException( final Throwable cause ) - { - super( cause ); - } - /** * Constructs a new exception with the specified detail message. * @@ -63,4 +53,14 @@ public PluginConfigurationException( final String msg, final Throwable cause ) super( msg, cause ); } + /** + * Constructs a new exception with the specified cause. + * + * @param cause underlying exception that caused this to be thrown + */ + public PluginConfigurationException( final Throwable cause ) + { + super( cause ); + } + } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/plugins/PluginContext.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/plugins/PluginContext.java index 123e497b0..affafe61b 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/plugins/PluginContext.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/plugins/PluginContext.java @@ -78,6 +78,56 @@ public class PluginContext // ------------------- methods --------------------------------------- + /** + * Gets the namespace for the xml attribute which indicates to a PluginCreateRule which class is to be plugged in. + *

+ * The return value is never null. + * + * @return the namespace for the xml attribute which indicates which class is to be plugged in. + */ + public String getPluginClassAttr() + { + return pluginClassAttr; + } + + /** + * Gets the namespace for the xml attribute which indicates to a PluginCreateRule which class is to be plugged in. + *

+ * May be null (in fact, normally will be). + * + * @return the namespace for the xml attribute which indicates which class is to be plugged in. + */ + public String getPluginClassAttrNs() + { + return pluginClassAttrNs; + } + + /** + * Gets the namespace for the xml attribute which indicates to a PluginCreateRule which previous plugin declaration + * should be used. + *

+ * The return value is never null. + * + * @return the namespace for the xml attribute which indicates which previous plugin declaration should be used. + */ + public String getPluginIdAttr() + { + return pluginIdAttr; + } + + /** + * Gets the namespace for the xml attribute which indicates to a PluginCreateRule which previous plugin declaration + * should be used. + *

+ * May be null (in fact, normally will be). + * + * @return the namespace for the xml attribute which indicates which previous plugin declaration should be used. + */ + public String getPluginIdAttrNs() + { + return pluginIdAttrNs; + } + /** * Return the list of RuleFinder objects. Under normal circumstances this method creates a default list of these * objects when first called (ie "on-demand" or "lazy initialization"). However if setRuleFinders has been called @@ -108,21 +158,6 @@ public List getRuleFinders() return ruleFinders; } - /** - * Sets the list of RuleFinder objects. This may be useful if working in a non-english language, allowing the - * application developer to replace the standard list with a list of objects which look for xml attributes in the - * local language. - *

- * If the intent is just to add an additional rule-finding algorithm, then it may be better to call #getRuleFinders, - * and insert a new object into the start of the list. - * - * @param ruleFinders the list of RuleFinder objects - */ - public void setRuleFinders( final List ruleFinders ) - { - this.ruleFinders = ruleFinders; - } - /** * Sets the xml attribute which the input xml uses to indicate to a PluginCreateRule which class should be * instantiated. @@ -205,53 +240,18 @@ public void setPluginIdAttribute( final String namespaceUri, final String attrNa } /** - * Gets the namespace for the xml attribute which indicates to a PluginCreateRule which class is to be plugged in. - *

- * May be null (in fact, normally will be). - * - * @return the namespace for the xml attribute which indicates which class is to be plugged in. - */ - public String getPluginClassAttrNs() - { - return pluginClassAttrNs; - } - - /** - * Gets the namespace for the xml attribute which indicates to a PluginCreateRule which class is to be plugged in. - *

- * The return value is never null. - * - * @return the namespace for the xml attribute which indicates which class is to be plugged in. - */ - public String getPluginClassAttr() - { - return pluginClassAttr; - } - - /** - * Gets the namespace for the xml attribute which indicates to a PluginCreateRule which previous plugin declaration - * should be used. - *

- * May be null (in fact, normally will be). - * - * @return the namespace for the xml attribute which indicates which previous plugin declaration should be used. - */ - public String getPluginIdAttrNs() - { - return pluginIdAttrNs; - } - - /** - * Gets the namespace for the xml attribute which indicates to a PluginCreateRule which previous plugin declaration - * should be used. + * Sets the list of RuleFinder objects. This may be useful if working in a non-english language, allowing the + * application developer to replace the standard list with a list of objects which look for xml attributes in the + * local language. *

- * The return value is never null. + * If the intent is just to add an additional rule-finding algorithm, then it may be better to call #getRuleFinders, + * and insert a new object into the start of the list. * - * @return the namespace for the xml attribute which indicates which previous plugin declaration should be used. + * @param ruleFinders the list of RuleFinder objects */ - public String getPluginIdAttr() + public void setRuleFinders( final List ruleFinders ) { - return pluginIdAttr; + this.ruleFinders = ruleFinders; } } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/plugins/PluginCreateRule.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/plugins/PluginCreateRule.java index 5845049fb..a6a929f9d 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/plugins/PluginCreateRule.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/plugins/PluginCreateRule.java @@ -118,183 +118,6 @@ public PluginCreateRule( final Class baseClass, final Class dfltPluginClas // ------------------- properties --------------------------------------- - /** - * Sets the xml attribute which the input xml uses to indicate to a PluginCreateRule which class should be - * instantiated. - *

- * See {@link PluginRules#setPluginClassAttribute} for more info. - * - * @param namespaceUri is the namespace uri that the specified attribute is in. If the attribute is in no namespace, - * then this should be null. Note that if a namespace is used, the attrName value should not - * contain any kind of namespace-prefix. Note also that if you are using a non-namespace-aware parser, - * this parameter must be null. - * @param attrName is the attribute whose value contains the name of the class to be instantiated. - */ - public void setPluginClassAttribute( final String namespaceUri, final String attrName ) - { - pluginClassAttrNs = namespaceUri; - pluginClassAttr = attrName; - } - - /** - * Sets the xml attribute which the input xml uses to indicate to a PluginCreateRule which plugin declaration is - * being referenced. - *

- * See {@link PluginRules#setPluginIdAttribute} for more info. - * - * @param namespaceUri is the namespace uri that the specified attribute is in. If the attribute is in no namespace, - * then this should be null. Note that if a namespace is used, the attrName value should not - * contain any kind of namespace-prefix. Note also that if you are using a non-namespace-aware parser, - * this parameter must be null. - * @param attrName is the attribute whose value contains the id of the plugin declaration to be used when - * instantiating an object. - */ - public void setPluginIdAttribute( final String namespaceUri, final String attrName ) - { - pluginIdAttrNs = namespaceUri; - pluginIdAttr = attrName; - } - - // ------------------- methods -------------------------------------------- - - /** - * {@inheritDoc} - */ - @Override - public void postRegisterInit( final String matchPattern ) - { - final Log log = LogUtils.getLogger( getDigester() ); - final boolean debug = log.isDebugEnabled(); - if ( debug ) - { - log.debug( "PluginCreateRule.postRegisterInit" + ": rule registered for pattern [" + matchPattern + "]" ); - } - - if ( getDigester() == null ) - { - // We require setDigester to be called before this method. - // Note that this means that PluginCreateRule cannot be added - // to a Rules object which has not yet been added to a - // Digester object. - initException = - new PluginConfigurationException( "Invalid invocation of postRegisterInit" + ": digester not set." ); - throw initException; - } - - if ( pattern != null ) - { - // We have been called twice, ie a single instance has been - // associated with multiple patterns. - // - // Generally, Digester Rule instances can be associated with - // multiple patterns. However for plugins, this creates some - // complications. Some day this may be supported; however for - // now we just reject this situation. - initException = - new PluginConfigurationException( "A single PluginCreateRule instance has been mapped to" - + " multiple patterns; this is not supported." ); - throw initException; - } - - if ( matchPattern.indexOf( '*' ) != -1 ) - { - // having wildcards in patterns is extremely difficult to - // deal with. For now, we refuse to allow this. - // - // TODO: check for any chars not valid in xml element name - // rather than just *. - // - // Reasons include: - // (a) handling recursive plugins, and - // (b) determining whether one pattern is "below" another, - // as done by PluginRules. Without wildcards, "below" - // just means startsWith, which is easy to check. - initException = - new PluginConfigurationException( "A PluginCreateRule instance has been mapped to" + " pattern [" - + matchPattern + "]." + " This pattern includes a wildcard character." - + " This is not supported by the plugin architecture." ); - throw initException; - } - - if ( baseClass == null ) - { - baseClass = Object.class; - } - - final PluginRules rules = (PluginRules) getDigester().getRules(); - final PluginManager pm = rules.getPluginManager(); - - // check default class is valid - if ( defaultPlugin != null ) - { - if ( !baseClass.isAssignableFrom( defaultPlugin.getPluginClass() ) ) - { - initException = - new PluginConfigurationException( "Default class [" + defaultPlugin.getPluginClass().getName() - + "] does not inherit from [" + baseClass.getName() + "]." ); - throw initException; - } - - try - { - defaultPlugin.init( getDigester(), pm ); - - } - catch ( final PluginException pwe ) - { - - throw new PluginConfigurationException( pwe.getMessage(), pwe.getCause() ); - } - } - - // remember the pattern for later - pattern = matchPattern; - - if ( pluginClassAttr == null ) - { - // the user hasn't set explicit xml attr names on this rule, - // so fetch the default values - pluginClassAttrNs = rules.getPluginClassAttrNs(); - pluginClassAttr = rules.getPluginClassAttr(); - - if ( debug ) - { - log.debug( "init: pluginClassAttr set to per-digester values [" + "ns=" + pluginClassAttrNs + ", name=" - + pluginClassAttr + "]" ); - } - } - else - { - if ( debug ) - { - log.debug( "init: pluginClassAttr set to rule-specific values [" + "ns=" + pluginClassAttrNs - + ", name=" + pluginClassAttr + "]" ); - } - } - - if ( pluginIdAttr == null ) - { - // the user hasn't set explicit xml attr names on this rule, - // so fetch the default values - pluginIdAttrNs = rules.getPluginIdAttrNs(); - pluginIdAttr = rules.getPluginIdAttr(); - - if ( debug ) - { - log.debug( "init: pluginIdAttr set to per-digester values [" + "ns=" + pluginIdAttrNs + ", name=" - + pluginIdAttr + "]" ); - } - } - else - { - if ( debug ) - { - log.debug( "init: pluginIdAttr set to rule-specific values [" + "ns=" + pluginIdAttrNs + ", name=" - + pluginIdAttr + "]" ); - } - } - } - /** * Invoked when the Digester matches this rule against an xml element. *

@@ -467,6 +290,8 @@ public void body( final String namespace, final String name, final String text ) fireBodyMethods( rules, namespace, name, text ); } + // ------------------- methods -------------------------------------------- + /** * {@inheritDoc} */ @@ -489,21 +314,6 @@ public void end( final String namespace, final String name ) getDigester().pop(); } - /** - * Return the pattern that this Rule is associated with. - *

- * In general, Rule instances can be associated with multiple patterns. A PluginCreateRule, however, will - * only function correctly when associated with a single pattern. It is possible to fix this, but I can't be - * bothered just now because this feature is unlikely to be used. - *

- * - * @return The pattern value - */ - public String getPattern() - { - return pattern; - } - /** * Duplicate the processing that the Digester does when firing the begin methods of rules. It would be really nice * if the Digester class provided a way for this functionality to just be invoked directly. @@ -630,4 +440,194 @@ public void fireEndMethods( final List rules, final String namespaceURI, f } } + /** + * Return the pattern that this Rule is associated with. + *

+ * In general, Rule instances can be associated with multiple patterns. A PluginCreateRule, however, will + * only function correctly when associated with a single pattern. It is possible to fix this, but I can't be + * bothered just now because this feature is unlikely to be used. + *

+ * + * @return The pattern value + */ + public String getPattern() + { + return pattern; + } + + /** + * {@inheritDoc} + */ + @Override + public void postRegisterInit( final String matchPattern ) + { + final Log log = LogUtils.getLogger( getDigester() ); + final boolean debug = log.isDebugEnabled(); + if ( debug ) + { + log.debug( "PluginCreateRule.postRegisterInit" + ": rule registered for pattern [" + matchPattern + "]" ); + } + + if ( getDigester() == null ) + { + // We require setDigester to be called before this method. + // Note that this means that PluginCreateRule cannot be added + // to a Rules object which has not yet been added to a + // Digester object. + initException = + new PluginConfigurationException( "Invalid invocation of postRegisterInit" + ": digester not set." ); + throw initException; + } + + if ( pattern != null ) + { + // We have been called twice, ie a single instance has been + // associated with multiple patterns. + // + // Generally, Digester Rule instances can be associated with + // multiple patterns. However for plugins, this creates some + // complications. Some day this may be supported; however for + // now we just reject this situation. + initException = + new PluginConfigurationException( "A single PluginCreateRule instance has been mapped to" + + " multiple patterns; this is not supported." ); + throw initException; + } + + if ( matchPattern.indexOf( '*' ) != -1 ) + { + // having wildcards in patterns is extremely difficult to + // deal with. For now, we refuse to allow this. + // + // TODO: check for any chars not valid in xml element name + // rather than just *. + // + // Reasons include: + // (a) handling recursive plugins, and + // (b) determining whether one pattern is "below" another, + // as done by PluginRules. Without wildcards, "below" + // just means startsWith, which is easy to check. + initException = + new PluginConfigurationException( "A PluginCreateRule instance has been mapped to" + " pattern [" + + matchPattern + "]." + " This pattern includes a wildcard character." + + " This is not supported by the plugin architecture." ); + throw initException; + } + + if ( baseClass == null ) + { + baseClass = Object.class; + } + + final PluginRules rules = (PluginRules) getDigester().getRules(); + final PluginManager pm = rules.getPluginManager(); + + // check default class is valid + if ( defaultPlugin != null ) + { + if ( !baseClass.isAssignableFrom( defaultPlugin.getPluginClass() ) ) + { + initException = + new PluginConfigurationException( "Default class [" + defaultPlugin.getPluginClass().getName() + + "] does not inherit from [" + baseClass.getName() + "]." ); + throw initException; + } + + try + { + defaultPlugin.init( getDigester(), pm ); + + } + catch ( final PluginException pwe ) + { + + throw new PluginConfigurationException( pwe.getMessage(), pwe.getCause() ); + } + } + + // remember the pattern for later + pattern = matchPattern; + + if ( pluginClassAttr == null ) + { + // the user hasn't set explicit xml attr names on this rule, + // so fetch the default values + pluginClassAttrNs = rules.getPluginClassAttrNs(); + pluginClassAttr = rules.getPluginClassAttr(); + + if ( debug ) + { + log.debug( "init: pluginClassAttr set to per-digester values [" + "ns=" + pluginClassAttrNs + ", name=" + + pluginClassAttr + "]" ); + } + } + else + { + if ( debug ) + { + log.debug( "init: pluginClassAttr set to rule-specific values [" + "ns=" + pluginClassAttrNs + + ", name=" + pluginClassAttr + "]" ); + } + } + + if ( pluginIdAttr == null ) + { + // the user hasn't set explicit xml attr names on this rule, + // so fetch the default values + pluginIdAttrNs = rules.getPluginIdAttrNs(); + pluginIdAttr = rules.getPluginIdAttr(); + + if ( debug ) + { + log.debug( "init: pluginIdAttr set to per-digester values [" + "ns=" + pluginIdAttrNs + ", name=" + + pluginIdAttr + "]" ); + } + } + else + { + if ( debug ) + { + log.debug( "init: pluginIdAttr set to rule-specific values [" + "ns=" + pluginIdAttrNs + ", name=" + + pluginIdAttr + "]" ); + } + } + } + + /** + * Sets the xml attribute which the input xml uses to indicate to a PluginCreateRule which class should be + * instantiated. + *

+ * See {@link PluginRules#setPluginClassAttribute} for more info. + * + * @param namespaceUri is the namespace uri that the specified attribute is in. If the attribute is in no namespace, + * then this should be null. Note that if a namespace is used, the attrName value should not + * contain any kind of namespace-prefix. Note also that if you are using a non-namespace-aware parser, + * this parameter must be null. + * @param attrName is the attribute whose value contains the name of the class to be instantiated. + */ + public void setPluginClassAttribute( final String namespaceUri, final String attrName ) + { + pluginClassAttrNs = namespaceUri; + pluginClassAttr = attrName; + } + + /** + * Sets the xml attribute which the input xml uses to indicate to a PluginCreateRule which plugin declaration is + * being referenced. + *

+ * See {@link PluginRules#setPluginIdAttribute} for more info. + * + * @param namespaceUri is the namespace uri that the specified attribute is in. If the attribute is in no namespace, + * then this should be null. Note that if a namespace is used, the attrName value should not + * contain any kind of namespace-prefix. Note also that if you are using a non-namespace-aware parser, + * this parameter must be null. + * @param attrName is the attribute whose value contains the id of the plugin declaration to be used when + * instantiating an object. + */ + public void setPluginIdAttribute( final String namespaceUri, final String attrName ) + { + pluginIdAttrNs = namespaceUri; + pluginIdAttr = attrName; + } + } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/plugins/PluginDeclarationRule.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/plugins/PluginDeclarationRule.java index e9d2d2b38..8e894d7c2 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/plugins/PluginDeclarationRule.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/plugins/PluginDeclarationRule.java @@ -40,13 +40,52 @@ public class PluginDeclarationRule // ------------------- constructors --------------------------------------- - /** constructor */ - public PluginDeclarationRule() + /** + * Helper method to declare a plugin inside the given Digester. + * + * @param digester The Digester instance to declare plugin + * @param props the properties where extracting plugin attributes + * @throws PluginException if any error occurs while declaring the plugin + */ + public static void declarePlugin( final Digester digester, final Properties props ) + throws PluginException { + final String id = props.getProperty( "id" ); + final String pluginClassName = props.getProperty( "class" ); + + if ( id == null ) + { + throw new PluginInvalidInputException( "mandatory attribute id not present on plugin declaration" ); + } + + if ( pluginClassName == null ) + { + throw new PluginInvalidInputException( "mandatory attribute class not present on plugin declaration" ); + } + + final Declaration newDecl = new Declaration( pluginClassName ); + newDecl.setId( id ); + newDecl.setProperties( props ); + + final PluginRules rc = (PluginRules) digester.getRules(); + final PluginManager pm = rc.getPluginManager(); + + newDecl.init( digester, pm ); + pm.addDeclaration( newDecl ); + + // Note that it is perfectly safe to redeclare a plugin, because + // the declaration doesn't add any rules to digester; all it does + // is create a RuleLoader instance whch is *capable* of adding the + // rules to the digester. } // ------------------- methods -------------------------------------------- + /** constructor */ + public PluginDeclarationRule() + { + } + /** * Invoked upon reading a tag defining a plugin declaration. The tag must have the following mandatory attributes: *

    @@ -87,43 +126,4 @@ public void begin( final String namespace, final String name, final Attributes a } } - /** - * Helper method to declare a plugin inside the given Digester. - * - * @param digester The Digester instance to declare plugin - * @param props the properties where extracting plugin attributes - * @throws PluginException if any error occurs while declaring the plugin - */ - public static void declarePlugin( final Digester digester, final Properties props ) - throws PluginException - { - final String id = props.getProperty( "id" ); - final String pluginClassName = props.getProperty( "class" ); - - if ( id == null ) - { - throw new PluginInvalidInputException( "mandatory attribute id not present on plugin declaration" ); - } - - if ( pluginClassName == null ) - { - throw new PluginInvalidInputException( "mandatory attribute class not present on plugin declaration" ); - } - - final Declaration newDecl = new Declaration( pluginClassName ); - newDecl.setId( id ); - newDecl.setProperties( props ); - - final PluginRules rc = (PluginRules) digester.getRules(); - final PluginManager pm = rc.getPluginManager(); - - newDecl.init( digester, pm ); - pm.addDeclaration( newDecl ); - - // Note that it is perfectly safe to redeclare a plugin, because - // the declaration doesn't add any rules to digester; all it does - // is create a RuleLoader instance whch is *capable* of adding the - // rules to the digester. - } - } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/plugins/PluginException.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/plugins/PluginException.java index 6f539839d..80179e1f0 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/plugins/PluginException.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/plugins/PluginException.java @@ -32,15 +32,6 @@ public class PluginException private Throwable cause; - /** - * @param cause underlying exception that caused this to be thrown - */ - public PluginException( final Throwable cause ) - { - this( cause.getMessage() ); - this.cause = cause; - } - /** * @param msg describes the reason this exception is being thrown. */ @@ -59,6 +50,15 @@ public PluginException( final String msg, final Throwable cause ) this.cause = cause; } + /** + * @param cause underlying exception that caused this to be thrown + */ + public PluginException( final Throwable cause ) + { + this( cause.getMessage() ); + this.cause = cause; + } + /** * @return the underlying exception that caused this to be thrown */ diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/plugins/PluginInvalidInputException.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/plugins/PluginInvalidInputException.java index 5fec4a58a..902d179d7 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/plugins/PluginInvalidInputException.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/plugins/PluginInvalidInputException.java @@ -30,16 +30,6 @@ public class PluginInvalidInputException private static final long serialVersionUID = 1L; - /** - * Constructs a new exception with the specified cause. - * - * @param cause underlying exception that caused this to be thrown - */ - public PluginInvalidInputException( final Throwable cause ) - { - super( cause ); - } - /** * Constructs a new exception with the specified detail message. * @@ -61,4 +51,14 @@ public PluginInvalidInputException( final String msg, final Throwable cause ) super( msg, cause ); } + /** + * Constructs a new exception with the specified cause. + * + * @param cause underlying exception that caused this to be thrown + */ + public PluginInvalidInputException( final Throwable cause ) + { + super( cause ); + } + } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/plugins/PluginManager.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/plugins/PluginManager.java index bf7b91d8e..4572d8408 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/plugins/PluginManager.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/plugins/PluginManager.java @@ -109,42 +109,6 @@ public void addDeclaration( final Declaration decl ) } } - /** - * Return the declaration object with the specified class. If no such plugin is known, null is returned. - * - * @param className The {@link Declaration} class name - * @return The Declaration instance obtained by the input class name - */ - public Declaration getDeclarationByClass( final String className ) - { - Declaration decl = declarationsByClass.get( className ); - - if ( ( decl == null ) && ( parent != null ) ) - { - decl = parent.getDeclarationByClass( className ); - } - - return decl; - } - - /** - * Return the declaration object with the specified id. If no such plugin is known, null is returned. - * - * @param id Description of the Parameter - * @return The declaration value - */ - public Declaration getDeclarationById( final String id ) - { - Declaration decl = declarationsById.get( id ); - - if ( ( decl == null ) && ( parent != null ) ) - { - decl = parent.getDeclarationById( id ); - } - - return decl; - } - /** * Given a plugin class and some associated properties, scan the list of known RuleFinder instances until one * detects a source of custom rules for this plugin (aka a RuleLoader). @@ -196,4 +160,40 @@ public RuleLoader findLoader( final Digester digester, final String id, final Cl return ruleLoader; } + /** + * Return the declaration object with the specified class. If no such plugin is known, null is returned. + * + * @param className The {@link Declaration} class name + * @return The Declaration instance obtained by the input class name + */ + public Declaration getDeclarationByClass( final String className ) + { + Declaration decl = declarationsByClass.get( className ); + + if ( ( decl == null ) && ( parent != null ) ) + { + decl = parent.getDeclarationByClass( className ); + } + + return decl; + } + + /** + * Return the declaration object with the specified id. If no such plugin is known, null is returned. + * + * @param id Description of the Parameter + * @return The declaration value + */ + public Declaration getDeclarationById( final String id ) + { + Declaration decl = declarationsById.get( id ); + + if ( ( decl == null ) && ( parent != null ) ) + { + decl = parent.getDeclarationById( id ); + } + + return decl; + } + } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/plugins/PluginRules.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/plugins/PluginRules.java index 06d273cd7..e91d43982 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/plugins/PluginRules.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/plugins/PluginRules.java @@ -94,19 +94,6 @@ public PluginRules() this( new RulesBase() ); } - /** - * Constructor for top-level Rules object which handles rule-matching using the specified implementation. - * - * @param decoratedRules The top-level Rules object which handles rule-matching using the specified implementation. - */ - public PluginRules( final Rules decoratedRules ) - { - this.decoratedRules = decoratedRules; - - pluginContext = new PluginContext(); - pluginManager = new PluginManager( pluginContext ); - } - /** * Constructs a Rules instance which has a parent Rules object (which is different from having a delegate rules * object). @@ -146,140 +133,20 @@ public PluginRules( final Rules decoratedRules ) pluginManager = new PluginManager( parent.pluginManager ); } - // ------------------------------------------------------------- Properties - - /** - * Return the parent Rules object. - * - * @return the parent Rules object. - */ - public Rules getParent() - { - return parent; - } - - /** - * Return the Digester instance with which this instance is associated. - * - * @return the Digester instance with which this instance is associated. - */ - @Override - public Digester getDigester() - { - return digester; - } - - /** - * Sets the Digester instance with which this Rules instance is associated. - * - * @param digester The newly associated Digester instance - */ - @Override - public void setDigester( final Digester digester ) - { - this.digester = digester; - decoratedRules.setDigester( digester ); - } - - /** - * Return the namespace URI that will be applied to all subsequently added {@code Rule} objects. - * - * @return the namespace URI that will be applied to all subsequently added {@code Rule} objects. - */ - @Override - public String getNamespaceURI() - { - return decoratedRules.getNamespaceURI(); - } - - /** - * Sets the namespace URI that will be applied to all subsequently added {@code Rule} objects. - * - * @param namespaceURI Namespace URI that must match on all subsequently added rules, or {@code null} for - * matching regardless of the current namespace URI - */ - @Override - public void setNamespaceURI( final String namespaceURI ) - { - decoratedRules.setNamespaceURI( namespaceURI ); - } - - /** - * Return the object which "knows" about all declared plugins. - * - * @return The pluginManager value - */ - public PluginManager getPluginManager() - { - return pluginManager; - } - - /** - * See {@link PluginContext#getRuleFinders}. - * - * @return the list of RuleFinder objects - */ - public List getRuleFinders() - { - return pluginContext.getRuleFinders(); - } - - /** - * See {@link PluginContext#setRuleFinders}. - * - * @param ruleFinders the list of RuleFinder objects - */ - public void setRuleFinders( final List ruleFinders ) - { - pluginContext.setRuleFinders( ruleFinders ); - } - - /** - * Return the rules factory object (or null if one has not been specified). - * - * @return the rules factory object. - */ - public RulesFactory getRulesFactory() - { - return rulesFactory; - } - /** - * Sets the object which is used to generate the new Rules instances created to hold and process the rules associated - * with each plugged-in class. + * Constructor for top-level Rules object which handles rule-matching using the specified implementation. * - * @param factory the rules factory object + * @param decoratedRules The top-level Rules object which handles rule-matching using the specified implementation. */ - public void setRulesFactory( final RulesFactory factory ) + public PluginRules( final Rules decoratedRules ) { - rulesFactory = factory; - } - - // --------------------------------------------------------- Public Methods + this.decoratedRules = decoratedRules; - /** - * This package-scope method is used by the PluginCreateRule class to get direct access to the rules that were - * dynamically added by the plugin. No other class should need access to this object. - * - * @return The decorated Rule instance - */ - Rules getDecoratedRules() - { - return decoratedRules; + pluginContext = new PluginContext(); + pluginManager = new PluginManager( pluginContext ); } - /** - * Return the list of rules registered with this object, in the order they were registered with this object. - *

    - * Note that Rule objects stored in parent Rules objects are not returned by this method. - * - * @return list of all Rule objects known to this Rules instance. - */ - @Override - public List rules() - { - return decoratedRules.rules(); - } + // ------------------------------------------------------------- Properties /** * Register a new Rule instance matching the specified pattern. @@ -361,6 +228,121 @@ public void clear() decoratedRules.clear(); } + /** + * This package-scope method is used by the PluginCreateRule class to get direct access to the rules that were + * dynamically added by the plugin. No other class should need access to this object. + * + * @return The decorated Rule instance + */ + Rules getDecoratedRules() + { + return decoratedRules; + } + + /** + * Return the Digester instance with which this instance is associated. + * + * @return the Digester instance with which this instance is associated. + */ + @Override + public Digester getDigester() + { + return digester; + } + + /** + * Return the namespace URI that will be applied to all subsequently added {@code Rule} objects. + * + * @return the namespace URI that will be applied to all subsequently added {@code Rule} objects. + */ + @Override + public String getNamespaceURI() + { + return decoratedRules.getNamespaceURI(); + } + + /** + * Return the parent Rules object. + * + * @return the parent Rules object. + */ + public Rules getParent() + { + return parent; + } + + /** + * See {@link PluginContext#getPluginClassAttr}. + * + * @return the namespace for the xml attribute which indicates which class is to be plugged in. + */ + public String getPluginClassAttr() + { + return pluginContext.getPluginClassAttr(); + } + + /** + * See {@link PluginContext#getPluginClassAttrNs}. + * + * @return the namespace for the xml attribute which indicates which class is to be plugged in. + */ + public String getPluginClassAttrNs() + { + return pluginContext.getPluginClassAttrNs(); + } + + /** + * See {@link PluginContext#getPluginIdAttr}. + * + * @return the namespace for the xml attribute which indicates which previous plugin declaration should be used. + */ + public String getPluginIdAttr() + { + return pluginContext.getPluginIdAttr(); + } + + /** + * See {@link PluginContext#getPluginIdAttrNs}. + * + * @return the namespace for the xml attribute which indicates which previous plugin declaration should be used. + */ + public String getPluginIdAttrNs() + { + return pluginContext.getPluginIdAttrNs(); + } + + // --------------------------------------------------------- Public Methods + + /** + * Return the object which "knows" about all declared plugins. + * + * @return The pluginManager value + */ + public PluginManager getPluginManager() + { + return pluginManager; + } + + /** + * See {@link PluginContext#getRuleFinders}. + * + * @return the list of RuleFinder objects + */ + public List getRuleFinders() + { + return pluginContext.getRuleFinders(); + } + + /** + * Return the rules factory object (or null if one has not been specified). + * + * @return the rules factory object. + */ + public RulesFactory getRulesFactory() + { + return rulesFactory; + } + /** * {@inheritDoc} */ @@ -400,6 +382,43 @@ public List match( final String namespaceURI, final String path, final Str return matches; } + /** + * Return the list of rules registered with this object, in the order they were registered with this object. + *

    + * Note that Rule objects stored in parent Rules objects are not returned by this method. + * + * @return list of all Rule objects known to this Rules instance. + */ + @Override + public List rules() + { + return decoratedRules.rules(); + } + + /** + * Sets the Digester instance with which this Rules instance is associated. + * + * @param digester The newly associated Digester instance + */ + @Override + public void setDigester( final Digester digester ) + { + this.digester = digester; + decoratedRules.setDigester( digester ); + } + + /** + * Sets the namespace URI that will be applied to all subsequently added {@code Rule} objects. + * + * @param namespaceURI Namespace URI that must match on all subsequently added rules, or {@code null} for + * matching regardless of the current namespace URI + */ + @Override + public void setNamespaceURI( final String namespaceURI ) + { + decoratedRules.setNamespaceURI( namespaceURI ); + } + /** * See {@link PluginContext#setPluginClassAttribute}. * @@ -430,43 +449,24 @@ public void setPluginIdAttribute( final String namespaceUri, final String attrNa } /** - * See {@link PluginContext#getPluginClassAttrNs}. - * - * @return the namespace for the xml attribute which indicates which class is to be plugged in. - */ - public String getPluginClassAttrNs() - { - return pluginContext.getPluginClassAttrNs(); - } - - /** - * See {@link PluginContext#getPluginClassAttr}. - * - * @return the namespace for the xml attribute which indicates which class is to be plugged in. - */ - public String getPluginClassAttr() - { - return pluginContext.getPluginClassAttr(); - } - - /** - * See {@link PluginContext#getPluginIdAttrNs}. + * See {@link PluginContext#setRuleFinders}. * - * @return the namespace for the xml attribute which indicates which previous plugin declaration should be used. + * @param ruleFinders the list of RuleFinder objects */ - public String getPluginIdAttrNs() + public void setRuleFinders( final List ruleFinders ) { - return pluginContext.getPluginIdAttrNs(); + pluginContext.setRuleFinders( ruleFinders ); } /** - * See {@link PluginContext#getPluginIdAttr}. + * Sets the object which is used to generate the new Rules instances created to hold and process the rules associated + * with each plugged-in class. * - * @return the namespace for the xml attribute which indicates which previous plugin declaration should be used. + * @param factory the rules factory object */ - public String getPluginIdAttr() + public void setRulesFactory( final RulesFactory factory ) { - return pluginContext.getPluginIdAttr(); + rulesFactory = factory; } } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/plugins/strategies/FinderFromResource.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/plugins/strategies/FinderFromResource.java index 5561cc6f9..3af4a17de 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/plugins/strategies/FinderFromResource.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/plugins/strategies/FinderFromResource.java @@ -44,6 +44,42 @@ public class FinderFromResource */ private static final String DFLT_RESOURCE_ATTR = "resource"; + /** + * Open the specified resource file (ie a file in the classpath, including being within a jar in the classpath), run + * it through the xmlrules module and return an object encapsulating those rules. + * + * @param d is the digester into which rules will eventually be loaded. + * @param pluginClass is the class whose xml params the rules are parsing. + * @param is is where the xmlrules will be read from, and must be non-null. + * @param resourceName is a string describing the source of the xmlrules, for use in generating error messages. + * @return a source of digester rules for the specified plugin class. + * @throws PluginException if any error occurs + */ + public static RuleLoader loadRules( final Digester d, final Class pluginClass, final InputStream is, final String resourceName ) + throws PluginException + { + try + { + final RuleLoader loader = new LoaderFromStream( is ); + return loader; + } + catch ( final Exception e ) + { + throw new PluginException( "Unable to load xmlrules from resource [" + resourceName + "]", e ); + } + finally + { + try + { + is.close(); + } + catch ( final IOException ioe ) + { + throw new PluginException( "Unable to close stream for resource [" + resourceName + "]", ioe ); + } + } + } + /** See {@link #findLoader}. */ private final String resourceAttr; @@ -103,40 +139,4 @@ public RuleLoader findLoader( final Digester d, final Class pluginClass, fina return loadRules( d, pluginClass, is, resourceName ); } - /** - * Open the specified resource file (ie a file in the classpath, including being within a jar in the classpath), run - * it through the xmlrules module and return an object encapsulating those rules. - * - * @param d is the digester into which rules will eventually be loaded. - * @param pluginClass is the class whose xml params the rules are parsing. - * @param is is where the xmlrules will be read from, and must be non-null. - * @param resourceName is a string describing the source of the xmlrules, for use in generating error messages. - * @return a source of digester rules for the specified plugin class. - * @throws PluginException if any error occurs - */ - public static RuleLoader loadRules( final Digester d, final Class pluginClass, final InputStream is, final String resourceName ) - throws PluginException - { - try - { - final RuleLoader loader = new LoaderFromStream( is ); - return loader; - } - catch ( final Exception e ) - { - throw new PluginException( "Unable to load xmlrules from resource [" + resourceName + "]", e ); - } - finally - { - try - { - is.close(); - } - catch ( final IOException ioe ) - { - throw new PluginException( "Unable to close stream for resource [" + resourceName + "]", ioe ); - } - } - } - } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/plugins/strategies/LoaderFromClass.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/plugins/strategies/LoaderFromClass.java index 520949d99..68cd8c04c 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/plugins/strategies/LoaderFromClass.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/plugins/strategies/LoaderFromClass.java @@ -38,6 +38,24 @@ public class LoaderFromClass extends RuleLoader { + /** + * Find a method on the specified class whose name matches methodName, and whose signature is: + * {@code public static void foo(Digester d, String patternPrefix);}. + * + * @param rulesClass The target class + * @param methodName The method name has to be invoked + * @return The method name has to be invoked, or null if no such method exists. + * @throws PluginException if any error occurs while discovering the method + */ + public static Method locateMethod( final Class rulesClass, final String methodName ) + throws PluginException + { + final Class[] paramSpec = { Digester.class, String.class }; + final Method rulesMethod = getAccessibleMethod( rulesClass, methodName, paramSpec ); + + return rulesMethod; + } + private final Class rulesClass; private final Method rulesMethod; @@ -104,22 +122,4 @@ public void addRules( final Digester d, final String path ) } } - /** - * Find a method on the specified class whose name matches methodName, and whose signature is: - * {@code public static void foo(Digester d, String patternPrefix);}. - * - * @param rulesClass The target class - * @param methodName The method name has to be invoked - * @return The method name has to be invoked, or null if no such method exists. - * @throws PluginException if any error occurs while discovering the method - */ - public static Method locateMethod( final Class rulesClass, final String methodName ) - throws PluginException - { - final Class[] paramSpec = { Digester.class, String.class }; - final Method rulesMethod = getAccessibleMethod( rulesClass, methodName, paramSpec ); - - return rulesMethod; - } - } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/substitution/VariableAttributes.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/substitution/VariableAttributes.java index da262242a..53fc8c483 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/substitution/VariableAttributes.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/substitution/VariableAttributes.java @@ -43,168 +43,168 @@ public class VariableAttributes // ------------------- Public Methods + // plain proxy methods follow : nothing interesting :-) /** - * Specify which attributes class this object is a proxy for. - * - * @param attrs The attributes where variables have to be expanded. - * @param expander The variables expander instance. + * {@inheritDoc} */ - public void init( final Attributes attrs, final VariableExpander expander ) + @Override + public int getIndex( final String qName ) { - this.attrs = attrs; - this.expander = expander; - - // I hope this doesn't release the memory for this array; for - // efficiency, this should just mark the array as being size 0. - values.clear(); + return attrs.getIndex( qName ); } /** * {@inheritDoc} */ @Override - public String getValue( final int index ) + public int getIndex( final String uri, final String localPart ) { - if ( index >= values.size() ) - { - // Expand the values array with null elements, so the later - // call to set(index, s) works ok. - // - // Unfortunately, there is no easy way to set the size of - // an arraylist; we must repeatedly add null elements to it.. - values.ensureCapacity( index + 1 ); - for ( int i = values.size(); i <= index; ++i ) - { - values.add( null ); - } - } - - String s = values.get( index ); - - if ( s == null ) - { - // we have never been asked for this value before. - // get the real attribute value and perform substitution - // on it. - s = attrs.getValue( index ); - if ( s != null ) - { - s = expander.expand( s ); - values.set( index, s ); - } - } - - return s; + return attrs.getIndex( uri, localPart ); } /** * {@inheritDoc} */ @Override - public String getValue( final String qName ) + public int getLength() { - final int index = attrs.getIndex( qName ); - if ( index == -1 ) - { - return null; - } - return getValue( index ); + return attrs.getLength(); } /** * {@inheritDoc} */ @Override - public String getValue( final String uri, final String localName ) + public String getLocalName( final int index ) { - final int index = attrs.getIndex( uri, localName ); - if ( index == -1 ) - { - return null; - } - return getValue( index ); + return attrs.getLocalName( index ); } - // plain proxy methods follow : nothing interesting :-) /** * {@inheritDoc} */ @Override - public int getIndex( final String qName ) + public String getQName( final int index ) { - return attrs.getIndex( qName ); + return attrs.getQName( index ); } /** * {@inheritDoc} */ @Override - public int getIndex( final String uri, final String localPart ) + public String getType( final int index ) { - return attrs.getIndex( uri, localPart ); + return attrs.getType( index ); } /** * {@inheritDoc} */ @Override - public int getLength() + public String getType( final String qName ) { - return attrs.getLength(); + return attrs.getType( qName ); } /** * {@inheritDoc} */ @Override - public String getLocalName( final int index ) + public String getType( final String uri, final String localName ) { - return attrs.getLocalName( index ); + return attrs.getType( uri, localName ); } /** * {@inheritDoc} */ @Override - public String getQName( final int index ) + public String getURI( final int index ) { - return attrs.getQName( index ); + return attrs.getURI( index ); } /** * {@inheritDoc} */ @Override - public String getType( final int index ) + public String getValue( final int index ) { - return attrs.getType( index ); + if ( index >= values.size() ) + { + // Expand the values array with null elements, so the later + // call to set(index, s) works ok. + // + // Unfortunately, there is no easy way to set the size of + // an arraylist; we must repeatedly add null elements to it.. + values.ensureCapacity( index + 1 ); + for ( int i = values.size(); i <= index; ++i ) + { + values.add( null ); + } + } + + String s = values.get( index ); + + if ( s == null ) + { + // we have never been asked for this value before. + // get the real attribute value and perform substitution + // on it. + s = attrs.getValue( index ); + if ( s != null ) + { + s = expander.expand( s ); + values.set( index, s ); + } + } + + return s; } /** * {@inheritDoc} */ @Override - public String getType( final String qName ) + public String getValue( final String qName ) { - return attrs.getType( qName ); + final int index = attrs.getIndex( qName ); + if ( index == -1 ) + { + return null; + } + return getValue( index ); } /** * {@inheritDoc} */ @Override - public String getType( final String uri, final String localName ) + public String getValue( final String uri, final String localName ) { - return attrs.getType( uri, localName ); + final int index = attrs.getIndex( uri, localName ); + if ( index == -1 ) + { + return null; + } + return getValue( index ); } /** - * {@inheritDoc} + * Specify which attributes class this object is a proxy for. + * + * @param attrs The attributes where variables have to be expanded. + * @param expander The variables expander instance. */ - @Override - public String getURI( final int index ) + public void init( final Attributes attrs, final VariableExpander expander ) { - return attrs.getURI( index ); + this.attrs = attrs; + this.expander = expander; + + // I hope this doesn't release the memory for this array; for + // efficiency, this should just mark the array as being size 0. + values.clear(); } } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/xmlrules/FromXmlRulesModule.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/xmlrules/FromXmlRulesModule.java index a1a129240..8a95f7d40 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/xmlrules/FromXmlRulesModule.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/xmlrules/FromXmlRulesModule.java @@ -77,11 +77,43 @@ protected void configure() } } + /** + * Returns the XML source SystemIds load by this module. + * + * @return The XML source SystemIds load by this module + */ + public final Set getSystemIds() + { + return unmodifiableSet( systemIds ); + } + /** * */ protected abstract void loadRules(); + /** + * Opens a new {@code org.xml.sax.InputSource} given a {@code java.io.File}. + * + * @param file The {@code java.io.File} where reading the XML rules from. + */ + protected final void loadXMLRules( final File file ) + { + if ( file == null ) + { + throw new IllegalArgumentException( "Argument 'input' must be not null" ); + } + + try + { + loadXMLRules( file.toURI().toURL() ); + } + catch ( final MalformedURLException e ) + { + rulesBinder().addError( e ); + } + } + /** * Reads the XML rules from the given {@code org.xml.sax.InputSource}. * @@ -149,28 +181,6 @@ protected final void loadXMLRules( final Reader reader ) loadXMLRules( new InputSource( reader ) ); } - /** - * Opens a new {@code org.xml.sax.InputSource} given a {@code java.io.File}. - * - * @param file The {@code java.io.File} where reading the XML rules from. - */ - protected final void loadXMLRules( final File file ) - { - if ( file == null ) - { - throw new IllegalArgumentException( "Argument 'input' must be not null" ); - } - - try - { - loadXMLRules( file.toURI().toURL() ); - } - catch ( final MalformedURLException e ) - { - rulesBinder().addError( e ); - } - } - /** * Opens a new {@code org.xml.sax.InputSource} given a URI in String representation. * @@ -246,14 +256,4 @@ protected final void useRootPath( final String rootPath ) this.rootPath = rootPath; } - /** - * Returns the XML source SystemIds load by this module. - * - * @return The XML source SystemIds load by this module - */ - public final Set getSystemIds() - { - return unmodifiableSet( systemIds ); - } - } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/xmlrules/NameSpaceURIRulesBinder.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/xmlrules/NameSpaceURIRulesBinder.java index 5997bd43a..c097b8ba8 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/xmlrules/NameSpaceURIRulesBinder.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/xmlrules/NameSpaceURIRulesBinder.java @@ -43,47 +43,48 @@ public NameSpaceURIRulesBinder( final RulesBinder wrappedBinder ) } /** - * - * @param namespaceURI + * {@inheritDoc} */ - public void addNamespaceURI( final String namespaceURI ) + @Override + public void addError( final String messagePattern, final Object... arguments ) { - namespaceURIs.push( namespaceURI ); + wrappedBinder.addError( messagePattern, arguments ); } /** - * + * {@inheritDoc} */ - public void removeNamespaceURI() + @Override + public void addError( final Throwable t ) { - namespaceURIs.pop(); + wrappedBinder.addError( t ); } /** - * {@inheritDoc} + * + * @param namespaceURI */ - @Override - public ClassLoader getContextClassLoader() + public void addNamespaceURI( final String namespaceURI ) { - return wrappedBinder.getContextClassLoader(); + namespaceURIs.push( namespaceURI ); } /** * {@inheritDoc} */ @Override - public void addError( final String messagePattern, final Object... arguments ) + public LinkedRuleBuilder forPattern( final String pattern ) { - wrappedBinder.addError( messagePattern, arguments ); + return wrappedBinder.forPattern( pattern ).withNamespaceURI( namespaceURIs.peek() ); } /** * {@inheritDoc} */ @Override - public void addError( final Throwable t ) + public ClassLoader getContextClassLoader() { - wrappedBinder.addError( t ); + return wrappedBinder.getContextClassLoader(); } /** @@ -96,12 +97,11 @@ public void install( final RulesModule rulesModule ) } /** - * {@inheritDoc} + * */ - @Override - public LinkedRuleBuilder forPattern( final String pattern ) + public void removeNamespaceURI() { - return wrappedBinder.forPattern( pattern ).withNamespaceURI( namespaceURIs.peek() ); + namespaceURIs.pop(); } } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/xmlrules/PrefixedRulesBinder.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/xmlrules/PrefixedRulesBinder.java index 2e6c13755..eb8909018 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/xmlrules/PrefixedRulesBinder.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/xmlrules/PrefixedRulesBinder.java @@ -41,49 +41,49 @@ public PrefixedRulesBinder( final RulesBinder wrappedRulesBinder, final String p * {@inheritDoc} */ @Override - public ClassLoader getContextClassLoader() + public void addError( final String messagePattern, final Object... arguments ) { - return this.wrappedRulesBinder.getContextClassLoader(); + this.wrappedRulesBinder.addError( messagePattern, arguments ); } /** * {@inheritDoc} */ @Override - public void addError( final String messagePattern, final Object... arguments ) + public void addError( final Throwable t ) { - this.wrappedRulesBinder.addError( messagePattern, arguments ); + this.wrappedRulesBinder.addError( t ); } /** * {@inheritDoc} */ @Override - public void addError( final Throwable t ) + public LinkedRuleBuilder forPattern( String pattern ) { - this.wrappedRulesBinder.addError( t ); + if ( this.prefix != null && !this.prefix.isEmpty() ) + { + pattern = this.prefix + '/' + pattern; + } + return this.wrappedRulesBinder.forPattern( pattern ); } /** * {@inheritDoc} */ @Override - public void install( final RulesModule rulesModule ) + public ClassLoader getContextClassLoader() { - this.wrappedRulesBinder.install( rulesModule ); + return this.wrappedRulesBinder.getContextClassLoader(); } /** * {@inheritDoc} */ @Override - public LinkedRuleBuilder forPattern( String pattern ) + public void install( final RulesModule rulesModule ) { - if ( this.prefix != null && !this.prefix.isEmpty() ) - { - pattern = this.prefix + '/' + pattern; - } - return this.wrappedRulesBinder.forPattern( pattern ); + this.wrappedRulesBinder.install( rulesModule ); } } diff --git a/commons-digester3-core/src/main/java/org/apache/commons/digester3/xmlrules/WithMemoryRulesBinder.java b/commons-digester3-core/src/main/java/org/apache/commons/digester3/xmlrules/WithMemoryRulesBinder.java index be490c4e4..5654c5716 100644 --- a/commons-digester3-core/src/main/java/org/apache/commons/digester3/xmlrules/WithMemoryRulesBinder.java +++ b/commons-digester3-core/src/main/java/org/apache/commons/digester3/xmlrules/WithMemoryRulesBinder.java @@ -56,45 +56,44 @@ public WithMemoryRulesBinder( final RulesBinder wrappedRulesBinder ) * {@inheritDoc} */ @Override - public ClassLoader getContextClassLoader() + public void addError( final String messagePattern, final Object... arguments ) { - return this.wrappedRulesBinder.getContextClassLoader(); + this.wrappedRulesBinder.addError( messagePattern, arguments ); } /** * {@inheritDoc} */ @Override - public void addError( final String messagePattern, final Object... arguments ) + public void addError( final Throwable t ) { - this.wrappedRulesBinder.addError( messagePattern, arguments ); + this.wrappedRulesBinder.addError( t ); } /** * {@inheritDoc} */ @Override - public void addError( final Throwable t ) + public LinkedRuleBuilder forPattern( final String pattern ) { - this.wrappedRulesBinder.addError( t ); + return this.wrappedRulesBinder.forPattern( pattern ); } /** * {@inheritDoc} */ @Override - public void install( final RulesModule rulesModule ) + public ClassLoader getContextClassLoader() { - this.wrappedRulesBinder.install( rulesModule ); + return this.wrappedRulesBinder.getContextClassLoader(); } /** - * {@inheritDoc} + * @return the set of included files */ - @Override - public LinkedRuleBuilder forPattern( final String pattern ) + public Set getIncludedFiles() { - return this.wrappedRulesBinder.forPattern( pattern ); + return this.includedFiles; } /** @@ -106,11 +105,12 @@ public PatternStack getPatternStack() } /** - * @return the set of included files + * {@inheritDoc} */ - public Set getIncludedFiles() + @Override + public void install( final RulesModule rulesModule ) { - return this.includedFiles; + this.wrappedRulesBinder.install( rulesModule ); } } diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/Address.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/Address.java index 62a463646..0e0b22222 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/Address.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/Address.java @@ -25,6 +25,16 @@ public class Address { + private String city; + + private String state; + + private String street; + + private String type; + + private String zipCode; + public Address() { this( "My Street", "My City", "US", "MyZip" ); @@ -38,69 +48,59 @@ public Address( final String street, final String city, final String state, fina setZipCode( zipCode ); } - private String city; - public String getCity() { return ( this.city ); } - public void setCity( final String city ) - { - this.city = city; - } - - private String state; - public String getState() { return ( this.state ); } - public void setState( final String state ) - { - this.state = state; - } - - private String street; - public String getStreet() { return ( this.street ); } - public void setStreet( final String street ) + public String getType() { - this.street = street; + return ( this.type ); } - private String type; + public String getZipCode() + { + return ( this.zipCode ); + } - public String getType() + public void setCity( final String city ) { - return ( this.type ); + this.city = city; } - public void setType( final String type ) + public void setEmployee( final Employee employee ) { - this.type = type; + employee.addAddress( this ); } - private String zipCode; + public void setState( final String state ) + { + this.state = state; + } - public String getZipCode() + public void setStreet( final String street ) { - return ( this.zipCode ); + this.street = street; } - public void setZipCode( final String zipCode ) + public void setType( final String type ) { - this.zipCode = zipCode; + this.type = type; } - public void setEmployee( final Employee employee ) + public void setZipCode( final String zipCode ) { - employee.addAddress( this ); + this.zipCode = zipCode; } @Override diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/AlphaBean.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/AlphaBean.java index 71f9fed1b..b43a3c541 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/AlphaBean.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/AlphaBean.java @@ -36,21 +36,15 @@ public AlphaBean( final String name ) setName( name ); } - @Override - public String getName() + public Nameable getChild() { - return name; + return child; } @Override - public void setName( final String name ) - { - this.name = name; - } - - public void setParent( final Nameable parent ) + public String getName() { - this.parent = parent; + return name; } public Nameable getParent() @@ -63,9 +57,15 @@ public void setChild( final Nameable child ) this.child = child; } - public Nameable getChild() + @Override + public void setName( final String name ) { - return child; + this.name = name; + } + + public void setParent( final Nameable parent ) + { + this.parent = parent; } @Override diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/AsyncReaderTestCase.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/AsyncReaderTestCase.java index 87db809e2..8f45bc2e4 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/AsyncReaderTestCase.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/AsyncReaderTestCase.java @@ -64,36 +64,36 @@ public void tearDown() } @Test - public void testParseFromFile() + public void testParseFromClasspathURL() throws Exception { - final Future future = digester.asyncParse( new File( getProperty( "user.dir" ), - "src/test/resources/org/apache/commons/digester3/Test9.xml" ) ); + final Future future = digester.asyncParse( getClass().getResource( "Test9.xml" ) ); verify( future ); } @Test - public void testParseFromClasspathURL() + public void testParseFromFile() throws Exception { - final Future future = digester.asyncParse( getClass().getResource( "Test9.xml" ) ); + final Future future = digester.asyncParse( new File( getProperty( "user.dir" ), + "src/test/resources/org/apache/commons/digester3/Test9.xml" ) ); verify( future ); } @Test - public void testParseFromInputStream() + public void testParseFromInputSource() throws Exception { - final Future future = digester.asyncParse( getClass().getResource( "Test9.xml" ).openStream() ); + final Future future = + digester.asyncParse( new InputSource( getClass().getResource( "Test9.xml" ).openStream() ) ); verify( future ); } @Test - public void testParseFromInputSource() + public void testParseFromInputStream() throws Exception { - final Future future = - digester.asyncParse( new InputSource( getClass().getResource( "Test9.xml" ).openStream() ) ); + final Future future = digester.asyncParse( getClass().getResource( "Test9.xml" ).openStream() ); verify( future ); } diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/BeanPropertySetterRuleTestCase.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/BeanPropertySetterRuleTestCase.java index 738e28a3f..59e6e8fb7 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/BeanPropertySetterRuleTestCase.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/BeanPropertySetterRuleTestCase.java @@ -53,6 +53,72 @@ public class BeanPropertySetterRuleTestCase // ------------------------------------------------ Individual Test Methods + /** + * Test that you can successfully automatically set properties. + */ + @Test + public void testAutomaticallySetProperties() + throws SAXException, IOException + { + final Digester digester = newLoader(new AbstractRulesModule() + { + + @Override + protected void configure() + { + forPattern( "root" ).createObject().ofType( "org.apache.commons.digester3.SimpleTestBean" ); + forPattern( "root/?" ).setBeanProperty(); + } + + }).newDigester( new ExtendedBaseRules() ); + + final SimpleTestBean bean = digester.parse( xmlTestReader() ); + + // check properties are set correctly + assertEquals( "Property alpha not set correctly", "ALPHA BODY", bean.getAlpha() ); + + assertEquals( "Property beta not set correctly", "BETA BODY", bean.getBeta() ); + + assertEquals( "Property gamma not set correctly", "GAMMA BODY", bean.getGamma() ); + + } + + /** + * This is a general digester test but it fits into here pretty well. This tests that the body text stack is + * functioning correctly. + */ + @Test + public void testDigesterBodyTextStack() + throws SAXException, IOException + { + final List callOrder = new ArrayList(); + + final Digester digester = newLoader(new AbstractRulesModule() + { + + @Override + protected void configure() + { + forPattern( "root" ).addRuleCreatedBy( new TestRule.TestRuleProvider( "root", callOrder ) ); + forPattern( "root/alpha" ).addRuleCreatedBy( new TestRule.TestRuleProvider( "root/alpha", callOrder ) ); + forPattern( "root/beta" ).addRuleCreatedBy( new TestRule.TestRuleProvider( "root/beta", callOrder ) ); + forPattern( "root/gamma" ).addRuleCreatedBy( new TestRule.TestRuleProvider( "root/gamma", callOrder ) ); + } + + }).newDigester(); + + digester.parse( xmlTestReader() ); + + assertEquals( "Root body text not set correct.", "ROOT BODY", ( (TestRule) callOrder.get( 0 ) ).getBodyText() ); + + assertEquals( "Alpha body text not set correct.", "ALPHA BODY", ( (TestRule) callOrder.get( 1 ) ).getBodyText() ); + + assertEquals( "Beta body text not set correct.", "BETA BODY", ( (TestRule) callOrder.get( 4 ) ).getBodyText() ); + + assertEquals( "Gamma body text not set correct.", "GAMMA BODY", ( (TestRule) callOrder.get( 7 ) ).getBodyText() ); + + } + /** * This is a general digester test but it fits into here pretty well. This tests that the rule calling order is * properly enforced. @@ -113,40 +179,27 @@ protected void configure() } - /** - * This is a general digester test but it fits into here pretty well. This tests that the body text stack is - * functioning correctly. - */ @Test - public void testDigesterBodyTextStack() - throws SAXException, IOException + public void testExtractPropertyNameFromAttribute() throws Exception { - final List callOrder = new ArrayList(); + final Employee expected = new Employee( "John", "Doe" ); - final Digester digester = newLoader(new AbstractRulesModule() + final Employee actual = newLoader( new AbstractRulesModule() { @Override protected void configure() { - forPattern( "root" ).addRuleCreatedBy( new TestRule.TestRuleProvider( "root", callOrder ) ); - forPattern( "root/alpha" ).addRuleCreatedBy( new TestRule.TestRuleProvider( "root/alpha", callOrder ) ); - forPattern( "root/beta" ).addRuleCreatedBy( new TestRule.TestRuleProvider( "root/beta", callOrder ) ); - forPattern( "root/gamma" ).addRuleCreatedBy( new TestRule.TestRuleProvider( "root/gamma", callOrder ) ); + forPattern( "employee" ).createObject().ofType( Employee.class ); + forPattern( "employee/property" ).setBeanProperty().extractPropertyNameFromAttribute( "name" ); } - }).newDigester(); - - digester.parse( xmlTestReader() ); - - assertEquals( "Root body text not set correct.", "ROOT BODY", ( (TestRule) callOrder.get( 0 ) ).getBodyText() ); - - assertEquals( "Alpha body text not set correct.", "ALPHA BODY", ( (TestRule) callOrder.get( 1 ) ).getBodyText() ); - - assertEquals( "Beta body text not set correct.", "BETA BODY", ( (TestRule) callOrder.get( 4 ) ).getBodyText() ); - - assertEquals( "Gamma body text not set correct.", "GAMMA BODY", ( (TestRule) callOrder.get( 7 ) ).getBodyText() ); + } ) + .newDigester() + .parse( getClass().getResource( "extractPropertyNameFromAttribute.xml" ) ); + assertEquals( expected.getFirstName(), actual.getFirstName() ); + assertEquals( expected.getLastName(), actual.getLastName() ); } /** @@ -234,59 +287,6 @@ protected void configure() } - /** - * Test that you can successfully automatically set properties. - */ - @Test - public void testAutomaticallySetProperties() - throws SAXException, IOException - { - final Digester digester = newLoader(new AbstractRulesModule() - { - - @Override - protected void configure() - { - forPattern( "root" ).createObject().ofType( "org.apache.commons.digester3.SimpleTestBean" ); - forPattern( "root/?" ).setBeanProperty(); - } - - }).newDigester( new ExtendedBaseRules() ); - - final SimpleTestBean bean = digester.parse( xmlTestReader() ); - - // check properties are set correctly - assertEquals( "Property alpha not set correctly", "ALPHA BODY", bean.getAlpha() ); - - assertEquals( "Property beta not set correctly", "BETA BODY", bean.getBeta() ); - - assertEquals( "Property gamma not set correctly", "GAMMA BODY", bean.getGamma() ); - - } - - @Test - public void testExtractPropertyNameFromAttribute() throws Exception - { - final Employee expected = new Employee( "John", "Doe" ); - - final Employee actual = newLoader( new AbstractRulesModule() - { - - @Override - protected void configure() - { - forPattern( "employee" ).createObject().ofType( Employee.class ); - forPattern( "employee/property" ).setBeanProperty().extractPropertyNameFromAttribute( "name" ); - } - - } ) - .newDigester() - .parse( getClass().getResource( "extractPropertyNameFromAttribute.xml" ) ); - - assertEquals( expected.getFirstName(), actual.getFirstName() ); - assertEquals( expected.getLastName(), actual.getLastName() ); - } - /** * Gets input stream from {@link #TEST_XML}. */ diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/BetaBean.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/BetaBean.java index 98eaf421c..48fb9e95b 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/BetaBean.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/BetaBean.java @@ -31,21 +31,15 @@ public BetaBean() { } - @Override - public String getName() + public Nameable getChild() { - return name; + return child; } @Override - public void setName( final String name ) - { - this.name = name; - } - - public void setParent( final Nameable parent ) + public String getName() { - this.parent = parent; + return name; } public Nameable getParent() @@ -58,9 +52,15 @@ public void setChild( final Nameable child ) this.child = child; } - public Nameable getChild() + @Override + public void setName( final String name ) { - return child; + this.name = name; + } + + public void setParent( final Nameable parent ) + { + this.parent = parent; } @Override diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/Box.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/Box.java index 494abc0df..0cbb95fa2 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/Box.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/Box.java @@ -34,16 +34,6 @@ public Box() { } - public String getId() - { - return id; - } - - public void setId( final String id ) - { - this.id = id; - } - public void addChild( final Box child ) { this.children.add( child ); @@ -54,21 +44,9 @@ public List getChildren() return children; } - @Override - public String toString() + public String getId() { - final StringBuilder buf = new StringBuilder(); - buf.append( "[Box] id=" ); - buf.append( id ); - buf.append( " nchildren=" ); - buf.append( children.size() ); - - for ( final Box child : children ) - { - buf.append( " " ); - buf.append( child.toString() ); - } - return buf.toString(); + return id; } /** @@ -86,4 +64,26 @@ public String getIds() } return buf.toString(); } + + public void setId( final String id ) + { + this.id = id; + } + + @Override + public String toString() + { + final StringBuilder buf = new StringBuilder(); + buf.append( "[Box] id=" ); + buf.append( id ); + buf.append( " nchildren=" ); + buf.append( children.size() ); + + for ( final Box child : children ) + { + buf.append( " " ); + buf.append( child.toString() ); + } + return buf.toString(); + } } diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/CallMethodRuleTestCase.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/CallMethodRuleTestCase.java index f8772887b..f1d3fda57 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/CallMethodRuleTestCase.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/CallMethodRuleTestCase.java @@ -44,6 +44,20 @@ public class CallMethodRuleTestCase { + /** + * Return an appropriate InputStream for the specified test file (which must be inside our current package. + * + * @param name Name of the test file we want + * @throws IOException if an input/output error occurs + */ + protected InputStream getInputStream( final String name ) + throws IOException + { + + return ( this.getClass().getResourceAsStream( "/org/apache/commons/digester3/" + name ) ); + + } + /** * Test method calls with the CallMethodRule rule. It should be possible to call a method with no arguments using * several rule syntaxes. @@ -79,6 +93,34 @@ protected void configure() assertNotNull( root1 ); } + /** + * Test invoking an object which does not exist on the stack. + */ + @Test + public void testCallInvalidTarget() + throws Exception + { + + final Digester digester = new Digester(); + digester.addObjectCreate( "employee", HashMap.class ); + + // there should be only one object on the stack (index zero), + // so selecting a target object with index 1 on the object stack + // should result in an exception. + final CallMethodRule r = new CallMethodRule( 1, "put", 0 ); + digester.addRule( "employee", r ); + + try + { + digester.parse( getInputStream( "Test5.xml" ) ); + fail( "Exception should be thrown for invalid target offset" ); + } + catch ( final SAXException e ) + { + // ok, exception expected + } + } + /** * Test method calls with the CallMethodRule reading from the element body, with no CallParamMethod rules added. */ @@ -109,302 +151,93 @@ protected void configure() } /** - * Test CallMethodRule variants which specify the classes of the parameters to target methods. String, int, boolean, - * float should all be acceptable as parameter types. + * Test invoking an object which is at top-1 on the stack, like SetNextRule does... */ @Test - public void testSettingProperties() - throws SAXException, IOException + public void testCallNext() + throws Exception { - Digester digester = newLoader( new AbstractRulesModule() - { - - @Override - protected void configure() - { - forPattern( "employee" ).createObject().ofType( Employee.class ) - .then() - .callMethod( "setLastName" ).withParamTypes( "java.lang.String" ); - forPattern( "employee/lastName" ).callParam().ofIndex( 0 ); - } - - }).newDigester(); - - // Parse our test input - // an exception will be thrown if the method can't be found - Employee employee = digester.parse( getInputStream( "Test5.xml" ) ); - assertEquals( "Failed to call Employee.setLastName", "Last Name", employee.getLastName() ); - - digester = newLoader( new AbstractRulesModule() - { - - @Override - protected void configure() - { - forPattern( "employee" ).createObject().ofType( Employee.class ) - .then() - .callMethod( "setAge" ).withParamTypes( int.class ); - forPattern( "employee/age" ).callParam(); - } - - }).newDigester(); - - // Parse our test input - // an exception will be thrown if the method can't be found - employee = digester.parse( getInputStream( "Test5.xml" ) ); - assertEquals( "Failed to call Employee.setAge", 21, employee.getAge() ); - - digester = newLoader( new AbstractRulesModule() - { - - @Override - protected void configure() - { - forPattern( "employee" ).createObject().ofType( Employee.class ) - .then() - .callMethod( "setActive" ).withParamTypes( boolean.class ); - forPattern( "employee/active" ).callParam(); - } - - }).newDigester(); - - // Parse our test input - // an exception will be thrown if the method can't be found - employee = digester.parse( getInputStream( "Test5.xml" ) ); - assertEquals( "Failed to call Employee.setActive", true, employee.isActive() ); - - digester = newLoader( new AbstractRulesModule() - { + final Digester digester = new Digester(); + digester.addObjectCreate( "employee", HashMap.class ); - @Override - protected void configure() - { - forPattern( "employee" ).createObject().ofType( Employee.class ) - .then() - .callMethod( "setSalary" ).withParamTypes( float.class ); - forPattern( "employee/salary" ).callParam(); - } + digester.addObjectCreate( "employee/address", Address.class ); + digester.addSetNestedProperties( "employee/address" ); + final CallMethodRule r = new CallMethodRule( 1, "put", 2 ); + digester.addRule( "employee/address", r ); + digester.addCallParam( "employee/address/type", 0 ); + digester.addCallParam( "employee/address", 1, 0 ); - }).newDigester(); + final HashMap map = digester.parse( getInputStream( "Test5.xml" ) ); - // Parse our test input - // an exception will be thrown if the method can't be found - employee = digester.parse( getInputStream( "Test5.xml" ) ); - assertEquals( "Failed to call Employee.setSalary", 1000000.0f, employee.getSalary(), 0.1f ); + assertNotNull( map ); + final Set keys = map.keySet(); + assertEquals( 2, keys.size() ); + final Address home = map.get( "home" ); + assertNotNull( home ); + assertEquals( "HmZip", home.getZipCode() ); + final Address office = map.get( "office" ); + assertNotNull( office ); + assertEquals( "OfZip", office.getZipCode() ); } /** - * This tests the call methods params enhancement that provides for more complex stack-based calls. + * Test invoking an object which is at the root of the stack, like SetRoot does... */ @Test - public void testParamsFromStack() - throws SAXException, IOException + public void testCallRoot() + throws Exception { - final Digester digester = newLoader( new AbstractRulesModule() - { - - @Override - protected void configure() - { - forPattern( "map" ).createObject().ofType( HashMap.class ) - .then() - .callMethod( "put" ).withParamCount( 2 ); - forPattern( "map/key" ).createObject().ofType( AlphaBean.class ) - .then() - .setProperties() - .then() - .callParam().fromStack( true ); - forPattern( "map/value" ).createObject().ofType( BetaBean.class ) - .then() - .setProperties() - .then() - .callParam().ofIndex( 1 ).fromStack( true ); - } - }).newDigester(); + final Digester digester = new Digester(); + digester.addObjectCreate( "employee", HashMap.class ); - final StringBuilder xml = - new StringBuilder().append( "" ).append( "" ).append( " " ).append( " " ).append( "" ); + digester.addObjectCreate( "employee/address", Address.class ); + digester.addSetNestedProperties( "employee/address" ); + final CallMethodRule r = new CallMethodRule( -1, "put", 2 ); + digester.addRule( "employee/address", r ); + digester.addCallParam( "employee/address/type", 0 ); + digester.addCallParam( "employee/address", 1, 0 ); - final HashMap map = digester.parse( new StringReader( xml.toString() ) ); + final HashMap map = digester.parse( getInputStream( "Test5.xml" ) ); assertNotNull( map ); - assertEquals( 1, map.size() ); - assertEquals( "The key", map.keySet().iterator().next().getName() ); - assertEquals( "The value", map.values().iterator().next().getName() ); + final Set keys = map.keySet(); + assertEquals( 2, keys.size() ); + final Address home = map.get( "home" ); + assertNotNull( home ); + assertEquals( "HmZip", home.getZipCode() ); + final Address office = map.get( "office" ); + assertNotNull( office ); + assertEquals( "OfZip", office.getZipCode() ); } - /** - * Test that the target object for a CallMethodRule is the object that was on top of the object stack when the - * CallMethodRule fired, even when other rules fire between the CallMethodRule and its associated CallParamRules. - *

    - * The current implementation of CallMethodRule ensures this works by firing only at the end of the tag that - * CallMethodRule triggered on. - */ @Test - public void testOrderNestedPartA() + public void testFromStack() throws Exception { - final Digester digester = newLoader( new AbstractRulesModule() - { - @Override - protected void configure() - { - // Here, we use the "grandchild element name" as a parameter to - // the created element, to ensure that all the params aren't - // avaiable to the CallMethodRule until some other rules have fired, - // in particular an ObjectCreateRule. The CallMethodRule should still - // function correctly in this scenario. - forPattern( "toplevel/element" ).createObject().ofType( NamedBean.class ) - .then() - .callMethod( "setName" ).withParamCount( 1 ); - forPattern( "toplevel/element/element/element" ).callParam().ofIndex( 0 ).fromAttribute( "name" ); - forPattern( "toplevel/element/element" ).createObject().ofType( NamedBean.class ); - } + final StringReader reader = + new StringReader( "" ); - }).newDigester(); + final Digester digester = new Digester(); - // Parse our test input - // an exception will be thrown if the method can't be found - final NamedBean root1 = digester.parse( getInputStream( "Test8.xml" ) ); + final Class[] params = { String.class }; + digester.addObjectCreate( "root/one", NamedBean.class ); + digester.addSetNext( "root/one", "add" ); + digester.addCallMethod( "root/one", "setName", 1, params ); + digester.addCallParam( "root/one", 0, 2 ); - // if the CallMethodRule were to incorrectly invoke the method call - // on the second-created NamedBean instance, then the root one would - // have a null name. If it works correctly, the target element will - // be the first-created (root) one, despite the fact that a second - // object instance was created between the firing of the - // CallMethodRule and its associated CallParamRule. - assertEquals( "Wrong method call order", "C", root1.getName() ); - } + digester.addObjectCreate( "root/two", NamedBean.class ); + digester.addSetNext( "root/two", "add" ); + digester.addCallMethod( "root/two", "setName", 1, params ); + digester.addCallParam( "root/two", 0, 3 ); - /** - * Test nested CallMethod rules. - *

    - * The current implementation of CallMethodRule, in which the method is invoked in its end() method, causes - * behavior which some users find non-intuitive. In this test it can be seen to "reverse" the order of data - * processed. However this is the way CallMethodRule has always behaved, and it is expected that apps out there rely - * on this call order so this test is present to ensure that no-one changes this behavior. - */ - @Test - public void testOrderNestedPartB() - throws Exception - { - final Digester digester = newLoader( new AbstractRulesModule() - { - - @Override - protected void configure() - { - forPattern( "*/element" ).callMethod( "append" ).withParamCount( 1 ) - .then() - .callParam().ofIndex( 0 ).fromAttribute( "name" ); - } - - }).newDigester(); - - // Configure the digester as required - final StringBuilder word = new StringBuilder(); - digester.push( word ); - - // Parse our test input - Object root1 = null; - try - { - // an exception will be thrown if the method can't be found - root1 = digester.parse( getInputStream( "Test8.xml" ) ); - assertNotNull( root1 ); - } - catch ( final Throwable t ) - { - // this means that the method can't be found and so the test fails - fail( "Digester threw Exception: " + t ); - } - - assertEquals( "Wrong method call order", "CBA", word.toString() ); - } - - @Test - public void testPrimitiveReading() - throws Exception - { - final StringReader reader = - new StringReader( "" - + "" - + "" ); - - final Digester digester = new Digester(); - - // SimpleLog log = new SimpleLog("[testPrimitiveReading:Digester]"); - // log.setLevel(SimpleLog.LOG_LEVEL_TRACE); - // digester.setLogger(log); - - digester.addObjectCreate( "root/bean", PrimitiveBean.class ); - digester.addSetNext( "root/bean", "add" ); - final Class[] params = { Boolean.TYPE }; - digester.addCallMethod( "root/bean", "setBoolean", 1, params ); - digester.addCallParam( "root/bean", 0, "good" ); - - digester.addObjectCreate( "root/beanie", PrimitiveBean.class ); - digester.addSetNext( "root/beanie", "add" ); - final Class[] beanieParams = { String.class, Boolean.TYPE }; - digester.addCallMethod( "root/beanie", "testSetBoolean", 2, beanieParams ); - digester.addCallParam( "root/beanie", 0, "bad" ); - digester.addCallParam( "root/beanie", 1, "good" ); - - final ArrayList list = new ArrayList(); - digester.push( list ); - digester.parse( reader ); - - assertEquals( "Wrong number of beans in list", 6, list.size() ); - PrimitiveBean bean = list.get( 0 ); - assertTrue( "Bean 0 property not called", bean.getSetBooleanCalled() ); - assertEquals( "Bean 0 property incorrect", true, bean.getBoolean() ); - bean = list.get( 1 ); - assertTrue( "Bean 1 property not called", bean.getSetBooleanCalled() ); - assertEquals( "Bean 1 property incorrect", false, bean.getBoolean() ); - bean = list.get( 2 ); - // no attibute, no call is what's expected - assertTrue( "Bean 2 property called", !bean.getSetBooleanCalled() ); - bean = list.get( 3 ); - assertTrue( "Bean 3 property not called", bean.getSetBooleanCalled() ); - assertEquals( "Bean 3 property incorrect", true, bean.getBoolean() ); - bean = list.get( 4 ); - assertTrue( "Bean 4 property not called", bean.getSetBooleanCalled() ); - assertEquals( "Bean 4 property incorrect", false, bean.getBoolean() ); - bean = list.get( 5 ); - assertTrue( "Bean 5 property not called", bean.getSetBooleanCalled() ); - assertEquals( "Bean 5 property incorrect", false, bean.getBoolean() ); - } - - @Test - public void testFromStack() - throws Exception - { - - final StringReader reader = - new StringReader( "" ); - - final Digester digester = new Digester(); - - final Class[] params = { String.class }; - - digester.addObjectCreate( "root/one", NamedBean.class ); - digester.addSetNext( "root/one", "add" ); - digester.addCallMethod( "root/one", "setName", 1, params ); - digester.addCallParam( "root/one", 0, 2 ); - - digester.addObjectCreate( "root/two", NamedBean.class ); - digester.addSetNext( "root/two", "add" ); - digester.addCallMethod( "root/two", "setName", 1, params ); - digester.addCallParam( "root/two", 0, 3 ); - - digester.addObjectCreate( "root/three", NamedBean.class ); - digester.addSetNext( "root/three", "add" ); - digester.addCallMethod( "root/three", "setName", 1, params ); - digester.addCallParam( "root/three", 0, 4 ); + digester.addObjectCreate( "root/three", NamedBean.class ); + digester.addSetNext( "root/three", "add" ); + digester.addCallMethod( "root/three", "setName", 1, params ); + digester.addCallParam( "root/three", 0, 4 ); digester.addObjectCreate( "root/four", NamedBean.class ); digester.addSetNext( "root/four", "add" ); @@ -441,47 +274,6 @@ public void testFromStack() assertEquals( "Out of stack not set to null", null, bean.getName() ); } - @Test - public void testTwoCalls() - throws Exception - { - - final StringReader reader = - new StringReader( "" + "25" - + "50" + "90" ); - - final Digester digester = new Digester(); - // SimpleLog log = new SimpleLog("{testTwoCalls:Digester]"); - // log.setLevel(SimpleLog.LOG_LEVEL_TRACE); - // digester.setLogger(log); - - digester.addObjectCreate( "root/param", ParamBean.class ); - digester.addSetNext( "root/param", "add" ); - digester.addCallMethod( "root/param", "setThisAndThat", 2 ); - digester.addCallParam( "root/param", 0, "class" ); - digester.addCallParam( "root/param", 1 ); - digester.addCallMethod( "root/param", "setCool", 1, new Class[] { boolean.class } ); - digester.addCallParam( "root/param", 0, "coolness" ); - - final ArrayList list = new ArrayList(); - digester.push( list ); - digester.parse( reader ); - - assertEquals( "Wrong number of objects created", 3, list.size() ); - ParamBean bean = list.get( 0 ); - assertEquals( "Coolness wrong (1)", true, bean.isCool() ); - assertEquals( "This wrong (1)", "int", bean.getThis() ); - assertEquals( "That wrong (1)", "25", bean.getThat() ); - bean = list.get( 1 ); - assertEquals( "Coolness wrong (2)", false, bean.isCool() ); - assertEquals( "This wrong (2)", "long", bean.getThis() ); - assertEquals( "That wrong (2)", "50", bean.getThat() ); - bean = list.get( 2 ); - assertEquals( "Coolness wrong (3)", false, bean.isCool() ); - assertEquals( "This wrong (3)", "float", bean.getThis() ); - assertEquals( "That wrong (3)", "90", bean.getThat() ); - } - @Test public void testNestedBody() throws Exception @@ -534,183 +326,391 @@ public void testNestedBody() assertEquals( "Wrong name (5)", "Deepest", bean.getName() ); } + /** + * Test that the target object for a CallMethodRule is the object that was on top of the object stack when the + * CallMethodRule fired, even when other rules fire between the CallMethodRule and its associated CallParamRules. + *

    + * The current implementation of CallMethodRule ensures this works by firing only at the end of the tag that + * CallMethodRule triggered on. + */ @Test - public void testProcessingHook() + public void testOrderNestedPartA() throws Exception { - - class TestCallMethodRule - extends CallMethodRule + final Digester digester = newLoader( new AbstractRulesModule() { - Object result; - - TestCallMethodRule( final String methodName, final int paramCount ) - { - super( methodName, paramCount ); - } @Override - protected void processMethodCallResult( final Object result ) + protected void configure() { - this.result = result; + // Here, we use the "grandchild element name" as a parameter to + // the created element, to ensure that all the params aren't + // avaiable to the CallMethodRule until some other rules have fired, + // in particular an ObjectCreateRule. The CallMethodRule should still + // function correctly in this scenario. + forPattern( "toplevel/element" ).createObject().ofType( NamedBean.class ) + .then() + .callMethod( "setName" ).withParamCount( 1 ); + forPattern( "toplevel/element/element/element" ).callParam().ofIndex( 0 ).fromAttribute( "name" ); + forPattern( "toplevel/element/element" ).createObject().ofType( NamedBean.class ); } - } - - final StringReader reader = - new StringReader( "" - + "90" ); - final Digester digester = new Digester(); - // SimpleLog log = new SimpleLog("{testTwoCalls:Digester]"); - // log.setLevel(SimpleLog.LOG_LEVEL_TRACE); - // digester.setLogger(log); + }).newDigester(); - digester.addObjectCreate( "root/param", ParamBean.class ); - digester.addSetNext( "root/param", "add" ); - final TestCallMethodRule rule = new TestCallMethodRule( "setThisAndThat", 2 ); - digester.addRule( "root/param", rule ); - digester.addCallParam( "root/param", 0, "class" ); - digester.addCallParam( "root/param", 1, "coolness" ); + // Parse our test input + // an exception will be thrown if the method can't be found + final NamedBean root1 = digester.parse( getInputStream( "Test8.xml" ) ); - final ArrayList list = new ArrayList(); - digester.push( list ); - digester.parse( reader ); - assertEquals( "Wrong number of objects created", 1, list.size() ); - assertEquals( "Result not passed into hook", "The Other", rule.result ); + // if the CallMethodRule were to incorrectly invoke the method call + // on the second-created NamedBean instance, then the root one would + // have a null name. If it works correctly, the target element will + // be the first-created (root) one, despite the fact that a second + // object instance was created between the firing of the + // CallMethodRule and its associated CallParamRule. + assertEquals( "Wrong method call order", "C", root1.getName() ); } - /** Test for the PathCallParamRule */ + /** + * Test nested CallMethod rules. + *

    + * The current implementation of CallMethodRule, in which the method is invoked in its end() method, causes + * behavior which some users find non-intuitive. In this test it can be seen to "reverse" the order of data + * processed. However this is the way CallMethodRule has always behaved, and it is expected that apps out there rely + * on this call order so this test is present to ensure that no-one changes this behavior. + */ @Test - public void testPathCallParam() + public void testOrderNestedPartB() throws Exception { - final String xml = - "

    " + "Ignore this" - + "Ignore that" + "
    "; - - final SimpleTestBean bean = new SimpleTestBean(); - bean.setAlphaBeta( "[UNSET]", "[UNSET]" ); + final Digester digester = newLoader( new AbstractRulesModule() + { - final StringReader in = new StringReader( xml ); - final Digester digester = new Digester(); - digester.setRules( new ExtendedBaseRules() ); - digester.addCallParamPath( "*/alpha/?", 0 ); - digester.addCallParamPath( "*/epsilon/?", 1 ); - digester.addCallMethod( "main", "setAlphaBeta", 2 ); + @Override + protected void configure() + { + forPattern( "*/element" ).callMethod( "append" ).withParamCount( 1 ) + .then() + .callParam().ofIndex( 0 ).fromAttribute( "name" ); + } - digester.push( bean ); + }).newDigester(); - digester.parse( in ); + // Configure the digester as required + final StringBuilder word = new StringBuilder(); + digester.push( word ); - assertEquals( "Test alpha property setting", "main/alpha/beta", bean.getAlpha() ); - assertEquals( "Test beta property setting", "main/beta/epsilon/gamma", bean.getBeta() ); - } + // Parse our test input + Object root1 = null; + try + { + // an exception will be thrown if the method can't be found + root1 = digester.parse( getInputStream( "Test8.xml" ) ); + assertNotNull( root1 ); + } + catch ( final Throwable t ) + { + // this means that the method can't be found and so the test fails + fail( "Digester threw Exception: " + t ); + } + + assertEquals( "Wrong method call order", "CBA", word.toString() ); + } /** - * Test invoking an object which does not exist on the stack. + * This tests the call methods params enhancement that provides for more complex stack-based calls. */ @Test - public void testCallInvalidTarget() + public void testParamsFromStack() + throws SAXException, IOException + { + final Digester digester = newLoader( new AbstractRulesModule() + { + + @Override + protected void configure() + { + forPattern( "map" ).createObject().ofType( HashMap.class ) + .then() + .callMethod( "put" ).withParamCount( 2 ); + forPattern( "map/key" ).createObject().ofType( AlphaBean.class ) + .then() + .setProperties() + .then() + .callParam().fromStack( true ); + forPattern( "map/value" ).createObject().ofType( BetaBean.class ) + .then() + .setProperties() + .then() + .callParam().ofIndex( 1 ).fromStack( true ); + } + + }).newDigester(); + + final StringBuilder xml = + new StringBuilder().append( "" ).append( "" ).append( " " ).append( " " ).append( "" ); + + final HashMap map = digester.parse( new StringReader( xml.toString() ) ); + + assertNotNull( map ); + assertEquals( 1, map.size() ); + assertEquals( "The key", map.keySet().iterator().next().getName() ); + assertEquals( "The value", map.values().iterator().next().getName() ); + } + + /** Test for the PathCallParamRule */ + @Test + public void testPathCallParam() throws Exception { + final String xml = + "
    " + "Ignore this" + + "Ignore that" + "
    "; + + final SimpleTestBean bean = new SimpleTestBean(); + bean.setAlphaBeta( "[UNSET]", "[UNSET]" ); + final StringReader in = new StringReader( xml ); final Digester digester = new Digester(); - digester.addObjectCreate( "employee", HashMap.class ); + digester.setRules( new ExtendedBaseRules() ); + digester.addCallParamPath( "*/alpha/?", 0 ); + digester.addCallParamPath( "*/epsilon/?", 1 ); + digester.addCallMethod( "main", "setAlphaBeta", 2 ); - // there should be only one object on the stack (index zero), - // so selecting a target object with index 1 on the object stack - // should result in an exception. - final CallMethodRule r = new CallMethodRule( 1, "put", 0 ); - digester.addRule( "employee", r ); + digester.push( bean ); - try - { - digester.parse( getInputStream( "Test5.xml" ) ); - fail( "Exception should be thrown for invalid target offset" ); - } - catch ( final SAXException e ) - { - // ok, exception expected - } + digester.parse( in ); + + assertEquals( "Test alpha property setting", "main/alpha/beta", bean.getAlpha() ); + assertEquals( "Test beta property setting", "main/beta/epsilon/gamma", bean.getBeta() ); } - /** - * Test invoking an object which is at top-1 on the stack, like SetNextRule does... - */ @Test - public void testCallNext() + public void testPrimitiveReading() throws Exception { + final StringReader reader = + new StringReader( "" + + "" + + "" ); final Digester digester = new Digester(); - digester.addObjectCreate( "employee", HashMap.class ); - digester.addObjectCreate( "employee/address", Address.class ); - digester.addSetNestedProperties( "employee/address" ); - final CallMethodRule r = new CallMethodRule( 1, "put", 2 ); - digester.addRule( "employee/address", r ); - digester.addCallParam( "employee/address/type", 0 ); - digester.addCallParam( "employee/address", 1, 0 ); + // SimpleLog log = new SimpleLog("[testPrimitiveReading:Digester]"); + // log.setLevel(SimpleLog.LOG_LEVEL_TRACE); + // digester.setLogger(log); - final HashMap map = digester.parse( getInputStream( "Test5.xml" ) ); + digester.addObjectCreate( "root/bean", PrimitiveBean.class ); + digester.addSetNext( "root/bean", "add" ); + final Class[] params = { Boolean.TYPE }; + digester.addCallMethod( "root/bean", "setBoolean", 1, params ); + digester.addCallParam( "root/bean", 0, "good" ); - assertNotNull( map ); - final Set keys = map.keySet(); - assertEquals( 2, keys.size() ); - final Address home = map.get( "home" ); - assertNotNull( home ); - assertEquals( "HmZip", home.getZipCode() ); - final Address office = map.get( "office" ); - assertNotNull( office ); - assertEquals( "OfZip", office.getZipCode() ); + digester.addObjectCreate( "root/beanie", PrimitiveBean.class ); + digester.addSetNext( "root/beanie", "add" ); + final Class[] beanieParams = { String.class, Boolean.TYPE }; + digester.addCallMethod( "root/beanie", "testSetBoolean", 2, beanieParams ); + digester.addCallParam( "root/beanie", 0, "bad" ); + digester.addCallParam( "root/beanie", 1, "good" ); + + final ArrayList list = new ArrayList(); + digester.push( list ); + digester.parse( reader ); + + assertEquals( "Wrong number of beans in list", 6, list.size() ); + PrimitiveBean bean = list.get( 0 ); + assertTrue( "Bean 0 property not called", bean.getSetBooleanCalled() ); + assertEquals( "Bean 0 property incorrect", true, bean.getBoolean() ); + bean = list.get( 1 ); + assertTrue( "Bean 1 property not called", bean.getSetBooleanCalled() ); + assertEquals( "Bean 1 property incorrect", false, bean.getBoolean() ); + bean = list.get( 2 ); + // no attibute, no call is what's expected + assertTrue( "Bean 2 property called", !bean.getSetBooleanCalled() ); + bean = list.get( 3 ); + assertTrue( "Bean 3 property not called", bean.getSetBooleanCalled() ); + assertEquals( "Bean 3 property incorrect", true, bean.getBoolean() ); + bean = list.get( 4 ); + assertTrue( "Bean 4 property not called", bean.getSetBooleanCalled() ); + assertEquals( "Bean 4 property incorrect", false, bean.getBoolean() ); + bean = list.get( 5 ); + assertTrue( "Bean 5 property not called", bean.getSetBooleanCalled() ); + assertEquals( "Bean 5 property incorrect", false, bean.getBoolean() ); } - /** - * Test invoking an object which is at the root of the stack, like SetRoot does... - */ @Test - public void testCallRoot() + public void testProcessingHook() throws Exception { + class TestCallMethodRule + extends CallMethodRule + { + Object result; + + TestCallMethodRule( final String methodName, final int paramCount ) + { + super( methodName, paramCount ); + } + + @Override + protected void processMethodCallResult( final Object result ) + { + this.result = result; + } + } + + final StringReader reader = + new StringReader( "" + + "90" ); + final Digester digester = new Digester(); - digester.addObjectCreate( "employee", HashMap.class ); + // SimpleLog log = new SimpleLog("{testTwoCalls:Digester]"); + // log.setLevel(SimpleLog.LOG_LEVEL_TRACE); + // digester.setLogger(log); - digester.addObjectCreate( "employee/address", Address.class ); - digester.addSetNestedProperties( "employee/address" ); - final CallMethodRule r = new CallMethodRule( -1, "put", 2 ); - digester.addRule( "employee/address", r ); - digester.addCallParam( "employee/address/type", 0 ); - digester.addCallParam( "employee/address", 1, 0 ); + digester.addObjectCreate( "root/param", ParamBean.class ); + digester.addSetNext( "root/param", "add" ); + final TestCallMethodRule rule = new TestCallMethodRule( "setThisAndThat", 2 ); + digester.addRule( "root/param", rule ); + digester.addCallParam( "root/param", 0, "class" ); + digester.addCallParam( "root/param", 1, "coolness" ); - final HashMap map = digester.parse( getInputStream( "Test5.xml" ) ); + final ArrayList list = new ArrayList(); + digester.push( list ); + digester.parse( reader ); - assertNotNull( map ); - final Set keys = map.keySet(); - assertEquals( 2, keys.size() ); - final Address home = map.get( "home" ); - assertNotNull( home ); - assertEquals( "HmZip", home.getZipCode() ); - final Address office = map.get( "office" ); - assertNotNull( office ); - assertEquals( "OfZip", office.getZipCode() ); + assertEquals( "Wrong number of objects created", 1, list.size() ); + assertEquals( "Result not passed into hook", "The Other", rule.result ); } - // ------------------------------------------------ Utility Support Methods - /** - * Return an appropriate InputStream for the specified test file (which must be inside our current package. - * - * @param name Name of the test file we want - * @throws IOException if an input/output error occurs + * Test CallMethodRule variants which specify the classes of the parameters to target methods. String, int, boolean, + * float should all be acceptable as parameter types. */ - protected InputStream getInputStream( final String name ) - throws IOException + @Test + public void testSettingProperties() + throws SAXException, IOException { + Digester digester = newLoader( new AbstractRulesModule() + { - return ( this.getClass().getResourceAsStream( "/org/apache/commons/digester3/" + name ) ); + @Override + protected void configure() + { + forPattern( "employee" ).createObject().ofType( Employee.class ) + .then() + .callMethod( "setLastName" ).withParamTypes( "java.lang.String" ); + forPattern( "employee/lastName" ).callParam().ofIndex( 0 ); + } + + }).newDigester(); + + // Parse our test input + + // an exception will be thrown if the method can't be found + Employee employee = digester.parse( getInputStream( "Test5.xml" ) ); + assertEquals( "Failed to call Employee.setLastName", "Last Name", employee.getLastName() ); + + digester = newLoader( new AbstractRulesModule() + { + + @Override + protected void configure() + { + forPattern( "employee" ).createObject().ofType( Employee.class ) + .then() + .callMethod( "setAge" ).withParamTypes( int.class ); + forPattern( "employee/age" ).callParam(); + } + + }).newDigester(); + // Parse our test input + // an exception will be thrown if the method can't be found + employee = digester.parse( getInputStream( "Test5.xml" ) ); + assertEquals( "Failed to call Employee.setAge", 21, employee.getAge() ); + + digester = newLoader( new AbstractRulesModule() + { + + @Override + protected void configure() + { + forPattern( "employee" ).createObject().ofType( Employee.class ) + .then() + .callMethod( "setActive" ).withParamTypes( boolean.class ); + forPattern( "employee/active" ).callParam(); + } + + }).newDigester(); + + // Parse our test input + // an exception will be thrown if the method can't be found + employee = digester.parse( getInputStream( "Test5.xml" ) ); + assertEquals( "Failed to call Employee.setActive", true, employee.isActive() ); + + digester = newLoader( new AbstractRulesModule() + { + + @Override + protected void configure() + { + forPattern( "employee" ).createObject().ofType( Employee.class ) + .then() + .callMethod( "setSalary" ).withParamTypes( float.class ); + forPattern( "employee/salary" ).callParam(); + } + + }).newDigester(); + + // Parse our test input + // an exception will be thrown if the method can't be found + employee = digester.parse( getInputStream( "Test5.xml" ) ); + assertEquals( "Failed to call Employee.setSalary", 1000000.0f, employee.getSalary(), 0.1f ); + } + + // ------------------------------------------------ Utility Support Methods + + @Test + public void testTwoCalls() + throws Exception + { + + final StringReader reader = + new StringReader( "" + "25" + + "50" + "90" ); + + final Digester digester = new Digester(); + // SimpleLog log = new SimpleLog("{testTwoCalls:Digester]"); + // log.setLevel(SimpleLog.LOG_LEVEL_TRACE); + // digester.setLogger(log); + + digester.addObjectCreate( "root/param", ParamBean.class ); + digester.addSetNext( "root/param", "add" ); + digester.addCallMethod( "root/param", "setThisAndThat", 2 ); + digester.addCallParam( "root/param", 0, "class" ); + digester.addCallParam( "root/param", 1 ); + digester.addCallMethod( "root/param", "setCool", 1, new Class[] { boolean.class } ); + digester.addCallParam( "root/param", 0, "coolness" ); + + final ArrayList list = new ArrayList(); + digester.push( list ); + digester.parse( reader ); + + assertEquals( "Wrong number of objects created", 3, list.size() ); + ParamBean bean = list.get( 0 ); + assertEquals( "Coolness wrong (1)", true, bean.isCool() ); + assertEquals( "This wrong (1)", "int", bean.getThis() ); + assertEquals( "That wrong (1)", "25", bean.getThat() ); + bean = list.get( 1 ); + assertEquals( "Coolness wrong (2)", false, bean.isCool() ); + assertEquals( "This wrong (2)", "long", bean.getThis() ); + assertEquals( "That wrong (2)", "50", bean.getThat() ); + bean = list.get( 2 ); + assertEquals( "Coolness wrong (3)", false, bean.isCool() ); + assertEquals( "This wrong (3)", "float", bean.getThis() ); + assertEquals( "That wrong (3)", "90", bean.getThat() ); } } diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/DTDValidationTestCase.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/DTDValidationTestCase.java index baeba3c35..65f9940fa 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/DTDValidationTestCase.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/DTDValidationTestCase.java @@ -50,7 +50,7 @@ protected void configure() { @Override - public void warning( final SAXParseException e ) + public void error( final SAXParseException e ) throws SAXException { throw e; @@ -64,7 +64,7 @@ public void fatalError( final SAXParseException e ) } @Override - public void error( final SAXParseException e ) + public void warning( final SAXParseException e ) throws SAXException { throw e; @@ -76,61 +76,61 @@ public void error( final SAXParseException e ) } @Test - public void testDigesterNoDTDValidation() + public void testDigesterLoaderFeatureDisabled() throws Exception { - newLoader( new AbstractRulesModule() + newLoader( new AbstractRulesModule() { - @Override + @Override protected void configure() { // do nothing } } ) - .setValidating( false ) + .setFeature("http://xml.org/sax/features/validation", false) + .setFeature("http://xml.org/sax/features/external-parameter-entities", false) + .setFeature("http://apache.org/xml/features/nonvalidating/load-dtd-grammar", false) + .setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false) .newDigester() .parse( new File( "src/test/resources/org/apache/commons/digester3/document-with-relative-dtd-error.xml" ) ); } @Test - public void testDigesterValidation() + public void testDigesterNoDTDValidation() throws Exception { newLoader( new AbstractRulesModule() { + @Override protected void configure() { // do nothing } + } ) - .setValidating( true ) + .setValidating( false ) .newDigester() - .parse( new File( "src/test/resources/org/apache/commons/digester3/document-with-relative-dtd.xml" ) ); + .parse( new File( "src/test/resources/org/apache/commons/digester3/document-with-relative-dtd-error.xml" ) ); } @Test - public void testDigesterLoaderFeatureDisabled() + public void testDigesterValidation() throws Exception { - newLoader( new AbstractRulesModule() + newLoader( new AbstractRulesModule() { - - @Override + @Override protected void configure() { // do nothing } - } ) - .setFeature("http://xml.org/sax/features/validation", false) - .setFeature("http://xml.org/sax/features/external-parameter-entities", false) - .setFeature("http://apache.org/xml/features/nonvalidating/load-dtd-grammar", false) - .setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false) + .setValidating( true ) .newDigester() - .parse( new File( "src/test/resources/org/apache/commons/digester3/document-with-relative-dtd-error.xml" ) ); + .parse( new File( "src/test/resources/org/apache/commons/digester3/document-with-relative-dtd.xml" ) ); } } diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/Digester153TestCase.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/Digester153TestCase.java index c0bda03d8..80b67d03a 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/Digester153TestCase.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/Digester153TestCase.java @@ -37,32 +37,32 @@ public final class Digester153TestCase { - @Test - public void testBasicConstructor() + @Test( expected = SAXParseException.class ) + public void basicConstructorWithWrongParameters() throws Exception { final ObjectCreateRule createRule = new ObjectCreateRule( TestBean.class ); - createRule.setConstructorArgumentTypes( boolean.class, double.class ); + createRule.setConstructorArgumentTypes( boolean.class ); final Digester digester = new Digester(); digester.addRule( "toplevel/bean", createRule ); - digester.addCallParam( "toplevel/bean", 0, "boolean" ); - digester.addCallParam( "toplevel/bean", 1, "double" ); - - TestBean bean = digester.parse( getClass().getResourceAsStream( "BasicConstructor.xml" ) ); - assertTrue( bean.getBooleanProperty() ); - assertEquals( 9.99D, bean.getDoubleProperty(), 0 ); + digester.parse( getClass().getResourceAsStream( "BasicConstructor.xml" ) ); + } - // do it again to exercise the cglib Factory: - bean = digester.parse( getClass().getResourceAsStream( "BasicConstructor.xml" ) ); + private void succesfullConstructor( final RulesModule rulesModule ) + throws Exception + { + final TestBean bean = newLoader( rulesModule ) + .newDigester() + .parse( getClass().getResourceAsStream( "BasicConstructor.xml" ) ); assertTrue( bean.getBooleanProperty() ); assertEquals( 9.99D, bean.getDoubleProperty(), 0 ); } @Test - public void testConstructorWithAttributeAndElement() + public void testBasicConstructor() throws Exception { final ObjectCreateRule createRule = new ObjectCreateRule( TestBean.class ); @@ -71,55 +71,52 @@ public void testConstructorWithAttributeAndElement() final Digester digester = new Digester(); digester.addRule( "toplevel/bean", createRule ); digester.addCallParam( "toplevel/bean", 0, "boolean" ); - digester.addCallParam( "toplevel/bean/double", 1 ); - digester.addBeanPropertySetter("toplevel/bean/float", "floatProperty"); + digester.addCallParam( "toplevel/bean", 1, "double" ); - TestBean bean = digester.parse( getClass().getResourceAsStream( "ConstructorWithAttributeAndElement.xml" ) ); + TestBean bean = digester.parse( getClass().getResourceAsStream( "BasicConstructor.xml" ) ); assertTrue( bean.getBooleanProperty() ); assertEquals( 9.99D, bean.getDoubleProperty(), 0 ); - assertEquals( Float.valueOf( 5.5f ), Float.valueOf( bean.getFloatProperty() ) ); // do it again to exercise the cglib Factory: - bean = digester.parse( getClass().getResourceAsStream( "ConstructorWithAttributeAndElement.xml" ) ); + bean = digester.parse( getClass().getResourceAsStream( "BasicConstructor.xml" ) ); assertTrue( bean.getBooleanProperty() ); assertEquals( 9.99D, bean.getDoubleProperty(), 0 ); - assertEquals( Float.valueOf( 5.5f ), Float.valueOf( bean.getFloatProperty() ) ); } @Test - public void testBasicConstructorViaBinder() + public void testBasicConstructorViaAnnotations() throws Exception { - succesfullConstructor( new AbstractRulesModule() + succesfullConstructor( new FromAnnotationsRuleModule() { @Override - protected void configure() + protected void configureRules() { - forPattern( "toplevel/bean" ) - .createObject().ofType( TestBean.class ).usingConstructor( boolean.class, double.class ) - .then() - .callParam().fromAttribute( "boolean" ).ofIndex( 0 ) - .then() - .callParam().fromAttribute( "double" ).ofIndex( 1 ); + bindRulesFrom( TestBean.class ); } } ); } @Test - public void testBasicConstructorViaAnnotations() + public void testBasicConstructorViaBinder() throws Exception { - succesfullConstructor( new FromAnnotationsRuleModule() + succesfullConstructor( new AbstractRulesModule() { @Override - protected void configureRules() + protected void configure() { - bindRulesFrom( TestBean.class ); + forPattern( "toplevel/bean" ) + .createObject().ofType( TestBean.class ).usingConstructor( boolean.class, double.class ) + .then() + .callParam().fromAttribute( "boolean" ).ofIndex( 0 ) + .then() + .callParam().fromAttribute( "double" ).ofIndex( 1 ); } } ); @@ -141,17 +138,6 @@ protected void loadRules() } ); } - private void succesfullConstructor( final RulesModule rulesModule ) - throws Exception - { - final TestBean bean = newLoader( rulesModule ) - .newDigester() - .parse( getClass().getResourceAsStream( "BasicConstructor.xml" ) ); - - assertTrue( bean.getBooleanProperty() ); - assertEquals( 9.99D, bean.getDoubleProperty(), 0 ); - } - @Test public void testBasicConstructorWithValuesNotFound() throws Exception @@ -170,17 +156,31 @@ public void testBasicConstructorWithValuesNotFound() assertEquals( 0D, bean.getDoubleProperty(), 0 ); } - @Test( expected = SAXParseException.class ) - public void basicConstructorWithWrongParameters() + @Test + public void testConstructorWithAttributeAndElement() throws Exception { final ObjectCreateRule createRule = new ObjectCreateRule( TestBean.class ); - createRule.setConstructorArgumentTypes( boolean.class ); + createRule.setConstructorArgumentTypes( boolean.class, double.class ); final Digester digester = new Digester(); digester.addRule( "toplevel/bean", createRule ); + digester.addCallParam( "toplevel/bean", 0, "boolean" ); + digester.addCallParam( "toplevel/bean/double", 1 ); + digester.addBeanPropertySetter("toplevel/bean/float", "floatProperty"); - digester.parse( getClass().getResourceAsStream( "BasicConstructor.xml" ) ); + TestBean bean = digester.parse( getClass().getResourceAsStream( "ConstructorWithAttributeAndElement.xml" ) ); + + assertTrue( bean.getBooleanProperty() ); + assertEquals( 9.99D, bean.getDoubleProperty(), 0 ); + assertEquals( Float.valueOf( 5.5f ), Float.valueOf( bean.getFloatProperty() ) ); + + // do it again to exercise the cglib Factory: + bean = digester.parse( getClass().getResourceAsStream( "ConstructorWithAttributeAndElement.xml" ) ); + + assertTrue( bean.getBooleanProperty() ); + assertEquals( 9.99D, bean.getDoubleProperty(), 0 ); + assertEquals( Float.valueOf( 5.5f ), Float.valueOf( bean.getFloatProperty() ) ); } @Test diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/Digester171TestCase.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/Digester171TestCase.java index 286b12a91..91e5d3127 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/Digester171TestCase.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/Digester171TestCase.java @@ -31,10 +31,12 @@ public class Digester171TestCase { - @Test - public void testNoErrorHandler() + @Test( expected = SAXParseException.class ) + public void testDefaultThrowingErrorHandler() throws Exception { + final ErrorHandler customErrorHandler = new DefaultThrowingErrorHandler(); + newLoader( new AbstractRulesModule() { @@ -46,16 +48,15 @@ protected void configure() } ) .setFeature( "http://xml.org/sax/features/validation", true ) + .setErrorHandler( customErrorHandler ) .newDigester() .parse( new File( "src/test/resources/org/apache/commons/digester3/document-with-relative-dtd-error.xml" ) ); } - @Test( expected = SAXParseException.class ) - public void testDefaultThrowingErrorHandler() + @Test + public void testNoErrorHandler() throws Exception { - final ErrorHandler customErrorHandler = new DefaultThrowingErrorHandler(); - newLoader( new AbstractRulesModule() { @@ -67,7 +68,6 @@ protected void configure() } ) .setFeature( "http://xml.org/sax/features/validation", true ) - .setErrorHandler( customErrorHandler ) .newDigester() .parse( new File( "src/test/resources/org/apache/commons/digester3/document-with-relative-dtd-error.xml" ) ); } diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/DigesterTestCase.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/DigesterTestCase.java index 1ba854df3..20d18454f 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/DigesterTestCase.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/DigesterTestCase.java @@ -55,10 +55,39 @@ public class DigesterTestCase // ----------------------------------------------------- Instance Variables - /** - * The digester instance we will be processing. - */ - protected Digester digester; + /** Utility class for method testStackAction */ + private static final class TrackingStackAction + implements StackAction + { + public ArrayList events = new ArrayList(); + + @Override + public Object onPop( final Digester d, final String stackName, final Object o ) + { + final String msg = "pop:" + stackName + ":" + o.toString(); + events.add( msg ); + final String str = o.toString(); + if ( str.startsWith( "replpop" ) ) + { + return new String( str ); + } + return o; + } + + @Override + public Object onPush( final Digester d, final String stackName, final Object o ) + { + final String msg = "push:" + stackName + ":" + o.toString(); + events.add( msg ); + + final String str = o.toString(); + if ( str.startsWith( "replpush" ) ) + { + return new String( str ); + } + return o; + } + } /** * The set of public identifiers, and corresponding resource names, for the versions of the DTDs that we know about. @@ -70,6 +99,11 @@ public class DigesterTestCase // -------------------------------------------------- Overall Test Methods + /** + * The digester instance we will be processing. + */ + protected Digester digester; + /** * Sets up instance variables required by this test case. */ @@ -82,6 +116,8 @@ public void setUp() } + // ------------------------------------------------ Individual Test Methods + /** * Tear down instance variables required by this test case. */ @@ -93,7 +129,170 @@ public void tearDown() } - // ------------------------------------------------ Individual Test Methods + @Test + public void testBasicSubstitution() + throws Exception + { + class TestSubRule + extends Rule + { + public String body; + + public Attributes attributes; + + @Override + public void begin( final String namespace, final String name, final Attributes attributes ) + { + this.attributes = new AttributesImpl( attributes ); + } + + @Override + public void body( final String namespace, final String name, final String text ) + { + this.body = text; + } + } + + final TestSubRule tsr = new TestSubRule(); + final Digester digester = new Digester(); + digester.addRule( "alpha/beta", tsr ); + + // it's not easy to transform dirty harry into the mighty circus - but let's give it a try + final String xml = + "Do you feel luck punk?"; + InputSource in = new InputSource( new StringReader( xml ) ); + + digester.parse( in ); + + assertEquals( "Unsubstituted body text", "Do you feel luck punk?", tsr.body ); + assertEquals( "Unsubstituted number of attributes", 2, tsr.attributes.getLength() ); + assertEquals( "Unsubstituted forname attribute value", "Dirty", tsr.attributes.getValue( "forname" ) ); + assertEquals( "Unsubstituted surname attribute value", "Harry", tsr.attributes.getValue( "surname" ) ); + + digester.setSubstitutor( new Substitutor() + { + @Override + public Attributes substitute( final Attributes attributes ) + { + final AttributesImpl results = new AttributesImpl(); + results.addAttribute( "", "python", "python", "CDATA", "Cleese" ); + return results; + } + + @Override + public String substitute( final String bodyText ) + { + return "And now for something completely different..."; + } + } ); + + // now transform into the full monty + in = new InputSource( new StringReader( xml ) ); + digester.parse( in ); + + assertEquals( "Substituted body text", "And now for something completely different...", tsr.body ); + assertEquals( "Substituted number of attributes", 1, tsr.attributes.getLength() ); + assertEquals( "Substituted python attribute value", "Cleese", tsr.attributes.getValue( "", "python" ) ); + } + + /** + * Test the Digester.getRoot method. + */ + @Test + public void testGetRoot() + throws Exception + { + final Digester digester = new Digester(); + digester.addRule( "root", new ObjectCreateRule( TestBean.class ) ); + + final String xml = ""; + final InputSource in = new InputSource( new StringReader( xml ) ); + + digester.parse( in ); + + final Object root = digester.getRoot(); + assertNotNull( "root object not retrieved", root ); + assertTrue( "root object not a TestRule instance", ( root instanceof TestBean ) ); + } + + /** Tests that values are stored independently */ + @Test + public void testNamedIndependence() + { + final String testStackOneName = "org.apache.commons.digester3.tests.testNamedIndependenceOne"; + final String testStackTwoName = "org.apache.commons.digester3.tests.testNamedIndependenceTwo"; + final Digester digester = new Digester(); + digester.push( testStackOneName, "Tweedledum" ); + digester.push( testStackTwoName, "Tweedledee" ); + assertEquals( "Popped value one:", "Tweedledum", digester.pop( testStackOneName ) ); + assertEquals( "Popped value two:", "Tweedledee", digester.pop( testStackTwoName ) ); + } + + /** Tests for isEmpty */ + @Test + public void testNamedStackIsEmpty() + { + final String testStackName = "org.apache.commons.digester3.tests.testNamedStackIsEmpty"; + final Digester digester = new Digester(); + assertTrue( "A named stack that has no object pushed onto it yet should be empty", + digester.isEmpty( testStackName ) ); + + digester.push( testStackName, "Some test value" ); + assertFalse( "A named stack that has an object pushed onto it should be not empty", + digester.isEmpty( testStackName ) ); + + digester.peek( testStackName ); + assertFalse( "Peek should not effect whether the stack is empty", digester.isEmpty( testStackName ) ); + + digester.pop( testStackName ); + assertTrue( "A named stack that has it's last object popped is empty", digester.isEmpty( testStackName ) ); + } + + /** Tests the push-peek-pop cycle for a named stack */ + @Test + public void testNamedStackPushPeekPop() + throws Exception + { + final BigDecimal archimedesAveragePi = new BigDecimal( "3.1418" ); + final String testStackName = "org.apache.commons.digester3.tests.testNamedStackPushPeekPop"; + final Digester digester = new Digester(); + assertTrue( "Stack starts empty:", digester.isEmpty( testStackName ) ); + digester.push( testStackName, archimedesAveragePi ); + assertEquals( "Peeked value:", archimedesAveragePi, digester.peek( testStackName ) ); + assertEquals( "Popped value:", archimedesAveragePi, digester.pop( testStackName ) ); + assertTrue( "Stack ends empty:", digester.isEmpty( testStackName ) ); + + digester.push( testStackName, "1" ); + digester.push( testStackName, "2" ); + digester.push( testStackName, "3" ); + + assertEquals( "Peek#1", "1", digester.peek( testStackName, 2 ) ); + assertEquals( "Peek#2", "2", digester.peek( testStackName, 1 ) ); + assertEquals( "Peek#3", "3", digester.peek( testStackName, 0 ) ); + assertEquals( "Peek#3a", "3", digester.peek( testStackName ) ); + + try + { + // peek beyond stack + digester.peek( testStackName, 3 ); + fail( "Peek#4 failed to throw an exception." ); + } + catch ( final EmptyStackException ex ) + { + // ok, expected + } + + try + { + // peek a nonexistent named stack + digester.peek( "no.such.stack", 0 ); + fail( "Peeking a non-existent stack failed to throw an exception." ); + } + catch ( final EmptyStackException ex ) + { + // ok, expected + } + } /** * Test {@code null} parsing. (should lead to {@code IllegalArgumentException}s) @@ -200,40 +399,100 @@ public void testNullURLParse() } - /** - * Test the basic property getters and setters. - */ @Test - public void testProperties() + public void testOnceAndOnceOnly() + throws Exception { - assertNull( "Initial error handler is null", digester.getErrorHandler() ); - digester.setErrorHandler( digester ); - assertTrue( "Set error handler is digester", digester.getErrorHandler() == digester ); - digester.setErrorHandler( null ); - assertNull( "Reset error handler is null", digester.getErrorHandler() ); + class TestConfigureDigester + extends Digester + { + public int called; - assertTrue( "Initial namespace aware is false", !digester.getNamespaceAware() ); - digester.setNamespaceAware( true ); - assertTrue( "Set namespace aware is true", digester.getNamespaceAware() ); - digester.setNamespaceAware( false ); - assertTrue( "Reset namespace aware is false", !digester.getNamespaceAware() ); + public TestConfigureDigester() + { + } - assertTrue( "Initial validating is false", !digester.getValidating() ); - digester.setValidating( true ); - assertTrue( "Set validating is true", digester.getValidating() ); - digester.setValidating( false ); - assertTrue( "Reset validating is false", !digester.getValidating() ); + @Override + protected void initialize() + { + called++; + } + } + + final TestConfigureDigester digester = new TestConfigureDigester(); + + final String xml = ""; + digester.parse( new StringReader( xml ) ); + assertEquals( "Initialize should be called once and only once", 1, digester.called ); } - /** - * Test registration of URLs for specified public identifiers. - */ + /** Tests popping named stack not yet pushed */ @Test - public void testRegistrations() + public void testPopNamedStackNotPushed() { - + final String testStackName = "org.apache.commons.digester3.tests.testPopNamedStackNotPushed"; + final Digester digester = new Digester(); + try + { + + digester.pop( testStackName ); + fail( "Expected an EmptyStackException" ); + + } + catch ( final EmptyStackException e ) + { + // expected + } + + try + { + + digester.peek( testStackName ); + fail( "Expected an EmptyStackException" ); + + } + catch ( final EmptyStackException e ) + { + // expected + } + } + + /** + * Test the basic property getters and setters. + */ + @Test + public void testProperties() + { + + assertNull( "Initial error handler is null", digester.getErrorHandler() ); + digester.setErrorHandler( digester ); + assertTrue( "Set error handler is digester", digester.getErrorHandler() == digester ); + digester.setErrorHandler( null ); + assertNull( "Reset error handler is null", digester.getErrorHandler() ); + + assertTrue( "Initial namespace aware is false", !digester.getNamespaceAware() ); + digester.setNamespaceAware( true ); + assertTrue( "Set namespace aware is true", digester.getNamespaceAware() ); + digester.setNamespaceAware( false ); + assertTrue( "Reset namespace aware is false", !digester.getNamespaceAware() ); + + assertTrue( "Initial validating is false", !digester.getValidating() ); + digester.setValidating( true ); + assertTrue( "Set validating is true", digester.getValidating() ); + digester.setValidating( false ); + assertTrue( "Reset validating is false", !digester.getValidating() ); + + } + + /** + * Test registration of URLs for specified public identifiers. + */ + @Test + public void testRegistrations() + { + Map map = digester.getRegistrations(); assertEquals( "Initially zero registrations", 0, map.size() ); int n = 0; @@ -330,314 +589,6 @@ public void testRulesBase() } - /** - * Test the basic stack mechanisms. - */ - @Test - public void testStackMethods() - { - - Object value; - - // New stack must be empty - assertEquals( "New stack is empty", 0, digester.getCount() ); - value = digester.peek(); - assertNull( "New stack peek() returns null", value ); - value = digester.pop(); - assertNull( "New stack pop() returns null", value ); - - // Test pushing and popping activities - digester.push( "First Item" ); - assertEquals( "Pushed one item size", 1, digester.getCount() ); - value = digester.peek(); - assertNotNull( "Peeked first item is not null", value ); - assertEquals( "Peeked first item value", "First Item", value ); - - digester.push( "Second Item" ); - assertEquals( "Pushed two items size", 2, digester.getCount() ); - value = digester.peek(); - assertNotNull( "Peeked second item is not null", value ); - assertEquals( "Peeked second item value", "Second Item", value ); - - value = digester.pop(); - assertEquals( "Popped stack size", 1, digester.getCount() ); - assertNotNull( "Popped second item is not null", value ); - assertEquals( "Popped second item value", "Second Item", value ); - value = digester.peek(); - assertNotNull( "Remaining item is not null", value ); - assertEquals( "Remaining item value", "First Item", value ); - assertEquals( "Remaining stack size", 1, digester.getCount() ); - - // Cleared stack is empty - digester.push( "Dummy Item" ); - digester.clear(); - assertEquals( "Cleared stack is empty", 0, digester.getCount() ); - value = digester.peek(); - assertNull( "Cleared stack peek() returns null", value ); - value = digester.pop(); - assertNull( "Cleared stack pop() returns null", value ); - - } - - @Test - public void testOnceAndOnceOnly() - throws Exception - { - - class TestConfigureDigester - extends Digester - { - public int called; - - public TestConfigureDigester() - { - } - - @Override - protected void initialize() - { - called++; - } - } - - final TestConfigureDigester digester = new TestConfigureDigester(); - - final String xml = ""; - digester.parse( new StringReader( xml ) ); - - assertEquals( "Initialize should be called once and only once", 1, digester.called ); - } - - @Test - public void testBasicSubstitution() - throws Exception - { - class TestSubRule - extends Rule - { - public String body; - - public Attributes attributes; - - @Override - public void begin( final String namespace, final String name, final Attributes attributes ) - { - this.attributes = new AttributesImpl( attributes ); - } - - @Override - public void body( final String namespace, final String name, final String text ) - { - this.body = text; - } - } - - final TestSubRule tsr = new TestSubRule(); - final Digester digester = new Digester(); - digester.addRule( "alpha/beta", tsr ); - - // it's not easy to transform dirty harry into the mighty circus - but let's give it a try - final String xml = - "Do you feel luck punk?"; - InputSource in = new InputSource( new StringReader( xml ) ); - - digester.parse( in ); - - assertEquals( "Unsubstituted body text", "Do you feel luck punk?", tsr.body ); - assertEquals( "Unsubstituted number of attributes", 2, tsr.attributes.getLength() ); - assertEquals( "Unsubstituted forname attribute value", "Dirty", tsr.attributes.getValue( "forname" ) ); - assertEquals( "Unsubstituted surname attribute value", "Harry", tsr.attributes.getValue( "surname" ) ); - - digester.setSubstitutor( new Substitutor() - { - @Override - public Attributes substitute( final Attributes attributes ) - { - final AttributesImpl results = new AttributesImpl(); - results.addAttribute( "", "python", "python", "CDATA", "Cleese" ); - return results; - } - - @Override - public String substitute( final String bodyText ) - { - return "And now for something completely different..."; - } - } ); - - // now transform into the full monty - in = new InputSource( new StringReader( xml ) ); - digester.parse( in ); - - assertEquals( "Substituted body text", "And now for something completely different...", tsr.body ); - assertEquals( "Substituted number of attributes", 1, tsr.attributes.getLength() ); - assertEquals( "Substituted python attribute value", "Cleese", tsr.attributes.getValue( "", "python" ) ); - } - - /** Tests the push-peek-pop cycle for a named stack */ - @Test - public void testNamedStackPushPeekPop() - throws Exception - { - final BigDecimal archimedesAveragePi = new BigDecimal( "3.1418" ); - final String testStackName = "org.apache.commons.digester3.tests.testNamedStackPushPeekPop"; - final Digester digester = new Digester(); - assertTrue( "Stack starts empty:", digester.isEmpty( testStackName ) ); - digester.push( testStackName, archimedesAveragePi ); - assertEquals( "Peeked value:", archimedesAveragePi, digester.peek( testStackName ) ); - assertEquals( "Popped value:", archimedesAveragePi, digester.pop( testStackName ) ); - assertTrue( "Stack ends empty:", digester.isEmpty( testStackName ) ); - - digester.push( testStackName, "1" ); - digester.push( testStackName, "2" ); - digester.push( testStackName, "3" ); - - assertEquals( "Peek#1", "1", digester.peek( testStackName, 2 ) ); - assertEquals( "Peek#2", "2", digester.peek( testStackName, 1 ) ); - assertEquals( "Peek#3", "3", digester.peek( testStackName, 0 ) ); - assertEquals( "Peek#3a", "3", digester.peek( testStackName ) ); - - try - { - // peek beyond stack - digester.peek( testStackName, 3 ); - fail( "Peek#4 failed to throw an exception." ); - } - catch ( final EmptyStackException ex ) - { - // ok, expected - } - - try - { - // peek a nonexistent named stack - digester.peek( "no.such.stack", 0 ); - fail( "Peeking a non-existent stack failed to throw an exception." ); - } - catch ( final EmptyStackException ex ) - { - // ok, expected - } - } - - /** Tests that values are stored independently */ - @Test - public void testNamedIndependence() - { - final String testStackOneName = "org.apache.commons.digester3.tests.testNamedIndependenceOne"; - final String testStackTwoName = "org.apache.commons.digester3.tests.testNamedIndependenceTwo"; - final Digester digester = new Digester(); - digester.push( testStackOneName, "Tweedledum" ); - digester.push( testStackTwoName, "Tweedledee" ); - assertEquals( "Popped value one:", "Tweedledum", digester.pop( testStackOneName ) ); - assertEquals( "Popped value two:", "Tweedledee", digester.pop( testStackTwoName ) ); - } - - /** Tests popping named stack not yet pushed */ - @Test - public void testPopNamedStackNotPushed() - { - final String testStackName = "org.apache.commons.digester3.tests.testPopNamedStackNotPushed"; - final Digester digester = new Digester(); - try - { - - digester.pop( testStackName ); - fail( "Expected an EmptyStackException" ); - - } - catch ( final EmptyStackException e ) - { - // expected - } - - try - { - - digester.peek( testStackName ); - fail( "Expected an EmptyStackException" ); - - } - catch ( final EmptyStackException e ) - { - // expected - } - } - - /** Tests for isEmpty */ - @Test - public void testNamedStackIsEmpty() - { - final String testStackName = "org.apache.commons.digester3.tests.testNamedStackIsEmpty"; - final Digester digester = new Digester(); - assertTrue( "A named stack that has no object pushed onto it yet should be empty", - digester.isEmpty( testStackName ) ); - - digester.push( testStackName, "Some test value" ); - assertFalse( "A named stack that has an object pushed onto it should be not empty", - digester.isEmpty( testStackName ) ); - - digester.peek( testStackName ); - assertFalse( "Peek should not effect whether the stack is empty", digester.isEmpty( testStackName ) ); - - digester.pop( testStackName ); - assertTrue( "A named stack that has it's last object popped is empty", digester.isEmpty( testStackName ) ); - } - - /** - * Test the Digester.getRoot method. - */ - @Test - public void testGetRoot() - throws Exception - { - final Digester digester = new Digester(); - digester.addRule( "root", new ObjectCreateRule( TestBean.class ) ); - - final String xml = ""; - final InputSource in = new InputSource( new StringReader( xml ) ); - - digester.parse( in ); - - final Object root = digester.getRoot(); - assertNotNull( "root object not retrieved", root ); - assertTrue( "root object not a TestRule instance", ( root instanceof TestBean ) ); - } - - /** Utility class for method testStackAction */ - private static final class TrackingStackAction - implements StackAction - { - public ArrayList events = new ArrayList(); - - @Override - public Object onPush( final Digester d, final String stackName, final Object o ) - { - final String msg = "push:" + stackName + ":" + o.toString(); - events.add( msg ); - - final String str = o.toString(); - if ( str.startsWith( "replpush" ) ) - { - return new String( str ); - } - return o; - } - - @Override - public Object onPop( final Digester d, final String stackName, final Object o ) - { - final String msg = "pop:" + stackName + ":" + o.toString(); - events.add( msg ); - final String str = o.toString(); - if ( str.startsWith( "replpop" ) ) - { - return new String( str ); - } - return o; - } - } - /** * Test custom StackAction subclasses. */ @@ -707,4 +658,53 @@ public void testStackAction() assertEquals( "pop:stack1:obj9", action.events.get( 10 ) ); assertEquals( "pop:stack1:obj8", action.events.get( 11 ) ); } + + /** + * Test the basic stack mechanisms. + */ + @Test + public void testStackMethods() + { + + Object value; + + // New stack must be empty + assertEquals( "New stack is empty", 0, digester.getCount() ); + value = digester.peek(); + assertNull( "New stack peek() returns null", value ); + value = digester.pop(); + assertNull( "New stack pop() returns null", value ); + + // Test pushing and popping activities + digester.push( "First Item" ); + assertEquals( "Pushed one item size", 1, digester.getCount() ); + value = digester.peek(); + assertNotNull( "Peeked first item is not null", value ); + assertEquals( "Peeked first item value", "First Item", value ); + + digester.push( "Second Item" ); + assertEquals( "Pushed two items size", 2, digester.getCount() ); + value = digester.peek(); + assertNotNull( "Peeked second item is not null", value ); + assertEquals( "Peeked second item value", "Second Item", value ); + + value = digester.pop(); + assertEquals( "Popped stack size", 1, digester.getCount() ); + assertNotNull( "Popped second item is not null", value ); + assertEquals( "Popped second item value", "Second Item", value ); + value = digester.peek(); + assertNotNull( "Remaining item is not null", value ); + assertEquals( "Remaining item value", "First Item", value ); + assertEquals( "Remaining stack size", 1, digester.getCount() ); + + // Cleared stack is empty + digester.push( "Dummy Item" ); + digester.clear(); + assertEquals( "Cleared stack is empty", 0, digester.getCount() ); + value = digester.peek(); + assertNull( "Cleared stack peek() returns null", value ); + value = digester.pop(); + assertNull( "Cleared stack pop() returns null", value ); + + } } diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/Employee.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/Employee.java index f12ab964b..909b86bff 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/Employee.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/Employee.java @@ -27,6 +27,19 @@ public class Employee { + private final ArrayList
    addresses = new ArrayList
    (); + + private String firstName; + + private String lastName; + + // this is to allow testing of primitive convertion + private int age; + + private boolean active; + + private float salary; + public Employee() { this( "My First Name", "My Last Name" ); @@ -38,8 +51,6 @@ public Employee( final String firstName, final String lastName ) setLastName( lastName ); } - private final ArrayList
    addresses = new ArrayList
    (); - public void addAddress( final Address address ) { addresses.add( address ); @@ -56,65 +67,54 @@ public Address getAddress( final String type ) return ( null ); } - public void removeAddress( final Address address ) + public int getAge() { - addresses.remove( address ); + return age; } - private String firstName; - public String getFirstName() { return ( this.firstName ); } - public void setFirstName( final String firstName ) - { - this.firstName = firstName; - } - - private String lastName; - public String getLastName() { return ( this.lastName ); } - public void setLastName( final String lastName ) + public float getSalary() { - this.lastName = lastName; + return salary; } - // this is to allow testing of primitive convertion - private int age; - - private boolean active; - - private float salary; + public boolean isActive() + { + return active; + } - public int getAge() + public void removeAddress( final Address address ) { - return age; + addresses.remove( address ); } - public void setAge( final int age ) + public void setActive( final boolean active ) { - this.age = age; + this.active = active; } - public boolean isActive() + public void setAge( final int age ) { - return active; + this.age = age; } - public void setActive( final boolean active ) + public void setFirstName( final String firstName ) { - this.active = active; + this.firstName = firstName; } - public float getSalary() + public void setLastName( final String lastName ) { - return salary; + this.lastName = lastName; } public void setSalary( final float salary ) diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/ErrorHandlerTest.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/ErrorHandlerTest.java index d7a71e4ff..583eb1230 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/ErrorHandlerTest.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/ErrorHandlerTest.java @@ -36,35 +36,7 @@ public class ErrorHandlerTest { @Test - public void testNoCustomErrorHandler() - { - - try - { - newLoader( new AbstractRulesModule() - { - @Override - protected void configure() - { - forPattern( "employee" ).createObject().ofType( Employee.class ); - forPattern( "employee/firstName" ).setBeanProperty().extractPropertyNameFromAttribute( "name" ); - } - } ).newDigester().parse( getClass().getResource( "Test-digester-172-wrong.xml" ) ); - fail( "Expected SAXException" ); - } - catch ( final IOException e ) - { - fail( "Expected SAXException" ); - } - catch ( final SAXException e ) - { - // expected - } - - } - - @Test - public void testCustomErrorHandlerWithStack() + public void testCustomErrorHandler() { final ErrorHandler customErrorHandler = new ErrorHandler() @@ -72,24 +44,24 @@ public void testCustomErrorHandlerWithStack() final Log log = LogFactory.getLog( this.getClass() ); @Override - public void warning( final SAXParseException arg0 ) + public void error( final SAXParseException e ) throws SAXException { - log.warn( "Custom Warn Handler" ); + log.error( "Custom Error Handler" ); } @Override public void fatalError( final SAXParseException e ) throws SAXException { - log.fatal( "Custom Fatal Error Handler", e ); + log.fatal( "Custom Fatal Error Handler" ); } @Override - public void error( final SAXParseException e ) + public void warning( final SAXParseException arg0 ) throws SAXException { - log.error( "Custom Error Handler", e ); + log.warn( "Custom Warn Handler" ); } }; @@ -106,7 +78,8 @@ protected void configure() } ).newDigester(); digester.setErrorHandler( customErrorHandler ); digester.parse( getClass().getResource( "Test-digester-172-wrong.xml" ) ); - fail( "Expected SAXException" ); + fail( "No Expected SAXException" ); + } catch ( final IOException e ) { @@ -119,7 +92,7 @@ protected void configure() } @Test - public void testCustomErrorHandler() + public void testCustomErrorHandlerWithStack() { final ErrorHandler customErrorHandler = new ErrorHandler() @@ -127,24 +100,24 @@ public void testCustomErrorHandler() final Log log = LogFactory.getLog( this.getClass() ); @Override - public void warning( final SAXParseException arg0 ) + public void error( final SAXParseException e ) throws SAXException { - log.warn( "Custom Warn Handler" ); + log.error( "Custom Error Handler", e ); } @Override public void fatalError( final SAXParseException e ) throws SAXException { - log.fatal( "Custom Fatal Error Handler" ); + log.fatal( "Custom Fatal Error Handler", e ); } @Override - public void error( final SAXParseException e ) + public void warning( final SAXParseException arg0 ) throws SAXException { - log.error( "Custom Error Handler" ); + log.warn( "Custom Warn Handler" ); } }; @@ -161,8 +134,34 @@ protected void configure() } ).newDigester(); digester.setErrorHandler( customErrorHandler ); digester.parse( getClass().getResource( "Test-digester-172-wrong.xml" ) ); - fail( "No Expected SAXException" ); + fail( "Expected SAXException" ); + } + catch ( final IOException e ) + { + fail( "Expected SAXException" ); + } + catch ( final SAXException e ) + { + // expected + } + } + @Test + public void testNoCustomErrorHandler() + { + + try + { + newLoader( new AbstractRulesModule() + { + @Override + protected void configure() + { + forPattern( "employee" ).createObject().ofType( Employee.class ); + forPattern( "employee/firstName" ).setBeanProperty().extractPropertyNameFromAttribute( "name" ); + } + } ).newDigester().parse( getClass().getResource( "Test-digester-172-wrong.xml" ) ); + fail( "Expected SAXException" ); } catch ( final IOException e ) { @@ -172,5 +171,6 @@ protected void configure() { // expected } + } } diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/ExtendedBaseRulesTestCase.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/ExtendedBaseRulesTestCase.java index fad9df266..30e90b4ef 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/ExtendedBaseRulesTestCase.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/ExtendedBaseRulesTestCase.java @@ -48,6 +48,67 @@ protected Rules createMatchingRulesForTest() return new ExtendedBaseRules(); } + @Test + public void testAncesterMatch() + throws Exception + { + // test fixed root ancester + digester.getRules().clear(); + + digester.addRule( "!a/b/*", new TestRule( "uni-a-b-star" ) ); + digester.addRule( "a/b/*", new TestRule( "a-b-star" ) ); + digester.addRule( "a/b/c", new TestRule( "a-b-c" ) ); + digester.addRule( "a/b/?", new TestRule( "a-b-child" ) ); + + List list = digester.getRules().match( null, "a/b/c", null, null ); + + assertEquals( "Simple ancester matches (1)", 2, list.size() ); + assertEquals( "Univeral ancester mismatch (1)", "uni-a-b-star", ( (TestRule) list.get( 0 ) ).getIdentifier() ); + assertEquals( "Parent precedence failure", "a-b-c", ( (TestRule) list.get( 1 ) ).getIdentifier() ); + + list = digester.getRules().match( null, "a/b/b", null, null ); + assertEquals( "Simple ancester matches (2)", 2, list.size() ); + assertEquals( "Univeral ancester mismatch (2)", "uni-a-b-star", ( (TestRule) list.get( 0 ) ).getIdentifier() ); + assertEquals( "Child precedence failure", "a-b-child", ( (TestRule) list.get( 1 ) ).getIdentifier() ); + + list = digester.getRules().match( null, "a/b/d", null, null ); + assertEquals( "Simple ancester matches (3)", 2, list.size() ); + assertEquals( "Univeral ancester mismatch (3)", "uni-a-b-star", ( (TestRule) list.get( 0 ) ).getIdentifier() ); + assertEquals( "Ancester mismatch (1)", "a-b-child", ( (TestRule) list.get( 1 ) ).getIdentifier() ); + + list = digester.getRules().match( null, "a/b/d/e/f", null, null ); + assertEquals( "Simple ancester matches (4)", 2, list.size() ); + assertEquals( "Univeral ancester mismatch (4)", "uni-a-b-star", ( (TestRule) list.get( 0 ) ).getIdentifier() ); + assertEquals( "Ancester mismatch (2)", "a-b-star", ( (TestRule) list.get( 1 ) ).getIdentifier() ); + + // test wild root ancester + digester.getRules().clear(); + + digester.addRule( "!*/a/b/*", new TestRule( "uni-star-a-b-star" ) ); + digester.addRule( "*/b/c/*", new TestRule( "star-b-c-star" ) ); + digester.addRule( "*/b/c/d", new TestRule( "star-b-c-d" ) ); + digester.addRule( "a/b/c", new TestRule( "a-b-c" ) ); + + list = digester.getRules().match( null, "a/b/c", null, null ); + assertEquals( "Wild ancester match (1)", 2, list.size() ); + assertEquals( "Univeral ancester mismatch (5)", "uni-star-a-b-star", + ( (TestRule) list.get( 0 ) ).getIdentifier() ); + assertEquals( "Match missed (1)", "a-b-c", ( (TestRule) list.get( 1 ) ).getIdentifier() ); + + list = digester.getRules().match( null, "b/c", null, null ); + assertEquals( "Wild ancester match (2)", 1, list.size() ); + assertEquals( "Match missed (2)", "star-b-c-star", ( (TestRule) list.get( 0 ) ).getIdentifier() ); + + list = digester.getRules().match( null, "a/b/c/d", null, null ); + assertEquals( "Wild ancester match (3)", 2, list.size() ); + assertEquals( "Match missed (3)", "uni-star-a-b-star", ( (TestRule) list.get( 0 ) ).getIdentifier() ); + assertEquals( "Match missed (4)", "star-b-c-d", ( (TestRule) list.get( 1 ) ).getIdentifier() ); + + list = digester.getRules().match( null, "b/b/c/e/d", null, null ); + assertEquals( "Wild ancester match (2)", 1, list.size() ); + assertEquals( "Match missed (5)", "star-b-c-star", ( (TestRule) list.get( 0 ) ).getIdentifier() ); + } + /** * Basic test of parent matching rules. A parent match matches any child of a particular kind of parent. A wild * parent has a wildcard prefix. This method tests non-universal wildcards. @@ -194,60 +255,53 @@ public void testBasicUniversal() } - /** - * Basic test of wild matches. A universal will match matches anything! A non-universal will match matches anything - * not matched by something else. This method tests non-universal and universal wild matches. - */ @Test - public void testWildMatch() + public void testInstructors() { - - // clear any existing rules digester.getRules().clear(); - assertEquals( "Initial rules list is empty", 0, digester.getRules().rules().size() ); + digester.addRule( "!instructors/*", new TestRule( "instructors" ) ); + digester.addRule( "!instructor/*", new TestRule( "instructor" ) ); - // Set up rules - // The combinations a little large to test everything but we'll pick a couple and try them. - digester.addRule( "*", new TestRule( "basic_wild" ) ); - digester.addRule( "!*", new TestRule( "universal_wild" ) ); - digester.addRule( "alpha/beta/gamma/delta", new TestRule( "exact" ) ); - digester.addRule( "*/beta/gamma/?", new TestRule( "wild_parent" ) ); + final List list = digester.getRules().match( null, "instructors", null, null ); + assertEquals( "Only expect to match instructors", 1, list.size() ); + assertEquals( "Instructors expected", "instructors", ( (TestRule) list.get( 0 ) ).getIdentifier() ); - List list; - Iterator it; + } - // The universal wild will always match whatever else does - list = digester.getRules().match( null, "alpha/beta/gamma/delta", null, null ); + @Test + public void testLongMatch() + { - // all three rules should match - assertEquals( "Testing wild mismatch (A)", 2, list.size() ); + digester.getRules().clear(); - it = list.iterator(); - assertEquals( "Testing wild mismatch (B)", "universal_wild", ( (TestRule) it.next() ).getIdentifier() ); - assertEquals( "Testing wild mismatch (C)", "exact", ( (TestRule) it.next() ).getIdentifier() ); + digester.addRule( "a/b/c/d/*", new TestRule( "a-b-c-d-star" ) ); - // The universal wild will always match whatever else does - list = digester.getRules().match( null, "alpha/beta/gamma/epsilon", null, null ); + List list = digester.getRules().match( null, "a/b/c/d/e", null, null ); + assertEquals( "Long match (1)", 1, list.size() ); + assertEquals( "Match missed (1)", "a-b-c-d-star", ( (TestRule) list.get( 0 ) ).getIdentifier() ); - assertEquals( "Testing wild mismatch (D)", 2, list.size() ); + list = digester.getRules().match( null, "a/b/c/d/e/f", null, null ); + assertEquals( "Long match (2)", 1, list.size() ); + assertEquals( "Match missed (2)", "a-b-c-d-star", ( (TestRule) list.get( 0 ) ).getIdentifier() ); - it = list.iterator(); - assertEquals( "Testing wild mismatch (E)", "universal_wild", ( (TestRule) it.next() ).getIdentifier() ); - assertEquals( "Testing wild mismatch (F)", "wild_parent", ( (TestRule) it.next() ).getIdentifier() ); + list = digester.getRules().match( null, "a/b/c/d/e/f/g", null, null ); + assertEquals( "Long match (3)", 1, list.size() ); + assertEquals( "Match missed (3)", "a-b-c-d-star", ( (TestRule) list.get( 0 ) ).getIdentifier() ); - // The universal wild will always match whatever else does - // we have no other non-universal matching so this will match the non-universal wild as well - list = digester.getRules().match( null, "alpha/gamma", null, null ); + list = digester.getRules().match( null, "a/b/c/d", null, null ); + assertEquals( "Long match (4)", 0, list.size() ); + } - assertEquals( "Testing wild mismatch (G)", 2, list.size() ); + @Test + public void testMiddleInstructors() + { + digester.getRules().clear(); - it = list.iterator(); - assertEquals( "Testing wild mismatch (H)", "basic_wild", ( (TestRule) it.next() ).getIdentifier() ); - assertEquals( "Testing wild mismatch (I)", "universal_wild", ( (TestRule) it.next() ).getIdentifier() ); + digester.addRule( "!instructors/*", new TestRule( "instructors" ) ); - // clean up - digester.getRules().clear(); + final List list = digester.getRules().match( null, "/tosh/instructors/fiddlesticks", null, null ); + assertEquals( "No matches expected", 0, list.size() ); } @@ -297,114 +351,60 @@ public void testRootTailMatch() } + /** + * Basic test of wild matches. A universal will match matches anything! A non-universal will match matches anything + * not matched by something else. This method tests non-universal and universal wild matches. + */ @Test - public void testAncesterMatch() - throws Exception + public void testWildMatch() { - // test fixed root ancester - digester.getRules().clear(); - - digester.addRule( "!a/b/*", new TestRule( "uni-a-b-star" ) ); - digester.addRule( "a/b/*", new TestRule( "a-b-star" ) ); - digester.addRule( "a/b/c", new TestRule( "a-b-c" ) ); - digester.addRule( "a/b/?", new TestRule( "a-b-child" ) ); - - List list = digester.getRules().match( null, "a/b/c", null, null ); - - assertEquals( "Simple ancester matches (1)", 2, list.size() ); - assertEquals( "Univeral ancester mismatch (1)", "uni-a-b-star", ( (TestRule) list.get( 0 ) ).getIdentifier() ); - assertEquals( "Parent precedence failure", "a-b-c", ( (TestRule) list.get( 1 ) ).getIdentifier() ); - - list = digester.getRules().match( null, "a/b/b", null, null ); - assertEquals( "Simple ancester matches (2)", 2, list.size() ); - assertEquals( "Univeral ancester mismatch (2)", "uni-a-b-star", ( (TestRule) list.get( 0 ) ).getIdentifier() ); - assertEquals( "Child precedence failure", "a-b-child", ( (TestRule) list.get( 1 ) ).getIdentifier() ); - - list = digester.getRules().match( null, "a/b/d", null, null ); - assertEquals( "Simple ancester matches (3)", 2, list.size() ); - assertEquals( "Univeral ancester mismatch (3)", "uni-a-b-star", ( (TestRule) list.get( 0 ) ).getIdentifier() ); - assertEquals( "Ancester mismatch (1)", "a-b-child", ( (TestRule) list.get( 1 ) ).getIdentifier() ); - list = digester.getRules().match( null, "a/b/d/e/f", null, null ); - assertEquals( "Simple ancester matches (4)", 2, list.size() ); - assertEquals( "Univeral ancester mismatch (4)", "uni-a-b-star", ( (TestRule) list.get( 0 ) ).getIdentifier() ); - assertEquals( "Ancester mismatch (2)", "a-b-star", ( (TestRule) list.get( 1 ) ).getIdentifier() ); - - // test wild root ancester + // clear any existing rules digester.getRules().clear(); - digester.addRule( "!*/a/b/*", new TestRule( "uni-star-a-b-star" ) ); - digester.addRule( "*/b/c/*", new TestRule( "star-b-c-star" ) ); - digester.addRule( "*/b/c/d", new TestRule( "star-b-c-d" ) ); - digester.addRule( "a/b/c", new TestRule( "a-b-c" ) ); - - list = digester.getRules().match( null, "a/b/c", null, null ); - assertEquals( "Wild ancester match (1)", 2, list.size() ); - assertEquals( "Univeral ancester mismatch (5)", "uni-star-a-b-star", - ( (TestRule) list.get( 0 ) ).getIdentifier() ); - assertEquals( "Match missed (1)", "a-b-c", ( (TestRule) list.get( 1 ) ).getIdentifier() ); - - list = digester.getRules().match( null, "b/c", null, null ); - assertEquals( "Wild ancester match (2)", 1, list.size() ); - assertEquals( "Match missed (2)", "star-b-c-star", ( (TestRule) list.get( 0 ) ).getIdentifier() ); - - list = digester.getRules().match( null, "a/b/c/d", null, null ); - assertEquals( "Wild ancester match (3)", 2, list.size() ); - assertEquals( "Match missed (3)", "uni-star-a-b-star", ( (TestRule) list.get( 0 ) ).getIdentifier() ); - assertEquals( "Match missed (4)", "star-b-c-d", ( (TestRule) list.get( 1 ) ).getIdentifier() ); - - list = digester.getRules().match( null, "b/b/c/e/d", null, null ); - assertEquals( "Wild ancester match (2)", 1, list.size() ); - assertEquals( "Match missed (5)", "star-b-c-star", ( (TestRule) list.get( 0 ) ).getIdentifier() ); - } + assertEquals( "Initial rules list is empty", 0, digester.getRules().rules().size() ); - @Test - public void testLongMatch() - { + // Set up rules + // The combinations a little large to test everything but we'll pick a couple and try them. + digester.addRule( "*", new TestRule( "basic_wild" ) ); + digester.addRule( "!*", new TestRule( "universal_wild" ) ); + digester.addRule( "alpha/beta/gamma/delta", new TestRule( "exact" ) ); + digester.addRule( "*/beta/gamma/?", new TestRule( "wild_parent" ) ); - digester.getRules().clear(); + List list; + Iterator it; - digester.addRule( "a/b/c/d/*", new TestRule( "a-b-c-d-star" ) ); + // The universal wild will always match whatever else does + list = digester.getRules().match( null, "alpha/beta/gamma/delta", null, null ); - List list = digester.getRules().match( null, "a/b/c/d/e", null, null ); - assertEquals( "Long match (1)", 1, list.size() ); - assertEquals( "Match missed (1)", "a-b-c-d-star", ( (TestRule) list.get( 0 ) ).getIdentifier() ); + // all three rules should match + assertEquals( "Testing wild mismatch (A)", 2, list.size() ); - list = digester.getRules().match( null, "a/b/c/d/e/f", null, null ); - assertEquals( "Long match (2)", 1, list.size() ); - assertEquals( "Match missed (2)", "a-b-c-d-star", ( (TestRule) list.get( 0 ) ).getIdentifier() ); + it = list.iterator(); + assertEquals( "Testing wild mismatch (B)", "universal_wild", ( (TestRule) it.next() ).getIdentifier() ); + assertEquals( "Testing wild mismatch (C)", "exact", ( (TestRule) it.next() ).getIdentifier() ); - list = digester.getRules().match( null, "a/b/c/d/e/f/g", null, null ); - assertEquals( "Long match (3)", 1, list.size() ); - assertEquals( "Match missed (3)", "a-b-c-d-star", ( (TestRule) list.get( 0 ) ).getIdentifier() ); + // The universal wild will always match whatever else does + list = digester.getRules().match( null, "alpha/beta/gamma/epsilon", null, null ); - list = digester.getRules().match( null, "a/b/c/d", null, null ); - assertEquals( "Long match (4)", 0, list.size() ); - } + assertEquals( "Testing wild mismatch (D)", 2, list.size() ); - @Test - public void testInstructors() - { - digester.getRules().clear(); + it = list.iterator(); + assertEquals( "Testing wild mismatch (E)", "universal_wild", ( (TestRule) it.next() ).getIdentifier() ); + assertEquals( "Testing wild mismatch (F)", "wild_parent", ( (TestRule) it.next() ).getIdentifier() ); - digester.addRule( "!instructors/*", new TestRule( "instructors" ) ); - digester.addRule( "!instructor/*", new TestRule( "instructor" ) ); + // The universal wild will always match whatever else does + // we have no other non-universal matching so this will match the non-universal wild as well + list = digester.getRules().match( null, "alpha/gamma", null, null ); - final List list = digester.getRules().match( null, "instructors", null, null ); - assertEquals( "Only expect to match instructors", 1, list.size() ); - assertEquals( "Instructors expected", "instructors", ( (TestRule) list.get( 0 ) ).getIdentifier() ); + assertEquals( "Testing wild mismatch (G)", 2, list.size() ); - } + it = list.iterator(); + assertEquals( "Testing wild mismatch (H)", "basic_wild", ( (TestRule) it.next() ).getIdentifier() ); + assertEquals( "Testing wild mismatch (I)", "universal_wild", ( (TestRule) it.next() ).getIdentifier() ); - @Test - public void testMiddleInstructors() - { + // clean up digester.getRules().clear(); - digester.addRule( "!instructors/*", new TestRule( "instructors" ) ); - - final List list = digester.getRules().match( null, "/tosh/instructors/fiddlesticks", null, null ); - assertEquals( "No matches expected", 0, list.size() ); - } } diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/LocationTrackerTestCase.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/LocationTrackerTestCase.java index ee06257d1..c3d1ea7fe 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/LocationTrackerTestCase.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/LocationTrackerTestCase.java @@ -44,6 +44,12 @@ private static final class LocationTracker { public Map locations = new HashMap(); + @Override + public T onPop( final Digester d, final String stackName, final T o ) + { + return o; + } + @Override public T onPush( final Digester d, final String stackName, final T o ) { @@ -61,12 +67,6 @@ public T onPush( final Digester d, final String stackName, final T o ) } return o; } - - @Override - public T onPop( final Digester d, final String stackName, final T o ) - { - return o; - } } @Test diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/NamespaceSnapshotTestCase.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/NamespaceSnapshotTestCase.java index c6b2218bb..232b2bad7 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/NamespaceSnapshotTestCase.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/NamespaceSnapshotTestCase.java @@ -44,17 +44,6 @@ public class NamespaceSnapshotTestCase static class NamespaceSnapshotRule extends Rule { - /** - * @see Rule#begin(String, String, Attributes) - */ - @Override - public final void begin( final String namespace, final String name, final Attributes attributes ) - { - final Digester d = getDigester(); - final Map namespaces = d.getCurrentNamespaces(); - ( (NamespacedBox) d.peek() ).setNamespaces( namespaces ); - } - public static class Provider implements RuleProvider { @@ -69,8 +58,35 @@ public NamespaceSnapshotRule get() } + /** + * @see Rule#begin(String, String, Attributes) + */ + @Override + public final void begin( final String namespace, final String name, final Attributes attributes ) + { + final Digester d = getDigester(); + final Map namespaces = d.getCurrentNamespaces(); + ( (NamespacedBox) d.peek() ).setNamespaces( namespaces ); + } + + } + + /** + * Return an appropriate InputStream for the specified test file (which must be inside our current package. + * + * @param name Name of the test file we want + * @throws IOException if an input/output error occurs + */ + protected InputStream getInputStream( final String name ) + throws IOException + { + + return ( this.getClass().getResourceAsStream( "/org/apache/commons/digester3/" + name ) ); + } + // ------------------------------------------------ Utility Support Methods + /** * Namespace snapshot test case. */ @@ -138,20 +154,4 @@ protected void configure() } - // ------------------------------------------------ Utility Support Methods - - /** - * Return an appropriate InputStream for the specified test file (which must be inside our current package. - * - * @param name Name of the test file we want - * @throws IOException if an input/output error occurs - */ - protected InputStream getInputStream( final String name ) - throws IOException - { - - return ( this.getClass().getResourceAsStream( "/org/apache/commons/digester3/" + name ) ); - - } - } diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/NodeCreateRuleTestCase.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/NodeCreateRuleTestCase.java index e7c813fce..b3e963697 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/NodeCreateRuleTestCase.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/NodeCreateRuleTestCase.java @@ -61,6 +61,210 @@ public class NodeCreateRuleTestCase // ------------------------------------------------ Individual Test Methods + /** + * Return an appropriate InputStream for the specified test file (which must be inside our current package. + * + * @param name Name of the test file we want + * @throws IOException if an input/output error occurs + */ + protected InputStream getInputStream( final String name ) + throws IOException + { + + return ( this.getClass().getResourceAsStream( "/org/apache/commons/digester3/" + name ) ); + + } + + /** + * Tests whether attributes are correctly imported into the fragment, using the example in the Test1 XML file. + */ + @Test + public void testAttributes() + throws SAXException, IOException + { + final Digester digester = newLoader( new AbstractRulesModule() + { + + @Override + protected void configure() + { + forPattern( "employee" ).createNode().ofType( NodeType.DOCUMENT_FRAGMENT ); + } + + }).newDigester(); + + final DocumentFragment fragment = digester.parse( getInputStream( "Test1.xml" ) ); + + assertNotNull( fragment ); + assertEquals( 2, fragment.getChildNodes().getLength() ); + + assertEquals( Node.ELEMENT_NODE, fragment.getFirstChild().getNodeType() ); + final Element address1 = (Element) fragment.getFirstChild(); + assertEquals( "address", address1.getNodeName() ); + assertEquals( 5, address1.getAttributes().getLength() ); + assertEquals( "home", address1.getAttribute( "type" ) ); + assertEquals( "Home Street", address1.getAttribute( "street" ) ); + assertEquals( "Home City", address1.getAttribute( "city" ) ); + assertEquals( "HS", address1.getAttribute( "state" ) ); + assertEquals( "HmZip", address1.getAttribute( "zipCode" ) ); + + assertEquals( Node.ELEMENT_NODE, fragment.getLastChild().getNodeType() ); + final Element address2 = (Element) fragment.getLastChild(); + assertEquals( "address", address2.getNodeName() ); + assertEquals( 5, address2.getAttributes().getLength() ); + assertEquals( "office", address2.getAttribute( "type" ) ); + assertEquals( "Office Street", address2.getAttribute( "street" ) ); + assertEquals( "Office City", address2.getAttribute( "city" ) ); + assertEquals( "OS", address2.getAttribute( "state" ) ); + assertEquals( "OfZip", address2.getAttribute( "zipCode" ) ); + + } + + /** + * Tests simple fragment construction, using the {@link #TEST_XML} XML input data. + */ + @Test + public void testDocumentFragment() + throws SAXException, IOException + { + final Digester digester = newLoader( new AbstractRulesModule() + { + + @Override + protected void configure() + { + forPattern( "root" ).createNode().ofType( NodeType.DOCUMENT_FRAGMENT ); + } + + }).newDigester(); + + final DocumentFragment fragment = digester.parse( new StringReader( TEST_XML ) ); + + assertNotNull( fragment ); + assertEquals( 4, fragment.getChildNodes().getLength() ); + + final Node rootBody = fragment.getFirstChild(); + assertEquals( Node.TEXT_NODE, rootBody.getNodeType() ); + assertEquals( "ROOT BODY", rootBody.getNodeValue() ); + + final Node alpha = fragment.getChildNodes().item( 1 ); + assertEquals( Node.ELEMENT_NODE, alpha.getNodeType() ); + assertEquals( "alpha", alpha.getNodeName() ); + assertNull( ( (Element) alpha ).getLocalName() ); + assertNull( ( (Element) alpha ).getNamespaceURI() ); + assertEquals( 1, alpha.getChildNodes().getLength() ); + assertEquals( "ALPHA BODY", alpha.getFirstChild().getNodeValue() ); + + final Node beta = fragment.getChildNodes().item( 2 ); + assertEquals( Node.ELEMENT_NODE, beta.getNodeType() ); + assertEquals( "beta", beta.getNodeName() ); + assertNull( ( (Element) beta ).getLocalName() ); + assertNull( ( (Element) beta ).getNamespaceURI() ); + assertEquals( 1, beta.getChildNodes().getLength() ); + assertEquals( "BETA BODY", beta.getFirstChild().getNodeValue() ); + + final Node gamma = fragment.getChildNodes().item( 3 ); + assertEquals( Node.ELEMENT_NODE, gamma.getNodeType() ); + assertEquals( "gamma", gamma.getNodeName() ); + assertNull( ( (Element) gamma ).getLocalName() ); + assertNull( ( (Element) gamma ).getNamespaceURI() ); + assertEquals( 1, gamma.getChildNodes().getLength() ); + assertEquals( "GAMMA BODY", gamma.getFirstChild().getNodeValue() ); + + } + + /** + * Tests simple element construction, using the {@link #TEST_XML} XML input data. + */ + @Test + public void testElement() + throws SAXException, IOException + { + final Digester digester = newLoader( new AbstractRulesModule() + { + + @Override + protected void configure() + { + forPattern( "root/alpha" ).createNode(); + } + + }).newDigester(); + + final Element element = digester.parse( new StringReader( TEST_XML ) ); + + assertNotNull( element ); + assertEquals( "alpha", element.getNodeName() ); + assertNull( element.getLocalName() ); + assertNull( element.getNamespaceURI() ); + assertEquals( 1, element.getChildNodes().getLength() ); + assertEquals( "ALPHA BODY", element.getFirstChild().getNodeValue() ); + + } + + /** + * This unit test checks that text nodes are correctly created when xml entities are used. In particular, this + * usually causes the xml parser to make multiple invocations of the characters(..) sax callback, rather than just + * one. + */ + @Test + public void testEntityText() + throws Exception + { + final String TEST_XML2 = "A A"; + + final Digester digester = newLoader( new AbstractRulesModule() + { + + @Override + protected void configure() + { + forPattern( "root/alpha" ).createNode(); + } + + }) + .newDigester(); + + final Element element = digester.parse( new StringReader( TEST_XML2 ) ); + + assertNotNull( element ); + assertEquals( "alpha", element.getNodeName() ); + assertNull( element.getLocalName() ); + assertNull( element.getNamespaceURI() ); + assertEquals( 1, element.getChildNodes().getLength() ); + assertEquals( "A A", element.getFirstChild().getNodeValue() ); + } + + /** + * Tests whether the created fragment can be imported into an existing document. + */ + @Test + public void testImport() + throws SAXException, ParserConfigurationException, IOException + { + final Digester digester = newLoader( new AbstractRulesModule() + { + + @Override + protected void configure() + { + forPattern( "root" ).createNode().ofType( NodeType.DOCUMENT_FRAGMENT ); + } + + }) + .newDigester(); + + final DocumentFragment fragment = digester.parse( new StringReader( TEST_XML ) ); + + final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + final DocumentBuilder builder = factory.newDocumentBuilder(); + final Document doc = builder.newDocument(); + final Node importedFragment = doc.importNode( fragment, true ); + doc.appendChild( doc.createElement( "root" ) ); + doc.getFirstChild().appendChild( importedFragment ); + + } + /** * Tests simple element construction, using the {@link #TEST_XML} XML input data. */ @@ -173,10 +377,10 @@ public void testInvalidNodeTypes() } /** - * Tests simple element construction, using the {@link #TEST_XML} XML input data. + * Tests whether namespaced attributes are handled correctly, using the example from the file Test10 XML file. */ @Test - public void testElement() + public void testNamespacedAttribute() throws SAXException, IOException { final Digester digester = newLoader( new AbstractRulesModule() @@ -185,159 +389,25 @@ public void testElement() @Override protected void configure() { - forPattern( "root/alpha" ).createNode(); + forPattern( "employee" ).createNode().ofType( NodeType.ELEMENT ); } - }).newDigester(); + }) + .setNamespaceAware( true ) + .newDigester(); - final Element element = digester.parse( new StringReader( TEST_XML ) ); + final Element element = digester.parse( getInputStream( "Test10.xml" ) ); assertNotNull( element ); - assertEquals( "alpha", element.getNodeName() ); - assertNull( element.getLocalName() ); - assertNull( element.getNamespaceURI() ); - assertEquals( 1, element.getChildNodes().getLength() ); - assertEquals( "ALPHA BODY", element.getFirstChild().getNodeValue() ); - - } - - /** - * Tests simple fragment construction, using the {@link #TEST_XML} XML input data. - */ - @Test - public void testDocumentFragment() - throws SAXException, IOException - { - final Digester digester = newLoader( new AbstractRulesModule() - { - - @Override - protected void configure() - { - forPattern( "root" ).createNode().ofType( NodeType.DOCUMENT_FRAGMENT ); - } - - }).newDigester(); - - final DocumentFragment fragment = digester.parse( new StringReader( TEST_XML ) ); - - assertNotNull( fragment ); - assertEquals( 4, fragment.getChildNodes().getLength() ); - - final Node rootBody = fragment.getFirstChild(); - assertEquals( Node.TEXT_NODE, rootBody.getNodeType() ); - assertEquals( "ROOT BODY", rootBody.getNodeValue() ); - - final Node alpha = fragment.getChildNodes().item( 1 ); - assertEquals( Node.ELEMENT_NODE, alpha.getNodeType() ); - assertEquals( "alpha", alpha.getNodeName() ); - assertNull( ( (Element) alpha ).getLocalName() ); - assertNull( ( (Element) alpha ).getNamespaceURI() ); - assertEquals( 1, alpha.getChildNodes().getLength() ); - assertEquals( "ALPHA BODY", alpha.getFirstChild().getNodeValue() ); - - final Node beta = fragment.getChildNodes().item( 2 ); - assertEquals( Node.ELEMENT_NODE, beta.getNodeType() ); - assertEquals( "beta", beta.getNodeName() ); - assertNull( ( (Element) beta ).getLocalName() ); - assertNull( ( (Element) beta ).getNamespaceURI() ); - assertEquals( 1, beta.getChildNodes().getLength() ); - assertEquals( "BETA BODY", beta.getFirstChild().getNodeValue() ); - - final Node gamma = fragment.getChildNodes().item( 3 ); - assertEquals( Node.ELEMENT_NODE, gamma.getNodeType() ); - assertEquals( "gamma", gamma.getNodeName() ); - assertNull( ( (Element) gamma ).getLocalName() ); - assertNull( ( (Element) gamma ).getNamespaceURI() ); - assertEquals( 1, gamma.getChildNodes().getLength() ); - assertEquals( "GAMMA BODY", gamma.getFirstChild().getNodeValue() ); - - } - - /** - * Tests whether control is returned to digester after fragment construction. - */ - @Test - public void testNested() - throws SAXException, IOException - { - final Digester digester = newLoader( new AbstractRulesModule() - { - - @Override - protected void configure() - { - forPattern( "root" ).createObject().ofType( ArrayList.class ); - forPattern( "root/a/b" ).createNode().ofType( NodeType.DOCUMENT_FRAGMENT ) - .then() - .setRoot( "add" ); - forPattern( "root/b" ).createObject().ofType( String.class ) - .then() - .setRoot( "add" ); - } - - }).newDigester(); - - final List list = digester.parse( getInputStream( "Test4.xml" ) ); - - assertNotNull( list ); - assertEquals( 2, list.size() ); - assertTrue( list.get( 0 ) instanceof DocumentFragment ); - final DocumentFragment fragment = (DocumentFragment) list.get( 0 ); - - assertEquals( Node.ELEMENT_NODE, fragment.getFirstChild().getNodeType() ); - final Element a = (Element) fragment.getFirstChild(); - assertEquals( "a", a.getNodeName() ); - assertEquals( 1, a.getAttributes().getLength() ); - assertEquals( "THREE", a.getAttribute( "name" ) ); - - assertTrue( list.get( 1 ) instanceof String ); - - } - - /** - * Tests whether attributes are correctly imported into the fragment, using the example in the Test1 XML file. - */ - @Test - public void testAttributes() - throws SAXException, IOException - { - final Digester digester = newLoader( new AbstractRulesModule() - { - - @Override - protected void configure() - { - forPattern( "employee" ).createNode().ofType( NodeType.DOCUMENT_FRAGMENT ); - } - - }).newDigester(); - - final DocumentFragment fragment = digester.parse( getInputStream( "Test1.xml" ) ); - - assertNotNull( fragment ); - assertEquals( 2, fragment.getChildNodes().getLength() ); - - assertEquals( Node.ELEMENT_NODE, fragment.getFirstChild().getNodeType() ); - final Element address1 = (Element) fragment.getFirstChild(); - assertEquals( "address", address1.getNodeName() ); - assertEquals( 5, address1.getAttributes().getLength() ); - assertEquals( "home", address1.getAttribute( "type" ) ); - assertEquals( "Home Street", address1.getAttribute( "street" ) ); - assertEquals( "Home City", address1.getAttribute( "city" ) ); - assertEquals( "HS", address1.getAttribute( "state" ) ); - assertEquals( "HmZip", address1.getAttribute( "zipCode" ) ); - - assertEquals( Node.ELEMENT_NODE, fragment.getLastChild().getNodeType() ); - final Element address2 = (Element) fragment.getLastChild(); - assertEquals( "address", address2.getNodeName() ); - assertEquals( 5, address2.getAttributes().getLength() ); - assertEquals( "office", address2.getAttribute( "type" ) ); - assertEquals( "Office Street", address2.getAttribute( "street" ) ); - assertEquals( "Office City", address2.getAttribute( "city" ) ); - assertEquals( "OS", address2.getAttribute( "state" ) ); - assertEquals( "OfZip", address2.getAttribute( "zipCode" ) ); + assertNotNull( element.getAttributeNodeNS( "http://commons.apache.org/digester/Bar", "test" ) ); + assertEquals( "MyTestAttribute", + element.getAttributeNodeNS( "http://commons.apache.org/digester/Bar", "test" ).getNodeValue() ); + assertEquals( "test", + element.getAttributeNodeNS( "http://commons.apache.org/digester/Bar", "test" ).getLocalName() ); + assertEquals( "bar", element.getAttributeNodeNS( "http://commons.apache.org/digester/Bar", "test" ).getPrefix() ); + assertEquals( "bar:test", + element.getAttributeNodeNS( "http://commons.apache.org/digester/Bar", "test" ).getName() ); } @@ -393,10 +463,10 @@ protected void configure() } /** - * Tests whether namespaced attributes are handled correctly, using the example from the file Test10 XML file. + * Tests whether control is returned to digester after fragment construction. */ @Test - public void testNamespacedAttribute() + public void testNested() throws SAXException, IOException { final Digester digester = newLoader( new AbstractRulesModule() @@ -405,28 +475,37 @@ public void testNamespacedAttribute() @Override protected void configure() { - forPattern( "employee" ).createNode().ofType( NodeType.ELEMENT ); + forPattern( "root" ).createObject().ofType( ArrayList.class ); + forPattern( "root/a/b" ).createNode().ofType( NodeType.DOCUMENT_FRAGMENT ) + .then() + .setRoot( "add" ); + forPattern( "root/b" ).createObject().ofType( String.class ) + .then() + .setRoot( "add" ); } - }) - .setNamespaceAware( true ) - .newDigester(); + }).newDigester(); - final Element element = digester.parse( getInputStream( "Test10.xml" ) ); + final List list = digester.parse( getInputStream( "Test4.xml" ) ); - assertNotNull( element ); + assertNotNull( list ); + assertEquals( 2, list.size() ); - assertNotNull( element.getAttributeNodeNS( "http://commons.apache.org/digester/Bar", "test" ) ); - assertEquals( "MyTestAttribute", - element.getAttributeNodeNS( "http://commons.apache.org/digester/Bar", "test" ).getNodeValue() ); - assertEquals( "test", - element.getAttributeNodeNS( "http://commons.apache.org/digester/Bar", "test" ).getLocalName() ); - assertEquals( "bar", element.getAttributeNodeNS( "http://commons.apache.org/digester/Bar", "test" ).getPrefix() ); - assertEquals( "bar:test", - element.getAttributeNodeNS( "http://commons.apache.org/digester/Bar", "test" ).getName() ); + assertTrue( list.get( 0 ) instanceof DocumentFragment ); + final DocumentFragment fragment = (DocumentFragment) list.get( 0 ); + + assertEquals( Node.ELEMENT_NODE, fragment.getFirstChild().getNodeType() ); + final Element a = (Element) fragment.getFirstChild(); + assertEquals( "a", a.getNodeName() ); + assertEquals( 1, a.getAttributes().getLength() ); + assertEquals( "THREE", a.getAttribute( "name" ) ); + + assertTrue( list.get( 1 ) instanceof String ); } + // ------------------------------------------------ Utility Support Methods + /** * Tests whether non-namespaced attributes are handled correctly, using the example from the file Test11 XML file. */ @@ -459,83 +538,4 @@ protected void configure() } - /** - * Tests whether the created fragment can be imported into an existing document. - */ - @Test - public void testImport() - throws SAXException, ParserConfigurationException, IOException - { - final Digester digester = newLoader( new AbstractRulesModule() - { - - @Override - protected void configure() - { - forPattern( "root" ).createNode().ofType( NodeType.DOCUMENT_FRAGMENT ); - } - - }) - .newDigester(); - - final DocumentFragment fragment = digester.parse( new StringReader( TEST_XML ) ); - - final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - final DocumentBuilder builder = factory.newDocumentBuilder(); - final Document doc = builder.newDocument(); - final Node importedFragment = doc.importNode( fragment, true ); - doc.appendChild( doc.createElement( "root" ) ); - doc.getFirstChild().appendChild( importedFragment ); - - } - - /** - * This unit test checks that text nodes are correctly created when xml entities are used. In particular, this - * usually causes the xml parser to make multiple invocations of the characters(..) sax callback, rather than just - * one. - */ - @Test - public void testEntityText() - throws Exception - { - final String TEST_XML2 = "A A"; - - final Digester digester = newLoader( new AbstractRulesModule() - { - - @Override - protected void configure() - { - forPattern( "root/alpha" ).createNode(); - } - - }) - .newDigester(); - - final Element element = digester.parse( new StringReader( TEST_XML2 ) ); - - assertNotNull( element ); - assertEquals( "alpha", element.getNodeName() ); - assertNull( element.getLocalName() ); - assertNull( element.getNamespaceURI() ); - assertEquals( 1, element.getChildNodes().getLength() ); - assertEquals( "A A", element.getFirstChild().getNodeValue() ); - } - - // ------------------------------------------------ Utility Support Methods - - /** - * Return an appropriate InputStream for the specified test file (which must be inside our current package. - * - * @param name Name of the test file we want - * @throws IOException if an input/output error occurs - */ - protected InputStream getInputStream( final String name ) - throws IOException - { - - return ( this.getClass().getResourceAsStream( "/org/apache/commons/digester3/" + name ) ); - - } - } diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/ParamBean.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/ParamBean.java index 9a66f734d..c688f8b6c 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/ParamBean.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/ParamBean.java @@ -34,24 +34,24 @@ public ParamBean() { } - public boolean isCool() + public String getThat() { - return cool; + return that; } - public void setCool( final boolean cool ) + public String getThis() { - this.cool = cool; + return _this; } - public String getThis() + public boolean isCool() { - return _this; + return cool; } - public String getThat() + public void setCool( final boolean cool ) { - return that; + this.cool = cool; } public String setThisAndThat( final String _this, final String that ) diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/RegexRulesTestCase.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/RegexRulesTestCase.java index 0ff0aa501..706e65f78 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/RegexRulesTestCase.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/RegexRulesTestCase.java @@ -30,9 +30,9 @@ public class RegexRulesTestCase { - /** Test regex that matches everything */ + /** Test rules and clear methods */ @Test - public void testMatchAll() + public void testClear() { // set up which should match every rule final RegexRules rules = new RegexRules( new RegexMatcher() @@ -44,28 +44,34 @@ public boolean match( final String pathPattern, final String rulePattern ) } } ); - rules.add( "/a/b/b", new TestRule( "alpha" ) ); - rules.add( "/a/d", new TestRule( "beta" ) ); - rules.add( "/b", new TestRule( "gamma" ) ); + rules.add( "/abba", new TestRule( "alpha" ) ); + rules.add( "/ad/ma", new TestRule( "beta" ) ); + rules.add( "/gamma", new TestRule( "gamma" ) ); - // now test a few patterns - // check that all are return in the order which they were added - List matches = rules.match( "", "x/g/e", null, null ); + // check that rules returns all rules in the order which they were added + List matches = rules.rules(); assertEquals( "Wrong number of rules returned (1)", 3, matches.size() ); assertEquals( "Rule Out Of Order (1)", "alpha", ( (TestRule) matches.get( 0 ) ).getIdentifier() ); assertEquals( "Rule Out Of Order (2)", "beta", ( (TestRule) matches.get( 1 ) ).getIdentifier() ); assertEquals( "Rule Out Of Order (3)", "gamma", ( (TestRule) matches.get( 2 ) ).getIdentifier() ); - matches = rules.match( "", "/a", null, null ); + matches = rules.match( "", "/eggs", null, null ); assertEquals( "Wrong number of rules returned (2)", 3, matches.size() ); assertEquals( "Rule Out Of Order (4)", "alpha", ( (TestRule) matches.get( 0 ) ).getIdentifier() ); assertEquals( "Rule Out Of Order (5)", "beta", ( (TestRule) matches.get( 1 ) ).getIdentifier() ); assertEquals( "Rule Out Of Order (6)", "gamma", ( (TestRule) matches.get( 2 ) ).getIdentifier() ); + + rules.clear(); + matches = rules.rules(); + assertEquals( "Wrong number of rules returned (3)", 0, matches.size() ); + + matches = rules.match( "", "/eggs", null, null ); + assertEquals( "Wrong number of rules returned (4)", 0, matches.size() ); } - /** Test regex matcher that matches nothing */ + /** Test regex that matches everything */ @Test - public void testMatchNothing() + public void testMatchAll() { // set up which should match every rule final RegexRules rules = new RegexRules( new RegexMatcher() @@ -73,21 +79,27 @@ public void testMatchNothing() @Override public boolean match( final String pathPattern, final String rulePattern ) { - return false; + return true; } } ); - rules.add( "/b/c/f", new TestRule( "alpha" ) ); - rules.add( "/c/f", new TestRule( "beta" ) ); + rules.add( "/a/b/b", new TestRule( "alpha" ) ); + rules.add( "/a/d", new TestRule( "beta" ) ); rules.add( "/b", new TestRule( "gamma" ) ); // now test a few patterns // check that all are return in the order which they were added - List matches = rules.match( "", "/b/c", null, null ); - assertEquals( "Wrong number of rules returned (1)", 0, matches.size() ); + List matches = rules.match( "", "x/g/e", null, null ); + assertEquals( "Wrong number of rules returned (1)", 3, matches.size() ); + assertEquals( "Rule Out Of Order (1)", "alpha", ( (TestRule) matches.get( 0 ) ).getIdentifier() ); + assertEquals( "Rule Out Of Order (2)", "beta", ( (TestRule) matches.get( 1 ) ).getIdentifier() ); + assertEquals( "Rule Out Of Order (3)", "gamma", ( (TestRule) matches.get( 2 ) ).getIdentifier() ); - matches = rules.match( "", "/b/c/f", null, null ); - assertEquals( "Wrong number of rules returned (2)", 0, matches.size() ); + matches = rules.match( "", "/a", null, null ); + assertEquals( "Wrong number of rules returned (2)", 3, matches.size() ); + assertEquals( "Rule Out Of Order (4)", "alpha", ( (TestRule) matches.get( 0 ) ).getIdentifier() ); + assertEquals( "Rule Out Of Order (5)", "beta", ( (TestRule) matches.get( 1 ) ).getIdentifier() ); + assertEquals( "Rule Out Of Order (6)", "gamma", ( (TestRule) matches.get( 2 ) ).getIdentifier() ); } /** Test a mixed regex - in other words, one that sometimes returns true and sometimes false */ @@ -118,9 +130,9 @@ public boolean match( final String pathPattern, final String rulePattern ) assertEquals( "Wrong Rule (2)", "beta", ( (TestRule) matches.get( 0 ) ).getIdentifier() ); } - /** Test rules and clear methods */ + /** Test regex matcher that matches nothing */ @Test - public void testClear() + public void testMatchNothing() { // set up which should match every rule final RegexRules rules = new RegexRules( new RegexMatcher() @@ -128,33 +140,21 @@ public void testClear() @Override public boolean match( final String pathPattern, final String rulePattern ) { - return true; + return false; } } ); - rules.add( "/abba", new TestRule( "alpha" ) ); - rules.add( "/ad/ma", new TestRule( "beta" ) ); - rules.add( "/gamma", new TestRule( "gamma" ) ); - - // check that rules returns all rules in the order which they were added - List matches = rules.rules(); - assertEquals( "Wrong number of rules returned (1)", 3, matches.size() ); - assertEquals( "Rule Out Of Order (1)", "alpha", ( (TestRule) matches.get( 0 ) ).getIdentifier() ); - assertEquals( "Rule Out Of Order (2)", "beta", ( (TestRule) matches.get( 1 ) ).getIdentifier() ); - assertEquals( "Rule Out Of Order (3)", "gamma", ( (TestRule) matches.get( 2 ) ).getIdentifier() ); - - matches = rules.match( "", "/eggs", null, null ); - assertEquals( "Wrong number of rules returned (2)", 3, matches.size() ); - assertEquals( "Rule Out Of Order (4)", "alpha", ( (TestRule) matches.get( 0 ) ).getIdentifier() ); - assertEquals( "Rule Out Of Order (5)", "beta", ( (TestRule) matches.get( 1 ) ).getIdentifier() ); - assertEquals( "Rule Out Of Order (6)", "gamma", ( (TestRule) matches.get( 2 ) ).getIdentifier() ); + rules.add( "/b/c/f", new TestRule( "alpha" ) ); + rules.add( "/c/f", new TestRule( "beta" ) ); + rules.add( "/b", new TestRule( "gamma" ) ); - rules.clear(); - matches = rules.rules(); - assertEquals( "Wrong number of rules returned (3)", 0, matches.size() ); + // now test a few patterns + // check that all are return in the order which they were added + List matches = rules.match( "", "/b/c", null, null ); + assertEquals( "Wrong number of rules returned (1)", 0, matches.size() ); - matches = rules.match( "", "/eggs", null, null ); - assertEquals( "Wrong number of rules returned (4)", 0, matches.size() ); + matches = rules.match( "", "/b/c/f", null, null ); + assertEquals( "Wrong number of rules returned (2)", 0, matches.size() ); } @Test diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/RuleTestCase.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/RuleTestCase.java index 79eefb605..2460d01f5 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/RuleTestCase.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/RuleTestCase.java @@ -50,6 +50,20 @@ public class RuleTestCase // --------------------------------------------------- Overall Test Methods + /** + * Return an appropriate InputStream for the specified test file (which must be inside our current package. + * + * @param name Name of the test file we want + * @throws IOException if an input/output error occurs + */ + protected InputStream getInputStream( final String name ) + throws IOException + { + + return ( this.getClass().getResourceAsStream( "/org/apache/commons/digester3/" + name ) ); + + } + /** * Sets up instance variables required by this test case. */ @@ -61,6 +75,8 @@ public void setUp() } + // ------------------------------------------------ Individual Test Methods + /** * Tear down instance variables required by this test case. */ @@ -72,7 +88,19 @@ public void tearDown() } - // ------------------------------------------------ Individual Test Methods + /** + * Test rule addition - this boils down to making sure that digester is set properly on rule addition. + */ + @Test + public void testAddRule() + { + final Digester digester = new Digester(); + final TestRule rule = new TestRule( "Test" ); + digester.addRule( "/root", rule ); + + assertEquals( "Digester is not properly on rule addition.", digester, rule.getDigester() ); + + } /** * Test object creation (and associated property setting) with nothing on the stack, which should cause an @@ -312,65 +340,64 @@ public void testRuleSet3() } /** - * Test the two argument version of the SetTopRule rule. This test is based on testObjectCreate3 and should result - * in the same tree of objects. Instead of using the SetNextRule rule which results in a method invocation on the - * (top-1) (parent) object with the top object (child) as an argument, this test uses the SetTopRule rule which - * results in a method invocation on the top object (child) with the top-1 (parent) object as an argument. The three - * argument form is tested in {@code testSetTopRule2}. */ @Test - public void testSetTopRule1() + public void testSetCustomProperties() throws SAXException, IOException { - // Configure the digester as required - digester.addObjectCreate( "employee", "org.apache.commons.digester3.Employee" ); - digester.addSetProperties( "employee" ); - digester.addObjectCreate( "employee/address", "org.apache.commons.digester3.Address" ); - digester.addSetProperties( "employee/address" ); - digester.addSetTop( "employee/address", "setEmployee" ); + final Digester digester = new Digester(); - // Parse our test input. - Object root; - root = digester.parse( getInputStream( "Test1.xml" ) ); - validateObjectCreate3( root ); + digester.setValidating( false ); - } + digester.addObjectCreate( "toplevel", ArrayList.class ); + digester.addObjectCreate( "toplevel/one", Address.class ); + digester.addSetNext( "toplevel/one", "add" ); + digester.addObjectCreate( "toplevel/two", Address.class ); + digester.addSetNext( "toplevel/two", "add" ); + digester.addObjectCreate( "toplevel/three", Address.class ); + digester.addSetNext( "toplevel/three", "add" ); + digester.addObjectCreate( "toplevel/four", Address.class ); + digester.addSetNext( "toplevel/four", "add" ); + digester.addSetProperties( "toplevel/one" ); + digester.addSetProperties( "toplevel/two", new String[] { "alt-street", "alt-city", "alt-state" }, + new String[] { "street", "city", "state" } ); + digester.addSetProperties( "toplevel/three", new String[] { "aCity", "state" }, new String[] { "city" } ); + digester.addSetProperties( "toplevel/four", "alt-city", "city" ); - /** - * Same as {@code testSetTopRule1} except using the three argument form of the SetTopRule rule. - */ - @Test - public void testSetTopRule2() - throws SAXException, IOException - { + final ArrayList root = digester.parse( getInputStream( "Test7.xml" ) ); - // Configure the digester as required - digester.addObjectCreate( "employee", "org.apache.commons.digester3.Employee" ); - digester.addSetProperties( "employee" ); - digester.addObjectCreate( "employee/address", "org.apache.commons.digester3.Address" ); - digester.addSetProperties( "employee/address" ); - digester.addSetTop( "employee/address", "setEmployee", "org.apache.commons.digester3.Employee" ); + assertEquals( "Wrong array size", 4, root.size() ); - // Parse our test input. - Object root; - root = digester.parse( getInputStream( "Test1.xml" ) ); + // note that the array is in popped order (rather than pushed) - validateObjectCreate3( root ); + Object obj = root.get( 0 ); + assertTrue( "(1) Should be an Address ", obj instanceof Address ); + final Address addressOne = (Address) obj; + assertEquals( "(1) Street attribute", "New Street", addressOne.getStreet() ); + assertEquals( "(1) City attribute", "Las Vegas", addressOne.getCity() ); + assertEquals( "(1) State attribute", "Nevada", addressOne.getState() ); - } + obj = root.get( 1 ); + assertTrue( "(2) Should be an Address ", obj instanceof Address ); + final Address addressTwo = (Address) obj; + assertEquals( "(2) Street attribute", "Old Street", addressTwo.getStreet() ); + assertEquals( "(2) City attribute", "Portland", addressTwo.getCity() ); + assertEquals( "(2) State attribute", "Oregon", addressTwo.getState() ); - /** - * Test rule addition - this boils down to making sure that digester is set properly on rule addition. - */ - @Test - public void testAddRule() - { - final Digester digester = new Digester(); - final TestRule rule = new TestRule( "Test" ); - digester.addRule( "/root", rule ); + obj = root.get( 2 ); + assertTrue( "(3) Should be an Address ", obj instanceof Address ); + final Address addressThree = (Address) obj; + assertEquals( "(3) Street attribute", "4th Street", addressThree.getStreet() ); + assertEquals( "(3) City attribute", "Dayton", addressThree.getCity() ); + assertEquals( "(3) State attribute", "US", addressThree.getState() ); - assertEquals( "Digester is not properly on rule addition.", digester, rule.getDigester() ); + obj = root.get( 3 ); + assertTrue( "(4) Should be an Address ", obj instanceof Address ); + final Address addressFour = (Address) obj; + assertEquals( "(4) Street attribute", "6th Street", addressFour.getStreet() ); + assertEquals( "(4) City attribute", "Cleveland", addressFour.getCity() ); + assertEquals( "(4) State attribute", "Ohio", addressFour.getState() ); } @@ -453,80 +480,53 @@ public void testSetTop() } /** + * Test the two argument version of the SetTopRule rule. This test is based on testObjectCreate3 and should result + * in the same tree of objects. Instead of using the SetNextRule rule which results in a method invocation on the + * (top-1) (parent) object with the top object (child) as an argument, this test uses the SetTopRule rule which + * results in a method invocation on the top object (child) with the top-1 (parent) object as an argument. The three + * argument form is tested in {@code testSetTopRule2}. */ @Test - public void testSetCustomProperties() + public void testSetTopRule1() throws SAXException, IOException { - final Digester digester = new Digester(); - - digester.setValidating( false ); - - digester.addObjectCreate( "toplevel", ArrayList.class ); - digester.addObjectCreate( "toplevel/one", Address.class ); - digester.addSetNext( "toplevel/one", "add" ); - digester.addObjectCreate( "toplevel/two", Address.class ); - digester.addSetNext( "toplevel/two", "add" ); - digester.addObjectCreate( "toplevel/three", Address.class ); - digester.addSetNext( "toplevel/three", "add" ); - digester.addObjectCreate( "toplevel/four", Address.class ); - digester.addSetNext( "toplevel/four", "add" ); - digester.addSetProperties( "toplevel/one" ); - digester.addSetProperties( "toplevel/two", new String[] { "alt-street", "alt-city", "alt-state" }, - new String[] { "street", "city", "state" } ); - digester.addSetProperties( "toplevel/three", new String[] { "aCity", "state" }, new String[] { "city" } ); - digester.addSetProperties( "toplevel/four", "alt-city", "city" ); - - final ArrayList root = digester.parse( getInputStream( "Test7.xml" ) ); - - assertEquals( "Wrong array size", 4, root.size() ); - - // note that the array is in popped order (rather than pushed) - - Object obj = root.get( 0 ); - assertTrue( "(1) Should be an Address ", obj instanceof Address ); - final Address addressOne = (Address) obj; - assertEquals( "(1) Street attribute", "New Street", addressOne.getStreet() ); - assertEquals( "(1) City attribute", "Las Vegas", addressOne.getCity() ); - assertEquals( "(1) State attribute", "Nevada", addressOne.getState() ); - - obj = root.get( 1 ); - assertTrue( "(2) Should be an Address ", obj instanceof Address ); - final Address addressTwo = (Address) obj; - assertEquals( "(2) Street attribute", "Old Street", addressTwo.getStreet() ); - assertEquals( "(2) City attribute", "Portland", addressTwo.getCity() ); - assertEquals( "(2) State attribute", "Oregon", addressTwo.getState() ); - - obj = root.get( 2 ); - assertTrue( "(3) Should be an Address ", obj instanceof Address ); - final Address addressThree = (Address) obj; - assertEquals( "(3) Street attribute", "4th Street", addressThree.getStreet() ); - assertEquals( "(3) City attribute", "Dayton", addressThree.getCity() ); - assertEquals( "(3) State attribute", "US", addressThree.getState() ); + // Configure the digester as required + digester.addObjectCreate( "employee", "org.apache.commons.digester3.Employee" ); + digester.addSetProperties( "employee" ); + digester.addObjectCreate( "employee/address", "org.apache.commons.digester3.Address" ); + digester.addSetProperties( "employee/address" ); + digester.addSetTop( "employee/address", "setEmployee" ); - obj = root.get( 3 ); - assertTrue( "(4) Should be an Address ", obj instanceof Address ); - final Address addressFour = (Address) obj; - assertEquals( "(4) Street attribute", "6th Street", addressFour.getStreet() ); - assertEquals( "(4) City attribute", "Cleveland", addressFour.getCity() ); - assertEquals( "(4) State attribute", "Ohio", addressFour.getState() ); + // Parse our test input. + Object root; + root = digester.parse( getInputStream( "Test1.xml" ) ); + validateObjectCreate3( root ); } // ------------------------------------------------ Utility Support Methods /** - * Return an appropriate InputStream for the specified test file (which must be inside our current package. - * - * @param name Name of the test file we want - * @throws IOException if an input/output error occurs + * Same as {@code testSetTopRule1} except using the three argument form of the SetTopRule rule. */ - protected InputStream getInputStream( final String name ) - throws IOException + @Test + public void testSetTopRule2() + throws SAXException, IOException { - return ( this.getClass().getResourceAsStream( "/org/apache/commons/digester3/" + name ) ); + // Configure the digester as required + digester.addObjectCreate( "employee", "org.apache.commons.digester3.Employee" ); + digester.addSetProperties( "employee" ); + digester.addObjectCreate( "employee/address", "org.apache.commons.digester3.Address" ); + digester.addSetProperties( "employee/address" ); + digester.addSetTop( "employee/address", "setEmployee", "org.apache.commons.digester3.Employee" ); + + // Parse our test input. + Object root; + root = digester.parse( getInputStream( "Test1.xml" ) ); + + validateObjectCreate3( root ); } diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/RulesBaseTestCase.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/RulesBaseTestCase.java index 5b52fdb47..55b643a7f 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/RulesBaseTestCase.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/RulesBaseTestCase.java @@ -45,18 +45,6 @@ public class RulesBaseTestCase // -------------------------------------------------- Overall Test Methods - /** - * Sets up instance variables required by this test case. - */ - @Before - public void setUp() - { - - digester = new Digester(); - digester.setRules( createMatchingRulesForTest() ); - - } - /** *

    * This should be overriden by subclasses. @@ -69,98 +57,29 @@ protected Rules createMatchingRulesForTest() } /** - * Tear down instance variables required by this test case. + * Sets up instance variables required by this test case. */ - @After - public void tearDown() + @Before + public void setUp() { - digester = null; + digester = new Digester(); + digester.setRules( createMatchingRulesForTest() ); } - // ------------------------------------------------ Individual Test Methods - /** - * Basic test for rule creation and matching. + * Tear down instance variables required by this test case. */ - @Test - public void testRules() + @After + public void tearDown() { - // clear any existing rules - digester.getRules().clear(); - - // perform tests - - assertEquals( "Initial rules list is empty", 0, digester.getRules().match( null, "a", null, null ).size() ); - digester.addSetProperties( "a" ); - assertEquals( "Add a matching rule", 1, digester.getRules().match( null, "a", null, null ).size() ); - digester.addSetProperties( "b" ); - assertEquals( "Add a non-matching rule", 1, digester.getRules().match( null, "a", null, null ).size() ); - digester.addSetProperties( "a/b" ); - assertEquals( "Add a non-matching nested rule", 1, digester.getRules().match( null, "a", null, null ).size() ); - digester.addSetProperties( "a/b" ); - assertEquals( "Add a second matching rule", 2, digester.getRules().match( null, "a/b", null, null ).size() ); - - // clean up - digester.getRules().clear(); + digester = null; } - /** - *

    - * Test matching rules in {@link RulesBase}. - *

    - *

    - * Tests: - *

    - *
      - *
    • exact match
    • - *
    • tail match
    • - *
    • longest pattern rule
    • - *
    - */ - @Test - public void testRulesBase() - { - - // clear any existing rules - digester.getRules().clear(); - - assertEquals( "Initial rules list is empty", 0, digester.getRules().rules().size() ); - - // We're going to set up - digester.addRule( "a/b/c/d", new TestRule( "a/b/c/d" ) ); - digester.addRule( "*/d", new TestRule( "*/d" ) ); - digester.addRule( "*/c/d", new TestRule( "*/c/d" ) ); - - // Test exact match - assertEquals( "Exact match takes precedence 1", 1, digester.getRules().match( null, "a/b/c/d", null, null ).size() ); - assertEquals( "Exact match takes precedence 2", "a/b/c/d", - ( (TestRule) digester.getRules().match( null, "a/b/c/d", null, null ).iterator().next() ).getIdentifier() ); - - // Test wildcard tail matching - assertEquals( "Wildcard tail matching rule 1", 1, digester.getRules().match( null, "a/b/d", null, null ).size() ); - assertEquals( "Wildcard tail matching rule 2", "*/d", - ( (TestRule) digester.getRules().match( null, "a/b/d", null, null ).iterator().next() ).getIdentifier() ); - - // Test the longest matching pattern rule - assertEquals( "Longest tail rule 1", 1, digester.getRules().match( null, "x/c/d", null, null ).size() ); - assertEquals( "Longest tail rule 2", "*/c/d", - ( (TestRule) digester.getRules().match( null, "x/c/d", null, null ).iterator().next() ).getIdentifier() ); - - // Test wildcard tail matching at the top level, - // i.e. the wildcard is nothing - digester.addRule( "*/a", new TestRule( "*/a" ) ); - assertEquals( "Wildcard tail matching rule 3", 1, digester.getRules().match( null, "a", null, null ).size() ); - - assertEquals( "Wildcard tail matching rule 3 (match too much)", 0, - digester.getRules().match( null, "aa", null, null ).size() ); - // clean up - digester.getRules().clear(); - - } + // ------------------------------------------------ Individual Test Methods /** * Test basic matchings involving namespaces. @@ -243,6 +162,87 @@ public void testOrdering() } + /** + * Basic test for rule creation and matching. + */ + @Test + public void testRules() + { + + // clear any existing rules + digester.getRules().clear(); + + // perform tests + + assertEquals( "Initial rules list is empty", 0, digester.getRules().match( null, "a", null, null ).size() ); + digester.addSetProperties( "a" ); + assertEquals( "Add a matching rule", 1, digester.getRules().match( null, "a", null, null ).size() ); + digester.addSetProperties( "b" ); + assertEquals( "Add a non-matching rule", 1, digester.getRules().match( null, "a", null, null ).size() ); + digester.addSetProperties( "a/b" ); + assertEquals( "Add a non-matching nested rule", 1, digester.getRules().match( null, "a", null, null ).size() ); + digester.addSetProperties( "a/b" ); + assertEquals( "Add a second matching rule", 2, digester.getRules().match( null, "a/b", null, null ).size() ); + + // clean up + digester.getRules().clear(); + + } + + /** + *

    + * Test matching rules in {@link RulesBase}. + *

    + *

    + * Tests: + *

    + *
      + *
    • exact match
    • + *
    • tail match
    • + *
    • longest pattern rule
    • + *
    + */ + @Test + public void testRulesBase() + { + + // clear any existing rules + digester.getRules().clear(); + + assertEquals( "Initial rules list is empty", 0, digester.getRules().rules().size() ); + + // We're going to set up + digester.addRule( "a/b/c/d", new TestRule( "a/b/c/d" ) ); + digester.addRule( "*/d", new TestRule( "*/d" ) ); + digester.addRule( "*/c/d", new TestRule( "*/c/d" ) ); + + // Test exact match + assertEquals( "Exact match takes precedence 1", 1, digester.getRules().match( null, "a/b/c/d", null, null ).size() ); + assertEquals( "Exact match takes precedence 2", "a/b/c/d", + ( (TestRule) digester.getRules().match( null, "a/b/c/d", null, null ).iterator().next() ).getIdentifier() ); + + // Test wildcard tail matching + assertEquals( "Wildcard tail matching rule 1", 1, digester.getRules().match( null, "a/b/d", null, null ).size() ); + assertEquals( "Wildcard tail matching rule 2", "*/d", + ( (TestRule) digester.getRules().match( null, "a/b/d", null, null ).iterator().next() ).getIdentifier() ); + + // Test the longest matching pattern rule + assertEquals( "Longest tail rule 1", 1, digester.getRules().match( null, "x/c/d", null, null ).size() ); + assertEquals( "Longest tail rule 2", "*/c/d", + ( (TestRule) digester.getRules().match( null, "x/c/d", null, null ).iterator().next() ).getIdentifier() ); + + // Test wildcard tail matching at the top level, + // i.e. the wildcard is nothing + digester.addRule( "*/a", new TestRule( "*/a" ) ); + assertEquals( "Wildcard tail matching rule 3", 1, digester.getRules().match( null, "a", null, null ).size() ); + + assertEquals( "Wildcard tail matching rule 3 (match too much)", 0, + digester.getRules().match( null, "aa", null, null ).size() ); + // clean up + digester.getRules().clear(); + + } + /** Tests the behavior when a rule is added with a trailing slash */ @Test public void testTrailingSlash() diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/SetNestedPropertiesRuleTestCase.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/SetNestedPropertiesRuleTestCase.java index ef6550947..63a048d8c 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/SetNestedPropertiesRuleTestCase.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/SetNestedPropertiesRuleTestCase.java @@ -81,51 +81,6 @@ protected void configure() assertEquals( "Property delta not set correctly", "DELTA BODY", bean.getDeltaValue() ); } - /** - * Test that it is an error when a child element exists but no corresponding java property exists. - */ - @Test - public void testMandatoryProperties() - throws SAXException, IOException - { - final Digester digester = newLoader( new AbstractRulesModule() - { - - @Override - protected void configure() - { - forPattern( "root" ).createObject().ofType( "org.apache.commons.digester3.SimpleTestBean" ) - .then() - .setNestedProperties(); - } - - }).newDigester(); - - final String TEST_XML = "" + "ROOT BODY" + "ALPHA BODY" + ""; - - try - { - final SimpleTestBean bean = digester.parse( new StringReader( TEST_XML ) ); - - // we should never get here... - fail( "No exception thrown by parse when unknown child element found." ); - assertNotNull( bean ); // just to prevent compiler warning on unused var - } - catch ( final org.xml.sax.SAXParseException e ) - { - final String msg = e.getMessage(); - if ( msg.indexOf( "badprop" ) >= 0 ) - { - // ok, this is expected; there is no "setBadprop" method on the - // SimpleTestBean class... - } - else - { - fail( "Unexpected parse exception:" + e.getMessage() ); - } - } - } - /** * Test that you can customise the property mappings using the constructor which takes arrays-of-strings. */ @@ -248,6 +203,51 @@ protected void configure() assertEquals( "Digester rules object not reset.", RulesBase.class, digester.getRules().getClass() ); } + /** + * Test that it is an error when a child element exists but no corresponding java property exists. + */ + @Test + public void testMandatoryProperties() + throws SAXException, IOException + { + final Digester digester = newLoader( new AbstractRulesModule() + { + + @Override + protected void configure() + { + forPattern( "root" ).createObject().ofType( "org.apache.commons.digester3.SimpleTestBean" ) + .then() + .setNestedProperties(); + } + + }).newDigester(); + + final String TEST_XML = "" + "ROOT BODY" + "ALPHA BODY" + ""; + + try + { + final SimpleTestBean bean = digester.parse( new StringReader( TEST_XML ) ); + + // we should never get here... + fail( "No exception thrown by parse when unknown child element found." ); + assertNotNull( bean ); // just to prevent compiler warning on unused var + } + catch ( final org.xml.sax.SAXParseException e ) + { + final String msg = e.getMessage(); + if ( msg.indexOf( "badprop" ) >= 0 ) + { + // ok, this is expected; there is no "setBadprop" method on the + // SimpleTestBean class... + } + else + { + fail( "Unexpected parse exception:" + e.getMessage() ); + } + } + } + /** * Test that: *
      @@ -300,6 +300,39 @@ protected void configure() assertEquals( "Digester rules object not reset.", RulesBase.class, digester.getRules().getClass() ); } + /** + * Test that the rule works in a sane manner when the associated pattern is a wildcard such that the rule matches + * one of its own child elements. + *

      + * See bugzilla entry 31393. + */ + @Test + public void testRecursiveNestedProperties() + throws SAXException, IOException + { + final Digester digester = newLoader( new AbstractRulesModule() + { + + @Override + protected void configure() + { + forPattern( "*/testbean" ).createObject().ofType( "org.apache.commons.digester3.SimpleTestBean" ) + .then() + .setNestedProperties().allowUnknownChildElements( true ); + } + + }).newDigester(); + + final String testXml = + "" + "" + "BETA BODY" + "" + "BETA BODY" + + "" + ""; + + final Reader reader = new StringReader( testXml ); + + final SimpleTestBean bean = digester.parse( reader ); + assertNotNull( bean ); + } + /** * Test that unknown child elements trigger an exception. */ @@ -373,39 +406,6 @@ protected void configure() assertNotNull( bean ); } - /** - * Test that the rule works in a sane manner when the associated pattern is a wildcard such that the rule matches - * one of its own child elements. - *

      - * See bugzilla entry 31393. - */ - @Test - public void testRecursiveNestedProperties() - throws SAXException, IOException - { - final Digester digester = newLoader( new AbstractRulesModule() - { - - @Override - protected void configure() - { - forPattern( "*/testbean" ).createObject().ofType( "org.apache.commons.digester3.SimpleTestBean" ) - .then() - .setNestedProperties().allowUnknownChildElements( true ); - } - - }).newDigester(); - - final String testXml = - "" + "" + "BETA BODY" + "" + "BETA BODY" - + "" + ""; - - final Reader reader = new StringReader( testXml ); - - final SimpleTestBean bean = digester.parse( reader ); - assertNotNull( bean ); - } - /** * Gets input stream from {@link #TEST_XML}. */ diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/SetPropertiesRuleTestCase.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/SetPropertiesRuleTestCase.java index dc4cb86a7..e7779ac69 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/SetPropertiesRuleTestCase.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/SetPropertiesRuleTestCase.java @@ -61,37 +61,6 @@ public class SetPropertiesRuleTestCase // ------------------------------------------------ Individual Test Methods - /** - * Positive test for SetPropertiesRule. - */ - @Test - public void testPositive() - throws Exception - { - final Digester digester = newLoader( new AbstractRulesModule() - { - - @Override - protected void configure() - { - forPattern( "root" ).createObject().ofType( "org.apache.commons.digester3.SimpleTestBean" ) - .then() - .setProperties(); - } - - }).newDigester(); - - // Parse the input - final SimpleTestBean bean = digester.parse( xmlTestReader( TEST_XML_1 ) ); - - // Check that the properties were set correctly - assertEquals( "alpha property set", "ALPHA VALUE", bean.getAlpha() ); - assertEquals( "beta property set", "BETA VALUE", bean.getBeta() ); - assertNull( "gamma property not set", bean.getGamma() ); - assertEquals( "delta property set", "DELTA VALUE", bean.getDeltaValue() ); - - } - /** * Positive test for SetPropertyRule ignoring missing properties. */ @@ -182,6 +151,37 @@ else if ( e instanceof SAXException ) } } + /** + * Positive test for SetPropertiesRule. + */ + @Test + public void testPositive() + throws Exception + { + final Digester digester = newLoader( new AbstractRulesModule() + { + + @Override + protected void configure() + { + forPattern( "root" ).createObject().ofType( "org.apache.commons.digester3.SimpleTestBean" ) + .then() + .setProperties(); + } + + }).newDigester(); + + // Parse the input + final SimpleTestBean bean = digester.parse( xmlTestReader( TEST_XML_1 ) ); + + // Check that the properties were set correctly + assertEquals( "alpha property set", "ALPHA VALUE", bean.getAlpha() ); + assertEquals( "beta property set", "BETA VALUE", bean.getBeta() ); + assertNull( "gamma property not set", bean.getGamma() ); + assertEquals( "delta property set", "DELTA VALUE", bean.getDeltaValue() ); + + } + /** * Negative test for SetPropertyRule ignoring missing properties. */ diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/SetPropertyRuleTestCase.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/SetPropertyRuleTestCase.java index 0e964f414..5780b6982 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/SetPropertyRuleTestCase.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/SetPropertyRuleTestCase.java @@ -102,21 +102,18 @@ public void tearDown() // ------------------------------------------------ Individual Test Methods /** - * Positive test for SetPropertyRule. + * Test SetPropertyRule when matched XML element has no attributes. See: DIGESTER-114 */ @Test - public void testPositive() + public void testElementWithNoAttributes() throws Exception { - // Parse the input - final SimpleTestBean bean = digester.parse( xmlTestReader( TEST_XML_1 ) ); - - // Check that the properties were set correctly - assertEquals( "alpha property set", "ALPHA VALUE", bean.getAlpha() ); - assertEquals( "beta property set", "BETA VALUE", bean.getBeta() ); - assertNull( "gamma property not set", bean.getGamma() ); - assertEquals( "delta property set", "DELTA VALUE", bean.getDeltaValue() ); + final String TEST_XML_3 = ""; + // Parse the input - should not throw an exception + @SuppressWarnings( "unused" ) + final + SimpleTestBean bean = digester.parse( xmlTestReader( TEST_XML_3 ) ); } /** @@ -178,26 +175,29 @@ else if ( e instanceof SAXException ) } /** - * Gets input stream from specified String containing XML data. + * Positive test for SetPropertyRule. */ - private Reader xmlTestReader( final String xml ) + @Test + public void testPositive() + throws Exception { - return new StringReader( xml ); + // Parse the input + final SimpleTestBean bean = digester.parse( xmlTestReader( TEST_XML_1 ) ); + + // Check that the properties were set correctly + assertEquals( "alpha property set", "ALPHA VALUE", bean.getAlpha() ); + assertEquals( "beta property set", "BETA VALUE", bean.getBeta() ); + assertNull( "gamma property not set", bean.getGamma() ); + assertEquals( "delta property set", "DELTA VALUE", bean.getDeltaValue() ); + } /** - * Test SetPropertyRule when matched XML element has no attributes. See: DIGESTER-114 + * Gets input stream from specified String containing XML data. */ - @Test - public void testElementWithNoAttributes() - throws Exception + private Reader xmlTestReader( final String xml ) { - final String TEST_XML_3 = ""; - - // Parse the input - should not throw an exception - @SuppressWarnings( "unused" ) - final - SimpleTestBean bean = digester.parse( xmlTestReader( TEST_XML_3 ) ); + return new StringReader( xml ); } } diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/SimpleTestBean.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/SimpleTestBean.java index 06e80a755..c6b90f3a6 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/SimpleTestBean.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/SimpleTestBean.java @@ -38,19 +38,14 @@ public String getAlpha() return alpha; } - public void setAlpha( final String alpha ) - { - this.alpha = alpha; - } - public String getBeta() { return beta; } - public void setBeta( final String beta ) - { - this.beta = beta; + public String getDeltaValue() + { // Retrieves "write only" value + return delta; } public String getGamma() @@ -58,14 +53,20 @@ public String getGamma() return gamma; } - public void setGamma( final String gamma ) + public void setAlpha( final String alpha ) { - this.gamma = gamma; + this.alpha = alpha; } - public String getDeltaValue() - { // Retrieves "write only" value - return delta; + public void setAlphaBeta( final String alpha, final String beta ) + { + setAlpha( alpha ); + setBeta( beta ); + } + + public void setBeta( final String beta ) + { + this.beta = beta; } public void setDelta( final String delta ) @@ -73,10 +74,9 @@ public void setDelta( final String delta ) this.delta = delta; } - public void setAlphaBeta( final String alpha, final String beta ) + public void setGamma( final String gamma ) { - setAlpha( alpha ); - setBeta( beta ); + this.gamma = gamma; } @Override diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/TestBean.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/TestBean.java index 920def3c7..84b2f20c8 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/TestBean.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/TestBean.java @@ -27,6 +27,89 @@ public class TestBean { + /** + * A boolean property whose initial value is true. + */ + private boolean booleanProperty = true; + + /** + * A double property. + */ + private double doubleProperty = 321.0; + + /** + * A boolean property whose initial value is false + */ + private boolean falseProperty; + + // ------------------------------------------------------------- Properties + + /** + * A float property. + */ + private float floatProperty = (float) 123.0; + + /** + * Integer arrays that are accessed as an array as well as indexed. + */ + private int[] intArray = { 0, 10, 20, 30, 40 }; + + private final int[] intIndexed = { 0, 10, 20, 30, 40 }; + + private int[] intMultibox = new int[0]; + + /** + * An integer property. + */ + private int intProperty = 123; + + /** + * A long property. + */ + private long longProperty = 321; + + /** + * A multiple-String SELECT element. + */ + private String[] multipleSelect = { "Multiple 3", "Multiple 5", "Multiple 7" }; + + /** + * A nested reference to another test bean (populated as needed). + */ + private TestBean nested; + + /** + * A String property with an initial value of null. + */ + private String nullProperty; + + /** + * A short property. + */ + private short shortProperty = (short) 987; + + /** + * A single-String value for a SELECT element. + */ + private String singleSelect = "Single 5"; + + /** + * String arrays that are accessed as an array as well as indexed. + */ + private String[] stringArray = { "String 0", "String 1", "String 2", "String 3", "String 4" }; + + private final String[] stringIndexed = { "String 0", "String 1", "String 2", "String 3", "String 4" }; + + /** + * A String property. + */ + private String stringProperty = "This is a string"; + + /** + * An empty String property. + */ + private String emptyStringProperty = ""; + public TestBean() { // do nothing @@ -51,157 +134,61 @@ public TestBean( final Boolean booleanProperty, final Double doubleProperty ) this( booleanProperty.booleanValue(), doubleProperty.doubleValue() ); } - // ------------------------------------------------------------- Properties - - /** - * A boolean property whose initial value is true. - */ - private boolean booleanProperty = true; - public boolean getBooleanProperty() { return ( booleanProperty ); } - public void setBooleanProperty( final boolean booleanProperty ) - { - this.booleanProperty = booleanProperty; - } - - /** - * A double property. - */ - private double doubleProperty = 321.0; - public double getDoubleProperty() { return ( this.doubleProperty ); } - public void setDoubleProperty( final double doubleProperty ) + public String getEmptyStringProperty() { - this.doubleProperty = doubleProperty; + return ( this.emptyStringProperty ); } - /** - * A boolean property whose initial value is false - */ - private boolean falseProperty; - public boolean getFalseProperty() { return ( falseProperty ); } - public void setFalseProperty( final boolean falseProperty ) - { - this.falseProperty = falseProperty; - } - - /** - * A float property. - */ - private float floatProperty = (float) 123.0; - public float getFloatProperty() { return ( this.floatProperty ); } - public void setFloatProperty( final float floatProperty ) - { - this.floatProperty = floatProperty; - } - - /** - * Integer arrays that are accessed as an array as well as indexed. - */ - private int[] intArray = { 0, 10, 20, 30, 40 }; - public int[] getIntArray() { return ( this.intArray ); } - public void setIntArray( final int intArray[] ) - { - this.intArray = intArray; - } - - private final int[] intIndexed = { 0, 10, 20, 30, 40 }; - public int getIntIndexed( final int index ) { return ( intIndexed[index] ); } - public void setIntIndexed( final int index, final int value ) - { - intIndexed[index] = value; - } - - private int[] intMultibox = new int[0]; - public int[] getIntMultibox() { return ( this.intMultibox ); } - public void setIntMultibox( final int intMultibox[] ) - { - this.intMultibox = intMultibox; - } - - /** - * An integer property. - */ - private int intProperty = 123; - public int getIntProperty() { return ( this.intProperty ); } - public void setIntProperty( final int intProperty ) - { - this.intProperty = intProperty; - } - - /** - * A long property. - */ - private long longProperty = 321; - public long getLongProperty() { return ( this.longProperty ); } - public void setLongProperty( final long longProperty ) - { - this.longProperty = longProperty; - } - - /** - * A multiple-String SELECT element. - */ - private String[] multipleSelect = { "Multiple 3", "Multiple 5", "Multiple 7" }; - public String[] getMultipleSelect() { return ( this.multipleSelect ); } - public void setMultipleSelect( final String multipleSelect[] ) - { - this.multipleSelect = multipleSelect; - } - - /** - * A nested reference to another test bean (populated as needed). - */ - private TestBean nested; - public TestBean getNested() { if ( nested == null ) { @@ -210,106 +197,119 @@ public TestBean getNested() return ( nested ); } - /** - * A String property with an initial value of null. - */ - private String nullProperty; - public String getNullProperty() { return ( this.nullProperty ); } - public void setNullProperty( final String nullProperty ) + public short getShortProperty() { - this.nullProperty = nullProperty; + return ( this.shortProperty ); } - /** - * A short property. - */ - private short shortProperty = (short) 987; + public String getSingleSelect() + { + return ( this.singleSelect ); + } - public short getShortProperty() + public String[] getStringArray() { - return ( this.shortProperty ); + return ( this.stringArray ); } - public void setShortProperty( final short shortProperty ) + public String getStringIndexed( final int index ) { - this.shortProperty = shortProperty; + return ( stringIndexed[index] ); } - /** - * A single-String value for a SELECT element. - */ - private String singleSelect = "Single 5"; + public String getStringProperty() + { + return ( this.stringProperty ); + } - public String getSingleSelect() + public void setBooleanProperty( final boolean booleanProperty ) { - return ( this.singleSelect ); + this.booleanProperty = booleanProperty; } - public void setSingleSelect( final String singleSelect ) + public void setDoubleProperty( final double doubleProperty ) { - this.singleSelect = singleSelect; + this.doubleProperty = doubleProperty; } - /** - * String arrays that are accessed as an array as well as indexed. - */ - private String[] stringArray = { "String 0", "String 1", "String 2", "String 3", "String 4" }; + public void setEmptyStringProperty( final String emptyStringProperty ) + { + this.emptyStringProperty = emptyStringProperty; + } - public String[] getStringArray() + public void setFalseProperty( final boolean falseProperty ) { - return ( this.stringArray ); + this.falseProperty = falseProperty; } - public void setStringArray( final String stringArray[] ) + public void setFloatProperty( final float floatProperty ) { - this.stringArray = stringArray; + this.floatProperty = floatProperty; } - private final String[] stringIndexed = { "String 0", "String 1", "String 2", "String 3", "String 4" }; + public void setIntArray( final int intArray[] ) + { + this.intArray = intArray; + } - public String getStringIndexed( final int index ) + public void setIntIndexed( final int index, final int value ) { - return ( stringIndexed[index] ); + intIndexed[index] = value; } - public void setStringIndexed( final int index, final String value ) + public void setIntMultibox( final int intMultibox[] ) { - stringIndexed[index] = value; + this.intMultibox = intMultibox; } - /** - * A String property. - */ - private String stringProperty = "This is a string"; + public void setIntProperty( final int intProperty ) + { + this.intProperty = intProperty; + } - public String getStringProperty() + public void setLongProperty( final long longProperty ) { - return ( this.stringProperty ); + this.longProperty = longProperty; } - public void setStringProperty( final String stringProperty ) + public void setMultipleSelect( final String multipleSelect[] ) { - this.stringProperty = stringProperty; + this.multipleSelect = multipleSelect; } - /** - * An empty String property. - */ - private String emptyStringProperty = ""; + public void setNullProperty( final String nullProperty ) + { + this.nullProperty = nullProperty; + } - public String getEmptyStringProperty() + public void setShortProperty( final short shortProperty ) { - return ( this.emptyStringProperty ); + this.shortProperty = shortProperty; } - public void setEmptyStringProperty( final String emptyStringProperty ) + public void setSingleSelect( final String singleSelect ) { - this.emptyStringProperty = emptyStringProperty; + this.singleSelect = singleSelect; + } + + public void setStringArray( final String stringArray[] ) + { + this.stringArray = stringArray; + } + + public void setStringIndexed( final int index, final String value ) + { + stringIndexed[index] = value; + } + + public void setStringProperty( final String stringProperty ) + { + this.stringProperty = stringProperty; } } diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/TestEntityResolution.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/TestEntityResolution.java index aec849126..f18dc56e6 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/TestEntityResolution.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/TestEntityResolution.java @@ -31,6 +31,15 @@ public class TestEntityResolution { + @Test + public void testDigesterResolveRelative() + throws Exception + { + final Digester digester = new Digester(); + digester.setValidating( true ); + digester.parse( new File( "src/test/resources/org/apache/commons/digester3/document-with-relative-dtd.xml" ) ); + } + @Test public void testParserResolveRelative() throws Exception @@ -43,13 +52,4 @@ public void testParserResolveRelative() parser.parse( new File( "src/test/resources/org/apache/commons/digester3/document-with-relative-dtd.xml" ), new DefaultHandler() ); } - - @Test - public void testDigesterResolveRelative() - throws Exception - { - final Digester digester = new Digester(); - digester.setValidating( true ); - digester.parse( new File( "src/test/resources/org/apache/commons/digester3/document-with-relative-dtd.xml" ) ); - } } diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/TestFactoryCreate.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/TestFactoryCreate.java index d9ef8292e..87d34b7fb 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/TestFactoryCreate.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/TestFactoryCreate.java @@ -39,13 +39,6 @@ public class TestFactoryCreate { - private final boolean ignoreCreateExceptions; - - public TestFactoryCreate( final boolean ignoreCreateExceptions ) - { - this.ignoreCreateExceptions = ignoreCreateExceptions; - } - @Parameters public static Collection data() { @@ -57,50 +50,15 @@ public static Collection data() return data; } - // --------------------------------------------------------------- Test cases + private final boolean ignoreCreateExceptions; - @Test - public void testPropagateException() - throws Exception + public TestFactoryCreate( final boolean ignoreCreateExceptions ) { - - // only used with this method - class ThrowExceptionCreateRule - extends AbstractObjectCreationFactory - { - @Override - public Object createObject( final Attributes attributes ) - throws Exception - { - throw new RuntimeException(); - } - } - - // now for the tests - final String xml = ""; - - // test default - which is to propagate the exception - final Digester digester = new Digester(); - digester.addFactoryCreate( "root", new ThrowExceptionCreateRule(), ignoreCreateExceptions ); - try - { - - digester.parse( new StringReader( xml ) ); - if ( !ignoreCreateExceptions ) - { - fail( "Exception should be propagated from create rule" ); - } - - } - catch ( final Exception e ) - { - if ( ignoreCreateExceptions ) - { - fail( "Exception should not be propagated" ); - } - } + this.ignoreCreateExceptions = ignoreCreateExceptions; } + // --------------------------------------------------------------- Test cases + @Test public void testFactoryCreateRule() throws Exception @@ -222,4 +180,46 @@ public void testFactoryCreateRule() assertEquals( "Attribute not passed (18)[" + ignoreCreateExceptions + "]", factory.attributes.getValue( "three" ), "ugly" ); } + + @Test + public void testPropagateException() + throws Exception + { + + // only used with this method + class ThrowExceptionCreateRule + extends AbstractObjectCreationFactory + { + @Override + public Object createObject( final Attributes attributes ) + throws Exception + { + throw new RuntimeException(); + } + } + + // now for the tests + final String xml = ""; + + // test default - which is to propagate the exception + final Digester digester = new Digester(); + digester.addFactoryCreate( "root", new ThrowExceptionCreateRule(), ignoreCreateExceptions ); + try + { + + digester.parse( new StringReader( xml ) ); + if ( !ignoreCreateExceptions ) + { + fail( "Exception should be propagated from create rule" ); + } + + } + catch ( final Exception e ) + { + if ( ignoreCreateExceptions ) + { + fail( "Exception should not be propagated" ); + } + } + } } diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/TestRule.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/TestRule.java index 781623cbc..b0f2dc782 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/TestRule.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/TestRule.java @@ -36,17 +36,45 @@ public class TestRule // ----------------------------------------------------- Instance Variables + public static class TestRuleProvider implements RuleProvider + { + + private final String identifier; + + private final List callOrder; + + public TestRuleProvider( final String identifier ) + { + this( identifier, null ); + } + + public TestRuleProvider( final String identifier, final List callOrder ) + { + this.identifier = identifier; + this.callOrder = callOrder; + } + + @Override + public TestRule get() + { + final TestRule testRule = new TestRule( identifier ); + testRule.setOrder( callOrder ); + return testRule; + } + + } + /** String identifing this particular {@code TestRule} */ private final String identifier; /** Used when testing body text */ private String bodyText; + // ----------------------------------------------------------- Constructors + /** Used when testing call orders */ private List order; - // ----------------------------------------------------------- Constructors - /** * Base constructor. * @@ -58,6 +86,8 @@ public TestRule( final String identifier ) this.identifier = identifier; } + // ------------------------------------------------ Rule Implementation + /** * Constructor sets namespace URI. * @@ -72,7 +102,15 @@ public TestRule( final String identifier, final String namespaceURI ) } - // ------------------------------------------------ Rule Implementation + /** + * If a list has been set, append this to the list. + */ + protected void appendCall() + { + if ( order != null ) { + order.add( this ); + } + } /** * 'Begin' call. @@ -84,6 +122,8 @@ public void begin( final String namespace, final String name, final Attributes a appendCall(); } + // ------------------------------------------------ Methods + /** * 'Body' call. */ @@ -105,18 +145,6 @@ public void end( final String namespace, final String name ) appendCall(); } - // ------------------------------------------------ Methods - - /** - * If a list has been set, append this to the list. - */ - protected void appendCall() - { - if ( order != null ) { - order.add( this ); - } - } - /** * Gets the body text that was set. */ @@ -158,32 +186,4 @@ public String toString() return identifier; } - public static class TestRuleProvider implements RuleProvider - { - - private final String identifier; - - private final List callOrder; - - public TestRuleProvider( final String identifier ) - { - this( identifier, null ); - } - - public TestRuleProvider( final String identifier, final List callOrder ) - { - this.identifier = identifier; - this.callOrder = callOrder; - } - - @Override - public TestRule get() - { - final TestRule testRule = new TestRule( identifier ); - testRule.setOrder( callOrder ); - return testRule; - } - - } - } diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/TestRuleSet.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/TestRuleSet.java index 93e13fb7b..38bc352d3 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/TestRuleSet.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/TestRuleSet.java @@ -28,6 +28,11 @@ public class TestRuleSet // ----------------------------------------------------------- Constructors + /** + * The prefix for each matching pattern added to the Digester instance, or an empty String for no prefix. + */ + protected String prefix; + /** * Construct an instance of this RuleSet with default values. */ @@ -50,6 +55,8 @@ public TestRuleSet( final String prefix ) } + // ----------------------------------------------------- Instance Variables + /** * Construct an instance of this RuleSet associated with the specified prefix and namespace URI. * @@ -71,13 +78,6 @@ public TestRuleSet( final String prefix, final String namespaceURI ) } - // ----------------------------------------------------- Instance Variables - - /** - * The prefix for each matching pattern added to the Digester instance, or an empty String for no prefix. - */ - protected String prefix; - // --------------------------------------------------------- Public Methods /** diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/URLTestCase.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/URLTestCase.java index 6325647d9..d33f5bccf 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/URLTestCase.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/URLTestCase.java @@ -36,30 +36,6 @@ public class URLTestCase // ----------------------------------------------------- Overall Test Methods - /** - * Sets up instance variables required by this test case. - */ - @Before - public void setUp() - { - - digester = new Digester(); - - } - - /** - * Tear down instance variables required by this test case. - */ - @After - public void tearDown() - { - - digester = null; - - } - - // ------------------------------------------------------ Manifest Constants - /** *

      * Public identifier of the Digester Rules DTD. @@ -74,6 +50,8 @@ public void tearDown() */ private static final String DIGESTER_RULES_SYSTEM_ID = "/org/apache/commons/digester3/xmlrules/digester-rules.dtd"; + // ------------------------------------------------------ Manifest Constants + /** *

      * System identifier for the Digester Rules file that we will parse. @@ -82,8 +60,6 @@ public void tearDown() private static final String TEST_INPUT_SYSTEM_ID = "/org/apache/commons/digester3/xmlrules/test-call-param-rules.xml"; - // ------------------------------------------------------ Instance Variables - /** *

      * The {@code Digester} instance under test. @@ -91,6 +67,30 @@ public void tearDown() */ private Digester digester; + /** + * Sets up instance variables required by this test case. + */ + @Before + public void setUp() + { + + digester = new Digester(); + + } + + // ------------------------------------------------------ Instance Variables + + /** + * Tear down instance variables required by this test case. + */ + @After + public void tearDown() + { + + digester = null; + + } + // ------------------------------------------------------------ Test Methods // Test a pristine instance diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/WithDefaultsRulesWrapperTestCase.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/WithDefaultsRulesWrapperTestCase.java index f4c741944..fb7d90bfd 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/WithDefaultsRulesWrapperTestCase.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/WithDefaultsRulesWrapperTestCase.java @@ -60,23 +60,6 @@ public void testClear() assertEquals( "Clear Failed (3)", 0, rules.rules().size() ); } - @Test - public void testRules() - { - // test rules - final WithDefaultsRulesWrapper rules = new WithDefaultsRulesWrapper( new RulesBase() ); - rules.add( "alpha", new TestRule( "Tom" ) ); - rules.add( "alpha", new TestRule( "Dick" ) ); - rules.addDefault( new TestRule( "Roger" ) ); - rules.add( "alpha", new TestRule( "Harry" ) ); - - assertNotNull( "Rules should not be null", rules.rules() ); - assertEquals( "Wrong order (1)", "Tom", ( (TestRule) rules.rules().get( 0 ) ).getIdentifier() ); - assertEquals( "Wrong order (2)", "Dick", ( (TestRule) rules.rules().get( 1 ) ).getIdentifier() ); - assertEquals( "Wrong order (3)", "Roger", ( (TestRule) rules.rules().get( 2 ) ).getIdentifier() ); - assertEquals( "Wrong order (4)", "Harry", ( (TestRule) rules.rules().get( 3 ) ).getIdentifier() ); - } - @Test public void testMatch() { @@ -99,4 +82,21 @@ public void testMatch() assertEquals( "Wrong order (4)", "Roger", ( (TestRule) matches.get( 0 ) ).getIdentifier() ); assertEquals( "Wrong order (5)", "Rabbit", ( (TestRule) matches.get( 1 ) ).getIdentifier() ); } + + @Test + public void testRules() + { + // test rules + final WithDefaultsRulesWrapper rules = new WithDefaultsRulesWrapper( new RulesBase() ); + rules.add( "alpha", new TestRule( "Tom" ) ); + rules.add( "alpha", new TestRule( "Dick" ) ); + rules.addDefault( new TestRule( "Roger" ) ); + rules.add( "alpha", new TestRule( "Harry" ) ); + + assertNotNull( "Rules should not be null", rules.rules() ); + assertEquals( "Wrong order (1)", "Tom", ( (TestRule) rules.rules().get( 0 ) ).getIdentifier() ); + assertEquals( "Wrong order (2)", "Dick", ( (TestRule) rules.rules().get( 1 ) ).getIdentifier() ); + assertEquals( "Wrong order (3)", "Roger", ( (TestRule) rules.rules().get( 2 ) ).getIdentifier() ); + assertEquals( "Wrong order (4)", "Harry", ( (TestRule) rules.rules().get( 3 ) ).getIdentifier() ); + } } diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/XIncludeTestCase.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/XIncludeTestCase.java index 019d0685f..8298ca83d 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/XIncludeTestCase.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/XIncludeTestCase.java @@ -39,6 +39,22 @@ public class XIncludeTestCase // ------------------------------------------------ Individual Test Methods + /** + * Return an appropriate InputStream for the specified test file (which must be inside our current package. + * + * @param name Name of the test file we want + * @throws IOException if an input/output error occurs + */ + protected InputStream getInputStream( final String name ) + throws IOException + { + + return ( this.getClass().getResourceAsStream( "/org/apache/commons/digester3/" + name ) ); + + } + + // ------------------------------------------------ Utility Support Methods + /** * Test XInclude. */ @@ -89,20 +105,4 @@ protected void configure() } - // ------------------------------------------------ Utility Support Methods - - /** - * Return an appropriate InputStream for the specified test file (which must be inside our current package. - * - * @param name Name of the test file we want - * @throws IOException if an input/output error occurs - */ - protected InputStream getInputStream( final String name ) - throws IOException - { - - return ( this.getClass().getResourceAsStream( "/org/apache/commons/digester3/" + name ) ); - - } - } diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/XMLSchemaTestCase.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/XMLSchemaTestCase.java index 45dff0121..d41dd35d1 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/XMLSchemaTestCase.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/XMLSchemaTestCase.java @@ -47,12 +47,56 @@ public class XMLSchemaTestCase // ----------------------------------------------------- Instance Variables + static final class TestErrorHandler + implements ErrorHandler + { + public boolean clean = true; + + public TestErrorHandler() + { + } + + @Override + public void error( final SAXParseException exception ) + { + clean = false; + } + + @Override + public void fatalError( final SAXParseException exception ) + { + clean = false; + } + + @Override + public void warning( final SAXParseException exception ) + { + clean = false; + } + } + + // --------------------------------------------------- Overall Test Methods + /** * The digester instance we will be processing. */ protected Digester digester; - // --------------------------------------------------- Overall Test Methods + /** + * Return an appropriate InputStream for the specified test file (which must be inside our current package. + * + * @param name Name of the test file we want + * @throws IOException if an input/output error occurs + */ + protected InputStream getInputStream( final String name ) + throws IOException + { + + return ( this.getClass().getResourceAsStream( "/org/apache/commons/digester3/" + name ) ); + + } + + // ------------------------------------------------ Individual Test Methods /** * Sets up instance variables required by this test case. @@ -94,13 +138,10 @@ public void tearDown() } - // ------------------------------------------------ Individual Test Methods + // ------------------------------------ Utility Support Methods and Classes - /** - * Test XML Schema validation. - */ @Test - public void testGoodDocument() + public void testBadDocument() throws SAXException, IOException { @@ -109,20 +150,16 @@ public void testGoodDocument() digester.setErrorHandler( teh ); // Parse our test input - final Employee employee = digester.parse( getInputStream( "Test13-01.xml" ) ); - assertNotNull( "failed to parsed an employee", employee ); - assertTrue( "Test13-01 should not generate errors in Schema validation", teh.clean ); - - // Test document has been processed - final Address ha = employee.getAddress( "home" ); - assertNotNull( ha ); - assertEquals( "Home City", ha.getCity() ); - assertEquals( "HS", ha.getState() ); + digester.parse( getInputStream( "Test13-02.xml" ) ); + assertFalse( "Test13-02 should generate errors in Schema validation", teh.clean ); } + /** + * Test XML Schema validation. + */ @Test - public void testBadDocument() + public void testGoodDocument() throws SAXException, IOException { @@ -131,53 +168,16 @@ public void testBadDocument() digester.setErrorHandler( teh ); // Parse our test input - digester.parse( getInputStream( "Test13-02.xml" ) ); - assertFalse( "Test13-02 should generate errors in Schema validation", teh.clean ); - - } - - // ------------------------------------ Utility Support Methods and Classes - - /** - * Return an appropriate InputStream for the specified test file (which must be inside our current package. - * - * @param name Name of the test file we want - * @throws IOException if an input/output error occurs - */ - protected InputStream getInputStream( final String name ) - throws IOException - { - - return ( this.getClass().getResourceAsStream( "/org/apache/commons/digester3/" + name ) ); - - } - - static final class TestErrorHandler - implements ErrorHandler - { - public boolean clean = true; - - public TestErrorHandler() - { - } - - @Override - public void error( final SAXParseException exception ) - { - clean = false; - } + final Employee employee = digester.parse( getInputStream( "Test13-01.xml" ) ); + assertNotNull( "failed to parsed an employee", employee ); + assertTrue( "Test13-01 should not generate errors in Schema validation", teh.clean ); - @Override - public void fatalError( final SAXParseException exception ) - { - clean = false; - } + // Test document has been processed + final Address ha = employee.getAddress( "home" ); + assertNotNull( ha ); + assertEquals( "Home City", ha.getCity() ); + assertEquals( "HS", ha.getState() ); - @Override - public void warning( final SAXParseException exception ) - { - clean = false; - } } } diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/AbstractAnnotatedPojoTestCase.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/AbstractAnnotatedPojoTestCase.java index 5875f7741..1d9ba8180 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/AbstractAnnotatedPojoTestCase.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/AbstractAnnotatedPojoTestCase.java @@ -35,6 +35,10 @@ public abstract class AbstractAnnotatedPojoTestCase { + protected Collection getAuxModules() { + return new ArrayList(); + } + /** * Loads the digester rules parsing the expected object class, parses the * XML and verify the digester produces the same result. @@ -70,8 +74,4 @@ protected void configureRules() assertEquals(expected, actual); } - protected Collection getAuxModules() { - return new ArrayList(); - } - } diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/addressbook/Address.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/addressbook/Address.java index 7a2a114ca..c0e449a38 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/addressbook/Address.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/addressbook/Address.java @@ -45,66 +45,6 @@ public class Address @BeanPropertySetter( pattern = "address-book/person/address/country" ) private String country; - public String getType() - { - return type; - } - - public void setType( final String type ) - { - this.type = type; - } - - public String getStreet() - { - return street; - } - - public void setStreet( final String street ) - { - this.street = street; - } - - public String getCity() - { - return city; - } - - public void setCity( final String city ) - { - this.city = city; - } - - public String getState() - { - return state; - } - - public void setState( final String state ) - { - this.state = state; - } - - public String getZip() - { - return zip; - } - - public void setZip( final String zip ) - { - this.zip = zip; - } - - public String getCountry() - { - return country; - } - - public void setCountry( final String country ) - { - this.country = country; - } - @Override public boolean equals( final Object obj ) { @@ -175,6 +115,66 @@ else if ( !zip.equals( other.zip ) ) { return true; } + public String getCity() + { + return city; + } + + public String getCountry() + { + return country; + } + + public String getState() + { + return state; + } + + public String getStreet() + { + return street; + } + + public String getType() + { + return type; + } + + public String getZip() + { + return zip; + } + + public void setCity( final String city ) + { + this.city = city; + } + + public void setCountry( final String country ) + { + this.country = country; + } + + public void setState( final String state ) + { + this.state = state; + } + + public void setStreet( final String street ) + { + this.street = street; + } + + public void setType( final String type ) + { + this.type = type; + } + + public void setZip( final String zip ) + { + this.zip = zip; + } + @Override public String toString() { diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/addressbook/Person.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/addressbook/Person.java index 4c4ec29ad..e9f3fcffe 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/addressbook/Person.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/addressbook/Person.java @@ -49,44 +49,10 @@ public class Person @BeanPropertySetter( pattern = "address-book/person/name" ) private String name; - public int getId() - { - return id; - } - - public void setId( final int id ) - { - this.id = id; - } - - public String getCategory() - { - return category; - } - - public void setCategory( final String category ) - { - this.category = category; - } - - public String getName() - { - return name; - } - - public void setName( final String name ) - { - this.name = name; - } - - public Map getEmails() - { - return emails; - } - - public List

      getAddresses() + @SetNext( fireOnBegin = true ) + public void addAddress( final Address addr ) { - return addresses; + this.addresses.add( addr ); } @CallMethod( pattern = "address-book/person/email" ) @@ -96,12 +62,6 @@ public void addEmail( @CallParam( pattern = "address-book/person/email", attribu this.emails.put( type, address ); } - @SetNext( fireOnBegin = true ) - public void addAddress( final Address addr ) - { - this.addresses.add( addr ); - } - @Override public boolean equals( final Object obj ) { @@ -157,6 +117,46 @@ else if ( !name.equals( other.name ) ) { return true; } + public List
      getAddresses() + { + return addresses; + } + + public String getCategory() + { + return category; + } + + public Map getEmails() + { + return emails; + } + + public int getId() + { + return id; + } + + public String getName() + { + return name; + } + + public void setCategory( final String category ) + { + this.category = category; + } + + public void setId( final int id ) + { + this.id = id; + } + + public void setName( final String name ) + { + this.name = name; + } + @Override public String toString() { diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/catalog/AudioVisual.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/catalog/AudioVisual.java index 355e206cb..0d277e97c 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/catalog/AudioVisual.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/catalog/AudioVisual.java @@ -39,56 +39,6 @@ public final class AudioVisual private int runtime; - public int getYearMade() - { - return yearMade; - } - - public void setYearMade( final int yearMade ) - { - this.yearMade = yearMade; - } - - public String getCategory() - { - return this.category; - } - - public void setCategory( final String category ) - { - this.category = category; - } - - public String getName() - { - return this.name; - } - - public void setName( final String name ) - { - this.name = name; - } - - public String getDesc() - { - return this.desc; - } - - public void setDesc( final String desc ) - { - this.desc = desc; - } - - public int getRuntime() - { - return this.runtime; - } - - public void setRuntime( final int runtime ) - { - this.runtime = runtime; - } - @Override public boolean equals( final Object obj ) { @@ -138,10 +88,60 @@ else if ( !this.name.equals( other.getName() ) ) { return true; } + public String getCategory() + { + return this.category; + } + + public String getDesc() + { + return this.desc; + } + + public String getName() + { + return this.name; + } + + public int getRuntime() + { + return this.runtime; + } + + public int getYearMade() + { + return yearMade; + } + @Override public void print() { System.out.println( this.toString() ); } + public void setCategory( final String category ) + { + this.category = category; + } + + public void setDesc( final String desc ) + { + this.desc = desc; + } + + public void setName( final String name ) + { + this.name = name; + } + + public void setRuntime( final int runtime ) + { + this.runtime = runtime; + } + + public void setYearMade( final int yearMade ) + { + this.yearMade = yearMade; + } + } diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/catalog/Book.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/catalog/Book.java index 534534517..034fa18d7 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/catalog/Book.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/catalog/Book.java @@ -44,41 +44,6 @@ public Book( final String isbn ) this.isbn = isbn; } - public String getTitle() - { - return this.title; - } - - public void setTitle( final String title ) - { - this.title = title; - } - - public String getAuthor() - { - return this.author; - } - - public void setAuthor( final String author ) - { - this.author = author; - } - - public String getDesc() - { - return this.desc; - } - - public void setDesc( final String desc ) - { - this.desc = desc; - } - - public String getIsbn() - { - return this.isbn; - } - @Override public boolean equals( final Object obj ) { @@ -131,10 +96,24 @@ else if ( !this.title.equals( other.getTitle() ) ) { return true; } - @Override - public String toString() + public String getAuthor() { - return "Book [author=" + author + ", desc=" + desc + ", isbn=" + isbn + ", title=" + title + "]"; + return this.author; + } + + public String getDesc() + { + return this.desc; + } + + public String getIsbn() + { + return this.isbn; + } + + public String getTitle() + { + return this.title; } @Override @@ -143,4 +122,25 @@ public void print() System.out.println( this.toString() ); } + public void setAuthor( final String author ) + { + this.author = author; + } + + public void setDesc( final String desc ) + { + this.desc = desc; + } + + public void setTitle( final String title ) + { + this.title = title; + } + + @Override + public String toString() + { + return "Book [author=" + author + ", desc=" + desc + ", isbn=" + isbn + ", title=" + title + "]"; + } + } diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/catalog/Catalog.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/catalog/Catalog.java index e597ec347..1f69d6aa5 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/catalog/Catalog.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/catalog/Catalog.java @@ -38,11 +38,6 @@ public void addItem( final Item item ) this.items.add( item ); } - public List getItems() - { - return this.items; - } - @Override public boolean equals( final Object obj ) { @@ -68,10 +63,9 @@ else if ( !this.items.equals( other.getItems() ) ) { return true; } - @Override - public String toString() + public List getItems() { - return "Catalog [items=" + items + "]"; + return this.items; } public void print() @@ -84,4 +78,10 @@ public void print() } } + @Override + public String toString() + { + return "Catalog [items=" + items + "]"; + } + } diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/catalog/CatalogTestCase.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/catalog/CatalogTestCase.java index 58102e0fa..53b83be30 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/catalog/CatalogTestCase.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/catalog/CatalogTestCase.java @@ -32,6 +32,23 @@ public final class CatalogTestCase extends AbstractAnnotatedPojoTestCase { + @Override + protected Collection getAuxModules() + { + final Collection modules = new Stack(); + modules.add( new AbstractRulesModule() + { + + @Override + public void configure() + { + forPattern( "catalog/dvd/attr" ).setProperty( "id" ).extractingValueFromAttribute( "value" ); + } + + } ); + return modules; + } + @Test public void testCatalog() throws Exception @@ -69,21 +86,4 @@ public void testCatalog() this.verifyExpectedEqualsToParsed( catalog ); } - @Override - protected Collection getAuxModules() - { - final Collection modules = new Stack(); - modules.add( new AbstractRulesModule() - { - - @Override - public void configure() - { - forPattern( "catalog/dvd/attr" ).setProperty( "id" ).extractingValueFromAttribute( "value" ); - } - - } ); - return modules; - } - } diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/employee/Address.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/employee/Address.java index 5bd98a433..cd8643bc0 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/employee/Address.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/employee/Address.java @@ -44,62 +44,6 @@ public class Address @BeanPropertySetter( pattern = "employee/address/zip-code" ) private String zipCode; - @SetTop( pattern = "employee/address" ) - public void setEmployee( final Employee employee ) - { - employee.addAddress( this ); - } - - public String getCity() - { - return this.city; - } - - public void setCity( final String city ) - { - this.city = city; - } - - public String getState() - { - return this.state; - } - - public void setState( final String state ) - { - this.state = state; - } - - public String getStreet() - { - return this.street; - } - - public void setStreet( final String street ) - { - this.street = street; - } - - public String getType() - { - return this.type; - } - - public void setType( final String type ) - { - this.type = type; - } - - public String getZipCode() - { - return this.zipCode; - } - - public void setZipCode( final String zipCode ) - { - this.zipCode = zipCode; - } - @Override public boolean equals( final Object obj ) { @@ -161,6 +105,62 @@ else if ( !this.zipCode.equals( other.getZipCode() ) ) { return true; } + public String getCity() + { + return this.city; + } + + public String getState() + { + return this.state; + } + + public String getStreet() + { + return this.street; + } + + public String getType() + { + return this.type; + } + + public String getZipCode() + { + return this.zipCode; + } + + public void setCity( final String city ) + { + this.city = city; + } + + @SetTop( pattern = "employee/address" ) + public void setEmployee( final Employee employee ) + { + employee.addAddress( this ); + } + + public void setState( final String state ) + { + this.state = state; + } + + public void setStreet( final String street ) + { + this.street = street; + } + + public void setType( final String type ) + { + this.type = type; + } + + public void setZipCode( final String zipCode ) + { + this.zipCode = zipCode; + } + @Override public String toString() { diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/employee/Employee.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/employee/Employee.java index ed52e9cbd..af693067b 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/employee/Employee.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/employee/Employee.java @@ -43,31 +43,6 @@ public void addAddress( final Address address ) this.addresses.add( address ); } - public String getFirstName() - { - return this.firstName; - } - - public void setFirstName( final String firstName ) - { - this.firstName = firstName; - } - - public String getLastName() - { - return this.lastName; - } - - public void setLastName( final String lastName ) - { - this.lastName = lastName; - } - - public List
      getAddresses() - { - return this.addresses; - } - @Override public boolean equals( final Object obj ) { @@ -111,6 +86,31 @@ else if ( !this.lastName.equals( other.getLastName() ) ) { return true; } + public List
      getAddresses() + { + return this.addresses; + } + + public String getFirstName() + { + return this.firstName; + } + + public String getLastName() + { + return this.lastName; + } + + public void setFirstName( final String firstName ) + { + this.firstName = firstName; + } + + public void setLastName( final String lastName ) + { + this.lastName = lastName; + } + @Override public String toString() { diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/employee/EmployeeTestCase.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/employee/EmployeeTestCase.java index 735a2f543..658df6b89 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/employee/EmployeeTestCase.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/employee/EmployeeTestCase.java @@ -32,6 +32,23 @@ public final class EmployeeTestCase extends AbstractAnnotatedPojoTestCase { + @Override + protected Collection getAuxModules() + { + final Collection modules = new Stack(); + modules.add( new FromAnnotationsRuleModule() + { + + @Override + protected void configureRules() + { + bindRulesFrom( Address.class ); + } + + }); + return modules; + } + @Test public void testEmployee() throws Exception @@ -59,21 +76,4 @@ public void testEmployee() this.verifyExpectedEqualsToParsed( employee ); } - @Override - protected Collection getAuxModules() - { - final Collection modules = new Stack(); - modules.add( new FromAnnotationsRuleModule() - { - - @Override - protected void configureRules() - { - bindRulesFrom( Address.class ); - } - - }); - return modules; - } - } diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/failingtests/BeanWithFakeHandler.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/failingtests/BeanWithFakeHandler.java index ceba47119..c59fab79c 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/failingtests/BeanWithFakeHandler.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/failingtests/BeanWithFakeHandler.java @@ -23,14 +23,14 @@ public final class BeanWithFakeHandler @FakeRule private String something; - public void setSomething( final String something ) + public String getSomething() { - this.something = something; + return something; } - public String getSomething() + public void setSomething( final String something ) { - return something; + this.something = something; } } diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/person/Person.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/person/Person.java index da8dce08a..544daebc8 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/person/Person.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/person/Person.java @@ -51,41 +51,6 @@ public void addEmail( @CallParam( pattern = "person/email", attributeName = "typ this.emails.put( type, address ); } - public int getId() - { - return id; - } - - public void setId( final int id ) - { - this.id = id; - } - - public String getCategory() - { - return category; - } - - public void setCategory( final String category ) - { - this.category = category; - } - - public String getName() - { - return name; - } - - public void setName( final String name ) - { - this.name = name; - } - - public Map getEmails() - { - return emails; - } - @Override public boolean equals( final Object obj ) { @@ -132,6 +97,41 @@ else if ( !name.equals( other.name ) ) { return true; } + public String getCategory() + { + return category; + } + + public Map getEmails() + { + return emails; + } + + public int getId() + { + return id; + } + + public String getName() + { + return name; + } + + public void setCategory( final String category ) + { + this.category = category; + } + + public void setId( final int id ) + { + this.id = id; + } + + public void setName( final String name ) + { + this.name = name; + } + @Override public String toString() { diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/rss/Channel.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/rss/Channel.java index 10dfe46bc..4b6d88c45 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/rss/Channel.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/rss/Channel.java @@ -47,62 +47,6 @@ public final class Channel private Image image; - public String getTitle() - { - return title; - } - - public void setTitle( final String title ) - { - this.title = title; - } - - public String getLink() - { - return link; - } - - public void setLink( final String link ) - { - this.link = link; - } - - public String getDescription() - { - return description; - } - - public void setDescription( final String description ) - { - this.description = description; - } - - public String getLanguage() - { - return language; - } - - public void setLanguage( final String language ) - { - this.language = language; - } - - public List getItems() - { - return items; - } - - public Image getImage() - { - return image; - } - - @SetNext - public void setImage( final Image image ) - { - this.image = image; - } - @SetNext public void addItem( final Item item ) { @@ -179,6 +123,62 @@ else if ( !title.equals( other.title ) ) { return true; } + public String getDescription() + { + return description; + } + + public Image getImage() + { + return image; + } + + public List getItems() + { + return items; + } + + public String getLanguage() + { + return language; + } + + public String getLink() + { + return link; + } + + public String getTitle() + { + return title; + } + + public void setDescription( final String description ) + { + this.description = description; + } + + @SetNext + public void setImage( final Image image ) + { + this.image = image; + } + + public void setLanguage( final String language ) + { + this.language = language; + } + + public void setLink( final String link ) + { + this.link = link; + } + + public void setTitle( final String title ) + { + this.title = title; + } + @Override public String toString() { diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/rss/Image.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/rss/Image.java index 113925b86..633e77ec5 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/rss/Image.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/rss/Image.java @@ -45,66 +45,6 @@ public final class Image @BeanPropertySetter( pattern = "rss/channel/image/url" ) private String url; - public String getDescription() - { - return description; - } - - public void setDescription( final String description ) - { - this.description = description; - } - - public int getWidth() - { - return width; - } - - public void setWidth( final int width ) - { - this.width = width; - } - - public int getHeight() - { - return height; - } - - public void setHeight( final int height ) - { - this.height = height; - } - - public String getLink() - { - return link; - } - - public void setLink( final String link ) - { - this.link = link; - } - - public String getTitle() - { - return title; - } - - public void setTitle( final String title ) - { - this.title = title; - } - - public String getUrl() - { - return url; - } - - public void setUrl( final String url ) - { - this.url = url; - } - @Override public boolean equals( final Object obj ) { @@ -163,6 +103,66 @@ else if ( !url.equals( other.url ) ) { return true; } + public String getDescription() + { + return description; + } + + public int getHeight() + { + return height; + } + + public String getLink() + { + return link; + } + + public String getTitle() + { + return title; + } + + public String getUrl() + { + return url; + } + + public int getWidth() + { + return width; + } + + public void setDescription( final String description ) + { + this.description = description; + } + + public void setHeight( final int height ) + { + this.height = height; + } + + public void setLink( final String link ) + { + this.link = link; + } + + public void setTitle( final String title ) + { + this.title = title; + } + + public void setUrl( final String url ) + { + this.url = url; + } + + public void setWidth( final int width ) + { + this.width = width; + } + @Override public String toString() { diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/rss/Item.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/rss/Item.java index 51e956b23..8b337a766 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/rss/Item.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/rss/Item.java @@ -36,36 +36,6 @@ public final class Item @BeanPropertySetter( pattern = "rss/channel/item/title" ) private String title; - public String getDescription() - { - return description; - } - - public void setDescription( final String description ) - { - this.description = description; - } - - public String getLink() - { - return link; - } - - public void setLink( final String link ) - { - this.link = link; - } - - public String getTitle() - { - return title; - } - - public void setTitle( final String title ) - { - this.title = title; - } - @Override public boolean equals( final Object obj ) { @@ -109,6 +79,36 @@ else if ( !title.equals( other.title ) ) { return true; } + public String getDescription() + { + return description; + } + + public String getLink() + { + return link; + } + + public String getTitle() + { + return title; + } + + public void setDescription( final String description ) + { + this.description = description; + } + + public void setLink( final String link ) + { + this.link = link; + } + + public void setTitle( final String title ) + { + this.title = title; + } + @Override public String toString() { diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/servletbean/ServletBean.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/servletbean/ServletBean.java index b1495d190..174d9f7eb 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/servletbean/ServletBean.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/annotations/servletbean/ServletBean.java @@ -47,31 +47,6 @@ public void addInitParam( @CallParam( pattern = "web-app/servlet/init-param/para this.initParams.put( name, value ); } - public String getServletName() - { - return servletName; - } - - public void setServletName( final String servletName ) - { - this.servletName = servletName; - } - - public String getServletClass() - { - return servletClass; - } - - public void setServletClass( final String servletClass ) - { - this.servletClass = servletClass; - } - - public Map getInitParams() - { - return initParams; - } - @Override public boolean equals( final Object obj ) { @@ -115,6 +90,31 @@ else if ( !servletName.equals( other.servletName ) ) { return true; } + public Map getInitParams() + { + return initParams; + } + + public String getServletClass() + { + return servletClass; + } + + public String getServletName() + { + return servletName; + } + + public void setServletClass( final String servletClass ) + { + this.servletClass = servletClass; + } + + public void setServletName( final String servletName ) + { + this.servletName = servletName; + } + @Override public String toString() { diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/binder/BinderClassLoaderTestCase.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/binder/BinderClassLoaderTestCase.java index 3685d8b2f..f35530fc9 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/binder/BinderClassLoaderTestCase.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/binder/BinderClassLoaderTestCase.java @@ -49,69 +49,157 @@ public final class BinderClassLoaderTestCase { - private final BinderClassLoader classLoader = createBinderClassLoader( new ExtendedClassLoader() ); - - @Test - public void testLoadBoolean() - throws Exception + public static class Dummy { - typeFound( "boolean", boolean.class ); } - @Test - public void testLoadByte() - throws Exception + private static final class ExtendedClassLoader + extends ClassLoader { - typeFound( "byte", byte.class ); - } - @Test - public void testLoadShort() - throws Exception - { - typeFound( "short", short.class ); - } + private static final class InMemoryURLStreamHandlerFactory + implements URLStreamHandlerFactory + { + @Override + public URLStreamHandler createURLStreamHandler( final String protocol ) + { + return new URLStreamHandler() + { + @Override + protected URLConnection openConnection( final URL u ) + throws IOException + { + return new URLConnection( u ) + { + private InputStream inputStream; - @Test - public void testLoadInt() - throws Exception - { - typeFound( "int", int.class ); - } + @Override + public void connect() + throws IOException + { + if ( !connected ) { + final byte[] data = IN_MEMORY_RESOURCES.get( url.getPath() ); + if ( data != null ) + { + inputStream = new ByteArrayInputStream( data ); + } + connected = true; + } + } - @Test - public void testLoadChar() - throws Exception - { - typeFound( "char", char.class ); - } + @Override + public InputStream getInputStream() + throws IOException + { + connect(); + if ( inputStream == null ) + { + throw new FileNotFoundException( url.getPath() ); + } + return inputStream; + } + }; + } + }; + } + } - @Test - public void testLoadLong() - throws Exception - { - typeFound( "long", long.class ); + private final InMemoryURLStreamHandlerFactory streamHandlerFactory = new InMemoryURLStreamHandlerFactory(); + + @Override + public URL getResource( final String name ) + { + if ( name.startsWith( "inmemory:" ) ) + { + try + { + return new URL( null, name, streamHandlerFactory.createURLStreamHandler( "inmemory" ) ); + } + catch ( final MalformedURLException e ) + { + throw new RuntimeException( e ); + } + } + return super.getResource( name ); + } + + @Override + protected Class loadClass( final String name, final boolean resolve ) + throws ClassNotFoundException + { + final String dummyClassName = Dummy.class.getName(); + if ( dummyClassName.equals( name ) ) { + Class result = findLoadedClass( name ); + if ( result == null ) + { + final byte[] byteCode = IN_MEMORY_RESOURCES.get( dummyClassName.replace( '.', '/' ) + ".class" ); + result = defineClass( name, byteCode, 0, byteCode.length ); + resolveClass( result ); + } + return result; + } + return super.loadClass( name, resolve ); + } } - @Test - public void testLoadFloat() - throws Exception + private static final Map IN_MEMORY_RESOURCES = new HashMap(); + + static { - typeFound( "float", float.class ); + try + { + IN_MEMORY_RESOURCES.put( "dummyResource", "Resource data".getBytes(StandardCharsets.UTF_8) ); + + // put bytes of Dummy class + final String dummyClassName = Dummy.class.getName(); + final String resourceName = dummyClassName.replace( '.', '/' ) + ".class"; + final InputStream input = Dummy.class.getClassLoader().getResourceAsStream( resourceName ); + try + { + IN_MEMORY_RESOURCES.put( resourceName, toByteArray( input ) ); + } + finally + { + input.close(); + } + } + catch ( final IOException e ) + { + throw new ExceptionInInitializerError( e ); + } } - @Test - public void testLoadDouble() - throws Exception + private static byte[] toByteArray( final InputStream input ) + throws IOException { - typeFound( "double", double.class ); + final ByteArrayOutputStream result = new ByteArrayOutputStream( 512 ); + int n; + while ( ( n = input.read() ) != -1 ) + { + result.write( n ); + } + return result.toByteArray(); } - private void typeFound( final String name, final Class expected ) + private final BinderClassLoader classLoader = createBinderClassLoader( new ExtendedClassLoader() ); + + @Test + public void testGetPrefixedResource() throws Exception { - final Class actual = classLoader.loadClass( name ); - assertSame( expected, actual ); + final URL resource = classLoader.getResource( "inmemory:dummyResource" ); + assertNotNull( resource ); + assertEquals( resource.getPath(), "dummyResource" ); + final InputStream input = resource.openStream(); + try + { + final byte[] bytes = toByteArray( input ); + assertArrayEquals( bytes, IN_MEMORY_RESOURCES.get( "dummyResource" ) ); + } + finally + { + input.close(); + } } @Test @@ -135,6 +223,27 @@ public URL getResource( final String name ) assertNotNull( binderCl.getResource( "xxx" ) ); } + @Test + public void testLoadBoolean() + throws Exception + { + typeFound( "boolean", boolean.class ); + } + + @Test + public void testLoadByte() + throws Exception + { + typeFound( "byte", byte.class ); + } + + @Test + public void testLoadChar() + throws Exception + { + typeFound( "char", char.class ); + } + @Test public void testLoadClass() throws Exception @@ -150,154 +259,45 @@ public void testLoadClass() } @Test - public void testGetPrefixedResource() + public void testLoadDouble() throws Exception { - final URL resource = classLoader.getResource( "inmemory:dummyResource" ); - assertNotNull( resource ); - assertEquals( resource.getPath(), "dummyResource" ); - final InputStream input = resource.openStream(); - try - { - final byte[] bytes = toByteArray( input ); - assertArrayEquals( bytes, IN_MEMORY_RESOURCES.get( "dummyResource" ) ); - } - finally - { - input.close(); - } + typeFound( "double", double.class ); } - private static byte[] toByteArray( final InputStream input ) - throws IOException + @Test + public void testLoadFloat() + throws Exception { - final ByteArrayOutputStream result = new ByteArrayOutputStream( 512 ); - int n; - while ( ( n = input.read() ) != -1 ) - { - result.write( n ); - } - return result.toByteArray(); + typeFound( "float", float.class ); } - private static final Map IN_MEMORY_RESOURCES = new HashMap(); - - static + @Test + public void testLoadInt() + throws Exception { - try - { - IN_MEMORY_RESOURCES.put( "dummyResource", "Resource data".getBytes(StandardCharsets.UTF_8) ); - - // put bytes of Dummy class - final String dummyClassName = Dummy.class.getName(); - final String resourceName = dummyClassName.replace( '.', '/' ) + ".class"; - final InputStream input = Dummy.class.getClassLoader().getResourceAsStream( resourceName ); - try - { - IN_MEMORY_RESOURCES.put( resourceName, toByteArray( input ) ); - } - finally - { - input.close(); - } - } - catch ( final IOException e ) - { - throw new ExceptionInInitializerError( e ); - } + typeFound( "int", int.class ); } - private static final class ExtendedClassLoader - extends ClassLoader + @Test + public void testLoadLong() + throws Exception { + typeFound( "long", long.class ); + } - private final InMemoryURLStreamHandlerFactory streamHandlerFactory = new InMemoryURLStreamHandlerFactory(); - - @Override - protected Class loadClass( final String name, final boolean resolve ) - throws ClassNotFoundException - { - final String dummyClassName = Dummy.class.getName(); - if ( dummyClassName.equals( name ) ) { - Class result = findLoadedClass( name ); - if ( result == null ) - { - final byte[] byteCode = IN_MEMORY_RESOURCES.get( dummyClassName.replace( '.', '/' ) + ".class" ); - result = defineClass( name, byteCode, 0, byteCode.length ); - resolveClass( result ); - } - return result; - } - return super.loadClass( name, resolve ); - } - - @Override - public URL getResource( final String name ) - { - if ( name.startsWith( "inmemory:" ) ) - { - try - { - return new URL( null, name, streamHandlerFactory.createURLStreamHandler( "inmemory" ) ); - } - catch ( final MalformedURLException e ) - { - throw new RuntimeException( e ); - } - } - return super.getResource( name ); - } - - private static final class InMemoryURLStreamHandlerFactory - implements URLStreamHandlerFactory - { - @Override - public URLStreamHandler createURLStreamHandler( final String protocol ) - { - return new URLStreamHandler() - { - @Override - protected URLConnection openConnection( final URL u ) - throws IOException - { - return new URLConnection( u ) - { - private InputStream inputStream; - - @Override - public void connect() - throws IOException - { - if ( !connected ) { - final byte[] data = IN_MEMORY_RESOURCES.get( url.getPath() ); - if ( data != null ) - { - inputStream = new ByteArrayInputStream( data ); - } - connected = true; - } - } - - @Override - public InputStream getInputStream() - throws IOException - { - connect(); - if ( inputStream == null ) - { - throw new FileNotFoundException( url.getPath() ); - } - return inputStream; - } - }; - } - }; - } - } + @Test + public void testLoadShort() + throws Exception + { + typeFound( "short", short.class ); } - public static class Dummy + private void typeFound( final String name, final Class expected ) + throws Exception { + final Class actual = classLoader.loadClass( name ); + assertSame( expected, actual ); } } diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/binder/Digester163TestCase.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/binder/Digester163TestCase.java index 792af011a..f2c593462 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/binder/Digester163TestCase.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/binder/Digester163TestCase.java @@ -60,17 +60,6 @@ protected void loadRules() } ); } - @Test - public void testSingle() - throws IOException, SAXException - { - final Digester dig = loader.newDigester(); - final URL url = Digester163TestCase.class.getResource( "test.xml" ); - // lets parse - result does not matter here - final Entity et = dig.parse( url ); - assertEquals( "Author 1", et.getAuthor() ); - } - @Test public void test() throws InterruptedException @@ -131,4 +120,15 @@ public void run() } } + @Test + public void testSingle() + throws IOException, SAXException + { + final Digester dig = loader.newDigester(); + final URL url = Digester163TestCase.class.getResource( "test.xml" ); + // lets parse - result does not matter here + final Entity et = dig.parse( url ); + assertEquals( "Author 1", et.getAuthor() ); + } + } diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/binder/DigesterLoaderTestCase.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/binder/DigesterLoaderTestCase.java index e028088a8..bd5c0f230 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/binder/DigesterLoaderTestCase.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/binder/DigesterLoaderTestCase.java @@ -32,36 +32,39 @@ public final class DigesterLoaderTestCase { - /** - * DIGESTER-151 - */ @Test - public void testDigester151() + public void testDigeser152() { - final ErrorHandler expected = new ErrorHandler() + final Locator expected = new Locator() { @Override - public void warning( final SAXParseException exception ) - throws SAXException + public int getColumnNumber() { - // do nothing + // just fake method + return 0; } @Override - public void fatalError( final SAXParseException exception ) - throws SAXException + public int getLineNumber() { - // do nothing + // just fake method + return 0; } @Override - public void error( final SAXParseException exception ) - throws SAXException + public String getPublicId() { - // do nothing + // just fake method + return null; } + @Override + public String getSystemId() + { + // just fake method + return null; + } }; final Digester digester = newLoader( new AbstractRulesModule() @@ -73,46 +76,43 @@ protected void configure() // do nothing } - } ).setErrorHandler( expected ).newDigester(); + } ).setDocumentLocator( expected ).newDigester(); - final ErrorHandler actual = digester.getErrorHandler(); + final Locator actual = digester.getDocumentLocator(); assertSame( expected, actual ); } + /** + * DIGESTER-151 + */ @Test - public void testDigeser152() + public void testDigester151() { - final Locator expected = new Locator() + final ErrorHandler expected = new ErrorHandler() { @Override - public String getSystemId() + public void error( final SAXParseException exception ) + throws SAXException { - // just fake method - return null; + // do nothing } @Override - public String getPublicId() + public void fatalError( final SAXParseException exception ) + throws SAXException { - // just fake method - return null; + // do nothing } @Override - public int getLineNumber() + public void warning( final SAXParseException exception ) + throws SAXException { - // just fake method - return 0; + // do nothing } - @Override - public int getColumnNumber() - { - // just fake method - return 0; - } }; final Digester digester = newLoader( new AbstractRulesModule() @@ -124,9 +124,9 @@ protected void configure() // do nothing } - } ).setDocumentLocator( expected ).newDigester(); + } ).setErrorHandler( expected ).newDigester(); - final Locator actual = digester.getDocumentLocator(); + final ErrorHandler actual = digester.getErrorHandler(); assertSame( expected, actual ); } diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/plugins/ObjectTestImpl.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/plugins/ObjectTestImpl.java index 97b1721ac..aa8777a1e 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/plugins/ObjectTestImpl.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/plugins/ObjectTestImpl.java @@ -31,13 +31,13 @@ public ObjectTestImpl() { } - public void setValue( final String val ) + public String getValue() { - value = val; + return value; } - public String getValue() + public void setValue( final String val ) { - return value; + value = val; } } diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/plugins/Slider.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/plugins/Slider.java index e9cdc3543..c52ca2bd8 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/plugins/Slider.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/plugins/Slider.java @@ -23,11 +23,15 @@ public class Slider implements Widget { - private String label = "nolabel"; - - private int min; - - private int max; + // define different rules on this class + public static void addRangeRules( final Digester digester, final String pattern ) + { + // note: deliberately no addSetProperties rule + final Class[] paramtypes = { Integer.class, Integer.class }; + digester.addCallMethod( pattern + "/range", "setRange", 2, paramtypes ); + digester.addCallParam( pattern + "/range", 0, "min" ); + digester.addCallParam( pattern + "/range", 1, "max" ); + } // define rules on this class public static void addRules( final Digester digester, final String pattern ) @@ -39,15 +43,11 @@ public static void addRules( final Digester digester, final String pattern ) digester.addCallMethod( pattern + "/max", "setMax", 0, paramtypes ); } - // define different rules on this class - public static void addRangeRules( final Digester digester, final String pattern ) - { - // note: deliberately no addSetProperties rule - final Class[] paramtypes = { Integer.class, Integer.class }; - digester.addCallMethod( pattern + "/range", "setRange", 2, paramtypes ); - digester.addCallParam( pattern + "/range", 0, "min" ); - digester.addCallParam( pattern + "/range", 1, "max" ); - } + private String label = "nolabel"; + + private int min; + + private int max; public Slider() { @@ -58,19 +58,19 @@ public String getLabel() return label; } - public void setLabel( final String label ) + public int getMax() { - this.label = label; + return max; } - public void setMin( final int min ) + public int getMin() { - this.min = min; + return min; } - public int getMin() + public void setLabel( final String label ) { - return min; + this.label = label; } public void setMax( final int max ) @@ -78,9 +78,9 @@ public void setMax( final int max ) this.max = max; } - public int getMax() + public void setMin( final int min ) { - return max; + this.min = min; } public void setRange( final int min, final int max ) diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/plugins/TestConfigurablePluginAttributes.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/plugins/TestConfigurablePluginAttributes.java index 6b1237594..e6e67f6df 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/plugins/TestConfigurablePluginAttributes.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/plugins/TestConfigurablePluginAttributes.java @@ -33,6 +33,37 @@ public class TestConfigurablePluginAttributes { + public static class MultiContainer + { + private final LinkedList widgets = new LinkedList(); + + private final LinkedList gadgets = new LinkedList(); + + public MultiContainer() + { + } + + public void addGadget( final Widget child ) + { + gadgets.add( child ); + } + + public void addWidget( final Widget child ) + { + widgets.add( child ); + } + + public List getGadgets() + { + return gadgets; + } + + public List getWidgets() + { + return widgets; + } + } + // --------------------------------------------------------------- Test cases @Test public void testDefaultBehaviour() @@ -151,6 +182,8 @@ public void testGlobalOverride() assertEquals( Slider.class, gadgets.get( 3 ).getClass() ); } + // inner classes used for testing + @Test public void testInstanceOverride() throws Exception @@ -211,37 +244,4 @@ public void testInstanceOverride() assertEquals( TextLabel.class, gadgets.get( 2 ).getClass() ); assertEquals( TextLabel.class, gadgets.get( 3 ).getClass() ); } - - // inner classes used for testing - - public static class MultiContainer - { - private final LinkedList widgets = new LinkedList(); - - private final LinkedList gadgets = new LinkedList(); - - public MultiContainer() - { - } - - public void addWidget( final Widget child ) - { - widgets.add( child ); - } - - public List getWidgets() - { - return widgets; - } - - public void addGadget( final Widget child ) - { - gadgets.add( child ); - } - - public List getGadgets() - { - return gadgets; - } - } } diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/plugins/TestDelegate.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/plugins/TestDelegate.java index 8917c4a32..31dd3d55f 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/plugins/TestDelegate.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/plugins/TestDelegate.java @@ -27,14 +27,6 @@ public class TestDelegate { - // --------------------------------------------------------------- Test cases - @Test - public void testDummy() - { - // it is an error if a TestSuite doesn't have at least one test, - // so here is one... - } - public void ignoretestDelegate() throws Exception { @@ -58,4 +50,12 @@ public void ignoretestDelegate() throw e; } } + + // --------------------------------------------------------------- Test cases + @Test + public void testDummy() + { + // it is an error if a TestSuite doesn't have at least one test, + // so here is one... + } } diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/plugins/TestRecursion.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/plugins/TestRecursion.java index ac926593d..5cde4cdde 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/plugins/TestRecursion.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/plugins/TestRecursion.java @@ -33,6 +33,21 @@ public class TestRecursion { + private int countWidgets( final Container c ) + { + final List l = c.getChildren(); + int sum = 0; + for ( final Widget w : l ) + { + ++sum; + if ( w instanceof Container ) + { + sum += countWidgets( (Container) w ); + } + } + return sum; + } + // --------------------------------------------------------------- Test cases @Test public void testRecursiveRules() @@ -68,19 +83,4 @@ public void testRecursiveRules() final int nDescendants = countWidgets( root ); assertEquals( 10, nDescendants ); } - - private int countWidgets( final Container c ) - { - final List l = c.getChildren(); - int sum = 0; - for ( final Widget w : l ) - { - ++sum; - if ( w instanceof Container ) - { - sum += countWidgets( (Container) w ); - } - } - return sum; - } } diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/plugins/TestRuleInfo.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/plugins/TestRuleInfo.java index c4aeb2979..5fd5ecdf6 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/plugins/TestRuleInfo.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/plugins/TestRuleInfo.java @@ -32,13 +32,13 @@ public class TestRuleInfo { - // --------------------------------------------------------------- Test cases @Test - public void testRuleInfoExplicitClass() + public void testRuleInfoAutoDetect() throws Exception { // * tests that custom rules can be declared on a - // separate class by explicitly declaring the rule class. + // separate class with name {plugin-class}RuleInfo, + // and they are automatically detected and loaded. final Digester digester = new Digester(); final PluginRules rc = new PluginRules(); @@ -56,7 +56,7 @@ public void testRuleInfoExplicitClass() try { - digester.parse( Utils.getInputStream( this, "test5a.xml" ) ); + digester.parse( Utils.getInputStream( this, "test5c.xml" ) ); } catch ( final Exception e ) { @@ -78,13 +78,13 @@ public void testRuleInfoExplicitClass() assertEquals( "std label", label.getLabel() ); } + // --------------------------------------------------------------- Test cases @Test - public void testRuleInfoExplicitMethod() + public void testRuleInfoExplicitClass() throws Exception { // * tests that custom rules can be declared on a // separate class by explicitly declaring the rule class. - // and explicitly declaring the rule method name. final Digester digester = new Digester(); final PluginRules rc = new PluginRules(); @@ -102,7 +102,7 @@ public void testRuleInfoExplicitMethod() try { - digester.parse( Utils.getInputStream( this, "test5b.xml" ) ); + digester.parse( Utils.getInputStream( this, "test5a.xml" ) ); } catch ( final Exception e ) { @@ -119,18 +119,18 @@ public void testRuleInfoExplicitMethod() assertEquals( TextLabel2.class, child.getClass() ); final TextLabel2 label = (TextLabel2) child; - // id should not be mapped, altlabel should + // id should not be mapped, label should assertEquals( "anonymous", label.getId() ); - assertEquals( "alt label", label.getLabel() ); + assertEquals( "std label", label.getLabel() ); } @Test - public void testRuleInfoAutoDetect() + public void testRuleInfoExplicitMethod() throws Exception { // * tests that custom rules can be declared on a - // separate class with name {plugin-class}RuleInfo, - // and they are automatically detected and loaded. + // separate class by explicitly declaring the rule class. + // and explicitly declaring the rule method name. final Digester digester = new Digester(); final PluginRules rc = new PluginRules(); @@ -148,7 +148,7 @@ public void testRuleInfoAutoDetect() try { - digester.parse( Utils.getInputStream( this, "test5c.xml" ) ); + digester.parse( Utils.getInputStream( this, "test5b.xml" ) ); } catch ( final Exception e ) { @@ -165,8 +165,8 @@ public void testRuleInfoAutoDetect() assertEquals( TextLabel2.class, child.getClass() ); final TextLabel2 label = (TextLabel2) child; - // id should not be mapped, label should + // id should not be mapped, altlabel should assertEquals( "anonymous", label.getId() ); - assertEquals( "std label", label.getLabel() ); + assertEquals( "alt label", label.getLabel() ); } } diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/plugins/TestXmlRuleInfo.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/plugins/TestXmlRuleInfo.java index 5d9ff73d4..ea8c75a79 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/plugins/TestXmlRuleInfo.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/plugins/TestXmlRuleInfo.java @@ -32,21 +32,20 @@ public class TestXmlRuleInfo { - // --------------------------------------------------------------- Test cases @Test - public void testXmlRuleInfoExplicitFile() + public void testXmlRuleImplicitResource() throws Exception { // * tests that custom rules can be declared on a - // separate class by explicitly declaring a file containing - // the rules, using a relative or absolute path name. + // separate class by explicitly declaring the rule class. + // and explicitly declaring a file which is somewhere in the + // classpath. final StringBuilder input = new StringBuilder(); input.append( "" ); input.append( " " ); input.append( " " ); input.append( "" ); @@ -73,24 +72,24 @@ public void testXmlRuleInfoExplicitFile() final Object root = digester.getRoot(); assertEquals( ObjectTestImpl.class, root.getClass() ); final ObjectTestImpl testObject = (ObjectTestImpl) root; - assertEquals( "xmlrules1", testObject.getValue() ); + assertEquals( "xmlrules-ruleinfo", testObject.getValue() ); } + // --------------------------------------------------------------- Test cases @Test - public void testXmlRuleInfoExplicitResource() + public void testXmlRuleInfoExplicitFile() throws Exception { // * tests that custom rules can be declared on a - // separate class by explicitly declaring the rule class. - // and explicitly declaring a file which is somewhere in the - // classpath. + // separate class by explicitly declaring a file containing + // the rules, using a relative or absolute path name. final StringBuilder input = new StringBuilder(); input.append( "" ); input.append( " " ); input.append( " " ); input.append( "" ); @@ -117,11 +116,11 @@ public void testXmlRuleInfoExplicitResource() final Object root = digester.getRoot(); assertEquals( ObjectTestImpl.class, root.getClass() ); final ObjectTestImpl testObject = (ObjectTestImpl) root; - assertEquals( "xmlrules2", testObject.getValue() ); + assertEquals( "xmlrules1", testObject.getValue() ); } @Test - public void testXmlRuleImplicitResource() + public void testXmlRuleInfoExplicitResource() throws Exception { // * tests that custom rules can be declared on a @@ -134,6 +133,7 @@ public void testXmlRuleImplicitResource() input.append( " " ); input.append( " " ); input.append( "" ); @@ -160,6 +160,6 @@ public void testXmlRuleImplicitResource() final Object root = digester.getRoot(); assertEquals( ObjectTestImpl.class, root.getClass() ); final ObjectTestImpl testObject = (ObjectTestImpl) root; - assertEquals( "xmlrules-ruleinfo", testObject.getValue() ); + assertEquals( "xmlrules2", testObject.getValue() ); } } diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/plugins/TextLabel.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/plugins/TextLabel.java index 3881ea65d..73408bd6f 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/plugins/TextLabel.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/plugins/TextLabel.java @@ -33,14 +33,14 @@ public String getId() return id; } - public void setId( final String id ) + public String getLabel() { - this.id = id; + return label; } - public String getLabel() + public void setId( final String id ) { - return label; + this.id = id; } public void setLabel( final String label ) diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/plugins/TextLabel2.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/plugins/TextLabel2.java index d7feec587..af33c542c 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/plugins/TextLabel2.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/plugins/TextLabel2.java @@ -34,14 +34,14 @@ public String getId() return id; } - public void setId( final String id ) + public String getLabel() { - this.id = id; + return label; } - public String getLabel() + public void setId( final String id ) { - return label; + this.id = id; } public void setLabel( final String label ) diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/plugins/TextLabel2RuleInfo.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/plugins/TextLabel2RuleInfo.java index ca4bb6439..9db4514c9 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/plugins/TextLabel2RuleInfo.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/plugins/TextLabel2RuleInfo.java @@ -22,17 +22,17 @@ public class TextLabel2RuleInfo { - // define default rules - public static void addRules( final Digester digester, final String pattern ) - { - digester.addCallMethod( pattern + "/id", "setId", 0 ); - digester.addCallMethod( pattern + "/label", "setLabel", 0 ); - } - // define different rules on this class public static void addAltRules( final Digester digester, final String pattern ) { digester.addCallMethod( pattern + "/alt-id", "setId", 0 ); digester.addCallMethod( pattern + "/alt-label", "setLabel", 0 ); } + + // define default rules + public static void addRules( final Digester digester, final String pattern ) + { + digester.addCallMethod( pattern + "/id", "setId", 0 ); + digester.addCallMethod( pattern + "/label", "setLabel", 0 ); + } } diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/substitution/CompoundSubstitutorTestCase.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/substitution/CompoundSubstitutorTestCase.java index 022664c2a..33de2e4bf 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/substitution/CompoundSubstitutorTestCase.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/substitution/CompoundSubstitutorTestCase.java @@ -82,6 +82,26 @@ public String substitute( final String bodyText ) private String bodyText; + private boolean areEqual( final Attributes a, final Attributes b ) + { + if ( a.getLength() != b.getLength() ) + { + return false; + } + + boolean success = true; + for ( int i = 0; i < a.getLength() && success; i++ ) + { + success = a.getLocalName( i ).equals( b.getLocalName( i ) ) + && a.getQName( i ).equals( b.getQName( i ) ) + && a.getType( i ).equals( b.getType( i ) ) + && a.getURI( i ).equals( b.getURI( i ) ) + && a.getValue( i ).equals( b.getValue( i ) ); + } + + return success; + } + @Before public void setUp() { @@ -94,6 +114,22 @@ public void setUp() bodyText = "Amazing Body Text!"; } + @Test + public void testChaining() + { + final Substitutor a = new SubstitutorStub( "XYZ", "", "a", "", "abc" ); + final Substitutor b = new SubstitutorStub( "STU", "", "b", "", "bcd" ); + + final Substitutor test = new CompoundSubstitutor( a, b ); + + final AttributesImpl attribFixture = new AttributesImpl( attrib ); + attribFixture.addAttribute( "", "a", ":a", "", "abc" ); + attribFixture.addAttribute( "", "b", ":b", "", "bcd" ); + + assertTrue( areEqual( test.substitute( attrib ), attribFixture ) ); + assertEquals( test.substitute( bodyText ), "STU" ); + } + @Test public void testConstructors() { @@ -130,40 +166,4 @@ public void testConstructors() } } - @Test - public void testChaining() - { - final Substitutor a = new SubstitutorStub( "XYZ", "", "a", "", "abc" ); - final Substitutor b = new SubstitutorStub( "STU", "", "b", "", "bcd" ); - - final Substitutor test = new CompoundSubstitutor( a, b ); - - final AttributesImpl attribFixture = new AttributesImpl( attrib ); - attribFixture.addAttribute( "", "a", ":a", "", "abc" ); - attribFixture.addAttribute( "", "b", ":b", "", "bcd" ); - - assertTrue( areEqual( test.substitute( attrib ), attribFixture ) ); - assertEquals( test.substitute( bodyText ), "STU" ); - } - - private boolean areEqual( final Attributes a, final Attributes b ) - { - if ( a.getLength() != b.getLength() ) - { - return false; - } - - boolean success = true; - for ( int i = 0; i < a.getLength() && success; i++ ) - { - success = a.getLocalName( i ).equals( b.getLocalName( i ) ) - && a.getQName( i ).equals( b.getQName( i ) ) - && a.getType( i ).equals( b.getType( i ) ) - && a.getURI( i ).equals( b.getURI( i ) ) - && a.getValue( i ).equals( b.getValue( i ) ); - } - - return success; - } - } diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/substitution/VariableExpansionTestCase.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/substitution/VariableExpansionTestCase.java index 4bcec5138..bca9946ce 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/substitution/VariableExpansionTestCase.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/substitution/VariableExpansionTestCase.java @@ -43,11 +43,6 @@ public class VariableExpansionTestCase // method used in tests4 private final LinkedList simpleTestBeans = new LinkedList(); - public void addSimpleTestBean( final SimpleTestBean bean ) - { - simpleTestBeans.add( bean ); - } - // implementation of source shared by the variable expander and // is updatable during digesting via an Ant-like property element private final HashMap mutableSource = new HashMap(); @@ -61,6 +56,11 @@ public void addProperty( final String key, final String value ) mutableSource.put( key, value ); } + public void addSimpleTestBean( final SimpleTestBean bean ) + { + simpleTestBeans.add( bean ); + } + /** * Creates a Digester configured to show Ant-like capability. * @@ -90,55 +90,103 @@ private Digester createDigesterThatCanDoAnt() // ------------------------------------------------ Individual Test Methods /** - * Test that by default no expansion occurs. + * Test expansion of text in element bodies. */ @Test - public void testNoExpansion() + public void testBodyExpansion() throws SAXException, IOException { - final String xml = ""; + final String xml = "" + "Twas noun{1} and the noun{2}" + " did verb{1} and verb{2} in the noun{3}" + ""; + final StringReader input = new StringReader( xml ); final Digester digester = new Digester(); // Configure the digester as required + final HashMap nouns = new HashMap(); + nouns.put( "1", "brillig" ); + nouns.put( "2", "slithy toves" ); + nouns.put( "3", "wabe" ); + + final HashMap verbs = new HashMap(); + verbs.put( "1", "gyre" ); + verbs.put( "2", "gimble" ); + + final MultiVariableExpander expander = new MultiVariableExpander(); + expander.addSource( "noun", nouns ); + expander.addSource( "verb", verbs ); + digester.setSubstitutor( new VariableSubstitutor( expander ) ); + digester.addObjectCreate( "root", SimpleTestBean.class ); - digester.addSetProperties( "root" ); + digester.addCallMethod( "root", "setAlpha", 0 ); // Parse our test input. final SimpleTestBean root = digester.parse( input ); assertNotNull( "Digester returned no object", root ); - assertEquals( "${attr1}", root.getAlpha() ); - assertEquals( "var{attr2}", root.getBeta() ); + assertEquals( "Twas brillig and the slithy toves" + " did gyre and gimble in the wabe", root.getAlpha() ); } /** - * Test that a MultiVariableExpander with no sources does no expansion. + * Test that an unknown variable causes a RuntimeException. */ @Test - public void testExpansionWithNoSource() - throws SAXException, IOException + public void testExpansionException() + throws IOException { - final String xml = ""; + final String xml = ""; final StringReader input = new StringReader( xml ); final Digester digester = new Digester(); // Configure the digester as required final MultiVariableExpander expander = new MultiVariableExpander(); + expander.addSource( "$", new HashMap() ); digester.setSubstitutor( new VariableSubstitutor( expander ) ); + digester.addObjectCreate( "root", SimpleTestBean.class ); digester.addSetProperties( "root" ); // Parse our test input. - final SimpleTestBean root = digester.parse( input ); + try + { + digester.parse( input ); + fail( "Exception expected due to unknown variable." ); + } + catch ( final SAXException e ) + { + // expected, due to reference to undefined variable + } + } - assertNotNull( "Digester returned no object", root ); + /** + * Second of two tests added to verify that the substitution framework is capable of processing Ant-like properties. + * This test shows that if properties were also set while processing a document, the resulting variables could also + * be expanded within a property element. This is thus effectively a "closure" test, since it shows that the + * mechanism used to bind properties is also capable of having property values that are driven by property + * variables. + * + * @throws IOException + * @throws SAXException + */ + @Test + public void testExpansionOfPropertyInProperty() + throws SAXException, IOException + { + final String xml = + "" + "" + + "" + "" + ""; + final StringReader input = new StringReader( xml ); + final Digester digester = createDigesterThatCanDoAnt(); - assertEquals( "${attr1}", root.getAlpha() ); - assertEquals( "var{attr2}", root.getBeta() ); + simpleTestBeans.clear(); + digester.push( this ); + digester.parse( input ); + + assertEquals( 1, simpleTestBeans.size() ); + final SimpleTestBean bean = simpleTestBeans.get( 0 ); + assertEquals( "substituted-prop.value1", bean.getAlpha() ); } /** @@ -196,130 +244,82 @@ public void testExpansionWithMultipleSources() } /** - * Test expansion of text in element bodies. + * First of two tests added to verify that the substitution framework is capable of processing Ant-like properties. + * The tests above essentially verify that if a property was pre-set (e.g. using the "-D" option to Ant), then the + * property could be expanded via a variable used either in an attribute or in body text. This test shows that if + * properties were also set while processing a document, you could still perform variable expansion (i.e. just like + * using the "property" task in Ant). + * + * @throws IOException + * @throws SAXException */ @Test - public void testBodyExpansion() + public void testExpansionWithMutableSource() throws SAXException, IOException { - - final String xml = "" + "Twas noun{1} and the noun{2}" + " did verb{1} and verb{2} in the noun{3}" + ""; - + final String xml = "" + "" + "" + ""; final StringReader input = new StringReader( xml ); - final Digester digester = new Digester(); - - // Configure the digester as required - final HashMap nouns = new HashMap(); - nouns.put( "1", "brillig" ); - nouns.put( "2", "slithy toves" ); - nouns.put( "3", "wabe" ); - - final HashMap verbs = new HashMap(); - verbs.put( "1", "gyre" ); - verbs.put( "2", "gimble" ); - - final MultiVariableExpander expander = new MultiVariableExpander(); - expander.addSource( "noun", nouns ); - expander.addSource( "verb", verbs ); - digester.setSubstitutor( new VariableSubstitutor( expander ) ); - - digester.addObjectCreate( "root", SimpleTestBean.class ); - digester.addCallMethod( "root", "setAlpha", 0 ); - - // Parse our test input. - final SimpleTestBean root = digester.parse( input ); + final Digester digester = createDigesterThatCanDoAnt(); - assertNotNull( "Digester returned no object", root ); + simpleTestBeans.clear(); + digester.push( this ); + digester.parse( input ); - assertEquals( "Twas brillig and the slithy toves" + " did gyre and gimble in the wabe", root.getAlpha() ); + assertEquals( 1, simpleTestBeans.size() ); + final SimpleTestBean bean = simpleTestBeans.get( 0 ); + assertEquals( "prop.value", bean.getAlpha() ); } /** - * Test that an unknown variable causes a RuntimeException. + * Test that a MultiVariableExpander with no sources does no expansion. */ @Test - public void testExpansionException() - throws IOException + public void testExpansionWithNoSource() + throws SAXException, IOException { - final String xml = ""; + final String xml = ""; final StringReader input = new StringReader( xml ); final Digester digester = new Digester(); // Configure the digester as required final MultiVariableExpander expander = new MultiVariableExpander(); - expander.addSource( "$", new HashMap() ); digester.setSubstitutor( new VariableSubstitutor( expander ) ); - digester.addObjectCreate( "root", SimpleTestBean.class ); digester.addSetProperties( "root" ); // Parse our test input. - try - { - digester.parse( input ); - fail( "Exception expected due to unknown variable." ); - } - catch ( final SAXException e ) - { - // expected, due to reference to undefined variable - } - } - - /** - * First of two tests added to verify that the substitution framework is capable of processing Ant-like properties. - * The tests above essentially verify that if a property was pre-set (e.g. using the "-D" option to Ant), then the - * property could be expanded via a variable used either in an attribute or in body text. This test shows that if - * properties were also set while processing a document, you could still perform variable expansion (i.e. just like - * using the "property" task in Ant). - * - * @throws IOException - * @throws SAXException - */ - @Test - public void testExpansionWithMutableSource() - throws SAXException, IOException - { - final String xml = "" + "" + "" + ""; - final StringReader input = new StringReader( xml ); - final Digester digester = createDigesterThatCanDoAnt(); + final SimpleTestBean root = digester.parse( input ); - simpleTestBeans.clear(); - digester.push( this ); - digester.parse( input ); + assertNotNull( "Digester returned no object", root ); - assertEquals( 1, simpleTestBeans.size() ); - final SimpleTestBean bean = simpleTestBeans.get( 0 ); - assertEquals( "prop.value", bean.getAlpha() ); + assertEquals( "${attr1}", root.getAlpha() ); + assertEquals( "var{attr2}", root.getBeta() ); } /** - * Second of two tests added to verify that the substitution framework is capable of processing Ant-like properties. - * This test shows that if properties were also set while processing a document, the resulting variables could also - * be expanded within a property element. This is thus effectively a "closure" test, since it shows that the - * mechanism used to bind properties is also capable of having property values that are driven by property - * variables. - * - * @throws IOException - * @throws SAXException + * Test that by default no expansion occurs. */ @Test - public void testExpansionOfPropertyInProperty() + public void testNoExpansion() throws SAXException, IOException { - final String xml = - "" + "" - + "" + "" + ""; + + final String xml = ""; final StringReader input = new StringReader( xml ); - final Digester digester = createDigesterThatCanDoAnt(); + final Digester digester = new Digester(); - simpleTestBeans.clear(); - digester.push( this ); - digester.parse( input ); + // Configure the digester as required + digester.addObjectCreate( "root", SimpleTestBean.class ); + digester.addSetProperties( "root" ); - assertEquals( 1, simpleTestBeans.size() ); - final SimpleTestBean bean = simpleTestBeans.get( 0 ); - assertEquals( "substituted-prop.value1", bean.getAlpha() ); + // Parse our test input. + final SimpleTestBean root = digester.parse( input ); + + assertNotNull( "Digester returned no object", root ); + + assertEquals( "${attr1}", root.getAlpha() ); + assertEquals( "var{attr2}", root.getBeta() ); } } diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/xmlrules/CallParamTestObject.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/xmlrules/CallParamTestObject.java index e8925b01e..bb3468a8e 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/xmlrules/CallParamTestObject.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/xmlrules/CallParamTestObject.java @@ -30,13 +30,6 @@ public class CallParamTestObject private String middle = "UNSET"; - public void triple( final String left, final String middle, final String right ) - { - this.left = left; - this.right = right; - this.middle = middle; - } - public void duo( final String left, final String right ) { this.left = left; @@ -48,14 +41,14 @@ public String getLeft() return left; } - public String getRight() + public String getMiddle() { - return right; + return middle; } - public String getMiddle() + public String getRight() { - return middle; + return right; } public void setLeft( final String left ) @@ -63,14 +56,14 @@ public void setLeft( final String left ) this.left = left; } - public void setRight( final String right ) + public void setMiddle( final String middle ) { - this.right = right; + this.middle = middle; } - public void setMiddle( final String middle ) + public void setRight( final String right ) { - this.middle = middle; + this.right = right; } @Override @@ -78,4 +71,11 @@ public String toString() { return "LEFT: " + left + " MIDDLE:" + middle + " RIGHT:" + right; } + + public void triple( final String left, final String middle, final String right ) + { + this.left = left; + this.right = right; + this.middle = middle; + } } diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/xmlrules/Entry.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/xmlrules/Entry.java index 37610a361..d0fedd42b 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/xmlrules/Entry.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/xmlrules/Entry.java @@ -35,14 +35,14 @@ public final class Entry private String content; - public String getTitle() + public String getContent() { - return title; + return content; } - public void setTitle( final String title ) + public String getId() { - this.title = title; + return id; } public URL getLink() @@ -50,9 +50,9 @@ public URL getLink() return link; } - public void setLink( final URL link ) + public String getTitle() { - this.link = link; + return title; } public Date getUpdated() @@ -60,29 +60,29 @@ public Date getUpdated() return updated; } - public void setUpdated( final Date updated ) + public void setContent( final String content ) { - this.updated = updated; + this.content = content; } - public String getId() + public void setId( final String id ) { - return id; + this.id = id; } - public void setId( final String id ) + public void setLink( final URL link ) { - this.id = id; + this.link = link; } - public String getContent() + public void setTitle( final String title ) { - return content; + this.title = title; } - public void setContent( final String content ) + public void setUpdated( final Date updated ) { - this.content = content; + this.updated = updated; } @Override diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/xmlrules/Feed.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/xmlrules/Feed.java index 0ef533b97..c459d40ac 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/xmlrules/Feed.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/xmlrules/Feed.java @@ -39,64 +39,64 @@ public final class Feed private final List entries = new ArrayList(); - public String getTitle() + public void addAuthor( final String author ) { - return title; + authors.add( author ); } - public void setTitle( final String title ) + public void addEntry( final Entry entry ) { - this.title = title; + entries.add( entry ); } - public URL getLink() + public List getAuthors() { - return link; + return authors; } - public void setLink( final URL link ) + public List getEntries() { - this.link = link; + return entries; } - public Date getUpdated() + public String getId() { - return updated; + return id; } - public void setUpdated( final Date updated ) + public URL getLink() { - this.updated = updated; + return link; } - public String getId() + public String getTitle() { - return id; + return title; } - public void setId( final String id ) + public Date getUpdated() { - this.id = id; + return updated; } - public List getAuthors() + public void setId( final String id ) { - return authors; + this.id = id; } - public void addAuthor( final String author ) + public void setLink( final URL link ) { - authors.add( author ); + this.link = link; } - public List getEntries() + public void setTitle( final String title ) { - return entries; + this.title = title; } - public void addEntry( final Entry entry ) + public void setUpdated( final Date updated ) { - entries.add( entry ); + this.updated = updated; } @Override diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/xmlrules/FromXmlRuleSetTest.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/xmlrules/FromXmlRuleSetTest.java index 512aa97e2..0bb7904e7 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/xmlrules/FromXmlRuleSetTest.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/xmlrules/FromXmlRuleSetTest.java @@ -42,7 +42,7 @@ public class FromXmlRuleSetTest { - private final RulesModule createRules( final URL xmlRules ) + private final RulesModule createRules( final String xmlText ) { return new FromXmlRulesModule() { @@ -50,13 +50,13 @@ private final RulesModule createRules( final URL xmlRules ) @Override protected void loadRules() { - loadXMLRules( xmlRules ); + loadXMLRulesFromText( xmlText ); } }; } - private final RulesModule createRules( final String xmlText ) + private final RulesModule createRules( final URL xmlRules ) { return new FromXmlRulesModule() { @@ -64,146 +64,80 @@ private final RulesModule createRules( final String xmlText ) @Override protected void loadRules() { - loadXMLRulesFromText( xmlText ); + loadXMLRules( xmlRules ); } }; } /** - * Tests the DigesterLoader.createDigester(), with multiple - * included rule sources: testrules.xml includes another rules xml - * file, and also includes programmatically created rules. + * Test the FromXmlRules.addRuleInstances(digester, path) method, ie + * test loading rules at a base position other than the root. */ @Test - public void testCreateDigester() + public void testBasePath() throws Exception { - final URL rules = getClass().getResource( "testrules.xml" ); - final URL input = getClass().getResource( "test.xml" ); - - final Digester digester = newLoader( createRules( rules ) ).newDigester(); - digester.push( new ArrayList() ); - final Object root = digester.parse( input.openStream() ); - assertEquals( "[foo1 baz1 foo2, foo3 foo4]", root.toString() ); - } + final String xmlRules = + "" + + "" + + " " + + " " + + " " + + ""; - /** - * Tests the DigesterLoader.load(), with multiple included rule - * sources: testrules.xml includes another rules xml file, and - * also includes programmatically created rules. - */ - @Test - public void testLoad1() - throws Exception - { - final ClassLoader classLoader = getClass().getClassLoader(); - final URL rules = getClass().getResource( "testrules.xml" ); - final URL input = getClass().getResource( "test.xml" ); + final String xml = + "" + + "" + + " success" + + ""; - final Digester digester = newLoader( createRules( rules ) ).setClassLoader( classLoader ).newDigester(); - digester.push( new ArrayList() ); + final ObjectTestImpl testObject = new ObjectTestImpl(); + final Digester digester = newLoader( createRules( xmlRules ) ).newDigester(); - final Object root = digester.parse( input ); - if ( !( root instanceof ArrayList ) ) - { - fail( "Unexpected object returned from DigesterLoader. Expected ArrayList; got " - + root.getClass().getName() ); - } - assertEquals( "[foo1 baz1 foo2, foo3 foo4]", root.toString() ); + digester.push( testObject ); + digester.parse( new InputSource( new StringReader( xml ) ) ); - @SuppressWarnings( "unchecked" ) - final - // root is an ArrayList - ArrayList al = (ArrayList) root; - final Object obj = al.get( 0 ); - if ( !( obj instanceof ObjectTestImpl ) ) - { - fail( "Unexpected object returned from DigesterLoader. Expected TestObject; got " - + obj.getClass().getName() ); - } - final ObjectTestImpl to = (ObjectTestImpl) obj; - assertEquals( new Long( 555 ), to.getLongValue() ); - assertEquals( "foo", to.getMapValue( "test1" ) ); - assertEquals( "bar", to.getMapValue( "test2" ) ); + assertEquals( "success", testObject.getProperty() ); } - /** - * The same as testLoad1, exception the input file is passed to - * DigesterLoader as an InputStream instead of a URL. - */ @Test - public void testLoad2() + public void testCallParamRule() throws Exception { - final URL rules = getClass().getResource( "testrules.xml" ); - final InputStream input = getClass().getResourceAsStream( "test.xml" ); + final URL rules = getClass().getResource( "test-call-param-rules.xml" ); + + final String xml = "" + + "shorttosh"; + + final CallParamTestObject testObject = new CallParamTestObject(); + final Digester digester = newLoader( createRules( rules ) ).setClassLoader( this.getClass().getClassLoader() ).newDigester(); - digester.push( new ArrayList() ); - - final ArrayList list = digester.parse( input ); + digester.push( testObject ); + digester.parse( new StringReader( xml ) ); - assertEquals( list.toString(), "[foo1 baz1 foo2, foo3 foo4]" ); - assertEquals( "Wrong number of classes created", 2, list.size() ); - assertEquals( "Pushed first", true, ( (ObjectTestImpl) list.get( 0 ) ).isPushed() ); - assertEquals( "Didn't push second", false, ( (ObjectTestImpl) list.get( 1 ) ).isPushed() ); - assertTrue( "Property was set properly", - ( (ObjectTestImpl) list.get( 0 ) ).getProperty().equals( "I am a property!" ) ); - } + assertEquals( "Incorrect left value", "long", testObject.getLeft() ); + assertEquals( "Incorrect middle value", "short", testObject.getMiddle() ); + assertEquals( "Incorrect right value", "", testObject.getRight() ); + } /** + * Tests the DigesterLoader.createDigester(), with multiple + * included rule sources: testrules.xml includes another rules xml + * file, and also includes programmatically created rules. */ @Test - public void testSetCustomProperties() + public void testCreateDigester() throws Exception { - final URL rules = this.getClass().getResource( "testPropertyAliasRules.xml" ); - final InputStream input = - getClass().getClassLoader().getResource( "org/apache/commons/digester3/Test7.xml" ).openStream(); + final URL rules = getClass().getResource( "testrules.xml" ); + final URL input = getClass().getResource( "test.xml" ); - final Digester digester = - newLoader( createRules( rules ) ).setClassLoader( this.getClass().getClassLoader() ).newDigester(); + final Digester digester = newLoader( createRules( rules ) ).newDigester(); digester.push( new ArrayList() ); - - final Object obj = digester.parse( input ); - - if ( !( obj instanceof ArrayList ) ) - { - fail( "Unexpected object returned from DigesterLoader. Expected ArrayList; got " + obj.getClass().getName() ); - } - - @SuppressWarnings("unchecked") // root is an ArrayList of Address - final - ArrayList
      root = (ArrayList
      ) obj; - - assertEquals( "Wrong array size", 4, root.size() ); - - // note that the array is in popped order (rather than pushed) - - Address add = root.get( 0 ); - final Address addressOne = add; - assertEquals( "(1) Street attribute", "New Street", addressOne.getStreet() ); - assertEquals( "(1) City attribute", "Las Vegas", addressOne.getCity() ); - assertEquals( "(1) State attribute", "Nevada", addressOne.getState() ); - - add = root.get( 1 ); - final Address addressTwo = add; - assertEquals( "(2) Street attribute", "Old Street", addressTwo.getStreet() ); - assertEquals( "(2) City attribute", "Portland", addressTwo.getCity() ); - assertEquals( "(2) State attribute", "Oregon", addressTwo.getState() ); - - add = root.get( 2 ); - final Address addressThree = add; - assertEquals( "(3) Street attribute", "4th Street", addressThree.getStreet() ); - assertEquals( "(3) City attribute", "Dayton", addressThree.getCity() ); - assertEquals( "(3) State attribute", "US", addressThree.getState() ); - - add = root.get( 3 ); - final Address addressFour = add; - assertEquals( "(4) Street attribute", "6th Street", addressFour.getStreet() ); - assertEquals( "(4) City attribute", "Cleveland", addressFour.getCity() ); - assertEquals( "(4) State attribute", "Ohio", addressFour.getState() ); + final Object root = digester.parse( input.openStream() ); + assertEquals( "[foo1 baz1 foo2, foo3 foo4]", root.toString() ); } @Test @@ -268,27 +202,6 @@ public void testFactoryNotIgnoreCreateRule() } } - @Test - public void testCallParamRule() - throws Exception - { - final URL rules = getClass().getResource( "test-call-param-rules.xml" ); - - final String xml = "" - + "shorttosh"; - - final CallParamTestObject testObject = new CallParamTestObject(); - - final Digester digester = - newLoader( createRules( rules ) ).setClassLoader( this.getClass().getClassLoader() ).newDigester(); - digester.push( testObject ); - digester.parse( new StringReader( xml ) ); - - assertEquals( "Incorrect left value", "long", testObject.getLeft() ); - assertEquals( "Incorrect middle value", "short", testObject.getMiddle() ); - assertEquals( "Incorrect right value", "", testObject.getRight() ); - } - @Test public void testInputSourceLoader() throws Exception { final String rulesXml = "" @@ -326,33 +239,120 @@ public void testInputSourceLoader() throws Exception { } /** - * Test the FromXmlRules.addRuleInstances(digester, path) method, ie - * test loading rules at a base position other than the root. + * Tests the DigesterLoader.load(), with multiple included rule + * sources: testrules.xml includes another rules xml file, and + * also includes programmatically created rules. */ @Test - public void testBasePath() + public void testLoad1() throws Exception { - final String xmlRules = - "" - + "" - + " " - + " " - + " " - + ""; + final ClassLoader classLoader = getClass().getClassLoader(); + final URL rules = getClass().getResource( "testrules.xml" ); + final URL input = getClass().getResource( "test.xml" ); - final String xml = - "" - + "" - + " success" - + ""; + final Digester digester = newLoader( createRules( rules ) ).setClassLoader( classLoader ).newDigester(); + digester.push( new ArrayList() ); - final ObjectTestImpl testObject = new ObjectTestImpl(); - final Digester digester = newLoader( createRules( xmlRules ) ).newDigester(); + final Object root = digester.parse( input ); + if ( !( root instanceof ArrayList ) ) + { + fail( "Unexpected object returned from DigesterLoader. Expected ArrayList; got " + + root.getClass().getName() ); + } + assertEquals( "[foo1 baz1 foo2, foo3 foo4]", root.toString() ); - digester.push( testObject ); - digester.parse( new InputSource( new StringReader( xml ) ) ); + @SuppressWarnings( "unchecked" ) + final + // root is an ArrayList + ArrayList al = (ArrayList) root; + final Object obj = al.get( 0 ); + if ( !( obj instanceof ObjectTestImpl ) ) + { + fail( "Unexpected object returned from DigesterLoader. Expected TestObject; got " + + obj.getClass().getName() ); + } + final ObjectTestImpl to = (ObjectTestImpl) obj; + assertEquals( new Long( 555 ), to.getLongValue() ); + assertEquals( "foo", to.getMapValue( "test1" ) ); + assertEquals( "bar", to.getMapValue( "test2" ) ); + } - assertEquals( "success", testObject.getProperty() ); + /** + * The same as testLoad1, exception the input file is passed to + * DigesterLoader as an InputStream instead of a URL. + */ + @Test + public void testLoad2() + throws Exception + { + final URL rules = getClass().getResource( "testrules.xml" ); + final InputStream input = getClass().getResourceAsStream( "test.xml" ); + final Digester digester = + newLoader( createRules( rules ) ).setClassLoader( this.getClass().getClassLoader() ).newDigester(); + digester.push( new ArrayList() ); + + final ArrayList list = digester.parse( input ); + + assertEquals( list.toString(), "[foo1 baz1 foo2, foo3 foo4]" ); + assertEquals( "Wrong number of classes created", 2, list.size() ); + assertEquals( "Pushed first", true, ( (ObjectTestImpl) list.get( 0 ) ).isPushed() ); + assertEquals( "Didn't push second", false, ( (ObjectTestImpl) list.get( 1 ) ).isPushed() ); + assertTrue( "Property was set properly", + ( (ObjectTestImpl) list.get( 0 ) ).getProperty().equals( "I am a property!" ) ); + } + + /** + */ + @Test + public void testSetCustomProperties() + throws Exception + { + final URL rules = this.getClass().getResource( "testPropertyAliasRules.xml" ); + final InputStream input = + getClass().getClassLoader().getResource( "org/apache/commons/digester3/Test7.xml" ).openStream(); + + final Digester digester = + newLoader( createRules( rules ) ).setClassLoader( this.getClass().getClassLoader() ).newDigester(); + digester.push( new ArrayList() ); + + final Object obj = digester.parse( input ); + + if ( !( obj instanceof ArrayList ) ) + { + fail( "Unexpected object returned from DigesterLoader. Expected ArrayList; got " + obj.getClass().getName() ); + } + + @SuppressWarnings("unchecked") // root is an ArrayList of Address + final + ArrayList
      root = (ArrayList
      ) obj; + + assertEquals( "Wrong array size", 4, root.size() ); + + // note that the array is in popped order (rather than pushed) + + Address add = root.get( 0 ); + final Address addressOne = add; + assertEquals( "(1) Street attribute", "New Street", addressOne.getStreet() ); + assertEquals( "(1) City attribute", "Las Vegas", addressOne.getCity() ); + assertEquals( "(1) State attribute", "Nevada", addressOne.getState() ); + + add = root.get( 1 ); + final Address addressTwo = add; + assertEquals( "(2) Street attribute", "Old Street", addressTwo.getStreet() ); + assertEquals( "(2) City attribute", "Portland", addressTwo.getCity() ); + assertEquals( "(2) State attribute", "Oregon", addressTwo.getState() ); + + add = root.get( 2 ); + final Address addressThree = add; + assertEquals( "(3) Street attribute", "4th Street", addressThree.getStreet() ); + assertEquals( "(3) City attribute", "Dayton", addressThree.getCity() ); + assertEquals( "(3) State attribute", "US", addressThree.getState() ); + + add = root.get( 3 ); + final Address addressFour = add; + assertEquals( "(4) Street attribute", "6th Street", addressFour.getStreet() ); + assertEquals( "(4) City attribute", "Cleveland", addressFour.getCity() ); + assertEquals( "(4) State attribute", "Ohio", addressFour.getState() ); } } diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/xmlrules/IncludeTest.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/xmlrules/IncludeTest.java index 434089c6d..4445302e8 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/xmlrules/IncludeTest.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/xmlrules/IncludeTest.java @@ -93,6 +93,26 @@ protected void loadRules() assertEquals( "Entry value", "short", list.get( 0 ) ); } + /** + * Validates that circular includes are detected and result in an exception + */ + @Test( expected = org.apache.commons.digester3.binder.DigesterLoadingException.class ) + public void testCircularInclude() + throws Exception + { + final URL url = ClassLoader.getSystemResource( "org/apache/commons/digester3/xmlrules/testCircularRules.xml" ); + newLoader( new FromXmlRulesModule() + { + + @Override + protected void loadRules() + { + loadXMLRules( url ); + } + + }).newDigester(); + } + @Test public void testUrlInclude() throws Exception @@ -127,24 +147,4 @@ protected void loadRules() assertEquals( "[foo1, foo2]", list.toString() ); } - /** - * Validates that circular includes are detected and result in an exception - */ - @Test( expected = org.apache.commons.digester3.binder.DigesterLoadingException.class ) - public void testCircularInclude() - throws Exception - { - final URL url = ClassLoader.getSystemResource( "org/apache/commons/digester3/xmlrules/testCircularRules.xml" ); - newLoader( new FromXmlRulesModule() - { - - @Override - protected void loadRules() - { - loadXMLRules( url ); - } - - }).newDigester(); - } - } diff --git a/commons-digester3-core/src/test/java/org/apache/commons/digester3/xmlrules/ObjectTestImpl.java b/commons-digester3-core/src/test/java/org/apache/commons/digester3/xmlrules/ObjectTestImpl.java index 520451b66..b3b22c383 100644 --- a/commons-digester3-core/src/test/java/org/apache/commons/digester3/xmlrules/ObjectTestImpl.java +++ b/commons-digester3-core/src/test/java/org/apache/commons/digester3/xmlrules/ObjectTestImpl.java @@ -43,39 +43,24 @@ public ObjectTestImpl() { } - @Override - public String toString() - { - String str = value; - for ( final Object o : children ) - { - str += " " + o; - } - return str; - } - public void add( final Object o ) { children.add( o ); } - public void setValue( final String val ) - { - value = val; - } - - public void setLongValue( final Long val ) + public Long getLongValue() { - longValue = val; + return longValue; } - public Long getLongValue() + public String getMapValue( final String name ) { - return longValue; + return this.mapValue.get( name ); } - public void setStringValue( final String val ) + public String getProperty() { + return property; } public boolean isPushed() @@ -88,23 +73,38 @@ public void push() pushed = true; } + public void setLongValue( final Long val ) + { + longValue = val; + } + public void setMapValue( final String name, final String value ) { this.mapValue.put( name, value ); } - public String getMapValue( final String name ) + public void setProperty( final String pProperty ) { - return this.mapValue.get( name ); + property = pProperty; } - public String getProperty() + public void setStringValue( final String val ) { - return property; } - public void setProperty( final String pProperty ) + public void setValue( final String val ) { - property = pProperty; + value = val; + } + + @Override + public String toString() + { + String str = value; + for ( final Object o : children ) + { + str += " " + o; + } + return str; } } diff --git a/commons-digester3-examples/annotations/atom/src/main/java/org/apache/commons/digester3/annotations/atom/Entry.java b/commons-digester3-examples/annotations/atom/src/main/java/org/apache/commons/digester3/annotations/atom/Entry.java index 1ad2dfad6..e8bde2fbf 100644 --- a/commons-digester3-examples/annotations/atom/src/main/java/org/apache/commons/digester3/annotations/atom/Entry.java +++ b/commons-digester3-examples/annotations/atom/src/main/java/org/apache/commons/digester3/annotations/atom/Entry.java @@ -45,14 +45,14 @@ public final class Entry @BeanPropertySetter( pattern = "feed/entry/content" ) private String content; - public String getTitle() + public String getContent() { - return title; + return content; } - public void setTitle( final String title ) + public String getId() { - this.title = title; + return id; } public URL getLink() @@ -60,9 +60,9 @@ public URL getLink() return link; } - public void setLink( final URL link ) + public String getTitle() { - this.link = link; + return title; } public Date getUpdated() @@ -70,29 +70,29 @@ public Date getUpdated() return updated; } - public void setUpdated( final Date updated ) + public void setContent( final String content ) { - this.updated = updated; + this.content = content; } - public String getId() + public void setId( final String id ) { - return id; + this.id = id; } - public void setId( final String id ) + public void setLink( final URL link ) { - this.id = id; + this.link = link; } - public String getContent() + public void setTitle( final String title ) { - return content; + this.title = title; } - public void setContent( final String content ) + public void setUpdated( final Date updated ) { - this.content = content; + this.updated = updated; } @Override diff --git a/commons-digester3-examples/annotations/atom/src/main/java/org/apache/commons/digester3/annotations/atom/Feed.java b/commons-digester3-examples/annotations/atom/src/main/java/org/apache/commons/digester3/annotations/atom/Feed.java index 3c0136a59..26d018052 100644 --- a/commons-digester3-examples/annotations/atom/src/main/java/org/apache/commons/digester3/annotations/atom/Feed.java +++ b/commons-digester3-examples/annotations/atom/src/main/java/org/apache/commons/digester3/annotations/atom/Feed.java @@ -50,66 +50,66 @@ public final class Feed private final List entries = new ArrayList(); - public String getTitle() + @CallMethod( pattern = "feed/author/name", usingElementBodyAsArgument = true ) + public void addAuthor( final String author ) { - return title; + authors.add( author ); } - public void setTitle( final String title ) + @SetNext + public void addEntry( final Entry entry ) { - this.title = title; + entries.add( entry ); } - public URL getLink() + public List getAuthors() { - return link; + return authors; } - public void setLink( final URL link ) + public List getEntries() { - this.link = link; + return entries; } - public Date getUpdated() + public String getId() { - return updated; + return id; } - public void setUpdated( final Date updated ) + public URL getLink() { - this.updated = updated; + return link; } - public String getId() + public String getTitle() { - return id; + return title; } - public void setId( final String id ) + public Date getUpdated() { - this.id = id; + return updated; } - public List getAuthors() + public void setId( final String id ) { - return authors; + this.id = id; } - @CallMethod( pattern = "feed/author/name", usingElementBodyAsArgument = true ) - public void addAuthor( final String author ) + public void setLink( final URL link ) { - authors.add( author ); + this.link = link; } - public List getEntries() + public void setTitle( final String title ) { - return entries; + this.title = title; } - @SetNext - public void addEntry( final Entry entry ) + public void setUpdated( final Date updated ) { - entries.add( entry ); + this.updated = updated; } @Override diff --git a/commons-digester3-examples/api/addressbook/src/main/java/org/apache/commons/digester3/examples/api/addressbook/Address.java b/commons-digester3-examples/api/addressbook/src/main/java/org/apache/commons/digester3/examples/api/addressbook/Address.java index bf7afbd6e..959a1e48f 100644 --- a/commons-digester3-examples/api/addressbook/src/main/java/org/apache/commons/digester3/examples/api/addressbook/Address.java +++ b/commons-digester3-examples/api/addressbook/src/main/java/org/apache/commons/digester3/examples/api/addressbook/Address.java @@ -35,37 +35,28 @@ public class Address private String country; - @Override - public String toString() + /** + * Returns the value of city. + */ + public String getCity() { - final StringBuilder sb = new StringBuilder(); - sb.append( " address (type " + type + ")\n" ); - sb.append( " " + street + "\n" ); - sb.append( " " + city + " " + state + " " + zip + "\n" ); - sb.append( " " + country + "\n" ); - return sb.toString(); + return city; } - public void print( final java.io.PrintStream out, int indentAmount ) + /** + * Returns the value of country. + */ + public String getCountry() { - final StringBuilder indentStr = new StringBuilder( indentAmount ); - for ( ; indentAmount > 0; --indentAmount ) - { - indentStr.append( ' ' ); - } - - out.print( indentStr ); - out.print( "address type: " ); - out.println( type ); - - out.print( indentStr ); - out.println( " " + street ); - - out.print( indentStr ); - out.println( " " + city + " " + state + " " + zip ); + return country; + } - out.print( indentStr ); - out.println( " " + country ); + /** + * Returns the value of state. + */ + public String getState() + { + return state; } /** @@ -77,21 +68,41 @@ public String getStreet() } /** - * Sets the value of street. - * - * @param street The value to assign to street. + * Returns the value of type. */ - public void setStreet( final String street ) + public String getType() { - this.street = street; + return type; } /** - * Returns the value of city. + * Returns the value of zip. */ - public String getCity() + public String getZip() { - return city; + return zip; + } + + public void print( final java.io.PrintStream out, int indentAmount ) + { + final StringBuilder indentStr = new StringBuilder( indentAmount ); + for ( ; indentAmount > 0; --indentAmount ) + { + indentStr.append( ' ' ); + } + + out.print( indentStr ); + out.print( "address type: " ); + out.println( type ); + + out.print( indentStr ); + out.println( " " + street ); + + out.print( indentStr ); + out.println( " " + city + " " + state + " " + zip ); + + out.print( indentStr ); + out.println( " " + country ); } /** @@ -105,11 +116,13 @@ public void setCity( final String city ) } /** - * Returns the value of state. + * Sets the value of country. + * + * @param country The value to assign to country. */ - public String getState() + public void setCountry( final String country ) { - return state; + this.country = country; } /** @@ -123,57 +136,44 @@ public void setState( final String state ) } /** - * Returns the value of zip. - */ - public String getZip() - { - return zip; - } - - /** - * Sets the value of zip. + * Sets the value of street. * - * @param zip The value to assign to zip. - */ - public void setZip( final String zip ) - { - this.zip = zip; - } - - /** - * Returns the value of country. + * @param street The value to assign to street. */ - public String getCountry() + public void setStreet( final String street ) { - return country; + this.street = street; } /** - * Sets the value of country. + * Sets the value of type. * - * @param country The value to assign to country. + * @param type The value to assign to type. */ - public void setCountry( final String country ) + public void setType( final String type ) { - this.country = country; + this.type = type; } /** - * Returns the value of type. + * Sets the value of zip. + * + * @param zip The value to assign to zip. */ - public String getType() + public void setZip( final String zip ) { - return type; + this.zip = zip; } - /** - * Sets the value of type. - * - * @param type The value to assign to type. - */ - public void setType( final String type ) + @Override + public String toString() { - this.type = type; + final StringBuilder sb = new StringBuilder(); + sb.append( " address (type " + type + ")\n" ); + sb.append( " " + street + "\n" ); + sb.append( " " + city + " " + state + " " + zip + "\n" ); + sb.append( " " + country + "\n" ); + return sb.toString(); } } diff --git a/commons-digester3-examples/api/addressbook/src/main/java/org/apache/commons/digester3/examples/api/addressbook/Main.java b/commons-digester3-examples/api/addressbook/src/main/java/org/apache/commons/digester3/examples/api/addressbook/Main.java index ee726cb46..efbb6df59 100644 --- a/commons-digester3-examples/api/addressbook/src/main/java/org/apache/commons/digester3/examples/api/addressbook/Main.java +++ b/commons-digester3-examples/api/addressbook/src/main/java/org/apache/commons/digester3/examples/api/addressbook/Main.java @@ -40,56 +40,6 @@ public class Main { - /** - * Main method : entry point for running this example program. - *

      - * Usage: java Example example.xml - */ - public static void main( final String[] args ) - { - if ( args.length != 1 ) - { - usage(); - System.exit( -1 ); - } - - final String filename = args[0]; - - // Create a Digester instance - final Digester d = new Digester(); - - // Prime the digester stack with an object for rules to - // operate on. Note that it is quite common for "this" - // to be the object pushed. - final AddressBook book = new AddressBook(); - d.push( book ); - - // Add rules to the digester that will be triggered while - // parsing occurs. - addRules( d ); - - // Process the input file. - try - { - final java.io.File srcfile = new java.io.File( filename ); - d.parse( srcfile ); - } - catch ( final java.io.IOException ioe ) - { - System.out.println( "Error reading input file:" + ioe.getMessage() ); - System.exit( -1 ); - } - catch ( final org.xml.sax.SAXException se ) - { - System.out.println( "Error parsing input file:" + se.getMessage() ); - System.exit( -1 ); - } - - // Print out all the contents of the address book, as loaded from - // the input file. - book.print(); - } - private static void addRules( final Digester d ) { @@ -154,6 +104,56 @@ private static void addRules( final Digester d ) d.addSetNestedProperties( "address-book/person/address" ); } + /** + * Main method : entry point for running this example program. + *

      + * Usage: java Example example.xml + */ + public static void main( final String[] args ) + { + if ( args.length != 1 ) + { + usage(); + System.exit( -1 ); + } + + final String filename = args[0]; + + // Create a Digester instance + final Digester d = new Digester(); + + // Prime the digester stack with an object for rules to + // operate on. Note that it is quite common for "this" + // to be the object pushed. + final AddressBook book = new AddressBook(); + d.push( book ); + + // Add rules to the digester that will be triggered while + // parsing occurs. + addRules( d ); + + // Process the input file. + try + { + final java.io.File srcfile = new java.io.File( filename ); + d.parse( srcfile ); + } + catch ( final java.io.IOException ioe ) + { + System.out.println( "Error reading input file:" + ioe.getMessage() ); + System.exit( -1 ); + } + catch ( final org.xml.sax.SAXException se ) + { + System.out.println( "Error parsing input file:" + se.getMessage() ); + System.exit( -1 ); + } + + // Print out all the contents of the address book, as loaded from + // the input file. + book.print(); + } + private static void usage() { System.out.println( "Usage: java Main example.xml" ); diff --git a/commons-digester3-examples/api/addressbook/src/main/java/org/apache/commons/digester3/examples/api/addressbook/Person.java b/commons-digester3-examples/api/addressbook/src/main/java/org/apache/commons/digester3/examples/api/addressbook/Person.java index ea9c5d609..e399cef1d 100644 --- a/commons-digester3-examples/api/addressbook/src/main/java/org/apache/commons/digester3/examples/api/addressbook/Person.java +++ b/commons-digester3-examples/api/addressbook/src/main/java/org/apache/commons/digester3/examples/api/addressbook/Person.java @@ -37,22 +37,9 @@ public class Person private final List

      addresses = new ArrayList
      (); - /** - * A unique id for this person. Note that the Digester automatically converts the id to an integer. - */ - public void setId( final int id ) - { - this.id = id; - } - - public void setCategory( final String category ) - { - this.category = category; - } - - public void setName( final String name ) + public void addAddress( final Address addr ) { - this.name = name; + addresses.add( addr ); } /** we assume only one email of each type... */ @@ -61,11 +48,6 @@ public void addEmail( final String type, final String address ) emails.put( type, address ); } - public void addAddress( final Address addr ) - { - addresses.add( addr ); - } - public void print() { System.out.println( "Person #" + id ); @@ -83,4 +65,22 @@ public void print() } } + public void setCategory( final String category ) + { + this.category = category; + } + + /** + * A unique id for this person. Note that the Digester automatically converts the id to an integer. + */ + public void setId( final int id ) + { + this.id = id; + } + + public void setName( final String name ) + { + this.name = name; + } + } diff --git a/commons-digester3-examples/api/catalog/src/main/java/org/apache/commons/digester3/examples/api/catalog/AudioVisual.java b/commons-digester3-examples/api/catalog/src/main/java/org/apache/commons/digester3/examples/api/catalog/AudioVisual.java index 54956038b..9f4f9b9cc 100644 --- a/commons-digester3-examples/api/catalog/src/main/java/org/apache/commons/digester3/examples/api/catalog/AudioVisual.java +++ b/commons-digester3-examples/api/catalog/src/main/java/org/apache/commons/digester3/examples/api/catalog/AudioVisual.java @@ -36,10 +36,16 @@ public class AudioVisual private String type; - // note: digester can convert a string in the xml file to an int. - public void setYearMade( final int yearMade ) + @Override + public void print() { - this.yearMade = yearMade; + System.out.println( "AudioVisual:" ); + System.out.println( " type=" + type ); + System.out.println( " yearMade=" + yearMade ); + System.out.println( " category=" + category ); + System.out.println( " name=" + name ); + System.out.println( " desc=" + desc ); + System.out.println( " runtime=" + runtime ); } public void setCategory( final String category ) @@ -47,14 +53,14 @@ public void setCategory( final String category ) this.category = category; } - public void setName( final String name ) + public void setDesc( final String desc ) { - this.name = name; + this.desc = desc; } - public void setDesc( final String desc ) + public void setName( final String name ) { - this.desc = desc; + this.name = name; } // note: digester can convert a string in the xml file to an Integer @@ -68,16 +74,10 @@ public void setType( final String type ) this.type = type; } - @Override - public void print() + // note: digester can convert a string in the xml file to an int. + public void setYearMade( final int yearMade ) { - System.out.println( "AudioVisual:" ); - System.out.println( " type=" + type ); - System.out.println( " yearMade=" + yearMade ); - System.out.println( " category=" + category ); - System.out.println( " name=" + name ); - System.out.println( " desc=" + desc ); - System.out.println( " runtime=" + runtime ); + this.yearMade = yearMade; } } diff --git a/commons-digester3-examples/api/catalog/src/main/java/org/apache/commons/digester3/examples/api/catalog/Book.java b/commons-digester3-examples/api/catalog/src/main/java/org/apache/commons/digester3/examples/api/catalog/Book.java index 35e1db53f..4c9aab0e4 100644 --- a/commons-digester3-examples/api/catalog/src/main/java/org/apache/commons/digester3/examples/api/catalog/Book.java +++ b/commons-digester3-examples/api/catalog/src/main/java/org/apache/commons/digester3/examples/api/catalog/Book.java @@ -37,9 +37,14 @@ public Book( final String isbn ) this.isbn = isbn; } - public void setTitle( final String title ) + @Override + public void print() { - this.title = title; + System.out.println( "Book:" ); + System.out.println( " isbn=" + isbn ); + System.out.println( " title=" + title ); + System.out.println( " author=" + author ); + System.out.println( " desc=" + desc ); } public void setAuthor( final String author ) @@ -52,14 +57,9 @@ public void setDesc( final String desc ) this.desc = desc; } - @Override - public void print() + public void setTitle( final String title ) { - System.out.println( "Book:" ); - System.out.println( " isbn=" + isbn ); - System.out.println( " title=" + title ); - System.out.println( " author=" + author ); - System.out.println( " desc=" + desc ); + this.title = title; } } diff --git a/commons-digester3-examples/api/catalog/src/main/java/org/apache/commons/digester3/examples/api/catalog/Main.java b/commons-digester3-examples/api/catalog/src/main/java/org/apache/commons/digester3/examples/api/catalog/Main.java index 5e942f790..8f0ef93eb 100644 --- a/commons-digester3-examples/api/catalog/src/main/java/org/apache/commons/digester3/examples/api/catalog/Main.java +++ b/commons-digester3-examples/api/catalog/src/main/java/org/apache/commons/digester3/examples/api/catalog/Main.java @@ -43,55 +43,6 @@ public class Main { - /** - * Main method : entry point for running this example program. - *

      - * Usage: java CatalogDigester example.xml - */ - public static void main( final String[] args ) - { - if ( args.length != 1 ) - { - usage(); - System.exit( -1 ); - } - - final String filename = args[0]; - - // Create a Digester instance - final Digester d = new Digester(); - - // Add rules to the digester that will be triggered while - // parsing occurs. - addRules( d ); - - // Process the input file. - try - { - final java.io.Reader reader = getInputData( filename ); - d.parse( reader ); - } - catch ( final java.io.IOException ioe ) - { - System.out.println( "Error reading input file:" + ioe.getMessage() ); - System.exit( -1 ); - } - catch ( final org.xml.sax.SAXException se ) - { - System.out.println( "Error parsing input file:" + se.getMessage() ); - System.exit( -1 ); - } - - // Get the first object created by the digester's rules - // (the "root" object). Note that this is exactly the same object - // returned by the Digester.parse method; either approach works. - final Catalog catalog = (Catalog) d.getRoot(); - - // Print out all the contents of the catalog, as loaded from - // the input file. - catalog.print(); - } - private static void addRules( final Digester d ) { @@ -226,6 +177,55 @@ private static java.io.Reader getInputData( final String filename ) } + /** + * Main method : entry point for running this example program. + *

      + * Usage: java CatalogDigester example.xml + */ + public static void main( final String[] args ) + { + if ( args.length != 1 ) + { + usage(); + System.exit( -1 ); + } + + final String filename = args[0]; + + // Create a Digester instance + final Digester d = new Digester(); + + // Add rules to the digester that will be triggered while + // parsing occurs. + addRules( d ); + + // Process the input file. + try + { + final java.io.Reader reader = getInputData( filename ); + d.parse( reader ); + } + catch ( final java.io.IOException ioe ) + { + System.out.println( "Error reading input file:" + ioe.getMessage() ); + System.exit( -1 ); + } + catch ( final org.xml.sax.SAXException se ) + { + System.out.println( "Error parsing input file:" + se.getMessage() ); + System.exit( -1 ); + } + + // Get the first object created by the digester's rules + // (the "root" object). Note that this is exactly the same object + // returned by the Digester.parse method; either approach works. + final Catalog catalog = (Catalog) d.getRoot(); + + // Print out all the contents of the catalog, as loaded from + // the input file. + catalog.print(); + } + private static void usage() { System.out.println( "Usage: java Main example.xml" ); diff --git a/commons-digester3-examples/api/dbinsert/src/main/java/org/apache/commons/digester3/examples/api/dbinsert/Main.java b/commons-digester3-examples/api/dbinsert/src/main/java/org/apache/commons/digester3/examples/api/dbinsert/Main.java index b584db53f..f34c8e62a 100644 --- a/commons-digester3-examples/api/dbinsert/src/main/java/org/apache/commons/digester3/examples/api/dbinsert/Main.java +++ b/commons-digester3-examples/api/dbinsert/src/main/java/org/apache/commons/digester3/examples/api/dbinsert/Main.java @@ -47,57 +47,6 @@ public class Main { - /** - * Main method : entry point for running this example program. - *

      - * Usage: java Main example.xml - */ - public static void main( final String[] args ) - { - if ( args.length != 1 ) - { - usage(); - System.exit( -1 ); - } - - final String filename = args[0]; - - // Create a Digester instance - final Digester d = new Digester(); - - // Here you would establish a real connection. - // There would also be a finally clause to ensure it is - // closed after parsing terminates, etc. - final Connection connection = null; - - // Add rules to the digester that will be triggered while - // parsing occurs. - addRules( d, connection ); - - // Process the input file. - System.out.println( "Parsing commencing..." ); - try - { - final File srcfile = new File( filename ); - d.parse( srcfile ); - } - catch ( final IOException ioe ) - { - System.out.println( "Error reading input file:" + ioe.getMessage() ); - System.exit( -1 ); - } - catch ( final SAXException se ) - { - System.out.println( "Error parsing input file:" + se.getMessage() ); - System.exit( -1 ); - } - - // And here there is nothing to do. The digester rules have - // (deliberately) not built a representation of the input, but - // instead processed the data as it was read. - System.out.println( "Parsing complete." ); - } - private static void addRules( final Digester d, final java.sql.Connection conn ) { @@ -153,6 +102,57 @@ private static void addRules( final Digester d, final java.sql.Connection conn ) d.addCallParam( "database/table/row/column", 1 ); } + /** + * Main method : entry point for running this example program. + *

      + * Usage: java Main example.xml + */ + public static void main( final String[] args ) + { + if ( args.length != 1 ) + { + usage(); + System.exit( -1 ); + } + + final String filename = args[0]; + + // Create a Digester instance + final Digester d = new Digester(); + + // Here you would establish a real connection. + // There would also be a finally clause to ensure it is + // closed after parsing terminates, etc. + final Connection connection = null; + + // Add rules to the digester that will be triggered while + // parsing occurs. + addRules( d, connection ); + + // Process the input file. + System.out.println( "Parsing commencing..." ); + try + { + final File srcfile = new File( filename ); + d.parse( srcfile ); + } + catch ( final IOException ioe ) + { + System.out.println( "Error reading input file:" + ioe.getMessage() ); + System.exit( -1 ); + } + catch ( final SAXException se ) + { + System.out.println( "Error parsing input file:" + se.getMessage() ); + System.exit( -1 ); + } + + // And here there is nothing to do. The digester rules have + // (deliberately) not built a representation of the input, but + // instead processed the data as it was read. + System.out.println( "Parsing complete." ); + } + private static void usage() { System.out.println( "Usage: java Main example.xml" ); diff --git a/commons-digester3-examples/api/dbinsert/src/main/java/org/apache/commons/digester3/examples/api/dbinsert/Table.java b/commons-digester3-examples/api/dbinsert/src/main/java/org/apache/commons/digester3/examples/api/dbinsert/Table.java index c33cdf924..a0637f448 100644 --- a/commons-digester3-examples/api/dbinsert/src/main/java/org/apache/commons/digester3/examples/api/dbinsert/Table.java +++ b/commons-digester3-examples/api/dbinsert/src/main/java/org/apache/commons/digester3/examples/api/dbinsert/Table.java @@ -29,14 +29,14 @@ public Table() { } - public void setName( final String name ) + public String getName() { - this.name = name; + return name; } - public String getName() + public void setName( final String name ) { - return name; + this.name = name; } } diff --git a/commons-digester3-examples/api/document-markup/src/main/java/org/apache/commons/digester3/examples/api/documentmarkup/Main.java b/commons-digester3-examples/api/document-markup/src/main/java/org/apache/commons/digester3/examples/api/documentmarkup/Main.java index f9580dbce..d0748a151 100644 --- a/commons-digester3-examples/api/document-markup/src/main/java/org/apache/commons/digester3/examples/api/documentmarkup/Main.java +++ b/commons-digester3-examples/api/document-markup/src/main/java/org/apache/commons/digester3/examples/api/documentmarkup/Main.java @@ -29,26 +29,15 @@ public class Main { - /** The input xml to be parsed by this example. */ - String in = "

      Hi, this is an example of some bold text.

      "; - - /** Invoked when a text segment is present in the parsed input. */ - public void addSegment( final String text ) - { - System.out.println( "Text segment: [" + text + "]" ); - } - - /** Invoked when an <i> node is found in the parsed input. */ - public void addItalic( final String text ) + /** See the run method. */ + public static void main( final String[] args ) + throws Exception { - System.out.println( "Italic: [" + text + "]" ); + new Main().run(); } - /** Invoked when an <b> node is found in the parsed input. */ - public void addBold( final String text ) - { - System.out.println( "Bold: [" + text + "]" ); - } + /** The input xml to be parsed by this example. */ + String in = "

      Hi, this is an example of some bold text.

      "; /** * Invoked via a standard Digester CallMethodRule, passing the @@ -61,6 +50,24 @@ public void addAllText( final String text ) System.out.println( "And the merged text for the p element is [" + text + "]" ); } + /** Invoked when an <b> node is found in the parsed input. */ + public void addBold( final String text ) + { + System.out.println( "Bold: [" + text + "]" ); + } + + /** Invoked when an <i> node is found in the parsed input. */ + public void addItalic( final String text ) + { + System.out.println( "Italic: [" + text + "]" ); + } + + /** Invoked when a text segment is present in the parsed input. */ + public void addSegment( final String text ) + { + System.out.println( "Text segment: [" + text + "]" ); + } + /** * Main method of this test harness. Set up some digester rules, * then parse the input xml contained in the "in" member variable. @@ -88,11 +95,4 @@ public void run() System.out.println( "Finished." ); } - /** See the run method. */ - public static void main( final String[] args ) - throws Exception - { - new Main().run(); - } - } diff --git a/commons-digester3-examples/api/document-markup/src/main/java/org/apache/commons/digester3/examples/api/documentmarkup/MarkupDigester.java b/commons-digester3-examples/api/document-markup/src/main/java/org/apache/commons/digester3/examples/api/documentmarkup/MarkupDigester.java index 6ae57304e..7b8c7fd10 100644 --- a/commons-digester3-examples/api/document-markup/src/main/java/org/apache/commons/digester3/examples/api/documentmarkup/MarkupDigester.java +++ b/commons-digester3-examples/api/document-markup/src/main/java/org/apache/commons/digester3/examples/api/documentmarkup/MarkupDigester.java @@ -38,6 +38,11 @@ public class MarkupDigester extends Digester { + /** + * The text found in the current element since the last child element. + */ + protected StringBuilder currTextSegment = new StringBuilder(); + /** See equivalent constructor in Digester class. */ public MarkupDigester() { @@ -49,19 +54,14 @@ public MarkupDigester( final SAXParser parser ) super( parser ); } + //=================================================================== + /** See equivalent constructor in Digester class. */ public MarkupDigester( final XMLReader reader ) { super( reader ); } - //=================================================================== - - /** - * The text found in the current element since the last child element. - */ - protected StringBuilder currTextSegment = new StringBuilder(); - /** * Process notification of character data received from the body of * an XML element. @@ -80,33 +80,6 @@ public void characters( final char buffer[], final int start, final int length ) currTextSegment.append( buffer, start, length ); } - /** - * Process notification of the start of an XML element being reached. - * - * @param namespaceURI The Namespace URI, or the empty string if the element - * has no Namespace URI or if Namespace processing is not being performed. - * @param localName The local name (without prefix), or the empty - * string if Namespace processing is not being performed. - * @param qName The qualified name (with prefix), or the empty - * string if qualified names are not available. - * @param list The attributes attached to the element. If there are - * no attributes, it shall be an empty Attributes object. - * @throws SAXException if a parsing error is to be reported - */ - @Override - public void startElement( final String namespaceURI, final String localName, final String qName, final Attributes list ) - throws SAXException - { - handleTextSegments(); - - // Unlike bodyText, which accumulates despite intervening child - // elements, currTextSegment gets cleared here. This means that - // we don't need to save it on a stack either. - currTextSegment.setLength( 0 ); - - super.startElement( namespaceURI, localName, qName, list ); - } - /** * Process notification of the end of an XML element being reached. * @@ -161,4 +134,31 @@ private void handleTextSegments() } } + /** + * Process notification of the start of an XML element being reached. + * + * @param namespaceURI The Namespace URI, or the empty string if the element + * has no Namespace URI or if Namespace processing is not being performed. + * @param localName The local name (without prefix), or the empty + * string if Namespace processing is not being performed. + * @param qName The qualified name (with prefix), or the empty + * string if qualified names are not available. + * @param list The attributes attached to the element. If there are + * no attributes, it shall be an empty Attributes object. + * @throws SAXException if a parsing error is to be reported + */ + @Override + public void startElement( final String namespaceURI, final String localName, final String qName, final Attributes list ) + throws SAXException + { + handleTextSegments(); + + // Unlike bodyText, which accumulates despite intervening child + // elements, currTextSegment gets cleared here. This means that + // we don't need to save it on a stack either. + currTextSegment.setLength( 0 ); + + super.startElement( namespaceURI, localName, qName, list ); + } + } diff --git a/commons-digester3-examples/api/document-markup/src/main/java/org/apache/commons/digester3/examples/api/documentmarkup/SetTextSegmentRule.java b/commons-digester3-examples/api/document-markup/src/main/java/org/apache/commons/digester3/examples/api/documentmarkup/SetTextSegmentRule.java index 1796566af..814734d54 100644 --- a/commons-digester3-examples/api/document-markup/src/main/java/org/apache/commons/digester3/examples/api/documentmarkup/SetTextSegmentRule.java +++ b/commons-digester3-examples/api/document-markup/src/main/java/org/apache/commons/digester3/examples/api/documentmarkup/SetTextSegmentRule.java @@ -31,18 +31,18 @@ public class SetTextSegmentRule // ----------------------------------------------------------- Constructors - public SetTextSegmentRule( final String methodName ) - { - this.methodName = methodName; - } - - // ----------------------------------------------------- Instance Variables - /** * The method name to call on the parent object. */ protected String methodName; + // ----------------------------------------------------- Instance Variables + + public SetTextSegmentRule( final String methodName ) + { + this.methodName = methodName; + } + // --------------------------------------------------------- Public Methods /** diff --git a/commons-digester3-examples/edsl/atom/src/main/java/org/apache/commons/digester3/edsl/atom/Entry.java b/commons-digester3-examples/edsl/atom/src/main/java/org/apache/commons/digester3/edsl/atom/Entry.java index 86ecf920e..ba68c22b1 100644 --- a/commons-digester3-examples/edsl/atom/src/main/java/org/apache/commons/digester3/edsl/atom/Entry.java +++ b/commons-digester3-examples/edsl/atom/src/main/java/org/apache/commons/digester3/edsl/atom/Entry.java @@ -35,14 +35,14 @@ public final class Entry private String content; - public String getTitle() + public String getContent() { - return title; + return content; } - public void setTitle( final String title ) + public String getId() { - this.title = title; + return id; } public URL getLink() @@ -50,9 +50,9 @@ public URL getLink() return link; } - public void setLink( final URL link ) + public String getTitle() { - this.link = link; + return title; } public Date getUpdated() @@ -60,29 +60,29 @@ public Date getUpdated() return updated; } - public void setUpdated( final Date updated ) + public void setContent( final String content ) { - this.updated = updated; + this.content = content; } - public String getId() + public void setId( final String id ) { - return id; + this.id = id; } - public void setId( final String id ) + public void setLink( final URL link ) { - this.id = id; + this.link = link; } - public String getContent() + public void setTitle( final String title ) { - return content; + this.title = title; } - public void setContent( final String content ) + public void setUpdated( final Date updated ) { - this.content = content; + this.updated = updated; } @Override diff --git a/commons-digester3-examples/edsl/atom/src/main/java/org/apache/commons/digester3/edsl/atom/Feed.java b/commons-digester3-examples/edsl/atom/src/main/java/org/apache/commons/digester3/edsl/atom/Feed.java index 37e8ee7c5..8df0ca75d 100644 --- a/commons-digester3-examples/edsl/atom/src/main/java/org/apache/commons/digester3/edsl/atom/Feed.java +++ b/commons-digester3-examples/edsl/atom/src/main/java/org/apache/commons/digester3/edsl/atom/Feed.java @@ -39,64 +39,64 @@ public final class Feed private final List entries = new ArrayList(); - public String getTitle() + public void addAuthor( final String author ) { - return title; + authors.add( author ); } - public void setTitle( final String title ) + public void addEntry( final Entry entry ) { - this.title = title; + entries.add( entry ); } - public URL getLink() + public List getAuthors() { - return link; + return authors; } - public void setLink( final URL link ) + public List getEntries() { - this.link = link; + return entries; } - public Date getUpdated() + public String getId() { - return updated; + return id; } - public void setUpdated( final Date updated ) + public URL getLink() { - this.updated = updated; + return link; } - public String getId() + public String getTitle() { - return id; + return title; } - public void setId( final String id ) + public Date getUpdated() { - this.id = id; + return updated; } - public List getAuthors() + public void setId( final String id ) { - return authors; + this.id = id; } - public void addAuthor( final String author ) + public void setLink( final URL link ) { - authors.add( author ); + this.link = link; } - public List getEntries() + public void setTitle( final String title ) { - return entries; + this.title = title; } - public void addEntry( final Entry entry ) + public void setUpdated( final Date updated ) { - entries.add( entry ); + this.updated = updated; } @Override diff --git a/commons-digester3-examples/plugins/pipeline/src/main/java/org/apache/commons/digester3/examples/plugins/pipeline/CompoundTransform.java b/commons-digester3-examples/plugins/pipeline/src/main/java/org/apache/commons/digester3/examples/plugins/pipeline/CompoundTransform.java index 841475465..9a401d9c2 100644 --- a/commons-digester3-examples/plugins/pipeline/src/main/java/org/apache/commons/digester3/examples/plugins/pipeline/CompoundTransform.java +++ b/commons-digester3-examples/plugins/pipeline/src/main/java/org/apache/commons/digester3/examples/plugins/pipeline/CompoundTransform.java @@ -34,6 +34,13 @@ public class CompoundTransform implements Transform { + public static void addRules( final Digester d, final String patternPrefix ) + { + final PluginCreateRule pcr = new PluginCreateRule( Transform.class ); + d.addRule( patternPrefix + "/subtransform", pcr ); + d.addSetNext( patternPrefix + "/subtransform", "addTransform" ); + } + private final LinkedList transforms = new LinkedList(); public void addTransform( final Transform transform ) @@ -50,11 +57,4 @@ public String transform( String s ) return s; } - public static void addRules( final Digester d, final String patternPrefix ) - { - final PluginCreateRule pcr = new PluginCreateRule( Transform.class ); - d.addRule( patternPrefix + "/subtransform", pcr ); - d.addSetNext( patternPrefix + "/subtransform", "addTransform" ); - } - } diff --git a/commons-digester3-examples/plugins/pipeline/src/main/java/org/apache/commons/digester3/examples/plugins/pipeline/Pipeline.java b/commons-digester3-examples/plugins/pipeline/src/main/java/org/apache/commons/digester3/examples/plugins/pipeline/Pipeline.java index 2dabce977..d13b68689 100644 --- a/commons-digester3-examples/plugins/pipeline/src/main/java/org/apache/commons/digester3/examples/plugins/pipeline/Pipeline.java +++ b/commons-digester3-examples/plugins/pipeline/src/main/java/org/apache/commons/digester3/examples/plugins/pipeline/Pipeline.java @@ -43,12 +43,6 @@ public class Pipeline { - private String source; - - private String dest; - - private Transform transformer; - public static void main( final String[] args ) { if ( args.length != 1 ) @@ -98,20 +92,11 @@ public static void main( final String[] args ) } } - public void setSource( final String source ) - { - this.source = source; - } + private String source; - public void setDest( final String dest ) - { - this.dest = dest; - } + private String dest; - public void setTransform( final Transform transformer ) - { - this.transformer = transformer; - } + private Transform transformer; private void execute() throws IOException @@ -140,4 +125,19 @@ private void execute() + "." ); } + public void setDest( final String dest ) + { + this.dest = dest; + } + + public void setSource( final String source ) + { + this.source = source; + } + + public void setTransform( final Transform transformer ) + { + this.transformer = transformer; + } + } diff --git a/commons-digester3-examples/plugins/pipeline/src/main/java/org/apache/commons/digester3/examples/plugins/pipeline/SubstituteTransform.java b/commons-digester3-examples/plugins/pipeline/src/main/java/org/apache/commons/digester3/examples/plugins/pipeline/SubstituteTransform.java index db975b28b..3fe681fad 100644 --- a/commons-digester3-examples/plugins/pipeline/src/main/java/org/apache/commons/digester3/examples/plugins/pipeline/SubstituteTransform.java +++ b/commons-digester3-examples/plugins/pipeline/src/main/java/org/apache/commons/digester3/examples/plugins/pipeline/SubstituteTransform.java @@ -33,6 +33,12 @@ public class SubstituteTransform implements Transform { + public static void addRules( final Digester d, final String patternPrefix ) + { + d.addCallMethod( patternPrefix + "/from", "setFrom", 0 ); + d.addCallMethod( patternPrefix + "/to", "setTo", 0 ); + } + private String from; private String to; @@ -64,10 +70,4 @@ public String transform( final String s ) return buf.toString(); } - public static void addRules( final Digester d, final String patternPrefix ) - { - d.addCallMethod( patternPrefix + "/from", "setFrom", 0 ); - d.addCallMethod( patternPrefix + "/to", "setTo", 0 ); - } - } diff --git a/commons-digester3-examples/rss/src/main/java/org/apache/commons/digester3/rss/Channel.java b/commons-digester3-examples/rss/src/main/java/org/apache/commons/digester3/rss/Channel.java index 38144e5f7..05bea2da5 100644 --- a/commons-digester3-examples/rss/src/main/java/org/apache/commons/digester3/rss/Channel.java +++ b/commons-digester3-examples/rss/src/main/java/org/apache/commons/digester3/rss/Channel.java @@ -63,213 +63,71 @@ public class Channel */ protected String copyright; - public String getCopyright() - { - return ( this.copyright ); - } - - public void setCopyright( final String copyright ) - { - this.copyright = copyright; - } - /** * The channel description (1-500 characters). */ protected String description; - public String getDescription() - { - return ( this.description ); - } - - public void setDescription( final String description ) - { - this.description = description; - } - /** * The channel description file URL (1-500 characters). */ protected String docs; - public String getDocs() - { - return ( this.docs ); - } - - public void setDocs( final String docs ) - { - this.docs = docs; - } - /** * The image describing this channel. */ protected Image image; - public Image getImage() - { - return ( this.image ); - } - - public void setImage( final Image image ) - { - this.image = image; - } - /** * The channel language (2-5 characters). */ protected String language; - public String getLanguage() - { - return ( this.language ); - } - - public void setLanguage( final String language ) - { - this.language = language; - } - /** * The channel last build date (1-100 characters). */ protected String lastBuildDate; - public String getLastBuildDate() - { - return ( this.lastBuildDate ); - } - - public void setLastBuildDate( final String lastBuildDate ) - { - this.lastBuildDate = lastBuildDate; - } - /** * The channel link (1-500 characters). */ protected String link; - public String getLink() - { - return ( this.link ); - } - - public void setLink( final String link ) - { - this.link = link; - } - /** * The managing editor (1-100 characters). */ protected String managingEditor; - public String getManagingEditor() - { - return ( this.managingEditor ); - } - - public void setManagingEditor( final String managingEditor ) - { - this.managingEditor = managingEditor; - } - /** * The channel publication date (1-100 characters). */ protected String pubDate; - public String getPubDate() - { - return ( this.pubDate ); - } - - public void setPubDate( final String pubDate ) - { - this.pubDate = pubDate; - } - /** * The channel rating (20-500 characters). */ protected String rating; - public String getRating() - { - return ( this.rating ); - } - - public void setRating( final String rating ) - { - this.rating = rating; - } - /** * The text input description for this channel. */ protected TextInput textInput; - public TextInput getTextInput() - { - return ( this.textInput ); - } - - public void setTextInput( final TextInput textInput ) - { - this.textInput = textInput; - } - /** * The channel title (1-100 characters). */ protected String title; - public String getTitle() - { - return ( this.title ); - } - - public void setTitle( final String title ) - { - this.title = title; - } - /** * The RSS specification version number used to create this Channel. */ protected double version = 0.91; - public double getVersion() - { - return ( this.version ); - } - - public void setVersion( final double version ) - { - this.version = version; - } - /** * The webmaster email address (1-100 characters). */ protected String webMaster; - public String getWebMaster() - { - return ( this.webMaster ); - } - - public void setWebMaster( final String webMaster ) - { - this.webMaster = webMaster; - } - - // --------------------------------------------------------- Public Methods - /** * Add an additional item. * @@ -321,14 +179,6 @@ public Item[] findItems() } } - /** - * Return the items for this channel. - */ - public Item[] getItems() - { - return findItems(); - } - /** * Return the skip days for this channel. */ @@ -341,14 +191,6 @@ public String[] findSkipDays() } } - /** - * Return the skip hours for this channel. - */ - public String[] getSkipHours() - { - return findSkipHours(); - } - /** * Return the skip hours for this channel. */ @@ -361,6 +203,64 @@ public String[] findSkipHours() } } + public String getCopyright() + { + return ( this.copyright ); + } + + public String getDescription() + { + return ( this.description ); + } + + public String getDocs() + { + return ( this.docs ); + } + + public Image getImage() + { + return ( this.image ); + } + + /** + * Return the items for this channel. + */ + public Item[] getItems() + { + return findItems(); + } + + public String getLanguage() + { + return ( this.language ); + } + + public String getLastBuildDate() + { + return ( this.lastBuildDate ); + } + + public String getLink() + { + return ( this.link ); + } + + public String getManagingEditor() + { + return ( this.managingEditor ); + } + + public String getPubDate() + { + return ( this.pubDate ); + } + + public String getRating() + { + return ( this.rating ); + } + /** * Return the skip days for this channel. */ @@ -369,6 +269,34 @@ public String[] getSkipDays() return findSkipDays(); } + /** + * Return the skip hours for this channel. + */ + public String[] getSkipHours() + { + return findSkipHours(); + } + + public TextInput getTextInput() + { + return ( this.textInput ); + } + + public String getTitle() + { + return ( this.title ); + } + + public double getVersion() + { + return ( this.version ); + } + + public String getWebMaster() + { + return ( this.webMaster ); + } + /** * Remove an item for this channel. * @@ -454,31 +382,7 @@ public void render( final OutputStream stream, final String encoding ) pw.flush(); } - /** - * Render this channel as XML conforming to the RSS 0.91 specification, - * to the specified writer, with no indication of character encoding. - * - * @param writer The writer to render output to - */ - public void render( final Writer writer ) - { - render( writer, null ); - } - - /** - * Render this channel as XML conforming to the RSS 0.91 specification, - * to the specified writer, indicating the specified character encoding. - * - * @param writer The writer to render output to - * @param encoding The character encoding to declare, or {@code null} - * for no declaration - */ - public void render( final Writer writer, final String encoding ) - { - final PrintWriter pw = new PrintWriter( writer ); - render( pw, encoding ); - pw.flush(); - } + // --------------------------------------------------------- Public Methods /** * Render this channel as XML conforming to the RSS 0.91 specification, @@ -633,4 +537,100 @@ public void render( final PrintWriter writer, final String encoding ) writer.println( "" ); } + /** + * Render this channel as XML conforming to the RSS 0.91 specification, + * to the specified writer, with no indication of character encoding. + * + * @param writer The writer to render output to + */ + public void render( final Writer writer ) + { + render( writer, null ); + } + + /** + * Render this channel as XML conforming to the RSS 0.91 specification, + * to the specified writer, indicating the specified character encoding. + * + * @param writer The writer to render output to + * @param encoding The character encoding to declare, or {@code null} + * for no declaration + */ + public void render( final Writer writer, final String encoding ) + { + final PrintWriter pw = new PrintWriter( writer ); + render( pw, encoding ); + pw.flush(); + } + + public void setCopyright( final String copyright ) + { + this.copyright = copyright; + } + + public void setDescription( final String description ) + { + this.description = description; + } + + public void setDocs( final String docs ) + { + this.docs = docs; + } + + public void setImage( final Image image ) + { + this.image = image; + } + + public void setLanguage( final String language ) + { + this.language = language; + } + + public void setLastBuildDate( final String lastBuildDate ) + { + this.lastBuildDate = lastBuildDate; + } + + public void setLink( final String link ) + { + this.link = link; + } + + public void setManagingEditor( final String managingEditor ) + { + this.managingEditor = managingEditor; + } + + public void setPubDate( final String pubDate ) + { + this.pubDate = pubDate; + } + + public void setRating( final String rating ) + { + this.rating = rating; + } + + public void setTextInput( final TextInput textInput ) + { + this.textInput = textInput; + } + + public void setTitle( final String title ) + { + this.title = title; + } + + public void setVersion( final double version ) + { + this.version = version; + } + + public void setWebMaster( final String webMaster ) + { + this.webMaster = webMaster; + } + } diff --git a/commons-digester3-examples/rss/src/main/java/org/apache/commons/digester3/rss/Image.java b/commons-digester3-examples/rss/src/main/java/org/apache/commons/digester3/rss/Image.java index 305ebde28..f4234e680 100644 --- a/commons-digester3-examples/rss/src/main/java/org/apache/commons/digester3/rss/Image.java +++ b/commons-digester3-examples/rss/src/main/java/org/apache/commons/digester3/rss/Image.java @@ -41,92 +41,60 @@ public class Image */ protected String description; - public String getDescription() - { - return ( this.description ); - } - - public void setDescription( final String description ) - { - this.description = description; - } - /** * The image height in pixels (1-400). */ protected int height = 31; - public int getHeight() - { - return ( this.height ); - } - - public void setHeight( final int height ) - { - this.height = height; - } - /** * The image link (1-500 characters). */ protected String link; - public String getLink() - { - return ( this.link ); - } - - public void setLink( final String link ) - { - this.link = link; - } - /** * The image alternate text (1-100 characters). */ protected String title; - public String getTitle() - { - return ( this.title ); - } - - public void setTitle( final String title ) - { - this.title = title; - } - /** * The image location URL (1-500 characters). */ protected String url; - public String getURL() + /** + * The image width in pixels (1-400). + */ + protected int width = 31; + + public String getDescription() { - return ( this.url ); + return ( this.description ); } - public void setURL( final String url ) + public int getHeight() { - this.url = url; + return ( this.height ); } - /** - * The image width in pixels (1-400). - */ - protected int width = 31; + public String getLink() + { + return ( this.link ); + } - public int getWidth() + public String getTitle() { - return ( this.width ); + return ( this.title ); } - public void setWidth( final int width ) + public String getURL() { - this.width = width; + return ( this.url ); } - // -------------------------------------------------------- Package Methods + public int getWidth() + { + return ( this.width ); + } /** * Render this channel as XML conforming to the RSS 0.91 specification, @@ -171,4 +139,36 @@ void render( final PrintWriter writer ) writer.println( " " ); } + public void setDescription( final String description ) + { + this.description = description; + } + + public void setHeight( final int height ) + { + this.height = height; + } + + public void setLink( final String link ) + { + this.link = link; + } + + public void setTitle( final String title ) + { + this.title = title; + } + + public void setURL( final String url ) + { + this.url = url; + } + + // -------------------------------------------------------- Package Methods + + public void setWidth( final int width ) + { + this.width = width; + } + } diff --git a/commons-digester3-examples/rss/src/main/java/org/apache/commons/digester3/rss/Item.java b/commons-digester3-examples/rss/src/main/java/org/apache/commons/digester3/rss/Item.java index 47dfc296f..5877a2298 100644 --- a/commons-digester3-examples/rss/src/main/java/org/apache/commons/digester3/rss/Item.java +++ b/commons-digester3-examples/rss/src/main/java/org/apache/commons/digester3/rss/Item.java @@ -39,47 +39,30 @@ public class Item implements Serializable { */ protected String description; - public String getDescription() - { - return ( this.description ); - } - - public void setDescription( final String description ) - { - this.description = description; - } - /** * The item link (1-500 characters). */ protected String link; - public String getLink() - { - return ( this.link ); - } - - public void setLink( final String link ) - { - this.link = link; - } - /** * The item title (1-100 characters). */ protected String title; - public String getTitle() + public String getDescription() { - return ( this.title ); + return ( this.description ); } - public void setTitle( final String title ) + public String getLink() { - this.title = title; + return ( this.link ); } - // -------------------------------------------------------- Package Methods + public String getTitle() + { + return ( this.title ); + } /** * Render this channel as XML conforming to the RSS 0.91 specification, @@ -109,4 +92,21 @@ void render( final PrintWriter writer ) writer.println( " " ); } + public void setDescription( final String description ) + { + this.description = description; + } + + public void setLink( final String link ) + { + this.link = link; + } + + // -------------------------------------------------------- Package Methods + + public void setTitle( final String title ) + { + this.title = title; + } + } diff --git a/commons-digester3-examples/rss/src/main/java/org/apache/commons/digester3/rss/RSSDigester.java b/commons-digester3-examples/rss/src/main/java/org/apache/commons/digester3/rss/RSSDigester.java index a3db9ff31..c0527e6ed 100644 --- a/commons-digester3-examples/rss/src/main/java/org/apache/commons/digester3/rss/RSSDigester.java +++ b/commons-digester3-examples/rss/src/main/java/org/apache/commons/digester3/rss/RSSDigester.java @@ -50,77 +50,147 @@ public class RSSDigester // ----------------------------------------------------- Instance Variables /** - * Have we been configured yet? + * Test main program that parses the channel description included in this + * package as a static resource. + * + * @param args The command line arguments (ignored) */ - protected boolean configured; + public static void main( final String args[] ) + { + try + { + System.out.println( "RSSDigester Test Program" ); + System.out.println( "Opening input stream ..." ); + final InputStream is = + RSSDigester.class.getResourceAsStream( "/org/apache/commons/digester3/rss/rss-example.xml" ); + System.out.println( "Creating new digester ..." ); + final RSSDigester digester = new RSSDigester(); + if ( ( args.length > 0 ) && ( args[0].equals( "-debug" ) ) ) + { + digester.setLogger( LogFactory.getLog( "RSSDigester" ) ); + } + System.out.println( "Parsing input stream ..." ); + final Channel channel = (Channel) digester.parse( is ); + System.out.println( "Closing input stream ..." ); + is.close(); + System.out.println( "Dumping channel info ..." ); + channel.render( System.out ); + } + catch ( final Exception e ) + { + System.out.println( "-->Exception" ); + e.printStackTrace( System.out ); + } + } // ------------------------------------------------------------- Properties + /** + * Have we been configured yet? + */ + protected boolean configured; + /** * The fully qualified class name of the {@code Channel} * implementation class. */ protected String channelClass = "org.apache.commons.digester3.rss.Channel"; - public String getChannelClass() - { - return ( this.channelClass ); - } - - public void setChannelClass( final String channelClass ) - { - this.channelClass = channelClass; - } - /** * The fully qualified class name of the {@code Image} * implementation class. */ protected String imageClass = "org.apache.commons.digester3.rss.Image"; - public String getImageClass() - { - return ( this.imageClass ); - } - - public void setImageClass( final String imageClass ) - { - this.imageClass = imageClass; - } - /** * The fully qualified class name of the {@code Item} * implementation class. */ protected String itemClass = "org.apache.commons.digester3.rss.Item"; - public String getItemClass() - { - return ( this.itemClass ); - } - - public void setItemClass( final String itemClass ) - { - this.itemClass = itemClass; - } - /** * The fully qualified class name of the {@code TextInput} * implementation class. */ protected String textInputClass = "org.apache.commons.digester3.rss.TextInput"; - public String getTextInputClass() + /** + * Configure the parsing rules that will be used to process RSS input. + */ + @Override + protected void configure() { - return ( this.textInputClass ); + if ( configured ) + { + return; + } + + // FIXME - validate the "version" attribute of the rss element? + + // Add the rules for the Channel object + addObjectCreate( "rss/channel", channelClass ); + addCallMethod( "rss/channel/copyright", "setCopyright", 0 ); + addCallMethod( "rss/channel/description", "setDescription", 0 ); + addCallMethod( "rss/channel/docs", "setDocs", 0 ); + addCallMethod( "rss/channel/language", "setLanguage", 0 ); + addCallMethod( "rss/channel/lastBuildDate", "setLastBuildDate", 0 ); + addCallMethod( "rss/channel/link", "setLink", 0 ); + addCallMethod( "rss/channel/managingEditor", "setManagingEditor", 0 ); + addCallMethod( "rss/channel/pubDate", "setPubDate", 0 ); + addCallMethod( "rss/channel/rating", "setRating", 0 ); + addCallMethod( "rss/channel/skipDays/day", "addSkipDay", 0 ); + addCallMethod( "rss/channel/skipHours/hour", "addSkipHour", 0 ); + addCallMethod( "rss/channel/title", "setTitle", 0 ); + addCallMethod( "rss/channel/webMaster", "setWebMaster", 0 ); + + // Add the rules for the Image object + addObjectCreate( "rss/channel/image", imageClass ); + addSetNext( "rss/channel/image", "setImage", "org.apache.commons.digester3.rss.Image" ); + addCallMethod( "rss/channel/image/description", "setDescription", 0 ); + addCallMethod( "rss/channel/image/height", "setHeight", 0, new Class[] { Integer.TYPE } ); + addCallMethod( "rss/channel/image/link", "setLink", 0 ); + addCallMethod( "rss/channel/image/title", "setTitle", 0 ); + addCallMethod( "rss/channel/image/url", "setURL", 0 ); + addCallMethod( "rss/channel/image/width", "setWidth", 0, new Class[] { Integer.TYPE } ); + + // Add the rules for the Item object + addObjectCreate( "rss/channel/item", itemClass ); + addSetNext( "rss/channel/item", "addItem", "org.apache.commons.digester3.rss.Item" ); + addCallMethod( "rss/channel/item/description", "setDescription", 0 ); + addCallMethod( "rss/channel/item/link", "setLink", 0 ); + addCallMethod( "rss/channel/item/title", "setTitle", 0 ); + + // Add the rules for the TextInput object + addObjectCreate( "rss/channel/textinput", textInputClass ); + addSetNext( "rss/channel/textinput", "setTextInput", "org.apache.commons.digester3.rss.TextInput" ); + addCallMethod( "rss/channel/textinput/description", "setDescription", 0 ); + addCallMethod( "rss/channel/textinput/link", "setLink", 0 ); + addCallMethod( "rss/channel/textinput/name", "setName", 0 ); + addCallMethod( "rss/channel/textinput/title", "setTitle", 0 ); + + // Mark this digester as having been configured + configured = true; } - public void setTextInputClass( final String textInputClass ) + public String getChannelClass() { - this.textInputClass = textInputClass; + return ( this.channelClass ); } - // --------------------------------------------------------- Public Methods + public String getImageClass() + { + return ( this.imageClass ); + } + + public String getItemClass() + { + return ( this.itemClass ); + } + + public String getTextInputClass() + { + return ( this.textInputClass ); + } /** * Parse the content of the specified file using this Digester. Returns @@ -157,6 +227,7 @@ public T parse( final InputSource input ) return ( super.parse( input ) ); } + // --------------------------------------------------------- Public Methods /** * Parse the content of the specified input stream using this Digester. @@ -194,102 +265,31 @@ public T parse( final String uri ) return ( super.parse( uri ) ); } - // -------------------------------------------------------- Package Methods - // ------------------------------------------------------ Protected Methods - - /** - * Configure the parsing rules that will be used to process RSS input. - */ - @Override - protected void configure() + public void setChannelClass( final String channelClass ) { - if ( configured ) - { - return; - } - - // FIXME - validate the "version" attribute of the rss element? - - // Add the rules for the Channel object - addObjectCreate( "rss/channel", channelClass ); - addCallMethod( "rss/channel/copyright", "setCopyright", 0 ); - addCallMethod( "rss/channel/description", "setDescription", 0 ); - addCallMethod( "rss/channel/docs", "setDocs", 0 ); - addCallMethod( "rss/channel/language", "setLanguage", 0 ); - addCallMethod( "rss/channel/lastBuildDate", "setLastBuildDate", 0 ); - addCallMethod( "rss/channel/link", "setLink", 0 ); - addCallMethod( "rss/channel/managingEditor", "setManagingEditor", 0 ); - addCallMethod( "rss/channel/pubDate", "setPubDate", 0 ); - addCallMethod( "rss/channel/rating", "setRating", 0 ); - addCallMethod( "rss/channel/skipDays/day", "addSkipDay", 0 ); - addCallMethod( "rss/channel/skipHours/hour", "addSkipHour", 0 ); - addCallMethod( "rss/channel/title", "setTitle", 0 ); - addCallMethod( "rss/channel/webMaster", "setWebMaster", 0 ); + this.channelClass = channelClass; + } - // Add the rules for the Image object - addObjectCreate( "rss/channel/image", imageClass ); - addSetNext( "rss/channel/image", "setImage", "org.apache.commons.digester3.rss.Image" ); - addCallMethod( "rss/channel/image/description", "setDescription", 0 ); - addCallMethod( "rss/channel/image/height", "setHeight", 0, new Class[] { Integer.TYPE } ); - addCallMethod( "rss/channel/image/link", "setLink", 0 ); - addCallMethod( "rss/channel/image/title", "setTitle", 0 ); - addCallMethod( "rss/channel/image/url", "setURL", 0 ); - addCallMethod( "rss/channel/image/width", "setWidth", 0, new Class[] { Integer.TYPE } ); + public void setImageClass( final String imageClass ) + { + this.imageClass = imageClass; + } - // Add the rules for the Item object - addObjectCreate( "rss/channel/item", itemClass ); - addSetNext( "rss/channel/item", "addItem", "org.apache.commons.digester3.rss.Item" ); - addCallMethod( "rss/channel/item/description", "setDescription", 0 ); - addCallMethod( "rss/channel/item/link", "setLink", 0 ); - addCallMethod( "rss/channel/item/title", "setTitle", 0 ); + // -------------------------------------------------------- Package Methods - // Add the rules for the TextInput object - addObjectCreate( "rss/channel/textinput", textInputClass ); - addSetNext( "rss/channel/textinput", "setTextInput", "org.apache.commons.digester3.rss.TextInput" ); - addCallMethod( "rss/channel/textinput/description", "setDescription", 0 ); - addCallMethod( "rss/channel/textinput/link", "setLink", 0 ); - addCallMethod( "rss/channel/textinput/name", "setName", 0 ); - addCallMethod( "rss/channel/textinput/title", "setTitle", 0 ); + // ------------------------------------------------------ Protected Methods - // Mark this digester as having been configured - configured = true; + public void setItemClass( final String itemClass ) + { + this.itemClass = itemClass; } // ------------------------------------------------------ Test Main Program - /** - * Test main program that parses the channel description included in this - * package as a static resource. - * - * @param args The command line arguments (ignored) - */ - public static void main( final String args[] ) + public void setTextInputClass( final String textInputClass ) { - try - { - System.out.println( "RSSDigester Test Program" ); - System.out.println( "Opening input stream ..." ); - final InputStream is = - RSSDigester.class.getResourceAsStream( "/org/apache/commons/digester3/rss/rss-example.xml" ); - System.out.println( "Creating new digester ..." ); - final RSSDigester digester = new RSSDigester(); - if ( ( args.length > 0 ) && ( args[0].equals( "-debug" ) ) ) - { - digester.setLogger( LogFactory.getLog( "RSSDigester" ) ); - } - System.out.println( "Parsing input stream ..." ); - final Channel channel = (Channel) digester.parse( is ); - System.out.println( "Closing input stream ..." ); - is.close(); - System.out.println( "Dumping channel info ..." ); - channel.render( System.out ); - } - catch ( final Exception e ) - { - System.out.println( "-->Exception" ); - e.printStackTrace( System.out ); - } + this.textInputClass = textInputClass; } } diff --git a/commons-digester3-examples/rss/src/main/java/org/apache/commons/digester3/rss/TextInput.java b/commons-digester3-examples/rss/src/main/java/org/apache/commons/digester3/rss/TextInput.java index 144d350bc..c4a905607 100644 --- a/commons-digester3-examples/rss/src/main/java/org/apache/commons/digester3/rss/TextInput.java +++ b/commons-digester3-examples/rss/src/main/java/org/apache/commons/digester3/rss/TextInput.java @@ -41,62 +41,40 @@ public class TextInput implements Serializable { */ protected String description; - public String getDescription() - { - return ( this.description ); - } - - public void setDescription( final String description ) - { - this.description = description; - } - /** * The text input link (1-500 characters). */ protected String link; - public String getLink() - { - return ( this.link ); - } - - public void setLink( final String link ) - { - this.link = link; - } - /** * The text input field name (1-100 characters). */ protected String name; - public String getName() - { - return ( this.name ); - } - - public void setName( final String name ) - { - this.name = name; - } - /** * The text input submit button label (1-100 characters). */ protected String title; - public String getTitle() + public String getDescription() { - return ( this.title ); + return ( this.description ); } - public void setTitle( final String title ) + public String getLink() { - this.title = title; + return ( this.link ); } - // -------------------------------------------------------- Package Methods + public String getName() + { + return ( this.name ); + } + + public String getTitle() + { + return ( this.title ); + } /** * Render this channel as XML conforming to the RSS 0.91 specification, @@ -127,4 +105,26 @@ void render( final PrintWriter writer ) writer.println( " " ); } + public void setDescription( final String description ) + { + this.description = description; + } + + public void setLink( final String link ) + { + this.link = link; + } + + public void setName( final String name ) + { + this.name = name; + } + + // -------------------------------------------------------- Package Methods + + public void setTitle( final String title ) + { + this.title = title; + } + } diff --git a/commons-digester3-examples/xmlrules/addressbook/src/main/java/org/apache/commons/digester3/examples/xmlrules/addressbook/Address.java b/commons-digester3-examples/xmlrules/addressbook/src/main/java/org/apache/commons/digester3/examples/xmlrules/addressbook/Address.java index c6592932d..71a141dd7 100644 --- a/commons-digester3-examples/xmlrules/addressbook/src/main/java/org/apache/commons/digester3/examples/xmlrules/addressbook/Address.java +++ b/commons-digester3-examples/xmlrules/addressbook/src/main/java/org/apache/commons/digester3/examples/xmlrules/addressbook/Address.java @@ -37,37 +37,28 @@ public class Address private String country; - @Override - public String toString() + /** + * Returns the value of city. + */ + public String getCity() { - final StringBuilder sb = new StringBuilder(); - sb.append( " address (type " + type + ")\n" ); - sb.append( " " + street + "\n" ); - sb.append( " " + city + " " + state + " " + zip + "\n" ); - sb.append( " " + country + "\n" ); - return sb.toString(); + return city; } - public void print( final PrintStream out, int indentAmount ) + /** + * Returns the value of country. + */ + public String getCountry() { - final StringBuilder indentStr = new StringBuilder( indentAmount ); - for ( ; indentAmount > 0; --indentAmount ) - { - indentStr.append( ' ' ); - } - - out.print( indentStr ); - out.print( "address type: " ); - out.println( type ); - - out.print( indentStr ); - out.println( " " + street ); - - out.print( indentStr ); - out.println( " " + city + " " + state + " " + zip ); + return country; + } - out.print( indentStr ); - out.println( " " + country ); + /** + * Returns the value of state. + */ + public String getState() + { + return state; } /** @@ -79,21 +70,41 @@ public String getStreet() } /** - * Sets the value of street. - * - * @param street The value to assign to street. + * Returns the value of type. */ - public void setStreet( final String street ) + public String getType() { - this.street = street; + return type; } /** - * Returns the value of city. + * Returns the value of zip. */ - public String getCity() + public String getZip() { - return city; + return zip; + } + + public void print( final PrintStream out, int indentAmount ) + { + final StringBuilder indentStr = new StringBuilder( indentAmount ); + for ( ; indentAmount > 0; --indentAmount ) + { + indentStr.append( ' ' ); + } + + out.print( indentStr ); + out.print( "address type: " ); + out.println( type ); + + out.print( indentStr ); + out.println( " " + street ); + + out.print( indentStr ); + out.println( " " + city + " " + state + " " + zip ); + + out.print( indentStr ); + out.println( " " + country ); } /** @@ -107,11 +118,13 @@ public void setCity( final String city ) } /** - * Returns the value of state. + * Sets the value of country. + * + * @param country The value to assign to country. */ - public String getState() + public void setCountry( final String country ) { - return state; + this.country = country; } /** @@ -125,57 +138,44 @@ public void setState( final String state ) } /** - * Returns the value of zip. - */ - public String getZip() - { - return zip; - } - - /** - * Sets the value of zip. + * Sets the value of street. * - * @param zip The value to assign to zip. - */ - public void setZip( final String zip ) - { - this.zip = zip; - } - - /** - * Returns the value of country. + * @param street The value to assign to street. */ - public String getCountry() + public void setStreet( final String street ) { - return country; + this.street = street; } /** - * Sets the value of country. + * Sets the value of type. * - * @param country The value to assign to country. + * @param type The value to assign to type. */ - public void setCountry( final String country ) + public void setType( final String type ) { - this.country = country; + this.type = type; } /** - * Returns the value of type. + * Sets the value of zip. + * + * @param zip The value to assign to zip. */ - public String getType() + public void setZip( final String zip ) { - return type; + this.zip = zip; } - /** - * Sets the value of type. - * - * @param type The value to assign to type. - */ - public void setType( final String type ) + @Override + public String toString() { - this.type = type; + final StringBuilder sb = new StringBuilder(); + sb.append( " address (type " + type + ")\n" ); + sb.append( " " + street + "\n" ); + sb.append( " " + city + " " + state + " " + zip + "\n" ); + sb.append( " " + country + "\n" ); + return sb.toString(); } } diff --git a/commons-digester3-examples/xmlrules/addressbook/src/main/java/org/apache/commons/digester3/examples/xmlrules/addressbook/Person.java b/commons-digester3-examples/xmlrules/addressbook/src/main/java/org/apache/commons/digester3/examples/xmlrules/addressbook/Person.java index 4d6bd6d8d..10089d9ed 100644 --- a/commons-digester3-examples/xmlrules/addressbook/src/main/java/org/apache/commons/digester3/examples/xmlrules/addressbook/Person.java +++ b/commons-digester3-examples/xmlrules/addressbook/src/main/java/org/apache/commons/digester3/examples/xmlrules/addressbook/Person.java @@ -37,22 +37,9 @@ public class Person private final List
      addresses = new ArrayList
      (); - /** - * A unique id for this person. Note that the Digester automatically converts the id to an integer. - */ - public void setId( final int id ) - { - this.id = id; - } - - public void setCategory( final String category ) - { - this.category = category; - } - - public void setName( final String name ) + public void addAddress( final Address addr ) { - this.name = name; + addresses.add( addr ); } /** we assume only one email of each type... */ @@ -61,11 +48,6 @@ public void addEmail( final String type, final String address ) emails.put( type, address ); } - public void addAddress( final Address addr ) - { - addresses.add( addr ); - } - public void print() { System.out.println( "Person #" + id ); @@ -83,4 +65,22 @@ public void print() } } + public void setCategory( final String category ) + { + this.category = category; + } + + /** + * A unique id for this person. Note that the Digester automatically converts the id to an integer. + */ + public void setId( final int id ) + { + this.id = id; + } + + public void setName( final String name ) + { + this.name = name; + } + }