| /* |
| * The Apache Software License, Version 1.1 |
| * |
| * Copyright (c) 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 "Ant" 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 apache@apache.org. |
| * |
| * 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.tools.ant.types.selectors.modifiedselector; |
| |
| |
| // Java |
| import java.util.Comparator; |
| import java.util.Vector; |
| import java.util.Iterator; |
| import java.io.File; |
| |
| // Ant |
| import org.apache.tools.ant.Project; |
| import org.apache.tools.ant.IntrospectionHelper; |
| import org.apache.tools.ant.types.EnumeratedAttribute; |
| import org.apache.tools.ant.types.Parameter; |
| import org.apache.tools.ant.types.selectors.BaseExtendSelector; |
| |
| |
| /** |
| * <p>Selector class that uses <i>Algorithm</i>, <i>Cache</i> and <i>Comparator</i> |
| * for its work. |
| * The <i>Algorithm</i> is used for computing a hashvalue for a file. |
| * The <i>Comparator</i> decides whether to select or not. |
| * The <i>Cache</i> stores the other value for comparison by the <i>Comparator</i> |
| * in a persistent manner.</p> |
| * |
| * <p>The ModifiedSelector is implemented as a <b>CoreSelector</b> and uses default |
| * values for all its attributes therefore the simpliest example is <pre> |
| * <copy todir="dest"> |
| * <filelist dir="src"> |
| * <modified/> |
| * </filelist> |
| * </copy> |
| * </pre></p> |
| * |
| * <p>The same example rewritten as CoreSelector with setting the all values |
| * (same as defaults are) would be <pre> |
| * <copy todir="dest"> |
| * <filelist dir="src"> |
| * <modified update="true" |
| * cache="propertyfile" |
| * algorithm="digest" |
| * comparator="equal"> |
| * <param name="cache.cachefile" value="cache.properties"/> |
| * <param name="algorithm.algorithm" value="MD5"/> |
| * </modified> |
| * </filelist> |
| * </copy> |
| * </pre></p> |
| * |
| * <p>And the same rewritten as CustomSelector would be<pre> |
| * <copy todir="dest"> |
| * <filelist dir="src"> |
| * <custom class="org.apache.tools.ant.type.selectors.ModifiedSelector"> |
| * <param name="update" value="true"/> |
| * <param name="cache" value="propertyfile"/> |
| * <param name="algorithm" value="digest"/> |
| * <param name="comparator" value="equal"/> |
| * <param name="cache.cachefile" value="cache.properties"/> |
| * <param name="algorithm.algorithm" value="MD5"/> |
| * </custom> |
| * </filelist> |
| * </copy> |
| * </pre></p> |
| * |
| * <p>All these three examples copy the files from <i>src</i> to <i>dest</i> |
| * using the ModifiedSelector. The ModifiedSelector uses the <i>PropertyfileCache |
| * </i>, the <i>DigestAlgorithm</i> and the <i>EqualComparator</i> for its |
| * work. The PropertyfileCache stores key-value-pairs in a simple java |
| * properties file. The filename is <i>cache.properties</i>. The <i>update</i> |
| * flag lets the selector update the values in the cache (and on first call |
| * creates the cache). The <i>DigestAlgorithm</i> computes a hashvalue using the |
| * java.security.MessageDigest class with its MD5-Algorithm and its standard |
| * provider. The new computed hashvalue and the stored one are compared by |
| * the <i>EqualComparator</i> which returns 'true' (more correct a value not |
| * equals zero (1)) if the values are not the same using simple String |
| * comparison.</p> |
| * |
| * <p>A useful scenario for this selector is inside a build environment |
| * for homepage generation (e.g. with <a href="http://xml.apache.org/forrest/"> |
| * Apache Forrest</a>). <pre> |
| * <target name="generate-and-upload-site"> |
| * <echo> generate the site using forrest </echo> |
| * <antcall target="site"/> |
| * |
| * <echo> upload the changed files </echo> |
| * <ftp server="${ftp.server}" userid="${ftp.user}" password="${ftp.pwd}"> |
| * <fileset dir="htdocs/manual"> |
| * <modified/> |
| * </fileset> |
| * </ftp> |
| * </target> |
| * </pre> Here all <b>changed</b> files are uploaded to the server. The |
| * ModifiedSelector saves therefore much upload time.</p> |
| * |
| * <p>This selector supports the following nested param's: |
| * <table> |
| * <tr><th>name</th><th>values</th><th>description</th><th>required</th></tr> |
| * <tr> |
| * <td> cache </td> |
| * <td> propertyfile </td> |
| * <td> which cache implementation should be used <ul> |
| * <li><b>propertyfile</b> - using java.util.Properties </li> |
| * </td> |
| * <td> no, defaults to 'propertyfile' </td> |
| * </tr> |
| * <tr> |
| * <td> algorithm </td> |
| * <td> hashvalue | digest </td> |
| * <td> which algorithm implementation should be used |
| * <li><b>hashvalue</b> - loads the file content into a String and |
| * uses its hashValue() method </li> |
| * <li><b>digest</b> - uses java.security.MessageDigest class </i> |
| * </td> |
| * <td> no, defaults to digest </td> |
| * </tr> |
| * <tr> |
| * <td> comparator </td> |
| * <td> equal | role </td> |
| * <td> which comparator implementation should be used |
| * <li><b>equal</b> - simple comparison using String.equals() </li> |
| * <li><b>role</b> - uses java.text.RuleBasedCollator class </i> |
| * </td> |
| * <td> no, defaults to equal </td> |
| * </tr> |
| * <tr> |
| * <td> update </td> |
| * <td> true | false </td> |
| * <td> If set to <i>true</i>, the cache will be stored, otherwise the values |
| * will be lost. </td> |
| * <td> no, defaults to true </td> |
| * </tr> |
| * <tr> |
| * <td> seldirs </td> |
| * <td> true | false </td> |
| * <td> If set to <i>true</i>, directories will be selected otherwise not </td> |
| * <td> no, defaults to true </td> |
| * </tr> |
| * <tr> |
| * <td> cache.* </td> |
| * <td> depends on used cache </td> |
| * <td> value is stored and given to the Cache-Object for initialisation </td> |
| * <td> depends on used cache </td> |
| * </tr> |
| * <tr> |
| * <td> algorithm.* </td> |
| * <td> depends on used algorithm </td> |
| * <td> value is stored and given to the Algorithm-Object for initialisation </td> |
| * <td> depends on used algorithm </td> |
| * </tr> |
| * <tr> |
| * <td> comparator.* </td> |
| * <td> depends on used comparator </td> |
| * <td> value is stored and given to the Comparator-Object for initialisation </td> |
| * <td> depends on used comparator </td> |
| * </tr> |
| * </table> |
| * If another name is used a BuildException "Invalid parameter" is thrown. </p> |
| * |
| * <p>This selector uses reflection for setting the values of its three interfaces |
| * (using org.apache.tools.ant.IntrospectionHelper) therefore no special |
| * 'configuration interfaces' has to be implemented by new caches, algorithms or |
| * comparators. All present <i>set</i>XX methods can be used. E.g. the DigestAlgorithm |
| * can use a specified provider for computing its value. For selecting this |
| * there is a <i>setProvider(String providername)</i> method. So you can use |
| * a nested <i><param name="algorithm.provider" value="MyProvider"/></i>. |
| * |
| * |
| * @author Jan Mat\u00e8rne |
| * @version 2003-09-13 |
| * @since Ant 1.6 |
| */ |
| public class ModifiedSelector extends BaseExtendSelector { |
| |
| |
| // ----- member variables - configuration |
| |
| |
| /** The Cache containing the old values. */ |
| private Cache cache = null; |
| |
| /** Algorithm for computing new values and updating the cache. */ |
| private Algorithm algorithm = null; |
| |
| /** How should the cached value and the new one compared? */ |
| private Comparator comparator = null; |
| |
| /** Should the cache be updated? */ |
| private boolean update = true; |
| |
| /** Are directories selected? */ |
| private boolean selectDirectories = true; |
| |
| |
| // ----- member variables - internal use |
| |
| |
| /** Flag whether this object is configured. Configuration is only done once. */ |
| private boolean isConfigured = false; |
| |
| /** Algorithm name for later instantiation. */ |
| private AlgorithmName algoName = null; |
| |
| /** Cache name for later instantiation. */ |
| private CacheName cacheName = null; |
| |
| /** Comparator name for later instantiation. */ |
| private ComparatorName compName = null; |
| |
| |
| /** |
| * Parameter vector with parameters for later initialization. |
| * @see #configure |
| */ |
| private Vector configParameter = new Vector(); |
| |
| /** |
| * Parameter vector with special parameters for later initialization. |
| * The names have the pattern '*.*', e.g. 'cache.cachefile'. |
| * These parameters are used <b>after</b> the parameters with the pattern '*'. |
| * @see #configure |
| */ |
| private Vector specialParameter = new Vector(); |
| |
| |
| // ----- constructors ----- |
| |
| |
| /** Bean-Constructor. */ |
| public ModifiedSelector() { |
| } |
| |
| |
| // ----- configuration ----- |
| |
| |
| /** Overrides BaseSelector.verifySettings(). */ |
| public void verifySettings() { |
| configure(); |
| if (cache == null) { |
| setError("Cache must be set."); |
| } else if (algorithm == null) { |
| setError("Algorithm must be set."); |
| } else if (!cache.isValid()) { |
| setError("Cache must be proper configured."); |
| } else if (!algorithm.isValid()) { |
| setError("Algorithm must be proper configured."); |
| } |
| } |
| |
| |
| /** |
| * Configures this Selector. |
| * Does this work only once per Selector object. |
| * <p>Because some problems while configuring from <custom>Selector |
| * the configuration is done in the following order:<ol> |
| * <li> collect the configuration data </li> |
| * <li> wait for the first isSelected() call </li> |
| * <li> set the default values </li> |
| * <li> set values for name pattern '*': update, cache, algorithm, comparator </li> |
| * <li> set values for name pattern '*.*: cache.cachefile, ... </li> |
| * </ol></p> |
| * <p>This configuration algorithm is needed because you don't know |
| * the order of arriving config-data. E.g. if you first set the |
| * <i>cache.cachefilename</i> and after that the <i>cache</i> itself, |
| * the default value for cachefilename is used, because setting the |
| * cache implies creating a new Cache instance - with its defaults.</p> |
| */ |
| public void configure() { |
| // |
| // ----- The "Singleton" ----- |
| // |
| if (isConfigured) { |
| return; |
| } |
| isConfigured = true; |
| |
| // |
| // ----- Set default values ----- |
| // |
| org.apache.tools.ant.Project project = getProject(); |
| String filename = "cache.properties"; |
| File cachefile = null; |
| if (project != null) { |
| // normal use inside Ant |
| cachefile = new File(project.getBaseDir(), filename); |
| } else { |
| // no reference to project - e.g. during JUnit tests |
| cachefile = new File(filename); |
| } |
| cache = new PropertiesfileCache(cachefile); |
| algorithm = new DigestAlgorithm(); |
| comparator = new EqualComparator(); |
| update = true; |
| selectDirectories = true; |
| |
| |
| // |
| // ----- Set the main attributes, pattern '*' ----- |
| // |
| for (Iterator itConfig = configParameter.iterator(); itConfig.hasNext();) { |
| Parameter par = (Parameter) itConfig.next(); |
| if (par.getName().indexOf(".") > 0) { |
| // this is a *.* parameter for later use |
| specialParameter.add(par); |
| } else { |
| useParameter(par); |
| } |
| } |
| configParameter = new Vector(); |
| |
| // |
| // ----- Instantiate the interfaces ----- |
| // |
| String className = null; |
| String pkg = "org.apache.tools.ant.types.selectors.cacheselector"; |
| |
| // the algorithm |
| if (algorithm == null) { |
| if ("hashvalue".equals(algoName.getValue())) { |
| className = pkg + ".HashvalueAlgorithm"; |
| } else if ("digest".equals(algoName.getValue())) { |
| className = pkg + ".DigestAlgorithm"; |
| } |
| if (className != null) { |
| try { |
| // load the specified Algorithm, save the reference and configure it |
| algorithm = (Algorithm) Class.forName(className).newInstance(); |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } |
| } |
| } |
| |
| // the cache |
| if (cache == null) { |
| if ("propertyfile".equals(cacheName.getValue())) { |
| className = pkg + ".PropertiesfileCache"; |
| } |
| if (className != null) { |
| try { |
| // load the specified Cache, save the reference and configure it |
| cache = (Cache) Class.forName(className).newInstance(); |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } |
| } |
| } |
| |
| // the comparator |
| if (comparator == null) { |
| if ("equal".equals(compName.getValue())) { |
| className = pkg + ".EqualComparator"; |
| } else if ("role".equals(compName.getValue())) { |
| className = "java.text.RuleBasedCollator"; |
| } |
| if (className != null) { |
| try { |
| // load the specified Cache, save the reference and configure it |
| comparator = (Comparator) Class.forName(className).newInstance(); |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } |
| } |
| } |
| |
| // |
| // ----- Set the special attributes, pattern '*.*' ----- |
| // |
| for (Iterator itSpecial = specialParameter.iterator(); itSpecial.hasNext();) { |
| Parameter par = (Parameter) itSpecial.next(); |
| useParameter(par); |
| } |
| specialParameter = new Vector(); |
| } |
| |
| |
| // ----- the selection work ----- |
| |
| |
| /** |
| * Implementation of BaseExtendSelector.isSelected(). |
| * @param basedir as described in BaseExtendSelector |
| * @param filename as described in BaseExtendSelector |
| * @param file as described in BaseExtendSelector |
| * @return as described in BaseExtendSelector |
| */ |
| public boolean isSelected(File basedir, String filename, File file) { |
| validate(); |
| File f = new File(basedir, filename); |
| |
| // You can not compute a value for a directory |
| if (f.isDirectory()) { |
| return selectDirectories; |
| } |
| |
| // Get the values and do the comparison |
| String cachedValue = String.valueOf(cache.get(f.getAbsolutePath())); |
| String newValue = algorithm.getValue(f); |
| boolean rv = (comparator.compare(cachedValue, newValue) != 0); |
| |
| // Maybe update the cache |
| if (update && !cachedValue.equals(newValue)) { |
| cache.put(f.getAbsolutePath(), newValue); |
| cache.save(); |
| } |
| |
| return rv; |
| } |
| |
| |
| // ----- attribute and nested element support ----- |
| |
| |
| /** |
| * Support for <i>update</i> attribute. |
| * @param update new value |
| */ |
| public void setUpdate(boolean update) { |
| this.update = update; |
| } |
| |
| |
| /** |
| * Support for <i>seldirs</i> attribute. |
| * @param seldirs new value |
| */ |
| public void setSeldirs(boolean seldirs) { |
| selectDirectories = seldirs; |
| } |
| |
| |
| /** |
| * Support for nested <param> tags. |
| * @param key the key of the parameter |
| * @param value the value of the parameter |
| */ |
| public void addParam(String key, Object value) { |
| Parameter par = new Parameter(); |
| par.setName(key); |
| par.setValue(String.valueOf(value)); |
| configParameter.add(par); |
| } |
| |
| |
| /** |
| * Support for nested <param> tags. |
| * @param parameter the parameter object |
| */ |
| public void addParam(Parameter parameter) { |
| configParameter.add(parameter); |
| } |
| |
| |
| /** |
| * Defined in org.apache.tools.ant.types.Parameterizable. |
| * Overwrite implementation in superclass because only special |
| * parameters are valid. |
| * @see #addParam(String,String). |
| */ |
| public void setParameters(Parameter[] parameters) { |
| if (parameters != null) { |
| for (int i = 0; i < parameters.length; i++) { |
| configParameter.add(parameters[i]); |
| } |
| } |
| } |
| |
| |
| /** |
| * Support for nested <param name="" value=""/> tags. |
| * Parameter named <i>cache</i>, <i>algorithm</i>, |
| * <i>comparator</i> or <i>update</i> are mapped to |
| * the respective set-Method. |
| * Parameter which names starts with <i>cache.</i> or |
| * <i>algorithm.</i> or <i>comparator.</i> are tried |
| * to set on the appropriate object via its set-methods. |
| * Other parameters are invalid and an BuildException will |
| * be thrown. |
| * |
| * @param parameter Key and value as parameter object |
| */ |
| public void useParameter(Parameter parameter) { |
| String key = parameter.getName(); |
| String value = parameter.getValue(); |
| if ("cache".equals(key)) { |
| CacheName cn = new CacheName(); |
| cn.setValue(value); |
| setCache(cn); |
| } else if ("algorithm".equals(key)) { |
| AlgorithmName an = new AlgorithmName(); |
| an.setValue(value); |
| setAlgorithm(an); |
| } else if ("comparator".equals(key)) { |
| ComparatorName cn = new ComparatorName(); |
| cn.setValue(value); |
| setComparator(cn); |
| } else if ("update".equals(key)) { |
| boolean updateValue = |
| ("true".equalsIgnoreCase(value)) |
| ? true |
| : false; |
| setUpdate(updateValue); |
| } else if ("seldirs".equals(key)) { |
| boolean sdValue = |
| ("true".equalsIgnoreCase(value)) |
| ? true |
| : false; |
| setSeldirs(sdValue); |
| } else if (key.startsWith("cache.")) { |
| String name = key.substring(6); |
| tryToSetAParameter(cache, name, value); |
| } else if (key.startsWith("algorithm.")) { |
| String name = key.substring(10); |
| tryToSetAParameter(algorithm, name, value); |
| } else if (key.startsWith("comparator.")) { |
| String name = key.substring(11); |
| tryToSetAParameter(comparator, name, value); |
| } else { |
| setError("Invalid parameter " + key); |
| } |
| } |
| |
| |
| /** |
| * Try to set a value on an object using reflection. |
| * Helper method for easier access to IntrospectionHelper.setAttribute(). |
| * @param obj the object on which the attribute should be set |
| * @param name the attributename |
| * @param value the new value |
| */ |
| protected void tryToSetAParameter(Object obj, String name, String value) { |
| Project prj = (getProject() != null) ? getProject() : new Project(); |
| IntrospectionHelper iHelper |
| = IntrospectionHelper.getHelper(prj, obj.getClass()); |
| |
| try { |
| iHelper.setAttribute(prj, obj, name, value); |
| } catch (org.apache.tools.ant.BuildException e) { |
| // no-op |
| } |
| } |
| |
| |
| // ----- 'beautiful' output ----- |
| |
| |
| /** |
| * Override Object.toString(). |
| * @return information about this selector |
| */ |
| public String toString() { |
| StringBuffer buf = new StringBuffer("{modifiedselector"); |
| buf.append(" update=").append(update); |
| buf.append(" seldirs=").append(selectDirectories); |
| buf.append(" cache=").append(cache); |
| buf.append(" algorithm=").append(algorithm); |
| buf.append(" comparator=").append(comparator); |
| buf.append("}"); |
| return buf.toString(); |
| } |
| |
| |
| // The EnumeratedAttributes for the three interface implementations. |
| // Name-Classname mapping is done in the configure() method. |
| |
| |
| public Cache getCache() { return cache; } |
| public void setCache(CacheName name) { |
| cacheName = name; |
| } |
| public static class CacheName extends EnumeratedAttribute { |
| public String[] getValues() { |
| return new String[] {"propertyfile" }; |
| } |
| } |
| |
| |
| public Algorithm getAlgorithm() { return algorithm; } |
| public void setAlgorithm(AlgorithmName name) { |
| algoName = name; |
| } |
| public static class AlgorithmName extends EnumeratedAttribute { |
| public String[] getValues() { |
| return new String[] {"hashvalue", "digest" }; |
| } |
| } |
| |
| |
| public Comparator getComparator() { return comparator; } |
| public void setComparator(ComparatorName name) { |
| compName = name; |
| } |
| public static class ComparatorName extends EnumeratedAttribute { |
| public String[] getValues() { |
| return new String[] {"equal", "rule" }; |
| } |
| } |
| |
| } |