You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by si...@apache.org on 2011/02/18 23:26:51 UTC

svn commit: r1072175 [1/2] - in /commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins: ./ strategies/

Author: simonetripodi
Date: Fri Feb 18 22:26:49 2011
New Revision: 1072175

URL: http://svn.apache.org/viewvc?rev=1072175&view=rev
Log:
reintegrated the 'plugin' package, adapted version of the proper one on /trunk

Added:
    commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/Declaration.java   (with props)
    commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/InitializableRule.java   (with props)
    commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/LogUtils.java   (with props)
    commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/PluginAssertionFailure.java   (with props)
    commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/PluginConfigurationException.java   (with props)
    commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/PluginContext.java   (with props)
    commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/PluginCreateRule.java   (with props)
    commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/PluginDeclarationRule.java   (with props)
    commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/PluginInvalidInputException.java   (with props)
    commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/PluginManager.java   (with props)
    commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/PluginRules.java   (with props)
    commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/RuleFinder.java   (with props)
    commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/RuleLoader.java   (with props)
    commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/RulesFactory.java   (with props)
    commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/package-info.java   (with props)
    commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/strategies/
    commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/strategies/FinderFromClass.java   (with props)
    commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/strategies/FinderFromDfltClass.java   (with props)
    commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/strategies/FinderFromDfltMethod.java   (with props)
    commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/strategies/FinderFromDfltResource.java   (with props)
    commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/strategies/FinderFromFile.java   (with props)
    commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/strategies/FinderFromMethod.java   (with props)
    commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/strategies/FinderFromResource.java   (with props)
    commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/strategies/FinderSetProperties.java   (with props)
    commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/strategies/LoaderFromClass.java   (with props)
    commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/strategies/LoaderFromStream.java   (with props)
    commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/strategies/LoaderSetProperties.java   (with props)
    commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/strategies/package-info.java   (with props)

Added: commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/Declaration.java
URL: http://svn.apache.org/viewvc/commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/Declaration.java?rev=1072175&view=auto
==============================================================================
--- commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/Declaration.java (added)
+++ commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/Declaration.java Fri Feb 18 22:26:49 2011
@@ -0,0 +1,209 @@
+/* $Id$
+ *
+ * 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.
+ */
+package org.apache.commons.digester3.plugins;
+
+import java.util.Properties;
+
+import org.apache.commons.digester3.Digester;
+import org.apache.commons.logging.Log;
+
+/**
+ * Represents a Class that can be instantiated by a PluginCreateRule, plus
+ * info on how to load custom digester rules for mapping xml into that
+ * plugged-in class.
+ */
+public class Declaration {
+
+    /** The class of the object to be instantiated. */
+    private Class<?> pluginClass;
+
+    /** The name of the class of the object to be instantiated. */
+    private String pluginClassName;
+
+    /** See {@link #setId}. */ 
+    private String id;
+
+    /** See {@link #setProperties}. */
+    private Properties properties = new Properties();
+
+    /** See {@link #init}. */
+    private boolean initialized = false;
+
+    /**
+     * Class which is responsible for dynamically loading this
+     * plugin's rules on demand.
+     */
+    private RuleLoader ruleLoader = null;
+
+    //---------------------- constructors ----------------------------------
+
+    /**
+     * Constructor.
+     */
+    public Declaration(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.
+     */
+    public Declaration(Class<?> pluginClass) {
+        this(pluginClass.getName());
+        this.pluginClass = pluginClass;
+    }
+
+    /**
+     * Create an instance where a fully-initialised ruleLoader instance
+     * is provided by the caller instead of having the PluginManager
+     * "discover" an appropriate one.
+     */
+    public Declaration(Class<?> pluginClass, RuleLoader ruleLoader) {
+        this(pluginClass);
+        this.ruleLoader = ruleLoader;
+    }
+
+    /** 
+     * 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.
+     * <p>
+     * For plugins declared "in-line", the id is null.
+     */
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    /**
+     * Return the id associated with this declaration. For plugins
+     * declared "inline", null will be returned.
+     * 
+     * @return The id value. May be null.
+     */
+    public String getId() {
+        return id;
+    }
+
+    /** 
+     * Copy all (key,value) pairs in the param into the properties member of
+     * this object.
+     * <p>
+     * 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.
+     * <p>
+     * The "id" and "class" properties are treated differently.
+     */
+    public void setProperties(Properties p) {
+        properties.putAll(p);
+    }
+
+    /**
+     * Return plugin class associated with this declaration.
+     * 
+     * @return The pluginClass.
+     */
+    public Class<?> getPluginClass() {
+        return pluginClass;
+    }
+
+    /**
+     * Must be called exactly once, and must be called before any call
+     * to the configure method.
+     */
+    public void init(Digester digester, PluginManager pm) throws PluginException {
+        Log log = digester.getLog();
+        boolean debug = log.isDebugEnabled();
+        if (debug) {
+            log.debug("init being called!");
+        }
+
+        if (initialized) {
+            throw new PluginAssertionFailure("Init called multiple times.");
+        }
+
+        if ((pluginClass == null) && (pluginClassName != null)) {
+            try {
+                // load the plugin class object
+                pluginClass = 
+                    digester.getClassLoader().loadClass(pluginClassName);
+            } catch(ClassNotFoundException cnfe) {
+                throw new PluginException(
+                    "Unable to load class " + pluginClassName, cnfe);
+            }
+        }
+
+        if (ruleLoader == null) {
+            // the caller didn't provide a ruleLoader to the constructor,
+            // so get the plugin manager to "discover" one.
+            log.debug("Searching for ruleloader...");
+            ruleLoader = pm.findLoader(digester, id, pluginClass, properties);
+        } else {
+            log.debug("This declaration has an explicit ruleLoader.");
+        }
+
+        if (debug) {
+            if (ruleLoader == null) {
+                log.debug(
+                    "No ruleLoader found for plugin declaration"
+                    + " id [" + id + "]"
+                    + ", class [" + pluginClass.getClass().getName() + "].");
+            } else {
+                log.debug(
+                    "RuleLoader of type [" + ruleLoader.getClass().getName()
+                    + "] associated with plugin declaration"
+                    + " id [" + id + "]"
+                    + ", class [" + pluginClass.getClass().getName() + "].");
+            }
+        }
+
+        initialized = true;
+    }
+
+    /**
+     * Attempt to load custom rules for the target class at the specified
+     * pattern.
+     * <p>
+     * On return, any custom rules associated with the plugin class have
+     * been loaded into the Rules object currently associated with the
+     * specified digester object.
+     */
+     
+    public void configure(Digester digester, String pattern) throws PluginException {
+        Log log = digester.getLog();
+        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);
+        }
+    }
+
+}

Propchange: commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/Declaration.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/Declaration.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/Declaration.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/InitializableRule.java
URL: http://svn.apache.org/viewvc/commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/InitializableRule.java?rev=1072175&view=auto
==============================================================================
--- commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/InitializableRule.java (added)
+++ commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/InitializableRule.java Fri Feb 18 22:26:49 2011
@@ -0,0 +1,40 @@
+/* $Id$
+ *
+ * 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.
+ */
+package org.apache.commons.digester3.plugins;
+
+/**
+ * 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.
+ */
+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.
+     * @exception
+     *  PluginConfigurationException is thrown if the InitializableRule 
+     *  determines that it cannot correctly initialise itself for any reason.
+     */
+    void postRegisterInit(String pattern) throws PluginConfigurationException;
+
+}

Propchange: commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/InitializableRule.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/InitializableRule.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/InitializableRule.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/LogUtils.java
URL: http://svn.apache.org/viewvc/commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/LogUtils.java?rev=1072175&view=auto
==============================================================================
--- commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/LogUtils.java (added)
+++ commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/LogUtils.java Fri Feb 18 22:26:49 2011
@@ -0,0 +1,71 @@
+/* $Id$
+ *
+ * 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.
+ */
+package org.apache.commons.digester3.plugins;
+
+import org.apache.commons.digester3.Digester;
+import org.apache.commons.logging.Log;
+
+/**
+ * Simple utility class to assist in logging.
+ * <p>
+ * This class is intended only for the use of the code in the
+ * plugins packages. No "user" code should use this package.
+ * <p>
+ * The Digester module has an interesting approach to logging:
+ * all logging should be done via the Log object stored on the
+ * digester instance that the object *doing* the logging is associated
+ * with.
+ * <p>
+ * This is done because apparently some "container"-type applications
+ * such as Avalon and Tomcat need to be able to configure different logging
+ * for different <i>instances</i> of the Digester class which have been
+ * loaded from the same ClassLoader [info from Craig McClanahan]. 
+ * Not only the logging of the Digester instance should be affected; all 
+ * objects associated with that Digester instance should obey the 
+ * reconfiguration of their owning Digester instance's logging. The current 
+ * solution is to force all objects to output logging info via a single 
+ * Log object stored on the Digester instance they are associated with.
+ * <p>
+ * Of course this causes problems if logging is attempted before an
+ * object <i>has</i> a valid reference to its owning Digester. The 
+ * getLogging method provided here resolves this issue by returning a
+ * Log object which silently discards all logging output in this
+ * situation.
+ * <p>
+ * And it also implies that logging filtering can no longer be applied
+ * to subcomponents of the Digester, because all logging is done via
+ * a single Log object (a single Category). C'est la vie...
+ */
+class LogUtils {
+
+    /**
+     * Get the Log object associated with the specified Digester instance,
+     * or a "no-op" logging object if the digester reference is null.
+     * <p>
+     * You should use this method instead of digester.getLogger() in
+     * any situation where the digester might be null.
+     */
+    static Log getLogger(Digester digester) {
+        if (digester == null) {
+            return new org.apache.commons.logging.impl.NoOpLog();
+        }
+
+        return digester.getLog();
+    }
+
+}

Propchange: commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/LogUtils.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/LogUtils.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/LogUtils.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/PluginAssertionFailure.java
URL: http://svn.apache.org/viewvc/commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/PluginAssertionFailure.java?rev=1072175&view=auto
==============================================================================
--- commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/PluginAssertionFailure.java (added)
+++ commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/PluginAssertionFailure.java Fri Feb 18 22:26:49 2011
@@ -0,0 +1,71 @@
+/* $Id$
+ *
+ * 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.
+ */
+package org.apache.commons.digester3.plugins;
+
+/**
+ * Thrown when a bug is detected in the plugins code.
+ * <p>
+ * This class is intended to be used in assertion statements, similar to
+ * the way that java 1.4's native assertion mechanism is used. However there
+ * is a difference: when a java 1.4 assertion fails, an AssertionError
+ * is thrown, which is a subclass of Error; here, the PluginAssertionFailure
+ * class extends RuntimeException rather than Error.
+ * <p>
+ * This difference in design is because throwing Error objects is not
+ * good in a container-based architecture.
+ * <p>
+ * Example:
+ * <pre>
+ *   if (impossibleCondition) {
+ *     throw new PluginAssertionFailure(
+ *       "internal error: impossible condition is true");
+ *   }
+ * </pre> 
+ * <p>
+ * Note that PluginAssertionFailure should <i>not</i> be thrown when user 
+ * input is bad, or when code external to the Digester module passes invalid 
+ * parameters to a plugins method. It should be used only in checks for 
+ * problems which indicate internal bugs within the plugins module.
+ */
+public class PluginAssertionFailure extends RuntimeException {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * @param cause underlying exception that caused this to be thrown
+     */
+    public PluginAssertionFailure(Throwable cause) {
+        super(cause);
+    }
+
+    /**
+     * @param msg describes the reason this exception is being thrown.
+     */
+    public PluginAssertionFailure(String msg) {
+        super(msg);
+    }
+
+    /**
+     * @param msg describes the reason this exception is being thrown.
+     * @param cause underlying exception that caused this to be thrown
+     */
+    public PluginAssertionFailure(String msg, Throwable cause) {
+        super(msg, cause);
+    }
+
+}

Propchange: commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/PluginAssertionFailure.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/PluginAssertionFailure.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/PluginAssertionFailure.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/PluginConfigurationException.java
URL: http://svn.apache.org/viewvc/commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/PluginConfigurationException.java?rev=1072175&view=auto
==============================================================================
--- commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/PluginConfigurationException.java (added)
+++ commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/PluginConfigurationException.java Fri Feb 18 22:26:49 2011
@@ -0,0 +1,53 @@
+/* $Id$
+ *
+ * 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.
+ */
+package org.apache.commons.digester3.plugins;
+
+/**
+ * Thrown when an error occurs due to the way the calling application uses
+ * the plugins module. Because the pre-existing Digester API doesn't provide
+ * any option for throwing checked exceptions at some points where Plugins
+ * can potentially fail, this exception extends RuntimeException so that it
+ * can "tunnel" through these points.
+ */
+public class PluginConfigurationException extends RuntimeException {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * @param cause underlying exception that caused this to be thrown
+     */
+    public PluginConfigurationException(Throwable cause) {
+        super(cause);
+    }
+
+    /**
+     * @param msg describes the reason this exception is being thrown.
+     */
+    public PluginConfigurationException(String msg) {
+        super(msg);
+    }
+
+    /**
+     * @param msg describes the reason this exception is being thrown.
+     * @param cause underlying exception that caused this to be thrown
+     */
+    public PluginConfigurationException(String msg, Throwable cause) {
+        super(msg, cause);
+    }
+
+}

Propchange: commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/PluginConfigurationException.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/PluginConfigurationException.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/PluginConfigurationException.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/PluginContext.java
URL: http://svn.apache.org/viewvc/commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/PluginContext.java?rev=1072175&view=auto
==============================================================================
--- commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/PluginContext.java (added)
+++ commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/PluginContext.java Fri Feb 18 22:26:49 2011
@@ -0,0 +1,239 @@
+/* $Id$
+ *
+ * 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.
+ */
+package org.apache.commons.digester3.plugins;
+
+import java.util.List;
+import java.util.LinkedList;
+
+import org.apache.commons.digester3.plugins.strategies.FinderFromFile;
+import org.apache.commons.digester3.plugins.strategies.FinderFromResource;
+import org.apache.commons.digester3.plugins.strategies.FinderFromClass;
+import org.apache.commons.digester3.plugins.strategies.FinderFromMethod;
+import org.apache.commons.digester3.plugins.strategies.FinderFromDfltMethod;
+import org.apache.commons.digester3.plugins.strategies.FinderFromDfltClass;
+import org.apache.commons.digester3.plugins.strategies.FinderFromDfltResource;
+import org.apache.commons.digester3.plugins.strategies.FinderSetProperties;
+
+/**
+ * Provides data and services which should exist only once per digester.
+ * <p>
+ * This class holds a number of useful items which should be shared by all
+ * plugin objects. Such data cannot be stored on the PluginRules or 
+ * PluginManager classes, as there can be multiple instances of these at 
+ * various times during a parse. 
+ * <p>
+ * The name "Context" refers to the similarity between this class and a
+ * ServletContext class in a servlet engine. A ServletContext object provides
+ * access to the container's services such as obtaining global configuration
+ * parameters for the container, or getting access to logging services. For
+ * plugins, a Digester instance can be regarded as "the container".
+ */
+public class PluginContext {
+
+    // TODO: Consider making following four constants static in 2.0
+    // the xml attribute the user uses on an xml element to specify
+    // the plugin's class
+    public final String DFLT_PLUGIN_CLASS_ATTR_NS = null;
+
+    public final String DFLT_PLUGIN_CLASS_ATTR = "plugin-class";
+
+    // the xml attribute the user uses on an xml element to specify
+    // the plugin's class
+    public final String DFLT_PLUGIN_ID_ATTR_NS = null;
+
+    public final String DFLT_PLUGIN_ID_ATTR = "plugin-id";
+
+    /** See {@link #setPluginClassAttribute}. */
+    private String pluginClassAttrNs = DFLT_PLUGIN_CLASS_ATTR_NS;
+
+    /** See {@link #setPluginClassAttribute}. */
+    private String pluginClassAttr = DFLT_PLUGIN_CLASS_ATTR;
+
+    /** See {@link #setPluginClassAttribute}. */
+    private String pluginIdAttrNs = DFLT_PLUGIN_ID_ATTR_NS;
+
+    /** See {@link #setPluginClassAttribute}. */
+    private String pluginIdAttr = DFLT_PLUGIN_ID_ATTR;
+
+    /** 
+     * A list of RuleFinder objects used by all Declarations (and thus
+     * indirectly by all PluginCreateRules to locate the custom rules
+     * for plugin classes.
+     */
+    private List<RuleFinder> ruleFinders;
+
+    /**
+     * 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 first, then the list specified there is returned.
+     * <p>
+     * It is explicitly permitted for the caller to modify this list
+     * by inserting or removing RuleFinder objects.
+     */
+    public List<RuleFinder> getRuleFinders() {
+        if (ruleFinders == null) {
+            // when processing a plugin declaration, attempts are made to
+            // find custom rules in the order in which the Finder objects
+            // are added below. However this list can be modified
+            ruleFinders = new LinkedList<RuleFinder>();
+            ruleFinders.add(new FinderFromFile());
+            ruleFinders.add(new FinderFromResource());
+            ruleFinders.add(new FinderFromClass());
+            ruleFinders.add(new FinderFromMethod());
+            ruleFinders.add(new FinderFromDfltMethod());
+            ruleFinders.add(new FinderFromDfltClass());
+            ruleFinders.add(new FinderFromDfltResource());
+            ruleFinders.add(new FinderFromDfltResource(".xml"));
+            ruleFinders.add(new FinderSetProperties());
+        }
+        return ruleFinders;
+    }
+
+    /**
+     * Set 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.
+     * <p>
+     * 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.
+     */
+    public void setRuleFinders(List<RuleFinder> ruleFinders) {
+        this.ruleFinders = ruleFinders;
+    }
+
+    /**
+     * Sets the xml attribute which the input xml uses to indicate to a 
+     * PluginCreateRule which class should be instantiated.
+     * <p>
+     * Example:
+     * <pre>
+     * setPluginClassAttribute(null, "class");
+     * </pre>
+     * will allow this in the input xml:
+     * <pre>
+     *  &lt;root&gt;
+     *    &lt;some-plugin class="com.acme.widget"&gt; ......
+     * </pre>
+     * instead of the default syntax:
+     * <pre>
+     *  &lt;root&gt;
+     *    &lt;some-plugin plugin-class="com.acme.widget"&gt; ......
+     * </pre>
+     * This is particularly useful if the input xml document is not in
+     * English.
+     * <p>
+     * Note that the xml attributes used by PluginDeclarationRules are not
+     * affected by this method.
+     *
+     * @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(String namespaceUri, 
+                                        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>
+     * Example:
+     * <pre>
+     * setPluginIdAttribute(null, "id");
+     * </pre>
+     * will allow this in the input xml:
+     * <pre>
+     *  &lt;root&gt;
+     *    &lt;some-plugin id="widget"&gt; ......
+     * </pre>
+     * rather than the default behaviour:
+     * <pre>
+     *  &lt;root&gt;
+     *    &lt;some-plugin plugin-id="widget"&gt; ......
+     * </pre>
+     * This is particularly useful if the input xml document is not in
+     * English.
+     * <p>
+     * Note that the xml attributes used by PluginDeclarationRules are not
+     * affected by this method.
+     *
+     * @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(String namespaceUri, 
+                                     String attrName) {
+        pluginIdAttrNs = namespaceUri;
+        pluginIdAttr = attrName;
+    }
+
+    /**
+     * Get the namespace for the xml attribute which indicates to a 
+     * PluginCreateRule which class is to be plugged in.
+     * <p>
+     * May be null (in fact, normally will be).
+     */
+    public String getPluginClassAttrNs() {
+        return pluginClassAttrNs;
+    }
+    
+    /**
+     * Get the namespace for the xml attribute which indicates to a 
+     * PluginCreateRule which class is to be plugged in.
+     * <p>
+     * The return value is never null.
+     */
+    public String getPluginClassAttr() {
+        return pluginClassAttr;
+    }
+    
+    /**
+     * Get the namespace for the xml attribute which indicates to a 
+     * PluginCreateRule which previous plugin declaration should be used.
+     * <p>
+     * May be null (in fact, normally will be).
+     */
+    public String getPluginIdAttrNs() {
+        return pluginIdAttrNs;
+    }
+    
+    /**
+     * Get the namespace for the xml attribute which indicates to a 
+     * PluginCreateRule which previous plugin declaration should be used.
+     * <p>
+     * The return value is never null.
+     */
+    public String getPluginIdAttr() {
+        return pluginIdAttr;
+    }
+}

Propchange: commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/PluginContext.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/PluginContext.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/PluginContext.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/PluginCreateRule.java
URL: http://svn.apache.org/viewvc/commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/PluginCreateRule.java?rev=1072175&view=auto
==============================================================================
--- commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/PluginCreateRule.java (added)
+++ commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/PluginCreateRule.java Fri Feb 18 22:26:49 2011
@@ -0,0 +1,571 @@
+/* $Id$
+ *
+ * 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.
+ */
+package org.apache.commons.digester3.plugins;
+
+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.
+ */
+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 initialise 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;
+
+    /**
+     * 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(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(Class<?> baseClass, 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(Class<?> baseClass, Class<?> dfltPluginClass, RuleLoader dfltPluginRuleLoader) {
+        this.baseClass = baseClass;
+        if (dfltPluginClass != null) {
+            defaultPlugin = 
+                new Declaration(dfltPluginClass, dfltPluginRuleLoader);
+        }
+    }
+
+    /**
+     * 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.
+     */
+    public void setPluginClassAttribute(String namespaceUri, 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.
+     */
+    public void setPluginIdAttribute(String namespaceUri, String attrName) {
+        pluginIdAttrNs = namespaceUri;
+        pluginIdAttr = attrName;
+    }
+
+    /**
+     * Invoked after this rule has been added to the set of digester rules,
+     * associated with the specified pattern. Check all configuration data is
+     * valid and remember the pattern for later.
+     * 
+     * @param matchPattern is the digester match pattern that is associated 
+     * with this rule instance, eg "root/widget".
+     * @exception PluginConfigurationException
+     */
+    public void postRegisterInit(String matchPattern) throws PluginConfigurationException {
+        Log log = LogUtils.getLogger(this.getDigester());
+        boolean debug = log.isDebugEnabled();
+        if (debug) {
+            log.debug("PluginCreateRule.postRegisterInit" + 
+                      ": rule registered for pattern [" + matchPattern + "]");
+        }
+
+        if (this.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;
+        }
+
+        PluginRules rules = (PluginRules) this.getDigester().getRules();
+        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(this.getDigester(), pm);
+            } catch(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 
+     * @param name 
+     * @param attributes
+     *
+     * @throws ClassNotFoundException
+     * @throws PluginInvalidInputException
+     * @throws PluginConfigurationException
+     */
+    @Override
+    public void begin(String namespace, String name,
+                      org.xml.sax.Attributes attributes)
+                      throws java.lang.Exception {
+        Log log = this.getDigester().getLog();
+        boolean debug = log.isDebugEnabled();
+        if (debug) {
+            log.debug("PluginCreateRule.begin" + ": pattern=[" + pattern + "]" + 
+                  " match=[" + this.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
+        PluginRules oldRules = (PluginRules) this.getDigester().getRules();
+        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(this.getDigester(), pluginManager);
+                } catch(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
+        Class<?> pluginClass = currDeclaration.getPluginClass();
+
+        String path = this.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.
+        PluginRules newRules = new PluginRules(this.getDigester(), path, oldRules, pluginClass);
+        this.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(this.getDigester(), pattern);
+
+        // create an instance of the plugin class
+        Object instance = pluginClass.newInstance();
+        getDigester().push(instance);
+        if (debug) {
+            log.debug(
+                "PluginCreateRule.begin" + ": pattern=[" + pattern + "]" + 
+                " match=[" + this.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.
+        List<Rule> rules = newRules.getDecoratedRules().match(namespace, path);
+        fireBeginMethods(rules, namespace, name, attributes); 
+    }
+
+    /**
+     * Process the body text of this element.
+     *
+     * @param text The body text of this element
+     */
+    @Override
+    public void body(String namespace, String name, 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. 
+
+        String path = this.getDigester().getMatch();
+        PluginRules newRules = (PluginRules) this.getDigester().getRules();
+        List<Rule> rules = newRules.getDecoratedRules().match(namespace, path);
+        fireBodyMethods(rules, namespace, name, text);
+    }
+
+    /**
+     * Invoked by the digester when the closing tag matching this Rule's
+     * pattern is encountered.
+     * </p>
+     * 
+     * @param namespace Description of the Parameter
+     * @param name Description of the Parameter
+     * @exception Exception Description of the Exception
+     *
+     * @see #begin
+     */
+    @Override
+    public void end(String namespace, String name) throws Exception {
+        // see body method for more info
+        String path = this.getDigester().getMatch();
+        PluginRules newRules = (PluginRules) this.getDigester().getRules();
+        List<Rule> rules = newRules.getDecoratedRules().match(namespace, path);
+        fireEndMethods(rules, namespace, name);
+
+        // pop the stack of PluginRules instances, which
+        // discards all custom rules associated with this plugin
+        this.getDigester().setRules(newRules.getParent());
+
+        // and get rid of the instance of the plugin class from the
+        // digester object stack.
+        this.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.
+     */
+    public void fireBeginMethods(List<Rule> rules,
+            String namespace,
+            String name,
+            Attributes list) throws Exception {
+        if ((rules != null) && (rules.size() > 0)) {
+            Log log = this.getDigester().getLog();
+            boolean debug = log.isDebugEnabled();
+            for (int i = 0; i < rules.size(); i++) {
+                try {
+                    Rule rule = rules.get(i);
+                    if (debug) {
+                        log.debug("  Fire begin() for " + rule);
+                    }
+                    rule.begin(namespace, name, list);
+                } catch (Exception e) {
+                    throw this.getDigester().createSAXException(e);
+                } catch (Error e) {
+                    throw e;
+                }
+            }
+        }
+    }
+
+    /**
+     * Duplicate the processing that the Digester does when firing the
+     * body methods of rules. It would be really nice if the Digester
+     * class provided a way for this functionality to just be invoked
+     * directly.
+     */
+    private void fireBodyMethods(List<Rule> rules, String namespaceURI, String name, String text) throws Exception {
+        if ((rules != null) && (rules.size() > 0)) {
+            Log log = this.getDigester().getLog();
+            boolean debug = log.isDebugEnabled();
+            for (int i = 0; i < rules.size(); i++) {
+                try {
+                    Rule rule = rules.get(i);
+                    if (debug) {
+                        log.debug("  Fire body() for " + rule);
+                    }
+                    rule.body(namespaceURI, name, text);
+                } catch (Exception e) {
+                    throw this.getDigester().createSAXException(e);
+                } catch (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.
+     */
+    public void fireEndMethods(List<Rule> rules, String namespaceURI, String name) throws Exception {
+        // Fire "end" events for all relevant rules in reverse order
+        if (rules != null) {
+            Log log = this.getDigester().getLog();
+            boolean debug = log.isDebugEnabled();
+            for (int i = 0; i < rules.size(); i++) {
+                int j = (rules.size() - i) - 1;
+                try {
+                    Rule rule = rules.get(j);
+                    if (debug) {
+                        log.debug("  Fire end() for " + rule);
+                    }
+                    rule.end(namespaceURI, name);
+                } catch (Exception e) {
+                    throw this.getDigester().createSAXException(e);
+                } catch (Error e) {
+                    throw e;
+                }
+            }
+        }
+    }
+
+}

Propchange: commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/PluginCreateRule.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/PluginCreateRule.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/PluginCreateRule.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/PluginDeclarationRule.java
URL: http://svn.apache.org/viewvc/commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/PluginDeclarationRule.java?rev=1072175&view=auto
==============================================================================
--- commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/PluginDeclarationRule.java (added)
+++ commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/PluginDeclarationRule.java Fri Feb 18 22:26:49 2011
@@ -0,0 +1,101 @@
+/* $Id$
+ *
+ * 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.
+ */
+package org.apache.commons.digester3.plugins;
+
+import java.util.Properties;
+
+import org.apache.commons.digester3.Digester;
+import org.apache.commons.digester3.Rule;
+import org.xml.sax.Attributes;
+
+/**
+ * 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.
+ * <p>
+ * Normally, a PluginDeclarationRule is added to a Digester instance with
+ * the pattern "{root}/plugin" or "* /plugin" where {root} is the name of 
+ * the root tag in the input document.
+ */
+public class PluginDeclarationRule extends Rule {
+
+    /**
+     * Invoked upon reading a tag defining a plugin declaration. The tag
+     * must have the following mandatory attributes:
+     * <ul>
+     *   <li> id </li>
+     *   <li> class </li>
+     * </ul>
+     *
+     *@param namespace The xml namespace in which the xml element which
+     *                 triggered this rule resides.
+     *@param name The name of the xml element which triggered this rule.
+     *@param attributes The set of attributes on the xml element which
+     *                  triggered this rule.
+     *@exception java.lang.Exception
+     */
+    @Override
+    public void begin(String namespace, String name, Attributes attributes) throws Exception {
+        int nAttrs = attributes.getLength();
+        Properties props = new Properties();
+        for(int i=0; i<nAttrs; ++i) {
+            String key = attributes.getLocalName(i);
+            if ((key == null) || (key.length() == 0)) {
+                key = attributes.getQName(i);
+            }
+            String value = attributes.getValue(i);
+            props.setProperty(key, value);
+        }
+
+        try {
+            declarePlugin(getDigester(), props);
+        } catch(PluginInvalidInputException ex) {
+            throw new PluginInvalidInputException(String.format("Error on element [%s]:",
+                    getDigester().getMatch(),
+                    ex.getMessage()), ex);
+        }
+    }
+
+    public static void declarePlugin(Digester digester, Properties props) throws PluginException {
+        String id = props.getProperty("id");
+        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");
+        }
+
+        Declaration newDecl = new Declaration(pluginClassName);
+        newDecl.setId(id);
+        newDecl.setProperties(props);
+
+        PluginRules rc = (PluginRules) digester.getRules();
+        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.
+    }
+
+}

Propchange: commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/PluginDeclarationRule.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/PluginDeclarationRule.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/PluginDeclarationRule.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/PluginInvalidInputException.java
URL: http://svn.apache.org/viewvc/commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/PluginInvalidInputException.java?rev=1072175&view=auto
==============================================================================
--- commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/PluginInvalidInputException.java (added)
+++ commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/PluginInvalidInputException.java Fri Feb 18 22:26:49 2011
@@ -0,0 +1,49 @@
+/* $Id$
+ *
+ * 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.
+ */
+package org.apache.commons.digester3.plugins;
+
+/**
+ * Thrown when an error occurs due to bad data in the file being parsed.
+ */
+public class PluginInvalidInputException extends PluginException {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * @param cause underlying exception that caused this to be thrown
+     */
+    public PluginInvalidInputException(Throwable cause) {
+        super(cause);
+    }
+
+    /**
+     * @param msg describes the reason this exception is being thrown.
+     */
+    public PluginInvalidInputException(String msg) {
+        super(msg);
+    }
+
+    /**
+     * @param msg describes the reason this exception is being thrown.
+     * @param cause underlying exception that caused this to be thrown
+     */
+    public PluginInvalidInputException(String msg, Throwable cause) {
+        super(msg, cause);
+    }
+
+}

Propchange: commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/PluginInvalidInputException.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/PluginInvalidInputException.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/PluginInvalidInputException.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/PluginManager.java
URL: http://svn.apache.org/viewvc/commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/PluginManager.java?rev=1072175&view=auto
==============================================================================
--- commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/PluginManager.java (added)
+++ commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/PluginManager.java Fri Feb 18 22:26:49 2011
@@ -0,0 +1,171 @@
+/* $Id$
+ *
+ * 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.
+ */
+package org.apache.commons.digester3.plugins;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import org.apache.commons.digester3.Digester;
+import org.apache.commons.logging.Log;
+
+/**
+ * Coordinates between PluginDeclarationRule and PluginCreateRule objects,
+ * providing a place to share data between instances of these rules.
+ * <p>
+ * One instance of this class exists per PluginRules instance.
+ */
+public class PluginManager {
+
+    /** Map of classname->Declaration */
+    private final Map<String, Declaration> declarationsByClass = new HashMap<String, Declaration>();
+
+    /** Map of id->Declaration  */
+    private final Map<String, Declaration> declarationsById = new HashMap<String, Declaration>();
+
+    /** the parent manager to which this one may delegate lookups. */
+    private PluginManager parent;
+
+    /**
+     * The object containing data that should only exist once for each
+     * Digester instance.
+     */
+    private PluginContext pluginContext;
+
+    /** Construct a "root" PluginManager, ie one with no parent. */
+    public PluginManager(PluginContext r) {
+        pluginContext = r;
+    }
+
+    /** 
+     * Construct a "child" PluginManager. When declarations are added to
+     * a "child", they are stored within the child and do not modify the
+     * parent, so when the child goes out of scope, those declarations
+     * disappear. When asking a "child" to retrieve a declaration, it 
+     * delegates the search to its parent if it does not hold a matching
+     * entry itself.
+     * <p>
+     * @param parent must be non-null.
+     */
+    public PluginManager(PluginManager parent) {
+        this.parent = parent;
+        this.pluginContext = parent.pluginContext;
+    }
+
+    /**
+     * Add the declaration to the set of known declarations.
+     * <p>
+     * TODO: somehow get a reference to a Digester object
+     * so that we can really log here. Currently, all
+     * logging is disabled from this method.
+     *
+     *@param decl an object representing a plugin class.
+     */
+    public void addDeclaration(Declaration decl) {
+        Log log = LogUtils.getLogger(null);
+        boolean debug = log.isDebugEnabled();
+
+        Class<?> pluginClass = decl.getPluginClass();
+        String id = decl.getId();
+
+        declarationsByClass.put(pluginClass.getName(), decl);
+
+        if (id != null) {
+            declarationsById.put(id, decl);
+            if (debug) {
+                log.debug(
+                    "Indexing plugin-id [" + id + "]" +
+                    " -> class [" + pluginClass.getName() + "]");
+            }
+        }
+    }
+
+    /**
+     * Return the declaration object with the specified class.
+     * If no such plugin is known, null is returned.
+     */
+    public Declaration getDeclarationByClass(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(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).
+     * <p>
+     * If no source of custom rules can be found, null is returned.
+     */
+    public RuleLoader findLoader(Digester digester, String id, Class<?> pluginClass, Properties props) throws PluginException {
+        // iterate over the list of RuleFinders, trying each one 
+        // until one of them locates a source of dynamic rules given
+        // this specific plugin class and the associated declaration 
+        // properties.
+        Log log = LogUtils.getLogger(digester);
+        boolean debug = log.isDebugEnabled();
+        log.debug("scanning ruleFinders to locate loader..");
+
+        List<RuleFinder> ruleFinders = pluginContext.getRuleFinders();
+        RuleLoader ruleLoader = null;
+        try {
+            for (Iterator<RuleFinder> i = ruleFinders.iterator(); i.hasNext() && ruleLoader == null;) {
+                RuleFinder finder = i.next();
+                if (debug) {
+                    log.debug("checking finder of type " + finder.getClass().getName());
+                }
+                ruleLoader = finder.findLoader(digester, pluginClass, props);
+            }
+        }
+        catch(PluginException e) {
+            throw new PluginException(
+                "Unable to locate plugin rules for plugin"
+                + " with id [" + id + "]"
+                + ", and class [" + pluginClass.getName() + "]"
+                + ":" + e.getMessage(), e.getCause());
+        }
+        log.debug("scanned ruleFinders.");
+
+        return ruleLoader;
+    }
+
+}

Propchange: commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/PluginManager.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/PluginManager.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: commons/sandbox/digester3/trunk/src/main/java/org/apache/commons/digester3/plugins/PluginManager.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain