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