EMMA Coverage Report (generated Thu Dec 06 15:52:10 GMT 2007)
[all classes][com.sun.tools.javac.zip]

COVERAGE SUMMARY FOR SOURCE FILE [ZipFileIndex.java]

nameclass, %method, %block, %line, %
ZipFileIndex.java100% (3/3)52%  (38/73)44%  (1170/2634)41%  (240.3/591)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class ZipFileIndex100% (1/1)49%  (27/55)28%  (484/1716)27%  (111/406)
access$1300 (ZipFileIndex): File 0%   (0/1)0%   (0/3)0%   (0/1)
access$800 (ZipFileIndex): void 0%   (0/1)0%   (0/3)0%   (0/1)
cleanupState (): void 0%   (0/1)0%   (0/13)0%   (0/5)
clearCache (): void 0%   (0/1)0%   (0/13)0%   (0/5)
clearCache (long): void 0%   (0/1)0%   (0/48)0%   (0/13)
close (): void 0%   (0/1)0%   (0/16)0%   (0/6)
contains (String): boolean 0%   (0/1)0%   (0/28)0%   (0/6)
finalize (): void 0%   (0/1)0%   (0/3)0%   (0/2)
getAllDirectories (): Set 0%   (0/1)0%   (0/52)0%   (0/12)
getAllDirectories (String): List 0%   (0/1)0%   (0/54)0%   (0/13)
getIndexFile (): File 0%   (0/1)0%   (0/34)0%   (0/5)
getLastModified (String): long 0%   (0/1)0%   (0/24)0%   (0/6)
getZipFileIndexes (): List 0%   (0/1)0%   (0/3)0%   (0/1)
getZipFileIndexes (boolean): List 0%   (0/1)0%   (0/41)0%   (0/10)
getZipFileLastModified (): long 0%   (0/1)0%   (0/16)0%   (0/4)
inflate (byte [], byte []): int 0%   (0/1)0%   (0/32)0%   (0/9)
isDirectory (String): boolean 0%   (0/1)0%   (0/42)0%   (0/9)
isOpen (): boolean 0%   (0/1)0%   (0/18)0%   (0/3)
length (String): int 0%   (0/1)0%   (0/48)0%   (0/11)
read (String): byte [] 0%   (0/1)0%   (0/33)0%   (0/6)
read (String, byte []): int 0%   (0/1)0%   (0/26)0%   (0/6)
read (ZipFileIndexEntry, byte []): int 0%   (0/1)0%   (0/18)0%   (0/4)
readBytes (ZipFileIndexEntry, byte []): int 0%   (0/1)0%   (0/87)0%   (0/20)
removeFromCache (File): void 0%   (0/1)0%   (0/15)0%   (0/5)
setOpenedIndexes (List): void 0%   (0/1)0%   (0/36)0%   (0/8)
toString (): String 0%   (0/1)0%   (0/12)0%   (0/1)
writeIndex (): boolean 0%   (0/1)0%   (0/270)0%   (0/70)
writeZipIndex (): boolean 0%   (0/1)0%   (0/14)0%   (0/3)
readIndex (): boolean 100% (1/1)6%   (8/136)6%   (2/34)
isUpToDate (): boolean 100% (1/1)63%  (12/19)84%  (2.5/3)
readBytes (ZipFileIndexEntry): byte [] 100% (1/1)63%  (33/52)58%  (7/12)
getFiles (String): List 100% (1/1)67%  (34/51)71%  (8.5/12)
getExistingZipIndex (File): ZipFileIndex 100% (1/1)69%  (11/16)89%  (2.7/3)
getHeader (ZipFileIndexEntry): byte [] 100% (1/1)72%  (26/36)75%  (6/8)
getZipIndexEntry (String): ZipFileIndexEntry 100% (1/1)74%  (53/72)74%  (11.1/15)
read (ZipFileIndexEntry): byte [] 100% (1/1)76%  (16/21)96%  (5.8/6)
checkIndex (): void 100% (1/1)83%  (58/70)87%  (18.3/21)
getZipFileIndex (File, int, boolean, String, boolean): ZipFileIndex 100% (1/1)88%  (36/41)93%  (8.4/9)
<static initializer> 100% (1/1)91%  (20/22)98%  (4.9/5)
closeFile (): void 100% (1/1)92%  (11/12)83%  (5/6)
ZipFileIndex (File, int, boolean, boolean, String): void 100% (1/1)100% (62/62)100% (22/22)
access$1000 (ZipFileIndex): boolean 100% (1/1)100% (3/3)100% (1/1)
access$1100 (): String 100% (1/1)100% (2/2)100% (1/1)
access$1200 (): String 100% (1/1)100% (2/2)100% (1/1)
access$400 (byte [], int): int 100% (1/1)100% (4/4)100% (1/1)
access$500 (byte [], int): int 100% (1/1)100% (4/4)100% (1/1)
access$600 (ZipFileIndex): ZipFileIndexEntry [] 100% (1/1)100% (3/3)100% (1/1)
access$602 (ZipFileIndex, ZipFileIndexEntry []): ZipFileIndexEntry [] 100% (1/1)100% (5/5)100% (1/1)
access$700 (ZipFileIndex): Map 100% (1/1)100% (3/3)100% (1/1)
access$702 (ZipFileIndex, Map): Map 100% (1/1)100% (5/5)100% (1/1)
access$900 (ZipFileIndex): int 100% (1/1)100% (3/3)100% (1/1)
get2ByteLittleEndian (byte [], int): int 100% (1/1)100% (16/16)100% (1/1)
get4ByteLittleEndian (byte [], int): int 100% (1/1)100% (36/36)100% (1/1)
getZipFile (): File 100% (1/1)100% (3/3)100% (1/1)
openFile (): void 100% (1/1)100% (15/15)100% (3/3)
     
class ZipFileIndex$DirectoryEntry100% (1/1)46%  (6/13)44%  (167/378)43%  (37.3/87)
access$1402 (ZipFileIndex$DirectoryEntry, int): int 0%   (0/1)0%   (0/5)0%   (0/1)
access$1502 (ZipFileIndex$DirectoryEntry, long): long 0%   (0/1)0%   (0/5)0%   (0/1)
access$1600 (ZipFileIndex$DirectoryEntry): String 0%   (0/1)0%   (0/3)0%   (0/1)
access$300 (ZipFileIndex$DirectoryEntry): List 0%   (0/1)0%   (0/3)0%   (0/1)
getDirectories (): List 0%   (0/1)0%   (0/36)0%   (0/8)
getEntries (): List 0%   (0/1)0%   (0/35)0%   (0/8)
getEntriesAsCollection (): List 0%   (0/1)0%   (0/5)0%   (0/2)
initEntries (): void 100% (1/1)35%  (59/171)28%  (11/40)
ZipFileIndex$DirectoryEntry (String, ZipFileIndex): void 100% (1/1)90%  (45/50)93%  (14/15)
getEntry (String): ZipFileIndexEntry 100% (1/1)91%  (20/22)80%  (4/5)
access$100 (ZipFileIndex$DirectoryEntry, String): ZipFileIndexEntry 100% (1/1)100% (4/4)100% (1/1)
access$200 (ZipFileIndex$DirectoryEntry): List 100% (1/1)100% (3/3)100% (1/1)
getFiles (): List 100% (1/1)100% (36/36)100% (8/8)
     
class ZipFileIndex$ZipDirectory100% (1/1)100% (5/5)96%  (519/540)94%  (92/98)
findCENRecord (long, long): void 100% (1/1)92%  (142/155)83%  (19/23)
buildIndex (): void 100% (1/1)96%  (81/84)95%  (18/19)
readEntry (int, List, Map): int 100% (1/1)98%  (271/276)98%  (47/48)
ZipFileIndex$ZipDirectory (ZipFileIndex, RandomAccessFile, long, long, ZipFil... 100% (1/1)100% (22/22)100% (7/7)
access$000 (ZipFileIndex$ZipDirectory): void 100% (1/1)100% (3/3)100% (1/1)

1package com.sun.tools.javac.zip;
2 
3import java.io.*;
4import java.text.MessageFormat;
5import java.util.*;
6import java.util.List;
7import java.util.concurrent.locks.ReentrantLock;
8import java.util.zip.*;
9 
10/** This class implements building of index of a zip archive and access to it's context.
11 *  It also uses prebuild index if available. It supports invocations where it will
12 *  serialize an optimized zip index file to disk.
13 *
14 *  In oreder to use secondary index file make sure the option "usezipindex" is in the Options object,
15 *  when JavacFileManager is invoked. (You can pass "-XDusezipindex" on the command line.
16 *
17 *  Location where to look for/generate optimized zip index files can be provided using
18 *  "-XDcachezipindexdir=<directory>". If this flag is not provided, the dfault location is
19 *  the value of the "java.io.tmpdir" system property.
20 *
21 *  If key "-XDwritezipindexfiles" is specified, there will be new optimized index file
22 *  created for each archive, used by the compiler for compilation, at location,
23 *  specified by "cachezipindexdir" option.
24 *
25 * If nonBatchMode option is specified (-XDnonBatchMode) the compiler will use timestamp
26 * checking to reindex the zip files if it is needed. In batch mode the timestamps are not checked
27 * and the compiler uses the cached indexes.
28 */
29public class ZipFileIndex {
30    private static final String MIN_CHAR = String.valueOf(Character.MIN_VALUE);
31    private static final String MAX_CHAR = String.valueOf(Character.MAX_VALUE);
32 
33    public final static long NOT_MODIFIED = Long.MIN_VALUE;
34 
35    private static Map<File, ZipFileIndex> zipFileIndexCache = new HashMap<File, ZipFileIndex>();
36    private static ReentrantLock lock = new ReentrantLock();
37 
38    private static boolean NON_BATCH_MODE = System.getProperty("nonBatchMode") != null;// TODO: Use -XD compiler switch for this.
39 
40    private Map<String, DirectoryEntry> directories = Collections.<String, DirectoryEntry>emptyMap();
41    private Set<String> allDirs = Collections.<String>emptySet();
42 
43    // ZipFileIndex data entries
44    private File zipFile;
45    private long zipFileLastModified = NOT_MODIFIED;
46    private RandomAccessFile zipRandomFile;
47    private ZipFileIndexEntry[] entries;
48 
49    private boolean readFromIndex = false;
50    private File zipIndexFile = null;
51    private boolean triedToReadIndex = false;
52    private int symbolFilePrefixLength = 0;
53    private boolean hasPopulatedData = false;
54    private long lastReferenceTimeStamp = NOT_MODIFIED;
55 
56    private boolean usePreindexedCache = false;
57    private String preindexedCacheLocation = null;
58 
59    private boolean writeIndex = false;
60 
61    /**
62     * Returns a list of all ZipFileIndex entries
63     *
64     * @return A list of ZipFileIndex entries, or an empty list
65     */
66    public static List<ZipFileIndex> getZipFileIndexes() {
67        return getZipFileIndexes(false);
68    }
69 
70    /**
71     * Returns a list of all ZipFileIndex entries
72     *
73     * @param openedOnly If true it returns a list of only opened ZipFileIndex entries, otherwise
74     *                   all ZipFileEntry(s) are included into the list.
75     * @return A list of ZipFileIndex entries, or an empty list
76     */
77    public static List<ZipFileIndex> getZipFileIndexes(boolean openedOnly) {
78        List<ZipFileIndex> zipFileIndexes = new ArrayList<ZipFileIndex>();
79        lock.lock();
80        try {
81            zipFileIndexes.addAll(zipFileIndexCache.values());
82 
83            if (openedOnly) {
84                for(ZipFileIndex elem : zipFileIndexes) {
85                    if (!elem.isOpen()) {
86                        zipFileIndexes.remove(elem);
87                    }
88                }
89            }
90        }
91        finally {
92            lock.unlock();
93        }
94        return zipFileIndexes;
95    }
96 
97    public boolean isOpen() {
98        lock.lock();
99        try {
100            return zipRandomFile != null;
101        }
102        finally {
103            lock.unlock();
104        }
105    }
106 
107    public static ZipFileIndex getZipFileIndex(File zipFile, int symbolFilePrefixLen, boolean useCache, String cacheLocation, boolean writeIndex) throws IOException {
108        ZipFileIndex zi = null;
109        lock.lock();
110        try {
111            zi = getExistingZipIndex(zipFile);
112 
113            if (zi == null || (zi != null && zipFile.lastModified() != zi.zipFileLastModified)) {
114                zi = new ZipFileIndex(zipFile, symbolFilePrefixLen, writeIndex,
115                        useCache, cacheLocation);
116                zipFileIndexCache.put(zipFile, zi);
117            }
118        }
119        finally {
120            lock.unlock();
121        }
122        return zi;
123    }
124 
125    public static ZipFileIndex getExistingZipIndex(File zipFile) {
126        lock.lock();
127        try {
128            return zipFileIndexCache.get(zipFile);
129        }
130        finally {
131            lock.unlock();
132        }
133    }
134 
135    public static void clearCache() {
136        lock.lock();
137        try {
138            zipFileIndexCache.clear();
139        }
140        finally {
141            lock.unlock();
142        }
143    }
144 
145    public static void clearCache(long timeNotUsed) {
146        lock.lock();
147        try {
148            Iterator<File> cachedFileIterator = zipFileIndexCache.keySet().iterator();
149            while (cachedFileIterator.hasNext()) {
150                File cachedFile = cachedFileIterator.next();
151                ZipFileIndex cachedZipIndex = zipFileIndexCache.get(cachedFile);
152                if (cachedZipIndex != null) {
153                    long timeToTest = cachedZipIndex.lastReferenceTimeStamp + timeNotUsed;
154                    if (timeToTest < cachedZipIndex.lastReferenceTimeStamp || // Overflow...
155                            System.currentTimeMillis() > timeToTest) {
156                        zipFileIndexCache.remove(cachedFile);
157                    }
158                }
159            }
160        }
161        finally {
162            lock.unlock();
163        }
164    }
165 
166    public static void removeFromCache(File file) {
167        lock.lock();
168        try {
169            zipFileIndexCache.remove(file);
170        }
171        finally {
172            lock.unlock();
173        }
174    }
175 
176    /** Sets already opened list of ZipFileIndexes from an outside client
177      * of the compiler. This functionality should be used in a non-batch clients of the compiler.
178      */
179    public static void setOpenedIndexes(List<ZipFileIndex>indexes) throws IllegalStateException {
180        lock.lock();
181        try {
182            if (zipFileIndexCache.isEmpty()) {
183                throw new IllegalStateException("Setting opened indexes should be called only when the ZipFileCache is empty. Call JavacFileManager.flush() before calling this method.");
184            }
185 
186            for (ZipFileIndex zfi : indexes) {
187                zipFileIndexCache.put(zfi.zipFile, zfi);
188            }
189        }
190        finally {
191            lock.unlock();
192        }
193    }
194 
195    private ZipFileIndex(File zipFile, int symbolFilePrefixLen, boolean writeIndex,
196            boolean useCache, String cacheLocation) throws IOException {
197        this.zipFile = zipFile;
198        this.symbolFilePrefixLength = symbolFilePrefixLen;
199        this.writeIndex = writeIndex;
200        this.usePreindexedCache = useCache;
201        this.preindexedCacheLocation = cacheLocation;
202 
203        if (zipFile != null) {
204            this.zipFileLastModified = zipFile.lastModified();
205        }
206 
207        // Validate integrity of the zip file
208        checkIndex();
209    }
210 
211    public String toString() {
212        return "ZipFileIndex of file:(" + zipFile + ")";
213    }
214 
215    // Just in case...
216    protected void finalize() {
217        closeFile();
218    }
219 
220    private boolean isUpToDate() {
221        if (zipFile != null &&
222                ((!NON_BATCH_MODE) || zipFileLastModified == zipFile.lastModified()) &&
223                hasPopulatedData) {
224            return true;
225        }
226 
227        return false;
228    }
229 
230    /**
231     * Here we need to make sure that the ZipFileIndex is valid. Check the timestamp of the file and
232     * if its the same as the one at the time the index was build we don't need to reopen anything.
233     */
234    private void checkIndex() throws IOException {
235        boolean isUpToDate = true;
236        if (!isUpToDate()) {
237            closeFile();
238            isUpToDate = false;
239        }
240 
241        if (zipRandomFile != null || isUpToDate) {
242            lastReferenceTimeStamp = System.currentTimeMillis();
243            return;
244        }
245 
246        hasPopulatedData = true;
247 
248        if (readIndex()) {
249            lastReferenceTimeStamp = System.currentTimeMillis();
250            return;
251        }
252 
253        directories = Collections.<String, DirectoryEntry>emptyMap();
254        allDirs = Collections.<String>emptySet();
255 
256        try {
257            openFile();
258            long totalLength = zipRandomFile.length();
259            ZipDirectory directory = new ZipDirectory(zipRandomFile, 0L, totalLength, this);
260            directory.buildIndex();
261        } finally {
262            if (zipRandomFile != null) {
263                closeFile();
264            }
265        }
266 
267        lastReferenceTimeStamp = System.currentTimeMillis();
268    }
269 
270    private void openFile() throws FileNotFoundException {
271        if (zipRandomFile == null && zipFile != null) {
272            zipRandomFile = new RandomAccessFile(zipFile, "r");
273        }
274    }
275 
276    private void cleanupState() {
277        // Make sure there is a valid but empty index if the file doesn't exist
278        entries = ZipFileIndexEntry.EMPTY_ARRAY;
279        directories = Collections.<String, DirectoryEntry>emptyMap();
280        zipFileLastModified = NOT_MODIFIED;
281        allDirs = Collections.<String>emptySet();
282    }
283 
284    public void close() {
285        lock.lock();
286        try {
287            writeIndex();
288            closeFile();
289        }
290        finally {
291            lock.unlock();
292        }
293    }
294 
295    private void closeFile() {
296        if (zipRandomFile != null) {
297            try {
298                zipRandomFile.close();
299            } catch (IOException ex) {
300            }
301            zipRandomFile = null;
302        }
303    }
304 
305    /**
306     * Returns the ZipFileIndexEntry for an absolute path, if there is one.
307     */
308    public ZipFileIndexEntry getZipIndexEntry(String path) {
309        if (File.separatorChar != '/') {
310            path = path.replace('/', File.separatorChar);
311        }
312        lock.lock();
313        try {
314            checkIndex();
315            String lookFor = "";
316            int lastSepIndex = path.lastIndexOf(File.separatorChar);
317            boolean noSeparator = false;
318            if (lastSepIndex == -1) {
319                noSeparator = true;
320            }
321 
322            DirectoryEntry de = directories.get(noSeparator ? "" : path.substring(0, lastSepIndex));
323 
324            lookFor = path.substring(noSeparator ? 0 : lastSepIndex + 1);
325 
326            return de == null ? null : de.getEntry(lookFor);
327        }
328        catch (IOException e) {
329            return null;
330        }
331        finally {
332            lock.unlock();
333        }
334    }
335 
336    /**
337     * Returns a javac List of filenames within an absolute path in the ZipFileIndex.
338     */
339    public com.sun.tools.javac.util.List<String> getFiles(String path) {
340        if (File.separatorChar != '/') {
341            path = path.replace('/', File.separatorChar);
342        }
343 
344        lock.lock();
345        try {
346            checkIndex();
347 
348            DirectoryEntry de = directories.get(path);
349            com.sun.tools.javac.util.List<String> ret = de == null ? null : de.getFiles();
350 
351            if (ret == null) {
352                return com.sun.tools.javac.util.List.<String>nil();
353            }
354            return ret;
355        }
356        catch (IOException e) {
357            return com.sun.tools.javac.util.List.<String>nil();
358        }
359        finally {
360            lock.unlock();
361        }
362    }
363 
364    public List<String> getAllDirectories(String path) {
365 
366        if (File.separatorChar != '/') {
367            path = path.replace('/', File.separatorChar);
368        }
369 
370        lock.lock();
371        try {
372            checkIndex();
373            path = path.intern();
374 
375            DirectoryEntry de = directories.get(path);
376            com.sun.tools.javac.util.List<String> ret = de == null ? null : de.getDirectories();
377 
378            if (ret == null) {
379                return com.sun.tools.javac.util.List.<String>nil();
380            }
381 
382            return ret;
383        }
384        catch (IOException e) {
385            return com.sun.tools.javac.util.List.<String>nil();
386        }
387        finally {
388            lock.unlock();
389        }
390    }
391 
392    public Set<String> getAllDirectories() {
393        lock.lock();
394        try {
395            checkIndex();
396            if (allDirs == Collections.EMPTY_SET) {
397                Set<String> alldirs = new HashSet<String>();
398                Iterator<String> dirsIter = directories.keySet().iterator();
399                while (dirsIter.hasNext()) {
400                    alldirs.add(new String(dirsIter.next()));
401                }
402 
403                allDirs = alldirs;
404            }
405 
406            return allDirs;
407        }
408        catch (IOException e) {
409            return Collections.<String>emptySet();
410        }
411        finally {
412            lock.unlock();
413        }
414    }
415 
416    /**
417     * Tests if a specific path exists in the zip.  This method will return true
418     * for file entries and directories.
419     *
420     * @param path A path within the zip.
421     * @return True if the path is a file or dir, false otherwise.
422     */
423    public boolean contains(String path) {
424        lock.lock();
425        try {
426            checkIndex();
427            return getZipIndexEntry(path) != null;
428        }
429        catch (IOException e) {
430            return false;
431        }
432        finally {
433            lock.unlock();
434        }
435    }
436 
437    public boolean isDirectory(String path) throws IOException {
438        lock.lock();
439        try {
440            // The top level in a zip file is always a directory.
441            if (path.length() == 0) {
442                lastReferenceTimeStamp = System.currentTimeMillis();
443                return true;
444            }
445 
446            if (File.separatorChar != '/')
447                path = path.replace('/', File.separatorChar);
448            checkIndex();
449            return directories.get(path) != null;
450        }
451        finally {
452            lock.unlock();
453        }
454    }
455 
456    public long getLastModified(String path) throws IOException {
457        lock.lock();
458        try {
459            ZipFileIndexEntry entry = getZipIndexEntry(path);
460            if (entry == null)
461                throw new FileNotFoundException();
462            return entry.getLastModified();
463        }
464        finally {
465            lock.unlock();
466        }
467    }
468 
469    public int length(String path) throws IOException {
470        lock.lock();
471        try {
472            ZipFileIndexEntry entry = getZipIndexEntry(path);
473            if (entry == null)
474                throw new FileNotFoundException();
475 
476            if (entry.isDir) {
477                return 0;
478            }
479 
480            byte[] header = getHeader(entry);
481            // entry is not compressed?
482            if (get2ByteLittleEndian(header, 8) == 0) {
483                return entry.compressedSize;
484            } else {
485                return entry.size;
486            }
487        }
488        finally {
489            lock.unlock();
490        }
491    }
492 
493    public byte[] read(String path) throws IOException {
494        lock.lock();
495        try {
496            ZipFileIndexEntry entry = getZipIndexEntry(path);
497            if (entry == null)
498                throw new FileNotFoundException(MessageFormat.format("Path not found in ZIP: {0}", path));
499            return read(entry);
500        }
501        finally {
502            lock.unlock();
503        }
504    }
505 
506    public byte[] read(ZipFileIndexEntry entry) throws IOException {
507        lock.lock();
508        try {
509            openFile();
510            byte[] result = readBytes(entry);
511            closeFile();
512            return result;
513        }
514        finally {
515            lock.unlock();
516        }
517    }
518 
519    public int read(String path, byte[] buffer) throws IOException {
520        lock.lock();
521        try {
522            ZipFileIndexEntry entry = getZipIndexEntry(path);
523            if (entry == null)
524                throw new FileNotFoundException();
525            return read(entry, buffer);
526        }
527        finally {
528            lock.unlock();
529        }
530    }
531 
532    public int read(ZipFileIndexEntry entry, byte[] buffer)
533            throws IOException {
534        lock.lock();
535        try {
536            int result = readBytes(entry, buffer);
537            return result;
538        }
539        finally {
540            lock.unlock();
541        }
542    }
543 
544    private byte[] readBytes(ZipFileIndexEntry entry) throws IOException {
545        byte[] header = getHeader(entry);
546        int csize = entry.compressedSize;
547        byte[] cbuf = new byte[csize];
548        zipRandomFile.skipBytes(get2ByteLittleEndian(header, 26) + get2ByteLittleEndian(header, 28));
549        zipRandomFile.readFully(cbuf, 0, csize);
550 
551        // is this compressed - offset 8 in the ZipEntry header
552        if (get2ByteLittleEndian(header, 8) == 0)
553            return cbuf;
554 
555        int size = entry.size;
556        byte[] buf = new byte[size];
557        if (inflate(cbuf, buf) != size)
558            throw new ZipException("corrupted zip file");
559 
560        return buf;
561    }
562 
563    /**
564     *
565     */
566    private int readBytes(ZipFileIndexEntry entry, byte[] buffer) throws IOException {
567        byte[] header = getHeader(entry);
568 
569        // entry is not compressed?
570        if (get2ByteLittleEndian(header, 8) == 0) {
571            zipRandomFile.skipBytes(get2ByteLittleEndian(header, 26) + get2ByteLittleEndian(header, 28));
572            int offset = 0;
573            int size = buffer.length;
574            while (offset < size) {
575                int count = zipRandomFile.read(buffer, offset, size - offset);
576                if (count == -1)
577                    break;
578                offset += count;
579            }
580            return entry.size;
581        }
582 
583        int csize = entry.compressedSize;
584        byte[] cbuf = new byte[csize];
585        zipRandomFile.skipBytes(get2ByteLittleEndian(header, 26) + get2ByteLittleEndian(header, 28));
586        zipRandomFile.readFully(cbuf, 0, csize);
587 
588        int count = inflate(cbuf, buffer);
589        if (count == -1)
590            throw new ZipException("corrupted zip file");
591 
592        return entry.size;
593    }
594 
595    //----------------------------------------------------------------------------
596    // Zip utilities
597    //----------------------------------------------------------------------------
598 
599    private byte[] getHeader(ZipFileIndexEntry entry) throws IOException {
600        zipRandomFile.seek(entry.offset);
601        byte[] header = new byte[30];
602        zipRandomFile.readFully(header);
603        if (get4ByteLittleEndian(header, 0) != 0x04034b50)
604            throw new ZipException("corrupted zip file");
605        if ((get2ByteLittleEndian(header, 6) & 1) != 0)
606            throw new ZipException("encrypted zip file"); // offset 6 in the header of the ZipFileEntry
607        return header;
608    }
609 
610  /*
611   * Inflate using the java.util.zip.Inflater class
612   */
613    private static Inflater inflater;
614    private int inflate(byte[] src, byte[] dest) {
615 
616        // construct the inflater object or reuse an existing one
617        if (inflater == null)
618            inflater = new Inflater(true);
619 
620        synchronized (inflater) {
621            inflater.reset();
622            inflater.setInput(src);
623            try {
624                return inflater.inflate(dest);
625            } catch (DataFormatException ex) {
626                return -1;
627            }
628        }
629    }
630 
631    /**
632     * return the two bytes buf[pos], buf[pos+1] as an unsigned integer in little
633     * endian format.
634     */
635    private static int get2ByteLittleEndian(byte[] buf, int pos) {
636        return (buf[pos] & 0xFF) + ((buf[pos+1] & 0xFF) << 8);
637    }
638 
639    /**
640     * return the 4 bytes buf[i..i+3] as an integer in little endian format.
641     */
642    private static int get4ByteLittleEndian(byte[] buf, int pos) {
643        return (buf[pos] & 0xFF) + ((buf[pos + 1] & 0xFF) << 8) +
644                ((buf[pos + 2] & 0xFF) << 16) + ((buf[pos + 3] & 0xFF) << 24);
645    }
646 
647    /* ----------------------------------------------------------------------------
648     * ZipDirectory
649     * ----------------------------------------------------------------------------*/
650 
651    private class ZipDirectory {
652        private String lastDir;
653        private int lastStart;
654        private int lastLen;
655 
656        byte[] zipDir;
657        RandomAccessFile zipRandomFile = null;
658        ZipFileIndex zipFileIndex = null;
659 
660        public ZipDirectory(RandomAccessFile zipRandomFile, long start, long end, ZipFileIndex index) throws IOException {
661            this.zipRandomFile = zipRandomFile;
662            this.zipFileIndex = index;
663 
664            findCENRecord(start, end);
665        }
666 
667        /*
668         * Reads zip file central directory.
669         * For more details see readCEN in zip_util.c from the JDK sources.
670         * This is a Java port of that function.
671         */
672        private void findCENRecord(long start, long end) throws IOException {
673            long totalLength = end - start;
674            int endbuflen = 1024;
675            byte[] endbuf = new byte[endbuflen];
676            long endbufend = end - start;
677 
678            // There is a variable-length field after the dir offset record. We need to do consequential search.
679            while (endbufend >= 22) {
680                if (endbufend < endbuflen)
681                    endbuflen = (int)endbufend;
682                long endbufpos = endbufend - endbuflen;
683                zipRandomFile.seek(start + endbufpos);
684                zipRandomFile.readFully(endbuf, 0, endbuflen);
685                int i = endbuflen - 22;
686                while (i >= 0 &&
687                        !(endbuf[i] == 0x50 &&
688                        endbuf[i + 1] == 0x4b &&
689                        endbuf[i + 2] == 0x05 &&
690                        endbuf[i + 3] == 0x06 &&
691                        endbufpos + i + 22 +
692                        get2ByteLittleEndian(endbuf, i + 20) == totalLength)) {
693                    i--;
694                }
695 
696                if (i >= 0) {
697                    zipDir = new byte[get4ByteLittleEndian(endbuf, i + 12) + 2];
698                    zipDir[0] = endbuf[i + 10];
699                    zipDir[1] = endbuf[i + 11];
700                    zipRandomFile.seek(start + get4ByteLittleEndian(endbuf, i + 16));
701                    zipRandomFile.readFully(zipDir, 2, zipDir.length - 2);
702                    return;
703                } else {
704                    endbufend = endbufpos + 21;
705                }
706            }
707            throw new ZipException("cannot read zip file");
708        }
709        private void buildIndex() throws IOException {
710            int entryCount = get2ByteLittleEndian(zipDir, 0);
711 
712            entries = new ZipFileIndexEntry[entryCount];
713            // Add each of the files
714            if (entryCount > 0) {
715                directories = new HashMap<String, DirectoryEntry>();
716                ArrayList<ZipFileIndexEntry> entryList = new ArrayList<ZipFileIndexEntry>();
717                int pos = 2;
718                for (int i = 0; i < entryCount; i++) {
719                    pos = readEntry(pos, entryList, directories);
720                }
721 
722                // Add the accumulated dirs into the same list
723                Iterator i = directories.keySet().iterator();
724                while (i.hasNext()) {
725                    ZipFileIndexEntry zipFileIndexEntry = new ZipFileIndexEntry( (String) i.next());
726                    zipFileIndexEntry.isDir = true;
727                    entryList.add(zipFileIndexEntry);
728                }
729 
730                entries = entryList.toArray(new ZipFileIndexEntry[entryList.size()]);
731                Arrays.sort(entries);
732            } else {
733                cleanupState();
734            }
735        }
736 
737        private int readEntry(int pos, List<ZipFileIndexEntry> entryList,
738                Map<String, DirectoryEntry> directories) throws IOException {
739            if (get4ByteLittleEndian(zipDir, pos) != 0x02014b50) {
740                throw new ZipException("cannot read zip file entry");
741            }
742 
743            int dirStart = pos + 46;
744            int fileStart = dirStart;
745            int fileEnd = fileStart + get2ByteLittleEndian(zipDir, pos + 28);
746 
747            if (zipFileIndex.symbolFilePrefixLength != 0 &&
748                    ((fileEnd - fileStart) >= symbolFilePrefixLength)) {
749                dirStart += zipFileIndex.symbolFilePrefixLength;
750               fileStart += zipFileIndex.symbolFilePrefixLength;
751            }
752 
753            // Use the OS's path separator. Keep the position of the last one.
754            for (int index = fileStart; index < fileEnd; index++) {
755                byte nextByte = zipDir[index];
756                if (nextByte == (byte)'\\' || nextByte == (byte)'/') {
757                    zipDir[index] = (byte)File.separatorChar;
758                    fileStart = index + 1;
759                }
760            }
761 
762            String directory = null;
763            if (fileStart == dirStart)
764                directory = "";
765            else if (lastDir != null && lastLen == fileStart - dirStart - 1) {
766                int index = lastLen - 1;
767                while (zipDir[lastStart + index] == zipDir[dirStart + index]) {
768                    if (index == 0) {
769                        directory = lastDir;
770                        break;
771                    }
772                    index--;
773                }
774            }
775 
776            // Sub directories
777            if (directory == null) {
778                lastStart = dirStart;
779                lastLen = fileStart - dirStart - 1;
780 
781                directory = new String(zipDir, dirStart, lastLen, "UTF-8").intern();
782                lastDir = directory;
783 
784                // Enter also all the parent directories
785                String tempDirectory = directory;
786 
787                while (directories.get(tempDirectory) == null) {
788                    directories.put(tempDirectory, new DirectoryEntry(tempDirectory, zipFileIndex));
789                    int separator = tempDirectory.lastIndexOf(File.separatorChar);
790                    if (separator == -1)
791                        break;
792                    tempDirectory = tempDirectory.substring(0, separator);
793                }
794            }
795            else {
796                directory = directory.intern();
797                if (directories.get(directory) == null) {
798                    directories.put(directory, new DirectoryEntry(directory, zipFileIndex));
799                }
800            }
801 
802            // For each dir create also a file
803            if (fileStart != fileEnd) {
804                ZipFileIndexEntry entry = new ZipFileIndexEntry(directory,
805                        new String(zipDir, fileStart, fileEnd - fileStart, "UTF-8"));
806 
807                entry.setNativeTime(get4ByteLittleEndian(zipDir, pos + 12));
808                entry.compressedSize = get4ByteLittleEndian(zipDir, pos + 20);
809                entry.size = get4ByteLittleEndian(zipDir, pos + 24);
810                entry.offset = get4ByteLittleEndian(zipDir, pos + 42);
811                entryList.add(entry);
812            }
813 
814            return pos + 46 +
815                    get2ByteLittleEndian(zipDir, pos + 28) +
816                    get2ByteLittleEndian(zipDir, pos + 30) +
817                    get2ByteLittleEndian(zipDir, pos + 32);
818        }
819    }
820 
821    /**
822     * Returns the last modified timestamp of a zip file.
823     * @return long
824     */
825    public long getZipFileLastModified() throws IOException {
826        lock.lock();
827        try {
828            checkIndex();
829            return zipFileLastModified;
830        }
831        finally {
832            lock.unlock();
833        }
834    }
835 
836    /** ------------------------------------------------------------------------
837     *  DirectoryEntry class
838     * -------------------------------------------------------------------------*/
839    static class DirectoryEntry {
840        private boolean filesInited;
841        private boolean directoriesInited;
842        private boolean zipFileEntriesInited;
843        private boolean entriesInited;
844 
845        private long writtenOffsetOffset = 0;
846 
847        private String dirName;
848 
849        private com.sun.tools.javac.util.List<String> zipFileEntriesFiles = com.sun.tools.javac.util.List.<String>nil();
850        private com.sun.tools.javac.util.List<String> zipFileEntriesDirectories = com.sun.tools.javac.util.List.<String>nil();
851        private com.sun.tools.javac.util.List<ZipFileIndexEntry>  zipFileEntries = com.sun.tools.javac.util.List.<ZipFileIndexEntry>nil();
852 
853        private List<ZipFileIndexEntry> entries = new ArrayList<ZipFileIndexEntry>();
854 
855        private ZipFileIndex zipFileIndex;
856 
857        private int numEntries;
858 
859        DirectoryEntry(String dirName, ZipFileIndex index) {
860        filesInited = false;
861            directoriesInited = false;
862            entriesInited = false;
863 
864            if (File.separatorChar == '/') {
865                dirName.replace('\\', '/');
866            }
867            else {
868                dirName.replace('/', '\\');
869            }
870 
871            this.dirName = dirName.intern();
872            this.zipFileIndex = index;
873        }
874 
875        private com.sun.tools.javac.util.List<String> getFiles() {
876            if (filesInited) {
877                return zipFileEntriesFiles;
878            }
879 
880            initEntries();
881 
882            for (ZipFileIndexEntry e : entries) {
883                if (!e.isDir) {
884                    zipFileEntriesFiles = zipFileEntriesFiles.append(e.name);
885                }
886            }
887            filesInited = true;
888            return zipFileEntriesFiles;
889        }
890 
891        private com.sun.tools.javac.util.List<String> getDirectories() {
892            if (directoriesInited) {
893                return zipFileEntriesFiles;
894            }
895 
896            initEntries();
897 
898            for (ZipFileIndexEntry e : entries) {
899                if (e.isDir) {
900                    zipFileEntriesDirectories = zipFileEntriesDirectories.append(e.name);
901                }
902            }
903 
904            directoriesInited = true;
905 
906            return zipFileEntriesDirectories;
907        }
908 
909        private com.sun.tools.javac.util.List<ZipFileIndexEntry> getEntries() {
910            if (zipFileEntriesInited) {
911                return zipFileEntries;
912            }
913 
914            initEntries();
915 
916            zipFileEntries = com.sun.tools.javac.util.List.nil();
917            for (ZipFileIndexEntry zfie : entries) {
918                zipFileEntries = zipFileEntries.append(zfie);
919            }
920 
921            zipFileEntriesInited = true;
922 
923            return zipFileEntries;
924        }
925 
926        private ZipFileIndexEntry getEntry(String rootName) {
927            initEntries();
928            int index = Collections.binarySearch(entries, new ZipFileIndexEntry(dirName, rootName));
929            if (index < 0) {
930                return null;
931            }
932 
933            return entries.get(index);
934        }
935 
936        private void initEntries() {
937            if (entriesInited) {
938                return;
939            }
940 
941            if (!zipFileIndex.readFromIndex) {
942                int from = -Arrays.binarySearch(zipFileIndex.entries,
943                        new ZipFileIndexEntry(dirName, ZipFileIndex.MIN_CHAR)) - 1;
944                int to = -Arrays.binarySearch(zipFileIndex.entries,
945                        new ZipFileIndexEntry(dirName, MAX_CHAR)) - 1;
946 
947                boolean emptyList = false;
948 
949                for (int i = from; i < to; i++) {
950                    entries.add(zipFileIndex.entries[i]);
951                }
952            } else {
953                File indexFile = zipFileIndex.getIndexFile();
954                if (indexFile != null) {
955                    RandomAccessFile raf = null;
956                    try {
957                        raf = new RandomAccessFile(indexFile, "r");
958                        raf.seek(writtenOffsetOffset);
959 
960                        for (int nFiles = 0; nFiles < numEntries; nFiles++) {
961                            // Read the name bytes
962                            int zfieNameBytesLen = raf.readInt();
963                            byte [] zfieNameBytes = new byte[zfieNameBytesLen];
964                            raf.read(zfieNameBytes);
965                            String eName = new String(zfieNameBytes, "UTF-8");
966 
967                            // Read isDir
968                            boolean eIsDir = raf.readByte() == (byte)0 ? false : true;
969 
970                            // Read offset of bytes in the real Jar/Zip file
971                            int eOffset = raf.readInt();
972 
973                            // Read size of the file in the real Jar/Zip file
974                            int eSize = raf.readInt();
975 
976                            // Read compressed size of the file in the real Jar/Zip file
977                            int eCsize = raf.readInt();
978 
979                            // Read java time stamp of the file in the real Jar/Zip file
980                            long eJavaTimestamp = raf.readLong();
981 
982                            ZipFileIndexEntry rfie = new ZipFileIndexEntry(dirName, eName);
983                            rfie.isDir = eIsDir;
984                            rfie.offset = eOffset;
985                            rfie.size = eSize;
986                            rfie.compressedSize = eCsize;
987                            rfie.javatime = eJavaTimestamp;
988                            entries.add(rfie);
989                        }
990                    } catch (Throwable t) {
991                        // Do nothing
992                    } finally {
993                        try {
994                            if (raf == null) {
995                                raf.close();
996                            }
997                        } catch (Throwable t) {
998                            // Do nothing
999                        }
1000                    }
1001                }
1002            }
1003 
1004            entriesInited = true;
1005        }
1006 
1007        List<ZipFileIndexEntry> getEntriesAsCollection() {
1008            initEntries();
1009 
1010            return entries;
1011        }
1012    }
1013 
1014    private boolean readIndex() {
1015        if (triedToReadIndex || !usePreindexedCache) {
1016            return false;
1017        }
1018 
1019        boolean ret = false;
1020        lock.lock();
1021        try {
1022            triedToReadIndex = true;
1023            RandomAccessFile raf = null;
1024            try {
1025                File indexFileName = getIndexFile();
1026                raf = new RandomAccessFile(indexFileName, "r");
1027 
1028                long fileStamp = raf.readLong();
1029                if (zipFile.lastModified() != fileStamp) {
1030                    ret = false;
1031                } else {
1032                    directories = new HashMap<String, DirectoryEntry>();
1033                    int numDirs = raf.readInt();
1034                    for (int nDirs = 0; nDirs < numDirs; nDirs++) {
1035                        int dirNameBytesLen = raf.readInt();
1036                        byte [] dirNameBytes = new byte[dirNameBytesLen];
1037                        raf.read(dirNameBytes);
1038 
1039                        String dirNameStr = new String(dirNameBytes, "UTF-8");
1040                        DirectoryEntry de = new DirectoryEntry(dirNameStr, this);
1041                        de.numEntries = raf.readInt();
1042                        de.writtenOffsetOffset = raf.readLong();
1043                        directories.put(dirNameStr, de);
1044                    }
1045                    ret = true;
1046                    zipFileLastModified = fileStamp;
1047                }
1048            } catch (Throwable t) {
1049                // Do nothing
1050            } finally {
1051                if (raf != null) {
1052                    try {
1053                        raf.close();
1054                    } catch (Throwable tt) {
1055                        // Do nothing
1056                    }
1057                }
1058            }
1059            if (ret == true) {
1060                readFromIndex = true;
1061            }
1062        }
1063        finally {
1064            lock.unlock();
1065        }
1066 
1067        return ret;
1068    }
1069 
1070    private boolean writeIndex() {
1071        boolean ret = false;
1072        if (readFromIndex || !usePreindexedCache) {
1073            return true;
1074        }
1075 
1076        if (!writeIndex) {
1077            return true;
1078        }
1079 
1080        File indexFile = getIndexFile();
1081        if (indexFile == null) {
1082            return false;
1083        }
1084 
1085        RandomAccessFile raf = null;
1086        long writtenSoFar = 0;
1087        try {
1088            raf = new RandomAccessFile(indexFile, "rw");
1089 
1090            raf.writeLong(zipFileLastModified);
1091            writtenSoFar += 8;
1092 
1093 
1094            Iterator<String> iterDirName = directories.keySet().iterator();
1095            List<DirectoryEntry> directoriesToWrite = new ArrayList<DirectoryEntry>();
1096            Map<String, Long> offsets = new HashMap<String, Long>();
1097            raf.writeInt(directories.keySet().size());
1098            writtenSoFar += 4;
1099 
1100            while(iterDirName.hasNext()) {
1101                String dirName = iterDirName.next();
1102                DirectoryEntry dirEntry = directories.get(dirName);
1103 
1104                directoriesToWrite.add(dirEntry);
1105 
1106                // Write the dir name bytes
1107                byte [] dirNameBytes = dirName.getBytes("UTF-8");
1108                int dirNameBytesLen = dirNameBytes.length;
1109                raf.writeInt(dirNameBytesLen);
1110                writtenSoFar += 4;
1111 
1112                raf.write(dirNameBytes);
1113                writtenSoFar += dirNameBytesLen;
1114 
1115                // Write the number of files in the dir
1116                List dirEntries = dirEntry.getEntriesAsCollection();
1117                raf.writeInt(dirEntries.size());
1118                writtenSoFar += 4;
1119 
1120                offsets.put(dirName, new Long(writtenSoFar));
1121 
1122                // Write the offset of the file's data in the dir
1123                dirEntry.writtenOffsetOffset = 0L;
1124                raf.writeLong(0L);
1125                writtenSoFar += 8;
1126            }
1127 
1128            for (DirectoryEntry de : directoriesToWrite) {
1129                // Fix up the offset in the directory table
1130                long currFP = raf.getFilePointer();
1131 
1132                long offsetOffset = offsets.get(de.dirName).longValue();
1133                raf.seek(offsetOffset);
1134                raf.writeLong(writtenSoFar);
1135 
1136                raf.seek(currFP);
1137 
1138                // Now write each of the files in the DirectoryEntry
1139                List<ZipFileIndexEntry> entries = de.getEntriesAsCollection();
1140                for (ZipFileIndexEntry zfie : entries) {
1141                    // Write the name bytes
1142                    byte [] zfieNameBytes = zfie.name.getBytes("UTF-8");
1143                    int zfieNameBytesLen = zfieNameBytes.length;
1144                    raf.writeInt(zfieNameBytesLen);
1145                    writtenSoFar += 4;
1146                    raf.write(zfieNameBytes);
1147                    writtenSoFar += zfieNameBytesLen;
1148 
1149                    // Write isDir
1150                    raf.writeByte(zfie.isDir ? (byte)1 : (byte)0);
1151                    writtenSoFar += 1;
1152 
1153                    // Write offset of bytes in the real Jar/Zip file
1154                    raf.writeInt(zfie.offset);
1155                    writtenSoFar += 4;
1156 
1157                    // Write size of the file in the real Jar/Zip file
1158                    raf.writeInt(zfie.size);
1159                    writtenSoFar += 4;
1160 
1161                    // Write compressed size of the file in the real Jar/Zip file
1162                    raf.writeInt(zfie.compressedSize);
1163                    writtenSoFar += 4;
1164 
1165                    // Write java time stamp of the file in the real Jar/Zip file
1166                    raf.writeLong(zfie.getLastModified());
1167                    writtenSoFar += 8;
1168                }
1169            }
1170        } catch (Throwable t) {
1171            // Do nothing
1172        } finally {
1173            try {
1174                if (raf != null) {
1175                    raf.close();
1176                }
1177            } catch(IOException ioe) {
1178                // Do nothing
1179            }
1180        }
1181 
1182        return ret;
1183    }
1184 
1185    public boolean writeZipIndex() {
1186        lock.lock();
1187        try {
1188            return writeIndex();
1189        }
1190        finally {
1191            lock.unlock();
1192        }
1193    }
1194 
1195    private File getIndexFile() {
1196        if (zipIndexFile == null) {
1197            if (zipFile == null) {
1198                return null;
1199            }
1200 
1201            zipIndexFile = new File((preindexedCacheLocation == null ? "" : preindexedCacheLocation) +
1202                    zipFile.getName() + ".index");
1203        }
1204 
1205        return zipIndexFile;
1206    }
1207 
1208    public File getZipFile() {
1209        return zipFile;
1210    }
1211}

[all classes][com.sun.tools.javac.zip]
EMMA 2.0.5312 (C) Vladimir Roubtsov