Normalize Javadoc spelling
diff --git a/core/src/main/java/org/apache/commons/digester3/plugins/InitializableRule.java b/core/src/main/java/org/apache/commons/digester3/plugins/InitializableRule.java
index fda1ddb..ad78afc 100644
--- a/core/src/main/java/org/apache/commons/digester3/plugins/InitializableRule.java
+++ b/core/src/main/java/org/apache/commons/digester3/plugins/InitializableRule.java
@@ -1,39 +1,39 @@
-package org.apache.commons.digester3.plugins;
-
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-/**
- * Defines an interface that a Rule class can implement if it wishes to get an initialisation callback after the rule
- * has been added to the set of Rules within a PluginRules instance.
- *
- * @since 1.6
- */
-public interface InitializableRule
-{
-
- /**
- * Called after this Rule object has been added to the list of all Rules. Note that if a single InitializableRule
- * instance is associated with more than one pattern, then this method will be called more than once.
- *
- * @param pattern is the digester match pattern that will trigger this rule.
- */
- void postRegisterInit( String pattern );
-
-}
+package org.apache.commons.digester3.plugins;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Defines an interface that a Rule class can implement if it wishes to get an initialization callback after the rule
+ * has been added to the set of Rules within a PluginRules instance.
+ *
+ * @since 1.6
+ */
+public interface InitializableRule
+{
+
+ /**
+ * Called after this Rule object has been added to the list of all Rules. Note that if a single InitializableRule
+ * instance is associated with more than one pattern, then this method will be called more than once.
+ *
+ * @param pattern is the digester match pattern that will trigger this rule.
+ */
+ void postRegisterInit( String pattern );
+
+}
diff --git a/core/src/main/java/org/apache/commons/digester3/plugins/PluginCreateRule.java b/core/src/main/java/org/apache/commons/digester3/plugins/PluginCreateRule.java
index 599943d..72bcc4c 100644
--- a/core/src/main/java/org/apache/commons/digester3/plugins/PluginCreateRule.java
+++ b/core/src/main/java/org/apache/commons/digester3/plugins/PluginCreateRule.java
@@ -1,633 +1,633 @@
-package org.apache.commons.digester3.plugins;
-
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import java.util.List;
-
-import org.apache.commons.digester3.Rule;
-import org.apache.commons.logging.Log;
-import org.xml.sax.Attributes;
-
-/**
- * Allows the original rules for parsing the configuration file to define points at which plugins are allowed, by
- * configuring a PluginCreateRule with the appropriate pattern.
- *
- * @since 1.6
- */
-public class PluginCreateRule
- extends Rule
- implements InitializableRule
-{
-
- // see setPluginClassAttribute
- private String pluginClassAttrNs = null;
-
- private String pluginClassAttr = null;
-
- // see setPluginIdAttribute
- private String pluginIdAttrNs = null;
-
- private String pluginIdAttr = null;
-
- /**
- * In order to invoke the addRules method on the plugin class correctly, we need to know the pattern which this rule
- * is matched by.
- */
- private String pattern;
-
- /** A base class that any plugin must derive from. */
- private Class<?> baseClass = null;
-
- /**
- * Info about optional default plugin to be used if no plugin-id is specified in the input data. This can simplify
- * the syntax where one particular plugin is usually used.
- */
- private Declaration defaultPlugin;
-
- /**
- * Currently, none of the Rules methods allow exceptions to be thrown. Therefore if this class cannot initialize
- * itself properly, it cannot cause the digester to stop. Instead, we cache the exception and throw it the first
- * time the begin() method is called.
- */
- private PluginConfigurationException initException;
-
- // -------------------- constructors -------------------------------------
-
- /**
- * Create a plugin rule where the user <i>must</i> specify a plugin-class or plugin-id.
- *
- * @param baseClass is the class which any specified plugin <i>must</i> be descended from.
- */
- public PluginCreateRule( final Class<?> baseClass )
- {
- this.baseClass = baseClass;
- }
-
- /**
- * Create a plugin rule where the user <i>may</i> specify a plugin. If the user doesn't specify a plugin, then the
- * default class specified in this constructor is used.
- *
- * @param baseClass is the class which any specified plugin <i>must</i> be descended from.
- * @param dfltPluginClass is the class which will be used if the user doesn't specify any plugin-class or plugin-id.
- * This class will have custom rules installed for it just like a declared plugin.
- */
- public PluginCreateRule( final Class<?> baseClass, final Class<?> dfltPluginClass )
- {
- this.baseClass = baseClass;
- if ( dfltPluginClass != null )
- {
- defaultPlugin = new Declaration( dfltPluginClass );
- }
- }
-
- /**
- * Create a plugin rule where the user <i>may</i> specify a plugin. If the user doesn't specify a plugin, then the
- * default class specified in this constructor is used.
- *
- * @param baseClass is the class which any specified plugin <i>must</i> be descended from.
- * @param dfltPluginClass is the class which will be used if the user doesn't specify any plugin-class or plugin-id.
- * This class will have custom rules installed for it just like a declared plugin.
- * @param dfltPluginRuleLoader is a RuleLoader instance which knows how to load the custom rules associated with
- * this default plugin.
- */
- public PluginCreateRule( final Class<?> baseClass, final Class<?> dfltPluginClass, final RuleLoader dfltPluginRuleLoader )
- {
- this.baseClass = baseClass;
- if ( dfltPluginClass != null )
- {
- defaultPlugin = new Declaration( dfltPluginClass, dfltPluginRuleLoader );
- }
- }
-
- // ------------------- properties ---------------------------------------
-
- /**
- * Sets the xml attribute which the input xml uses to indicate to a PluginCreateRule which class should be
- * instantiated.
- * <p>
- * 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 <i>not</i>
- * contain any kind of namespace-prefix. Note also that if you are using a non-namespace-aware parser,
- * this parameter <i>must</i> 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.
- * <p>
- * 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 <i>not</i>
- * contain any kind of namespace-prefix. Note also that if you are using a non-namespace-aware parser,
- * this parameter <i>must</i> 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.
- * <p>
- * A new instance of the target class is created, and pushed onto the stack. A new "private" PluginRules object is
- * then created and set as the digester's default Rules object. Any custom rules associated with the plugin class
- * are then loaded into that new Rules object. Finally, any custom rules that are associated with the current
- * pattern (such as SetPropertiesRules) have their begin methods executed.
- *
- * @param namespace the namespace URI of the matching element, or an empty string if the parser is not namespace
- * aware or the element has no namespace
- * @param name the local name if the parser is namespace aware, or just the element name otherwise
- * @param attributes The attribute list of this element
- * @throws Exception if any error occurs
- */
- @Override
- public void begin( final String namespace, final String name, final org.xml.sax.Attributes attributes )
- throws Exception
- {
- final Log log = getDigester().getLogger();
- final boolean debug = log.isDebugEnabled();
- if ( debug )
- {
- log.debug( "PluginCreateRule.begin" + ": pattern=[" + pattern + "]" + " match=[" + getDigester().getMatch()
- + "]" );
- }
-
- if ( initException != null )
- {
- // we had a problem during initialisation that we could
- // not report then; report it now.
- throw initException;
- }
-
- // load any custom rules associated with the plugin
- final PluginRules oldRules = (PluginRules) getDigester().getRules();
- final PluginManager pluginManager = oldRules.getPluginManager();
- Declaration currDeclaration = null;
-
- String pluginClassName;
- if ( pluginClassAttrNs == null )
- {
- // Yep, this is ugly.
- //
- // In a namespace-aware parser, the one-param version will
- // return attributes with no namespace.
- //
- // In a non-namespace-aware parser, the two-param version will
- // never return any attributes, ever.
- pluginClassName = attributes.getValue( pluginClassAttr );
- }
- else
- {
- pluginClassName = attributes.getValue( pluginClassAttrNs, pluginClassAttr );
- }
-
- String pluginId;
- if ( pluginIdAttrNs == null )
- {
- pluginId = attributes.getValue( pluginIdAttr );
- }
- else
- {
- pluginId = attributes.getValue( pluginIdAttrNs, pluginIdAttr );
- }
-
- if ( pluginClassName != null )
- {
- // The user is using a plugin "inline", ie without a previous
- // explicit declaration. If they have used the same plugin class
- // before, we have already gone to the effort of creating a
- // Declaration object, so retrieve it. If there is no existing
- // declaration object for this class, then create one.
-
- currDeclaration = pluginManager.getDeclarationByClass( pluginClassName );
-
- if ( currDeclaration == null )
- {
- currDeclaration = new Declaration( pluginClassName );
- try
- {
- currDeclaration.init( getDigester(), pluginManager );
- }
- catch ( final PluginException pwe )
- {
- throw new PluginInvalidInputException( pwe.getMessage(), pwe.getCause() );
- }
- pluginManager.addDeclaration( currDeclaration );
- }
- }
- else if ( pluginId != null )
- {
- currDeclaration = pluginManager.getDeclarationById( pluginId );
-
- if ( currDeclaration == null )
- {
- throw new PluginInvalidInputException( "Plugin id [" + pluginId + "] is not defined." );
- }
- }
- else if ( defaultPlugin != null )
- {
- currDeclaration = defaultPlugin;
- }
- else
- {
- throw new PluginInvalidInputException( "No plugin class specified for element " + pattern );
- }
-
- // get the class of the user plugged-in type
- final Class<?> pluginClass = currDeclaration.getPluginClass();
-
- final String path = getDigester().getMatch();
-
- // create a new Rules object and effectively push it onto a stack of
- // rules objects. The stack is actually a linked list; using the
- // PluginRules constructor below causes the new instance to link
- // to the previous head-of-stack, then the Digester.setRules() makes
- // the new instance the new head-of-stack.
- final PluginRules newRules = new PluginRules( getDigester(), path, oldRules, pluginClass );
- getDigester().setRules( newRules );
-
- if ( debug )
- {
- log.debug( "PluginCreateRule.begin: installing new plugin: " + "oldrules=" + oldRules.toString()
- + ", newrules=" + newRules.toString() );
- }
-
- // load up the custom rules
- currDeclaration.configure( getDigester(), pattern );
-
- // create an instance of the plugin class
- final Object instance = pluginClass.newInstance();
- getDigester().push( instance );
- if ( debug )
- {
- log.debug( "PluginCreateRule.begin" + ": pattern=[" + pattern + "]" + " match=[" + getDigester().getMatch()
- + "]" + " pushed instance of plugin [" + pluginClass.getName() + "]" );
- }
-
- // and now we have to fire any custom rules which would have
- // been matched by the same path that matched this rule, had
- // they been loaded at that time.
- final List<Rule> rules = newRules.getDecoratedRules().match( namespace, path, name, attributes );
- fireBeginMethods( rules, namespace, name, attributes );
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void body( final String namespace, final String name, final String text )
- throws Exception
- {
-
- // While this class itself has no work to do in the body method,
- // we do need to fire the body methods of all dynamically-added
- // rules matching the same path as this rule. During begin, we had
- // to manually execute the dynamic rules' begin methods because they
- // didn't exist in the digester's Rules object when the match begin.
- // So in order to ensure consistent ordering of rule execution, the
- // PluginRules class deliberately avoids returning any such rules
- // in later calls to the match method, instead relying on this
- // object to execute them at the appropriate time.
- //
- // Note that this applies only to rules matching exactly the path
- // which is also matched by this PluginCreateRule.
-
- final String path = getDigester().getMatch();
- final PluginRules newRules = (PluginRules) getDigester().getRules();
- final List<Rule> rules = newRules.getDecoratedRules().match( namespace, path, name, null );
- fireBodyMethods( rules, namespace, name, text );
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void end( final String namespace, final String name )
- throws Exception
- {
- // see body method for more info
- final String path = getDigester().getMatch();
- final PluginRules newRules = (PluginRules) getDigester().getRules();
- final List<Rule> rules = newRules.getDecoratedRules().match( namespace, path, name, null );
- fireEndMethods( rules, namespace, name );
-
- // pop the stack of PluginRules instances, which
- // discards all custom rules associated with this plugin
- getDigester().setRules( newRules.getParent() );
-
- // and get rid of the instance of the plugin class from the
- // digester object stack.
- getDigester().pop();
- }
-
- /**
- * Return the pattern that this Rule is associated with.
- * <p>
- * In general, Rule instances <i>can</i> 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.
- * </p>
- *
- * @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.
- *
- * @param rules The rules which {@link Rule#begin(String, String, Attributes)} method has to be fired
- * @param namespace the namespace URI of the matching element, or an empty string if the parser is not namespace
- * aware or the element has no namespace
- * @param name the local name if the parser is namespace aware, or just the element name otherwise
- * @param list The attribute list of this element
- * @throws Exception if any error occurs
- */
- public void fireBeginMethods( final List<Rule> rules, final String namespace, final String name, final Attributes list )
- throws Exception
- {
-
- if ( ( rules != null ) && ( !rules.isEmpty() ) )
- {
- final Log log = getDigester().getLogger();
- final boolean debug = log.isDebugEnabled();
- for ( final Rule rule : rules )
- {
- if ( debug )
- {
- log.debug( " Fire begin() for " + rule );
- }
- try
- {
- rule.begin( namespace, name, list );
- }
- catch ( final Exception e )
- {
- throw getDigester().createSAXException( e );
- }
- catch ( final Error e )
- {
- throw e;
- }
- }
- }
- }
-
- /**
- * Duplicate the processing that the Digester does when firing the {@link Rule#body(String, String, String)} methods
- * of rules.
- *
- * It would be really nice if the Digester class provided a way for this functionality to just be invoked directly.
- *
- * @param rules The rules which {@link Rule#body(String, String, String)} method has to be fired
- * @param namespace the namespace URI of the matching element, or an empty string if the parser is not namespace
- * aware or the element has no namespace
- * @param name the local name if the parser is namespace aware, or just the element name otherwise
- * @param text The text of the body of this element
- * @throws Exception if any error occurs
- */
- private void fireBodyMethods( final List<Rule> rules, final String namespaceURI, final String name, final String text )
- throws Exception
- {
- if ( ( rules != null ) && ( !rules.isEmpty() ) )
- {
- final Log log = getDigester().getLogger();
- final boolean debug = log.isDebugEnabled();
- for ( final Rule rule : rules )
- {
- if ( debug )
- {
- log.debug( " Fire body() for " + rule );
- }
- try
- {
- rule.body( namespaceURI, name, text );
- }
- catch ( final Exception e )
- {
- throw getDigester().createSAXException( e );
- }
- catch ( final Error e )
- {
- throw e;
- }
- }
- }
- }
-
- /**
- * Duplicate the processing that the Digester does when firing the end methods of rules.
- *
- * It would be really nice if the Digester class provided a way for this functionality to just be invoked directly.
- *
- * @param rules The rules which {@link Rule#end(String, String)} method has to be fired
- * @param namespaceURI the namespace URI of the matching element, or an empty string if the parser is not namespace
- * aware or the element has no namespace
- * @param name the local name if the parser is namespace aware, or just the element name otherwise
- * @throws Exception if any error occurs
- */
- public void fireEndMethods( final List<Rule> rules, final String namespaceURI, final String name )
- throws Exception
- {
- // Fire "end" events for all relevant rules in reverse order
- if ( rules != null )
- {
- final Log log = getDigester().getLogger();
- final boolean debug = log.isDebugEnabled();
- for ( int i = 0; i < rules.size(); i++ )
- {
- final int j = ( rules.size() - i ) - 1;
- final Rule rule = rules.get( j );
- if ( debug )
- {
- log.debug( " Fire end() for " + rule );
- }
- try
- {
- rule.end( namespaceURI, name );
- }
- catch ( final Exception e )
- {
- throw getDigester().createSAXException( e );
- }
- catch ( final Error e )
- {
- throw e;
- }
- }
- }
- }
-
-}
+package org.apache.commons.digester3.plugins;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.List;
+
+import org.apache.commons.digester3.Rule;
+import org.apache.commons.logging.Log;
+import org.xml.sax.Attributes;
+
+/**
+ * Allows the original rules for parsing the configuration file to define points at which plugins are allowed, by
+ * configuring a PluginCreateRule with the appropriate pattern.
+ *
+ * @since 1.6
+ */
+public class PluginCreateRule
+ extends Rule
+ implements InitializableRule
+{
+
+ // see setPluginClassAttribute
+ private String pluginClassAttrNs = null;
+
+ private String pluginClassAttr = null;
+
+ // see setPluginIdAttribute
+ private String pluginIdAttrNs = null;
+
+ private String pluginIdAttr = null;
+
+ /**
+ * In order to invoke the addRules method on the plugin class correctly, we need to know the pattern which this rule
+ * is matched by.
+ */
+ private String pattern;
+
+ /** A base class that any plugin must derive from. */
+ private Class<?> baseClass = null;
+
+ /**
+ * Info about optional default plugin to be used if no plugin-id is specified in the input data. This can simplify
+ * the syntax where one particular plugin is usually used.
+ */
+ private Declaration defaultPlugin;
+
+ /**
+ * Currently, none of the Rules methods allow exceptions to be thrown. Therefore if this class cannot initialize
+ * itself properly, it cannot cause the digester to stop. Instead, we cache the exception and throw it the first
+ * time the begin() method is called.
+ */
+ private PluginConfigurationException initException;
+
+ // -------------------- constructors -------------------------------------
+
+ /**
+ * Create a plugin rule where the user <i>must</i> specify a plugin-class or plugin-id.
+ *
+ * @param baseClass is the class which any specified plugin <i>must</i> be descended from.
+ */
+ public PluginCreateRule( final Class<?> baseClass )
+ {
+ this.baseClass = baseClass;
+ }
+
+ /**
+ * Create a plugin rule where the user <i>may</i> specify a plugin. If the user doesn't specify a plugin, then the
+ * default class specified in this constructor is used.
+ *
+ * @param baseClass is the class which any specified plugin <i>must</i> be descended from.
+ * @param dfltPluginClass is the class which will be used if the user doesn't specify any plugin-class or plugin-id.
+ * This class will have custom rules installed for it just like a declared plugin.
+ */
+ public PluginCreateRule( final Class<?> baseClass, final Class<?> dfltPluginClass )
+ {
+ this.baseClass = baseClass;
+ if ( dfltPluginClass != null )
+ {
+ defaultPlugin = new Declaration( dfltPluginClass );
+ }
+ }
+
+ /**
+ * Create a plugin rule where the user <i>may</i> specify a plugin. If the user doesn't specify a plugin, then the
+ * default class specified in this constructor is used.
+ *
+ * @param baseClass is the class which any specified plugin <i>must</i> be descended from.
+ * @param dfltPluginClass is the class which will be used if the user doesn't specify any plugin-class or plugin-id.
+ * This class will have custom rules installed for it just like a declared plugin.
+ * @param dfltPluginRuleLoader is a RuleLoader instance which knows how to load the custom rules associated with
+ * this default plugin.
+ */
+ public PluginCreateRule( final Class<?> baseClass, final Class<?> dfltPluginClass, final RuleLoader dfltPluginRuleLoader )
+ {
+ this.baseClass = baseClass;
+ if ( dfltPluginClass != null )
+ {
+ defaultPlugin = new Declaration( dfltPluginClass, dfltPluginRuleLoader );
+ }
+ }
+
+ // ------------------- properties ---------------------------------------
+
+ /**
+ * Sets the xml attribute which the input xml uses to indicate to a PluginCreateRule which class should be
+ * instantiated.
+ * <p>
+ * 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 <i>not</i>
+ * contain any kind of namespace-prefix. Note also that if you are using a non-namespace-aware parser,
+ * this parameter <i>must</i> 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.
+ * <p>
+ * 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 <i>not</i>
+ * contain any kind of namespace-prefix. Note also that if you are using a non-namespace-aware parser,
+ * this parameter <i>must</i> 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.
+ * <p>
+ * A new instance of the target class is created, and pushed onto the stack. A new "private" PluginRules object is
+ * then created and set as the digester's default Rules object. Any custom rules associated with the plugin class
+ * are then loaded into that new Rules object. Finally, any custom rules that are associated with the current
+ * pattern (such as SetPropertiesRules) have their begin methods executed.
+ *
+ * @param namespace the namespace URI of the matching element, or an empty string if the parser is not namespace
+ * aware or the element has no namespace
+ * @param name the local name if the parser is namespace aware, or just the element name otherwise
+ * @param attributes The attribute list of this element
+ * @throws Exception if any error occurs
+ */
+ @Override
+ public void begin( final String namespace, final String name, final org.xml.sax.Attributes attributes )
+ throws Exception
+ {
+ final Log log = getDigester().getLogger();
+ final boolean debug = log.isDebugEnabled();
+ if ( debug )
+ {
+ log.debug( "PluginCreateRule.begin" + ": pattern=[" + pattern + "]" + " match=[" + getDigester().getMatch()
+ + "]" );
+ }
+
+ if ( initException != null )
+ {
+ // we had a problem during initialization that we could
+ // not report then; report it now.
+ throw initException;
+ }
+
+ // load any custom rules associated with the plugin
+ final PluginRules oldRules = (PluginRules) getDigester().getRules();
+ final PluginManager pluginManager = oldRules.getPluginManager();
+ Declaration currDeclaration = null;
+
+ String pluginClassName;
+ if ( pluginClassAttrNs == null )
+ {
+ // Yep, this is ugly.
+ //
+ // In a namespace-aware parser, the one-param version will
+ // return attributes with no namespace.
+ //
+ // In a non-namespace-aware parser, the two-param version will
+ // never return any attributes, ever.
+ pluginClassName = attributes.getValue( pluginClassAttr );
+ }
+ else
+ {
+ pluginClassName = attributes.getValue( pluginClassAttrNs, pluginClassAttr );
+ }
+
+ String pluginId;
+ if ( pluginIdAttrNs == null )
+ {
+ pluginId = attributes.getValue( pluginIdAttr );
+ }
+ else
+ {
+ pluginId = attributes.getValue( pluginIdAttrNs, pluginIdAttr );
+ }
+
+ if ( pluginClassName != null )
+ {
+ // The user is using a plugin "inline", ie without a previous
+ // explicit declaration. If they have used the same plugin class
+ // before, we have already gone to the effort of creating a
+ // Declaration object, so retrieve it. If there is no existing
+ // declaration object for this class, then create one.
+
+ currDeclaration = pluginManager.getDeclarationByClass( pluginClassName );
+
+ if ( currDeclaration == null )
+ {
+ currDeclaration = new Declaration( pluginClassName );
+ try
+ {
+ currDeclaration.init( getDigester(), pluginManager );
+ }
+ catch ( final PluginException pwe )
+ {
+ throw new PluginInvalidInputException( pwe.getMessage(), pwe.getCause() );
+ }
+ pluginManager.addDeclaration( currDeclaration );
+ }
+ }
+ else if ( pluginId != null )
+ {
+ currDeclaration = pluginManager.getDeclarationById( pluginId );
+
+ if ( currDeclaration == null )
+ {
+ throw new PluginInvalidInputException( "Plugin id [" + pluginId + "] is not defined." );
+ }
+ }
+ else if ( defaultPlugin != null )
+ {
+ currDeclaration = defaultPlugin;
+ }
+ else
+ {
+ throw new PluginInvalidInputException( "No plugin class specified for element " + pattern );
+ }
+
+ // get the class of the user plugged-in type
+ final Class<?> pluginClass = currDeclaration.getPluginClass();
+
+ final String path = getDigester().getMatch();
+
+ // create a new Rules object and effectively push it onto a stack of
+ // rules objects. The stack is actually a linked list; using the
+ // PluginRules constructor below causes the new instance to link
+ // to the previous head-of-stack, then the Digester.setRules() makes
+ // the new instance the new head-of-stack.
+ final PluginRules newRules = new PluginRules( getDigester(), path, oldRules, pluginClass );
+ getDigester().setRules( newRules );
+
+ if ( debug )
+ {
+ log.debug( "PluginCreateRule.begin: installing new plugin: " + "oldrules=" + oldRules.toString()
+ + ", newrules=" + newRules.toString() );
+ }
+
+ // load up the custom rules
+ currDeclaration.configure( getDigester(), pattern );
+
+ // create an instance of the plugin class
+ final Object instance = pluginClass.newInstance();
+ getDigester().push( instance );
+ if ( debug )
+ {
+ log.debug( "PluginCreateRule.begin" + ": pattern=[" + pattern + "]" + " match=[" + getDigester().getMatch()
+ + "]" + " pushed instance of plugin [" + pluginClass.getName() + "]" );
+ }
+
+ // and now we have to fire any custom rules which would have
+ // been matched by the same path that matched this rule, had
+ // they been loaded at that time.
+ final List<Rule> rules = newRules.getDecoratedRules().match( namespace, path, name, attributes );
+ fireBeginMethods( rules, namespace, name, attributes );
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void body( final String namespace, final String name, final String text )
+ throws Exception
+ {
+
+ // While this class itself has no work to do in the body method,
+ // we do need to fire the body methods of all dynamically-added
+ // rules matching the same path as this rule. During begin, we had
+ // to manually execute the dynamic rules' begin methods because they
+ // didn't exist in the digester's Rules object when the match begin.
+ // So in order to ensure consistent ordering of rule execution, the
+ // PluginRules class deliberately avoids returning any such rules
+ // in later calls to the match method, instead relying on this
+ // object to execute them at the appropriate time.
+ //
+ // Note that this applies only to rules matching exactly the path
+ // which is also matched by this PluginCreateRule.
+
+ final String path = getDigester().getMatch();
+ final PluginRules newRules = (PluginRules) getDigester().getRules();
+ final List<Rule> rules = newRules.getDecoratedRules().match( namespace, path, name, null );
+ fireBodyMethods( rules, namespace, name, text );
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void end( final String namespace, final String name )
+ throws Exception
+ {
+ // see body method for more info
+ final String path = getDigester().getMatch();
+ final PluginRules newRules = (PluginRules) getDigester().getRules();
+ final List<Rule> rules = newRules.getDecoratedRules().match( namespace, path, name, null );
+ fireEndMethods( rules, namespace, name );
+
+ // pop the stack of PluginRules instances, which
+ // discards all custom rules associated with this plugin
+ getDigester().setRules( newRules.getParent() );
+
+ // and get rid of the instance of the plugin class from the
+ // digester object stack.
+ getDigester().pop();
+ }
+
+ /**
+ * Return the pattern that this Rule is associated with.
+ * <p>
+ * In general, Rule instances <i>can</i> 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.
+ * </p>
+ *
+ * @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.
+ *
+ * @param rules The rules which {@link Rule#begin(String, String, Attributes)} method has to be fired
+ * @param namespace the namespace URI of the matching element, or an empty string if the parser is not namespace
+ * aware or the element has no namespace
+ * @param name the local name if the parser is namespace aware, or just the element name otherwise
+ * @param list The attribute list of this element
+ * @throws Exception if any error occurs
+ */
+ public void fireBeginMethods( final List<Rule> rules, final String namespace, final String name, final Attributes list )
+ throws Exception
+ {
+
+ if ( ( rules != null ) && ( !rules.isEmpty() ) )
+ {
+ final Log log = getDigester().getLogger();
+ final boolean debug = log.isDebugEnabled();
+ for ( final Rule rule : rules )
+ {
+ if ( debug )
+ {
+ log.debug( " Fire begin() for " + rule );
+ }
+ try
+ {
+ rule.begin( namespace, name, list );
+ }
+ catch ( final Exception e )
+ {
+ throw getDigester().createSAXException( e );
+ }
+ catch ( final Error e )
+ {
+ throw e;
+ }
+ }
+ }
+ }
+
+ /**
+ * Duplicate the processing that the Digester does when firing the {@link Rule#body(String, String, String)} methods
+ * of rules.
+ *
+ * It would be really nice if the Digester class provided a way for this functionality to just be invoked directly.
+ *
+ * @param rules The rules which {@link Rule#body(String, String, String)} method has to be fired
+ * @param namespace the namespace URI of the matching element, or an empty string if the parser is not namespace
+ * aware or the element has no namespace
+ * @param name the local name if the parser is namespace aware, or just the element name otherwise
+ * @param text The text of the body of this element
+ * @throws Exception if any error occurs
+ */
+ private void fireBodyMethods( final List<Rule> rules, final String namespaceURI, final String name, final String text )
+ throws Exception
+ {
+ if ( ( rules != null ) && ( !rules.isEmpty() ) )
+ {
+ final Log log = getDigester().getLogger();
+ final boolean debug = log.isDebugEnabled();
+ for ( final Rule rule : rules )
+ {
+ if ( debug )
+ {
+ log.debug( " Fire body() for " + rule );
+ }
+ try
+ {
+ rule.body( namespaceURI, name, text );
+ }
+ catch ( final Exception e )
+ {
+ throw getDigester().createSAXException( e );
+ }
+ catch ( final Error e )
+ {
+ throw e;
+ }
+ }
+ }
+ }
+
+ /**
+ * Duplicate the processing that the Digester does when firing the end methods of rules.
+ *
+ * It would be really nice if the Digester class provided a way for this functionality to just be invoked directly.
+ *
+ * @param rules The rules which {@link Rule#end(String, String)} method has to be fired
+ * @param namespaceURI the namespace URI of the matching element, or an empty string if the parser is not namespace
+ * aware or the element has no namespace
+ * @param name the local name if the parser is namespace aware, or just the element name otherwise
+ * @throws Exception if any error occurs
+ */
+ public void fireEndMethods( final List<Rule> rules, final String namespaceURI, final String name )
+ throws Exception
+ {
+ // Fire "end" events for all relevant rules in reverse order
+ if ( rules != null )
+ {
+ final Log log = getDigester().getLogger();
+ final boolean debug = log.isDebugEnabled();
+ for ( int i = 0; i < rules.size(); i++ )
+ {
+ final int j = ( rules.size() - i ) - 1;
+ final Rule rule = rules.get( j );
+ if ( debug )
+ {
+ log.debug( " Fire end() for " + rule );
+ }
+ try
+ {
+ rule.end( namespaceURI, name );
+ }
+ catch ( final Exception e )
+ {
+ throw getDigester().createSAXException( e );
+ }
+ catch ( final Error e )
+ {
+ throw e;
+ }
+ }
+ }
+ }
+
+}
diff --git a/core/src/main/java/org/apache/commons/digester3/plugins/PluginRules.java b/core/src/main/java/org/apache/commons/digester3/plugins/PluginRules.java
index b1faa0c..f50eb91 100644
--- a/core/src/main/java/org/apache/commons/digester3/plugins/PluginRules.java
+++ b/core/src/main/java/org/apache/commons/digester3/plugins/PluginRules.java
@@ -1,472 +1,472 @@
-package org.apache.commons.digester3.plugins;
-
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import java.util.List;
-
-import org.apache.commons.digester3.Digester;
-import org.apache.commons.digester3.Rule;
-import org.apache.commons.digester3.Rules;
-import org.apache.commons.digester3.RulesBase;
-import org.apache.commons.logging.Log;
-import org.xml.sax.Attributes;
-
-/**
- * A custom digester Rules manager which must be used as the Rules object when using the plugins module functionality.
- * <p>
- * During parsing, a linked list of PluginCreateRule instances develop, and this list also acts like a stack. The
- * original instance that was set before the Digester started parsing is always at the tail of the list, and the
- * Digester always holds a reference to the instance at the head of the list in the rules member. Initially, this
- * list/stack holds just one instance, ie head and tail are the same object.
- * <p>
- * When the start of an xml element causes a PluginCreateRule to fire, a new PluginRules instance is created and
- * inserted at the head of the list (ie pushed onto the stack of Rules objects). Digester.getRules() therefore returns
- * this new Rules object, and any custom rules associated with that plugin are added to that instance.
- * <p>
- * When the end of the xml element is encountered (and therefore the PluginCreateRule end method fires), the stack of
- * Rules objects is popped, so that Digester.getRules returns the previous Rules object.
- *
- * @since 1.6
- */
-public class PluginRules
- implements Rules
-{
-
- /**
- * The Digester instance with which this Rules instance is associated.
- */
- protected Digester digester = null;
-
- /**
- * The (optional) object which generates new rules instances.
- */
- private RulesFactory rulesFactory;
-
- /**
- * The rules implementation that we are "enhancing" with plugins functionality, as per the Decorator pattern.
- */
- private final Rules decoratedRules;
-
- /** Object which contains information about all known plugins. */
- private final PluginManager pluginManager;
-
- /**
- * The path below which this rules object has responsibility. For paths shorter than or equal the mountpoint, the
- * parent's match is called.
- */
- private String mountPoint = null;
-
- /**
- * The Rules object that holds rules applying "above" the mountpoint, ie the next Rules object down in the stack.
- */
- private PluginRules parent = null;
-
- /**
- * A reference to the object that holds all data which should only exist once per digester instance.
- */
- private PluginContext pluginContext = null;
-
- // ------------------------------------------------------------- Constructor
-
- /**
- * Constructor for top-level Rules objects. Exactly one of these must be created and installed into the Digester
- * instance as the Rules object before parsing starts.
- */
- 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).
- * <p>
- * One of these is created each time a PluginCreateRule's begin method fires, in order to manage the custom rules
- * associated with whatever concrete plugin class the user has specified.
- *
- * @param digester is the object this rules will be associated with.
- * @param mountPoint is the digester match path for the element matching a PluginCreateRule which caused this
- * "nested parsing scope" to begin. This is expected to be equal to digester.getMatch().
- * @param parent must be non-null.
- * @param pluginClass is the plugin class whose custom rules will be loaded into this new PluginRules object.
- * @throws PluginException if any error occurs
- */
- PluginRules( final Digester digester, final String mountPoint, final PluginRules parent, final Class<?> pluginClass )
- throws PluginException
- {
- // no need to set digester or decoratedRules.digester,
- // because when Digester.setRules is called, the setDigester
- // method on this object will be called.
-
- this.digester = digester;
- this.mountPoint = mountPoint;
- this.parent = parent;
- this.rulesFactory = parent.rulesFactory;
-
- if ( rulesFactory == null )
- {
- decoratedRules = new RulesBase();
- }
- else
- {
- decoratedRules = rulesFactory.newRules( digester, pluginClass );
- }
-
- pluginContext = parent.pluginContext;
- 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;
- }
-
- /**
- * Set 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();
- }
-
- /**
- * Set 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<RuleFinder> getRuleFinders()
- {
- return pluginContext.getRuleFinders();
- }
-
- /**
- * See {@link PluginContext#setRuleFinders}.
- *
- * @param ruleFinders the list of RuleFinder objects
- */
- public void setRuleFinders( final List<RuleFinder> 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;
- }
-
- /**
- * Set the object which is used to generate the new Rules instances created to hold and process the rules associated
- * with each plugged-in class.
- *
- * @param factory the rules factory object
- */
- public void setRulesFactory( final RulesFactory factory )
- {
- rulesFactory = factory;
- }
-
- // --------------------------------------------------------- Public Methods
-
- /**
- * 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 list of rules registered with this object, in the order they were registered with this object.
- * <p>
- * 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<Rule> rules()
- {
- return decoratedRules.rules();
- }
-
- /**
- * Register a new Rule instance matching the specified pattern.
- *
- * @param pattern Nesting pattern to be matched for this Rule. This parameter treats equally patterns that begin
- * with and without a leading slash ('/').
- * @param rule Rule instance to be registered
- */
- @Override
- public void add( String pattern, final Rule rule )
- {
- final Log log = LogUtils.getLogger( digester );
- final boolean debug = log.isDebugEnabled();
-
- if ( debug )
- {
- log.debug( "add entry" + ": mapping pattern [" + pattern + "]" + " to rule of type ["
- + rule.getClass().getName() + "]" );
- }
-
- // allow patterns with a leading slash character
- if ( pattern.startsWith( "/" ) )
- {
- pattern = pattern.substring( 1 );
- }
-
- if ( mountPoint != null && !pattern.equals( mountPoint ) && !pattern.startsWith( mountPoint + "/" ) )
- {
- // This can only occur if a plugin attempts to add a
- // rule with a pattern that doesn't start with the
- // prefix passed to the addRules method. Plugins mustn't
- // add rules outside the scope of the tag they were specified
- // on, so refuse this.
-
- // alas, can't throw exception
- log.warn( "An attempt was made to add a rule with a pattern that"
- + "is not at or below the mountpoint of the current" + " PluginRules object." + " Rule pattern: "
- + pattern + ", mountpoint: " + mountPoint + ", rule type: " + rule.getClass().getName() );
- return;
- }
-
- decoratedRules.add( pattern, rule );
-
- if ( rule instanceof InitializableRule )
- {
- try
- {
- ( (InitializableRule) rule ).postRegisterInit( pattern );
- }
- catch ( final PluginConfigurationException e )
- {
- // Currently, Digester doesn't handle exceptions well
- // from the add method. The workaround is for the
- // initialisable rule to remember that its initialisation
- // failed, and to throw the exception when begin is
- // called for the first time.
- if ( debug )
- {
- log.debug( "Rule initialisation failed", e );
- }
- // throw e; -- alas, can't do this
- return;
- }
- }
-
- if ( debug )
- {
- log.debug( "add exit" + ": mapped pattern [" + pattern + "]" + " to rule of type ["
- + rule.getClass().getName() + "]" );
- }
- }
-
- /**
- * Clear all rules.
- */
- @Override
- public void clear()
- {
- decoratedRules.clear();
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public List<Rule> match( final String namespaceURI, final String path, final String name, final Attributes attributes )
- {
- final Log log = LogUtils.getLogger( digester );
- final boolean debug = log.isDebugEnabled();
-
- if ( debug )
- {
- log.debug( "Matching path [" + path + "] on rules object " + this.toString() );
- }
-
- List<Rule> matches;
- if ( ( mountPoint != null ) && ( path.length() <= mountPoint.length() ) )
- {
- if ( debug )
- {
- log.debug( "Path [" + path + "] delegated to parent." );
- }
-
- matches = parent.match( namespaceURI, path, name, attributes );
-
- // Note that in the case where path equals mountPoint,
- // we deliberately return only the rules from the parent,
- // even though this object may hold some rules matching
- // this same path. See PluginCreateRule's begin, body and end
- // methods for the reason.
- }
- else
- {
- log.debug( "delegating to decorated rules." );
- matches = decoratedRules.match( namespaceURI, path, name, attributes );
- }
-
- return matches;
- }
-
- /**
- * See {@link PluginContext#setPluginClassAttribute}.
- *
- * @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 <i>not</i>
- * contain any kind of namespace-prefix. Note also that if you are using a non-namespace-aware parser,
- * this parameter <i>must</i> 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 )
- {
- pluginContext.setPluginClassAttribute( namespaceUri, attrName );
- }
-
- /**
- * See {@link PluginContext#setPluginIdAttribute}.
- *
- * @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 <i>not</i>
- * contain any kind of namespace-prefix. Note also that if you are using a non-namespace-aware parser,
- * this parameter <i>must</i> 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 )
- {
- pluginContext.setPluginIdAttribute( namespaceUri, attrName );
- }
-
- /**
- * 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}.
- *
- * @return the namespace for the xml attribute which indicates which previous plugin declaration should be used.
- */
- public String getPluginIdAttrNs()
- {
- return pluginContext.getPluginIdAttrNs();
- }
-
- /**
- * 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();
- }
-
-}
+package org.apache.commons.digester3.plugins;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.List;
+
+import org.apache.commons.digester3.Digester;
+import org.apache.commons.digester3.Rule;
+import org.apache.commons.digester3.Rules;
+import org.apache.commons.digester3.RulesBase;
+import org.apache.commons.logging.Log;
+import org.xml.sax.Attributes;
+
+/**
+ * A custom digester Rules manager which must be used as the Rules object when using the plugins module functionality.
+ * <p>
+ * During parsing, a linked list of PluginCreateRule instances develop, and this list also acts like a stack. The
+ * original instance that was set before the Digester started parsing is always at the tail of the list, and the
+ * Digester always holds a reference to the instance at the head of the list in the rules member. Initially, this
+ * list/stack holds just one instance, ie head and tail are the same object.
+ * <p>
+ * When the start of an xml element causes a PluginCreateRule to fire, a new PluginRules instance is created and
+ * inserted at the head of the list (ie pushed onto the stack of Rules objects). Digester.getRules() therefore returns
+ * this new Rules object, and any custom rules associated with that plugin are added to that instance.
+ * <p>
+ * When the end of the xml element is encountered (and therefore the PluginCreateRule end method fires), the stack of
+ * Rules objects is popped, so that Digester.getRules returns the previous Rules object.
+ *
+ * @since 1.6
+ */
+public class PluginRules
+ implements Rules
+{
+
+ /**
+ * The Digester instance with which this Rules instance is associated.
+ */
+ protected Digester digester = null;
+
+ /**
+ * The (optional) object which generates new rules instances.
+ */
+ private RulesFactory rulesFactory;
+
+ /**
+ * The rules implementation that we are "enhancing" with plugins functionality, as per the Decorator pattern.
+ */
+ private final Rules decoratedRules;
+
+ /** Object which contains information about all known plugins. */
+ private final PluginManager pluginManager;
+
+ /**
+ * The path below which this rules object has responsibility. For paths shorter than or equal the mountpoint, the
+ * parent's match is called.
+ */
+ private String mountPoint = null;
+
+ /**
+ * The Rules object that holds rules applying "above" the mountpoint, ie the next Rules object down in the stack.
+ */
+ private PluginRules parent = null;
+
+ /**
+ * A reference to the object that holds all data which should only exist once per digester instance.
+ */
+ private PluginContext pluginContext = null;
+
+ // ------------------------------------------------------------- Constructor
+
+ /**
+ * Constructor for top-level Rules objects. Exactly one of these must be created and installed into the Digester
+ * instance as the Rules object before parsing starts.
+ */
+ 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).
+ * <p>
+ * One of these is created each time a PluginCreateRule's begin method fires, in order to manage the custom rules
+ * associated with whatever concrete plugin class the user has specified.
+ *
+ * @param digester is the object this rules will be associated with.
+ * @param mountPoint is the digester match path for the element matching a PluginCreateRule which caused this
+ * "nested parsing scope" to begin. This is expected to be equal to digester.getMatch().
+ * @param parent must be non-null.
+ * @param pluginClass is the plugin class whose custom rules will be loaded into this new PluginRules object.
+ * @throws PluginException if any error occurs
+ */
+ PluginRules( final Digester digester, final String mountPoint, final PluginRules parent, final Class<?> pluginClass )
+ throws PluginException
+ {
+ // no need to set digester or decoratedRules.digester,
+ // because when Digester.setRules is called, the setDigester
+ // method on this object will be called.
+
+ this.digester = digester;
+ this.mountPoint = mountPoint;
+ this.parent = parent;
+ this.rulesFactory = parent.rulesFactory;
+
+ if ( rulesFactory == null )
+ {
+ decoratedRules = new RulesBase();
+ }
+ else
+ {
+ decoratedRules = rulesFactory.newRules( digester, pluginClass );
+ }
+
+ pluginContext = parent.pluginContext;
+ 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;
+ }
+
+ /**
+ * Set 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();
+ }
+
+ /**
+ * Set 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<RuleFinder> getRuleFinders()
+ {
+ return pluginContext.getRuleFinders();
+ }
+
+ /**
+ * See {@link PluginContext#setRuleFinders}.
+ *
+ * @param ruleFinders the list of RuleFinder objects
+ */
+ public void setRuleFinders( final List<RuleFinder> 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;
+ }
+
+ /**
+ * Set the object which is used to generate the new Rules instances created to hold and process the rules associated
+ * with each plugged-in class.
+ *
+ * @param factory the rules factory object
+ */
+ public void setRulesFactory( final RulesFactory factory )
+ {
+ rulesFactory = factory;
+ }
+
+ // --------------------------------------------------------- Public Methods
+
+ /**
+ * 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 list of rules registered with this object, in the order they were registered with this object.
+ * <p>
+ * 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<Rule> rules()
+ {
+ return decoratedRules.rules();
+ }
+
+ /**
+ * Register a new Rule instance matching the specified pattern.
+ *
+ * @param pattern Nesting pattern to be matched for this Rule. This parameter treats equally patterns that begin
+ * with and without a leading slash ('/').
+ * @param rule Rule instance to be registered
+ */
+ @Override
+ public void add( String pattern, final Rule rule )
+ {
+ final Log log = LogUtils.getLogger( digester );
+ final boolean debug = log.isDebugEnabled();
+
+ if ( debug )
+ {
+ log.debug( "add entry" + ": mapping pattern [" + pattern + "]" + " to rule of type ["
+ + rule.getClass().getName() + "]" );
+ }
+
+ // allow patterns with a leading slash character
+ if ( pattern.startsWith( "/" ) )
+ {
+ pattern = pattern.substring( 1 );
+ }
+
+ if ( mountPoint != null && !pattern.equals( mountPoint ) && !pattern.startsWith( mountPoint + "/" ) )
+ {
+ // This can only occur if a plugin attempts to add a
+ // rule with a pattern that doesn't start with the
+ // prefix passed to the addRules method. Plugins mustn't
+ // add rules outside the scope of the tag they were specified
+ // on, so refuse this.
+
+ // alas, can't throw exception
+ log.warn( "An attempt was made to add a rule with a pattern that"
+ + "is not at or below the mountpoint of the current" + " PluginRules object." + " Rule pattern: "
+ + pattern + ", mountpoint: " + mountPoint + ", rule type: " + rule.getClass().getName() );
+ return;
+ }
+
+ decoratedRules.add( pattern, rule );
+
+ if ( rule instanceof InitializableRule )
+ {
+ try
+ {
+ ( (InitializableRule) rule ).postRegisterInit( pattern );
+ }
+ catch ( final PluginConfigurationException e )
+ {
+ // Currently, Digester doesn't handle exceptions well
+ // from the add method. The workaround is for the
+ // initialisable rule to remember that its initialization
+ // failed, and to throw the exception when begin is
+ // called for the first time.
+ if ( debug )
+ {
+ log.debug( "Rule initialisation failed", e );
+ }
+ // throw e; -- alas, can't do this
+ return;
+ }
+ }
+
+ if ( debug )
+ {
+ log.debug( "add exit" + ": mapped pattern [" + pattern + "]" + " to rule of type ["
+ + rule.getClass().getName() + "]" );
+ }
+ }
+
+ /**
+ * Clear all rules.
+ */
+ @Override
+ public void clear()
+ {
+ decoratedRules.clear();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public List<Rule> match( final String namespaceURI, final String path, final String name, final Attributes attributes )
+ {
+ final Log log = LogUtils.getLogger( digester );
+ final boolean debug = log.isDebugEnabled();
+
+ if ( debug )
+ {
+ log.debug( "Matching path [" + path + "] on rules object " + this.toString() );
+ }
+
+ List<Rule> matches;
+ if ( ( mountPoint != null ) && ( path.length() <= mountPoint.length() ) )
+ {
+ if ( debug )
+ {
+ log.debug( "Path [" + path + "] delegated to parent." );
+ }
+
+ matches = parent.match( namespaceURI, path, name, attributes );
+
+ // Note that in the case where path equals mountPoint,
+ // we deliberately return only the rules from the parent,
+ // even though this object may hold some rules matching
+ // this same path. See PluginCreateRule's begin, body and end
+ // methods for the reason.
+ }
+ else
+ {
+ log.debug( "delegating to decorated rules." );
+ matches = decoratedRules.match( namespaceURI, path, name, attributes );
+ }
+
+ return matches;
+ }
+
+ /**
+ * See {@link PluginContext#setPluginClassAttribute}.
+ *
+ * @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 <i>not</i>
+ * contain any kind of namespace-prefix. Note also that if you are using a non-namespace-aware parser,
+ * this parameter <i>must</i> 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 )
+ {
+ pluginContext.setPluginClassAttribute( namespaceUri, attrName );
+ }
+
+ /**
+ * See {@link PluginContext#setPluginIdAttribute}.
+ *
+ * @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 <i>not</i>
+ * contain any kind of namespace-prefix. Note also that if you are using a non-namespace-aware parser,
+ * this parameter <i>must</i> 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 )
+ {
+ pluginContext.setPluginIdAttribute( namespaceUri, attrName );
+ }
+
+ /**
+ * 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}.
+ *
+ * @return the namespace for the xml attribute which indicates which previous plugin declaration should be used.
+ */
+ public String getPluginIdAttrNs()
+ {
+ return pluginContext.getPluginIdAttrNs();
+ }
+
+ /**
+ * 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();
+ }
+
+}