rdonkin 2003/10/04 05:28:44 Added: digester/src/java/org/apache/commons/digester/plugins PluginCreateRule.java Log: Added plugins module. Submitted by Simon Kitching. Revision Changes Path 1.1 jakarta-commons/digester/src/java/org/apache/commons/digester/plugins/PluginCreateRule.java Index: PluginCreateRule.java =================================================================== /* * ==================================================================== * * The Apache Software License, Version 1.1 * * Copyright (c) 1999-2003 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, if * any, must include the following acknowlegement: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowlegement may appear in the software itself, * if and wherever such third-party acknowlegements normally appear. * * 4. The names "The Jakarta Project", "Commons", and "Apache Software * Foundation" must not be used to endorse or promote products derived * from this software without prior written permission. For written * permission, please contact [EMAIL PROTECTED] * * 5. Products derived from this software may not be called "Apache" * nor may "Apache" appear in their names without prior written * permission of the Apache Group. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. * */ package org.apache.commons.digester.plugins; import java.util.Iterator; import java.util.ListIterator; import java.util.List; import java.io.File; import org.apache.commons.digester.Digester; import org.apache.commons.digester.Rule; import org.apache.commons.digester.Rules; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * 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. * * @author Simon Kitching */ public class PluginCreateRule extends Rule implements InitializableRule { private static Log log = LogFactory.getLog(PluginCreateRule.class); private static final String PLUGIN_CLASS_ATTR = "plugin-class"; private static final String PLUGIN_ID_ATTR = "plugin-id"; /** * 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_; /** * Our private set of rules associated with the concrete class that * the user requested to be instantiated. This object is only valid * between a call to begin() and the corresponding call to end(). */ private PluginRules localRules_; //-------------------- 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(Class baseClass) { super(); 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) { super(); baseClass_ = baseClass; if (dfltPluginClass != null) { defaultPlugin_ = new Declaration(dfltPluginClass); } } //------------------- properties --------------------------------------- public void setDefaultRuleMethod(String dfltPluginRuleMethod) { if (defaultPlugin_ != null) { defaultPlugin_.setRuleMethod(dfltPluginRuleMethod); } } public void setDefaultRuleClass(Class dfltPluginRuleClass) { if (defaultPlugin_ != null) { defaultPlugin_.setRuleClass(dfltPluginRuleClass); } } public void setDefaultRuleResource(String dfltPluginRuleResource) { if (defaultPlugin_ != null) { defaultPlugin_.setRuleResource(dfltPluginRuleResource); } } public void setDefaultRuleFile(String dfltPluginRuleFile) { if (defaultPlugin_ != null) { defaultPlugin_.setRuleFile(new File(dfltPluginRuleFile)); } } public void setDefaultRuleAutoSetProperties(boolean enabled) { if (defaultPlugin_ != null) { defaultPlugin_.setAutoSetProperties(enabled); } } //------------------- methods -------------------------------------------- /** * 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 pattern is the digester match pattern that is associated with * this rule instance, eg "root/widget". * @exception PluginConfigurationException */ public void postRegisterInit(String pattern) throws PluginConfigurationException { log.debug("PluginCreateRule.postRegisterInit" + ": rule registered for pattern [" + pattern + "]"); if (digester == 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 (pattern.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 [" + pattern + "]." + " This pattern includes a wildcard character." + " This is not supported by the plugin architecture."); throw initException_; } if (baseClass_ == null) { baseClass_ = Object.class; } // 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(digester); } catch(PluginWrappedException pwe) { throw new PluginConfigurationException( pwe.getMessage(), pwe.getCause()); } } // remember the pattern for later pattern_ = pattern; } /** * 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. * <p> * Because a PluginCreateRule is also a Delegate, this method is also * called on the start of any element occurring below the pattern * associated with this rule. In this case, this method acts like the * Digester's startElement method: it fires the begin() method of every * custom rule associated with the plugin class that matches that pattern. * See [EMAIL PROTECTED] #delegateBegin}. * * @param namespace * @param name * @param attributes * * @throws ClassNotFoundException * @throws PluginInvalidInputException * @throws PluginConfigurationException */ public void begin( String namespace, String name, org.xml.sax.Attributes attributes) throws java.lang.Exception { if (log.isDebugEnabled()) { log.debug("PluginCreateRule.begin" + ": pattern=[" + pattern_ + "]" + " match=[" + digester.getMatch() + "]"); } if (initException_ != null) { // we had a problem during initialisation that we could // not report then; report it now. throw initException_; } String currMatch = digester.getMatch(); if (currMatch.length() == pattern_.length()) { // ok here we are actually instantiating a new plugin object, // and storing its rules into a new Rules object if (localRules_ != null) { throw new PluginAssertionError( "Begin called when localRules_ is not null."); } PluginRules oldRules = (PluginRules) digester.getRules(); localRules_ = new PluginRules(this, oldRules); PluginManager pluginManager = localRules_.getPluginManager(); Declaration currDeclaration = null; if (log.isDebugEnabled()) { log.debug("PluginCreateRule.begin: installing new plugin: " + "oldrules=" + oldRules.toString() + ", localrules=" + localRules_.toString()); } String pluginClassName = attributes.getValue(PLUGIN_CLASS_ATTR); String pluginId = attributes.getValue(PLUGIN_ID_ATTR); if (pluginClassName != null) { currDeclaration = pluginManager.getDeclarationByClass( pluginClassName); if (currDeclaration == null) { currDeclaration = new Declaration(pluginClassName); try { currDeclaration.init(digester); } catch(PluginWrappedException pwe) { throw new PluginInvalidInputException( pwe.getMessage(), pwe.getCause()); } pluginManager.addDeclaration(currDeclaration); } } else if (pluginId != null) { currDeclaration = pluginManager.getDeclarationById(pluginId); if (currDeclaration == null) { throw new Exception( "Plugin id [" + pluginId + "] is not defined."); } } else if (defaultPlugin_ != null) { currDeclaration = defaultPlugin_; } else { throw new PluginInvalidInputException( "No plugin class specified for element " + pattern_); } // now load up the custom rules into a private Rules instance digester.setRules(localRules_); { currDeclaration.configure(digester, pattern_); Class pluginClass = currDeclaration.getPluginClass(); Object instance = pluginClass.newInstance(); getDigester().push(instance); if (log.isDebugEnabled()) { log.debug( "PluginCreateRule.begin" + ": pattern=[" + pattern_ + "]" + " match=[" + digester.getMatch() + "]" + " pushed instance of plugin [" + pluginClass.getName() + "]"); } } digester.setRules(oldRules); ((PluginRules) oldRules).beginPlugin(this); } // fire the begin method of all custom rules Rules oldRules = digester.getRules(); if (log.isDebugEnabled()) { log.debug("PluginCreateRule.begin: firing nested rules: " + "oldrules=" + oldRules.toString() + ", localrules=" + localRules_.toString()); } // assert oldRules = localRules_.oldRules digester.setRules(localRules_); delegateBegin(namespace, name, attributes); digester.setRules(oldRules); if (log.isDebugEnabled()) { log.debug("PluginCreateRule.begin: restored old rules to " + "oldrules=" + oldRules.toString()); } } /** * Invoked by the digester when the closing tag matching this Rule's * pattern is encountered. See [EMAIL PROTECTED] #delegateBody}. * * @see #begin */ public void body(String namespace, String name, String text) throws Exception { Rules oldRules = digester.getRules(); // assert oldRules == localRules_.oldRules digester.setRules(localRules_); delegateBody(namespace, name, text); digester.setRules(oldRules); } /** * Invoked by the digester when the closing tag matching this Rule's * pattern is encountered. * </p> * As noted on method begin, because PluginCreateRule is a Delegate, * this method is also called at the end tag of every pattern that * is "below" the pattern associated with this rule. In this case, we * fire the end method of every custom rule associated with the * current plugin class. See [EMAIL PROTECTED] #delegateEnd}. * <p> * If we are really encountering the end tag associated with this rule * (rather than the end of an element "below" that tag), then we * remove the object we pushed onto the digester stack when the * opening tag was encountered. * * @param namespace Description of the Parameter * @param name Description of the Parameter * @exception Exception Description of the Exception * * @see #begin */ public void end(String namespace, String name) throws Exception { Rules oldRules = digester.getRules(); // assert oldRules == localRules_.parentRules digester.setRules(localRules_); delegateEnd(namespace, name); digester.setRules(oldRules); String currMatch = digester.getMatch(); if (currMatch.length() == pattern_.length()) { // the end of the element on which the PluginCreateRule has // been mounted has been reached. localRules_ = null; ((PluginRules) oldRules).endPlugin(this); digester.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_; } /** * Here we act like Digester.begin, finding a match for the pattern * in our private rules object, then executing the begin method of * each matching rule. */ public void delegateBegin( String namespace, String name, org.xml.sax.Attributes attributes) throws java.lang.Exception { // Fire "begin" events for all relevant rules boolean debug = log.isDebugEnabled(); String match = digester.getMatch(); List rules = digester.getRules().match(namespace, match); Iterator ri = rules.iterator(); while (ri.hasNext()) { Rule rule = (Rule) ri.next(); if (debug) { log.debug(" Fire begin() for " + rule); } rule.begin(namespace, name, attributes); } } /** * Here we act like Digester.body, except against our private rules. */ public void delegateBody(String namespace, String name, String text) throws Exception { // Fire "body" events for all relevant rules boolean debug = log.isDebugEnabled(); String match = digester.getMatch(); List rules = digester.getRules().match(namespace, match); Iterator ri = rules.iterator(); while (ri.hasNext()) { Rule rule = (Rule) ri.next(); if (debug) { log.debug(" Fire body() for " + rule); } rule.body(namespace, name, text); } } /** * Here we act like Digester.end. */ public void delegateEnd(String namespace, String name) throws Exception { // Fire "end" events for all relevant rules in reverse order boolean debug = log.isDebugEnabled(); String match = digester.getMatch(); List rules = digester.getRules().match(namespace, match); ListIterator ri = rules.listIterator(); while (ri.hasNext()) { ri.next(); } while (ri.hasPrevious()) { Rule rule = (Rule) ri.previous(); if (debug) { log.debug(" Fire end() for " + rule); } rule.end(namespace, name); } } }
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]