rdonkin 2003/10/04 05:26:51 Modified: digester build.xml Added: digester/src/java/org/apache/commons/digester/plugins Declaration.java Log: Added plugins module. Submitted by Simon Kitching.src/java/org/apache/commons/digester/plugins/Declaration.java Revision Changes Path 1.47 +13 -3 jakarta-commons/digester/build.xml Index: build.xml =================================================================== RCS file: /home/cvs/jakarta-commons/digester/build.xml,v retrieving revision 1.46 retrieving revision 1.47 diff -u -r1.46 -r1.47 --- build.xml 28 Sep 2003 09:41:29 -0000 1.46 +++ build.xml 4 Oct 2003 12:26:51 -0000 1.47 @@ -263,7 +263,8 @@ test.node, test.factory, test.regex, - test.wdrules + test.wdrules, + test.plugins " description="Run all unit test cases"> </target> @@ -412,5 +413,14 @@ <classpath refid="test.classpath"/> </java> </target> - + + <target name="test.plugins" depends="compile.tests" + description="Run Plugins tests ..."> + <echo message="Running Plugins tests ..."/> + <java classname="${test.runner}" fork="yes" + failonerror="${test.failonerror}"> + <arg value="org.apache.commons.digester.plugins.TestAll"/> + <classpath refid="test.classpath"/> + </java> + </target> </project> 1.1 jakarta-commons/digester/src/java/org/apache/commons/digester/plugins/Declaration.java Index: Declaration.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.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.io.IOException; import java.lang.reflect.Method; import org.apache.commons.beanutils.MethodUtils; import org.apache.commons.digester.Digester; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * Simple structure to store the set of attributes that can be present on * a plugin declaration. * * @author Simon Kitching */ public class Declaration { private static Log log = LogFactory.getLog(Declaration.class); /** * 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_; /** The name of the class of the object to be instantiated. */ private String pluginClassName_; /** 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] #setAutoSetProperties}. */ private boolean autoSetProperties_ = true; /** See [EMAIL PROTECTED] #init}. */ private boolean initialised_ = false; //---------------------- constructors ---------------------------------- /** * Constructor. */ public Declaration(Class pluginClass) { pluginClass_ = pluginClass; pluginClassName_ = pluginClass_.getName(); } /** * Constructor. */ public Declaration(String pluginClassName) throws ClassNotFoundException { pluginClassName_ = pluginClassName; } //---------------------- properties ----------------------------------- /** * The id of the object defined in a plugin declaration. * For plugins declared "in-line", the id is null. */ public void setId(String id) { id_ = id; } /** * Sets the name of a method which defines custom rules. May be null. */ public void setRuleMethod(String ruleMethodName) { 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) { 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) { 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) { ruleFile_ = ruleFile; } /** See [EMAIL PROTECTED] #autoSetProperties}. */ public void setAutoSetProperties(boolean autoSetProperties) { autoSetProperties_ = autoSetProperties; } /** * Return the id associated with this declaration. * * @return The id value. May be null. */ public String getId() { return id_; } /** * Return plugin class associated with this declaration. * * @return The pluginClass. */ public Class getPluginClass() { 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 { log.debug("init being called!"); if (initialised_) { throw new PluginAssertionError("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 PluginWrappedException( "Unable to load class " + pluginClassName_, cnfe); } } initialised_ = true; } /** * 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 { log.debug("configure being called!"); if (!initialised_) { throw new PluginAssertionError("Not initialised."); } // 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 (log.isDebugEnabled()) { 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 (log.isDebugEnabled()) { log.debug("adding autoset for pattern [" + pattern + "]"); } digester.addSetProperties(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 PluginAssertionError( "Load from stream not yet supported."); } finally { try { is.close(); } catch(IOException ioe) { 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; // all significant fields match; these declarations are identical. return true; } /** * Returns true if the two objects are both null or both equal. */ private static boolean different(Object o1, Object o2) { if (o1 == null) { return o1 == null; } return o1.equals(o2); } }
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]