jhm 2004/07/10 10:15:37 Modified: src/etc/testcases/types selectors.xml src/testcases/org/apache/tools/ant/types/selectors ModifiedSelectorTest.java docs/manual/CoreTypes selectors.html src/main/org/apache/tools/ant/types/selectors/modifiedselector DigestAlgorithm.java ModifiedSelector.java PropertiesfileCache.java Added: src/testcases/org/apache/tools/ant/types/selectors MockAlgorithm.java MockCache.java MockComparator.java Log: Small redesign of ModifiedSelector. Integrating BugIDs 29742+29743. Adding support of custom classes. Checkstyle. Revision Changes Path 1.8 +15 -0 ant/src/etc/testcases/types/selectors.xml Index: selectors.xml =================================================================== RCS file: /home/cvs/ant/src/etc/testcases/types/selectors.xml,v retrieving revision 1.7 retrieving revision 1.8 diff -u -r1.7 -r1.8 --- selectors.xml 30 Sep 2003 09:45:21 -0000 1.7 +++ selectors.xml 10 Jul 2004 17:15:37 -0000 1.8 @@ -242,4 +242,19 @@ </copy> </target> + <target name="modifiedselectortest-customClasses" depends="modifiedselectortest-scenario-prepare"> + <property name="pkg.live" value="org.apache.tools.ant.types.selectors.modifiedselector"/> + <property name="pkg.test" value="org.apache.tools.ant.types.selectors"/> + <fileset id="fs.mod" dir="${test.dir}/src"> + <modified + algorithmclass="${pkg.test}.MockAlgorithm" + cacheclass="${pkg.test}.MockCache" + comparatorclass="${pkg.test}.MockComparator" + /> + </fileset> + <fileset id="fs.full" dir="${test.dir}/src"/> + <property name="fs.mod.value" refid="fs.mod"/> + <property name="fs.full.value" refid="fs.full"/> + </target> + </project> 1.8 +632 -289 ant/src/testcases/org/apache/tools/ant/types/selectors/ModifiedSelectorTest.java Index: ModifiedSelectorTest.java =================================================================== RCS file: /home/cvs/ant/src/testcases/org/apache/tools/ant/types/selectors/ModifiedSelectorTest.java,v retrieving revision 1.7 retrieving revision 1.8 diff -u -r1.7 -r1.8 --- ModifiedSelectorTest.java 9 Mar 2004 16:49:07 -0000 1.7 +++ ModifiedSelectorTest.java 10 Jul 2004 17:15:37 -0000 1.8 @@ -29,6 +29,12 @@ import org.apache.tools.ant.BuildException; import org.apache.tools.ant.types.Parameter; +// inside MockProject +import org.apache.tools.ant.Project; +import org.apache.tools.ant.Target; +import org.apache.tools.ant.Task; +import org.apache.tools.ant.BuildEvent; + // The classes to test import org.apache.tools.ant.types.selectors.modifiedselector.*; @@ -36,15 +42,22 @@ /** * Unit tests for ModifiedSelector. * - * @version 2003-09-13 + * @version 2004-07-07 * @since Ant 1.6 */ public class ModifiedSelectorTest extends BaseSelectorTest { + + // ===================== attributes ===================== + + /** Package of the CacheSelector classes. */ private static String pkg = "org.apache.tools.ant.types.selectors.modifiedselector"; + // ===================== constructors, factories ===================== + + public ModifiedSelectorTest(String name) { super(name); } @@ -59,6 +72,68 @@ } + // ===================== JUnit stuff ===================== + + + + /* There are two tests which cannot run until the test package is added + * to the core classloader. See comment in ModifiedSelelector.loadClass(). + * The tests should pass then - but because the usual build wont add + * these classes I exclude them from being executed: + * - classloaderProblem_testCustomAlgorithm2 + * - classloaderProblem_testCustomClasses + * + * For activating decomment the suite method. + * + * the addTest-part can be generated via grep and sed: + * grep "void test" ModifiedSelectorTest.java + * | sed -e 's/() {/"));/' + * -e 's/public void / suite.addTest(new ModifiedSelectorTest("/' + */ + + /* * / + // for test only - ignore tests where we arent work at the moment + public static junit.framework.Test suite() { + junit.framework.TestSuite suite= new junit.framework.TestSuite(); + suite.addTest(new ModifiedSelectorTest("testValidateWrongCache")); + suite.addTest(new ModifiedSelectorTest("testValidateWrongAlgorithm")); + suite.addTest(new ModifiedSelectorTest("testValidateWrongComparator")); + suite.addTest(new ModifiedSelectorTest("testIllegalCustomAlgorithm")); + suite.addTest(new ModifiedSelectorTest("testNonExistentCustomAlgorithm")); + suite.addTest(new ModifiedSelectorTest("testCustomAlgorithm")); + suite.addTest(new ModifiedSelectorTest("testPropcacheInvalid")); + suite.addTest(new ModifiedSelectorTest("testPropertyfileCache")); + suite.addTest(new ModifiedSelectorTest("testCreatePropertiesCacheDirect")); + suite.addTest(new ModifiedSelectorTest("testCreatePropertiesCacheViaModifiedSelector")); + suite.addTest(new ModifiedSelectorTest("testCreatePropertiesCacheViaCustomSelector")); + suite.addTest(new ModifiedSelectorTest("testHashvalueAlgorithm")); + suite.addTest(new ModifiedSelectorTest("testDigestAlgorithmMD5")); + suite.addTest(new ModifiedSelectorTest("testDigestAlgorithmSHA")); + suite.addTest(new ModifiedSelectorTest("testChecksumAlgorithm")); + suite.addTest(new ModifiedSelectorTest("testChecksumAlgorithmCRC")); + suite.addTest(new ModifiedSelectorTest("testChecksumAlgorithmAdler")); + suite.addTest(new ModifiedSelectorTest("testEqualComparator")); + suite.addTest(new ModifiedSelectorTest("testRuleComparator")); + suite.addTest(new ModifiedSelectorTest("testEqualComparatorViaSelector")); + suite.addTest(new ModifiedSelectorTest("testSeldirs")); + suite.addTest(new ModifiedSelectorTest("testScenario1")); + suite.addTest(new ModifiedSelectorTest("testScenarioCoreSelectorDefaults")); + suite.addTest(new ModifiedSelectorTest("testScenarioCoreSelectorSettings")); + suite.addTest(new ModifiedSelectorTest("testScenarioCustomSelectorSettings")); + + suite.addTest(new ModifiedSelectorTest("classloaderProblem_testDelayUpdateTaskFinished")); + suite.addTest(new ModifiedSelectorTest("classloaderProblem_testDelayUpdateTargetFinished")); + suite.addTest(new ModifiedSelectorTest("classloaderProblem_testDelayUpdateBuildFinished")); + suite.addTest(new ModifiedSelectorTest("classloaderProblem_testCustomAlgorithm2")); + suite.addTest(new ModifiedSelectorTest("classloaderProblem_testCustomClasses")); + return suite; + } + /* */ + + + // ======= testcases for the attributes and nested elements of the selector ===== + + /** Test right use of cache names. */ public void testValidateWrongCache() { String name = "this-is-not-a-valid-cache-name"; @@ -103,209 +178,177 @@ } - /** - * 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."); + public void testIllegalCustomAlgorithm() { + try { + String algo = getAlgoName("javax.swing.JFrame"); + fail("Illegal classname used."); + } catch (Exception e) { + assertTrue("Wrong exception type: " + e.getClass().getName(), e instanceof BuildException); + assertEquals("Wrong exception message.", + "Specified class (javax.swing.JFrame) is not an Algorithm.", + e.getMessage()); + + } } - /** - * Tests whether the seldirs attribute is used. - */ - public void testSeldirs() { - ModifiedSelector s = (ModifiedSelector)getSelector(); + public void testNonExistentCustomAlgorithm() { + boolean noExcThrown = false; 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"); - } + String algo = getAlgoName("non.existent.custom.Algorithm"); + noExcThrown = true; + } catch (Exception e) { + if (noExcThrown) { + fail("does 'non.existent.custom.Algorithm' really exist?"); } + assertTrue("Wrong exception type: " + e.getClass().getName(), e instanceof BuildException); + assertEquals("Wrong exception message.", + "Specified class (non.existent.custom.Algorithm) not found.", + e.getMessage()); + + } + } - s.setSeldirs(true); - performTests(s, sbTrue.toString()); - s.getCache().delete(); + public void testCustomAlgorithm() { + String algo = getAlgoName("org.apache.tools.ant.types.selectors.modifiedselector.HashvalueAlgorithm"); + assertTrue("Wrong algorithm used: "+algo, algo.startsWith("HashvalueAlgorithm")); + } - s.setSeldirs(false); - performTests(s, sbFalse.toString()); - s.getCache().delete(); - } finally { - cleanupBed(); - if (s!=null) s.getCache().delete(); - } + public void classloaderProblem_testCustomAlgorithm2() { + String algo = getAlgoName("org.apache.tools.ant.types.selectors.MockAlgorithm"); + assertTrue("Wrong algorithm used: "+algo, algo.startsWith("MockAlgorithm")); } - /** - * 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; + public void classloaderProblem_testCustomClasses() { + BFT bft = new BFT(); + bft.setUp(); try { - // - // ***** initialize test environment (called "bed") ***** - // - makeBed(); - String results = null; + // do the actions + bft.doTarget("modifiedselectortest-customClasses"); + // do the checks - the buildfile stores the fileset as property + String fsFullValue = bft.getProperty("fs.full.value"); + String fsModValue = bft.getProperty("fs.mod.value"); + + assertNotNull("'fs.full.value' must be set.", fsFullValue); + assertTrue("'fs.full.value' must not be null.", !"".equals(fsFullValue)); + assertTrue("'fs.full.value' must contain ant.bat.", fsFullValue.indexOf("ant.bat")>-1); + + assertNotNull("'fs.mod.value' must be set.", fsModValue); + // must be empty according to the Mock* implementations + assertTrue("'fs.mod.value' must be empty.", "".equals(fsModValue)); + // don't catch the JUnit exceptions + } finally { + bft.doTarget("modifiedselectortest-scenario-clean"); + bft.deletePropertiesfile(); + bft.tearDown(); + } + } - // 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"); + public void classloaderProblem_testDelayUpdateTaskFinished() { + doDelayUpdateTest(1); + } - // - // ***** Second Run ***** - // the second call should get no files, because no content - // has changed - // - performTests(s, "TFFFFFFFFFFT"); - // - // ***** make some files dirty ***** - // + public void classloaderProblem_testDelayUpdateTargetFinished() { + doDelayUpdateTest(2); + } - // 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"); + public void classloaderProblem_testDelayUpdateBuildFinished() { + doDelayUpdateTest(3); + } - // - // ***** Third Run ***** - // third call should get only those files, which CONTENT changed - // (no timestamp changes required!) - results = selectionString(s); + public void doDelayUpdateTest(int kind) { + // no check for 1<=kind<=3 - only internal use therefore check it + // while development - // - // ***** Check the result ***** - // + // readable form of parameter kind + String[] kinds = {"task", "target", "build"}; - // 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); - } + // setup the "Ant project" + MockProject project = new MockProject(); + File base = new File("base"); + File file1 = new File("file1"); + File file2 = new File("file2"); - assertEquals( - "Wrong files selected. Differing files: " // info text - + resolve(diff(expected.toString(), results)), // list of files - expected.toString(), // expected result - results // result - ); + // setup the selector + ModifiedSelector sel = new ModifiedSelector(); + sel.setProject(project); + sel.setUpdate(true); + sel.setDelayUpdate(true); + sel.setAlgorithmClass("org.apache.tools.ant.types.selectors.MockAlgorithm"); + sel.setCacheClass("org.apache.tools.ant.types.selectors.MockCache"); + sel.configure(); - } finally { - // cleanup the environment - cleanupBed(); - if (s!=null) s.getCache().delete(); - if (bft!=null) bft.deletePropertiesfile(); + // get the cache, so we can check our things + MockCache cache = (MockCache)sel.getCache(); + + // the test + assertFalse("Cache must not be saved before 1st selection.", cache.saved); + sel.isSelected(base, "file1", file1); + assertFalse("Cache must not be saved after 1st selection.", cache.saved); + sel.isSelected(base, "file2", file2); + assertFalse("Cache must not be saved after 2nd selection.", cache.saved); + switch (kind) { + case 1 : project.fireTaskFinished(); break; + case 2 : project.fireTargetFinished(); break; + case 3 : project.fireBuildFinished(); break; } - } + assertTrue("Cache must be saved after " + kinds[kind-1] + "Finished-Event.", cache.saved); + // MockCache doesnt create a file - therefore no cleanup needed + } /** - * 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> + * Extracts the real used algorithm name from the ModifiedSelector using + * its toString() method. + * @param classname the classname from the algorithm to use + * @return the algorithm part from the toString() (without brackets) */ - public void testScenario2() { - ExtendSelector s = new ExtendSelector(); - BFT bft = new BFT(); - String cachefile = System.getProperty("java.io.tmpdir")+"/mycache.txt"; - try { - makeBed(); + private String getAlgoName(String classname) { + ModifiedSelector sel = new ModifiedSelector(); + sel.setAlgorithmClass(classname); + // let the selector do its checks + sel.validate(); + // extract the algorithm name (and config) from the selectors output + String s1 = sel.toString(); + int posStart = s1.indexOf("algorithm=") + 10; + int posEnd = s1.indexOf(" comparator="); + String algo = s1.substring(posStart, posEnd); + // '<' and '>' are only used if the algorithm has properties + if (algo.startsWith("<")) algo = algo.substring(1); + if (algo.endsWith(">")) algo = algo.substring(0, algo.length()-1); + // return the clean value + return algo; + } - 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")); + // ================ testcases for the cache implementations ================ - // 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(); - } + + /** + * 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."); + } + + + 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()); } @@ -338,6 +381,7 @@ // Configure the selector ModifiedSelector s = (ModifiedSelector)getSelector(); + s.setDelayUpdate(false); s.addParam("cache.cachefile", cachefile); ModifiedSelector.CacheName cacheName = new ModifiedSelector.CacheName(); @@ -396,30 +440,56 @@ } - 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 _testCustomCache() { + // same logic as on algorithm, no testcases created } - 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(); - } - } + /** + * Test the interface semantic of Caches. + * 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()); + } + + + // ============== testcases for the algorithm implementations ============== public void testHashvalueAlgorithm() { @@ -427,12 +497,14 @@ doTest(algo); } + public void testDigestAlgorithmMD5() { DigestAlgorithm algo = new DigestAlgorithm(); algo.setAlgorithm("MD5"); doTest(algo); } + public void testDigestAlgorithmSHA() { DigestAlgorithm algo = new DigestAlgorithm(); algo.setAlgorithm("SHA"); @@ -440,77 +512,28 @@ } - 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 testChecksumAlgorithm() { + ChecksumAlgorithm algo = new ChecksumAlgorithm(); + doTest(algo); } - public void testScenarioCustomSelectorSettings() { - doScenarioTest("modifiedselectortest-scenario-customselector-settings", "core.cache.properties"); + public void testChecksumAlgorithmCRC() { + ChecksumAlgorithm algo = new ChecksumAlgorithm(); + algo.setAlgorithm("CRC"); + doTest(algo); } - 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(); - } + public void testChecksumAlgorithmAdler() { + ChecksumAlgorithm algo = new ChecksumAlgorithm(); + algo.setAlgorithm("Adler"); + doTest(algo); } - // ==================== Test interface semantic =================== - - /** + * Test the interface semantic of Algorithms. * 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>. @@ -550,50 +573,55 @@ } - /** - * 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"; + // ============== testcases for the comparator implementations ============== - // 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)); + public void testEqualComparator() { + EqualComparator comp = new EqualComparator(); + doTest(comp); + } - // 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()); + public void testRuleComparator() { + RuleBasedCollator comp = (RuleBasedCollator)RuleBasedCollator.getInstance(); + doTest(comp); + } + + + 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() { //not yet supported see note in selector + 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 _testCustomComparator() { + // same logic as on algorithm, no testcases created } /** + * Test the interface semantic of Comparators. * This method does some common test for comparator implementations. * * @param algo configured test object @@ -609,9 +637,255 @@ } - // ======================== Helper methods ======================== + // ===================== scenario tests ===================== + + + /** + * 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() { // RuleBasedCollator not yet supported - see Selector:375 note + 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(); + } + } + + + public void testScenarioCoreSelectorDefaults() { + doScenarioTest("modifiedselectortest-scenario-coreselector-defaults", "cache.properties"); + } + + + public void testScenarioCoreSelectorSettings() { + 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(); + } + } + + + // ===================== helper methods and classes ==================== + + + /** + * Creates a configured parameter object. + * @param name name of the parameter + * @param value value of the parameter + * @return the parameter object + */ private Parameter createParam(String name, String value) { Parameter p = new Parameter(); p.setName(name); @@ -620,6 +894,11 @@ } + /** + * The BFT class wrapps the selector test-builfile inside an + * ant project (BuildFileTest). It supports target execution + * and property transfer to that project. + */ private class BFT extends org.apache.tools.ant.BuildFileTest { BFT() { super("nothing"); } BFT(String name) { @@ -641,6 +920,10 @@ executeTarget(target); } + public String getProperty(String property) { + return project.getProperty(property); + } + public void writeProperties(String line) { if (!isConfigured) setUp(); File dir = getProject().getBaseDir(); @@ -667,4 +950,64 @@ } }//class-BFT -}//class-ModifiedSelectorTest + + /** + * MockProject wrappes a very small ant project (one target, one task) + * but provides public methods to fire the build events. + */ + private class MockProject extends Project { + private Task task; + private Target target; + + public MockProject() { + task = new Task(){ + public void execute() { + } + }; + task.setTaskName("testTask"); + target = new Target(); + target.setName("testTarget"); + target.setProject(this); + target.addTask(task); + task.setOwningTarget(target); + } + + public void fireBuildStarted() { + super.fireBuildStarted(); + } + public void fireBuildFinished() { + super.fireBuildFinished(null); + } + public void fireSubBuildStarted() { + super.fireSubBuildStarted(); + } + public void fireSubBuildFinished() { + super.fireSubBuildFinished(null); + } + public void fireTargetStarted() { + super.fireTargetStarted(target); + } + public void fireTargetFinished() { + super.fireTargetFinished(target, null); + } + public void fireTaskStarted() { + super.fireTaskStarted(task); + } + public void fireTaskFinished() { + super.fireTaskFinished(task, null); + } + private void fireMessageLoggedEvent(BuildEvent event, String message, int priority) { + } + private void fireMessageLoggedProject(String message) { + super.fireMessageLogged(this, message, Project.MSG_INFO); + } + private void fireMessageLoggedTarget(String message) { + super.fireMessageLogged(target, message, Project.MSG_INFO); + } + private void fireMessageLoggedTask(String message) { + super.fireMessageLogged(task, message, Project.MSG_INFO); + } + }//class-MockProject + + +}//class-ModifiedSelectorTest \ No newline at end of file 1.1 ant/src/testcases/org/apache/tools/ant/types/selectors/MockAlgorithm.java Index: MockAlgorithm.java =================================================================== /* * Copyright 2003-2004 The Apache Software Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package org.apache.tools.ant.types.selectors; import java.io.File; import org.apache.tools.ant.types.selectors.modifiedselector.Algorithm; public class MockAlgorithm implements Algorithm { public boolean isValid() { return true; } public String getValue(File file) { return "TEST"; } public String toString() { return "MockAlgorithm@" + hashCode(); } } 1.1 ant/src/testcases/org/apache/tools/ant/types/selectors/MockCache.java Index: MockCache.java =================================================================== /* * Copyright 2003-2004 The Apache Software Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package org.apache.tools.ant.types.selectors; import java.util.Iterator; import org.apache.tools.ant.types.selectors.modifiedselector.Cache; public class MockCache implements Cache { public boolean debug = false; public boolean saved = false; public MockCache() { log("()"); } public boolean isValid() { log(".isValid()"); return true; } public void delete() { log(".delete()"); } public void load() { log(".load()"); } public void save() { log(".save()"); saved = true; } public Object get(Object key) { log(".get("+key+")"); return key; } public void put(Object key, Object value) { log(".put("+key+", "+value+")"); saved = false; } public Iterator iterator() { log("iterator()"); return null; } public String toString() { return "MockCache@" + hashCode(); } private void log(String msg) { if (debug) System.out.println(this+msg); } }//class-MockCache 1.1 ant/src/testcases/org/apache/tools/ant/types/selectors/MockComparator.java Index: MockComparator.java =================================================================== /* * Copyright 2003-2004 The Apache Software Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package org.apache.tools.ant.types.selectors; import java.util.Comparator; public class MockComparator implements Comparator { public int compare(Object o1, Object o2) { return 0; } public String toString() { return "MockComparator"; } }//class-MockCache 1.23 +70 -3 ant/docs/manual/CoreTypes/selectors.html Index: selectors.html =================================================================== RCS file: /home/cvs/ant/docs/manual/CoreTypes/selectors.html,v retrieving revision 1.22 retrieving revision 1.23 diff -u -r1.22 -r1.23 --- selectors.html 26 Apr 2004 19:22:21 -0000 1.22 +++ selectors.html 10 Jul 2004 17:15:37 -0000 1.23 @@ -2,7 +2,7 @@ <head> <meta http-equiv="Content-Language" content="en-us"> <title>Selectors in Ant</title> -<link rel="stylesheet" type="text/css" href="../stylesheets/antmanual.css"> + <link rel="stylesheet" type="text/css" href="../stylesheets/antmanual.css"> </head> <body> @@ -652,6 +652,7 @@ <ul> <li> hashvalue - HashvalueAlgorithm </li> <li> digest - DigestAlgorithm </li> + <li> checksum - ChecksumAlgorithm </li> </ul> </td> <td valign="top" align="center"> No, defaults to <i>digest</i> </td> @@ -672,12 +673,36 @@ Acceptable values are (further information see later): <ul> <li> equal - EqualComparator </li> - <li> rule - java.text.RuleBasedCollator </li> + <li> rule - java.text.RuleBasedCollator + <!-- NOTE --> + <i>(see <a href="#ModSelNote">note</a> for restrictions)</i> + </li> </ul> </td> <td valign="top" align="center"> No, defaults to <i>equal</i> </td> </tr> <tr> + <td valign="top"> algorithmclass </td> + <td valign="top"> Classname of custom algorithm implementation. Lower + priority than <i>algorithm</i>. + <!-- NOTE --> (see <a href="#ModSelNote">note</a> for restrictions) </td> + <td valign="top" align="center"> No </td> + </tr> + <tr> + <td valign="top"> cacheclass </td> + <td valign="top"> Classname of custom cache implementation. Lower + priority than <i>cache</i>. + <!-- NOTE --> (see <a href="#ModSelNote">note</a> for restrictions) </td> + <td valign="top" align="center"> No </td> + </tr> + <tr> + <td valign="top"> comparatorclass </td> + <td valign="top"> Classname of custom comparator implementation. Lower + priority than <i>comparator</i>. + <!-- NOTE --> (see <a href="#ModSelNote">note</a> for restrictions) </td> + <td valign="top" align="center"> No </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> @@ -687,6 +712,15 @@ <td valign="top"> Should directories be selected? (boolean) </td> <td valign="top" align="center"> No, defaults to <i>true</i> </td> </tr> + <tr> + <td valign="top"> delayupdate </td> + <td valign="top"> If set to <i>true</i>, the storage of the cache will be delayed until the + next finished BuildEvent; task finished, target finished or build finished, + whichever comes first. This is provided for increased performance. If set + to <i>false</i>, the storage of the cache will happen with each change. This + attribute depends upon the <i>update</i> attribute. (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/> @@ -695,6 +729,9 @@ <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 comparator </li> + <li> <b> algorithmclass </b>: same as attribute algorithmclass </li> + <li> <b> cacheclass </b>: same as attribute cacheclass </li> + <li> <b> comparatorclass </b>: same as attribute comparatorclass </li> <li> <b> update </b>: same as attribute update </li> <li> <b> seldirs </b>: same as attribute seldirs </li> <li> <b> algorithm.* </b>: Value is transfered to the algorithm via its @@ -729,6 +766,16 @@ </ul> </td> </tr> + <tr> + <td valign="top"> checksum </td> + <td valign="top"> Uses java.util.zip.Checksum. This Algorithm supports + the following attributes: + <ul> + <li><i>algorithm.algorithm</i> (optional): Name of the algorithm + (e.g. 'CRC' or 'ADLER', default = <i>CRC</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> @@ -750,6 +797,8 @@ <td valign="top"> rule </td> <td valign="top"> Uses <i>java.text.RuleBasedCollator</i> for Object comparison. + <!-- NOTE --> + <i>(see <a href="#ModSelNote">note</a> for restrictions)</i> </td> </tr> </table> @@ -820,6 +869,24 @@ CacheSelector saves therefore much upload time.</p> + <!-- NOTE --> + <i>(see <a href="#ModSelNote">note</a> for restrictions)</i> + <a name="ModSelNote"></a> + <h4>Note on RuleBasedCollator</h4> + <p>The RuleBasedCollator needs a format for its work, but its needed while + instantiation. There is a problem in the initialization algorithm for this + case. Therefore you should not use this (or tell me the workaround :-).</p> + + <p>The selector can not find the specified algorithm-, cache- or comparator- + class if the selector is loaded from a different classloader. + To be able to use your own classes you have to ensure that the selector + can find the classes by adding the your package to the core: <ul> + <li> by placing your JAR in %ANT_HOME/lib </li> + <li> by adding '-lib myclasses/' while invocation </li> + <li> by placing your JAR in ${ant.home}/.ant/lib </li> + </ul></p> + + <a name="selectcontainers"></a> <h3>Selector Containers</h3> @@ -1216,4 +1283,4 @@ </body> -</html> +</html> \ No newline at end of file 1.7 +4 -5 ant/src/main/org/apache/tools/ant/types/selectors/modifiedselector/DigestAlgorithm.java Index: DigestAlgorithm.java =================================================================== RCS file: /home/cvs/ant/src/main/org/apache/tools/ant/types/selectors/modifiedselector/DigestAlgorithm.java,v retrieving revision 1.6 retrieving revision 1.7 diff -u -r1.6 -r1.7 --- DigestAlgorithm.java 9 Mar 2004 16:48:49 -0000 1.6 +++ DigestAlgorithm.java 10 Jul 2004 17:15:37 -0000 1.7 @@ -51,7 +51,7 @@ * </tr> * </table> * - * @version 2003-09-13 + * @version 2004-07-08 * @since Ant 1.6 */ public class DigestAlgorithm implements Algorithm { @@ -132,12 +132,11 @@ /** - * This algorithm doesn't need any configuration. - * Therefore it's always valid. + * This algorithm supports only MD5 and SHA. * @return <i>true</i> if all is ok, otherwise <i>false</i>. */ public boolean isValid() { - return true; + return "SHA".equalsIgnoreCase(algorithm) || "MD5".equalsIgnoreCase(algorithm); } @@ -200,4 +199,4 @@ buf.append(">"); return buf.toString(); } -} +} \ No newline at end of file 1.7 +331 -69 ant/src/main/org/apache/tools/ant/types/selectors/modifiedselector/ModifiedSelector.java Index: ModifiedSelector.java =================================================================== RCS file: /home/cvs/ant/src/main/org/apache/tools/ant/types/selectors/modifiedselector/ModifiedSelector.java,v retrieving revision 1.6 retrieving revision 1.7 diff -u -r1.6 -r1.7 --- ModifiedSelector.java 9 Mar 2004 16:48:49 -0000 1.6 +++ ModifiedSelector.java 10 Jul 2004 17:15:37 -0000 1.7 @@ -27,6 +27,9 @@ // Ant import org.apache.tools.ant.Project; import org.apache.tools.ant.IntrospectionHelper; +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.BuildListener; +import org.apache.tools.ant.BuildEvent; import org.apache.tools.ant.types.EnumeratedAttribute; import org.apache.tools.ant.types.Parameter; import org.apache.tools.ant.types.selectors.BaseExtendSelector; @@ -93,7 +96,7 @@ * 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/"> + * for homepage generation (e.g. with <a href="http://forrest.apache.org/"> * Apache Forrest</a>). <pre> * <target name="generate-and-upload-site"> * <echo> generate the site using forrest </echo> @@ -109,7 +112,7 @@ * </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: + * <p>This selector supports the following attributes: * <table> * <tr><th>name</th><th>values</th><th>description</th><th>required</th></tr> * <tr> @@ -122,20 +125,21 @@ * </tr> * <tr> * <td> algorithm </td> - * <td> hashvalue | digest </td> + * <td> hashvalue | digest | checksum </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> + * <li><b>checksum</b> - uses java.util.zip.Checksum interface </i> * </td> * <td> no, defaults to digest </td> * </tr> * <tr> * <td> comparator </td> - * <td> equal | role </td> + * <td> equal | rule </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> + * <li><b>rule</b> - uses java.text.RuleBasedCollator class </i> * </td> * <td> no, defaults to equal </td> * </tr> @@ -153,6 +157,34 @@ * <td> no, defaults to true </td> * </tr> * <tr> + * <td> delayupdate </td> + * <td> true | false </td> + * <td> If set to <i>true</i>, the storage of the cache will be delayed until the + * next finished BuildEvent; task finished, target finished or build finished, + * whichever comes first. This is provided for increased performance. If set + * to <i>false</i>, the storage of the cache will happen with each change. This + * attribute depends upon the <i>update</i> attribute.</td> + * <td> no, defaults to true </td> + * </tr> + * <tr> + * <td> cacheclass </td> + * <td> <i>classname</i> </td> + * <td> which custom cache implementation should be used </td> + * <td> no </td> + * </tr> + * <tr> + * <td> algorithmclass </td> + * <td> <i>classname</i> </td> + * <td> which custom algorithm implementation should be used </td> + * <td> no </td> + * </tr> + * <tr> + * <td> comparatorclass </td> + * <td> <i>classname</i> </td> + * <td> which custom comparator implementation should be used </td> + * <td> no </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> @@ -182,23 +214,32 @@ * a nested <i><param name="algorithm.provider" value="MyProvider"/></i>. * * - * @version 2003-09-13 + * @version 2004-07-09 * @since Ant 1.6 -*/ -public class ModifiedSelector extends BaseExtendSelector { + */ +public class ModifiedSelector extends BaseExtendSelector implements BuildListener { - // ----- member variables - configuration + // ----- attributes ----- - /** The Cache containing the old values. */ - private Cache cache = null; + /** Cache name for later instantiation. */ + private CacheName cacheName = null; - /** Algorithm for computing new values and updating the cache. */ - private Algorithm algorithm = null; + /** User specified classname for Cache. */ + private String cacheClass; - /** How should the cached value and the new one compared? */ - private Comparator comparator = null; + /** Algorithm name for later instantiation. */ + private AlgorithmName algoName = null; + + /** User specified classname for Algorithm. */ + private String algorithmClass; + + /** Comparator name for later instantiation. */ + private ComparatorName compName = null; + + /** User specified classname for Comparator. */ + private String comparatorClass; /** Should the cache be updated? */ private boolean update = true; @@ -206,22 +247,27 @@ /** Are directories selected? */ private boolean selectDirectories = true; + /** Delay the writing of the cache file */ + private boolean delayUpdate = true; - // ----- member variables - internal use + // ----- internal member variables ----- - /** Flag whether this object is configured. Configuration is only done once. */ - private boolean isConfigured = false; - /** Algorithm name for later instantiation. */ - private AlgorithmName algoName = null; + /** How should the cached value and the new one compared? */ + private Comparator comparator = null; - /** Cache name for later instantiation. */ - private CacheName cacheName = null; + /** Algorithm for computing new values and updating the cache. */ + private Algorithm algorithm = null; - /** Comparator name for later instantiation. */ - private ComparatorName compName = null; + /** The Cache containing the old values. */ + private Cache cache = null; + /** Count of modified properties */ + private int modified = 0; + + /** Flag whether this object is configured. Configuration is only done once. */ + private boolean isConfigured = false; /** * Parameter vector with parameters for later initialization. @@ -293,23 +339,26 @@ // // ----- Set default values ----- // - org.apache.tools.ant.Project project = getProject(); + Project project = getProject(); String filename = "cache.properties"; File cachefile = null; if (project != null) { // normal use inside Ant cachefile = new File(project.getBaseDir(), filename); + + // set self as a BuildListener to delay cachefile saves + getProject().addBuildListener(this); } else { - // no reference to project - e.g. during JUnit tests + // no reference to project - e.g. during normal JUnit tests cachefile = new File(filename); + setDelayUpdate(false); } - cache = new PropertiesfileCache(cachefile); - algorithm = new DigestAlgorithm(); - comparator = new EqualComparator(); + Cache defaultCache = new PropertiesfileCache(cachefile); + Algorithm defaultAlgorithm = new DigestAlgorithm(); + Comparator defaultComparator = new EqualComparator(); update = true; selectDirectories = true; - // // ----- Set the main attributes, pattern '*' ----- // @@ -328,54 +377,70 @@ // ----- Instantiate the interfaces ----- // String className = null; - String pkg = "org.apache.tools.ant.types.selectors.cacheselector"; + String pkg = "org.apache.tools.ant.types.selectors.modifiedselector"; - // the algorithm - if (algorithm == null) { + // specify the algorithm classname + if (algoName != null) { + // use Algorithm defined via name if ("hashvalue".equals(algoName.getValue())) { - className = pkg + ".HashvalueAlgorithm"; + algorithm = new HashvalueAlgorithm(); } else if ("digest".equals(algoName.getValue())) { - className = pkg + ".DigestAlgorithm"; + algorithm = new DigestAlgorithm(); + } else if ("checksum".equals(algoName.getValue())) { + algorithm = new ChecksumAlgorithm(); } - 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(); - } + } else { + if (algorithmClass != null) { + // use Algorithm specified by classname + algorithm = (Algorithm) loadClass( + algorithmClass, + "is not an Algorithm.", + Algorithm.class); + } else { + // nothing specified - use default + algorithm = defaultAlgorithm; } } - // the cache - if (cache == null) { + // specify the cache classname + if (cacheName != null) { + // use Cache defined via name if ("propertyfile".equals(cacheName.getValue())) { - className = pkg + ".PropertiesfileCache"; + cache = new 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(); - } + } else { + if (cacheClass != null) { + // use Cache specified by classname + cache = (Cache) loadClass(cacheClass, "is not a Cache.", Cache.class); + } else { + // nothing specified - use default + cache = defaultCache; } } - // the comparator - if (comparator == null) { + // specify the comparator classname + if (compName != null) { + // use Algorithm defined via name if ("equal".equals(compName.getValue())) { - className = pkg + ".EqualComparator"; - } else if ("role".equals(compName.getValue())) { - className = "java.text.RuleBasedCollator"; + comparator = new EqualComparator(); + } else if ("rule".equals(compName.getValue())) { + // TODO there is a problem with the constructor for the RBC. + // you have to provide the rules in the constructors - no setters + // available. + throw new BuildException("RuleBasedCollator not yet supported."); + // Have to think about lazy initialization here... JHM + // comparator = new java.text.RuleBasedCollator(); } - 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(); - } + } else { + if (comparatorClass != null) { + // use Algorithm specified by classname + comparator = (Comparator) loadClass( + comparatorClass, + "is not a Comparator.", + Comparator.class); + } else { + // nothing specified - use default + comparator = defaultComparator; } } @@ -390,6 +455,47 @@ } + /** + * Loads the specified class and initializes an object of that class. + * Throws a BuildException using the given message if an error occurs during + * loading/instantiation or if the object is not from the given type. + * @param classname the classname + * @param msg the message-part for the BuildException + * @param type the type to check against + * @return a castable object + */ + protected Object loadClass(String classname, String msg, Class type) { + try { + // load the specified class + + /* TODO: the selector cant find the specified class if the + * selector is loaded from a different classloader. + * See ModifiedSelectorTest.testCustom<Algorithm|Cache|Comparator|Classes>(). + * To be able to run these tests you have to ensure that <junit> can find + * the classes by adding the test package to the core: + * - by placing the ant-testutils.jar in %ANT_HOME/lib + * - by adding '-lib build/testcases' while invocation + * + * IMO this is not only a problem for the Mock-Classes inside the + * tests. The *classname attributes are designed for the user to + * provide his own implementations. Therefore they should be + * found ... Workaround again: -lib, ~/.ant/lib, ant.home/lib + * JHM + */ + Object rv = Class.forName(classname).newInstance(); + if (!type.isInstance(rv)) { + throw new BuildException("Specified class (" + classname + ") " + msg); + } + return rv; + } catch (ClassNotFoundException e) { + throw new BuildException("Specified class (" + classname + ") not found."); + } catch (Exception e) { + throw new BuildException(e); + } + } + + + // ----- the selection work ----- @@ -412,22 +518,64 @@ // 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)) { + if (update && rv) { cache.put(f.getAbsolutePath(), newValue); - cache.save(); + setModified(getModified() + 1); + if (!getDelayUpdate()) { + saveCache(); + } } return rv; } + /** + * save the cache file + */ + protected void saveCache() { + if (getModified() > 1) { + cache.save(); + setModified(0); + } + } + + // ----- attribute and nested element support ----- /** + * Setter for algorithmClass. + * @param classname new value + */ + public void setAlgorithmClass(String classname) { + algorithmClass = classname; + } + + + /** + * Setter for comparatorClass. + * @param classname new value + */ + public void setComparatorClass(String classname) { + comparatorClass = classname; + } + + + /** + * Setter for cacheClass. + * @param classname new value + */ + public void setCacheClass(String classname) { + cacheClass = classname; + } + + + /** * Support for <i>update</i> attribute. * @param update new value */ @@ -446,6 +594,42 @@ /** + * Getter for the modified count + * @return modified count + */ + public int getModified() { + return modified; + } + + + /** + * Setter for the modified count + * @param modified count + */ + public void setModified(int modified) { + this.modified = modified; + } + + + /** + * Getter for the delay update + * @return true if we should delay for performance + */ + public boolean getDelayUpdate() { + return delayUpdate; + } + + + /** + * Setter for the delay update + * @param delayUpdate true if we should delay for performance + */ + public void setDelayUpdate(boolean delayUpdate) { + this.delayUpdate = delayUpdate; + } + + + /** * Support for nested <param> tags. * @param key the key of the parameter * @param value the value of the parameter @@ -516,6 +700,12 @@ ? true : false; setUpdate(updateValue); + } else if ("delayupdate".equals(key)) { + boolean updateValue = + ("true".equalsIgnoreCase(value)) + ? true + : false; + setDelayUpdate(updateValue); } else if ("seldirs".equals(key)) { boolean sdValue = ("true".equalsIgnoreCase(value)) @@ -548,7 +738,6 @@ 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) { @@ -576,6 +765,79 @@ } + // ----- BuildListener interface methods ----- + + + /** + * Signals that the last target has finished. + * @param event recieved BuildEvent + */ + public void buildFinished(BuildEvent event) { + if (getDelayUpdate()) { + saveCache(); + } + } + + + /** + * Signals that a target has finished. + * @param event recieved BuildEvent + */ + public void targetFinished(BuildEvent event) { + if (getDelayUpdate()) { + saveCache(); + } + } + + + /** + * Signals that a task has finished. + * @param event recieved BuildEvent + */ + public void taskFinished(BuildEvent event) { + if (getDelayUpdate()) { + saveCache(); + } + } + + + /** + * Signals that a build has started. + * @param event recieved BuildEvent + */ + public void buildStarted(BuildEvent event) { + // no-op + } + + + /** + * Signals that a target is starting. + * @param event recieved BuildEvent + */ + public void targetStarted(BuildEvent event) { + // no-op + } + + + + /** + * Signals that a task is starting. + * @param event recieved BuildEvent + */ + public void taskStarted(BuildEvent event) { + // no-op + } + + + /** + * Signals a message logging event. + * @param event recieved BuildEvent + */ + public void messageLogged(BuildEvent event) { + // no-op + } + + // The EnumeratedAttributes for the three interface implementations. // Name-Classname mapping is done in the configure() method. @@ -597,7 +859,7 @@ } public static class AlgorithmName extends EnumeratedAttribute { public String[] getValues() { - return new String[] {"hashvalue", "digest" }; + return new String[] {"hashvalue", "digest", "checksum" }; } } @@ -612,4 +874,4 @@ } } -} +} //class-ModifiedSelector \ No newline at end of file 1.7 +20 -2 ant/src/main/org/apache/tools/ant/types/selectors/modifiedselector/PropertiesfileCache.java Index: PropertiesfileCache.java =================================================================== RCS file: /home/cvs/ant/src/main/org/apache/tools/ant/types/selectors/modifiedselector/PropertiesfileCache.java,v retrieving revision 1.6 retrieving revision 1.7 diff -u -r1.6 -r1.7 --- PropertiesfileCache.java 9 Mar 2004 16:48:49 -0000 1.6 +++ PropertiesfileCache.java 10 Jul 2004 17:15:37 -0000 1.7 @@ -96,12 +96,27 @@ // ----- Cache-Configuration ----- + /** + * Setter. + * @param file new value + */ public void setCachefile(File file) { cachefile = file; } - public File getCachefile() { return cachefile; } + /** + * Getter. + * @return the cachefile + */ + public File getCachefile() { + return cachefile; + } + + /** + * This cache is valid if the cachefile is set. + * @return true if all is ok false otherwise + */ public boolean isValid() { return (cachefile != null); } @@ -110,6 +125,9 @@ // ----- Data Access + /** + * Load the cache from underlying properties file. + */ public void load() { if ((cachefile != null) && cachefile.isFile() && cachefile.canRead()) { try { @@ -214,4 +232,4 @@ buf.append(">"); return buf.toString(); } -} +} \ No newline at end of file
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]