001 /* 002 * Copyright (C) 2012 eXo Platform SAS. 003 * 004 * This is free software; you can redistribute it and/or modify it 005 * under the terms of the GNU Lesser General Public License as 006 * published by the Free Software Foundation; either version 2.1 of 007 * the License, or (at your option) any later version. 008 * 009 * This software is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * You should have received a copy of the GNU Lesser General Public 015 * License along with this software; if not, write to the Free 016 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 017 * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 018 */ 019 020 package org.crsh.util; 021 022 import java.io.ByteArrayInputStream; 023 import java.io.ByteArrayOutputStream; 024 import java.io.Closeable; 025 import java.io.File; 026 import java.io.IOException; 027 import java.io.InputStream; 028 import java.net.URISyntaxException; 029 import java.net.URL; 030 import java.util.Enumeration; 031 import java.util.NoSuchElementException; 032 import java.util.zip.ZipEntry; 033 import java.util.zip.ZipFile; 034 import java.util.zip.ZipInputStream; 035 036 /** @author <a href="mailto:julien.viet@exoplatform.com">Julien Viet</a> */ 037 public abstract class ZipIterator implements Closeable { 038 039 public static ZipIterator create(URL url) throws IOException, URISyntaxException { 040 if (url.getProtocol().equals("file")) { 041 return create(Utils.toFile(url)); 042 } else if (url.getProtocol().equals("jar")) { 043 int pos = url.getPath().lastIndexOf("!/"); 044 URL jarURL = new URL(url.getPath().substring(0, pos)); 045 String path = url.getPath().substring(pos + 2); 046 final ZipIterator container = create(jarURL); 047 ZipIterator zip = null; 048 try { 049 while (container.hasNext()) { 050 ZipEntry entry = container.next(); 051 if (entry.getName().equals(path)) { 052 InputStreamFactory resolved = container.getStreamFactory(); 053 final InputStream nested = resolved.open(); 054 InputStream filter = new InputStream() { 055 @Override 056 public int read() throws IOException { 057 return nested.read(); 058 } 059 @Override 060 public int read(byte[] b) throws IOException { 061 return nested.read(b); 062 } 063 @Override 064 public int read(byte[] b, int off, int len) throws IOException { 065 return nested.read(b, off, len); 066 } 067 @Override 068 public long skip(long n) throws IOException { 069 return nested.skip(n); 070 } 071 @Override 072 public int available() throws IOException { 073 return nested.available(); 074 } 075 @Override 076 public void close() throws IOException { 077 Utils.close(nested); 078 Utils.close(container); 079 } 080 @Override 081 public synchronized void mark(int readlimit) { 082 nested.mark(readlimit); 083 } 084 @Override 085 public synchronized void reset() throws IOException { 086 nested.reset(); 087 } 088 @Override 089 public boolean markSupported() { 090 return nested.markSupported(); 091 } 092 }; 093 zip = create(filter); 094 break; 095 } 096 } 097 if (zip != null) { 098 return zip; 099 } else { 100 throw new IOException("Cannot resolve " + url); 101 } 102 } 103 finally { 104 // We close the container if we return nothing 105 // otherwise it will be the responsibility of the caller to close the zip 106 // with the wrapper that will close both the container and the nested zip 107 if (zip != null) { 108 Utils.close(container); 109 } 110 } 111 } else { 112 return create(url.openStream()); 113 } 114 } 115 116 static ZipIterator create(File file) throws IOException { 117 // The fast way (but that requires a File object) 118 final ZipFile jarFile = new ZipFile(file); 119 final Enumeration<? extends ZipEntry> en = jarFile.entries();en.hasMoreElements(); 120 return new ZipIterator() { 121 ZipEntry next; 122 @Override 123 public boolean hasNext() throws IOException { 124 return en.hasMoreElements(); 125 } 126 @Override 127 public ZipEntry next() throws IOException { 128 return next = en.nextElement(); 129 } 130 public void close() throws IOException { 131 } 132 @Override 133 public InputStreamFactory getStreamFactory() throws IOException { 134 final ZipEntry capture = next; 135 return new InputStreamFactory() { 136 public InputStream open() throws IOException { 137 return jarFile.getInputStream(capture); 138 } 139 }; 140 } 141 }; 142 } 143 144 static ZipIterator create(InputStream in) throws IOException { 145 final byte[] tmp = new byte[512]; 146 final ByteArrayOutputStream baos = new ByteArrayOutputStream(); 147 final ZipInputStream zip = new ZipInputStream(in); 148 return new ZipIterator() { 149 ZipEntry next; 150 public boolean hasNext() throws IOException { 151 if (next == null) { 152 next = zip.getNextEntry(); 153 } 154 return next != null; 155 } 156 public ZipEntry next() throws IOException { 157 if (!hasNext()) { 158 throw new NoSuchElementException(); 159 } 160 ZipEntry tmp = next; 161 next = null; 162 return tmp; 163 } 164 @Override 165 public InputStreamFactory getStreamFactory() throws IOException { 166 while (true) { 167 int len = zip.read(tmp, 0, tmp.length); 168 if (len == -1) { 169 break; 170 } else { 171 baos.write(tmp, 0, len); 172 } 173 } 174 final byte[] buffer = baos.toByteArray(); 175 baos.reset(); 176 return new InputStreamFactory() { 177 public InputStream open() throws IOException { 178 return new ByteArrayInputStream(buffer); 179 } 180 }; 181 } 182 public void close() throws IOException { 183 zip.close(); 184 } 185 }; 186 } 187 188 public abstract boolean hasNext() throws IOException; 189 190 public abstract ZipEntry next() throws IOException; 191 192 /** 193 * Return a stream factory for the current entry. 194 * 195 * @return the stream factory 196 * @throws IOException anything that would prevent to obtain a stream factory 197 */ 198 public abstract InputStreamFactory getStreamFactory() throws IOException; 199 200 }