EMMA Coverage Report (generated Mon Nov 01 16:48:29 PDT 2010)
[all classes][com.google.caja.ancillary.servlet]

COVERAGE SUMMARY FOR SOURCE FILE [ZipFileSystem.java]

nameclass, %method, %block, %line, %
ZipFileSystem.java100% (3/3)91%  (20/22)79%  (490/624)86%  (88.9/103)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class ZipFileSystem100% (1/1)89%  (16/18)77%  (440/574)85%  (80.9/95)
<static initializer> 100% (1/1)100% (6/6)100% (2/2)
ZipFileSystem (String): void 100% (1/1)89%  (40/45)89%  (8/9)
access$000 (ZipFileSystem): Map 100% (1/1)100% (3/3)100% (1/1)
basename (String): String 100% (1/1)100% (35/35)100% (5/5)
canonicalPath (String): String 100% (1/1)86%  (60/70)92%  (9.2/10)
dirname (String): String 100% (1/1)100% (41/41)100% (7/7)
exists (String): boolean 100% (1/1)77%  (10/13)81%  (1.6/2)
fileUriWithPath (String): URI 100% (1/1)60%  (9/15)33%  (1/3)
isDirectory (String): boolean 0%   (0/1)0%   (0/23)0%   (0/2)
isFile (String): boolean 0%   (0/1)0%   (0/18)0%   (0/2)
join (String, String): String 100% (1/1)79%  (34/43)75%  (3.8/5)
mkdir (String): void 100% (1/1)62%  (20/32)83%  (5/6)
read (String): CharProducer 100% (1/1)100% (25/25)100% (5/5)
requireParent (String): void 100% (1/1)57%  (16/28)75%  (3/4)
toInputSource (String): InputSource 100% (1/1)100% (6/6)100% (1/1)
toZip (): Job 100% (1/1)100% (75/75)100% (17/17)
write (String): Writer 100% (1/1)62%  (30/48)81%  (5.7/7)
writeBytes (String): OutputStream 100% (1/1)62%  (30/48)81%  (5.7/7)
     
class ZipFileSystem$1100% (1/1)100% (2/2)100% (25/25)100% (4/4)
ZipFileSystem$1 (ZipFileSystem, String): void 100% (1/1)100% (9/9)100% (1/1)
close (): void 100% (1/1)100% (16/16)100% (3/3)
     
class ZipFileSystem$2100% (1/1)100% (2/2)100% (25/25)100% (4/4)
ZipFileSystem$2 (ZipFileSystem, String): void 100% (1/1)100% (9/9)100% (1/1)
close (): void 100% (1/1)100% (16/16)100% (3/3)

1// Copyright (C) 2009 Google Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14 
15package com.google.caja.ancillary.servlet;
16 
17import com.google.caja.ancillary.jsdoc.FileSystem;
18import com.google.caja.lexer.CharProducer;
19import com.google.caja.lexer.InputSource;
20import com.google.caja.util.Maps;
21 
22import java.io.ByteArrayOutputStream;
23import java.io.FileNotFoundException;
24import java.io.IOException;
25import java.io.OutputStream;
26import java.io.StringWriter;
27import java.io.Writer;
28import java.net.URI;
29import java.net.URISyntaxException;
30import java.util.Map;
31import java.util.regex.Pattern;
32import java.util.zip.ZipEntry;
33import java.util.zip.ZipOutputStream;
34 
35/**
36 * A virtual file system backed by an in-memory ZIP file.
37 *
38 * @author mikesamuel@gmail.com
39 */
40class ZipFileSystem implements FileSystem {
41  private final String root;
42  private final URI rootUri;
43  private final Map<String, Content> files = Maps.newLinkedHashMap();
44 
45  private static final Pattern BAD_PATH = Pattern.compile(
46      "(?:^|/)(?:\\.\\.?)(?:/|$)");
47  private static final Content DIRECTORY = null;
48 
49  /**
50   * @param root an absolute directory under which all files must be organized.
51   *    E.g. {@code /foo}.
52   */
53  ZipFileSystem(String root) {
54    if (!root.startsWith("/") || root.endsWith("/")) {
55      throw new IllegalArgumentException(root);
56    }
57    this.root = root;
58    this.rootUri = fileUriWithPath(root + "/");
59    files.put("/", DIRECTORY);
60    files.put(root, DIRECTORY);
61  }
62  private static URI fileUriWithPath(String path) {
63    try {
64      return new URI("file", null, path, null, null);
65    } catch (URISyntaxException ex) {
66      throw new RuntimeException(ex);
67    }
68  }
69 
70  public String basename(String path) {
71    int end = path.length();
72    while (end > 0 && path.charAt(end - 1) == '/') { --end; }
73    if (end == 0 && path.length() != 0) { return "/"; }
74    int lastSlash = path.lastIndexOf('/', end - 1);
75    return path.substring(lastSlash + 1, end);
76  }
77 
78  public String canonicalPath(final String path) throws IOException {
79    URI canon = rootUri.resolve(path);
80    if (canon == null) { throw new IOException(path); }
81    String canonPath = canon.getPath();
82    if (BAD_PATH.matcher(canonPath).find()) { throw new IOException(path); }
83    canonPath = canonPath.replaceAll("/{2,}", "/");
84    if (canonPath.endsWith("/") && !"/".equals(canonPath)) {
85      canonPath = canonPath.substring(0, canonPath.length() - 1);
86    }
87    if (root.equals(canonPath) || canonPath.startsWith(root + "/")) {
88      return canonPath;
89    } else {
90      throw new IOException(path);
91    }
92  }
93 
94  public String dirname(String path) {
95    int end = path.length();
96    while (end > 0 && path.charAt(end - 1) == '/') { --end; }
97    if (end == 0 && path.length() != 0) { return "/"; }
98 
99    int lastSlash = path.lastIndexOf('/', end - 1);
100    if (lastSlash < 0) { return null; }
101    if (lastSlash == 0) { return "/"; }
102    return path.substring(0, lastSlash);
103  }
104 
105  public boolean exists(String path) {
106    try { path = canonicalPath(path); } catch (IOException ex) { return false; }
107    return files.containsKey(path);
108  }
109 
110  public boolean isDirectory(String path) {
111    try { path = canonicalPath(path); } catch (IOException ex) { return false; }
112    return files.containsKey(path) && DIRECTORY == files.get(path);
113  }
114 
115  public boolean isFile(String path) {
116    try { path = canonicalPath(path); } catch (IOException ex) { return false; }
117    return files.get(path) != DIRECTORY;
118  }
119 
120  public String join(String dir, String path) {
121    if (path == null) { throw new NullPointerException(); }
122    if (path.startsWith("/")) { throw new IllegalArgumentException(path); }
123    if (dir == null || "".equals(dir)) { return path; }
124    if ("".equals(path)) { return dir; }
125    return (dir + "/" + path).replace("//", "/");
126  }
127 
128  private void requireParent(String path) throws IOException {
129    String parent = dirname(path);
130    if (!(files.containsKey(parent) && files.get(parent) == DIRECTORY)) {
131      throw new IOException(parent + " is not a directory");
132    }
133  }
134 
135  public void mkdir(String path) throws IOException {
136    path = canonicalPath(path);
137    requireParent(path);
138    if (files.get(path) != DIRECTORY) {
139      throw new IOException(path + " is a file");
140    }
141    files.put(path, null);
142  }
143 
144  public CharProducer read(String path) throws IOException {
145    path = canonicalPath(path);
146    Content content = files.get(path);
147    if (content != DIRECTORY) {
148      return CharProducer.Factory.fromString(
149          content.getText(), toInputSource(path));
150    } else {
151      throw new FileNotFoundException(path);
152    }
153  }
154 
155  public InputSource toInputSource(String path) {
156    return new InputSource(fileUriWithPath(path));
157  }
158 
159  public Writer write(String path) throws IOException {
160    path = canonicalPath(path);
161    requireParent(path);
162    if (files.containsKey(path) && files.get(path) == DIRECTORY) {
163      throw new IOException(path + " is a directory");
164    }
165    files.put(path, new Content("", null));
166    final String outPath = path;
167    return new StringWriter() {
168      @Override
169      public void close() throws IOException {
170        super.close();
171        files.put(outPath, new Content(this.toString(), null));
172      }
173    };
174  }
175 
176  public OutputStream writeBytes(String path) throws IOException {
177    path = canonicalPath(path);
178    requireParent(path);
179    if (files.containsKey(path) && files.get(path) == DIRECTORY) {
180      throw new IOException(path + " is a directory");
181    }
182    files.put(path, new Content("", null));
183    final String outPath = path;
184    return new ByteArrayOutputStream() {
185      @Override
186      public void close() throws IOException {
187        super.close();
188        files.put(outPath, new Content(this.toByteArray(), null));
189      }
190    };
191  }
192 
193  public Job toZip() throws IOException {
194    ByteArrayOutputStream zippedBytes = new ByteArrayOutputStream();
195    ZipOutputStream zipOut = new ZipOutputStream(zippedBytes);
196    for (Map.Entry<String, Content> file : files.entrySet()) {
197      String path = file.getKey();
198      Content content = file.getValue();
199      if (content == DIRECTORY) {
200        if ("/".equals(path)) { continue; }
201        // Zip file format treats paths that end in "/" as dirs.
202        ZipEntry ze = new ZipEntry(path + "/");
203        zipOut.putNextEntry(ze);
204      } else {
205        ZipEntry ze = new ZipEntry(path);
206        ze.setSize(content.byteLength());
207        zipOut.putNextEntry(ze);
208        content.toOutputStream(zipOut);
209      }
210    }
211    zipOut.close();
212    return Job.zip(zippedBytes.toByteArray());
213  }
214}

[all classes][com.google.caja.ancillary.servlet]
EMMA 2.0.5312 (C) Vladimir Roubtsov