source: other-projects/trunk/gs3-release-maker/apache-ant-1.6.5/src/main/org/apache/tools/ant/types/selectors/modifiedselector/ModifiedSelector.java@ 14627

Last change on this file since 14627 was 14627, checked in by oranfry, 17 years ago

initial import of the gs3-release-maker

File size: 21.2 KB
Line 
1/*
2 * Copyright 2003-2004 The Apache Software Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
17
18package org.apache.tools.ant.types.selectors.modifiedselector;
19
20
21// Java
22import java.util.Comparator;
23import java.util.Vector;
24import java.util.Iterator;
25import java.io.File;
26
27// Ant
28import org.apache.tools.ant.Project;
29import org.apache.tools.ant.IntrospectionHelper;
30import org.apache.tools.ant.types.EnumeratedAttribute;
31import org.apache.tools.ant.types.Parameter;
32import org.apache.tools.ant.types.selectors.BaseExtendSelector;
33
34
35/**
36 * <p>Selector class that uses <i>Algorithm</i>, <i>Cache</i> and <i>Comparator</i>
37 * for its work.
38 * The <i>Algorithm</i> is used for computing a hashvalue for a file.
39 * The <i>Comparator</i> decides whether to select or not.
40 * The <i>Cache</i> stores the other value for comparison by the <i>Comparator</i>
41 * in a persistent manner.</p>
42 *
43 * <p>The ModifiedSelector is implemented as a <b>CoreSelector</b> and uses default
44 * values for all its attributes therefore the simpliest example is <pre>
45 * <copy todir="dest">
46 * <filelist dir="src">
47 * <modified/>
48 * </filelist>
49 * </copy>
50 * </pre></p>
51 *
52 * <p>The same example rewritten as CoreSelector with setting the all values
53 * (same as defaults are) would be <pre>
54 * <copy todir="dest">
55 * <filelist dir="src">
56 * <modified update="true"
57 * cache="propertyfile"
58 * algorithm="digest"
59 * comparator="equal">
60 * <param name="cache.cachefile" value="cache.properties"/>
61 * <param name="algorithm.algorithm" value="MD5"/>
62 * </modified>
63 * </filelist>
64 * </copy>
65 * </pre></p>
66 *
67 * <p>And the same rewritten as CustomSelector would be<pre>
68 * <copy todir="dest">
69 * <filelist dir="src">
70 * <custom class="org.apache.tools.ant.type.selectors.ModifiedSelector">
71 * <param name="update" value="true"/>
72 * <param name="cache" value="propertyfile"/>
73 * <param name="algorithm" value="digest"/>
74 * <param name="comparator" value="equal"/>
75 * <param name="cache.cachefile" value="cache.properties"/>
76 * <param name="algorithm.algorithm" value="MD5"/>
77 * </custom>
78 * </filelist>
79 * </copy>
80 * </pre></p>
81 *
82 * <p>All these three examples copy the files from <i>src</i> to <i>dest</i>
83 * using the ModifiedSelector. The ModifiedSelector uses the <i>PropertyfileCache
84 * </i>, the <i>DigestAlgorithm</i> and the <i>EqualComparator</i> for its
85 * work. The PropertyfileCache stores key-value-pairs in a simple java
86 * properties file. The filename is <i>cache.properties</i>. The <i>update</i>
87 * flag lets the selector update the values in the cache (and on first call
88 * creates the cache). The <i>DigestAlgorithm</i> computes a hashvalue using the
89 * java.security.MessageDigest class with its MD5-Algorithm and its standard
90 * provider. The new computed hashvalue and the stored one are compared by
91 * the <i>EqualComparator</i> which returns 'true' (more correct a value not
92 * equals zero (1)) if the values are not the same using simple String
93 * comparison.</p>
94 *
95 * <p>A useful scenario for this selector is inside a build environment
96 * for homepage generation (e.g. with <a href="http://xml.apache.org/forrest/">
97 * Apache Forrest</a>). <pre>
98 * <target name="generate-and-upload-site">
99 * <echo> generate the site using forrest </echo>
100 * <antcall target="site"/>
101 *
102 * <echo> upload the changed files </echo>
103 * <ftp server="${ftp.server}" userid="${ftp.user}" password="${ftp.pwd}">
104 * <fileset dir="htdocs/manual">
105 * <modified/>
106 * </fileset>
107 * </ftp>
108 * </target>
109 * </pre> Here all <b>changed</b> files are uploaded to the server. The
110 * ModifiedSelector saves therefore much upload time.</p>
111 *
112 * <p>This selector supports the following nested param's:
113 * <table>
114 * <tr><th>name</th><th>values</th><th>description</th><th>required</th></tr>
115 * <tr>
116 * <td> cache </td>
117 * <td> propertyfile </td>
118 * <td> which cache implementation should be used <ul>
119 * <li><b>propertyfile</b> - using java.util.Properties </li>
120 * </td>
121 * <td> no, defaults to 'propertyfile' </td>
122 * </tr>
123 * <tr>
124 * <td> algorithm </td>
125 * <td> hashvalue | digest </td>
126 * <td> which algorithm implementation should be used
127 * <li><b>hashvalue</b> - loads the file content into a String and
128 * uses its hashValue() method </li>
129 * <li><b>digest</b> - uses java.security.MessageDigest class </i>
130 * </td>
131 * <td> no, defaults to digest </td>
132 * </tr>
133 * <tr>
134 * <td> comparator </td>
135 * <td> equal | role </td>
136 * <td> which comparator implementation should be used
137 * <li><b>equal</b> - simple comparison using String.equals() </li>
138 * <li><b>role</b> - uses java.text.RuleBasedCollator class </i>
139 * </td>
140 * <td> no, defaults to equal </td>
141 * </tr>
142 * <tr>
143 * <td> update </td>
144 * <td> true | false </td>
145 * <td> If set to <i>true</i>, the cache will be stored, otherwise the values
146 * will be lost. </td>
147 * <td> no, defaults to true </td>
148 * </tr>
149 * <tr>
150 * <td> seldirs </td>
151 * <td> true | false </td>
152 * <td> If set to <i>true</i>, directories will be selected otherwise not </td>
153 * <td> no, defaults to true </td>
154 * </tr>
155 * <tr>
156 * <td> cache.* </td>
157 * <td> depends on used cache </td>
158 * <td> value is stored and given to the Cache-Object for initialisation </td>
159 * <td> depends on used cache </td>
160 * </tr>
161 * <tr>
162 * <td> algorithm.* </td>
163 * <td> depends on used algorithm </td>
164 * <td> value is stored and given to the Algorithm-Object for initialisation </td>
165 * <td> depends on used algorithm </td>
166 * </tr>
167 * <tr>
168 * <td> comparator.* </td>
169 * <td> depends on used comparator </td>
170 * <td> value is stored and given to the Comparator-Object for initialisation </td>
171 * <td> depends on used comparator </td>
172 * </tr>
173 * </table>
174 * If another name is used a BuildException "Invalid parameter" is thrown. </p>
175 *
176 * <p>This selector uses reflection for setting the values of its three interfaces
177 * (using org.apache.tools.ant.IntrospectionHelper) therefore no special
178 * 'configuration interfaces' has to be implemented by new caches, algorithms or
179 * comparators. All present <i>set</i>XX methods can be used. E.g. the DigestAlgorithm
180 * can use a specified provider for computing its value. For selecting this
181 * there is a <i>setProvider(String providername)</i> method. So you can use
182 * a nested <i><param name="algorithm.provider" value="MyProvider"/></i>.
183 *
184 *
185 * @version 2003-09-13
186 * @since Ant 1.6
187*/
188public class ModifiedSelector extends BaseExtendSelector {
189
190
191 // ----- member variables - configuration
192
193
194 /** The Cache containing the old values. */
195 private Cache cache = null;
196
197 /** Algorithm for computing new values and updating the cache. */
198 private Algorithm algorithm = null;
199
200 /** How should the cached value and the new one compared? */
201 private Comparator comparator = null;
202
203 /** Should the cache be updated? */
204 private boolean update = true;
205
206 /** Are directories selected? */
207 private boolean selectDirectories = true;
208
209
210 // ----- member variables - internal use
211
212
213 /** Flag whether this object is configured. Configuration is only done once. */
214 private boolean isConfigured = false;
215
216 /** Algorithm name for later instantiation. */
217 private AlgorithmName algoName = null;
218
219 /** Cache name for later instantiation. */
220 private CacheName cacheName = null;
221
222 /** Comparator name for later instantiation. */
223 private ComparatorName compName = null;
224
225
226 /**
227 * Parameter vector with parameters for later initialization.
228 * @see #configure
229 */
230 private Vector configParameter = new Vector();
231
232 /**
233 * Parameter vector with special parameters for later initialization.
234 * The names have the pattern '*.*', e.g. 'cache.cachefile'.
235 * These parameters are used <b>after</b> the parameters with the pattern '*'.
236 * @see #configure
237 */
238 private Vector specialParameter = new Vector();
239
240
241 // ----- constructors -----
242
243
244 /** Bean-Constructor. */
245 public ModifiedSelector() {
246 }
247
248
249 // ----- configuration -----
250
251
252 /** Overrides BaseSelector.verifySettings(). */
253 public void verifySettings() {
254 configure();
255 if (cache == null) {
256 setError("Cache must be set.");
257 } else if (algorithm == null) {
258 setError("Algorithm must be set.");
259 } else if (!cache.isValid()) {
260 setError("Cache must be proper configured.");
261 } else if (!algorithm.isValid()) {
262 setError("Algorithm must be proper configured.");
263 }
264 }
265
266
267 /**
268 * Configures this Selector.
269 * Does this work only once per Selector object.
270 * <p>Because some problems while configuring from <custom>Selector
271 * the configuration is done in the following order:<ol>
272 * <li> collect the configuration data </li>
273 * <li> wait for the first isSelected() call </li>
274 * <li> set the default values </li>
275 * <li> set values for name pattern '*': update, cache, algorithm, comparator </li>
276 * <li> set values for name pattern '*.*: cache.cachefile, ... </li>
277 * </ol></p>
278 * <p>This configuration algorithm is needed because you don't know
279 * the order of arriving config-data. E.g. if you first set the
280 * <i>cache.cachefilename</i> and after that the <i>cache</i> itself,
281 * the default value for cachefilename is used, because setting the
282 * cache implies creating a new Cache instance - with its defaults.</p>
283 */
284 public void configure() {
285 //
286 // ----- The "Singleton" -----
287 //
288 if (isConfigured) {
289 return;
290 }
291 isConfigured = true;
292
293 //
294 // ----- Set default values -----
295 //
296 org.apache.tools.ant.Project project = getProject();
297 String filename = "cache.properties";
298 File cachefile = null;
299 if (project != null) {
300 // normal use inside Ant
301 cachefile = new File(project.getBaseDir(), filename);
302 } else {
303 // no reference to project - e.g. during JUnit tests
304 cachefile = new File(filename);
305 }
306 cache = new PropertiesfileCache(cachefile);
307 algorithm = new DigestAlgorithm();
308 comparator = new EqualComparator();
309 update = true;
310 selectDirectories = true;
311
312
313 //
314 // ----- Set the main attributes, pattern '*' -----
315 //
316 for (Iterator itConfig = configParameter.iterator(); itConfig.hasNext();) {
317 Parameter par = (Parameter) itConfig.next();
318 if (par.getName().indexOf(".") > 0) {
319 // this is a *.* parameter for later use
320 specialParameter.add(par);
321 } else {
322 useParameter(par);
323 }
324 }
325 configParameter = new Vector();
326
327 //
328 // ----- Instantiate the interfaces -----
329 //
330 String className = null;
331 String pkg = "org.apache.tools.ant.types.selectors.cacheselector";
332
333 // the algorithm
334 if (algorithm == null) {
335 if ("hashvalue".equals(algoName.getValue())) {
336 className = pkg + ".HashvalueAlgorithm";
337 } else if ("digest".equals(algoName.getValue())) {
338 className = pkg + ".DigestAlgorithm";
339 }
340 if (className != null) {
341 try {
342 // load the specified Algorithm, save the reference and configure it
343 algorithm = (Algorithm) Class.forName(className).newInstance();
344 } catch (Exception e) {
345 e.printStackTrace();
346 }
347 }
348 }
349
350 // the cache
351 if (cache == null) {
352 if ("propertyfile".equals(cacheName.getValue())) {
353 className = pkg + ".PropertiesfileCache";
354 }
355 if (className != null) {
356 try {
357 // load the specified Cache, save the reference and configure it
358 cache = (Cache) Class.forName(className).newInstance();
359 } catch (Exception e) {
360 e.printStackTrace();
361 }
362 }
363 }
364
365 // the comparator
366 if (comparator == null) {
367 if ("equal".equals(compName.getValue())) {
368 className = pkg + ".EqualComparator";
369 } else if ("role".equals(compName.getValue())) {
370 className = "java.text.RuleBasedCollator";
371 }
372 if (className != null) {
373 try {
374 // load the specified Cache, save the reference and configure it
375 comparator = (Comparator) Class.forName(className).newInstance();
376 } catch (Exception e) {
377 e.printStackTrace();
378 }
379 }
380 }
381
382 //
383 // ----- Set the special attributes, pattern '*.*' -----
384 //
385 for (Iterator itSpecial = specialParameter.iterator(); itSpecial.hasNext();) {
386 Parameter par = (Parameter) itSpecial.next();
387 useParameter(par);
388 }
389 specialParameter = new Vector();
390 }
391
392
393 // ----- the selection work -----
394
395
396 /**
397 * Implementation of BaseExtendSelector.isSelected().
398 * @param basedir as described in BaseExtendSelector
399 * @param filename as described in BaseExtendSelector
400 * @param file as described in BaseExtendSelector
401 * @return as described in BaseExtendSelector
402 */
403 public boolean isSelected(File basedir, String filename, File file) {
404 validate();
405 File f = new File(basedir, filename);
406
407 // You can not compute a value for a directory
408 if (f.isDirectory()) {
409 return selectDirectories;
410 }
411
412 // Get the values and do the comparison
413 String cachedValue = String.valueOf(cache.get(f.getAbsolutePath()));
414 String newValue = algorithm.getValue(f);
415 boolean rv = (comparator.compare(cachedValue, newValue) != 0);
416
417 // Maybe update the cache
418 if (update && !cachedValue.equals(newValue)) {
419 cache.put(f.getAbsolutePath(), newValue);
420 cache.save();
421 }
422
423 return rv;
424 }
425
426
427 // ----- attribute and nested element support -----
428
429
430 /**
431 * Support for <i>update</i> attribute.
432 * @param update new value
433 */
434 public void setUpdate(boolean update) {
435 this.update = update;
436 }
437
438
439 /**
440 * Support for <i>seldirs</i> attribute.
441 * @param seldirs new value
442 */
443 public void setSeldirs(boolean seldirs) {
444 selectDirectories = seldirs;
445 }
446
447
448 /**
449 * Support for nested &lt;param&gt; tags.
450 * @param key the key of the parameter
451 * @param value the value of the parameter
452 */
453 public void addParam(String key, Object value) {
454 Parameter par = new Parameter();
455 par.setName(key);
456 par.setValue(String.valueOf(value));
457 configParameter.add(par);
458 }
459
460
461 /**
462 * Support for nested &lt;param&gt; tags.
463 * @param parameter the parameter object
464 */
465 public void addParam(Parameter parameter) {
466 configParameter.add(parameter);
467 }
468
469
470 /**
471 * Defined in org.apache.tools.ant.types.Parameterizable.
472 * Overwrite implementation in superclass because only special
473 * parameters are valid.
474 * @see #addParam(String,Object).
475 */
476 public void setParameters(Parameter[] parameters) {
477 if (parameters != null) {
478 for (int i = 0; i < parameters.length; i++) {
479 configParameter.add(parameters[i]);
480 }
481 }
482 }
483
484
485 /**
486 * Support for nested <param name="" value=""/> tags.
487 * Parameter named <i>cache</i>, <i>algorithm</i>,
488 * <i>comparator</i> or <i>update</i> are mapped to
489 * the respective set-Method.
490 * Parameter which names starts with <i>cache.</i> or
491 * <i>algorithm.</i> or <i>comparator.</i> are tried
492 * to set on the appropriate object via its set-methods.
493 * Other parameters are invalid and an BuildException will
494 * be thrown.
495 *
496 * @param parameter Key and value as parameter object
497 */
498 public void useParameter(Parameter parameter) {
499 String key = parameter.getName();
500 String value = parameter.getValue();
501 if ("cache".equals(key)) {
502 CacheName cn = new CacheName();
503 cn.setValue(value);
504 setCache(cn);
505 } else if ("algorithm".equals(key)) {
506 AlgorithmName an = new AlgorithmName();
507 an.setValue(value);
508 setAlgorithm(an);
509 } else if ("comparator".equals(key)) {
510 ComparatorName cn = new ComparatorName();
511 cn.setValue(value);
512 setComparator(cn);
513 } else if ("update".equals(key)) {
514 boolean updateValue =
515 ("true".equalsIgnoreCase(value))
516 ? true
517 : false;
518 setUpdate(updateValue);
519 } else if ("seldirs".equals(key)) {
520 boolean sdValue =
521 ("true".equalsIgnoreCase(value))
522 ? true
523 : false;
524 setSeldirs(sdValue);
525 } else if (key.startsWith("cache.")) {
526 String name = key.substring(6);
527 tryToSetAParameter(cache, name, value);
528 } else if (key.startsWith("algorithm.")) {
529 String name = key.substring(10);
530 tryToSetAParameter(algorithm, name, value);
531 } else if (key.startsWith("comparator.")) {
532 String name = key.substring(11);
533 tryToSetAParameter(comparator, name, value);
534 } else {
535 setError("Invalid parameter " + key);
536 }
537 }
538
539
540 /**
541 * Try to set a value on an object using reflection.
542 * Helper method for easier access to IntrospectionHelper.setAttribute().
543 * @param obj the object on which the attribute should be set
544 * @param name the attributename
545 * @param value the new value
546 */
547 protected void tryToSetAParameter(Object obj, String name, String value) {
548 Project prj = (getProject() != null) ? getProject() : new Project();
549 IntrospectionHelper iHelper
550 = IntrospectionHelper.getHelper(prj, obj.getClass());
551
552 try {
553 iHelper.setAttribute(prj, obj, name, value);
554 } catch (org.apache.tools.ant.BuildException e) {
555 // no-op
556 }
557 }
558
559
560 // ----- 'beautiful' output -----
561
562
563 /**
564 * Override Object.toString().
565 * @return information about this selector
566 */
567 public String toString() {
568 StringBuffer buf = new StringBuffer("{modifiedselector");
569 buf.append(" update=").append(update);
570 buf.append(" seldirs=").append(selectDirectories);
571 buf.append(" cache=").append(cache);
572 buf.append(" algorithm=").append(algorithm);
573 buf.append(" comparator=").append(comparator);
574 buf.append("}");
575 return buf.toString();
576 }
577
578
579 // The EnumeratedAttributes for the three interface implementations.
580 // Name-Classname mapping is done in the configure() method.
581
582
583 public Cache getCache() { return cache; }
584 public void setCache(CacheName name) {
585 cacheName = name;
586 }
587 public static class CacheName extends EnumeratedAttribute {
588 public String[] getValues() {
589 return new String[] {"propertyfile" };
590 }
591 }
592
593
594 public Algorithm getAlgorithm() { return algorithm; }
595 public void setAlgorithm(AlgorithmName name) {
596 algoName = name;
597 }
598 public static class AlgorithmName extends EnumeratedAttribute {
599 public String[] getValues() {
600 return new String[] {"hashvalue", "digest" };
601 }
602 }
603
604
605 public Comparator getComparator() { return comparator; }
606 public void setComparator(ComparatorName name) {
607 compName = name;
608 }
609 public static class ComparatorName extends EnumeratedAttribute {
610 public String[] getValues() {
611 return new String[] {"equal", "rule" };
612 }
613 }
614
615}
Note: See TracBrowser for help on using the repository browser.