EMMA Coverage Report (generated Sun Mar 01 22:06:14 CET 2015)
[all classes][org.h2.store.fs]

COVERAGE SUMMARY FOR SOURCE FILE [FilePathZip.java]

nameclass, %method, %block, %line, %
FilePathZip.java100% (3/3)90%  (36/40)77%  (499/644)78%  (122/157)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class FilePathZip100% (1/1)88%  (22/25)74%  (326/441)74%  (78/105)
createDirectory (): void 0%   (0/1)0%   (0/1)0%   (0/1)
createFile (): boolean 0%   (0/1)0%   (0/3)0%   (0/1)
delete (): void 0%   (0/1)0%   (0/3)0%   (0/1)
createTempFile (String, boolean, boolean): FilePath 100% (1/1)39%  (7/18)67%  (2/3)
isDirectory (): boolean 100% (1/1)43%  (33/77)47%  (9.3/20)
size (): long 100% (1/1)63%  (17/27)57%  (3.4/6)
open (String): FileChannel 100% (1/1)67%  (16/24)67%  (4/6)
exists (): boolean 100% (1/1)74%  (23/31)69%  (5.5/8)
newDirectoryStream (): ArrayList 100% (1/1)79%  (95/120)81%  (20.9/26)
getParent (): FilePath 100% (1/1)88%  (15/17)94%  (1.9/2)
FilePathZip (): void 100% (1/1)100% (3/3)100% (1/1)
canWrite (): boolean 100% (1/1)100% (2/2)100% (1/1)
getEntryName (): String 100% (1/1)100% (32/32)100% (8/8)
getPath (String): FilePathZip 100% (1/1)100% (9/9)100% (3/3)
getScheme (): String 100% (1/1)100% (2/2)100% (1/1)
isAbsolute (): boolean 100% (1/1)100% (8/8)100% (2/2)
lastModified (): long 100% (1/1)100% (2/2)100% (1/1)
moveTo (FilePath, boolean): void 100% (1/1)100% (3/3)100% (1/1)
newInputStream (): InputStream 100% (1/1)100% (8/8)100% (1/1)
newOutputStream (boolean): OutputStream 100% (1/1)100% (5/5)100% (1/1)
openZipFile (): ZipFile 100% (1/1)100% (9/9)100% (2/2)
setReadOnly (): boolean 100% (1/1)100% (2/2)100% (1/1)
toRealPath (): FilePath 100% (1/1)100% (2/2)100% (1/1)
translateFileName (String): String 100% (1/1)100% (23/23)100% (6/6)
unwrap (): FilePath 100% (1/1)100% (10/10)100% (1/1)
     
class FileZip$1100% (1/1)67%  (2/3)85%  (11/13)67%  (2/3)
isValid (): boolean 0%   (0/1)0%   (0/2)0%   (0/1)
FileZip$1 (FileZip, FileChannel, long, long, boolean): void 100% (1/1)100% (10/10)100% (1/1)
release (): void 100% (1/1)100% (1/1)100% (1/1)
     
class FileZip100% (1/1)100% (12/12)85%  (162/190)86%  (43/50)
seek (): void 100% (1/1)67%  (56/84)68%  (15/22)
<static initializer> 100% (1/1)100% (4/4)100% (1/1)
FileZip (ZipFile, ZipEntry): void 100% (1/1)100% (13/13)100% (5/5)
force (boolean): void 100% (1/1)100% (1/1)100% (1/1)
implCloseChannel (): void 100% (1/1)100% (13/13)100% (5/5)
position (): long 100% (1/1)100% (3/3)100% (1/1)
position (long): FileChannel 100% (1/1)100% (5/5)100% (2/2)
read (ByteBuffer): int 100% (1/1)100% (40/40)100% (7/7)
size (): long 100% (1/1)100% (3/3)100% (1/1)
truncate (long): FileChannel 100% (1/1)100% (5/5)100% (1/1)
tryLock (long, long, boolean): FileLock 100% (1/1)100% (14/14)100% (3/3)
write (ByteBuffer): int 100% (1/1)100% (5/5)100% (1/1)

1/*
2 * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0,
3 * and the EPL 1.0 (http://h2database.com/html/license.html).
4 * Initial Developer: H2 Group
5 */
6package org.h2.store.fs;
7 
8import java.io.FileNotFoundException;
9import java.io.IOException;
10import java.io.InputStream;
11import java.io.OutputStream;
12import java.nio.ByteBuffer;
13import java.nio.channels.FileChannel;
14import java.nio.channels.FileLock;
15import java.util.ArrayList;
16import java.util.Enumeration;
17import java.util.zip.ZipEntry;
18import java.util.zip.ZipFile;
19import org.h2.message.DbException;
20import org.h2.util.IOUtils;
21import org.h2.util.New;
22 
23/**
24 * This is a read-only file system that allows
25 * to access databases stored in a .zip or .jar file.
26 */
27public class FilePathZip extends FilePath {
28 
29    @Override
30    public FilePathZip getPath(String path) {
31        FilePathZip p = new FilePathZip();
32        p.name = path;
33        return p;
34    }
35 
36    @Override
37    public void createDirectory() {
38        // ignore
39    }
40 
41    @Override
42    public boolean createFile() {
43        throw DbException.getUnsupportedException("write");
44    }
45 
46    @Override
47    public void delete() {
48        throw DbException.getUnsupportedException("write");
49    }
50 
51    @Override
52    public boolean exists() {
53        try {
54            String entryName = getEntryName();
55            if (entryName.length() == 0) {
56                return true;
57            }
58            ZipFile file = openZipFile();
59            try {
60                return file.getEntry(entryName) != null;
61            } finally {
62                file.close();
63            }
64        } catch (IOException e) {
65            return false;
66        }
67    }
68 
69    @Override
70    public long lastModified() {
71        return 0;
72    }
73 
74    @Override
75    public FilePath getParent() {
76        int idx = name.lastIndexOf('/');
77        return idx < 0 ? null : getPath(name.substring(0, idx));
78    }
79 
80    @Override
81    public boolean isAbsolute() {
82        String fileName = translateFileName(name);
83        return FilePath.get(fileName).isAbsolute();
84    }
85 
86    @Override
87    public FilePath unwrap() {
88        return FilePath.get(name.substring(getScheme().length() + 1));
89    }
90 
91    @Override
92    public boolean isDirectory() {
93        try {
94            String entryName = getEntryName();
95            if (entryName.length() == 0) {
96                return true;
97            }
98            ZipFile file = openZipFile();
99            try {
100                Enumeration<? extends ZipEntry> en = file.entries();
101                while (en.hasMoreElements()) {
102                    ZipEntry entry = en.nextElement();
103                    String n = entry.getName();
104                    if (n.equals(entryName)) {
105                        return entry.isDirectory();
106                    } else  if (n.startsWith(entryName)) {
107                        if (n.length() == entryName.length() + 1) {
108                            if (n.equals(entryName + "/")) {
109                                return true;
110                            }
111                        }
112                    }
113                }
114            } finally {
115                file.close();
116            }
117            return false;
118        } catch (IOException e) {
119            return false;
120        }
121    }
122 
123    @Override
124    public boolean canWrite() {
125        return false;
126    }
127 
128    @Override
129    public boolean setReadOnly() {
130        return true;
131    }
132 
133    @Override
134    public long size() {
135        try {
136            ZipFile file = openZipFile();
137            try {
138                ZipEntry entry = file.getEntry(getEntryName());
139                return entry == null ? 0 : entry.getSize();
140            } finally {
141                file.close();
142            }
143        } catch (IOException e) {
144            return 0;
145        }
146    }
147 
148    @Override
149    public ArrayList<FilePath> newDirectoryStream() {
150        String path = name;
151        ArrayList<FilePath> list = New.arrayList();
152        try {
153            if (path.indexOf('!') < 0) {
154                path += "!";
155            }
156            if (!path.endsWith("/")) {
157                path += "/";
158            }
159            ZipFile file = openZipFile();
160            try {
161                String dirName = getEntryName();
162                String prefix = path.substring(0, path.length() - dirName.length());
163                Enumeration<? extends ZipEntry> en = file.entries();
164                while (en.hasMoreElements()) {
165                    ZipEntry entry = en.nextElement();
166                    String name = entry.getName();
167                    if (!name.startsWith(dirName)) {
168                        continue;
169                    }
170                    if (name.length() <= dirName.length()) {
171                        continue;
172                    }
173                    int idx = name.indexOf('/', dirName.length());
174                    if (idx < 0 || idx >= name.length() - 1) {
175                        list.add(getPath(prefix + name));
176                    }
177                }
178            } finally {
179                file.close();
180            }
181            return list;
182        } catch (IOException e) {
183            throw DbException.convertIOException(e, "listFiles " + path);
184        }
185    }
186 
187    @Override
188    public InputStream newInputStream() throws IOException {
189        return new FileChannelInputStream(open("r"), true);
190    }
191 
192    @Override
193    public FileChannel open(String mode) throws IOException {
194        ZipFile file = openZipFile();
195        ZipEntry entry = file.getEntry(getEntryName());
196        if (entry == null) {
197            file.close();
198            throw new FileNotFoundException(name);
199        }
200        return new FileZip(file, entry);
201    }
202 
203    @Override
204    public OutputStream newOutputStream(boolean append) throws IOException {
205        throw new IOException("write");
206    }
207 
208    @Override
209    public void moveTo(FilePath newName, boolean atomicReplace) {
210        throw DbException.getUnsupportedException("write");
211    }
212 
213    private static String translateFileName(String fileName) {
214        if (fileName.startsWith("zip:")) {
215            fileName = fileName.substring("zip:".length());
216        }
217        int idx = fileName.indexOf('!');
218        if (idx >= 0) {
219            fileName = fileName.substring(0, idx);
220        }
221        return FilePathDisk.expandUserHomeDirectory(fileName);
222    }
223 
224    @Override
225    public FilePath toRealPath() {
226        return this;
227    }
228 
229    private String getEntryName() {
230        int idx = name.indexOf('!');
231        String fileName;
232        if (idx <= 0) {
233            fileName = "";
234        } else {
235            fileName = name.substring(idx + 1);
236        }
237        fileName = fileName.replace('\\', '/');
238        if (fileName.startsWith("/")) {
239            fileName = fileName.substring(1);
240        }
241        return fileName;
242    }
243 
244    private ZipFile openZipFile() throws IOException {
245        String fileName = translateFileName(name);
246        return new ZipFile(fileName);
247    }
248 
249    @Override
250    public FilePath createTempFile(String suffix, boolean deleteOnExit,
251            boolean inTempDir) throws IOException {
252        if (!inTempDir) {
253            throw new IOException("File system is read-only");
254        }
255        return new FilePathDisk().getPath(name).createTempFile(suffix,
256                deleteOnExit, true);
257    }
258 
259    @Override
260    public String getScheme() {
261        return "zip";
262    }
263 
264}
265 
266/**
267 * The file is read from a stream. When reading from start to end, the same
268 * input stream is re-used, however when reading from end to start, a new input
269 * stream is opened for each request.
270 */
271class FileZip extends FileBase {
272 
273    private static final byte[] SKIP_BUFFER = new byte[1024];
274 
275    private final ZipFile file;
276    private final ZipEntry entry;
277    private long pos;
278    private InputStream in;
279    private long inPos;
280    private final long length;
281    private boolean skipUsingRead;
282 
283    FileZip(ZipFile file, ZipEntry entry) {
284        this.file = file;
285        this.entry = entry;
286        length = entry.getSize();
287    }
288 
289    @Override
290    public long position() {
291        return pos;
292    }
293 
294    @Override
295    public long size() {
296        return length;
297    }
298 
299    @Override
300    public int read(ByteBuffer dst) throws IOException {
301        seek();
302        int len = in.read(dst.array(), dst.arrayOffset() + dst.position(),
303                dst.remaining());
304        if (len > 0) {
305            dst.position(dst.position() + len);
306            pos += len;
307            inPos += len;
308        }
309        return len;
310    }
311 
312    private void seek() throws IOException {
313        if (inPos > pos) {
314            if (in != null) {
315                in.close();
316            }
317            in = null;
318        }
319        if (in == null) {
320            in = file.getInputStream(entry);
321            inPos = 0;
322        }
323        if (inPos < pos) {
324            long skip = pos - inPos;
325            if (!skipUsingRead) {
326                try {
327                    IOUtils.skipFully(in, skip);
328                } catch (NullPointerException e) {
329                    // workaround for Android
330                    skipUsingRead = true;
331                }
332            }
333            if (skipUsingRead) {
334                while (skip > 0) {
335                    int s = (int) Math.min(SKIP_BUFFER.length, skip);
336                    s = in.read(SKIP_BUFFER, 0, s);
337                    skip -= s;
338                }
339            }
340            inPos = pos;
341        }
342    }
343 
344    @Override
345    public FileChannel position(long newPos) {
346        this.pos = newPos;
347        return this;
348    }
349 
350    @Override
351    public FileChannel truncate(long newLength) throws IOException {
352        throw new IOException("File is read-only");
353    }
354 
355    @Override
356    public void force(boolean metaData) throws IOException {
357        // nothing to do
358    }
359 
360    @Override
361    public int write(ByteBuffer src) throws IOException {
362        throw new IOException("File is read-only");
363    }
364 
365    @Override
366    public synchronized FileLock tryLock(long position, long size,
367            boolean shared) throws IOException {
368        if (shared) {
369            // cast to FileChannel to avoid JDK 1.7 ambiguity
370            return new FileLock((FileChannel) null, position, size, shared) {
371 
372                @Override
373                public boolean isValid() {
374                    return true;
375                }
376 
377                @Override
378                public void release() throws IOException {
379                    // ignore
380                }};
381        }
382        return null;
383    }
384 
385    @Override
386    protected void implCloseChannel() throws IOException {
387        if (in != null) {
388            in.close();
389            in = null;
390        }
391        file.close();
392    }
393 
394}

[all classes][org.h2.store.fs]
EMMA 2.0.5312 (C) Vladimir Roubtsov