PageRenderTime 43ms CodeModel.GetById 11ms app.highlight 28ms RepoModel.GetById 1ms app.codeStats 0ms

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