Browse Source

New <modified> container.

Bugzilla Report 20474


git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@275236 13f79535-47bb-0310-9956-ffa450edef68
master
Jan Materne 21 years ago
parent
commit
3c1db41ac7
18 changed files with 2853 additions and 30 deletions
  1. +3
    -0
      WHATSNEW
  2. +127
    -5
      docs/manual/CoreTypes/selectors-program.html
  3. +203
    -14
      docs/manual/CoreTypes/selectors.html
  4. +133
    -3
      src/etc/testcases/types/selectors.xml
  5. +11
    -1
      src/main/org/apache/tools/ant/taskdefs/Delete.java
  6. +11
    -1
      src/main/org/apache/tools/ant/taskdefs/MatchingTask.java
  7. +11
    -1
      src/main/org/apache/tools/ant/types/AbstractFileSet.java
  8. +10
    -2
      src/main/org/apache/tools/ant/types/selectors/BaseSelectorContainer.java
  9. +8
    -2
      src/main/org/apache/tools/ant/types/selectors/SelectorContainer.java
  10. +85
    -0
      src/main/org/apache/tools/ant/types/selectors/modifiedselector/Algorithm.java
  11. +109
    -0
      src/main/org/apache/tools/ant/types/selectors/modifiedselector/Cache.java
  12. +241
    -0
      src/main/org/apache/tools/ant/types/selectors/modifiedselector/DigestAlgorithm.java
  13. +97
    -0
      src/main/org/apache/tools/ant/types/selectors/modifiedselector/EqualComparator.java
  14. +116
    -0
      src/main/org/apache/tools/ant/types/selectors/modifiedselector/HashvalueAlgorithm.java
  15. +653
    -0
      src/main/org/apache/tools/ant/types/selectors/modifiedselector/ModifiedSelector.java
  16. +255
    -0
      src/main/org/apache/tools/ant/types/selectors/modifiedselector/PropertiesfileCache.java
  17. +73
    -1
      src/testcases/org/apache/tools/ant/types/selectors/BaseSelectorTest.java
  18. +707
    -0
      src/testcases/org/apache/tools/ant/types/selectors/ModifiedSelectorTest.java

+ 3
- 0
WHATSNEW View File

@@ -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
===================================



+ 127
- 5
docs/manual/CoreTypes/selectors-program.html View File

@@ -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>&lt;custom&gt;</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:&lt;<font color=blue>FTTTFTTTF...</font>> but was:&lt;TTTTTTTTT...>
[junit] junit.framework.ComparisonFailure: Error for files: .;copy.filterset.filtered;tar/gz/asf-logo.gif.tar.gz
[junit] expected:&lt;FTTTFTTTF...> but was:&lt;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 &copy; 2002 Apache Software
<p align="center">Copyright &copy; 2002-2003 Apache Software
Foundation. All rights Reserved.</p>

</body>

</html>
</html>

+ 203
- 14
docs/manual/CoreTypes/selectors.html View File

@@ -40,28 +40,31 @@

<ul>
<li><a href="#containsselect">&lt;contains&gt;</a> - Select
files that contain a particular text string
files that contain a particular text string</li>
<li><a href="#dateselect">&lt;date&gt;</a> - Select files
that have been modified either before or after a particular date
and time
and time</li>
<li><a href="#dependselect">&lt;depend&gt;</a> - Select files
that have been modified more recently than equivalent files
elsewhere
elsewhere</li>
<li><a href="#depthselect">&lt;depth&gt;</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">&lt;different&gt;</a> - Select files
that are different from those elsewhere
that are different from those elsewhere</li>
<li><a href="#filenameselect">&lt;filename&gt;</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">&lt;present&gt;</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">&lt;containsregexp&gt;</a> - Select
files that match a regular expression
files that match a regular expression</li>
<li><a href="#sizeselect">&lt;size&gt;</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">&lt;type&gt;</a> - Select files
that are either regular files or directories.
that are either regular files or directories.</li>
<li><a href="#modified">&lt;modified&gt;</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 @@
&lt;/fileset&gt;
</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 &lt;modified&gt; 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 &lt;param/&gt; tags. With &lt;param/&gt;
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>
&lt;copy todir="dest">
&lt;filelist dir="src">
&lt;modified/>
&lt;/filelist>
&lt;/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>
&lt;copy todir="dest">
&lt;filelist dir="src">
&lt;modified update="true"
seldirs="true"
cache="propertyfile"
algorithm="digest"
comparator="equal">
&lt;param name="cache.cachefile" value="cache.properties"/>
&lt;param name="algorithm.algorithm" value="MD5"/>
&lt;/modified>
&lt;/filelist>
&lt;/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>
&lt;copy todir="dest">
&lt;filelist dir="src">
&lt;custom class="org.apache.tools.ant.types.selectors.modifiedselector.ModifiedSelector">
&lt;param name="update" value="true"/>
&lt;param name="seldirs" value="true"/>
&lt;param name="cache" value="propertyfile"/>
&lt;param name="algorithm" value="digest"/>
&lt;param name="comparator" value="equal"/>
&lt;param name="cache.cachefile" value="cache.properties"/>
&lt;param name="algorithm.algorithm" value="MD5"/>
&lt;/custom>
&lt;/filelist>
&lt;/copy>
</pre></blockquote>
<p>And this is the same rewritten as CustomSelector.</p>

<blockquote><pre>
&lt;target name="generate-and-upload-site">
&lt;echo> generate the site using forrest &lt;/echo>
&lt;antcall target="site"/>

&lt;echo> upload the changed file &lt;/echo>
&lt;ftp server="${ftp.server}" userid="${ftp.user}" password="${ftp.pwd}">
&lt;fileset dir="htdocs/manual">
&lt;modified/>
&lt;/fileset>
&lt;/ftp>
&lt;/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 @@





+ 133
- 3
src/etc/testcases/types/selectors.xml View File

@@ -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>

+ 11
- 1
src/main/org/apache/tools/ant/taskdefs/Delete.java View File

@@ -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 {
}
}
}


+ 11
- 1
src/main/org/apache/tools/ant/taskdefs/MatchingTask.java View File

@@ -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;
}
}
}

+ 11
- 1
src/main/org/apache/tools/ant/types/AbstractFileSet.java View File

@@ -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,
}
}

}
}

+ 10
- 2
src/main/org/apache/tools/ant/types/selectors/BaseSelectorContainer.java View File

@@ -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);
}

}

}

+ 8
- 2
src/main/org/apache/tools/ant/types/selectors/SelectorContainer.java View File

@@ -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);
}


+ 85
- 0
src/main/org/apache/tools/ant/types/selectors/modifiedselector/Algorithm.java View File

@@ -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);
}

+ 109
- 0
src/main/org/apache/tools/ant/types/selectors/modifiedselector/Cache.java View 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();
}

+ 241
- 0
src/main/org/apache/tools/ant/types/selectors/modifiedselector/DigestAlgorithm.java View File

@@ -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();
}
}

+ 97
- 0
src/main/org/apache/tools/ant/types/selectors/modifiedselector/EqualComparator.java View File

@@ -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";
}
}

+ 116
- 0
src/main/org/apache/tools/ant/types/selectors/modifiedselector/HashvalueAlgorithm.java View File

@@ -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";
}

}

+ 653
- 0
src/main/org/apache/tools/ant/types/selectors/modifiedselector/ModifiedSelector.java View File

@@ -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 &lt;param&gt; 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 &lt;param&gt; 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" };
}
}

}

+ 255
- 0
src/main/org/apache/tools/ant/types/selectors/modifiedselector/PropertiesfileCache.java View File

@@ -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();
}
}

+ 73
- 1
src/testcases/org/apache/tools/ant/types/selectors/BaseSelectorTest.java View File

@@ -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 {



}
}

+ 707
- 0
src/testcases/org/apache/tools/ant/types/selectors/ModifiedSelectorTest.java View File

@@ -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

Loading…
Cancel
Save