001 /*
002 * $Id: ArchivEntry.java,v 1.7 2012/01/16 21:58:11 oboehm Exp $
003 *
004 * Copyright (c) 2008 by Oliver Boehm
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express orimplied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 *
018 * (c)reated 11-May-2009 by oliver (ob@oasd.de)
019 */
020 package patterntesting.runtime.util;
021
022 import java.io.*;
023 import java.net.*;
024 import java.util.Arrays;
025 import java.util.zip.*;
026
027 import org.apache.commons.io.*;
028 import org.slf4j.*;
029
030 /**
031 * Unfortunately we can't extends URI because this is a final class.
032 * So now it is more or less implemented as URI wrapper and is intended for
033 * the use with zip and jar files to describe an entry inside an archive.
034 * <br/>
035 * Historically some parts of this class were developed for a log browser
036 * for Log4J. The facility to read (compressed) tar files (using
037 * org.apache.commons.compress.tar.*) were removed because we use it here only
038 * for zip and jar files.
039 *
040 * @author oliver (ob@aosd.de)
041 * @since 20.09.2007
042 * @version $Revision: 1.7 $
043 */
044 public class ArchivEntry {
045
046 private static final Logger log = LoggerFactory.getLogger(ArchivEntry.class);
047 private File archiv = null;
048 private String entry = null;
049 private URI uri;
050 private Long size = null;
051
052 /**
053 * Instantiates a new archiv entry.
054 *
055 * @param file the file
056 */
057 protected ArchivEntry(final File file) {
058 this.archiv = file;
059 try {
060 archiv = archiv.getCanonicalFile();
061 } catch (IOException ioe) {
062 log.info(ioe + " ignored - using absolute file for " + archiv);
063 archiv = archiv.getAbsoluteFile();
064 }
065 uri = this.archiv.toURI();
066 }
067
068 /**
069 * Instantiates a new archiv entry.
070 *
071 * @param s the s
072 */
073 public ArchivEntry(final String s) {
074 try {
075 this.uri = new URI(s);
076 if (uri.getScheme() == null) {
077 throw new IllegalArgumentException('"' + s + "\" is not a valid URI (scheme missing)");
078 }
079 initArchiv();
080 } catch (URISyntaxException e) {
081 throw new IllegalArgumentException('"' + s + "\" is not a valid URI", e);
082 }
083 }
084
085 /**
086 * Instantiates a new archiv entry.
087 *
088 * @param uri the uri
089 */
090 public ArchivEntry(final URI uri) {
091 this.uri = uri;
092 initArchiv();
093 }
094
095 /**
096 * Instantiates a new archiv entry.
097 *
098 * @param url the url
099 */
100 public ArchivEntry(final URL url) {
101 this.uri = Converter.toURI(url);
102 initArchiv();
103 }
104
105 /**
106 * Instantiates a new archiv entry.
107 *
108 * @param scheme the scheme
109 * @param archive the archive
110 * @param entry the entry
111 *
112 * @throws URISyntaxException the URI syntax exception
113 */
114 public ArchivEntry(final String scheme, final File archive, final String entry) throws URISyntaxException {
115 this(archive);
116 uri = toURI(scheme, entry);
117 }
118
119 private void initArchiv() {
120 assert uri.getScheme() != null;
121 if (uri.getScheme().equalsIgnoreCase("file")) {
122 this.archiv = new File(uri);
123 this.entry = null;
124 } else {
125 String name = uri.toString().substring(4);
126 int n = name.lastIndexOf("!");
127 try {
128 URI fileURI = new URI(name.substring(0, n));
129 this.archiv = Converter.toFile(fileURI);
130 } catch (URISyntaxException e) {
131 this.archiv = new File(name.substring(5, n));
132 }
133 this.entry = name.substring(n+1);
134 if (this.entry.charAt(0) == '/') {
135 this.entry = this.entry.substring(1);
136 }
137 }
138 }
139
140 /**
141 * To uri.
142 *
143 * @return the uRI
144 */
145 public URI toURI() {
146 return this.uri;
147 }
148
149 private URI toURI(final String scheme, final String name) throws URISyntaxException {
150 String schemeURI = scheme + ":" + archiv.toURI().toString() + "!" + name;
151 return new URI(schemeURI).normalize();
152 }
153
154 /**
155 * Checks if is file.
156 *
157 * @return true, if is file
158 */
159 public boolean isFile() {
160 return this.entry == null;
161 }
162
163 /**
164 * Gets the file.
165 *
166 * @return the file
167 */
168 public File getFile() {
169 return this.archiv;
170 }
171
172 /**
173 * Gets the zip file.
174 *
175 * @return the zip file
176 *
177 * @throws IOException Signals that an I/O exception has occurred.
178 */
179 public ZipFile getZipFile() throws IOException {
180 try {
181 return new ZipFile(this.archiv);
182 } catch (IOException ioe) {
183 IOException betterException = new IOException("can't get zip file \"" + this.archiv + '"');
184 betterException.initCause(ioe);
185 throw betterException;
186 }
187 }
188
189 /**
190 * Gets the entry.
191 *
192 * @return the entry
193 */
194 public String getEntry() {
195 return this.entry;
196 }
197
198 /**
199 * Gets the zip entry.
200 *
201 * @return the zip entry
202 */
203 public ZipEntry getZipEntry() {
204 return new ZipEntry(this.entry);
205 }
206
207 /**
208 * Gets the size.
209 *
210 * @return the size
211 *
212 * @throws IOException Signals that an I/O exception has occurred.
213 */
214 public long getSize() throws IOException {
215 if (size == null) {
216 if (isFile()) {
217 size = this.archiv.length();
218 } else {
219 ZipFile zipFile = getZipFile();
220 ZipEntry zipEntry = zipFile.getEntry(this.entry);
221 size = zipEntry.getSize();
222 }
223 }
224 return size;
225 }
226
227 /**
228 * Gets the bytes.
229 *
230 * @return the bytes
231 *
232 * @throws IOException Signals that an I/O exception has occurred.
233 */
234 public byte[] getBytes() throws IOException {
235 if (isFile()) {
236 return FileUtils.readFileToByteArray(this.archiv);
237 } else {
238 ZipFile zipFile = getZipFile();
239 ZipEntry zipEntry = zipFile.getEntry(this.entry);
240 InputStream istream = zipFile.getInputStream(zipEntry);
241 byte[] bytes = IOUtils.toByteArray(istream);
242 istream.close();
243 return bytes;
244 }
245 }
246
247 /**
248 * Equals.
249 *
250 * @param other the other
251 * @return true, if successful
252 * @see java.lang.Object#equals(java.lang.Object)
253 */
254 @Override
255 public boolean equals(final Object other) {
256 if (other == null) {
257 return false;
258 }
259 try {
260 return equals((ArchivEntry) other);
261 } catch (ClassCastException e) {
262 return false;
263 }
264 }
265
266 /**
267 * If two entries have a different size or not the same byte code they are
268 * considered as not equal.
269 *
270 * @param other the other
271 *
272 * @return true if they have the same size and the same byte code.
273 */
274 public boolean equals(final ArchivEntry other) {
275 try {
276 if (this.getSize() != other.getSize()) {
277 return false;
278 }
279 return Arrays.equals(this.getBytes(), other.getBytes());
280 } catch (IOException ioe) {
281 log.debug("can't compare " + this + " with " + other, ioe);
282 return false;
283 }
284 }
285
286 /**
287 * Hash code.
288 *
289 * @return the hash code
290 * @see java.lang.Object#hashCode()
291 */
292 @Override
293 public int hashCode() {
294 if (this.size == null) {
295 return 0;
296 } else {
297 return this.size.hashCode();
298 }
299 }
300
301 /**
302 * To string.
303 *
304 * @return the string
305 * @see java.lang.Object#toString()
306 */
307 @Override
308 public String toString() {
309 return "ArchivEntry " + this.uri;
310 }
311
312 }