/* * Copyright 2013 eXo Platform SAS * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package juzu.impl.fs.spi.jar; import juzu.impl.common.Resource; import juzu.impl.common.Spliterator; import juzu.io.UndeclaredIOException; import juzu.impl.common.Timestamped; import juzu.impl.common.Tools; import juzu.impl.fs.spi.PathType; import juzu.impl.fs.spi.ReadFileSystem; import java.io.File; import java.io.IOException; import java.net.URL; import java.net.URLConnection; import java.nio.charset.Charset; import java.util.Enumeration; import java.util.Iterator; import java.util.NoSuchElementException; import java.util.TreeMap; import java.util.jar.JarFile; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; /** @author <a href="mailto:julien.viet@exoplatform.com">Julien Viet</a> */ public class JarFileSystem extends ReadFileSystem<String> { /** . */ private final URL baseURL; /** . */ private final TreeMap<String, ZipEntry> entries; public JarFileSystem(JarFile f) throws IOException { TreeMap<String, ZipEntry> entries = new TreeMap<String, ZipEntry>(); for (Enumeration<? extends ZipEntry> en = f.entries();en.hasMoreElements();) { ZipEntry entry = en.nextElement(); String name = entry.getName(); if (name.length() > 0 && name.charAt(name.length() - 1) != '/') { entries.put(name, entry); String current = name; while (true) { int index = current.lastIndexOf('/'); if (index == -1) { break; } else { current = name.substring(0, index); if (entries.containsKey(current)) { break; } else { entries.put(current, null); } } } } } this.baseURL = new File(f.getName()).toURI().toURL(); this.entries = entries; } public JarFileSystem(URL baseURL) throws IOException { final ZipInputStream in = new ZipInputStream(baseURL.openStream()); try { this.baseURL = baseURL; this.entries = entries(new Enumeration<ZipEntry>() { ZipEntry next; public boolean hasMoreElements() { try { if (next == null) { next = in.getNextEntry(); } return next != null; } catch (IOException e) { throw new UndeclaredIOException(e); } } public ZipEntry nextElement() { if (!hasMoreElements()) { throw new NoSuchElementException(); } ZipEntry tmp = next; next = null; return tmp; } }); } catch (UndeclaredIOException e) { throw e.getCause(); } finally { Tools.safeClose(in); } } private TreeMap<String, ZipEntry> entries(Enumeration<ZipEntry> e) { TreeMap<String, ZipEntry> entries = new TreeMap<String, ZipEntry>(); while (e.hasMoreElements()) { ZipEntry entry = e.nextElement(); String name = entry.getName(); if (name.length() > 0 && name.charAt(name.length() - 1) != '/') { entries.put(name, entry); String current = name; while (true) { int index = current.lastIndexOf('/'); if (index == -1) { break; } else { current = name.substring(0, index); if (entries.containsKey(current)) { break; } else { entries.put(current, null); } } } } } return entries; } @Override public boolean equals(String left, String right) { return left.equals(right); } @Override public String getRoot() throws IOException { return ""; } @Override public String getChild(String dir, String name) throws IOException { String key = dir + name; if (entries.containsKey(key)) { if (entries.get(key) != null) { return key; } else { return key + "/"; } } else { return null; } } @Override public long getLastModified(String path) throws IOException { if (path.isEmpty()) { return 1; } else { ZipEntry entry = entries.get(path); return entry.getTime(); } } @Override public Class<String> getType() { return String.class; } @Override public String getDescription() { return ""; } @Override public String getName(String path) { if (path.isEmpty()) { return ""; } else { int from = path.length(); if (path.length() > 0 && path.charAt(path.length() - 1) == '/') { from--; } int index = path.lastIndexOf('/', from - 1); return index == -1 ? path.substring(0, from) : path.substring(index + 1, from); } } @Override public Iterable<String> getNames(String path) { return Spliterator.split(path, '/'); } @Override public Iterator<String> getChildren(final String dir) throws IOException { final Iterator<String> i = entries.navigableKeySet().tailSet(dir, false).iterator(); return new Iterator<String>() { String next; public boolean hasNext() { if (next == null) { while (i.hasNext()) { String next = i.next(); if (next.startsWith(dir)) { int pos = next.lastIndexOf('/'); if (pos == -1) { if (dir.isEmpty()) { this.next = next; break; } } else { if (pos == dir.length() - 1) { this.next = next; break; } } } else { break; } } } return next != null; } public String next() { if (hasNext()) { String ret; if (entries.get(next) != null) { ret = next; } else { ret = next + "/"; } next = null; return ret; } else { throw new NoSuchElementException(); } } public void remove() { throw new UnsupportedOperationException(); } }; } @Override public PathType typeOf(String path) throws IOException { if (path.isEmpty() || path.length() > 0 && path.charAt(path.length() - 1) == '/') { return PathType.DIR; } else { return PathType.FILE; } } @Override public Timestamped<Resource> getResource(String file) throws IOException { URL url = getURL(file); URLConnection conn = url.openConnection(); long lastModified = conn.getLastModified(); byte[] bytes = Tools.bytes(conn.getInputStream()); return new Timestamped<Resource>(lastModified, new Resource(bytes, Charset.defaultCharset())); } @Override public File getFile(String path) { return null; } @Override public URL getURL(String path) throws NullPointerException, IOException { return new URL("jar:" + baseURL + "!/" + path); } }