skitching 2004/03/27 21:51:43 Modified: digester/src/java/org/apache/commons/digester/plugins Tag: DIGESTER_PLUGIN_REFACTORING_BRANCH Declaration.java Log: * store declaration attributes provided by the user as a Properties object rather than explicit Object attributes. This allows the set of attributes to be open-ended, and removes use of hard-wired English attribute names. * use new RuleFinder/RuleLoader interfaces to find "pluggable" algorithms for finding/loading dynamic plugin rules rather than hard-coding the algorithms in this class. * move rule finding/loading algorithms to separate classes in the strategies subpackage. Revision Changes Path No revision No revision 1.11.2.1 +87 -271 jakarta-commons/digester/src/java/org/apache/commons/digester/plugins/Declaration.java Index: Declaration.java =================================================================== RCS file: /home/cvs/jakarta-commons/digester/src/java/org/apache/commons/digester/plugins/Declaration.java,v retrieving revision 1.11 retrieving revision 1.11.2.1 diff -u -r1.11 -r1.11.2.1 --- Declaration.java 29 Feb 2004 02:22:15 -0000 1.11 +++ Declaration.java 28 Mar 2004 05:51:43 -0000 1.11.2.1 @@ -15,27 +15,19 @@ */ package org.apache.commons.digester.plugins; -import java.io.File; -import java.io.FileInputStream; -import java.io.InputStream; import java.io.IOException; -import java.lang.reflect.Method; +import java.util.Properties; +import java.util.List; -import org.apache.commons.beanutils.MethodUtils; -import org.apache.commons.digester.Digester; import org.apache.commons.logging.Log; +import org.apache.commons.digester.Digester; /** - * Simple structure to store the set of attributes that can be present on - * a plugin declaration. + * 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 name of the method looked for on the plugin class and any - * specific rule class. - */ - public final static String DFLT_RULE_METHOD_NAME = "addRules"; /** The class of the object to be instantiated. */ private Class pluginClass; @@ -46,89 +38,66 @@ /** See [EMAIL PROTECTED] #setId}. */ private String id; - /** See [EMAIL PROTECTED] #setRuleMethod}. */ - private String ruleMethodName = DFLT_RULE_METHOD_NAME; - - /** See [EMAIL PROTECTED] #setRuleClass}. */ - private Class ruleClass; - - /** See [EMAIL PROTECTED] #setRuleResource}. */ - private String ruleResource; - - /** See [EMAIL PROTECTED] #setRuleFile}. */ - private File ruleFile; + /** See [EMAIL PROTECTED] #setProperties}. */ + private Properties properties = new Properties(); - /** See [EMAIL PROTECTED] #setAutoSetProperties}. */ - private boolean autoSetProperties = true; - /** See [EMAIL PROTECTED] #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 = pluginClass; this.pluginClassName = pluginClass.getName(); } /** - * Constructor. + * 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(String pluginClassName) { - this.pluginClassName = pluginClassName; + public Declaration(Class pluginClass, RuleLoader ruleLoader) { + this.pluginClass = pluginClass; + this.pluginClassName = pluginClass.getName(); + this.ruleLoader = ruleLoader; } //---------------------- properties ----------------------------------- /** - * The id of the object defined in a plugin declaration. + * 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; } - /** - * Sets the name of a method which defines custom rules. May be null. - */ - public void setRuleMethod(String ruleMethodName) { - this.ruleMethodName = ruleMethodName; - } - - /** - * The name of a class containing a method which defines custom rules - * for the plugin class. May be null. - */ - public void setRuleClass(Class ruleClass) { - this.ruleClass = ruleClass; - } - - /** - * The name of a resource file in the classpath containg xmlrules - * specifications of custom rules for the plugin class. May be null. - */ - public void setRuleResource(String ruleResource) { - this.ruleResource = ruleResource; - } - - /** - * The name of a file containg xmlrules specifications of custom rules - * for the plugin class. May be null. - */ - public void setRuleFile(File ruleFile) { - this.ruleFile = ruleFile; - } - - /** See [EMAIL PROTECTED] #autoSetProperties}. */ - public void setAutoSetProperties(boolean autoSetProperties) { - this.autoSetProperties = autoSetProperties; - } - /** - * Return the id associated with this declaration. + * Return the id associated with this declaration. For plugins + * declared "inline", null will be returned. * * @return The id value. May be null. */ @@ -136,6 +105,23 @@ 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. * @@ -145,36 +131,13 @@ return pluginClass; } - /** - * return class which specifies custom rules for this plugin. - * - * @return The ruleClass value. May be null. - */ - public Class getRuleClass() { - return ruleClass; - } - - /** - * Indicates whether plugins which do <i>not</i> implement custom rules - * should have a SetProperties rule automatically associated with the - * parent tag. In almost all cases this is desirable, so autoSetProperties - * defaults to true. If for some reason you are plugging in a class - * without custom rules and you do not want xml attributes to be mapped - * to bean properties, you can pass <i>false</i> here to disable this. - * - * @return true if SetPropertiesRule is automatically applied. - */ - public boolean autoSetProperties() { - return autoSetProperties; - } - //---------------------- methods ----------------------------------- /** * Must be called exactly once, and must be called before any call * to the configure method. */ - public void init(Digester digester) throws PluginWrappedException { + public void init(Digester digester, PluginManager pm) throws PluginException { Log log = digester.getLogger(); boolean debug = log.isDebugEnabled(); if (debug) { @@ -191,10 +154,35 @@ pluginClass = digester.getClassLoader().loadClass(pluginClassName); } catch(ClassNotFoundException cnfe) { - throw new PluginWrappedException( + 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; } @@ -202,49 +190,13 @@ * Attempt to load custom rules for the target class at the specified * pattern. * <p> - * <ol> - * <li>If there is an explicit File, load from that file.</li> - * <li>If there is an explicit Resource, load from that resource.</li> - * <li>If there is an explicit RuleClass, load from that class.</li> - * <li>If there is an explicit RuleMethod, load from that method.</li> - * <li>If there is a default method, load from that method.</li> - * <li>If there is a default RuleInfo class, load from that class.</li> - * <li>If there is a default resource, load from that resource.</li> - * </ol> - * <p> - * When loading from a File or Resource (a file in the classpath), the - * contents of the file are expected to be xml in xmlrules format. - * <p> - * When loading from a RuleClass, that class is expected to have a - * method with the signature <code>public static void addRules(Digester, - * String)</code>. - * <p> - * When loading from a specified Method on the plugin class, that method - * is expected to have signature <code> public static void xxx(Digester, - * String)</code> where xxx is the specified method name. - * <p> - * When loading from the default method on the plugin class, the method - * is expected to have signature <code>public static void addRules(Digester, - * String)</code>. - * <p> - * When looking for a default RuleInfo class, the plugin class name has - * the suffix "RuleInfo" applied to it. If there exists a class of that - * name, then that class is expected to have an addRules method on it. - * <p> - * When looking for a default resource file, the plugin class name has - * the suffix "RuleInfo.xml" applied to it. If there exists a resource - * file of that name, then that file is expected to contain xmlrules - * format rules. - * <p> - * The first source of rules found is used, and searching stops. - * <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 PluginWrappedException { + throws PluginException { Log log = digester.getLogger(); boolean debug = log.isDebugEnabled(); if (debug) { @@ -254,160 +206,24 @@ if (!initialized) { throw new PluginAssertionFailure("Not initialized."); } - - // load from explicit file - if (ruleFile != null) { - InputStream is = null; - try { - is = new FileInputStream(ruleFile); - } catch(IOException ioe) { - throw new PluginWrappedException( - "Unable to process file [" + ruleFile + "]", ioe); - } - loadRulesFromStream(is, digester, pattern); - return; - } - - // load from explicit resource in classpath - if (ruleResource != null) { - InputStream is = - pluginClass.getClassLoader().getResourceAsStream( - ruleResource); - if (is != null) { - loadRulesFromStream(is, digester, pattern); - return; - } - } - - // load via method on explicit Rule Class - if (ruleClass != null) { - loadRulesFromClass(ruleClass, digester, pattern); - return; - } - - // load via method on plugin class - { - Class[] paramSpec = { Digester.class, String.class }; - Method ruleMethod = MethodUtils.getAccessibleMethod( - pluginClass, ruleMethodName, paramSpec); - if (ruleMethod != null) - { - try { - Object[] params = {digester, pattern}; - Object none = ruleMethod.invoke(null, params); - } catch (Exception e) { - throw new PluginWrappedException( - "Unable to configure class [" + pluginClass + "]" + - " using method [" + ruleMethodName + "]", e); - } - return; - } - } - - // look for rule class - { - if (debug) { - log.debug("plugin class type:" + pluginClass.getName()); - } - String ruleClassName = pluginClass.getName() + "RuleInfo"; - - Class ruleClass; - try { - ruleClass = digester.getClassLoader().loadClass(ruleClassName); - } catch(ClassNotFoundException cnfe) { - ruleClass = null; - } - if (ruleClass != null) { - loadRulesFromClass(ruleClass, digester, pattern); - return; - } - } - - // look for resource - { - String resourceName = - pluginClass.getClass().getName().replace('.', '/') + - "RuleInfo.xml"; - InputStream is = - pluginClass.getClassLoader().getResourceAsStream( - resourceName); - if (is != null) { - loadRulesFromStream(is, digester, pattern); - return; - } - } - - // try autoSetProperties - if (autoSetProperties) { - if (debug) { - log.debug("adding autoset for pattern [" + pattern + "]"); - } - digester.addSetProperties(pattern); + if (ruleLoader != null) { + ruleLoader.addRules(digester, pattern); } } /** - * Load custom rules from a specified stream of xml data. - */ - private void loadRulesFromStream(InputStream is, Digester digester, - String pattern) - throws PluginWrappedException { - try - { - throw new PluginAssertionFailure( - "Load from stream not yet supported."); - } - finally { - try { - is.close(); - } catch(IOException ioe) { - Log log = digester.getLogger(); - log.warn("Unable to close stream after reading rules", ioe); - } - } - } - - /** - * Load custom rules from a specified class. - */ - private void loadRulesFromClass(Class ruleClass, Digester digester, - String pattern) - throws PluginWrappedException { - Class[] paramSpec = { Digester.class, String.class }; - Method ruleMethod = MethodUtils.getAccessibleMethod( - ruleClass, ruleMethodName, paramSpec); - if (ruleMethod == null) { - throw new PluginWrappedException( - "rule class specified, but rules method not found on it."); - } - try { - Object[] params = {digester, pattern}; - Object none = ruleMethod.invoke(null, params); - } catch (Exception e) { - throw new PluginWrappedException( - "Unable to configure class [" + pluginClass + "]" + - " using rule class [" + ruleClass + "]" + - " method [" + ruleMethodName + "]", e); - } - } - - /** * Returns true if the declarations are equivalent. Perhaps this would be * better as overriding equals, but then I should really override hash as * well and I can't be bothered. - * + * * @param d the Declaration object to be compared to this object. * @return true if the specified object has the same options as this one. */ public boolean isEquivalent(Declaration d) { if (different(id, d.id)) return false; if (pluginClass != d.pluginClass) return false; - if (different(ruleMethodName, d.ruleMethodName)) return false; - if (ruleClass != d.ruleClass) return false; - if (different(ruleResource, d.ruleResource)) return false; - if (different(ruleFile, d.ruleFile)) return false; - if (autoSetProperties != d.autoSetProperties) return false; + if (!properties.equals(d.properties)) return false; // all significant fields match; these declarations are identical. return true;
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]