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