You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Builder.java 24KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673
  1. /* *******************************************************************
  2. * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC),
  3. * 2003 Contributors.
  4. * All rights reserved.
  5. * This program and the accompanying materials are made available
  6. * under the terms of the Eclipse Public License v1.0
  7. * which accompanies this distribution and is available at
  8. * http://www.eclipse.org/legal/epl-v10.html
  9. *
  10. * Contributors:
  11. * PARC initial implementation
  12. * ******************************************************************/
  13. package org.aspectj.internal.tools.build;
  14. import java.io.File;
  15. import java.io.FileFilter;
  16. import java.io.InputStream;
  17. import java.util.ArrayList;
  18. import java.util.Arrays;
  19. import java.util.Collections;
  20. import java.util.Iterator;
  21. import java.util.List;
  22. import java.util.ListIterator;
  23. import java.util.Properties;
  24. import java.util.StringTokenizer;
  25. import org.apache.tools.ant.BuildException;
  26. import org.aspectj.internal.tools.build.Result.Kind;
  27. /**
  28. * Template class to build (eclipse) modules (and, weakly, products), including
  29. * any required modules. When building modules, this assumes:
  30. * <ul>
  31. * <li>the name of the module is the base name of the module directory</li>
  32. * <li>all module directories are in the same base (workspace) directory</li>
  33. * <li>the name of the target module jar is {moduleName}.jar</li>
  34. * <li>a module directory contains a <code>.classpath</code> file with
  35. * (currently line-parseable) entries per Eclipse (XML) conventions</li>
  36. * <li><code>Builder.RESOURCE_PATTERN</code> identifies all resources to copy
  37. * to output.</li>
  38. * <li>This can safely trim test-related code:
  39. * <ul>
  40. * <li>source directories named "testsrc"</li>
  41. * <li>libraries named "junit.jar"</li>
  42. * <li>required modules whose names start with "testing"</li>
  43. * </ul>
  44. * <li>A file <code>{moduleDir}/{moduleName}.properties</code> is a property
  45. * file possibly containing entries defining requirements to be merged with the
  46. * output jar (deprecated mechanism - use assembleAll or products)</li>
  47. * </ul>
  48. * This currently provides no control over the compile or assembly process, but
  49. * clients can harvest <code>{moduleDir}/bin</code> directories to re-use the
  50. * results of eclipse compiles.
  51. * <p>
  52. * When building products, this assumes:
  53. * <ul>
  54. * <li>the installer-resources directory is a peer of the products directory,
  55. * itself the parent of the particular product directory.</li>
  56. * <li>the dist, jar, product, and base (module) directory are set</li>
  57. * <li>the product distribution consists of all (and only) the files in the
  58. * dist sub-directory of the product directory</li>
  59. * <li>files in the dist sub-directory that are empty and end with .jar
  60. * represent modules to build, either as named or through aliases known here.</li>
  61. * <li>When assembling the distribution, all non-binary files are to be
  62. * filtered.
  63. * <li>
  64. * <li>the name of the product installer is
  65. * aspectj-{productName}-{version}.jar, where {productName} is the base name of
  66. * the product directory</li>
  67. * </ul>
  68. * <p>
  69. * When run using main(String[]), all relevant Ant libraries and properties must
  70. * be defined.
  71. * <p>
  72. * Written to compile standalone. Refactor if using utils, bridge, etc.
  73. */
  74. public abstract class Builder {
  75. /**
  76. * This has only weak forms for build instructions needed: - resource
  77. * pattern - compiler selection and control
  78. *
  79. * Both assumed and generated paths are scattered; see XXXNameLiteral and
  80. * XXXFileLiteral.
  81. *
  82. * Builder is supposed to be thread-safe, but currently caches build
  83. * properties to tunnel for filters. hmm.
  84. */
  85. public static final String RESOURCE_PATTERN;
  86. public static final String BINARY_SOURCE_PATTERN;
  87. public static final String ALL_PATTERN;
  88. /** enable copy filter semantics */
  89. protected static final boolean FILTER_ON = true;
  90. /** disable copy filter semantics */
  91. protected static final boolean FILTER_OFF = false;
  92. /** define libraries to skip as comma-delimited values for this key */
  93. private static final String SKIP_LIBRARIES_KEY = "skip.libraries";
  94. /** List (String) names of libraries to skip during assembly */
  95. private static final List<String> SKIP_LIBRARIES;
  96. private static final String ERROR_KEY = "error loading properties";
  97. private static final Properties PROPS;
  98. static {
  99. PROPS = new Properties();
  100. List<String> skips = Collections.emptyList();
  101. String resourcePattern = "**/*.txt,**/*.rsc,**/*.gif,**/*.properties";
  102. String allPattern = "**/*";
  103. String binarySourcePattern = "**/*.rsc,**/*.gif,**/*.jar,**/*.zip";
  104. String name = Builder.class.getName().replace('.', '/') + ".properties";
  105. try {
  106. InputStream in = Builder.class.getClassLoader()
  107. .getResourceAsStream(name);
  108. PROPS.load(in);
  109. allPattern = PROPS.getProperty("all.pattern");
  110. resourcePattern = PROPS.getProperty("resource.pattern");
  111. binarySourcePattern = PROPS.getProperty("binarySource.pattern");
  112. skips = commaStrings(PROPS.getProperty(SKIP_LIBRARIES_KEY));
  113. } catch (Throwable t) {
  114. if (t instanceof ThreadDeath) {
  115. throw (ThreadDeath) t;
  116. }
  117. String m = "error loading " + name + ": " + t.getClass() + " " + t;
  118. PROPS.setProperty(ERROR_KEY, m);
  119. }
  120. SKIP_LIBRARIES = skips;
  121. ALL_PATTERN = allPattern;
  122. BINARY_SOURCE_PATTERN = binarySourcePattern;
  123. RESOURCE_PATTERN = resourcePattern;
  124. }
  125. /**
  126. * Splits strings into an unmodifable <code>List</code> of String using
  127. * comma as the delimiter and trimming whitespace from the result.
  128. *
  129. * @param text
  130. * <code>String</code> to split.
  131. * @return unmodifiable List (String) of String delimited by comma in text
  132. */
  133. public static List<String> commaStrings(String text) {
  134. if ((null == text) || (0 == text.length())) {
  135. return Collections.EMPTY_LIST;
  136. }
  137. List<String> strings = new ArrayList<String>();
  138. StringTokenizer tok = new StringTokenizer(text, ",");
  139. while (tok.hasMoreTokens()) {
  140. String token = tok.nextToken().trim();
  141. if (0 < token.length()) {
  142. strings.add(token);
  143. }
  144. }
  145. return Collections.unmodifiableList(strings);
  146. }
  147. /**
  148. * Map delivered-jar name to created-module name
  149. *
  150. * @param jarName
  151. * the String (lowercased) of the jar/zip to map
  152. */
  153. private String moduleAliasFor(String jarName) {
  154. String result = PROPS.getProperty("alias." + jarName, jarName);
  155. if (verbose && result.equals(jarName)) {
  156. String m = "expected alias for " + jarName;
  157. handler.error(m + PROPS.getProperty(ERROR_KEY, ""));
  158. }
  159. return result;
  160. }
  161. protected final Messager handler;
  162. protected boolean buildingEnabled;
  163. private final File tempDir;
  164. private final ArrayList tempFiles;
  165. private final boolean useEclipseCompiles;
  166. protected boolean verbose;
  167. protected Builder(File tempDir, boolean useEclipseCompiles, Messager handler) {
  168. Util.iaxIfNull(handler, "handler");
  169. this.useEclipseCompiles = useEclipseCompiles;
  170. this.handler = handler;
  171. this.tempFiles = new ArrayList();
  172. if ((null == tempDir) || !tempDir.canWrite() || !tempDir.isDirectory()) {
  173. this.tempDir = Util.makeTempDir("Builder");
  174. } else {
  175. this.tempDir = tempDir;
  176. }
  177. buildingEnabled = true;
  178. }
  179. /** tell builder to stop or that it's ok to run */
  180. public void setBuildingEnabled(boolean enabled) {
  181. buildingEnabled = enabled;
  182. }
  183. public void setVerbose(boolean verbose) {
  184. this.verbose = verbose;
  185. }
  186. private void verifyBuildSpec(BuildSpec buildSpec) {
  187. if (null == buildSpec.productDir) { // ensure module properties
  188. // derive moduleDir from baseDir + module
  189. if (null == buildSpec.moduleDir) {
  190. if (null == buildSpec.baseDir) {
  191. throw new BuildException("require baseDir or moduleDir");
  192. } else if (null == buildSpec.module) {
  193. throw new BuildException("require module with baseDir");
  194. } else {
  195. if (null == buildSpec.baseDir) {
  196. buildSpec.baseDir = new File("."); // user.home?
  197. }
  198. buildSpec.moduleDir = new File(buildSpec.baseDir,
  199. buildSpec.module);
  200. }
  201. } else if (null == buildSpec.baseDir) {
  202. // derive baseDir from moduleDir parent
  203. buildSpec.baseDir = buildSpec.moduleDir.getParentFile();
  204. // rule: base is parent
  205. if (null == buildSpec.baseDir) {
  206. buildSpec.baseDir = new File("."); // user.home?
  207. }
  208. handler.log("Builder using derived baseDir: "
  209. + buildSpec.baseDir);
  210. }
  211. Util.iaxIfNotCanReadDir(buildSpec.moduleDir, "moduleDir");
  212. if (null == buildSpec.module) {
  213. // derive module name from directory
  214. buildSpec.module = buildSpec.moduleDir.getName();
  215. if (null == buildSpec.module) {
  216. throw new BuildException("no name, even from "
  217. + buildSpec.moduleDir);
  218. }
  219. }
  220. }
  221. }
  222. /**
  223. * Find the Result (and hence Module and Modules) for this BuildSpec.
  224. */
  225. protected Result specifyResultFor(BuildSpec buildSpec) {
  226. if (buildSpec.trimTesting
  227. && (-1 != buildSpec.module.indexOf("testing"))) { // XXXNameLiteral
  228. String warning = "Warning - cannot trimTesting for testing modules: ";
  229. handler.log(warning + buildSpec.module);
  230. }
  231. Messager handler = new Messager();
  232. Modules modules = new Modules(buildSpec.baseDir, buildSpec.jarDir,
  233. handler);
  234. final Module moduleToBuild = modules.getModule(buildSpec.module);
  235. Kind kind = Result.kind(buildSpec.trimTesting,
  236. buildSpec.assembleAll);
  237. return moduleToBuild.getResult(kind);
  238. }
  239. public final boolean build(BuildSpec buildSpec) {
  240. if (!buildingEnabled) {
  241. return false;
  242. }
  243. verifyBuildSpec(buildSpec);
  244. if (null != buildSpec.productDir) {
  245. return buildProduct(buildSpec);
  246. }
  247. Result result = specifyResultFor(buildSpec);
  248. ArrayList<String> errors = new ArrayList<String>();
  249. try {
  250. return buildAll(result, errors);
  251. } finally {
  252. if (0 < errors.size()) {
  253. String label = "error building " + buildSpec + ": ";
  254. for (String error : errors) {
  255. String m = label + error;
  256. handler.error(m);
  257. }
  258. }
  259. }
  260. }
  261. /**
  262. * Clean up any temporary files, etc. after build completes
  263. */
  264. public boolean cleanup() {
  265. boolean noErr = true;
  266. for (Object tempFile : tempFiles) {
  267. File file = (File) tempFile;
  268. if (!Util.deleteContents(file) || !file.delete()) {
  269. if (noErr) {
  270. noErr = false;
  271. }
  272. handler.log("unable to clean up " + file);
  273. }
  274. }
  275. return noErr;
  276. }
  277. protected final boolean isLogging() {
  278. return (verbose && (null != this.handler));
  279. }
  280. protected Result[] skipUptodate(Result[] results) {
  281. if (null == results) {
  282. return new Result[0];
  283. }
  284. Result[] done = new Result[results.length];
  285. int to = 0;
  286. for (int i = 0; i < done.length; i++) {
  287. if ((null != results[i]) && results[i].outOfDate()) {
  288. done[to++] = results[i];
  289. }
  290. }
  291. if (to < results.length) {
  292. Result[] newdone = new Result[to];
  293. System.arraycopy(done, 0, newdone, 0, newdone.length);
  294. done = newdone;
  295. }
  296. return done;
  297. }
  298. /**
  299. * Build a result with all antecedants.
  300. *
  301. * @param result
  302. * the Result to build
  303. * @param errors
  304. * the List sink for errors, if any
  305. * @return false after successful build, when module jar should exist
  306. */
  307. protected final boolean buildAll(Result result, List<String> errors) {
  308. Result[] buildList = skipUptodate(getAntecedantResults(result));
  309. ArrayList<String> doneList = new ArrayList<String>();
  310. if ((null != buildList) && (0 < buildList.length)) {
  311. if (isLogging()) {
  312. handler.log("modules to build: " + Arrays.asList(buildList));
  313. }
  314. for (Result required : buildList) {
  315. if (!buildingEnabled) {
  316. return false;
  317. }
  318. String requiredName = required.getName();
  319. if (!doneList.contains(requiredName)) {
  320. doneList.add(requiredName);
  321. if (!buildOnly(required, errors)) {
  322. return false;
  323. }
  324. }
  325. }
  326. }
  327. return true;
  328. }
  329. /**
  330. * Build a module but no antecedants.
  331. *
  332. * @param module
  333. * the Module to build
  334. * @param errors
  335. * the List sink for errors, if any
  336. * @return false after successful build, when module jar should exist
  337. */
  338. protected final boolean buildOnly(Result result, List<String> errors) {
  339. if (!result.outOfDate()) {
  340. return true;
  341. }
  342. if (isLogging()) {
  343. handler.log("building " + result);
  344. }
  345. if (!buildingEnabled) {
  346. return false;
  347. }
  348. if (result.getKind().assemble) {
  349. return assembleAll(result, handler);
  350. }
  351. Module module = result.getModule();
  352. final File classesDir;
  353. if (useEclipseCompiles) {
  354. classesDir = new File(module.moduleDir, "bin"); // FileLiteral
  355. } else {
  356. String name = "classes-" + System.currentTimeMillis();
  357. classesDir = new File(tempDir, name);
  358. }
  359. if (verbose) {
  360. handler.log("buildOnly " + module);
  361. }
  362. try {
  363. return (compile(result, classesDir,useEclipseCompiles, errors))
  364. && assemble(result, classesDir, errors);
  365. } finally {
  366. if (!useEclipseCompiles && !Util.delete(classesDir)) {
  367. errors.add("buildOnly unable to delete " + classesDir);
  368. }
  369. }
  370. }
  371. /**
  372. * Register temporary file or directory to be deleted when the build is
  373. * complete, even if an Exception is thrown.
  374. */
  375. protected void addTempFile(File tempFile) {
  376. if (null != tempFile) {
  377. tempFiles.add(tempFile);
  378. }
  379. }
  380. /**
  381. * Build product by discovering any modules to build, building those,
  382. * assembling the product distribution, and optionally creating an installer
  383. * for it.
  384. *
  385. * @return true on success
  386. */
  387. protected final boolean buildProduct(BuildSpec buildSpec)
  388. throws BuildException {
  389. Util.iaxIfNull(buildSpec, "buildSpec");
  390. if (!buildSpec.trimTesting) {
  391. buildSpec.trimTesting = true;
  392. handler.log("testing trimmed for " + buildSpec);
  393. }
  394. Util.iaxIfNotCanReadDir(buildSpec.productDir, "productDir");
  395. Util.iaxIfNotCanReadDir(buildSpec.baseDir, "baseDir");
  396. Util.iaxIfNotCanWriteDir(buildSpec.distDir, "distDir");
  397. // ---- discover modules to build, and build them
  398. Modules modules = new Modules(buildSpec.baseDir, buildSpec.jarDir,
  399. handler);
  400. ProductModule[] productModules = discoverModules(buildSpec.productDir,
  401. modules);
  402. for (ProductModule module : productModules) {
  403. if (buildSpec.verbose) {
  404. handler.log("building product module " + module);
  405. }
  406. if (!buildProductModule(module)) {
  407. return false;
  408. }
  409. }
  410. if (buildSpec.verbose) {
  411. handler.log("assembling product module for " + buildSpec);
  412. }
  413. // ---- assemble product distribution
  414. final String productName = buildSpec.productDir.getName();
  415. final File targDir = new File(buildSpec.distDir, productName);
  416. final String targDirPath = targDir.getPath();
  417. if (targDir.canWrite()) {
  418. Util.deleteContents(targDir);
  419. }
  420. if (!targDir.canWrite() && !targDir.mkdirs()) {
  421. if (buildSpec.verbose) {
  422. handler.log("buildProduct unable to create " + targDir);
  423. }
  424. return false;
  425. }
  426. // copy non-binaries (with filter)
  427. File distDir = new File(buildSpec.productDir, "dist");
  428. if (!copyNonBinaries(buildSpec, distDir, targDir)) {
  429. return false;
  430. }
  431. // copy binaries (but not module flag files)
  432. String excludes = null;
  433. {
  434. StringBuffer buf = new StringBuffer();
  435. for (ProductModule productModule : productModules) {
  436. if (0 < buf.length()) {
  437. buf.append(",");
  438. }
  439. buf.append(productModule.relativePath);
  440. }
  441. if (0 < buf.length()) {
  442. excludes = buf.toString();
  443. }
  444. }
  445. if (!copyBinaries(buildSpec, distDir, targDir, excludes)) {
  446. return false;
  447. }
  448. // copy binaries associated with module flag files
  449. for (final ProductModule product : productModules) {
  450. final Kind kind = Result.kind(Result.NORMAL, product.assembleAll);
  451. Result result = product.module.getResult(kind);
  452. String targPath = Util.path(targDirPath, product.relativePath);
  453. File jarFile = result.getOutputFile();
  454. copyFile(jarFile, new File(targPath), FILTER_OFF);
  455. }
  456. handler.log("created product in " + targDir);
  457. // ---- create installer
  458. if (buildSpec.createInstaller) {
  459. return buildInstaller(buildSpec, targDirPath);
  460. } else {
  461. return true;
  462. }
  463. }
  464. protected boolean copyBinaries(BuildSpec buildSpec, File distDir,
  465. File targDir, String excludes) {
  466. String includes = Builder.BINARY_SOURCE_PATTERN;
  467. return copyFiles(distDir, targDir, includes, excludes, FILTER_OFF);
  468. }
  469. /**
  470. * filter-copy everything but the binaries
  471. */
  472. protected boolean copyNonBinaries(BuildSpec buildSpec, File distDir,
  473. File targDir) {
  474. String excludes = Builder.BINARY_SOURCE_PATTERN;
  475. String includes = Builder.ALL_PATTERN;
  476. return copyFiles(distDir, targDir, includes, excludes, FILTER_ON);
  477. }
  478. protected final boolean buildProductModule(ProductModule module) {
  479. ArrayList<String> errors = new ArrayList<>();
  480. try {
  481. Kind productKind = Result.kind(Result.NORMAL, Result.ASSEMBLE);
  482. Result result = module.module.getResult(productKind);
  483. return buildAll(result, errors);
  484. } finally {
  485. for (String error : errors) {
  486. handler.error("error building " + module + ": " + error);
  487. }
  488. }
  489. }
  490. /**
  491. * Discover any modules that might need to be built in order to assemble the
  492. * product distribution. This interprets empty .jar files as module
  493. * deliverables.
  494. */
  495. protected ProductModule[] discoverModules(File productDir, Modules modules) {
  496. final ArrayList<File> found = new ArrayList<File>();
  497. FileFilter filter = new FileFilter() {// empty jar files
  498. public boolean accept(File file) {
  499. if ((null != file) && file.canRead()
  500. && file.getPath().endsWith(".jar") // XXXFileLiteral
  501. && (0l == file.length())) {
  502. found.add(file);
  503. }
  504. return true;
  505. }
  506. };
  507. Util.visitFiles(productDir, filter);
  508. ArrayList<ProductModule> results = new ArrayList<ProductModule>();
  509. for (File file: found) {
  510. String jarName = moduleAliasFor(file.getName().toLowerCase());
  511. if (jarName.endsWith(".jar") || jarName.endsWith(".zip")) { // XXXFileLiteral
  512. jarName = jarName.substring(0, jarName.length() - 4);
  513. } else {
  514. handler.log("can only replace .[jar|zip]: " + file);
  515. // XXX error?
  516. }
  517. boolean assembleAll = jarName.endsWith("-all");
  518. // XXXFileLiteral
  519. String name = (!assembleAll ? jarName : jarName.substring(0,
  520. jarName.length() - 4));
  521. Module module = modules.getModule(name);
  522. if (null == module) {
  523. handler.log("unable to find module for " + file);
  524. } else {
  525. results.add(new ProductModule(productDir, file, module,
  526. assembleAll));
  527. }
  528. }
  529. return results.toArray(new ProductModule[0]);
  530. }
  531. /**
  532. * Subclasses should query whether to include library files in the assembly.
  533. *
  534. * @param module
  535. * the Module being built
  536. * @param libraries
  537. * the List of File path to the jar to consider assembling
  538. * @return true if the jar should be included, false otherwise.
  539. */
  540. protected void removeLibraryFilesToSkip(Module module, List<File> libraries) {
  541. for (ListIterator<File> liter = libraries.listIterator(); liter.hasNext();) {
  542. File library = liter.next();
  543. final String fname = library.getName();
  544. if (null != fname) {
  545. for (String name : SKIP_LIBRARIES) {
  546. if (fname.equals(name)) {
  547. liter.remove();
  548. break;
  549. }
  550. }
  551. }
  552. }
  553. }
  554. /**
  555. * @return String[] names of results to build for this module
  556. */
  557. abstract protected Result[] getAntecedantResults(Result toBuild);
  558. /**
  559. * Compile module classes to classesDir, saving String errors.
  560. *
  561. * @param module
  562. * the Module to compile
  563. * @param classesDir
  564. * the File directory to compile to
  565. * @param useExistingClasses
  566. * if true, don't recompile and ensure classes are available
  567. * @param errors
  568. * the List to add error messages to
  569. */
  570. abstract protected boolean compile(Result result, File classesDir,
  571. boolean useExistingClasses, List<String> errors);
  572. /**
  573. * Assemble the module distribution from the classesDir, saving String
  574. * errors.
  575. *
  576. * @see #removeLibraryFilesToSkip(Module, File)
  577. */
  578. abstract protected boolean assemble(Result result, File classesDir,
  579. List<String> errors);
  580. /**
  581. * Assemble the module distribution from the classesDir and all
  582. * antecendants, saving String errors.
  583. *
  584. * @see #removeLibraryFilesToSkip(Module, File)
  585. */
  586. abstract protected boolean assembleAll(Result result, Messager handler);
  587. /**
  588. * Generate the installer for this product to targDirPath
  589. */
  590. abstract protected boolean buildInstaller(BuildSpec buildSpec,
  591. String targDirPath);
  592. /**
  593. * Copy fromFile to toFile, optionally filtering contents
  594. */
  595. abstract protected boolean copyFile(File fromFile, File toFile,
  596. boolean filter);
  597. /**
  598. * Copy toDir any fromDir included files without any exluded files,
  599. * optionally filtering contents.
  600. *
  601. * @param fromDir
  602. * File dir to read from - error if not readable
  603. * @param toDir
  604. * File dir to write to - error if not writable
  605. * @param included
  606. * String Ant pattern of included files (if null, include all)
  607. * @param excluded
  608. * String Ant pattern of excluded files (if null, exclude none)
  609. * @param filter
  610. * if FILTER_ON, then filter file contents using global
  611. * token/value pairs
  612. */
  613. abstract protected boolean copyFiles(File fromDir, File toDir,
  614. String included, String excluded, boolean filter);
  615. }