| /* |
| * 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 |
| * |
| * https://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.tools.ant.types.selectors.modifiedselector; |
| |
| |
| // Java |
| import java.io.File; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.List; |
| |
| import org.apache.tools.ant.BuildEvent; |
| import org.apache.tools.ant.BuildException; |
| import org.apache.tools.ant.BuildListener; |
| import org.apache.tools.ant.IntrospectionHelper; |
| // Ant |
| import org.apache.tools.ant.Project; |
| import org.apache.tools.ant.types.EnumeratedAttribute; |
| import org.apache.tools.ant.types.Parameter; |
| import org.apache.tools.ant.types.Path; |
| import org.apache.tools.ant.types.Resource; |
| import org.apache.tools.ant.types.resources.FileResource; |
| import org.apache.tools.ant.types.resources.selectors.ResourceSelector; |
| import org.apache.tools.ant.types.selectors.BaseExtendSelector; |
| import org.apache.tools.ant.util.FileUtils; |
| import org.apache.tools.ant.util.ResourceUtils; |
| |
| |
| /** |
| * <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 simplest example is</p><pre> |
| * <copy todir="dest"> |
| * <filelist dir="src"> |
| * <modified/> |
| * </filelist> |
| * </copy> |
| * </pre> |
| * |
| * <p>The same example rewritten as CoreSelector with setting the all values |
| * (same as defaults are) would be</p><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>And the same rewritten as CustomSelector would be</p><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>If you want to provide your own interface implementation you can do |
| * that via the *classname attributes. If the classes are not on Ant's core |
| * classpath, you will have to provide the path via nested <classpath> |
| * element, so that the selector can find the classes.</p><pre> |
| * <modified cacheclassname="com.mycompany.MyCache"> |
| * <classpath> |
| * <pathelement location="lib/mycompany-antutil.jar"/> |
| * </classpath> |
| * </modified> |
| * </pre> |
| * |
| * <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="https://forrest.apache.org/"> |
| * Apache Forrest</a>).</p><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><p>Here all <b>changed</b> files are uploaded to the server. The |
| * ModifiedSelector saves therefore much upload time.</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>. |
| * |
| * |
| * @since Ant 1.6 |
| */ |
| public class ModifiedSelector extends BaseExtendSelector |
| implements BuildListener, ResourceSelector { |
| |
| private static final String CACHE_PREFIX = "cache."; |
| private static final String ALGORITHM_PREFIX = "algorithm."; |
| private static final String COMPARATOR_PREFIX = "comparator."; |
| |
| |
| // ----- attributes ----- |
| |
| |
| /** Cache name for later instantiation. */ |
| private CacheName cacheName = null; |
| |
| /** User specified classname for Cache. */ |
| private String cacheClass; |
| |
| /** Algorithm name for later instantiation. */ |
| private AlgorithmName algoName = null; |
| |
| /** User specified classname for Algorithm. */ |
| private String algorithmClass; |
| |
| /** Comparator name for later instantiation. */ |
| private ComparatorName compName = null; |
| |
| /** User specified classname for Comparator. */ |
| private String comparatorClass; |
| |
| /** Should the cache be updated? */ |
| private boolean update = true; |
| |
| /** Are directories selected? */ |
| private boolean selectDirectories = true; |
| |
| /** |
| * Should Resources without an InputStream, and |
| * therefore without checking, be selected? |
| */ |
| private boolean selectResourcesWithoutInputStream = true; |
| |
| /** Delay the writing of the cache file */ |
| private boolean delayUpdate = true; |
| |
| |
| // ----- internal member variables ----- |
| |
| |
| /** How should the cached value and the new one compared? */ |
| private Comparator<? super String> comparator = null; |
| |
| /** Algorithm for computing new values and updating the cache. */ |
| private Algorithm algorithm = null; |
| |
| /** The Cache containing the old values. */ |
| private Cache cache = null; |
| |
| /** Count of modified properties */ |
| private int modified = 0; |
| |
| /** Flag whether this object is configured. Configuration is only done once. */ |
| private boolean isConfigured = false; |
| |
| /** |
| * Parameter vector with parameters for later initialization. |
| * @see #configure |
| */ |
| private List<Parameter> configParameter = |
| Collections.synchronizedList(new ArrayList<>()); |
| |
| /** |
| * 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 List<Parameter> specialParameter = |
| Collections.synchronizedList(new ArrayList<>()); |
| |
| /** The classloader of this class. */ |
| private ClassLoader myClassLoader = null; |
| |
| /** provided classpath for the classloader */ |
| private Path classpath = null; |
| |
| |
| // ----- constructors ----- |
| |
| |
| /** Bean-Constructor. */ |
| public ModifiedSelector() { |
| } |
| |
| |
| // ----- configuration ----- |
| |
| |
| /** Overrides BaseSelector.verifySettings(). */ |
| @Override |
| 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:</p><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>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 ----- |
| // |
| Project p = getProject(); |
| String filename = "cache.properties"; |
| File cachefile; |
| if (p != null) { |
| // normal use inside Ant |
| cachefile = new File(p.getBaseDir(), filename); |
| |
| // set self as a BuildListener to delay cachefile saves |
| getProject().addBuildListener(this); |
| } else { |
| // no reference to project - e.g. during normal JUnit tests |
| cachefile = new File(filename); |
| setDelayUpdate(false); |
| } |
| Cache defaultCache = new PropertiesfileCache(cachefile); |
| Algorithm defaultAlgorithm = new DigestAlgorithm(); |
| Comparator<? super String> defaultComparator = new EqualComparator(); |
| |
| // |
| // ----- Set the main attributes, pattern '*' ----- |
| // |
| for (Parameter parameter : configParameter) { |
| if (parameter.getName().indexOf('.') > 0) { |
| // this is a *.* parameter for later use |
| specialParameter.add(parameter); |
| } else { |
| useParameter(parameter); |
| } |
| } |
| configParameter.clear(); |
| |
| // specify the algorithm classname |
| if (algoName != null) { |
| // use Algorithm defined via name |
| if ("hashvalue".equals(algoName.getValue())) { |
| algorithm = new HashvalueAlgorithm(); |
| } else if ("digest".equals(algoName.getValue())) { |
| algorithm = new DigestAlgorithm(); |
| } else if ("checksum".equals(algoName.getValue())) { |
| algorithm = new ChecksumAlgorithm(); |
| } else if ("lastmodified".equals(algoName.getValue())) { |
| algorithm = new LastModifiedAlgorithm(); |
| } |
| } else if (algorithmClass != null) { |
| // use Algorithm specified by classname |
| algorithm = loadClass( |
| algorithmClass, |
| "is not an Algorithm.", |
| Algorithm.class); |
| } else { |
| // nothing specified - use default |
| algorithm = defaultAlgorithm; |
| } |
| |
| // specify the cache classname |
| if (cacheName != null) { |
| // use Cache defined via name |
| if ("propertyfile".equals(cacheName.getValue())) { |
| cache = new PropertiesfileCache(); |
| } |
| } else if (cacheClass != null) { |
| // use Cache specified by classname |
| cache = loadClass(cacheClass, "is not a Cache.", Cache.class); |
| } else { |
| // nothing specified - use default |
| cache = defaultCache; |
| } |
| |
| // specify the comparator classname |
| if (compName != null) { |
| // use Algorithm defined via name |
| if ("equal".equals(compName.getValue())) { |
| comparator = new EqualComparator(); |
| } else if ("rule".equals(compName.getValue())) { |
| // TODO there is a problem with the constructor for the RBC. |
| // you have to provide the rules in the constructors - no setters |
| // available. |
| throw new BuildException("RuleBasedCollator not yet supported."); |
| // Have to think about lazy initialization here... JHM |
| // comparator = new java.text.RuleBasedCollator(); |
| } |
| } else if (comparatorClass != null) { |
| // use Algorithm specified by classname |
| @SuppressWarnings("unchecked") |
| Comparator<? super String> localComparator = loadClass( |
| comparatorClass, "is not a Comparator.", Comparator.class); |
| comparator = localComparator; |
| } else { |
| // nothing specified - use default |
| comparator = defaultComparator; |
| } |
| |
| // |
| // ----- Set the special attributes, pattern '*.*' ----- |
| // |
| specialParameter.forEach(this::useParameter); |
| specialParameter.clear(); |
| } |
| |
| |
| /** |
| * Loads the specified class and initializes an object of that class. |
| * Throws a BuildException using the given message if an error occurs during |
| * loading/instantiation or if the object is not from the given type. |
| * @param <T> desired type |
| * @param classname the classname |
| * @param msg the message-part for the BuildException |
| * @param type the type to check against |
| * @return a castable object |
| */ |
| @SuppressWarnings("unchecked") |
| protected <T> T loadClass(String classname, String msg, Class<? extends T> type) { |
| try { |
| // load the specified class |
| ClassLoader cl = getClassLoader(); |
| Class<?> clazz; |
| if (cl != null) { |
| clazz = cl.loadClass(classname); |
| } else { |
| clazz = Class.forName(classname); |
| } |
| |
| Object rv = clazz.newInstance(); |
| |
| if (!type.isInstance(rv)) { |
| throw new BuildException("Specified class (%s) %s", classname, msg); |
| } |
| return (T) rv; |
| } catch (ClassNotFoundException e) { |
| throw new BuildException("Specified class (%s) not found.", classname); |
| } catch (Exception e) { |
| throw new BuildException(e); |
| } |
| } |
| |
| |
| // ----- the selection work ----- |
| |
| |
| /** |
| * Implementation of ResourceSelector.isSelected(). |
| * |
| * @param resource The resource to check |
| * @return whether the resource is selected |
| * @see ResourceSelector#isSelected(Resource) |
| */ |
| @Override |
| public boolean isSelected(Resource resource) { |
| if (resource.isFilesystemOnly()) { |
| // We have a 'resourced' file, so reconvert it and use |
| // the 'old' implementation. |
| FileResource fileResource = (FileResource) resource; |
| File file = fileResource.getFile(); |
| String filename = fileResource.getName(); |
| File basedir = fileResource.getBaseDir(); |
| return isSelected(basedir, filename, file); |
| } |
| try { |
| // How to handle non-file-Resources? I copy temporarily the |
| // resource to a file and use the file-implementation. |
| FileUtils fu = FileUtils.getFileUtils(); |
| File tmpFile = fu.createTempFile(getProject(), "modified-", ".tmp", null, true, false); |
| Resource tmpResource = new FileResource(tmpFile); |
| ResourceUtils.copyResource(resource, tmpResource); |
| boolean isSelected = isSelected(tmpFile.getParentFile(), |
| tmpFile.getName(), |
| resource.toLongString()); |
| tmpFile.delete(); |
| return isSelected; |
| } catch (UnsupportedOperationException uoe) { |
| log("The resource '" |
| + resource.getName() |
| + "' does not provide an InputStream, so it is not checked. " |
| + "According to 'selres' attribute value it is " |
| + ((selectResourcesWithoutInputStream) ? "" : " not") |
| + "selected.", Project.MSG_INFO); |
| return selectResourcesWithoutInputStream; |
| } catch (Exception e) { |
| throw new BuildException(e); |
| } |
| } |
| |
| |
| /** |
| * 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 |
| */ |
| @Override |
| public boolean isSelected(File basedir, String filename, File file) { |
| return isSelected(basedir, filename, file.getAbsolutePath()); |
| } |
| |
| /** |
| * The business logic of this selector for use as ResourceSelector of |
| * FileSelector. |
| * |
| * @param basedir as described in BaseExtendSelector |
| * @param filename as described in BaseExtendSelector |
| * @param cacheKey the name for the key for storing the hashvalue |
| * @return <code>true</code> if the file is selected otherwise <code>false</code> |
| */ |
| private boolean isSelected(File basedir, String filename, String cacheKey) { |
| 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 && rv) { |
| cache.put(f.getAbsolutePath(), newValue); |
| setModified(getModified() + 1); |
| if (!getDelayUpdate()) { |
| saveCache(); |
| } |
| } |
| return rv; |
| } |
| |
| |
| /** |
| * save the cache file |
| */ |
| protected void saveCache() { |
| if (getModified() > 0) { |
| cache.save(); |
| setModified(0); |
| } |
| } |
| |
| |
| // ----- attribute and nested element support ----- |
| |
| |
| /** |
| * Setter for algorithmClass. |
| * @param classname new value |
| */ |
| public void setAlgorithmClass(String classname) { |
| algorithmClass = classname; |
| } |
| |
| |
| /** |
| * Setter for comparatorClass. |
| * @param classname new value |
| */ |
| public void setComparatorClass(String classname) { |
| comparatorClass = classname; |
| } |
| |
| |
| /** |
| * Setter for cacheClass. |
| * @param classname new value |
| */ |
| public void setCacheClass(String classname) { |
| cacheClass = classname; |
| } |
| |
| |
| /** |
| * 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 <i>selres</i> attribute. |
| * @param newValue the new value |
| */ |
| public void setSelres(boolean newValue) { |
| this.selectResourcesWithoutInputStream = newValue; |
| } |
| |
| |
| /** |
| * Getter for the modified count |
| * @return modified count |
| */ |
| public int getModified() { |
| return modified; |
| } |
| |
| |
| /** |
| * Setter for the modified count |
| * @param modified count |
| */ |
| public void setModified(int modified) { |
| this.modified = modified; |
| } |
| |
| |
| /** |
| * Getter for the delay update |
| * @return true if we should delay for performance |
| */ |
| public boolean getDelayUpdate() { |
| return delayUpdate; |
| } |
| |
| |
| /** |
| * Setter for the delay update |
| * @param delayUpdate true if we should delay for performance |
| */ |
| public void setDelayUpdate(boolean delayUpdate) { |
| this.delayUpdate = delayUpdate; |
| } |
| |
| |
| /** |
| * Add the classpath. |
| * @param path the classpath |
| */ |
| public void addClasspath(Path path) { |
| if (classpath != null) { |
| throw new BuildException("<classpath> can be set only once."); |
| } |
| classpath = path; |
| } |
| |
| |
| /** |
| * Returns and initializes the classloader for this class. |
| * @return the classloader |
| */ |
| public ClassLoader getClassLoader() { |
| if (myClassLoader == null) { |
| myClassLoader = (classpath == null) |
| // the usual classloader |
| ? getClass().getClassLoader() |
| // additional use the provided classpath |
| // Memory leak in line below |
| : getProject().createClassLoader(classpath); |
| } |
| return myClassLoader; |
| } |
| |
| |
| /** |
| * Set the used ClassLoader. |
| * If you invoke this selector by API (e.g. inside some testcases) the selector |
| * will use a different classloader for loading the interface implementations than |
| * the caller. Therefore you will get a ClassCastException if you get the |
| * implementations from the selector and cast them. |
| * @param loader the ClassLoader to use |
| */ |
| public void setClassLoader(ClassLoader loader) { |
| myClassLoader = loader; |
| } |
| |
| |
| /** |
| * 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,Object) |
| * @param parameters the parameters to set. |
| */ |
| @Override |
| public void setParameters(Parameter... parameters) { |
| if (parameters != null) { |
| Collections.addAll(configParameter, parameters); |
| } |
| } |
| |
| |
| /** |
| * Support for nested <code><param name="" value=""/></code> 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)) { |
| setUpdate("true".equalsIgnoreCase(value)); |
| } else if ("delayupdate".equals(key)) { |
| setDelayUpdate("true".equalsIgnoreCase(value)); |
| } else if ("seldirs".equals(key)) { |
| setSeldirs("true".equalsIgnoreCase(value)); |
| } else if (key.startsWith(CACHE_PREFIX)) { |
| String name = key.substring(CACHE_PREFIX.length()); |
| tryToSetAParameter(cache, name, value); |
| } else if (key.startsWith(ALGORITHM_PREFIX)) { |
| String name = key.substring(ALGORITHM_PREFIX.length()); |
| tryToSetAParameter(algorithm, name, value); |
| } else if (key.startsWith(COMPARATOR_PREFIX)) { |
| String name = key.substring(COMPARATOR_PREFIX.length()); |
| 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 (BuildException e) { |
| // no-op |
| } |
| } |
| |
| |
| // ----- 'beautiful' output ----- |
| |
| |
| /** |
| * Override Object.toString(). |
| * @return information about this selector |
| */ |
| @Override |
| public String toString() { |
| return String.format("{modifiedselector update=%s seldirs=%s cache=%s algorithm=%s comparator=%s}", |
| update, selectDirectories, cache, algorithm, comparator); |
| } |
| |
| |
| // ----- BuildListener interface methods ----- |
| |
| |
| /** |
| * Signals that the last target has finished. |
| * @param event received BuildEvent |
| */ |
| @Override |
| public void buildFinished(BuildEvent event) { |
| if (getDelayUpdate()) { |
| saveCache(); |
| } |
| } |
| |
| |
| /** |
| * Signals that a target has finished. |
| * @param event received BuildEvent |
| */ |
| @Override |
| public void targetFinished(BuildEvent event) { |
| if (getDelayUpdate()) { |
| saveCache(); |
| } |
| } |
| |
| |
| /** |
| * Signals that a task has finished. |
| * @param event received BuildEvent |
| */ |
| @Override |
| public void taskFinished(BuildEvent event) { |
| if (getDelayUpdate()) { |
| saveCache(); |
| } |
| } |
| |
| |
| /** |
| * Signals that a build has started. |
| * @param event received BuildEvent |
| */ |
| @Override |
| public void buildStarted(BuildEvent event) { |
| // no-op |
| } |
| |
| |
| /** |
| * Signals that a target is starting. |
| * @param event received BuildEvent |
| */ |
| @Override |
| public void targetStarted(BuildEvent event) { |
| // no-op |
| } |
| |
| |
| |
| /** |
| * Signals that a task is starting. |
| * @param event received BuildEvent |
| */ |
| @Override |
| public void taskStarted(BuildEvent event) { |
| // no-op |
| } |
| |
| |
| /** |
| * Signals a message logging event. |
| * @param event received BuildEvent |
| */ |
| @Override |
| public void messageLogged(BuildEvent event) { |
| // no-op |
| } |
| |
| |
| // The EnumeratedAttributes for the three interface implementations. |
| // Name-Classname mapping is done in the configure() method. |
| |
| |
| /** |
| * Get the cache type to use. |
| * @return the enumerated cache type |
| */ |
| public Cache getCache() { |
| return cache; |
| } |
| |
| /** |
| * Set the cache type to use. |
| * @param name an enumerated cache type. |
| */ |
| public void setCache(CacheName name) { |
| cacheName = name; |
| } |
| |
| /** |
| * The enumerated type for cache. |
| * The values are "propertyfile". |
| */ |
| public static class CacheName extends EnumeratedAttribute { |
| /** |
| * {@inheritDoc} |
| * @see EnumeratedAttribute#getValues() |
| */ |
| @Override |
| public String[] getValues() { |
| return new String[] {"propertyfile"}; |
| } |
| } |
| |
| /** |
| * Get the algorithm type to use. |
| * @return the enumerated algorithm type |
| */ |
| public Algorithm getAlgorithm() { |
| return algorithm; |
| } |
| |
| /** |
| * Set the algorithm type to use. |
| * @param name an enumerated algorithm type. |
| */ |
| public void setAlgorithm(AlgorithmName name) { |
| algoName = name; |
| } |
| |
| /** |
| * The enumerated type for algorithm. |
| * The values are "hashValue", "digest", "checksum" and "lastmodified". |
| */ |
| public static class AlgorithmName extends EnumeratedAttribute { |
| /** |
| * {@inheritDoc} |
| * @see EnumeratedAttribute#getValues() |
| */ |
| @Override |
| public String[] getValues() { |
| return new String[] {"hashvalue", "digest", "checksum", "lastmodified"}; |
| } |
| } |
| |
| /** |
| * Get the comparator type to use. |
| * @return the enumerated comparator type |
| */ |
| public Comparator<? super String> getComparator() { |
| return comparator; |
| } |
| |
| /** |
| * Set the comparator type to use. |
| * @param name an enumerated comparator type. |
| */ |
| public void setComparator(ComparatorName name) { |
| compName = name; |
| } |
| |
| /** |
| * The enumerated type for algorithm. |
| * The values are "equal" and "rule". |
| */ |
| public static class ComparatorName extends EnumeratedAttribute { |
| /** |
| * {@inheritDoc} |
| * @see EnumeratedAttribute#getValues() |
| */ |
| @Override |
| public String[] getValues() { |
| return new String[] {"equal", "rule"}; |
| } |
| } |
| |
| } |