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.

FileUtil.java 29KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745
  1. /* *******************************************************************
  2. * Copyright (c) 1999-2000 Xerox Corporation.
  3. * All rights reserved.
  4. * This program and the accompanying materials are made available
  5. * under the terms of the Eclipse Public License v1.0
  6. * which accompanies this distribution and is available at
  7. * http://www.eclipse.org/legal/epl-v10.html
  8. *
  9. * Contributors:
  10. * Xerox/PARC initial implementation
  11. * ******************************************************************/
  12. package org.aspectj.testing.util;
  13. import java.io.File;
  14. import java.io.FileFilter;
  15. import java.io.FileInputStream;
  16. import java.io.FileOutputStream;
  17. import java.io.IOException;
  18. import java.io.InputStream;
  19. import java.io.OutputStream;
  20. import java.io.PrintWriter;
  21. import java.io.StringBufferInputStream;
  22. import java.io.StringWriter;
  23. import java.net.MalformedURLException;
  24. import java.net.URL;
  25. import java.util.ArrayList;
  26. import java.util.Arrays;
  27. import java.util.Collection;
  28. import java.util.Enumeration;
  29. import java.util.Iterator;
  30. import java.util.Vector;
  31. import java.util.jar.Attributes;
  32. import java.util.jar.JarOutputStream;
  33. import java.util.jar.Manifest;
  34. import java.util.zip.ZipEntry;
  35. import java.util.zip.ZipFile;
  36. import java.util.zip.ZipOutputStream;
  37. /**
  38. * misc file utilities
  39. */
  40. public class FileUtil {
  41. /** default filename if URL has none (i.e., a directory URL): index.html */
  42. public static final String DEFAULT_URL_FILENAME = "index.html";
  43. /**
  44. * @param args the String[]
  45. * <code>{ "-copy", "-srcFile" | "-srcUrl", {src}, "-destFile", {destFile} }</code>
  46. */
  47. public static void main (String[] args) {
  48. if (null == args) return;
  49. for (int i = 0; (i+4) < args.length; i++) {
  50. if ("-copy".equals(args[i])) {
  51. String arg = args[++i];
  52. String src = null;
  53. String destFile = null;
  54. boolean srcIsFile = ("-srcFile".equals(arg));
  55. if (srcIsFile) {
  56. src = args[++i];
  57. } else if ("-srcUrl".equals(arg)) {
  58. src = args[++i];
  59. }
  60. if ((null != src) && ("-destFile".equals(args[++i]))) {
  61. destFile = args[++i];
  62. StringBuffer errs = new StringBuffer();
  63. if (srcIsFile) {
  64. copyFile(new File(src), new File(destFile), errs);
  65. } else {
  66. URL url = null;
  67. try { url = new URL(src) ; }
  68. catch (MalformedURLException e) { render(e, errs); }
  69. if (null != url) {
  70. copyURL(url, new File(destFile), errs);
  71. }
  72. }
  73. if (0 < errs.length()) {
  74. System.err.println("Error copying " + src + " to " + destFile);
  75. System.err.println(errs.toString());
  76. }
  77. }
  78. } // ("-copy".equals(args[i])){
  79. }
  80. } // end of main ()
  81. /**
  82. * Generate a list of missing and extra files by comparison to a
  83. * timestamp, optionally excluding certain files.
  84. * This is a call to select all files after a given time:
  85. *
  86. * <pre>Diffs d = dirDiffs(dir, givenTime, null, null, null);</pre>
  87. *
  88. * Given files
  89. * <pre>classes/Foo.class
  90. * classes/bar/Bash.class
  91. * classes/Old.class
  92. * classes/one/Unexpected.class
  93. * classes/start.gif</pre>
  94. * where only Old.class predated startTime, this is a call that
  95. * reports "one/Unexpected.class" as unexpected and "Foo"
  96. * as missing:
  97. * <pre>String requireSuffix = ".class";
  98. * String[] expectedPaths = new String[] { "Foo", "bar/Bas" };
  99. * File file = new File("classes");
  100. * Diffs d = dirDiffs(dir, startTime, requireSuffix,expectedPaths, true);</pre>
  101. *
  102. * @param label the String to use for the Diffs label
  103. * @param dir the File for the dir to search
  104. * @param startTime collect files modified after this time
  105. * (ignored if less than 0)
  106. * @param requireSuffix ignore all actual files without this suffix
  107. * (ignored if null)
  108. * @param expectedPaths paths (relative to dir) of the expected files
  109. * (if null, none expected)
  110. * @param acceptFilePrefix if true, then accept a file which
  111. * differs from an expected file name only by a suffix
  112. * (which need not begin with ".").
  113. */
  114. public static Diffs dirDiffs( // XXX too complicated, weak prefix checking
  115. final String label,
  116. final File dir,
  117. final long startTime,
  118. final String requiredSuffix,
  119. final String[] expectedPaths,
  120. final boolean acceptFilePrefix) {
  121. LangUtil.throwIaxIfNull(dir, "dir");
  122. final boolean checkExpected = !LangUtil.isEmpty(expectedPaths);
  123. // normalize sources to ignore
  124. final ArrayList expected = (!checkExpected ? null : new ArrayList());
  125. if (checkExpected) {
  126. for (int i = 0; i < expectedPaths.length; i++) {
  127. String srcPath = expectedPaths[i];
  128. if (!LangUtil.isEmpty(srcPath)) {
  129. expected.add(org.aspectj.util.FileUtil.weakNormalize(srcPath));
  130. }
  131. }
  132. }
  133. // gather, normalize paths changed
  134. FileFilter touchedCollector = new FileFilter() {
  135. /**
  136. * For files complying with time and suffix rules,
  137. * return true (accumulate - unexpected)
  138. * unless they match expected files,
  139. * (deleting any matches from sources
  140. * so the remainder is missing).
  141. * @return true for unexpected files after date */
  142. public boolean accept(File file) {
  143. if (file.isFile()
  144. && ((0 > startTime)
  145. || (startTime < file.lastModified()))) {
  146. String path = file.getPath();
  147. if ((null == requiredSuffix) || path.endsWith(requiredSuffix)) {
  148. path = org.aspectj.util.FileUtil.weakNormalize(path);
  149. if (checkExpected) {
  150. if (!acceptFilePrefix) {
  151. // File.equals(..) does lexical compare
  152. if (expected.contains(path)) {
  153. expected.remove(path);
  154. // found - do not add to unexpected
  155. return false;
  156. }
  157. } else {
  158. for (Iterator iter = expected.iterator();
  159. iter.hasNext();
  160. ) {
  161. String exp = (String) iter.next();
  162. if (path.startsWith(exp)) {
  163. String suffix = path.substring(exp.length());
  164. if (-1 == suffix.indexOf("/")) { // normalized...
  165. expected.remove(path);
  166. // found - do not add to unexpected
  167. return false;
  168. }
  169. }
  170. }
  171. }
  172. }
  173. // add if is file, right time, and have or don't need suffix
  174. return true;
  175. }
  176. }
  177. // skip if not file or not right time
  178. return false;
  179. }
  180. };
  181. ArrayList unexp = new ArrayList();
  182. unexp.addAll(Arrays.asList(dir.listFiles(touchedCollector)));
  183. // report any unexpected changes
  184. return Diffs.makeDiffs(label, expected, unexp, String.CASE_INSENSITIVE_ORDER);
  185. }
  186. /**
  187. * Visit the entries in a zip file, halting when visitor balks.
  188. * Errors are silently ignored.
  189. * @throws IllegalArgumentException if zipfile or visitor is null
  190. */
  191. public static void visitZipEntries(ZipFile zipfile, StringVisitor visitor) {
  192. visitZipEntries(zipfile, visitor, (StringBuffer) null);
  193. }
  194. /**
  195. * Visit the entries in a zip file, halting when visitor balks.
  196. * Errors are reported in errs, if not null.
  197. * @throws IllegalArgumentException if zipfile or visitor is null
  198. */
  199. public static void visitZipEntries(ZipFile zipfile, StringVisitor visitor,
  200. StringBuffer errs) {
  201. if (null == zipfile) throw new IllegalArgumentException("null zipfile");
  202. if (null == visitor) throw new IllegalArgumentException("null visitor");
  203. int index = 0;
  204. try {
  205. Enumeration enu = zipfile.entries();
  206. while (enu.hasMoreElements()) {
  207. ZipEntry entry = (ZipEntry) enu.nextElement();
  208. index++;
  209. if (! visitor.accept(entry.getName())) {
  210. break;
  211. }
  212. }
  213. } catch (Throwable e) {
  214. if (null != errs) {
  215. errs.append("FileUtil.visitZipEntries error accessing entry " + index
  216. + ": " + e.getMessage());
  217. StringWriter sw = new StringWriter();
  218. e.printStackTrace(new PrintWriter(sw));
  219. errs.append(sw.toString());
  220. }
  221. } finally {
  222. if (null != zipfile) {
  223. try { zipfile.close(); }
  224. catch (IOException x) {} // ignore
  225. }
  226. }
  227. }
  228. /**
  229. * descend filesystem tree, invoking FileFilter.accept() on files.
  230. * E.g., To list files from current directory:
  231. * <code><pre>descendFileTree(new File("."), new FileFilter() {
  232. * public boolean accept(File f){
  233. * System.out.println(f.getAbsolutePath());
  234. * return true;
  235. * }});</code></pre>
  236. * @param file root/starting point. If a file, the only one visited.
  237. * @param filter supplies accept(File) routine
  238. */
  239. public static void descendFileTree(File file, FileFilter filter) {
  240. descendFileTree(file, filter, false);
  241. }
  242. /**
  243. * Descend filesystem tree, invoking FileFilter.accept() on files
  244. * and, if userRecursion, on dirs. If userRecursion, accept() must
  245. * call descendFileTree() again to recurse down directories.
  246. * This calls fileFilter.accept(File) on all files before doing any dirs.
  247. * E.g., To list only files from Unix root:
  248. * <code><pre>descendFileTree(new File("/"), new FileFilter() {
  249. * public boolean run(File f){
  250. * System.out.println(f.getAbsolutePath());
  251. * return true;
  252. * }}, false);</code></pre>
  253. * To list files/dir from root using user recursion:
  254. * <code><pre>descendFileTree(new File("/"), new FileFilter() {
  255. * public boolean run(File f){
  256. * System.out.println(f.getAbsolutePath());
  257. * if (f.isDirectory() && (-1 == f.getName().indexOf("CVS")))
  258. * return descendFileTree(f, this, true);
  259. * return true;
  260. * }}, true);</code></pre>
  261. * @param file root/starting point. If a file, the only one visited.
  262. * @param filter supplies boolean accept(File) method
  263. * @param userRecursion - if true, do accept() on dirs; else, recurse
  264. * @return false if any fileFilter.accept(File) did.
  265. * @throws IllegalArgumentException if file or fileFilter is null
  266. */
  267. public static boolean descendFileTree(File file, FileFilter fileFilter,
  268. boolean userRecursion) {
  269. if (null == file) {throw new IllegalArgumentException("parm File"); }
  270. if (null == fileFilter){throw new IllegalArgumentException("parm FileFilter");}
  271. if (!file.isDirectory()) {
  272. return fileFilter.accept(file);
  273. } else if (file.canRead()) {
  274. // go through files first
  275. File[] files = file.listFiles(ValidFileFilter.FILE_EXISTS);
  276. if (null != files) {
  277. for (int i = 0; i < files.length; i++) {
  278. if (!fileFilter.accept(files[i])) {
  279. return false;
  280. }
  281. }
  282. }
  283. // now recurse to handle directories
  284. File[] dirs = file.listFiles(ValidFileFilter.DIR_EXISTS);
  285. if (null != dirs) {
  286. for (int i = 0; i < dirs.length; i++) {
  287. if (userRecursion) {
  288. if (!fileFilter.accept(dirs[i])) {
  289. return false;
  290. }
  291. } else {
  292. if (!descendFileTree(dirs[i], fileFilter,userRecursion)) {
  293. return false;
  294. }
  295. }
  296. }
  297. }
  298. } // readable directory (ignore unreadable ones)
  299. return true;
  300. } // descendFiles
  301. /**
  302. * Return the names of all files below a directory.
  303. * If file is a directory, then all files under the directory
  304. * are returned. If file is absolute or relative, all the files are.
  305. * If file is a zip or jar file, then all entries in the zip or jar
  306. * are listed. Entries inside those jarfiles/zipfiles are not listed.
  307. * There are no guarantees about ordering.
  308. * @param dir the File to list for
  309. * @param results the Collection to use for the results (may be null)
  310. * @throws IllegalArgumentException if null == dir
  311. * @return a Collection of String of paths, including paths inside jars
  312. */
  313. public static Collection<String> directoryToString(File dir, Collection results) {
  314. if (null == dir) throw new IllegalArgumentException("null dir");
  315. final Collection<String> result = (results != null? results : new Vector());
  316. if (isZipFile(dir)) {
  317. zipFileToString(dir, result);
  318. } else if (!dir.isDirectory()) {
  319. throw new IllegalArgumentException("not a dir: " + dir);
  320. } else {
  321. AccumulatingFileFilter acFilter = new AccumulatingFileFilter() {
  322. public boolean accumulate(File file) {
  323. String name = file.getPath();
  324. result.add(name);
  325. if (isZipFile(file)) {
  326. zipFileToString(file, result);
  327. }
  328. return true;
  329. }
  330. };
  331. descendFileTree(dir, acFilter, false);
  332. }
  333. return result;
  334. } // directoryToString
  335. /**
  336. * Render as String the entries in a zip or jar file,
  337. * converting each to String beforehand (as jarpath!jarentry)
  338. * applying policies for whitespace, etc.
  339. * @param file the File to enumerate ZipEntry for
  340. * @param results the Colection to use to return the FileLine - may be null
  341. * @return FileLines with string as text and
  342. * canonical as string modified by any canonicalizing policies.
  343. */
  344. public static Collection zipFileToString(final File zipfile, Collection results) {
  345. Collection result = (results != null ? results : new Vector());
  346. ZipFile zip = null;
  347. try {
  348. zip = new ZipFile(zipfile); // ZipFile.OPEN_READ| ZipFile.OPEN_DELETE); delete is 1.3 only
  349. Enumeration enu = zip.entries();
  350. while (enu.hasMoreElements()) {
  351. results.add(renderZipEntry(zipfile, (ZipEntry) enu.nextElement()));
  352. }
  353. zip.close();
  354. zip = null;
  355. } catch (Throwable t) {
  356. String err = "Error opening " + zipfile + " attempting to continue...";
  357. System.err.println(err);
  358. t.printStackTrace(System.err);
  359. } finally {
  360. if (null != zip) {
  361. try { zip.close(); }
  362. catch (IOException e) {
  363. e.printStackTrace(System.err);
  364. }
  365. }
  366. }
  367. return result;
  368. }
  369. /**
  370. * @return true if file represents an existing file with a zip extension
  371. */
  372. public static boolean isZipFile(File f) {
  373. String s = null;
  374. if ((null == f) || (null == (s = f.getPath()))) {
  375. return false;
  376. } else {
  377. return (f.canRead() && !f.isDirectory()
  378. && (s.endsWith(".zip")
  379. || (s.endsWith(".jar"))));
  380. }
  381. }
  382. /**
  383. * Render a zip/entry combination to String
  384. */
  385. public static String renderZipEntry(File zipfile, ZipEntry entry) {
  386. String filename = (null == zipfile ? "null File" : zipfile.getName());
  387. String entryname = (null == entry ? "null ZipEntry" : entry.getName());
  388. return filename + "!" + entryname;
  389. }
  390. /**
  391. * Write all files in directory out to jarFile
  392. * @param jarFile the File to create and write to
  393. * @param directory the File representing the directory to read
  394. * @param mainClass the value of the main class attribute - may be null
  395. */
  396. public static boolean createJarFile(File jarFile, File directory,
  397. String mainClass, FileFilter filter) {
  398. String label = "createJarFile("+jarFile
  399. +","+directory +","+mainClass +","+filter + "): ";
  400. Log.signal(label + " start");
  401. if (null == directory)
  402. throw new IllegalArgumentException("null directory");
  403. Manifest manifest = createManifest(mainClass);
  404. Log.signal(label + " manifest=" + manifest);
  405. JarOutputStream out = null;
  406. try {
  407. File jarFileDir = jarFile.getParentFile();
  408. if (null == jarFileDir) {
  409. Log.signal(label + " null jarFileDir");
  410. } else if (!jarFileDir.exists() && !jarFileDir.mkdirs()) { // XXX convert to Error
  411. Log.signal(label + " unable to create jarFileDir: " + jarFileDir);
  412. }
  413. OutputStream os = new FileOutputStream(jarFile);
  414. out = (null == manifest ? new JarOutputStream(os)
  415. : new JarOutputStream(os, manifest));
  416. Log.signal(label + " out=" + out);
  417. ZipAccumulator reader = new ZipAccumulator(directory, out, filter);
  418. Log.signal(label + " reader=" + reader);
  419. FileUtil.descendFileTree(directory, reader);
  420. out.closeEntry();
  421. return true;
  422. } catch (IOException e) {
  423. e.printStackTrace(System.err); // todo
  424. } finally {
  425. if (null != out) {
  426. try { out.close();}
  427. catch (IOException e) {} // todo ignored
  428. }
  429. }
  430. return false;
  431. }
  432. protected static Manifest createManifest(String mainClass) {
  433. final String mainKey = "Main-Class";
  434. Manifest result = null;
  435. if (null != mainClass) {
  436. String entry = "Manifest-Version: 1.0\n"
  437. + mainKey + ": " + mainClass + "\n";
  438. try {
  439. result = new Manifest(new StringBufferInputStream(entry));
  440. Attributes attributes = result.getMainAttributes();
  441. String main = attributes.getValue(mainKey);
  442. if (null == main) {
  443. attributes.putValue(mainKey, mainClass);
  444. main = attributes.getValue(mainKey);
  445. if (null == main) {
  446. Log.signal("createManifest unable to set main "
  447. + mainClass);
  448. }
  449. }
  450. } catch (IOException e) { // todo ignoring
  451. Log.signal(e, " IOException creating manifest with " + mainClass);
  452. }
  453. }
  454. return result;
  455. }
  456. /** read a file out to the zip stream */
  457. protected static void addFileToZip(File in, File parent,
  458. ZipOutputStream out)
  459. throws IOException {
  460. String path = in.getCanonicalPath();
  461. String parentPath = parent.getCanonicalPath();
  462. if (!path.startsWith(parentPath)) {
  463. throw new Error("not parent: " + parentPath + " of " + path);
  464. } else {
  465. path = path.substring(1+parentPath.length());
  466. path = path.replace('\\', '/'); // todo: use filesep
  467. }
  468. ZipEntry entry = new ZipEntry(path);
  469. entry.setTime(in.lastModified());
  470. // todo: default behavior is DEFLATED
  471. out.putNextEntry(entry);
  472. InputStream input = null;
  473. try {
  474. input = new FileInputStream(in);
  475. byte[] buf = new byte[1024];
  476. int count;
  477. while (0 < (count = input.read(buf, 0, buf.length))) {
  478. out.write(buf, 0, count);
  479. }
  480. } finally {
  481. if (null != input) input.close();
  482. }
  483. }
  484. public static void returnTempDir(File dir) {
  485. deleteDirectory(dir);
  486. }
  487. /** @return true if path ends with gif, properties, jpg */
  488. public static boolean isResourcePath(String path) {
  489. if (null == path) return false;
  490. path = path.toLowerCase();
  491. return (path.endsWith(".gif")
  492. || path.endsWith(".properties")
  493. || path.endsWith(".jpg")
  494. || path.endsWith(".jpeg")
  495. || path.endsWith(".props")
  496. );
  497. }
  498. public static void render(Throwable t, StringBuffer err) { // todo: move
  499. String name = t.getClass().getName();
  500. int loc = name.lastIndexOf(".");
  501. name = name.substring(1+loc);
  502. err.append(name + ": " + t.getMessage() + "\n"); // todo
  503. StringWriter sw = new StringWriter();
  504. t.printStackTrace(new PrintWriter(sw));
  505. err.append(sw.toString());
  506. }
  507. private static boolean report(StringBuffer err, String context, String status,
  508. Throwable throwable) {
  509. boolean failed = ((null != status) || (null != throwable));
  510. if ((null != err) && (failed)) {
  511. if (null != context) {
  512. err.append(context);
  513. }
  514. if (null != status) {
  515. err.append(status);
  516. }
  517. if (null != throwable) {
  518. render(throwable, err);
  519. }
  520. }
  521. return failed;
  522. }
  523. /**
  524. * Copy file.
  525. * @param src the File to copy - must exist
  526. * @param dest the File for the target file or directory (will not create directories)
  527. * @param err the StringBuffer for returning any errors - may be null
  528. **/
  529. public static boolean copyFile(File src, File dest, StringBuffer err) {
  530. boolean result = false;
  531. String label = "start";
  532. Throwable throwable = null;
  533. try {
  534. if (!ValidFileFilter.FILE_EXISTS.accept(src)) {
  535. label = "src file does not exist";
  536. } else {
  537. if (dest.isDirectory()) {
  538. dest = new File(dest, src.getName());
  539. }
  540. if (ValidFileFilter.FILE_EXISTS.accept(dest)) {
  541. label = "dest file exists";
  542. }
  543. boolean closeWhenDone = true;
  544. result = copy(new FileInputStream(src),
  545. new FileOutputStream(dest),
  546. closeWhenDone);
  547. }
  548. label = null;
  549. } catch (Throwable t) {
  550. throwable = t;
  551. }
  552. String context = "FileUtil.copyFile(src, dest, err)";
  553. boolean report = report(err, context, label, throwable);
  554. return (result && !report);
  555. }
  556. /**
  557. * Copy URL to file.
  558. * @param src the URL to copy - must exist
  559. * @param dest the File for the target file or directory (will not create directories)
  560. * @param err the StringBuffer for returning any errors - may be null
  561. **/
  562. public static boolean copyURL(URL url, File dest, StringBuffer err) { // todo untested.
  563. boolean result = false;
  564. String label = "start";
  565. Throwable throwable = null;
  566. try {
  567. if (dest.isDirectory()) {
  568. String filename = url.getFile();
  569. if ((null == filename) || (0 == filename.length())) {
  570. filename = DEFAULT_URL_FILENAME;
  571. }
  572. dest = new File(dest, filename);
  573. }
  574. if (ValidFileFilter.FILE_EXISTS.accept(dest)) {
  575. label = "dest file exists";
  576. }
  577. boolean closeWhenDone = true;
  578. result = copy(url.openConnection().getInputStream(),
  579. new FileOutputStream(dest),
  580. closeWhenDone);
  581. label = null;
  582. } catch (Throwable t) {
  583. throwable = t;
  584. }
  585. String context = "FileUtil.copyURL(src, dest, err)"; // add actual parm to labels?
  586. boolean report = report(err, context, label, throwable);
  587. return (result && report);
  588. }
  589. /**
  590. * Copy input to output - does not close either
  591. * @param src the InputStream to copy - must exist
  592. * @param dest the OutputStream for the target
  593. * @param close if true, close when done
  594. */
  595. public static boolean copy(InputStream src, OutputStream dest,
  596. boolean close)
  597. throws IOException {
  598. boolean result = false;
  599. IOException throwable = null;
  600. try {
  601. byte[] buf = new byte[8*1024];
  602. int count;
  603. while (0 < (count = src.read(buf, 0, buf.length))) {
  604. dest.write(buf, 0, count);
  605. }
  606. result = true;
  607. } catch (IOException t) {
  608. throwable = t;
  609. } finally {
  610. if (close) {
  611. try { if (null != src) src.close(); }
  612. catch (IOException e) {
  613. if (null == throwable) { throwable = e; }
  614. }
  615. try { if (null != dest) dest.close(); }
  616. catch (IOException i) {
  617. if (null == throwable) { throwable = i; }
  618. }
  619. }
  620. }
  621. if (null != throwable) throw throwable;
  622. return result;
  623. }
  624. /**
  625. * @return true if dir was an existing directory that is now deleted
  626. */
  627. protected static boolean deleteDirectory(File dir) {
  628. return ((null != dir)
  629. && dir.exists()
  630. && dir.isDirectory()
  631. && FileUtil.descendFileTree(dir, DELETE_FILES, false)
  632. && FileUtil.descendFileTree(dir, DELETE_DIRS, true)
  633. && dir.delete());
  634. }
  635. public static String[] getPaths(File[] files) { // util
  636. String[] result = new String[files.length];
  637. for (int i = 0; i < result.length; i++) {
  638. result[i] = files[i].getPath(); // preserves absolute?
  639. }
  640. return result;
  641. }
  642. //-------- first-order, input and visible interface
  643. protected static final FileFilter DELETE_DIRS = new FileFilter() {
  644. public boolean accept(File file) {
  645. return ((null != file) && file.isDirectory()
  646. && file.exists() && file.delete());
  647. }
  648. };
  649. protected static final FileFilter DELETE_FILES = new FileFilter() {
  650. public boolean accept(File file) {
  651. return ((null != file) && !file.isDirectory()
  652. && file.exists() && file.delete());
  653. }
  654. };
  655. } // class FileUtil
  656. /**
  657. * Localize FileUtil log/signals for now
  658. * ordinary signals are ignored,
  659. * but exceptions are printed to err
  660. * and errors are thrown as Error
  661. */
  662. class Log {
  663. /** ordinary logging - may be suppressed */
  664. public static final void signal(String s) {
  665. //System.err.println(s);
  666. }
  667. /** print stack trace to System.err */
  668. public static final void signal(Throwable t, String s) {
  669. System.err.println(s);
  670. t.printStackTrace(System.err);
  671. }
  672. /** @throws Error(s) always */
  673. public static final void error(String s) {
  674. throw new Error(s);
  675. }
  676. }
  677. /** read each file out to the zip file */
  678. class ZipAccumulator implements FileFilter {
  679. final File parentDir;
  680. final ZipOutputStream out;
  681. final FileFilter filter;
  682. public ZipAccumulator(File parentDir, ZipOutputStream out,
  683. FileFilter filter) {
  684. this.parentDir = parentDir;
  685. this.out = out;
  686. this.filter = filter;
  687. }
  688. public boolean accept(File f) {
  689. if ((null != filter) && (!filter.accept(f))) {
  690. return false;
  691. }
  692. try {
  693. FileUtil.addFileToZip(f, parentDir, out);
  694. return true;
  695. } catch (IOException e) {
  696. e.printStackTrace(System.err); // todo
  697. }
  698. return false;
  699. }
  700. }