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 48KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608
  1. /* *******************************************************************
  2. * Copyright (c) 1999-2001 Xerox Corporation,
  3. * 2002 Palo Alto Research Center, Incorporated (PARC).
  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. * Xerox/PARC initial implementation
  12. * ******************************************************************/
  13. package org.aspectj.util;
  14. import java.io.BufferedOutputStream;
  15. import java.io.BufferedReader;
  16. import java.io.ByteArrayOutputStream;
  17. import java.io.DataInputStream;
  18. import java.io.DataOutputStream;
  19. import java.io.File;
  20. import java.io.FileFilter;
  21. import java.io.FileInputStream;
  22. import java.io.FileNotFoundException;
  23. import java.io.FileOutputStream;
  24. import java.io.FileReader;
  25. import java.io.FileWriter;
  26. import java.io.FilenameFilter;
  27. import java.io.IOException;
  28. import java.io.InputStream;
  29. import java.io.OutputStream;
  30. import java.io.PrintStream;
  31. import java.io.Reader;
  32. import java.io.StringReader;
  33. import java.io.Writer;
  34. import java.net.MalformedURLException;
  35. import java.net.URISyntaxException;
  36. import java.net.URL;
  37. import java.util.ArrayList;
  38. import java.util.Arrays;
  39. import java.util.Collections;
  40. import java.util.LinkedList;
  41. import java.util.List;
  42. import java.util.zip.ZipEntry;
  43. import java.util.zip.ZipFile;
  44. /**
  45. * @author Andy Clement
  46. * @author Kris De Volder
  47. */
  48. public class FileUtil {
  49. /** default parent directory File when a file has a null parent */
  50. public static final File DEFAULT_PARENT = new File("."); // XXX user.dir?
  51. /** unmodifiable List of String source file suffixes (including leading ".") */
  52. public static final List<String> SOURCE_SUFFIXES = Collections.unmodifiableList(Arrays.asList(new String[] { ".java", ".aj" }));
  53. public static final FileFilter ZIP_FILTER = new FileFilter() {
  54. public boolean accept(File file) {
  55. return isZipFile(file);
  56. }
  57. public String toString() {
  58. return "ZIP_FILTER";
  59. }
  60. };
  61. // public static final FileFilter SOURCE_FILTER = new FileFilter() {
  62. // public boolean accept(File file) {
  63. // return hasSourceSuffix(file);
  64. // }
  65. //
  66. // public String toString() {
  67. // return "SOURCE_FILTER";
  68. // }
  69. // };
  70. final static int[] INT_RA = new int[0];
  71. /** accept all files */
  72. public static final FileFilter ALL = new FileFilter() {
  73. public boolean accept(File f) {
  74. return true;
  75. }
  76. };
  77. public static final FileFilter DIRS_AND_WRITABLE_CLASSES = new FileFilter() {
  78. public boolean accept(File file) {
  79. return ((null != file) && (file.isDirectory() || (file.canWrite() && file.getName().toLowerCase().endsWith(".class"))));
  80. }
  81. };
  82. private static final boolean PERMIT_CVS;
  83. static {
  84. String name = FileUtil.class.getName() + ".PERMIT_CVS";
  85. PERMIT_CVS = LangUtil.getBoolean(name, false);
  86. }
  87. /** @return true if file exists and is a zip file */
  88. public static boolean isZipFile(File file) {
  89. try {
  90. return (null != file) && new ZipFile(file) != null;
  91. } catch (IOException e) {
  92. return false;
  93. }
  94. }
  95. /** @return true if path ends with .zip or .jar */
  96. // public static boolean hasZipSuffix(String path) {
  97. // return ((null != path) && (0 != zipSuffixLength(path)));
  98. // }
  99. /** @return 0 if file has no zip/jar suffix or 4 otherwise */
  100. public static int zipSuffixLength(File file) {
  101. return (null == file ? 0 : zipSuffixLength(file.getPath()));
  102. }
  103. /** @return 0 if no zip/jar suffix or 4 otherwise */
  104. public static int zipSuffixLength(String path) {
  105. if ((null != path) && (4 < path.length())) {
  106. String test = path.substring(path.length() - 4).toLowerCase();
  107. if (".zip".equals(test) || ".jar".equals(test)) {
  108. return 4;
  109. }
  110. }
  111. return 0;
  112. }
  113. /** @return true if file path has a source suffix */
  114. public static boolean hasSourceSuffix(File file) {
  115. return ((null != file) && hasSourceSuffix(file.getPath()));
  116. }
  117. /** @return true if path ends with .java or .aj */
  118. public static boolean hasSourceSuffix(String path) {
  119. return ((null != path) && (0 != sourceSuffixLength(path)));
  120. }
  121. /**
  122. * @return 0 if file has no source suffix or the length of the suffix otherwise
  123. */
  124. public static int sourceSuffixLength(File file) {
  125. return (null == file ? 0 : sourceSuffixLength(file.getPath()));
  126. }
  127. /** @return 0 if no source suffix or the length of the suffix otherwise */
  128. public static int sourceSuffixLength(String path) {
  129. if (LangUtil.isEmpty(path)) {
  130. return 0;
  131. }
  132. for (String suffix : SOURCE_SUFFIXES) {
  133. if (path.endsWith(suffix) || path.toLowerCase().endsWith(suffix)) {
  134. return suffix.length();
  135. }
  136. }
  137. return 0;
  138. }
  139. /** @return true if this is a readable directory */
  140. public static boolean canReadDir(File dir) {
  141. return ((null != dir) && dir.canRead() && dir.isDirectory());
  142. }
  143. /** @return true if this is a readable file */
  144. public static boolean canReadFile(File file) {
  145. return ((null != file) && file.canRead() && file.isFile());
  146. }
  147. /** @return true if dir is a writable directory */
  148. public static boolean canWriteDir(File dir) {
  149. return ((null != dir) && dir.canWrite() && dir.isDirectory());
  150. }
  151. /** @return true if this is a writable file */
  152. public static boolean canWriteFile(File file) {
  153. return ((null != file) && file.canWrite() && file.isFile());
  154. }
  155. // /**
  156. // * @throws IllegalArgumentException unless file is readable and not a
  157. // * directory
  158. // */
  159. // public static void throwIaxUnlessCanReadFile(File file, String label) {
  160. // if (!canReadFile(file)) {
  161. // throw new IllegalArgumentException(label + " not readable file: " +
  162. // file);
  163. // }
  164. // }
  165. /**
  166. * @throws IllegalArgumentException unless dir is a readable directory
  167. */
  168. public static void throwIaxUnlessCanReadDir(File dir, String label) {
  169. if (!canReadDir(dir)) {
  170. throw new IllegalArgumentException(label + " not readable dir: " + dir);
  171. }
  172. }
  173. /**
  174. * @throws IllegalArgumentException unless file is readable and not a directory
  175. */
  176. public static void throwIaxUnlessCanWriteFile(File file, String label) {
  177. if (!canWriteFile(file)) {
  178. throw new IllegalArgumentException(label + " not writable file: " + file);
  179. }
  180. }
  181. /** @throws IllegalArgumentException unless dir is a readable directory */
  182. public static void throwIaxUnlessCanWriteDir(File dir, String label) {
  183. if (!canWriteDir(dir)) {
  184. throw new IllegalArgumentException(label + " not writable dir: " + dir);
  185. }
  186. }
  187. /** @return array same length as input, with String paths */
  188. public static String[] getPaths(File[] files) {
  189. if ((null == files) || (0 == files.length)) {
  190. return new String[0];
  191. }
  192. String[] result = new String[files.length];
  193. for (int i = 0; i < result.length; i++) {
  194. if (null != files[i]) {
  195. result[i] = files[i].getPath();
  196. }
  197. }
  198. return result;
  199. }
  200. /** @return array same length as input, with String paths */
  201. public static String[] getPaths(List<File> files) {
  202. final int size = (null == files ? 0 : files.size());
  203. if (0 == size) {
  204. return new String[0];
  205. }
  206. String[] result = new String[size];
  207. for (int i = 0; i < size; i++) {
  208. File file = files.get(i);
  209. if (null != file) {
  210. result[i] = file.getPath();
  211. }
  212. }
  213. return result;
  214. }
  215. /**
  216. * Extract the name of a class from the path to its file. If the basedir is null, then the class is assumed to be in the default
  217. * package unless the classFile has one of the top-level suffixes { com, org, java, javax } as a parent directory.
  218. *
  219. * @param basedir the File of the base directory (prefix of classFile)
  220. * @param classFile the File of the class to extract the name for
  221. * @throws IllegalArgumentException if classFile is null or does not end with ".class" or a non-null basedir is not a prefix of
  222. * classFile
  223. */
  224. public static String fileToClassName(File basedir, File classFile) {
  225. LangUtil.throwIaxIfNull(classFile, "classFile");
  226. String classFilePath = normalizedPath(classFile);
  227. if (!classFilePath.endsWith(".class")) {
  228. String m = classFile + " does not end with .class";
  229. throw new IllegalArgumentException(m);
  230. }
  231. classFilePath = classFilePath.substring(0, classFilePath.length() - 6);
  232. if (null != basedir) {
  233. String basePath = normalizedPath(basedir);
  234. if (!classFilePath.startsWith(basePath)) {
  235. String m = classFile + " does not start with " + basedir;
  236. throw new IllegalArgumentException(m);
  237. }
  238. classFilePath = classFilePath.substring(basePath.length() + 1);
  239. } else {
  240. final String[] suffixes = new String[] { "com", "org", "java", "javax" };
  241. boolean found = false;
  242. for (int i = 0; !found && (i < suffixes.length); i++) {
  243. int loc = classFilePath.indexOf(suffixes[i] + "/");
  244. if ((0 == loc) || ((-1 != loc) && ('/' == classFilePath.charAt(loc - 1)))) {
  245. classFilePath = classFilePath.substring(loc);
  246. found = true;
  247. }
  248. }
  249. if (!found) {
  250. int loc = classFilePath.lastIndexOf("/");
  251. if (-1 != loc) { // treat as default package
  252. classFilePath = classFilePath.substring(loc + 1);
  253. }
  254. }
  255. }
  256. return classFilePath.replace('/', '.');
  257. }
  258. /**
  259. * Normalize path for comparisons by rendering absolute, clipping basedir prefix, trimming and changing '\\' to '/'
  260. *
  261. * @param file the File with the path to normalize
  262. * @param basedir the File for the prefix of the file to normalize - ignored if null
  263. * @return "" if null or normalized path otherwise
  264. * @throws IllegalArgumentException if basedir is not a prefix of file
  265. */
  266. public static String normalizedPath(File file, File basedir) {
  267. String filePath = normalizedPath(file);
  268. if (null != basedir) {
  269. String basePath = normalizedPath(basedir);
  270. if (filePath.startsWith(basePath)) {
  271. filePath = filePath.substring(basePath.length());
  272. if (filePath.startsWith("/")) {
  273. filePath = filePath.substring(1);
  274. }
  275. }
  276. }
  277. return filePath;
  278. }
  279. /**
  280. * Render a set of files to String as a path by getting absolute paths of each and delimiting with infix.
  281. *
  282. * @param files the File[] to flatten - may be null or empty
  283. * @param infix the String delimiter internally between entries (if null, then use File.pathSeparator). (alias to
  284. * <code>flatten(getAbsolutePaths(files), infix)</code>
  285. * @return String with absolute paths to entries in order, delimited with infix
  286. */
  287. public static String flatten(File[] files, String infix) {
  288. if (LangUtil.isEmpty(files)) {
  289. return "";
  290. }
  291. return flatten(getPaths(files), infix);
  292. }
  293. /**
  294. * Flatten File[] to String.
  295. *
  296. * @param paths the String[] of paths to flatten - null ignored
  297. * @param infix the String infix to use - null treated as File.pathSeparator
  298. */
  299. public static String flatten(String[] paths, String infix) {
  300. if (null == infix) {
  301. infix = File.pathSeparator;
  302. }
  303. StringBuffer result = new StringBuffer();
  304. boolean first = true;
  305. for (String path : paths) {
  306. if (null == path) {
  307. continue;
  308. }
  309. if (first) {
  310. first = false;
  311. }
  312. else {
  313. result.append(infix);
  314. }
  315. result.append(path);
  316. }
  317. return result.toString();
  318. }
  319. /**
  320. * Normalize path for comparisons by rendering absolute trimming and changing '\\' to '/'
  321. *
  322. * @return "" if null or normalized path otherwise
  323. */
  324. public static String normalizedPath(File file) {
  325. return (null == file ? "" : weakNormalize(file.getAbsolutePath()));
  326. }
  327. /**
  328. * Weakly normalize path for comparisons by trimming and changing '\\' to '/'
  329. */
  330. public static String weakNormalize(String path) {
  331. if (null != path) {
  332. path = path.replace('\\', '/').trim();
  333. }
  334. return path;
  335. }
  336. /**
  337. * Get best File for the first-readable path in input paths, treating entries prefixed "sp:" as system property keys. Safe to
  338. * call in static initializers.
  339. *
  340. * @param paths the String[] of paths to check.
  341. * @return null if not found, or valid File otherwise
  342. */
  343. public static File getBestFile(String[] paths) {
  344. if (null == paths) {
  345. return null;
  346. }
  347. File result = null;
  348. for (int i = 0; (null == result) && (i < paths.length); i++) {
  349. String path = paths[i];
  350. if (null == path) {
  351. continue;
  352. }
  353. if (path.startsWith("sp:")) {
  354. try {
  355. path = System.getProperty(path.substring(3));
  356. } catch (Throwable t) {
  357. path = null;
  358. }
  359. if (null == path) {
  360. continue;
  361. }
  362. }
  363. try {
  364. File f = new File(path);
  365. if (f.exists() && f.canRead()) {
  366. result = FileUtil.getBestFile(f);
  367. }
  368. } catch (Throwable t) {
  369. // swallow
  370. }
  371. }
  372. return result;
  373. }
  374. public static File getBestFile(String[] paths, boolean mustBeJar) {
  375. if (null == paths) {
  376. return null;
  377. }
  378. File result = null;
  379. for (int i = 0; (null == result) && (i < paths.length); i++) {
  380. String path = paths[i];
  381. if (null == path) {
  382. continue;
  383. }
  384. if (path.startsWith("sp:")) {
  385. try {
  386. path = System.getProperty(path.substring(3));
  387. } catch (Throwable t) {
  388. path = null;
  389. }
  390. if (null == path) {
  391. continue;
  392. }
  393. }
  394. try {
  395. File f = new File(path);
  396. if (f.exists() && f.canRead()) {
  397. if (mustBeJar && !f.isDirectory()) {
  398. result = FileUtil.getBestFile(f);
  399. }
  400. }
  401. } catch (Throwable t) {
  402. // swallow
  403. }
  404. }
  405. return result;
  406. }
  407. /**
  408. * Render as best file, canonical or absolute.
  409. *
  410. * @param file the File to get the best File for (not null)
  411. * @return File of the best-available path
  412. * @throws IllegalArgumentException if file is null
  413. */
  414. public static File getBestFile(File file) {
  415. LangUtil.throwIaxIfNull(file, "file");
  416. if (file.exists()) {
  417. try {
  418. return file.getCanonicalFile();
  419. } catch (IOException e) {
  420. return file.getAbsoluteFile();
  421. }
  422. } else {
  423. return file;
  424. }
  425. }
  426. /**
  427. * Render as best path, canonical or absolute.
  428. *
  429. * @param file the File to get the path for (not null)
  430. * @return String of the best-available path
  431. * @throws IllegalArgumentException if file is null
  432. */
  433. public static String getBestPath(File file) {
  434. LangUtil.throwIaxIfNull(file, "file");
  435. if (file.exists()) {
  436. try {
  437. return file.getCanonicalPath();
  438. } catch (IOException e) {
  439. return file.getAbsolutePath();
  440. }
  441. } else {
  442. return file.getPath();
  443. }
  444. }
  445. /** @return array same length as input, with String absolute paths */
  446. public static String[] getAbsolutePaths(File[] files) {
  447. if ((null == files) || (0 == files.length)) {
  448. return new String[0];
  449. }
  450. String[] result = new String[files.length];
  451. for (int i = 0; i < result.length; i++) {
  452. if (null != files[i]) {
  453. result[i] = files[i].getAbsolutePath();
  454. }
  455. }
  456. return result;
  457. }
  458. /**
  459. * Recursively delete the contents of dir, but not the dir itself
  460. *
  461. * @return the total number of files deleted
  462. */
  463. public static int deleteContents(File dir) {
  464. return deleteContents(dir, ALL);
  465. }
  466. /**
  467. * Recursively delete some contents of dir, but not the dir itself. This deletes any subdirectory which is empty after its files
  468. * are deleted.
  469. *
  470. * @return the total number of files deleted
  471. */
  472. public static int deleteContents(File dir, FileFilter filter) {
  473. return deleteContents(dir, filter, true);
  474. }
  475. /**
  476. * Recursively delete some contents of dir, but not the dir itself. If deleteEmptyDirs is true, this deletes any subdirectory
  477. * which is empty after its files are deleted.
  478. *
  479. * @param dir the File directory (if a file, the the file is deleted)
  480. * @return the total number of files deleted
  481. */
  482. public static int deleteContents(File dir, FileFilter filter,
  483. boolean deleteEmptyDirs) {
  484. if (null == dir) {
  485. throw new IllegalArgumentException("null dir");
  486. }
  487. if ((!dir.exists()) || (!dir.canWrite())) {
  488. return 0;
  489. }
  490. if (!dir.isDirectory()) {
  491. dir.delete();
  492. return 1;
  493. }
  494. String[] fromFiles = dir.list();
  495. if (fromFiles == null) {
  496. return 0;
  497. }
  498. int result = 0;
  499. for (String string : fromFiles) {
  500. File file = new File(dir, string);
  501. if ((null == filter) || filter.accept(file)) {
  502. if (file.isDirectory()) {
  503. result += deleteContents(file, filter, deleteEmptyDirs);
  504. String[] fileContent = file.list();
  505. if (deleteEmptyDirs && fileContent != null
  506. && 0 == fileContent.length) {
  507. file.delete();
  508. }
  509. }
  510. else {
  511. /* boolean ret = */
  512. file.delete();
  513. result++;
  514. }
  515. }
  516. }
  517. return result;
  518. }
  519. /**
  520. * Copy contents of fromDir into toDir
  521. *
  522. * @param fromDir must exist and be readable
  523. * @param toDir must exist or be creatable and be writable
  524. * @return the total number of files copied
  525. */
  526. public static int copyDir(File fromDir, File toDir) throws IOException {
  527. return copyDir(fromDir, toDir, null, null);
  528. }
  529. /**
  530. * Recursively copy files in fromDir (with any fromSuffix) to toDir, replacing fromSuffix with toSuffix if any. This silently
  531. * ignores dirs and files that are not readable but throw IOException for directories that are not writable. This does not clean
  532. * out the original contents of toDir. (subdirectories are not renamed per directory rules)
  533. *
  534. * @param fromSuffix select files with this suffix - select all if null or empty
  535. * @param toSuffix replace fromSuffix with toSuffix in the destination file name - ignored if null or empty, appended to name if
  536. * fromSuffix is null or empty
  537. * @return the total number of files copied
  538. */
  539. public static int copyDir(File fromDir, File toDir, final String fromSuffix, String toSuffix) throws IOException {
  540. return copyDir(fromDir, toDir, fromSuffix, toSuffix, (FileFilter) null);
  541. }
  542. // /**
  543. // * Recursively copy files in fromDir (with any fromSuffix) to toDir,
  544. // * replacing fromSuffix with toSuffix if any, and adding the destination
  545. // * file to any collector. This silently ignores dirs and files that are
  546. // not
  547. // * readable but throw IOException for directories that are not writable.
  548. // * This does not clean out the original contents of toDir. (subdirectories
  549. // * are not renamed per directory rules) This calls any delegate
  550. // * FilenameFilter to collect any selected file.
  551. // *
  552. // * @param fromSuffix select files with this suffix - select all if null or
  553. // * empty
  554. // * @param toSuffix replace fromSuffix with toSuffix in the destination
  555. // file
  556. // * name - ignored if null or empty, appended to name if
  557. // * fromSuffix is null or empty
  558. // * @param collector the List sink for destination files - ignored if null
  559. // * @return the total number of files copied
  560. // */
  561. // public static int copyDir(File fromDir, File toDir, final String
  562. // fromSuffix, final String toSuffix, final List collector)
  563. // throws IOException {
  564. // // int before = collector.size();
  565. // if (null == collector) {
  566. // return copyDir(fromDir, toDir, fromSuffix, toSuffix);
  567. // } else {
  568. // FileFilter collect = new FileFilter() {
  569. // public boolean accept(File pathname) {
  570. // return collector.add(pathname);
  571. // }
  572. // };
  573. // return copyDir(fromDir, toDir, fromSuffix, toSuffix, collect);
  574. // }
  575. // }
  576. /**
  577. * Recursively copy files in fromDir (with any fromSuffix) to toDir, replacing fromSuffix with toSuffix if any. This silently
  578. * ignores dirs and files that are not readable but throw IOException for directories that are not writable. This does not clean
  579. * out the original contents of toDir. (subdirectories are not renamed per directory rules) This calls any delegate
  580. * FilenameFilter to collect any selected file.
  581. *
  582. * @param fromSuffix select files with this suffix - select all if null or empty
  583. * @param toSuffix replace fromSuffix with toSuffix in the destination file name - ignored if null or empty, appended to name if
  584. * fromSuffix is null or empty
  585. * @return the total number of files copied
  586. */
  587. public static int copyDir(File fromDir, File toDir, final String fromSuffix, final String toSuffix, final FileFilter delegate)
  588. throws IOException {
  589. if ((null == fromDir) || (!fromDir.canRead())) {
  590. return 0;
  591. }
  592. final boolean haveSuffix = ((null != fromSuffix) && (0 < fromSuffix.length()));
  593. final int slen = (!haveSuffix ? 0 : fromSuffix.length());
  594. if (!toDir.exists()) {
  595. toDir.mkdirs();
  596. }
  597. final String[] fromFiles;
  598. if (!haveSuffix) {
  599. fromFiles = fromDir.list();
  600. } else {
  601. FilenameFilter filter = new FilenameFilter() {
  602. public boolean accept(File dir, String name) {
  603. return (new File(dir, name).isDirectory() || (name.endsWith(fromSuffix)));
  604. }
  605. };
  606. fromFiles = fromDir.list(filter);
  607. }
  608. int result = 0;
  609. final int MAX = (null == fromFiles ? 0 : fromFiles.length);
  610. for (int i = 0; i < MAX; i++) {
  611. String filename = fromFiles[i];
  612. File fromFile = new File(fromDir, filename);
  613. if (fromFile.canRead()) {
  614. if (fromFile.isDirectory()) {
  615. result += copyDir(fromFile, new File(toDir, filename), fromSuffix, toSuffix, delegate);
  616. } else if (fromFile.isFile()) {
  617. if (haveSuffix) {
  618. filename = filename.substring(0, filename.length() - slen);
  619. }
  620. if (null != toSuffix) {
  621. filename = filename + toSuffix;
  622. }
  623. File targetFile = new File(toDir, filename);
  624. if ((null == delegate) || delegate.accept(targetFile)) {
  625. copyFile(fromFile, targetFile);
  626. }
  627. result++;
  628. }
  629. }
  630. }
  631. return result;
  632. }
  633. /**
  634. * Recursively list files in srcDir.
  635. *
  636. * @return ArrayList with String paths of File under srcDir (relative to srcDir)
  637. */
  638. public static String[] listFiles(File srcDir) {
  639. ArrayList<String> result = new ArrayList<>();
  640. if ((null != srcDir) && srcDir.canRead()) {
  641. listFiles(srcDir, null, result);
  642. }
  643. return result.toArray(new String[0]);
  644. }
  645. public static final FileFilter aspectjSourceFileFilter = new FileFilter() {
  646. public boolean accept(File pathname) {
  647. String name = pathname.getName().toLowerCase();
  648. return name.endsWith(".java") || name.endsWith(".aj");
  649. }
  650. };
  651. /**
  652. * Recursively list files in srcDir.
  653. *
  654. * @return ArrayList with String paths of File under srcDir (relative to srcDir)
  655. */
  656. public static File[] listFiles(File srcDir, FileFilter fileFilter) {
  657. ArrayList<File> result = new ArrayList<>();
  658. if ((null != srcDir) && srcDir.canRead()) {
  659. listFiles(srcDir, result, fileFilter);
  660. }
  661. return result.toArray(new File[0]);
  662. }
  663. /**
  664. * Recursively list .class files in specified directory
  665. *
  666. * @return List of File objects
  667. */
  668. public static List<File> listClassFiles(File dir) {
  669. ArrayList<File> result = new ArrayList<>();
  670. if ((null != dir) && dir.canRead()) {
  671. listClassFiles(dir, result);
  672. }
  673. return result;
  674. }
  675. /**
  676. * Convert String[] paths to File[] as offset of base directory
  677. *
  678. * @param basedir the non-null File base directory for File to create with paths
  679. * @param paths the String[] of paths to create
  680. * @return File[] with same length as paths
  681. */
  682. public static File[] getBaseDirFiles(File basedir, String[] paths) {
  683. return getBaseDirFiles(basedir, paths, (String[]) null);
  684. }
  685. /**
  686. * Convert String[] paths to File[] as offset of base directory
  687. *
  688. * @param basedir the non-null File base directory for File to create with paths
  689. * @param paths the String[] of paths to create
  690. * @param suffixes the String[] of suffixes to limit sources to - ignored if null
  691. * @return File[] with same length as paths
  692. */
  693. public static File[] getBaseDirFiles(File basedir, String[] paths, String[] suffixes) {
  694. LangUtil.throwIaxIfNull(basedir, "basedir");
  695. LangUtil.throwIaxIfNull(paths, "paths");
  696. File[] result = null;
  697. if (!LangUtil.isEmpty(suffixes)) {
  698. ArrayList<File> list = new ArrayList<>();
  699. for (String path : paths) {
  700. for (String suffix : suffixes) {
  701. if (path.endsWith(suffix)) {
  702. list.add(new File(basedir, path));
  703. break;
  704. }
  705. }
  706. }
  707. result = list.toArray(new File[0]);
  708. } else {
  709. result = new File[paths.length];
  710. for (int i = 0; i < result.length; i++) {
  711. result[i] = newFile(basedir, paths[i]);
  712. }
  713. }
  714. return result;
  715. }
  716. /**
  717. * Create a new File, resolving paths ".." and "." specially.
  718. *
  719. * @param dir the File for the parent directory of the file
  720. * @param path the path in the parent directory (filename only?)
  721. * @return File for the new file.
  722. */
  723. private static File newFile(File dir, String path) {
  724. if (".".equals(path)) {
  725. return dir;
  726. } else if ("..".equals(path)) {
  727. File parentDir = dir.getParentFile();
  728. if (null != parentDir) {
  729. return parentDir;
  730. } else {
  731. return new File(dir, "..");
  732. }
  733. } else {
  734. return new File(dir, path);
  735. }
  736. }
  737. /**
  738. * Copy files from source dir into destination directory, creating any needed directories. This differs from copyDir in not
  739. * being recursive; each input with the source dir creates a full path. However, if the source is a directory, it is copied as
  740. * such.
  741. *
  742. * @param srcDir an existing, readable directory containing relativePaths files
  743. * @param relativePaths a set of paths relative to srcDir to readable File to copy
  744. * @param destDir an existing, writable directory to copy files to
  745. * @throws IllegalArgumentException if input invalid, IOException if operations fail
  746. */
  747. public static File[] copyFiles(File srcDir, String[] relativePaths, File destDir) throws IllegalArgumentException, IOException {
  748. final String[] paths = relativePaths;
  749. throwIaxUnlessCanReadDir(srcDir, "srcDir");
  750. throwIaxUnlessCanWriteDir(destDir, "destDir");
  751. LangUtil.throwIaxIfNull(paths, "relativePaths");
  752. File[] result = new File[paths.length];
  753. for (int i = 0; i < paths.length; i++) {
  754. String path = paths[i];
  755. LangUtil.throwIaxIfNull(path, "relativePaths-entry");
  756. File src = newFile(srcDir, paths[i]);
  757. File dest = newFile(destDir, path);
  758. File destParent = dest.getParentFile();
  759. if (!destParent.exists()) {
  760. destParent.mkdirs();
  761. }
  762. LangUtil.throwIaxIfFalse(canWriteDir(destParent), "dest-entry-parent");
  763. copyFile(src, dest); // both file-dir and dir-dir copies
  764. result[i] = dest;
  765. }
  766. return result;
  767. }
  768. /**
  769. * Copy fromFile to toFile, handling file-file, dir-dir, and file-dir copies.
  770. *
  771. * @param fromFile the File path of the file or directory to copy - must be readable
  772. * @param toFile the File path of the target file or directory - must be writable (will be created if it does not exist)
  773. */
  774. public static void copyFile(File fromFile, File toFile) throws IOException {
  775. LangUtil.throwIaxIfNull(fromFile, "fromFile");
  776. LangUtil.throwIaxIfNull(toFile, "toFile");
  777. LangUtil.throwIaxIfFalse(!toFile.equals(fromFile), "same file");
  778. if (toFile.isDirectory()) { // existing directory
  779. throwIaxUnlessCanWriteDir(toFile, "toFile");
  780. if (fromFile.isFile()) { // file-dir
  781. File targFile = new File(toFile, fromFile.getName());
  782. copyValidFiles(fromFile, targFile);
  783. } else if (fromFile.isDirectory()) { // dir-dir
  784. copyDir(fromFile, toFile);
  785. } else {
  786. LangUtil.throwIaxIfFalse(false, "not dir or file: " + fromFile);
  787. }
  788. } else if (toFile.isFile()) { // target file exists
  789. if (fromFile.isDirectory()) {
  790. LangUtil.throwIaxIfFalse(false, "can't copy to file dir: " + fromFile);
  791. }
  792. copyValidFiles(fromFile, toFile); // file-file
  793. } else { // target file is a non-existent path -- could be file or dir
  794. /* File toFileParent = */ensureParentWritable(toFile);
  795. if (fromFile.isFile()) {
  796. copyValidFiles(fromFile, toFile);
  797. } else if (fromFile.isDirectory()) {
  798. toFile.mkdirs();
  799. throwIaxUnlessCanWriteDir(toFile, "toFile");
  800. copyDir(fromFile, toFile);
  801. } else {
  802. LangUtil.throwIaxIfFalse(false, "not dir or file: " + fromFile);
  803. }
  804. }
  805. }
  806. /**
  807. * Ensure that the parent directory to path can be written. If the path has a null parent, DEFAULT_PARENT is tested. If the path
  808. * parent does not exist, this tries to create it.
  809. *
  810. * @param path the File path whose parent should be writable
  811. * @return the File path of the writable parent directory
  812. * @throws IllegalArgumentException if parent cannot be written or path is null.
  813. */
  814. public static File ensureParentWritable(File path) {
  815. LangUtil.throwIaxIfNull(path, "path");
  816. File pathParent = path.getParentFile();
  817. if (null == pathParent) {
  818. pathParent = DEFAULT_PARENT;
  819. }
  820. if (!pathParent.canWrite()) {
  821. pathParent.mkdirs();
  822. }
  823. throwIaxUnlessCanWriteDir(pathParent, "pathParent");
  824. return pathParent;
  825. }
  826. /**
  827. * Copy file to file.
  828. *
  829. * @param fromFile the File to copy (readable, non-null file)
  830. * @param toFile the File to copy to (non-null, parent dir exists)
  831. * @throws IOException
  832. */
  833. public static void copyValidFiles(File fromFile, File toFile) throws IOException {
  834. FileInputStream in = null;
  835. FileOutputStream out = null;
  836. try {
  837. in = new FileInputStream(fromFile);
  838. out = new FileOutputStream(toFile);
  839. copyStream(in, out);
  840. } finally {
  841. if (out != null) {
  842. out.close();
  843. }
  844. if (in != null) {
  845. in.close();
  846. }
  847. }
  848. }
  849. /** do line-based copying */
  850. @SuppressWarnings("deprecation")
  851. public static void copyStream(DataInputStream in, PrintStream out) throws IOException {
  852. LangUtil.throwIaxIfNull(in, "in");
  853. LangUtil.throwIaxIfNull(in, "out");
  854. String s;
  855. while (null != (s = in.readLine())) {
  856. out.println(s);
  857. }
  858. }
  859. public static void copyStream(InputStream in, OutputStream out) throws IOException {
  860. final int MAX = 4096;
  861. byte[] buf = new byte[MAX];
  862. for (int bytesRead = in.read(buf, 0, MAX); bytesRead != -1; bytesRead = in.read(buf, 0, MAX)) {
  863. out.write(buf, 0, bytesRead);
  864. }
  865. }
  866. public static void copyStream(Reader in, Writer out) throws IOException {
  867. final int MAX = 4096;
  868. char[] buf = new char[MAX];
  869. for (int bytesRead = in.read(buf, 0, MAX); bytesRead != -1; bytesRead = in.read(buf, 0, MAX)) {
  870. out.write(buf, 0, bytesRead);
  871. }
  872. }
  873. /**
  874. * Make a new child directory of parent
  875. *
  876. * @param parent a File for the parent (writable)
  877. * @param child a prefix for the child directory
  878. * @return a File dir that exists with parentDir as the parent file or null
  879. */
  880. public static File makeNewChildDir(File parent, String child) {
  881. if (null == parent || !parent.canWrite() || !parent.isDirectory()) {
  882. throw new IllegalArgumentException("bad parent: " + parent);
  883. } else if (null == child) {
  884. child = "makeNewChildDir";
  885. } else if (!isValidFileName(child)) {
  886. throw new IllegalArgumentException("bad child: " + child);
  887. }
  888. File result = new File(parent, child);
  889. int safety = 1000;
  890. for (String suffix = FileUtil.randomFileString(); ((0 < --safety) && result.exists()); suffix = FileUtil.randomFileString()) {
  891. result = new File(parent, child + suffix);
  892. }
  893. if (result.exists()) {
  894. System.err.println("exhausted files for child dir in " + parent);
  895. return null;
  896. }
  897. return ((result.mkdirs() && result.exists()) ? result : null);
  898. }
  899. /**
  900. * Make a new temporary directory in the same directory that the system uses for temporary files, or if that files, in the
  901. * current directory.
  902. *
  903. * @param name the preferred (simple) name of the directory - may be null.
  904. * @return File of an existing new temp dir, or null if unable to create
  905. */
  906. public static File getTempDir(String name) {
  907. if (null == name) {
  908. name = "FileUtil_getTempDir";
  909. } else if (!isValidFileName(name)) {
  910. throw new IllegalArgumentException(" invalid: " + name);
  911. }
  912. File result = null;
  913. File tempFile = null;
  914. try {
  915. tempFile = File.createTempFile("ignoreMe", ".txt");
  916. File tempParent = tempFile.getParentFile();
  917. result = makeNewChildDir(tempParent, name);
  918. } catch (IOException t) {
  919. result = makeNewChildDir(new File("."), name);
  920. } finally {
  921. if (null != tempFile) {
  922. tempFile.delete();
  923. }
  924. }
  925. return result;
  926. }
  927. public static URL[] getFileURLs(File[] files) {
  928. if ((null == files) || (0 == files.length)) {
  929. return new URL[0];
  930. }
  931. URL[] result = new URL[files.length]; // XXX dangerous non-copy...
  932. for (int i = 0; i < result.length; i++) {
  933. result[i] = getFileURL(files[i]);
  934. }
  935. return result;
  936. }
  937. /**
  938. * Get URL for a File. This appends "/" for directories. prints errors to System.err
  939. *
  940. * @param file the File to convert to URL (not null)
  941. */
  942. @SuppressWarnings("deprecation")
  943. public static URL getFileURL(File file) {
  944. LangUtil.throwIaxIfNull(file, "file");
  945. URL result = null;
  946. try {
  947. result = file.toURL();// TODO AV - was toURI.toURL that does not
  948. // works on Java 1.3
  949. if (null != result) {
  950. return result;
  951. }
  952. String url = "file:" + file.getAbsolutePath().replace('\\', '/');
  953. result = new URL(url + (file.isDirectory() ? "/" : ""));
  954. } catch (MalformedURLException e) {
  955. String m = "Util.makeURL(\"" + file.getPath() + "\" MUE " + e.getMessage();
  956. System.err.println(m);
  957. }
  958. return result;
  959. }
  960. /**
  961. * Write contents to file, returning null on success or error message otherwise. This tries to make any necessary parent
  962. * directories first.
  963. *
  964. * @param file the File to write (not null)
  965. * @param contents the String to write (use "" if null)
  966. * @return String null on no error, error otherwise
  967. */
  968. public static String writeAsString(File file, String contents) {
  969. LangUtil.throwIaxIfNull(file, "file");
  970. if (null == contents) {
  971. contents = "";
  972. }
  973. Writer out = null;
  974. try {
  975. File parentDir = file.getParentFile();
  976. if (!parentDir.exists() && !parentDir.mkdirs()) {
  977. return "unable to make parent dir for " + file;
  978. }
  979. Reader in = new StringReader(contents);
  980. out = new FileWriter(file);
  981. FileUtil.copyStream(in, out);
  982. return null;
  983. } catch (IOException e) {
  984. return LangUtil.unqualifiedClassName(e) + " writing " + file + ": " + e.getMessage();
  985. } finally {
  986. if (null != out) {
  987. try {
  988. out.close();
  989. } catch (IOException e) {
  990. } // ignored
  991. }
  992. }
  993. }
  994. /**
  995. * Reads a boolean array with our encoding
  996. */
  997. public static boolean[] readBooleanArray(DataInputStream s) throws IOException {
  998. int len = s.readInt();
  999. boolean[] ret = new boolean[len];
  1000. for (int i = 0; i < len; i++) {
  1001. ret[i] = s.readBoolean();
  1002. }
  1003. return ret;
  1004. }
  1005. /**
  1006. * Writes a boolean array with our encoding
  1007. */
  1008. public static void writeBooleanArray(boolean[] a, DataOutputStream s) throws IOException {
  1009. int len = a.length;
  1010. s.writeInt(len);
  1011. for (boolean b : a) {
  1012. s.writeBoolean(b);
  1013. }
  1014. }
  1015. /**
  1016. * Reads an int array with our encoding
  1017. */
  1018. public static int[] readIntArray(DataInputStream s) throws IOException {
  1019. int len = s.readInt();
  1020. int[] ret = new int[len];
  1021. for (int i = 0; i < len; i++) {
  1022. ret[i] = s.readInt();
  1023. }
  1024. return ret;
  1025. }
  1026. /**
  1027. * Writes an int array with our encoding
  1028. */
  1029. public static void writeIntArray(int[] a, DataOutputStream s) throws IOException {
  1030. int len = a.length;
  1031. s.writeInt(len);
  1032. for (int j : a) {
  1033. s.writeInt(j);
  1034. }
  1035. }
  1036. /**
  1037. * Reads an int array with our encoding
  1038. */
  1039. public static String[] readStringArray(DataInputStream s) throws IOException {
  1040. int len = s.readInt();
  1041. String[] ret = new String[len];
  1042. for (int i = 0; i < len; i++) {
  1043. ret[i] = s.readUTF();
  1044. }
  1045. return ret;
  1046. }
  1047. /**
  1048. * Writes an int array with our encoding
  1049. */
  1050. public static void writeStringArray(String[] a, DataOutputStream s) throws IOException {
  1051. if (a == null) {
  1052. s.writeInt(0);
  1053. return;
  1054. }
  1055. int len = a.length;
  1056. s.writeInt(len);
  1057. for (String value : a) {
  1058. s.writeUTF(value);
  1059. }
  1060. }
  1061. /**
  1062. * Returns the contents of this file as a String
  1063. */
  1064. public static String readAsString(File file) throws IOException {
  1065. BufferedReader r = new BufferedReader(new FileReader(file));
  1066. StringBuffer b = new StringBuffer();
  1067. while (true) {
  1068. int ch = r.read();
  1069. if (ch == -1) {
  1070. break;
  1071. }
  1072. b.append((char) ch);
  1073. }
  1074. r.close();
  1075. return b.toString();
  1076. }
  1077. // /**
  1078. // * Returns the contents of this stream as a String
  1079. // */
  1080. // public static String readAsString(InputStream in) throws IOException {
  1081. // BufferedReader r = new BufferedReader(new InputStreamReader(in));
  1082. // StringBuffer b = new StringBuffer();
  1083. // while (true) {
  1084. // int ch = r.read();
  1085. // if (ch == -1)
  1086. // break;
  1087. // b.append((char) ch);
  1088. // }
  1089. // in.close();
  1090. // r.close();
  1091. // return b.toString();
  1092. // }
  1093. /**
  1094. * Returns the contents of this file as a byte[]
  1095. */
  1096. public static byte[] readAsByteArray(File file) throws IOException {
  1097. FileInputStream in = new FileInputStream(file);
  1098. byte[] ret = FileUtil.readAsByteArray(in);
  1099. in.close();
  1100. return ret;
  1101. }
  1102. /**
  1103. * Reads this input stream and returns contents as a byte[]
  1104. */
  1105. public static byte[] readAsByteArray(InputStream inStream) throws IOException {
  1106. int size = 1024;
  1107. byte[] ba = new byte[size];
  1108. int readSoFar = 0;
  1109. while (true) {
  1110. int nRead = inStream.read(ba, readSoFar, size - readSoFar);
  1111. if (nRead == -1) {
  1112. break;
  1113. }
  1114. readSoFar += nRead;
  1115. if (readSoFar == size) {
  1116. int newSize = size * 2;
  1117. byte[] newBa = new byte[newSize];
  1118. System.arraycopy(ba, 0, newBa, 0, size);
  1119. ba = newBa;
  1120. size = newSize;
  1121. }
  1122. }
  1123. byte[] newBa = new byte[readSoFar];
  1124. System.arraycopy(ba, 0, newBa, 0, readSoFar);
  1125. return newBa;
  1126. }
  1127. final static String FILECHARS = "abcdefghijklmnopqrstuvxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
  1128. /** @return semi-random String of length 6 usable as filename suffix */
  1129. static String randomFileString() {
  1130. final double FILECHARS_length = FILECHARS.length();
  1131. final int LEN = 6;
  1132. final char[] result = new char[LEN];
  1133. int index = (int) (Math.random() * 6d);
  1134. for (int i = 0; i < LEN; i++) {
  1135. if (index >= LEN) {
  1136. index = 0;
  1137. }
  1138. result[index++] = FILECHARS.charAt((int) (Math.random() * FILECHARS_length));
  1139. }
  1140. return new String(result);
  1141. }
  1142. public static InputStream getStreamFromZip(String zipFile, String name) {
  1143. try {
  1144. ZipFile zf = new ZipFile(zipFile);
  1145. try {
  1146. ZipEntry entry = zf.getEntry(name);
  1147. return zf.getInputStream(entry);
  1148. } finally {
  1149. // ??? is it safe not to close this zf.close();
  1150. }
  1151. } catch (IOException ioe) {
  1152. return null;
  1153. }
  1154. }
  1155. //
  1156. // public static void extractJar(String zipFile, String outDir) throws
  1157. // IOException {
  1158. // ZipInputStream zs = new ZipInputStream(new FileInputStream(zipFile));
  1159. // ZipEntry entry;
  1160. // while ((entry = zs.getNextEntry()) != null) {
  1161. // if (entry.isDirectory())
  1162. // continue;
  1163. // byte[] in = readAsByteArray(zs);
  1164. //
  1165. // File outFile = new File(outDir + "/" + entry.getName());
  1166. // // if (!outFile.getParentFile().exists())
  1167. // // System.err.println("parent: " + outFile.getParentFile());
  1168. // // System.err.println("parent: " + outFile.getParentFile());
  1169. // outFile.getParentFile().mkdirs();
  1170. // FileOutputStream os = new FileOutputStream(outFile);
  1171. // os.write(in);
  1172. // os.close();
  1173. // zs.closeEntry();
  1174. // }
  1175. // zs.close();
  1176. // }
  1177. /**
  1178. * Do line-based search for literal text in source files, returning file:line where found.
  1179. *
  1180. * @param sought the String text to seek in the file
  1181. * @param sources the List of String paths to the source files
  1182. * @param listAll if false, only list first match in file
  1183. * @param errorSink the PrintStream to print any errors to (one per line) (use null to silently ignore errors)
  1184. * @return List of String of the form file:line for each found entry (never null, might be empty)
  1185. */
  1186. // OPTIMIZE only used by tests? move it out
  1187. public static List<String> lineSeek(String sought, List<String> sources, boolean listAll, PrintStream errorSink) {
  1188. if (LangUtil.isEmpty(sought) || LangUtil.isEmpty(sources)) {
  1189. return Collections.emptyList();
  1190. }
  1191. ArrayList<String> result = new ArrayList<>();
  1192. for (String path : sources) {
  1193. String error = lineSeek(sought, path, listAll, result);
  1194. if ((null != error) && (null != errorSink)) {
  1195. errorSink.println(error);
  1196. }
  1197. }
  1198. return result;
  1199. }
  1200. /**
  1201. * Do line-based search for literal text in source file, returning line where found as a String in the form
  1202. * {sourcePath}:line:column submitted to the collecting parameter sink. Any error is rendered to String and returned as the
  1203. * result.
  1204. *
  1205. * @param sought the String text to seek in the file
  1206. * @param sourcePath the String of paths to the source files
  1207. * @param listAll if false, only list first match in file
  1208. * @param sink the List of String entries of the form {sourcePath}:line:column
  1209. * @return String error if any, or add String entries to sink
  1210. */
  1211. public static String lineSeek(String sought, String sourcePath, boolean listAll, List<String> sink) {
  1212. if (LangUtil.isEmpty(sought) || LangUtil.isEmpty(sourcePath)) {
  1213. return "nothing sought";
  1214. }
  1215. if (LangUtil.isEmpty(sourcePath)) {
  1216. return "no sourcePath";
  1217. }
  1218. final File file = new File(sourcePath);
  1219. if (!file.canRead() || !file.isFile()) {
  1220. return "sourcePath not a readable file";
  1221. }
  1222. int lineNum = 0;
  1223. FileReader fin = null;
  1224. try {
  1225. fin = new FileReader(file);
  1226. BufferedReader reader = new BufferedReader(fin);
  1227. String line;
  1228. while (null != (line = reader.readLine())) {
  1229. lineNum++;
  1230. int loc = line.indexOf(sought);
  1231. if (-1 != loc) {
  1232. sink.add(sourcePath + ":" + lineNum + ":" + loc);
  1233. if (!listAll) {
  1234. break;
  1235. }
  1236. }
  1237. }
  1238. } catch (IOException e) {
  1239. return LangUtil.unqualifiedClassName(e) + " reading " + sourcePath + ":" + lineNum;
  1240. } finally {
  1241. try {
  1242. if (null != fin) {
  1243. fin.close();
  1244. }
  1245. } catch (IOException e) {
  1246. } // ignore
  1247. }
  1248. return null;
  1249. }
  1250. public static BufferedOutputStream makeOutputStream(File file) throws FileNotFoundException {
  1251. File parent = file.getParentFile();
  1252. if (parent != null) {
  1253. parent.mkdirs();
  1254. }
  1255. return new BufferedOutputStream(new FileOutputStream(file));
  1256. }
  1257. /**
  1258. * Sleep until after the last last-modified stamp from the files.
  1259. *
  1260. * @param files the File[] of files to inspect for last modified times (this ignores null or empty files array and null or
  1261. * non-existing components of files array)
  1262. * @return true if succeeded without 100 interrupts
  1263. */
  1264. public static boolean sleepPastFinalModifiedTime(File[] files) {
  1265. if ((null == files) || (0 == files.length)) {
  1266. return true;
  1267. }
  1268. long delayUntil = System.currentTimeMillis();
  1269. for (File file : files) {
  1270. if ((null == file) || !file.exists()) {
  1271. continue;
  1272. }
  1273. long nextModTime = file.lastModified();
  1274. if (nextModTime > delayUntil) {
  1275. delayUntil = nextModTime;
  1276. }
  1277. }
  1278. return LangUtil.sleepUntil(++delayUntil);
  1279. }
  1280. private static void listClassFiles(final File baseDir, ArrayList<File> result) {
  1281. File[] files = baseDir.listFiles();
  1282. for (File f : files) {
  1283. if (f.isDirectory()) {
  1284. listClassFiles(f, result);
  1285. }
  1286. else {
  1287. if (f.getName().endsWith(".class")) {
  1288. result.add(f);
  1289. }
  1290. }
  1291. }
  1292. }
  1293. private static void listFiles(final File baseDir, ArrayList<File> result, FileFilter filter) {
  1294. File[] files = baseDir.listFiles();
  1295. // hack https://bugs.eclipse.org/bugs/show_bug.cgi?id=48650
  1296. final boolean skipCVS = (!PERMIT_CVS && (filter == aspectjSourceFileFilter));
  1297. for (File f : files) {
  1298. if (f.isDirectory()) {
  1299. if (skipCVS) {
  1300. String name = f.getName().toLowerCase();
  1301. if ("cvs".equals(name) || "sccs".equals(name)) {
  1302. continue;
  1303. }
  1304. }
  1305. listFiles(f, result, filter);
  1306. }
  1307. else {
  1308. if (filter.accept(f)) {
  1309. result.add(f);
  1310. }
  1311. }
  1312. }
  1313. }
  1314. /** @return true if input is not null and contains no path separator */
  1315. private static boolean isValidFileName(String input) {
  1316. return ((null != input) && (!input.contains(File.pathSeparator)));
  1317. }
  1318. private static void listFiles(final File baseDir, String dir, ArrayList<String> result) {
  1319. final String dirPrefix = (null == dir ? "" : dir + "/");
  1320. final File dirFile = (null == dir ? baseDir : new File(baseDir.getPath() + "/" + dir));
  1321. final String[] files = dirFile.list();
  1322. for (String file : files) {
  1323. File f = new File(dirFile, file);
  1324. String path = dirPrefix + file;
  1325. if (f.isDirectory()) {
  1326. listFiles(baseDir, path, result);
  1327. }
  1328. else {
  1329. result.add(path);
  1330. }
  1331. }
  1332. }
  1333. private FileUtil() {
  1334. }
  1335. public static List<String> makeClasspath(URL[] urls) {
  1336. List<String> ret = new LinkedList<>();
  1337. if (urls != null) {
  1338. for (URL url : urls) {
  1339. ret.add(toPathString(url));
  1340. }
  1341. }
  1342. return ret;
  1343. }
  1344. private static String toPathString(URL url) {
  1345. try {
  1346. return url.toURI().getPath();
  1347. } catch (URISyntaxException e) {
  1348. System.err.println("Warning!! Malformed URL may cause problems: "+url); // TODO: Better way to report this?
  1349. // In this case it was likely not using properly escaped
  1350. // characters so we just use the 'bad' method that doesn't decode
  1351. // special chars
  1352. return url.getPath();
  1353. }
  1354. }
  1355. /**
  1356. * A pipe when run reads from an input stream to an output stream, optionally sleeping between reads.
  1357. *
  1358. * @see #copyStream(InputStream, OutputStream)
  1359. */
  1360. public static class Pipe implements Runnable {
  1361. private final InputStream in;
  1362. private final OutputStream out;
  1363. private final long sleep;
  1364. private ByteArrayOutputStream snoop;
  1365. private long totalWritten;
  1366. private Throwable thrown;
  1367. private boolean halt;
  1368. /**
  1369. * Seem to be unable to detect erroroneous closing of System.out...
  1370. */
  1371. private final boolean closeInput;
  1372. private final boolean closeOutput;
  1373. /**
  1374. * If true, then continue processing stream until no characters are returned when halting.
  1375. */
  1376. private boolean finishStream;
  1377. private boolean done; // true after completing() completes
  1378. /**
  1379. * alias for <code>Pipe(in, out, 100l, false, false)</code>
  1380. *
  1381. * @param in the InputStream source to read
  1382. * @param out the OutputStream sink to write
  1383. */
  1384. Pipe(InputStream in, OutputStream out) {
  1385. this(in, out, 100l, false, false);
  1386. }
  1387. /**
  1388. * @param in the InputStream source to read
  1389. * @param out the OutputStream sink to write
  1390. * @param tryClosingStreams if true, then try closing both streams when done
  1391. * @param sleep milliseconds to delay between reads (pinned to 0..1 minute)
  1392. */
  1393. Pipe(InputStream in, OutputStream out, long sleep, boolean closeInput, boolean closeOutput) {
  1394. LangUtil.throwIaxIfNull(in, "in");
  1395. LangUtil.throwIaxIfNull(out, "out");
  1396. this.in = in;
  1397. this.out = out;
  1398. this.closeInput = closeInput;
  1399. this.closeOutput = closeOutput;
  1400. this.sleep = Math.min(0l, Math.max(60l * 1000l, sleep));
  1401. }
  1402. public void setSnoop(ByteArrayOutputStream snoop) {
  1403. this.snoop = snoop;
  1404. }
  1405. /**
  1406. * Run the pipe. This halts on the first Throwable thrown or when a read returns -1 (for end-of-file) or on demand.
  1407. */
  1408. public void run() {
  1409. totalWritten = 0;
  1410. if (halt) {
  1411. return;
  1412. }
  1413. try {
  1414. final int MAX = 4096;
  1415. byte[] buf = new byte[MAX];
  1416. // TODO this blocks, hanging the harness
  1417. int count = in.read(buf, 0, MAX);
  1418. ByteArrayOutputStream mySnoop;
  1419. while ((halt && finishStream && (0 < count)) || (!halt && (-1 != count))) {
  1420. out.write(buf, 0, count);
  1421. mySnoop = snoop;
  1422. if (null != mySnoop) {
  1423. mySnoop.write(buf, 0, count);
  1424. }
  1425. totalWritten += count;
  1426. if (halt && !finishStream) {
  1427. break;
  1428. }
  1429. if (!halt && (0 < sleep)) {
  1430. Thread.sleep(sleep);
  1431. }
  1432. if (halt && !finishStream) {
  1433. break;
  1434. }
  1435. count = in.read(buf, 0, MAX);
  1436. }
  1437. } catch (Throwable e) {
  1438. thrown = e;
  1439. } finally {
  1440. halt = true;
  1441. if (closeInput) {
  1442. try {
  1443. in.close();
  1444. } catch (IOException e) {
  1445. // ignore
  1446. }
  1447. }
  1448. if (closeOutput) {
  1449. try {
  1450. out.close();
  1451. } catch (IOException e) {
  1452. // ignore
  1453. }
  1454. }
  1455. done = true;
  1456. completing(totalWritten, thrown);
  1457. }
  1458. }
  1459. /**
  1460. * Tell the pipe to halt the next time it gains control.
  1461. *
  1462. * @param wait if true, this waits synchronously until pipe is done
  1463. * @param finishStream if true, then continue until a read from the input stream returns no bytes, then halt.
  1464. * @return true if <code>run()</code> will return the next time it gains control
  1465. */
  1466. public boolean halt(boolean wait, boolean finishStream) {
  1467. if (!halt) {
  1468. halt = true;
  1469. }
  1470. if (wait) {
  1471. while (!done) {
  1472. synchronized (this) {
  1473. notifyAll();
  1474. }
  1475. if (!done) {
  1476. try {
  1477. Thread.sleep(5l);
  1478. } catch (InterruptedException e) {
  1479. break;
  1480. }
  1481. }
  1482. }
  1483. }
  1484. return halt;
  1485. }
  1486. /** @return the total number of bytes written */
  1487. public long totalWritten() {
  1488. return totalWritten;
  1489. }
  1490. /** @return any exception thrown when reading/writing */
  1491. public Throwable getThrown() {
  1492. return thrown;
  1493. }
  1494. /**
  1495. * This is called when the pipe is completing. This implementation does nothing. Subclasses implement this to get notice.
  1496. * Note that halt(true, true) might or might not have completed before this method is called.
  1497. */
  1498. protected void completing(long totalWritten, Throwable thrown) {
  1499. }
  1500. }
  1501. }