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

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