Bugzilla Report 20474 git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@275236 13f79535-47bb-0310-9956-ffa450edef68master
@@ -597,6 +597,9 @@ Other changes: | |||
* additional shortcuts for ant options (-d --> -debug, -e --> -emacs, | |||
-h --> -help, -p --> -projecthelp, -s --> -find). | |||
* new selector <modified>. "cache" was renamed to "modified". | |||
Bugzilla Report 20474. | |||
Changes from Ant 1.5.3 to Ant 1.5.4 | |||
=================================== | |||
@@ -18,7 +18,7 @@ | |||
<ol> | |||
<li>Custom Selectors | |||
<p>This is the category that Ant provides specifically for youto | |||
<p>This is the category that Ant provides specifically for you to | |||
define your own Selectors. Anywhere you want to use your selector | |||
you use the <code><custom></code> element and specify | |||
the class name of your selector within it. See the | |||
@@ -86,7 +86,8 @@ | |||
<code>org.apache.tools.ant.types.selectors.SelectorContainer</code>. | |||
This is an interface, so you will also have to add an implementation | |||
for the method in the classes which implement it, namely | |||
<code>org.apache.tools.ant.types.AbstractFileSet</code> and | |||
<code>org.apache.tools.ant.types.AbstractFileSet</code>, | |||
<code>org.apache.tools.ant.taskdefs.MatchingTask</code> and | |||
<code>org.apache.tools.ant.types.selectors.BaseSelectorContainer</code>. | |||
Once it is in there, it will be available everywhere that core | |||
selectors are appropriate.</p> | |||
@@ -118,10 +119,131 @@ | |||
</ul> | |||
</ol> | |||
<h3>Testing Selectors</h3> | |||
<p>For a robust component (and selectors are (Project)Component´s) tests are | |||
necessary. For testing Tasks we use JUnit TestCases - more specific | |||
<tt>org.apache.tools.ant.BuildFileTest extends junit.framework.TestCase</tt>. | |||
Some of its features like configure the (test) project by reading its buildfile and | |||
execute targets we need for selector tests also. Therefore we use that BuildFileTest. | |||
But testing selectors requires some more work: having a set of files, instantiate | |||
and configure the selector, check the selection work and more. Because we usually | |||
extend <tt>BaseExtendSelector</tt> its features have to be tested also (e.g. setError()). | |||
</p> | |||
<p>That´s why we have a base class for doing our selector tests: | |||
<tt>org.apache.tools.ant.types.selectors.BaseSelectorTest</tt>.</p> | |||
<p>This class extends TestCase and therefore can included in the set of Ant´s | |||
unit tests. It holds an instance of preconfigured BuildFileTest. Configuration | |||
is done by parsing the src/etc/testcases/types/selectors.xml. BaseSelectorTest | |||
then gives us helper methods for handling multiple selections. </p> | |||
<p>Because the term "testcase" or "testenvironment" are so often used, this | |||
special testenvironment got a new name: <i>bed</i>. Like you initialize the | |||
test environment by calling setUp() and cleaning by calling tearDown() (<i>or like | |||
to make your bed before go sleeping</i>) you have to do that work with your | |||
<i>bed</i> by calling <tt>makeBed()</tt> respecitive <tt>cleanupBed()</tt>.</p> | |||
<p>A usual test scenario is<ol> | |||
<li>make the bed</li> | |||
<li>instantiate the selector</li> | |||
<li>configure the selector</li> | |||
<li>let the selector do some work</li> | |||
<li>verify the work</li> | |||
<li>clean the bed</li> | |||
</ol> | |||
</p> | |||
<p>For common way of instantiation you have to override the <tt>getInstance()</tt> | |||
simply by returning a new object of your selector. For easier "selection and verification work" | |||
BaseSelectorTest provides the method <tt>performTests()</tt> which | |||
iterates over all files (and directories) in the String array <tt>filenames</tt> | |||
and checks whether the given selector returns the expected result. If an error | |||
occured (especially the selector does not return the expected result) the test | |||
fails and the failing filenames are logged.</p> | |||
<p>An example test would be:<pre> | |||
package org.apache.tools.ant.types.selectors; | |||
public class MySelectorTest extends BaseSelectorTest { | |||
public MySelectorTest(String name) { | |||
super(name); | |||
} | |||
public BaseSelector getInstance() { | |||
return new MySelector(); | |||
} | |||
public void testCase1() { | |||
try { | |||
// initialize test environment 'bed' | |||
makeBed(); | |||
// Configure the selector | |||
MySelector s = (MySelector)getSelector(); | |||
s.addParam("key1", "value1"); | |||
s.addParam("key2", "value2"); | |||
s.setXX(true); | |||
s.setYY("a value"); | |||
// do the tests | |||
performTests(s, "FTTTTTTTTTTT"); // First is not selected - rest is | |||
} finally { | |||
// cleanup the environment | |||
cleanupBed(); | |||
} | |||
} | |||
} | |||
</pre> | |||
As an example of an error JUnit could log<pre> | |||
[junit] FAILED | |||
[junit] Error for files: <font color=blue>.;copy.filterset.filtered;tar/gz/asf-logo.gif.tar.gz</font> | |||
[junit] expected:<<font color=blue>FTTTFTTTF...</font>> but was:<TTTTTTTTT...> | |||
[junit] junit.framework.ComparisonFailure: Error for files: .;copy.filterset.filtered;tar/gz/asf-logo.gif.tar.gz | |||
[junit] expected:<FTTTFTTTF...> but was:<TTTTTTTTT...> | |||
[junit] at junit.framework.Assert.assertEquals(Assert.java:81) | |||
[junit] at org.apache.tools.ant.types.selectors.BaseSelectorTest.performTest(BaseSelectorTest.java:194) | |||
</pre></p> | |||
<p>Described above the test class should provide a <tt>getInstance()</tt> | |||
method. But that isn´t used here. The used <tt>getSelector()</tt> method is | |||
implemented in the base class and gives an instance of an Ant Project to | |||
the selector. This is usually done inside normal build file runs, but not | |||
inside this special environment, so this method gives the selector the | |||
ability to use its own Project object (<tt>getProject()</tt>), for example | |||
for logging.</p> | |||
<h3>Logging</h3> | |||
<p>During development and maybe later you sometimes need the output of information. | |||
Therefore Logging is needed. Because the selector extends BaseExtendSelector or directly | |||
BaseSelector it is an Ant <tt>DataType</tt> and therefore a <tt>ProjectComponent</tt>. <br> | |||
That means that you have access to the project object and its logging capability. | |||
<tt>ProjectComponent</tt> itself provides <i>log</i> methods which will do the | |||
access to the project instance. Logging is therefore done simply with: | |||
<pre> | |||
log( "message" ); | |||
</pre> | |||
or | |||
<pre> | |||
log( "message" , loglevel ); | |||
</pre> | |||
where the <tt>loglevel</tt> is one of the values <ul> | |||
<li> org.apache.tools.ant.Project.MSG_ERR </li> | |||
<li> org.apache.tools.ant.Project.MSG_WARN </li> | |||
<li> org.apache.tools.ant.Project.MSG_INFO (= default) </li> | |||
<li> org.apache.tools.ant.Project.MSG_VERBOSE </li> | |||
<li> org.apache.tools.ant.Project.MSG_DEBUG </li> | |||
</ul> | |||
</p> | |||
<hr> | |||
<p align="center">Copyright © 2002 Apache Software | |||
<p align="center">Copyright © 2002-2003 Apache Software | |||
Foundation. All rights Reserved.</p> | |||
</body> | |||
</html> | |||
</html> |
@@ -40,28 +40,31 @@ | |||
<ul> | |||
<li><a href="#containsselect"><contains></a> - Select | |||
files that contain a particular text string | |||
files that contain a particular text string</li> | |||
<li><a href="#dateselect"><date></a> - Select files | |||
that have been modified either before or after a particular date | |||
and time | |||
and time</li> | |||
<li><a href="#dependselect"><depend></a> - Select files | |||
that have been modified more recently than equivalent files | |||
elsewhere | |||
elsewhere</li> | |||
<li><a href="#depthselect"><depth></a> - Select files | |||
that appear so many directories down in a directory tree | |||
that appear so many directories down in a directory tree</li> | |||
<li><a href="#differentselect"><different></a> - Select files | |||
that are different from those elsewhere | |||
that are different from those elsewhere</li> | |||
<li><a href="#filenameselect"><filename></a> - Select | |||
files whose name matches a particular pattern. Equivalent to | |||
the include and exclude elements of a patternset. | |||
the include and exclude elements of a patternset.</li> | |||
<li><a href="#presentselect"><present></a> - Select | |||
files that either do or do not exist in some other location | |||
files that either do or do not exist in some other location</li> | |||
<li><a href="#regexpselect"><containsregexp></a> - Select | |||
files that match a regular expression | |||
files that match a regular expression</li> | |||
<li><a href="#sizeselect"><size></a> - Select files | |||
that are larger or smaller than a particular number of bytes. | |||
that are larger or smaller than a particular number of bytes.</li> | |||
<li><a href="#typeselect"><type></a> - Select files | |||
that are either regular files or directories. | |||
that are either regular files or directories.</li> | |||
<li><a href="#modified"><modified></a> - Select files if | |||
the return value of the configured algorithm is different from that | |||
stored in a cache.</li> | |||
</ul> | |||
<a name="containsselect"></a> | |||
@@ -465,7 +468,7 @@ | |||
</tr> | |||
<tr> | |||
<td valign="top">expression</td> | |||
<td valign="top">Specifies the regular expression that must | |||
<td valign="top">Specifies the regular expression that must | |||
match true in every file</td> | |||
<td valign="top" align="center">Yes</td> | |||
</tr> | |||
@@ -479,10 +482,10 @@ | |||
</fileset> | |||
</pre></blockquote> | |||
<p>Selects all the text files that match the regular expression | |||
<p>Selects all the text files that match the regular expression | |||
(have a 4,5 or 6 followed by a period and a number from 0 to 9). | |||
<a name="sizeselect"></a> | |||
<h4>Size Selector</h4> | |||
@@ -592,6 +595,193 @@ | |||
</pre></blockquote> | |||
<a name="modified"></a> | |||
<h4>Modified Selector</h4> | |||
<p>The <modified> computes a value for a file, compares that | |||
to the value stored in a cache and select the file, if these two values | |||
differ.</p> | |||
<p>Because this selector is highly configurable the order in which the selection is done | |||
is: <ol> | |||
<li> get the absolute path for the file </li> | |||
<li> get the cached value from the configured cache (absolute path as key) </li> | |||
<li> get the new value from the configured algorithm </li> | |||
<li> compare these two values with the configured comparator </li> | |||
<li> update the cache if needed and wished </li> | |||
<li> do the selection according to the comparison result </li> | |||
</ol> | |||
The comparison, computing of the hashvalue and the store is done by implementation | |||
of special interfaces. Therefore they may provide additional parameters.</p> | |||
<table border="1" cellpadding="2" cellspacing="0"> | |||
<tr> | |||
<td valign="top"><b>Attribute</b></td> | |||
<td valign="top"><b>Description</b></td> | |||
<td align="center" valign="top"><b>Required</b></td> | |||
</tr> | |||
<tr> | |||
<td valign="top"> algorithm </td> | |||
<td valign="top"> The type of algorithm should be used. | |||
Acceptable values are (further information see later): | |||
<ul> | |||
<li> hashvalue - HashvalueAlgorithm </li> | |||
<li> digest - DigestAlgorithm </li> | |||
</ul> | |||
</td> | |||
<td valign="top" align="center"> No, defaults to <i>digest</i> </td> | |||
</tr> | |||
<tr> | |||
<td valign="top"> cache </td> | |||
<td valign="top"> The type of cache should be used. | |||
Acceptable values are (further information see later): | |||
<ul> | |||
<li> propertyfile - PropertyfileCache </li> | |||
</ul> | |||
</td> | |||
<td valign="top" align="center"> No, defaults to <i>propertyfile</i> </td> | |||
</tr> | |||
<tr> | |||
<td valign="top"> comparator </td> | |||
<td valign="top"> The type of comparator should be used. | |||
Acceptable values are (further information see later): | |||
<ul> | |||
<li> equal - EqualComparator </li> | |||
<li> rule - java.text.RuleBasedCollator </li> | |||
</ul> | |||
</td> | |||
<td valign="top" align="center"> No, defaults to <i>equal</i> </td> | |||
</tr> | |||
<tr> | |||
<td valign="top"> update </td> | |||
<td valign="top"> Should the cache be updated when values differ? (boolean) </td> | |||
<td valign="top" align="center"> No, defaults to <i>true</i> </td> | |||
</tr> | |||
<tr> | |||
<td valign="top"> seldirs </td> | |||
<td valign="top"> Should directories be selected? (boolean) </td> | |||
<td valign="top" align="center"> No, defaults to <i>true</i> </td> | |||
</tr> | |||
</table> | |||
<p>These attributes can be set with nested <param/> tags. With <param/> | |||
tags you can set other values too - as long as they are named according to | |||
the following rules: <ul> | |||
<li> <b> algorithm </b>: same as attribute algorithm </li> | |||
<li> <b> cache </b>: same as attribute cache </li> | |||
<li> <b> comparator </b>: same as attribute cache </li> | |||
<li> <b> update </b>: same as attribute comparator </li> | |||
<li> <b> seldirs </b>: same as attribute seldirs </li> | |||
<li> <b> algorithm.* </b>: Value is transfered to the algorithm via its | |||
<i>set</i>XX-methods </li> | |||
<li> <b> cache.* </b>: Value is transfered to the cache via its | |||
<i>set</i>XX-methods </li> | |||
<li> <b> comparator.* </b>: Value is transfered to the comparator via its | |||
<i>set</i>XX-methods </li> | |||
</ul></p> | |||
<table border="1" cellpadding="2" cellspacing="0"> | |||
<tr><td colspan="2"><font size="+1"><b> Algorithm´s </b></font></td></tr> | |||
<tr> | |||
<td valign="top"><b>Name</b></td> | |||
<td valign="top"><b>Description</b></td> | |||
</tr> | |||
<tr> | |||
<td valign="top"> hashvalue </td> | |||
<td valign="top"> Reads the content of a file into a java.lang.String | |||
and use thats hashValue(). No additional configuration required. | |||
</td> | |||
</tr> | |||
<tr> | |||
<td valign="top"> digest </td> | |||
<td valign="top"> Uses java.security.MessageDigest. This Algorithm supports | |||
the following attributes: | |||
<ul> | |||
<li><i>algorithm.algorithm</i> (optional): Name of the Digest algorithm | |||
(e.g. 'MD5' or 'SHA', default = <i>MD5</i>) </li> | |||
<li><i>algorithm.provider</i> (optional): Name of the Digest provider | |||
(default = <i>null</i>) </li> | |||
</ul> | |||
</td> | |||
</tr> | |||
<tr><td colspan="2"><font size="+1"><b> Cache´s </b></font></td></tr> | |||
<tr> | |||
<td valign="top"> propertyfile </td> | |||
<td valign="top"> Use the java.util.Properties class and its possibility | |||
to load and store to file. | |||
This Cache implementation supports the following attributes: | |||
<ul> | |||
<li><i>cache.cachefile</i> (optional): Name of the properties-file | |||
(default = <i>cache.properties</i>) </li> | |||
</ul> | |||
</td> | |||
</tr> | |||
</table> | |||
<p>Here are some examples of how to use the Modified Selector:</p> | |||
<blockquote><pre> | |||
<copy todir="dest"> | |||
<filelist dir="src"> | |||
<modified/> | |||
</filelist> | |||
</copy | |||
</pre></blockquote> | |||
<p>This will copy all files from <i>src</i> to <i>dest</i> which content has changed. | |||
Using an updating PropertyfileCache with cache.properties and | |||
MD5-DigestAlgorithm.</p> | |||
<blockquote><pre> | |||
<copy todir="dest"> | |||
<filelist dir="src"> | |||
<modified update="true" | |||
seldirs="true" | |||
cache="propertyfile" | |||
algorithm="digest" | |||
comparator="equal"> | |||
<param name="cache.cachefile" value="cache.properties"/> | |||
<param name="algorithm.algorithm" value="MD5"/> | |||
</modified> | |||
</filelist> | |||
</copy> | |||
</pre></blockquote> | |||
<p>This is the same example rewritten as CoreSelector with setting the all the values | |||
(same as defaults are).</p> | |||
<blockquote><pre> | |||
<copy todir="dest"> | |||
<filelist dir="src"> | |||
<custom class="org.apache.tools.ant.types.selectors.modifiedselector.ModifiedSelector"> | |||
<param name="update" value="true"/> | |||
<param name="seldirs" 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></blockquote> | |||
<p>And this is the same rewritten as CustomSelector.</p> | |||
<blockquote><pre> | |||
<target name="generate-and-upload-site"> | |||
<echo> generate the site using forrest </echo> | |||
<antcall target="site"/> | |||
<echo> upload the changed file </echo> | |||
<ftp server="${ftp.server}" userid="${ftp.user}" password="${ftp.pwd}"> | |||
<fileset dir="htdocs/manual"> | |||
<modified/> | |||
</fileset> | |||
</ftp> | |||
</target> | |||
</pre></blockquote> | |||
<p>A useful scenario for this selector inside a build environment | |||
for homepage generation (e.g. with <a href="http://xml.apache.org/forrest/"> | |||
Apache Forrest</a>). Here all <b>changed</b> files are uploaded to the server. The | |||
CacheSelector saves therefore much upload time.</p> | |||
<a name="selectcontainers"></a> | |||
<h3>Selector Containers</h3> | |||
@@ -992,4 +1182,3 @@ | |||
@@ -85,12 +85,12 @@ | |||
<target name="cleanup.mirrorfiles"> | |||
<delete dir="${mirror.dir}" /> | |||
</target> | |||
<target name="cleanupregexp"> | |||
<delete dir="${testregexpsrc.dir}" /> | |||
<delete dir="${testregexpdest.dir}" /> | |||
</target> | |||
<target name="containsregexp"> | |||
<mkdir dir="${testregexpsrc.dir}" /> | |||
<mkdir dir="${testregexpdest.dir}" /> | |||
@@ -108,4 +108,134 @@ | |||
</copy> | |||
</target> | |||
</project> | |||
<!-- ========== Test for ModifiedSelector ========== --> | |||
<target name="modifiedselectortest-makeDirty"> | |||
<!-- Load propertyfile generated by SelectorTest-class --> | |||
<property file="ModifiedSelectorTest.properties"/> | |||
<!-- Modify only timestamp --> | |||
<touch file="${test.dir}/${f2name}" datetime="02/28/2003 9:55 AM"/> | |||
<!-- Change content but keep timestamp --> | |||
<echo file="${test.dir}/${f3name}" append="true" message="new content"/> | |||
<touch file="${test.dir}/${f3name}" datetime="11/21/2001 4:55 AM"/> | |||
<!-- Change content and timestamp --> | |||
<echo file="${test.dir}/${f4name}" append="true" message="new content"/> | |||
</target> | |||
<target name="modifiedselectortest-scenario-clean"> | |||
<delete dir="${test.dir}"/> | |||
</target> | |||
<target name="modifiedselectortest-scenario-prepare"> | |||
<mkdir dir="${test.dir}/src"/> | |||
<copy todir="${test.dir}/src"> | |||
<fileset dir="${ant.home}/lib" includes="ant.jar"/> | |||
<fileset dir="${ant.home}/bin"/> | |||
</copy> | |||
<touch datetime="12/24/2002 4:00 pm"> | |||
<fileset dir="${test.dir}"/> | |||
</touch> | |||
<mkdir dir="${test.dir}/to-1"/> | |||
<mkdir dir="${test.dir}/to-2"/> | |||
<mkdir dir="${test.dir}/to-3"/> | |||
</target> | |||
<target name="modifiedselectortest-scenario-makeDirty"> | |||
<touch file="${test.dir}/src/ant.jar"/> | |||
<echo file="${test.dir}/src/ant.bat" append="true" message="new-content"/> | |||
<echo file="${test.dir}/src/antRun.pl" append="true" message="new-content"/> | |||
<touch file="${test.dir}/src/antRun.pl" datetime="12/24/2002 4:00 pm"/> | |||
</target> | |||
<target name="modifiedselectortest-scenario-coreselector-defaults" depends="modifiedselectortest-scenario-prepare"> | |||
<!-- copy first time and create cachefile --> | |||
<copy todir="${test.dir}/to-1"> | |||
<fileset dir="${test.dir}/src"> | |||
<modified/> | |||
</fileset> | |||
</copy> | |||
<!-- copy second time: nothing should be copied --> | |||
<copy todir="${test.dir}/to-2"> | |||
<fileset dir="${test.dir}/src"> | |||
<modified/> | |||
</fileset> | |||
</copy> | |||
<!-- 'modify' the source files --> | |||
<antcall target="modifiedselectortest-scenario-makeDirty"/> | |||
<!-- copy third time: only the files with new CONTENT should be copied --> | |||
<copy todir="${test.dir}/to-3"> | |||
<fileset dir="${test.dir}/src"> | |||
<modified/> | |||
</fileset> | |||
</copy> | |||
</target> | |||
<target name="modifiedselectortest-scenario-coreselector-settings" depends="modifiedselectortest-scenario-prepare"> | |||
<!-- copy first time and create cachefile --> | |||
<copy todir="${test.dir}/to-1"> | |||
<fileset dir="${test.dir}/src"> | |||
<modified cache="propertyfile" algorithm="hashvalue" update="true"> | |||
<param name="cache.cachefile" value="core.cache.properties" /> | |||
</modified> | |||
</fileset> | |||
</copy> | |||
<!-- copy second time: nothing should be copied --> | |||
<copy todir="${test.dir}/to-2"> | |||
<fileset dir="${test.dir}/src"> | |||
<modified cache="propertyfile" algorithm="hashvalue" update="true"> | |||
<param name="cache.cachefile" value="core.cache.properties" /> | |||
</modified> | |||
</fileset> | |||
</copy> | |||
<!-- 'modify' the source files --> | |||
<antcall target="modifiedselectortest-scenario-makeDirty"/> | |||
<!-- copy third time: only the files with new CONTENT should be copied --> | |||
<copy todir="${test.dir}/to-3"> | |||
<fileset dir="${test.dir}/src"> | |||
<modified cache="propertyfile" algorithm="hashvalue" update="true"> | |||
<param name="cache.cachefile" value="core.cache.properties" /> | |||
</modified> | |||
</fileset> | |||
</copy> | |||
</target> | |||
<target name="modifiedselectortest-scenario-customselector-settings" depends="modifiedselectortest-scenario-prepare"> | |||
<!-- copy first time and create cachefile --> | |||
<copy todir="${test.dir}/to-1"> | |||
<fileset dir="${test.dir}/src"> | |||
<custom classname="org.apache.tools.ant.types.selectors.modifiedselector.ModifiedSelector"> | |||
<param name="cache" value="propertyfile"/> | |||
<param name="algorithm" value="hashvalue"/> | |||
<param name="update" value="true"/> | |||
<param name="cache.cachefile" value="core.cache.properties"/> | |||
</custom> | |||
</fileset> | |||
</copy> | |||
<!-- copy second time: nothing should be copied --> | |||
<copy todir="${test.dir}/to-2"> | |||
<fileset dir="${test.dir}/src"> | |||
<custom classname="org.apache.tools.ant.types.selectors.modifiedselector.ModifiedSelector"> | |||
<param name="cache" value="propertyfile"/> | |||
<param name="algorithm" value="hashvalue"/> | |||
<param name="update" value="true"/> | |||
<param name="cache.cachefile" value="core.cache.properties"/> | |||
</custom> | |||
</fileset> | |||
</copy> | |||
<!-- 'modify' the source files --> | |||
<antcall target="modifiedselectortest-scenario-makeDirty"/> | |||
<!-- copy third time: only the files with new CONTENT should be copied --> | |||
<copy todir="${test.dir}/to-3"> | |||
<fileset dir="${test.dir}/src"> | |||
<custom classname="org.apache.tools.ant.types.selectors.modifiedselector.ModifiedSelector"> | |||
<param name="cache" value="propertyfile"/> | |||
<param name="algorithm" value="hashvalue"/> | |||
<param name="update" value="true"/> | |||
<param name="cache.cachefile" value="core.cache.properties"/> | |||
</custom> | |||
</fileset> | |||
</copy> | |||
</target> | |||
</project> |
@@ -77,6 +77,7 @@ import org.apache.tools.ant.types.selectors.PresentSelector; | |||
import org.apache.tools.ant.types.selectors.SelectSelector; | |||
import org.apache.tools.ant.types.selectors.SizeSelector; | |||
import org.apache.tools.ant.types.selectors.FileSelector; | |||
import org.apache.tools.ant.types.selectors.modifiedselector.ModifiedSelector; | |||
/** | |||
* Deletes a file or directory, or set of files defined by a fileset. | |||
@@ -443,6 +444,16 @@ public class Delete extends MatchingTask { | |||
super.addContainsRegexp(selector); | |||
} | |||
/** | |||
* add the modified selector | |||
* @param selector the selector to add | |||
* @since ant 1.6 | |||
*/ | |||
public void addModified(ModifiedSelector selector) { | |||
usedMatchingTask = true; | |||
super.addModified(selector); | |||
} | |||
/** | |||
* add an arbitrary selector | |||
* @param selector the selector to be added | |||
@@ -676,4 +687,3 @@ public class Delete extends MatchingTask { | |||
} | |||
} | |||
} | |||
@@ -81,6 +81,7 @@ import org.apache.tools.ant.types.selectors.SelectSelector; | |||
import org.apache.tools.ant.types.selectors.SelectorContainer; | |||
import org.apache.tools.ant.types.selectors.SizeSelector; | |||
import org.apache.tools.ant.types.selectors.TypeSelector; | |||
import org.apache.tools.ant.types.selectors.modifiedselector.ModifiedSelector; | |||
/** | |||
* This is an abstract task that should be used by all those tasks that | |||
@@ -454,6 +455,15 @@ public abstract class MatchingTask extends Task implements SelectorContainer { | |||
fileset.addType(selector); | |||
} | |||
/** | |||
* add the modified selector | |||
* @param selector the selector to add | |||
* @since ant 1.6 | |||
*/ | |||
public void addModified(ModifiedSelector selector) { | |||
fileset.addModified(selector); | |||
} | |||
/** | |||
* add an arbitary selector | |||
* @param selector the selector to add | |||
@@ -471,4 +481,4 @@ public abstract class MatchingTask extends Task implements SelectorContainer { | |||
protected final FileSet getImplicitFileSet() { | |||
return fileset; | |||
} | |||
} | |||
} |
@@ -82,6 +82,7 @@ import org.apache.tools.ant.types.selectors.SelectSelector; | |||
import org.apache.tools.ant.types.selectors.SelectorContainer; | |||
import org.apache.tools.ant.types.selectors.SelectorScanner; | |||
import org.apache.tools.ant.types.selectors.SizeSelector; | |||
import org.apache.tools.ant.types.selectors.modifiedselector.ModifiedSelector; | |||
/** | |||
* Class that holds an implicit patternset and supports nested | |||
@@ -680,6 +681,15 @@ public abstract class AbstractFileSet extends DataType implements Cloneable, | |||
appendSelector(selector); | |||
} | |||
/** | |||
* add the modified selector | |||
* @param selector the selector to add | |||
* @since ant 1.6 | |||
*/ | |||
public void addModified(ModifiedSelector selector) { | |||
appendSelector(selector); | |||
} | |||
/** | |||
* add an arbitary selector | |||
* @param selector the selector to add | |||
@@ -721,4 +731,4 @@ public abstract class AbstractFileSet extends DataType implements Cloneable, | |||
} | |||
} | |||
} | |||
} |
@@ -60,6 +60,7 @@ import java.util.Vector; | |||
import org.apache.tools.ant.BuildException; | |||
import org.apache.tools.ant.Project; | |||
import org.apache.tools.ant.types.selectors.modifiedselector.ModifiedSelector; | |||
/** | |||
* This is the base class for selectors that can contain other selectors. | |||
@@ -328,6 +329,14 @@ public abstract class BaseSelectorContainer extends BaseSelector | |||
appendSelector(selector); | |||
} | |||
/** | |||
* add the modified selector | |||
* @param selector the selector to add | |||
* @since ant 1.6 | |||
*/ | |||
public void addModified(ModifiedSelector selector) { | |||
appendSelector(selector); | |||
} | |||
/** | |||
* add an arbitary selector | |||
@@ -338,5 +347,4 @@ public abstract class BaseSelectorContainer extends BaseSelector | |||
appendSelector(selector); | |||
} | |||
} | |||
} |
@@ -55,8 +55,8 @@ | |||
package org.apache.tools.ant.types.selectors; | |||
import java.util.Enumeration; | |||
import org.apache.tools.ant.Project; | |||
import org.apache.tools.ant.types.selectors.modifiedselector.ModifiedSelector; | |||
/** | |||
* This is the interface for selectors that can contain other selectors. | |||
@@ -207,6 +207,13 @@ public interface SelectorContainer { | |||
*/ | |||
void addDifferent(DifferentSelector selector); | |||
/** | |||
* add the modified selector | |||
* @param selector the selector to add | |||
* @since ant 1.6 | |||
*/ | |||
void addModified(ModifiedSelector selector); | |||
/** | |||
* add an arbitary selector | |||
* @param selector the selector to add | |||
@@ -214,4 +221,3 @@ public interface SelectorContainer { | |||
*/ | |||
void add(FileSelector selector); | |||
} | |||
@@ -0,0 +1,85 @@ | |||
/* | |||
* 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; | |||
import java.io.File; | |||
/** | |||
* The <i>Algorithm</i> defines how a value for a file is computed. | |||
* It must be sure that multiple calls for the same file results in the | |||
* same value. | |||
* The implementing class should implement a useful toString() method. | |||
* | |||
* @author Jan Matèrne | |||
* @version 2003-09-13 | |||
* @since Ant 1.6 | |||
*/ | |||
public interface Algorithm { | |||
/** | |||
* Checks its prerequisites. | |||
* @return <i>true</i> if all is ok, otherwise <i>false</i>. | |||
*/ | |||
boolean isValid(); | |||
/** | |||
* Get the value for a file. | |||
* @param file File object for which the value should be evaluated. | |||
* @return The value for that file | |||
*/ | |||
String getValue(File file); | |||
} |
@@ -0,0 +1,109 @@ | |||
/* | |||
* 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; | |||
import java.util.Iterator; | |||
/** | |||
* A Cache let the user store key-value-pairs in a permanent manner and access | |||
* them. | |||
* It is possible that a client uses get() before load() therefore the | |||
* implementation must ensure that no error occured because of the wrong | |||
* <i>order</i>. | |||
* The implementing class should implement a useful toString() method. | |||
* | |||
* @author Jan Matèrne | |||
* @version 2003-09-13 | |||
* @since Ant 1.6 | |||
*/ | |||
public interface Cache { | |||
/** | |||
* Checks its prerequisites. | |||
* @return <i>true</i> if all is ok, otherwise <i>false</i>. | |||
*/ | |||
boolean isValid(); | |||
/** Deletes the cache. If file based the file has to be deleted also. */ | |||
void delete(); | |||
/** Loads the cache, must handle not existing cache. */ | |||
void load(); | |||
/** Saves modification of the cache. */ | |||
void save(); | |||
/** | |||
* Returns a value for a given key from the cache. | |||
* @param key the key | |||
* @return the stored value | |||
*/ | |||
Object get(Object key); | |||
/** | |||
* Saves a key-value-pair in the cache. | |||
* @param key the key | |||
* @param value the value | |||
*/ | |||
void put(Object key, Object value); | |||
/** | |||
* Returns an iterator over the keys in the cache. | |||
* @return An iterator over the keys. | |||
*/ | |||
Iterator iterator(); | |||
} |
@@ -0,0 +1,241 @@ | |||
/* | |||
* 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; | |||
import java.io.File; | |||
import java.io.FileInputStream; | |||
import java.io.FileOutputStream; | |||
import java.security.DigestInputStream; | |||
import java.security.MessageDigest; | |||
import java.security.NoSuchAlgorithmException; | |||
import java.security.NoSuchProviderException; | |||
import org.apache.tools.ant.BuildException; | |||
/** | |||
* Computes a 'hashvalue' for the content of file using | |||
* java.security.MessageDigest. | |||
* Use of this algorithm doesn´t require any additional nested <param>s. | |||
* Supported <param>s are: | |||
* <table> | |||
* <tr> | |||
* <th>name</th><th>values</th><th>description</th><th>required</th> | |||
* </tr> | |||
* <tr> | |||
* <td> algorithm.algorithm </td> | |||
* <td> MD5 | SHA (default provider) </td> | |||
* <td> name of the algorithm the provider should use </td> | |||
* <td> no, defaults to MD5 </td> | |||
* </tr> | |||
* <tr> | |||
* <td> algorithm.provider </td> | |||
* <td> </td> | |||
* <td> name of the provider to use </td> | |||
* <td> no, defaults to <i>null</i> </td> | |||
* </tr> | |||
* </table> | |||
* | |||
* @author Jan Matèrne | |||
* @version 2003-09-13 | |||
* @since Ant 1.6 | |||
*/ | |||
public class DigestAlgorithm implements Algorithm { | |||
// ----- member variables ----- | |||
/** | |||
* MessageDigest algorithm to be used. | |||
*/ | |||
private String algorithm = "MD5"; | |||
/** | |||
* MessageDigest Algorithm provider | |||
*/ | |||
private String provider = null; | |||
/** | |||
* Message Digest instance | |||
*/ | |||
private MessageDigest messageDigest = null; | |||
/** | |||
* Size of the read buffer to use. | |||
*/ | |||
private int readBufferSize = 8 * 1024; | |||
// ----- Algorithm-Configuration ----- | |||
/** | |||
* Specifies the algorithm to be used to compute the checksum. | |||
* Defaults to "MD5". Other popular algorithms like "SHA" may be used as well. | |||
* @param algorithm the digest algorithm to use | |||
*/ | |||
public void setAlgorithm(String algorithm) { | |||
this.algorithm = algorithm; | |||
} | |||
/** | |||
* Sets the MessageDigest algorithm provider to be used | |||
* to calculate the checksum. | |||
* @param provider provider to use | |||
*/ | |||
public void setProvider(String provider) { | |||
this.provider = provider; | |||
} | |||
/** Initialize the security message digest. */ | |||
public void initMessageDigest() { | |||
if (messageDigest != null) { | |||
return; | |||
} | |||
if ((provider != null) && !"".equals(provider) && !"null".equals(provider)) { | |||
try { | |||
messageDigest = MessageDigest.getInstance(algorithm, provider); | |||
} catch (NoSuchAlgorithmException noalgo) { | |||
throw new BuildException(noalgo); | |||
} catch (NoSuchProviderException noprovider) { | |||
throw new BuildException(noprovider); | |||
} | |||
} else { | |||
try { | |||
messageDigest = MessageDigest.getInstance(algorithm); | |||
} catch (NoSuchAlgorithmException noalgo) { | |||
throw new BuildException(noalgo); | |||
} | |||
} | |||
} | |||
// ----- Logic ----- | |||
/** | |||
* This algorithm doesn´t need any configuration. | |||
* Therefore it´s always valid. | |||
* @return <i>true</i> if all is ok, otherwise <i>false</i>. | |||
*/ | |||
public boolean isValid() { | |||
return true; | |||
} | |||
/** | |||
* Computes a value for a file content with the specified digest algorithm. | |||
* @param file File object for which the value should be evaluated. | |||
* @return The value for that file | |||
*/ | |||
// implementation adapted from ...taskdefs.Checksum, thanks to Magesh for hint | |||
public String getValue(File file) { | |||
initMessageDigest(); | |||
String checksum = null; | |||
try { | |||
if (!file.canRead()) { | |||
return null; | |||
} | |||
FileInputStream fis = null; | |||
FileOutputStream fos = null; | |||
byte[] buf = new byte[readBufferSize]; | |||
try { | |||
messageDigest.reset(); | |||
fis = new FileInputStream(file); | |||
DigestInputStream dis = new DigestInputStream(fis, | |||
messageDigest); | |||
while (dis.read(buf, 0, readBufferSize) != -1) { | |||
// do nothing | |||
} | |||
dis.close(); | |||
fis.close(); | |||
fis = null; | |||
byte[] fileDigest = messageDigest.digest(); | |||
StringBuffer checksumSb = new StringBuffer(); | |||
for (int i = 0; i < fileDigest.length; i++) { | |||
String hexStr = Integer.toHexString(0x00ff & fileDigest[i]); | |||
if (hexStr.length() < 2) { | |||
checksumSb.append("0"); | |||
} | |||
checksumSb.append(hexStr); | |||
} | |||
checksum = checksumSb.toString(); | |||
} catch (Exception e) { | |||
return null; | |||
} | |||
} catch (Exception e) { | |||
return null; | |||
} | |||
return checksum; | |||
} | |||
/** | |||
* Override Object.toString(). | |||
* @return some information about this algorithm. | |||
*/ | |||
public String toString() { | |||
StringBuffer buf = new StringBuffer(); | |||
buf.append("<DigestAlgorithm:"); | |||
buf.append("algorithm=").append(algorithm); | |||
buf.append(";provider=").append(provider); | |||
buf.append(">"); | |||
return buf.toString(); | |||
} | |||
} |
@@ -0,0 +1,97 @@ | |||
/* | |||
* 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; | |||
import java.util.Comparator; | |||
/** | |||
* Simple implementation of Comparator for use in CacheSelector. | |||
* compare() returns '0' (should not be selected) if both parameter | |||
* are equal otherwise '1' (should be selected). | |||
* | |||
* @author Jan Matèrne | |||
* @version 2003-09-13 | |||
* @since Ant 1.6 | |||
*/ | |||
public class EqualComparator implements Comparator { | |||
/** | |||
* Implements Comparator.compare(). | |||
* @param o1 the first object | |||
* @param o2 the second object | |||
* @return 0, if both are equal, otherwise 1 | |||
*/ | |||
public int compare(Object o1, Object o2) { | |||
if (o1 == null) { | |||
if (o2 == null) { | |||
return 1; | |||
} else { | |||
return 0; | |||
} | |||
} else { | |||
return (o1.equals(o2)) ? 0 : 1; | |||
} | |||
} | |||
/** | |||
* Override Object.toString(). | |||
* @return information about this comparator | |||
*/ | |||
public String toString() { | |||
return "EqualComparator"; | |||
} | |||
} |
@@ -0,0 +1,116 @@ | |||
/* | |||
* 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; | |||
import java.io.File; | |||
/** | |||
* Computes a 'hashvalue' for the content of file using String.hashValue(). | |||
* Use of this algorithm doesn´t require any additional nested <param>s and | |||
* doesn´t support any. | |||
* | |||
* @author Jan Matèrne | |||
* @version 2003-09-13 | |||
* @since Ant 1.6 | |||
*/ | |||
public class HashvalueAlgorithm implements Algorithm { | |||
/** | |||
* This algorithm doesn´t need any configuration. | |||
* Therefore it´s always valid. | |||
* @return always true | |||
*/ | |||
public boolean isValid() { | |||
return true; | |||
} | |||
/** | |||
* Computes a 'hashvalue' for a file content. | |||
* It reads the content of a file, convert that to String and use the | |||
* String.hashCode() method. | |||
* @param file The file for which the value should be computed | |||
* @return the hashvalue or <i>null</i> if the file couldn´t be read | |||
*/ | |||
// Because the content is only read the file will not be damaged. I tested | |||
// with JPG, ZIP and PDF as binary files. | |||
public String getValue(File file) { | |||
try { | |||
if (!file.canRead()) { | |||
return null; | |||
} | |||
java.io.FileInputStream fis = new java.io.FileInputStream(file); | |||
byte[] content = new byte[fis.available()]; | |||
fis.read(content); | |||
fis.close(); | |||
String s = new String(content); | |||
int hash = s.hashCode(); | |||
return Integer.toString(hash); | |||
} catch (Exception e) { | |||
return null; | |||
} | |||
} | |||
/** | |||
* Override Object.toString(). | |||
* @return information about this comparator | |||
*/ | |||
public String toString() { | |||
return "HashvalueAlgorithm"; | |||
} | |||
} |
@@ -0,0 +1,653 @@ | |||
/* | |||
* 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èrne | |||
* @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" }; | |||
} | |||
} | |||
} |
@@ -0,0 +1,255 @@ | |||
/* | |||
* 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; | |||
import java.util.Iterator; | |||
import java.util.Vector; | |||
import java.util.Enumeration; | |||
import java.util.Properties; | |||
import java.io.File; | |||
import java.io.BufferedInputStream; | |||
import java.io.FileInputStream; | |||
import java.io.BufferedOutputStream; | |||
import java.io.FileOutputStream; | |||
/** | |||
* Use java.util.Properties for storing the values. | |||
* The use of this Cache-implementation requires the use of the parameter | |||
* <param name="cache.cachefile" .../> for defining, where to store the | |||
* properties file. | |||
* | |||
* The ModifiedSelector sets the <i>cachefile</i> to the default value | |||
* <i>cache.properties</i>. | |||
* | |||
* Supported <param>s are: | |||
* <table> | |||
* <tr> | |||
* <th>name</th><th>values</th><th>description</th><th>required</th> | |||
* </tr> | |||
* <tr> | |||
* <td> cache.cachefile </td> | |||
* <td> <i>path to file</i> </td> | |||
* <td> the name of the properties file </td> | |||
* <td> yes </td> | |||
* </tr> | |||
* </table> | |||
* | |||
* @author Jan Matèrne | |||
* @version 2003-09-13 | |||
* @since Ant 1.6 | |||
*/ | |||
public class PropertiesfileCache implements Cache { | |||
// ----- member variables - configuration ----- | |||
/** Where to store the properties? */ | |||
private File cachefile = null; | |||
/** Object for storing the key-value-pairs. */ | |||
private Properties cache = new Properties(); | |||
// ----- member variables - internal use ----- | |||
/** Is the cache already loaded? Prevents from multiple load operations. */ | |||
private boolean cacheLoaded = false; | |||
/** Must the cache be saved? Prevents from multiple save operations. */ | |||
private boolean cacheDirty = true; | |||
// ----- Constructors ----- | |||
/** Bean-Constructor. */ | |||
public PropertiesfileCache() { | |||
} | |||
/** | |||
* Constructor. | |||
* @param cachefile set the cachefile | |||
*/ | |||
public PropertiesfileCache(File cachefile) { | |||
this.cachefile = cachefile; | |||
} | |||
// ----- Cache-Configuration ----- | |||
public void setCachefile(File file) { | |||
cachefile = file; | |||
} | |||
public File getCachefile() { return cachefile; } | |||
public boolean isValid() { | |||
return (cachefile != null); | |||
} | |||
// ----- Data Access | |||
public void load() { | |||
if ((cachefile != null) && cachefile.isFile() && cachefile.canRead()) { | |||
try { | |||
BufferedInputStream bis = new BufferedInputStream( | |||
new FileInputStream(cachefile)); | |||
cache.load(bis); | |||
bis.close(); | |||
} catch (Exception e) { | |||
e.printStackTrace(); | |||
} | |||
} | |||
// after loading the cache is up to date with the file | |||
cacheLoaded = true; | |||
cacheDirty = false; | |||
} | |||
/** | |||
* Saves modification of the cache. | |||
* Cache is only saved if there is one ore more entries. | |||
* Because entries can not be deleted by this API, this Cache | |||
* implementation checks the existence of entries before creating the file | |||
* for performance optimisation. | |||
*/ | |||
public void save() { | |||
if (!cacheDirty) { | |||
return; | |||
} | |||
if ((cachefile != null) && cache.propertyNames().hasMoreElements()) { | |||
try { | |||
BufferedOutputStream bos = new BufferedOutputStream( | |||
new FileOutputStream(cachefile)); | |||
cache.store(bos, null); | |||
bos.flush(); | |||
bos.close(); | |||
} catch (Exception e) { | |||
e.printStackTrace(); | |||
} | |||
} | |||
cacheDirty = false; | |||
} | |||
/** Deletes the cache and its underlying file. */ | |||
public void delete() { | |||
cache = new Properties(); | |||
cachefile.delete(); | |||
cacheLoaded = true; | |||
cacheDirty = false; | |||
} | |||
/** | |||
* Returns a value for a given key from the cache. | |||
* @param key the key | |||
* @return the stored value | |||
*/ | |||
public Object get(Object key) { | |||
if (!cacheLoaded) { | |||
load(); | |||
} | |||
try { | |||
return cache.getProperty(String.valueOf(key)); | |||
} catch (ClassCastException e) { | |||
return null; | |||
} | |||
} | |||
/** | |||
* Saves a key-value-pair in the cache. | |||
* @param key the key | |||
* @param value the value | |||
*/ | |||
public void put(Object key, Object value) { | |||
cache.put(String.valueOf(key), String.valueOf(value)); | |||
cacheDirty = true; | |||
} | |||
/** | |||
* Returns an iterator over the keys in the cache. | |||
* @return An iterator over the keys. | |||
*/ | |||
public Iterator iterator() { | |||
Vector v = new java.util.Vector(); | |||
Enumeration en = cache.propertyNames(); | |||
while (en.hasMoreElements()) { | |||
v.add(en.nextElement()); | |||
} | |||
return v.iterator(); | |||
} | |||
// ----- additional ----- | |||
/** | |||
* Override Object.toString(). | |||
* @return information about this cache | |||
*/ | |||
public String toString() { | |||
StringBuffer buf = new StringBuffer(); | |||
buf.append("<PropertiesfileCache:"); | |||
buf.append("cachefile=").append(cachefile); | |||
buf.append(";noOfEntries=").append(cache.size()); | |||
buf.append(">"); | |||
return buf.toString(); | |||
} | |||
} |
@@ -108,6 +108,18 @@ public abstract class BaseSelectorTest extends TestCase { | |||
public abstract BaseSelector getInstance(); | |||
/** | |||
* Return a preconfigured selector (with a set reference to | |||
* project instance). | |||
* @return the selector | |||
*/ | |||
public BaseSelector getSelector() { | |||
BaseSelector selector = getInstance(); | |||
selector.setProject( getProject() ); | |||
return selector; | |||
} | |||
public Project getProject() { | |||
return project; | |||
} | |||
@@ -171,6 +183,66 @@ public abstract class BaseSelectorTest extends TestCase { | |||
return buf.toString(); | |||
} | |||
/** | |||
* Does the selection test for a given selector and prints the | |||
* filenames of the differing files (selected but shouldn´t, | |||
* not selected but should). | |||
* @param selector The selector to test | |||
* @param expected The expected result | |||
*/ | |||
public void performTests(FileSelector selector, String expected) { | |||
String result = selectionString(selector); | |||
String diff = diff(expected, result); | |||
String resolved = resolve(diff); | |||
assertEquals("Differing files: " + resolved, result, expected); | |||
} | |||
/** | |||
* Checks which files are selected and shouldn´t be or which | |||
* are not selected but should. | |||
* @param expected String containing 'F's and 'T's | |||
* @param result String containing 'F's and 'T's | |||
* @return Difference as String containing '-' (equal) and | |||
* 'X' (difference). | |||
*/ | |||
public String diff(String expected, String result) { | |||
int length1 = expected.length(); | |||
int length2 = result.length(); | |||
int min = (length1 > length2) ? length2 : length1; | |||
StringBuffer sb = new StringBuffer(); | |||
for (int i=0; i<min; i++) { | |||
sb.append( | |||
(expected.charAt(i) == result.charAt(i)) | |||
? "-" | |||
: "X" | |||
); | |||
} | |||
return sb.toString(); | |||
} | |||
/** | |||
* Resolves a diff-String (@see diff()) against the (inherited) filenames- | |||
* and files arrays. | |||
* @param filelist Diff-String | |||
* @return String containing the filenames for all differing files, | |||
* separated with semicolons ';' | |||
*/ | |||
public String resolve(String filelist) { | |||
StringBuffer sb = new StringBuffer(); | |||
int min = (filenames.length > filelist.length()) | |||
? filelist.length() | |||
: filenames.length; | |||
for (int i=0; i<min; i++) { | |||
if ('X'==filelist.charAt(i)) { | |||
sb.append(filenames[i]); | |||
sb.append(";"); | |||
} | |||
} | |||
return sb.toString(); | |||
} | |||
/** | |||
* <p>Creates a testbed. We avoid the dreaded "test" word so that we | |||
* don't falsely identify this as a test to be run. The actual | |||
@@ -252,4 +324,4 @@ public abstract class BaseSelectorTest extends TestCase { | |||
} | |||
} |
@@ -0,0 +1,707 @@ | |||
/* | |||
* 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; | |||
// Java | |||
import java.io.File; | |||
import java.io.FileWriter; | |||
import java.util.Comparator; | |||
import java.util.Iterator; | |||
import java.text.RuleBasedCollator; | |||
// Ant | |||
import org.apache.tools.ant.BuildException; | |||
import org.apache.tools.ant.types.Parameter; | |||
// The classes to test | |||
import org.apache.tools.ant.types.selectors.modifiedselector.*; | |||
/** | |||
* Unit tests for ModifiedSelector. | |||
* | |||
* @author Jan Matèrne | |||
* @version 2003-09-13 | |||
* @since Ant 1.6 | |||
*/ | |||
public class ModifiedSelectorTest extends BaseSelectorTest { | |||
/** Package of the CacheSelector classes. */ | |||
private static String pkg = "org.apache.tools.ant.types.selectors.modifiedselector"; | |||
public ModifiedSelectorTest(String name) { | |||
super(name); | |||
} | |||
/** | |||
* Factory method from base class. This should be overriden in child | |||
* classes to return a specific Selector class (like here). | |||
*/ | |||
public BaseSelector getInstance() { | |||
return new ModifiedSelector(); | |||
} | |||
/** Test right use of cache names. */ | |||
public void testValidateWrongCache() { | |||
String name = "this-is-not-a-valid-cache-name"; | |||
try { | |||
ModifiedSelector.CacheName cacheName = new ModifiedSelector.CacheName(); | |||
cacheName.setValue(name); | |||
fail("CacheSelector.CacheName accepted invalid value."); | |||
} catch (BuildException be) { | |||
assertEquals(name + " is not a legal value for this attribute", | |||
be.getMessage()); | |||
} | |||
} | |||
/** Test right use of cache names. */ | |||
public void testValidateWrongAlgorithm() { | |||
String name = "this-is-not-a-valid-algorithm-name"; | |||
try { | |||
ModifiedSelector.AlgorithmName algoName | |||
= new ModifiedSelector.AlgorithmName(); | |||
algoName.setValue(name); | |||
fail("CacheSelector.AlgorithmName accepted invalid value."); | |||
} catch (BuildException be) { | |||
assertEquals(name + " is not a legal value for this attribute", | |||
be.getMessage()); | |||
} | |||
} | |||
/** Test right use of comparator names. */ | |||
public void testValidateWrongComparator() { | |||
String name = "this-is-not-a-valid-comparator-name"; | |||
try { | |||
ModifiedSelector.ComparatorName compName | |||
= new ModifiedSelector.ComparatorName(); | |||
compName.setValue(name); | |||
fail("ModifiedSelector.ComparatorName accepted invalid value."); | |||
} catch (BuildException be) { | |||
assertEquals(name + " is not a legal value for this attribute", | |||
be.getMessage()); | |||
} | |||
} | |||
/** | |||
* Propertycache must have a set 'cachefile' attribute. | |||
* The default in ModifiedSelector "cache.properties" is set by the selector. | |||
*/ | |||
public void testPropcacheInvalid() { | |||
Cache cache = new PropertiesfileCache(); | |||
if (cache.isValid()) | |||
fail("PropertyfilesCache does not check its configuration."); | |||
} | |||
/** | |||
* Tests whether the seldirs attribute is used. | |||
*/ | |||
public void testSeldirs() { | |||
ModifiedSelector s = (ModifiedSelector)getSelector(); | |||
try { | |||
makeBed(); | |||
StringBuffer sbTrue = new StringBuffer(); | |||
StringBuffer sbFalse = new StringBuffer(); | |||
for (int i=0; i<filenames.length; i++) { | |||
if (files[i].isDirectory()) { | |||
sbTrue.append("T"); | |||
sbFalse.append("F"); | |||
} else { | |||
sbTrue.append("T"); | |||
sbFalse.append("T"); | |||
} | |||
} | |||
s.setSeldirs(true); | |||
performTests(s, sbTrue.toString()); | |||
s.getCache().delete(); | |||
s.setSeldirs(false); | |||
performTests(s, sbFalse.toString()); | |||
s.getCache().delete(); | |||
} finally { | |||
cleanupBed(); | |||
if (s!=null) s.getCache().delete(); | |||
} | |||
} | |||
/** | |||
* Complex test scenario using default values (DigestAlgorithm with MD5, | |||
* PropertiesfileCache with file=cache.properties, EqualComparator | |||
* and update=true). <ol> | |||
* <li> try fist time --> should select all </li> | |||
* <li> try second time --> should select no files (only directories) </li> | |||
* <li> modify timestamp of one file and content of a nother one </li> | |||
* <li> try third time --> should select only the file with modified | |||
* content </li> | |||
*/ | |||
public void testScenario1() { | |||
BFT bft = null; | |||
ModifiedSelector s = null; | |||
try { | |||
// | |||
// ***** initialize test environment (called "bed") ***** | |||
// | |||
makeBed(); | |||
String results = null; | |||
// Configure the selector - only defaults are used | |||
s = (ModifiedSelector)getSelector(); | |||
// | |||
// ***** First Run ***** | |||
// the first call should get all files, because nothing is in | |||
// the cache | |||
// | |||
performTests(s, "TTTTTTTTTTTT"); | |||
// | |||
// ***** Second Run ***** | |||
// the second call should get no files, because no content | |||
// has changed | |||
// | |||
performTests(s, "TFFFFFFFFFFT"); | |||
// | |||
// ***** make some files dirty ***** | |||
// | |||
// these files are made dirty --> 3+4 with different content | |||
String f2name = "tar/bz2/asf-logo-huge.tar.bz2"; | |||
String f3name = "asf-logo.gif.md5"; | |||
String f4name = "copy.filterset.filtered"; | |||
// AccessObject to the test-Ant-environment | |||
bft = new BFT(); | |||
// give some values (via property file) to that environment | |||
bft.writeProperties("f2name="+f2name); | |||
bft.writeProperties("f3name="+f3name); | |||
bft.writeProperties("f4name="+f4name); | |||
// call the target for making the files dirty | |||
bft.doTarget("modifiedselectortest-makeDirty"); | |||
// | |||
// ***** Third Run ***** | |||
// third call should get only those files, which CONTENT changed | |||
// (no timestamp changes required!) | |||
results = selectionString(s); | |||
// | |||
// ***** Check the result ***** | |||
// | |||
// Mark all files which should be selected as (T)rue and all others | |||
// as (F)alse. Directories are always selected so they always are | |||
// (T)rue. | |||
StringBuffer expected = new StringBuffer(); | |||
for (int i=0; i<filenames.length; i++) { | |||
String ch = "F"; | |||
if (files[i].isDirectory()) ch = "T"; | |||
// f2name shouldn´t be selected: only timestamp has changed! | |||
if (filenames[i].equalsIgnoreCase(f3name)) ch = "T"; | |||
if (filenames[i].equalsIgnoreCase(f4name)) ch = "T"; | |||
expected.append(ch); | |||
} | |||
assertEquals( | |||
"Wrong files selected. Differing files: " // info text | |||
+ resolve(diff(expected.toString(), results)), // list of files | |||
expected.toString(), // expected result | |||
results // result | |||
); | |||
} finally { | |||
// cleanup the environment | |||
cleanupBed(); | |||
if (s!=null) s.getCache().delete(); | |||
if (bft!=null) bft.deletePropertiesfile(); | |||
} | |||
} | |||
/** | |||
* This scenario is based on scenario 1, but does not use any | |||
* default value and its based on <custom> selector. Used values are:<ul> | |||
* <li><b>Cache: </b> Propertyfile, | |||
* cachefile={java.io.tmpdir}/mycache.txt </li> | |||
* <li><b>Algorithm: </b> Digest | |||
* algorithm=SHA, Provider=null </li> | |||
* <li><b>Comparator: </b> java.text.RuleBasedCollator | |||
* <li><b>Update: </b> true </li> | |||
*/ | |||
public void testScenario2() { | |||
ExtendSelector s = new ExtendSelector(); | |||
BFT bft = new BFT(); | |||
String cachefile = System.getProperty("java.io.tmpdir")+"/mycache.txt"; | |||
try { | |||
makeBed(); | |||
s.setClassname("org.apache.tools.ant.types.selectors.modifiedselector.ModifiedSelector"); | |||
s.addParam(createParam("cache.cachefile", cachefile)); | |||
//s.addParam(createParam("algorithm.provider","---")); // i don´t know any valid | |||
s.addParam(createParam("cache","propertyfile")); | |||
s.addParam(createParam("update","true")); | |||
s.addParam(createParam("comparator","rule")); | |||
s.addParam(createParam("algorithm.name","sha")); | |||
s.addParam(createParam("algorithm","digest")); | |||
// first and second run | |||
performTests(s, "TTTTTTTTTTTT"); | |||
performTests(s, "TFFFFFFFFFFT"); | |||
// make dirty | |||
String f2name = "tar/bz2/asf-logo-huge.tar.bz2"; | |||
String f3name = "asf-logo.gif.md5"; | |||
String f4name = "copy.filterset.filtered"; | |||
bft.writeProperties("f2name="+f2name); | |||
bft.writeProperties("f3name="+f3name); | |||
bft.writeProperties("f4name="+f4name); | |||
bft.doTarget("modifiedselectortest-makeDirty"); | |||
// third run | |||
String results = selectionString(s); | |||
StringBuffer expected = new StringBuffer(); | |||
for (int i=0; i<filenames.length; i++) { | |||
String ch = "F"; | |||
if (files[i].isDirectory()) ch = "T"; | |||
if (filenames[i].equalsIgnoreCase(f3name)) ch = "T"; | |||
if (filenames[i].equalsIgnoreCase(f4name)) ch = "T"; | |||
expected.append(ch); | |||
} | |||
assertEquals( | |||
"Wrong files selected. Differing files: " // info text | |||
+ resolve(diff(expected.toString(), results)), // list of files | |||
expected.toString(), // expected result | |||
results // result | |||
); | |||
} finally { | |||
// cleanup the environment | |||
cleanupBed(); | |||
(new java.io.File(cachefile)).delete(); | |||
if (bft!=null) bft.deletePropertiesfile(); | |||
} | |||
} | |||
/** Checks whether a cache file is created. */ | |||
public void testCreatePropertiesCacheDirect() { | |||
File basedir = getSelector().getProject().getBaseDir(); | |||
File cachefile = new File(basedir, "cachefile.properties"); | |||
PropertiesfileCache cache = new PropertiesfileCache(); | |||
cache.setCachefile(cachefile); | |||
cache.put("key", "value"); | |||
cache.save(); | |||
assertTrue("Cachefile not created.", cachefile.exists()); | |||
cache.delete(); | |||
assertFalse("Cachefile not deleted.", cachefile.exists()); | |||
} | |||
/** Checks whether a cache file is created. */ | |||
public void testCreatePropertiesCacheViaModifiedSelector() { | |||
File basedir = getSelector().getProject().getBaseDir(); | |||
File cachefile = new File(basedir, "cachefile.properties"); | |||
try { | |||
// initialize test environment (called "bed") | |||
makeBed(); | |||
// Configure the selector | |||
ModifiedSelector s = (ModifiedSelector)getSelector(); | |||
s.addParam("cache.cachefile", cachefile); | |||
ModifiedSelector.CacheName cacheName = new ModifiedSelector.CacheName(); | |||
cacheName.setValue("propertyfile"); | |||
s.setCache(cacheName); | |||
s.setUpdate(true); | |||
// does the selection | |||
String results = selectionString(s); | |||
// evaluate correctness | |||
assertTrue("Cache file is not created.", cachefile.exists()); | |||
} finally { | |||
cleanupBed(); | |||
if (cachefile!=null) cachefile.delete(); | |||
} | |||
} | |||
/** | |||
* In earlier implementations there were problems with the <i>order</i> | |||
* of the <param>s. The scenario was <pre> | |||
* <custom class="ModifiedSelector"> | |||
* <param name="cache.cachefile" value="mycache.properties" /> | |||
* <param name="cache" value="propertyfiles" /> | |||
* </custom> | |||
* </pre> It was important first to set the cache and then to set | |||
* the cache´s configuration parameters. That results in the reorganized | |||
* configure() method of ModifiedSelector. This testcase tests that. | |||
*/ | |||
public void testCreatePropertiesCacheViaCustomSelector() { | |||
File cachefile = org.apache.tools.ant.util.FileUtils.newFileUtils() | |||
.createTempFile("tmp-cache-", ".properties", null); | |||
try { | |||
// initialize test environment (called "bed") | |||
makeBed(); | |||
// Configure the selector | |||
ExtendSelector s = new ExtendSelector(); | |||
s.setClassname("org.apache.tools.ant.types.selectors.modifiedselector.ModifiedSelector"); | |||
s.addParam(createParam("update", "true")); | |||
s.addParam(createParam("cache.cachefile", cachefile.getAbsolutePath())); | |||
s.addParam(createParam("cache", "propertyfile")); | |||
// does the selection | |||
String results = selectionString(s); | |||
// evaluate correctness | |||
assertTrue("Cache file is not created.", cachefile.exists()); | |||
} finally { | |||
cleanupBed(); | |||
if (cachefile!=null) cachefile.delete(); | |||
} | |||
} | |||
public void testEqualComparatorViaSelector() { | |||
ModifiedSelector s = (ModifiedSelector)getSelector(); | |||
ModifiedSelector.ComparatorName compName = new ModifiedSelector.ComparatorName(); | |||
compName.setValue("equal"); | |||
s.setComparator(compName); | |||
try { | |||
performTests(s, "TTTTTTTTTTTT"); | |||
} finally { | |||
s.getCache().delete(); | |||
} | |||
} | |||
public void testRuleComparatorViaSelector() { | |||
ModifiedSelector s = (ModifiedSelector)getSelector(); | |||
ModifiedSelector.ComparatorName compName = new ModifiedSelector.ComparatorName(); | |||
compName.setValue("rule"); | |||
s.setComparator(compName); | |||
try { | |||
performTests(s, "TTTTTTTTTTTT"); | |||
} finally { | |||
s.getCache().delete(); | |||
} | |||
} | |||
public void testHashvalueAlgorithm() { | |||
HashvalueAlgorithm algo = new HashvalueAlgorithm(); | |||
doTest(algo); | |||
} | |||
public void testDigestAlgorithmMD5() { | |||
DigestAlgorithm algo = new DigestAlgorithm(); | |||
algo.setAlgorithm("MD5"); | |||
doTest(algo); | |||
} | |||
public void testDigestAlgorithmSHA() { | |||
DigestAlgorithm algo = new DigestAlgorithm(); | |||
algo.setAlgorithm("SHA"); | |||
doTest(algo); | |||
} | |||
public void testPropertyfileCache() { | |||
PropertiesfileCache cache = new PropertiesfileCache(); | |||
File cachefile = new File("cache.properties"); | |||
cache.setCachefile(cachefile); | |||
doTest(cache); | |||
assertFalse("Cache file not deleted.", cachefile.exists()); | |||
} | |||
public void testEqualComparator() { | |||
EqualComparator comp = new EqualComparator(); | |||
doTest(comp); | |||
} | |||
public void testRuleComparator() { | |||
RuleBasedCollator comp = (RuleBasedCollator)RuleBasedCollator.getInstance(); | |||
doTest(comp); | |||
} | |||
public void testScenarioCoreSelectorDefaults() { | |||
doScenarioTest("modifiedselectortest-scenario-coreselector-defaults", "cache.properties"); | |||
} | |||
public void testSceanrioCoreSelectorSettings() { | |||
doScenarioTest("modifiedselectortest-scenario-coreselector-settings", "core.cache.properties"); | |||
} | |||
public void testScenarioCustomSelectorSettings() { | |||
doScenarioTest("modifiedselectortest-scenario-customselector-settings", "core.cache.properties"); | |||
} | |||
public void doScenarioTest(String target, String cachefilename) { | |||
BFT bft = new BFT(); | |||
bft.setUp(); | |||
File basedir = bft.getProject().getBaseDir(); | |||
File cachefile = new File(basedir, cachefilename); | |||
try { | |||
// do the actions | |||
bft.doTarget("modifiedselectortest-scenario-clean"); | |||
bft.doTarget(target); | |||
// the directories to check | |||
File to1 = new File(basedir, "selectortest/to-1"); | |||
File to2 = new File(basedir, "selectortest/to-2"); | |||
File to3 = new File(basedir, "selectortest/to-3"); | |||
// do the checks | |||
assertTrue("Cache file not created.", cachefile.exists()); | |||
assertTrue("Not enough files copied on first time.", to1.list().length>5); | |||
assertTrue("Too much files copied on second time.", to2.list().length==0); | |||
assertTrue("Too much files copied on third time.", to3.list().length==2); | |||
// don´t catch the JUnit exceptions | |||
} finally { | |||
bft.doTarget("modifiedselectortest-scenario-clean"); | |||
bft.deletePropertiesfile(); | |||
bft.tearDown(); | |||
cachefile.delete(); | |||
} | |||
} | |||
// ==================== Test interface semantic =================== | |||
/** | |||
* This method does some common test for algorithm implementations. | |||
* An algorithm must return always the same value for the same file and | |||
* it must not return <i>null</i>. | |||
* | |||
* @param algo configured test object | |||
*/ | |||
protected void doTest(Algorithm algo) { | |||
assertTrue("Algorithm not proper configured.", algo.isValid()); | |||
try { | |||
makeBed(); | |||
for (int i=0; i<files.length; i++) { | |||
File file = files[i]; // must not be a directory | |||
if (file.isFile()) { | |||
// get the Hashvalues | |||
String hash1 = algo.getValue(file); | |||
String hash2 = algo.getValue(file); | |||
String hash3 = algo.getValue(file); | |||
String hash4 = algo.getValue(file); | |||
String hash5 = algo.getValue(new File(file.getAbsolutePath())); | |||
// Assert !=null and equality | |||
assertNotNull("Hashvalue was null for "+file.getAbsolutePath(), hash1); | |||
assertNotNull("Hashvalue was null for "+file.getAbsolutePath(), hash2); | |||
assertNotNull("Hashvalue was null for "+file.getAbsolutePath(), hash3); | |||
assertNotNull("Hashvalue was null for "+file.getAbsolutePath(), hash4); | |||
assertNotNull("Hashvalue was null for "+file.getAbsolutePath(), hash5); | |||
assertEquals("getHashvalue() returned different value for "+file.getAbsolutePath(), hash1, hash2); | |||
assertEquals("getHashvalue() returned different value for "+file.getAbsolutePath(), hash1, hash3); | |||
assertEquals("getHashvalue() returned different value for "+file.getAbsolutePath(), hash1, hash4); | |||
assertEquals("getHashvalue() returned different value for "+file.getAbsolutePath(), hash1, hash5); | |||
}//if-isFile | |||
}//for | |||
} finally { | |||
cleanupBed(); | |||
} | |||
} | |||
/** | |||
* This method does some common test for cache implementations. | |||
* A cache must return a stored value and a valid iterator. | |||
* After calling the delete() the cache must be empty. | |||
* | |||
* @param algo configured test object | |||
*/ | |||
protected void doTest(Cache cache) { | |||
assertTrue("Cache not proper configured.", cache.isValid()); | |||
String key1 = "key1"; | |||
String value1 = "value1"; | |||
String key2 = "key2"; | |||
String value2 = "value2"; | |||
// given cache must be empty | |||
Iterator it1 = cache.iterator(); | |||
assertFalse("Cache is not empty", it1.hasNext()); | |||
// cache must return a stored value | |||
cache.put(key1, value1); | |||
cache.put(key2, value2); | |||
assertEquals("cache returned wrong value", value1, cache.get(key1)); | |||
assertEquals("cache returned wrong value", value2, cache.get(key2)); | |||
// test the iterator | |||
Iterator it2 = cache.iterator(); | |||
Object returned = it2.next(); | |||
boolean ok = (key1.equals(returned) || key2.equals(returned)); | |||
String msg = "Iterator returned unexpected value." | |||
+ " key1.equals(returned)="+key1.equals(returned) | |||
+ " key2.equals(returned)="+key2.equals(returned) | |||
+ " returned="+returned | |||
+ " ok="+ok; | |||
assertTrue(msg, ok); | |||
// clear the cache | |||
cache.delete(); | |||
Iterator it3 = cache.iterator(); | |||
assertFalse("Cache is not empty", it1.hasNext()); | |||
} | |||
/** | |||
* This method does some common test for comparator implementations. | |||
* | |||
* @param algo configured test object | |||
*/ | |||
protected void doTest(Comparator comp) { | |||
Object o1 = new String("string1"); | |||
Object o2 = new String("string2"); | |||
Object o3 = new String("string2"); // really "2" | |||
assertTrue("Comparator gave wrong value.", comp.compare(o1, o2) != 0); | |||
assertTrue("Comparator gave wrong value.", comp.compare(o1, o3) != 0); | |||
assertTrue("Comparator gave wrong value.", comp.compare(o2, o3) == 0); | |||
} | |||
// ======================== Helper methods ======================== | |||
private Parameter createParam(String name, String value) { | |||
Parameter p = new Parameter(); | |||
p.setName(name); | |||
p.setValue(value); | |||
return p; | |||
} | |||
private class BFT extends org.apache.tools.ant.BuildFileTest { | |||
BFT() { super("nothing"); } | |||
BFT(String name) { | |||
super(name); | |||
} | |||
String propfile = "ModifiedSelectorTest.properties"; | |||
boolean isConfigured = false; | |||
public void setUp() { | |||
configureProject("src/etc/testcases/types/selectors.xml"); | |||
isConfigured = true; | |||
} | |||
public void tearDown() { } | |||
public void doTarget(String target) { | |||
if (!isConfigured) setUp(); | |||
executeTarget(target); | |||
} | |||
public void writeProperties(String line) { | |||
if (!isConfigured) setUp(); | |||
File dir = getProject().getBaseDir(); | |||
File file = new File(dir, propfile); | |||
try { | |||
java.io.FileWriter out = new java.io.FileWriter(file, true); | |||
out.write(line); | |||
out.write(System.getProperty("line.separator")); | |||
out.flush(); | |||
out.close(); | |||
} catch (Exception e) { | |||
e.printStackTrace(); | |||
} | |||
} | |||
public void deletePropertiesfile() { | |||
if (!isConfigured) setUp(); | |||
new File(getProject().getBaseDir(), propfile).delete(); | |||
} | |||
public org.apache.tools.ant.Project getProject() { | |||
return super.getProject(); | |||
} | |||
}//class-BFT | |||
}//class-ModifiedSelectorTest |