/src/kilim/analysis/FileLister.java

http://github.com/kilim/kilim · Java · 242 lines · 170 code · 35 blank · 37 comment · 19 complexity · b3b5c21ef2954d01f8fa6f451011b041 MD5 · raw file

  1. /* Copyright (c) 2006, Sriram Srinivasan, nqzero 2016
  2. *
  3. * You may distribute this software under the terms of the license
  4. * specified in the file "License"
  5. */
  6. package kilim.analysis;
  7. import java.io.BufferedInputStream;
  8. import java.io.File;
  9. import java.io.FileInputStream;
  10. import java.io.IOException;
  11. import java.io.InputStream;
  12. import java.lang.ref.WeakReference;
  13. import java.nio.file.Paths;
  14. import java.util.Enumeration;
  15. import java.util.Iterator;
  16. import java.util.Stack;
  17. import java.util.jar.JarEntry;
  18. import java.util.jar.JarFile;
  19. /**
  20. * Utility class to present a uniform iterator interface for file containers; presently
  21. * includes directories and jar files.
  22. */
  23. public class FileLister implements Iterable<FileLister.Entry> {
  24. public static abstract class Entry {
  25. public abstract String getFileName();
  26. public abstract long getSize();
  27. public abstract InputStream getInputStream() throws IOException;
  28. /**
  29. * check if a newer version of the file exists in an output directory. false negatives are allowed
  30. * @param outdir the output directory
  31. * @return true if the newer version exists
  32. */
  33. public boolean check(String outdir) { return false; }
  34. };
  35. /**
  36. * weak ref to a container to avoid hanging on to an open jar file.
  37. */
  38. volatile WeakReference<FileContainer> containerRef;
  39. String name;
  40. public FileLister(String dirOrJarName) throws IOException {
  41. name= dirOrJarName;
  42. }
  43. /**
  44. * @param relativeFileName
  45. * @return if the relativeFileName exists in the directory or jar represented by FileLister object
  46. * open it. If not return null.
  47. * @throws IOException
  48. */
  49. public Entry open(String relativeFileName) throws IOException {
  50. return getContainer().open(relativeFileName);
  51. }
  52. // Lazily initialize the container.
  53. private FileContainer getContainer() throws IOException {
  54. FileContainer container = null;
  55. if (containerRef != null) {
  56. container = containerRef.get();
  57. if (container != null) return container;
  58. }
  59. if (name.endsWith(".jar")) {
  60. container = openJar(this.name);
  61. } else {
  62. File f = new File(this.name);
  63. if (f.exists() && f.isDirectory()) {
  64. container = new DirIterator(f);
  65. } else {
  66. throw new IOException("Expected jar file or directory name");
  67. }
  68. }
  69. containerRef = new WeakReference<FileContainer>(container);
  70. return container;
  71. }
  72. private FileContainer openJar(String jarFile) throws IOException {
  73. return new JarIterator(new JarFile(jarFile));
  74. }
  75. public Iterator<FileLister.Entry> iterator() {
  76. try {
  77. return getContainer();
  78. } catch (IOException ignore) {}
  79. return null;
  80. }
  81. /**
  82. * check if dst is up to date with src, ie at least as new
  83. * @param src the source filename
  84. * @param dst the destination filename
  85. * @return true if dst exists and is at least as new as src
  86. */
  87. public static boolean check(String src,String dst) {
  88. File infile = new File(src), outfile = new File(dst);
  89. long dtime = outfile.lastModified();
  90. return dtime > infile.lastModified();
  91. }
  92. }
  93. abstract class FileContainer implements Iterator<FileLister.Entry> {
  94. abstract FileLister.Entry open(String relativeFileName) throws IOException;
  95. }
  96. /**
  97. * Preorder traversal of a directory. Returns everything including directory
  98. * names.
  99. */
  100. class DirIterator extends FileContainer {
  101. final File root;
  102. String rootpath;
  103. private class DirEntry extends FileLister.Entry {
  104. final File file;
  105. DirEntry(File f) {file = f;}
  106. @Override
  107. public long getSize() {
  108. return file.length();
  109. }
  110. @Override
  111. public String getFileName() {
  112. return file.getPath();
  113. }
  114. @Override
  115. public InputStream getInputStream() throws IOException {
  116. return new BufferedInputStream(new FileInputStream(file));
  117. }
  118. public boolean check(String outdir) {
  119. String name = getFileName();
  120. if (rootpath==null || ! name.startsWith(rootpath)) return false;
  121. String relative = name.substring(rootpath.length());
  122. File outfile = Paths.get(outdir,relative).toFile();
  123. return outfile.lastModified() > file.lastModified();
  124. }
  125. }
  126. Stack<File> stack = new Stack<File>();
  127. DirIterator(File f) {
  128. root = f;
  129. rootpath = root.getPath();
  130. stack.push(f);
  131. }
  132. public boolean hasNext() {
  133. return !stack.isEmpty();
  134. }
  135. public FileLister.Entry next() {
  136. File ret = stack.pop();
  137. if (ret.isDirectory()) {
  138. // prepare for next round
  139. File[] files = ret.listFiles();
  140. // first add all directories to stack, then the files, so that
  141. // all files in a directory are processed continuously
  142. for (int i = files.length - 1; i >= 0; i--) {
  143. File ff = files[i];
  144. if (ff.isDirectory()) {
  145. stack.push(ff);
  146. }
  147. }
  148. for (int i = files.length - 1; i >= 0; i--) {
  149. File ff = files[i];
  150. if (!ff.isDirectory()) {
  151. stack.push(ff);
  152. }
  153. }
  154. }
  155. return new DirEntry(ret);
  156. }
  157. public void remove() {
  158. throw new RuntimeException("FileLister does not remove files");
  159. }
  160. @Override
  161. FileLister.Entry open(String fileName) throws IOException {
  162. File ret = new File(root.getAbsolutePath() + File.separatorChar + fileName);
  163. if (ret.exists() && ret.isFile()) {
  164. return new DirEntry(ret);
  165. }
  166. return null;
  167. }
  168. }
  169. class JarIterator extends FileContainer {
  170. Enumeration<JarEntry> jarEnum;
  171. JarFile jarFile;
  172. String nextName;
  173. private class JEntry extends FileLister.Entry {
  174. private final JarEntry jarEntry;
  175. JEntry(JarEntry j) {jarEntry = j;}
  176. @Override
  177. public String getFileName() {
  178. return jarEntry.getName();
  179. }
  180. @Override
  181. public InputStream getInputStream() throws IOException {
  182. return jarFile.getInputStream(jarEntry);
  183. }
  184. @Override
  185. public long getSize() {
  186. return jarEntry.getSize();
  187. }
  188. }
  189. JarIterator(JarFile f) {
  190. jarFile = f;
  191. jarEnum = f.entries();
  192. }
  193. public boolean hasNext() {
  194. return jarEnum.hasMoreElements();
  195. }
  196. public FileLister.Entry next() {
  197. return new JEntry(jarEnum.nextElement());
  198. }
  199. public void remove() {
  200. throw new RuntimeException("FileLister does not remove files");
  201. }
  202. @Override
  203. FileLister.Entry open(String relativeFileName) throws IOException {
  204. JarEntry e = jarFile.getJarEntry(relativeFileName);
  205. return e == null ? null : new JEntry(e);
  206. }
  207. }