blob: 46bec891b7ce28ca5aadd52e90f9f047f7435a00 [file] [log] [blame]
Torsten Curdtca165392008-07-10 10:17:44 +00001/*
Torsten Curdtab9ebfc2009-01-12 11:15:34 +00002 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
Torsten Curdtca165392008-07-10 10:17:44 +00008 *
Torsten Curdtab9ebfc2009-01-12 11:15:34 +00009 * http://www.apache.org/licenses/LICENSE-2.0
Torsten Curdtca165392008-07-10 10:17:44 +000010 *
Torsten Curdtab9ebfc2009-01-12 11:15:34 +000011 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
Torsten Curdtca165392008-07-10 10:17:44 +000017 */
18package org.apache.commons.compress.archivers.zip;
19
Julius Davies11209142013-01-14 21:42:48 +000020import org.apache.commons.compress.archivers.ArchiveEntry;
Julius Davies11209142013-01-14 21:42:48 +000021
Sebastian Bazleyfec51a12009-03-31 00:35:56 +000022import java.io.File;
Stefan Bodewig91695e42010-02-16 13:59:03 +000023import java.util.ArrayList;
Stefan Bodewig7be97962010-01-26 05:24:17 +000024import java.util.Arrays;
Stefan Bodewig17ffd7f2009-08-01 14:52:15 +000025import java.util.Date;
Stefan Bodewig2ae5e282009-02-04 08:45:31 +000026import java.util.LinkedHashMap;
Stefan Bodewig91695e42010-02-16 13:59:03 +000027import java.util.List;
Torsten Curdtca165392008-07-10 10:17:44 +000028import java.util.zip.ZipException;
29
30/**
Torsten Curdtab9ebfc2009-01-12 11:15:34 +000031 * Extension that adds better handling of extra fields and provides
32 * access to the internal and external file attributes.
33 *
Stefan Bodewig91695e42010-02-16 13:59:03 +000034 * <p>The extra data is expected to follow the recommendation of
Sebastian Bazley75ecf4f2010-03-13 13:33:38 +000035 * {@link <a href="http://www.pkware.com/documents/casestudies/APPNOTE.TXT">
36 * APPNOTE.txt</a>}:</p>
Stefan Bodewig91695e42010-02-16 13:59:03 +000037 * <ul>
38 * <li>the extra byte array consists of a sequence of extra fields</li>
39 * <li>each extra fields starts by a two byte header id followed by
40 * a two byte sequence holding the length of the remainder of
41 * data.</li>
42 * </ul>
43 *
44 * <p>Any extra data that cannot be parsed by the rules above will be
45 * consumed as "unparseable" extra data and treated differently by the
46 * methods of this class. Versions prior to Apache Commons Compress
47 * 1.1 would have thrown an exception if any attempt was made to read
48 * or write extra data not conforming to the recommendation.</p>
49 *
Sebastian Bazley99870ef2009-03-28 00:04:36 +000050 * @NotThreadSafe
Torsten Curdtca165392008-07-10 10:17:44 +000051 */
Stefan Bodewig463d6eb2009-02-06 17:11:40 +000052public class ZipArchiveEntry extends java.util.zip.ZipEntry
Gary D. Gregory9f0db872012-04-01 13:14:28 +000053 implements ArchiveEntry {
Torsten Curdtca165392008-07-10 10:17:44 +000054
Torsten Curdtab9ebfc2009-01-12 11:15:34 +000055 public static final int PLATFORM_UNIX = 3;
56 public static final int PLATFORM_FAT = 0;
57 private static final int SHORT_MASK = 0xFFFF;
58 private static final int SHORT_SHIFT = 16;
Stefan Bodewiga2f978e2013-01-05 19:28:42 +000059 private static final byte[] EMPTY = new byte[0];
Torsten Curdtca165392008-07-10 10:17:44 +000060
Jukka Zitting7b125a32009-12-13 18:31:55 +000061 /**
62 * The {@link java.util.zip.ZipEntry} base class only supports
63 * the compression methods STORED and DEFLATED. We override the
64 * field so that any compression methods can be used.
65 * <p>
66 * The default value -1 means that the method has not been specified.
67 *
68 * @see <a href="https://issues.apache.org/jira/browse/COMPRESS-93"
69 * >COMPRESS-93</a>
70 */
71 private int method = -1;
72
Stefan Bodewigd7d90da2011-07-21 13:18:36 +000073 /**
74 * The {@link java.util.zip.ZipEntry#setSize} method in the base
75 * class throws an IllegalArgumentException if the size is bigger
76 * than 2GB for Java versions < 7. Need to keep our own size
77 * information for Zip64 support.
78 */
79 private long size = SIZE_UNKNOWN;
80
Torsten Curdtab9ebfc2009-01-12 11:15:34 +000081 private int internalAttributes = 0;
82 private int platform = PLATFORM_FAT;
83 private long externalAttributes = 0;
Stefan Bodewige53e88a2011-07-25 13:28:31 +000084 private LinkedHashMap<ZipShort, ZipExtraField> extraFields = null;
Stefan Bodewig91695e42010-02-16 13:59:03 +000085 private UnparseableExtraFieldData unparseableExtra = null;
Torsten Curdtab9ebfc2009-01-12 11:15:34 +000086 private String name = null;
Stefan Bodewig934a96e2011-07-23 04:33:44 +000087 private byte[] rawName = null;
Stefan Bodewig62cb9c42010-03-12 16:06:20 +000088 private GeneralPurposeBit gpb = new GeneralPurposeBit();
Torsten Curdtca165392008-07-10 10:17:44 +000089
90 /**
91 * Creates a new zip entry with the specified name.
Stefan Bodewig9b4b4772010-04-19 15:18:05 +000092 *
93 * <p>Assumes the entry represents a directory if and only if the
94 * name ends with a forward slash "/".</p>
95 *
Torsten Curdtab9ebfc2009-01-12 11:15:34 +000096 * @param name the name of the entry
Torsten Curdtca165392008-07-10 10:17:44 +000097 */
Stefan Bodewig463d6eb2009-02-06 17:11:40 +000098 public ZipArchiveEntry(String name) {
Torsten Curdtab9ebfc2009-01-12 11:15:34 +000099 super(name);
Stefan Bodewig5066e9a2010-01-07 09:34:55 +0000100 setName(name);
Torsten Curdtca165392008-07-10 10:17:44 +0000101 }
102
103 /**
104 * Creates a new zip entry with fields taken from the specified zip entry.
Stefan Bodewig9b4b4772010-04-19 15:18:05 +0000105 *
106 * <p>Assumes the entry represents a directory if and only if the
107 * name ends with a forward slash "/".</p>
108 *
Torsten Curdtab9ebfc2009-01-12 11:15:34 +0000109 * @param entry the entry to get fields from
Torsten Curdtab9ebfc2009-01-12 11:15:34 +0000110 * @throws ZipException on error
Torsten Curdtca165392008-07-10 10:17:44 +0000111 */
Stefan Bodewig463d6eb2009-02-06 17:11:40 +0000112 public ZipArchiveEntry(java.util.zip.ZipEntry entry) throws ZipException {
Torsten Curdtab9ebfc2009-01-12 11:15:34 +0000113 super(entry);
Stefan Bodewig008ca942009-03-26 22:29:59 +0000114 setName(entry.getName());
Torsten Curdtab9ebfc2009-01-12 11:15:34 +0000115 byte[] extra = entry.getExtra();
116 if (extra != null) {
Stefan Bodewig91695e42010-02-16 13:59:03 +0000117 setExtraFields(ExtraFieldUtils.parse(extra, true,
118 ExtraFieldUtils
119 .UnparseableExtraField.READ));
Torsten Curdtab9ebfc2009-01-12 11:15:34 +0000120 } else {
Torsten Curdtca165392008-07-10 10:17:44 +0000121 // initializes extra data to an empty byte array
122 setExtra();
123 }
Jukka Zitting7b125a32009-12-13 18:31:55 +0000124 setMethod(entry.getMethod());
Stefan Bodewigd7d90da2011-07-21 13:18:36 +0000125 this.size = entry.getSize();
Torsten Curdtca165392008-07-10 10:17:44 +0000126 }
127
128 /**
129 * Creates a new zip entry with fields taken from the specified zip entry.
Stefan Bodewig9b4b4772010-04-19 15:18:05 +0000130 *
131 * <p>Assumes the entry represents a directory if and only if the
132 * name ends with a forward slash "/".</p>
133 *
Torsten Curdtab9ebfc2009-01-12 11:15:34 +0000134 * @param entry the entry to get fields from
135 * @throws ZipException on error
Torsten Curdtca165392008-07-10 10:17:44 +0000136 */
Stefan Bodewig463d6eb2009-02-06 17:11:40 +0000137 public ZipArchiveEntry(ZipArchiveEntry entry) throws ZipException {
Torsten Curdtab9ebfc2009-01-12 11:15:34 +0000138 this((java.util.zip.ZipEntry) entry);
139 setInternalAttributes(entry.getInternalAttributes());
140 setExternalAttributes(entry.getExternalAttributes());
Stefan Bodewig91695e42010-02-16 13:59:03 +0000141 setExtraFields(entry.getExtraFields(true));
Torsten Curdtca165392008-07-10 10:17:44 +0000142 }
143
144 /**
Torsten Curdtca165392008-07-10 10:17:44 +0000145 */
Stefan Bodewig463d6eb2009-02-06 17:11:40 +0000146 protected ZipArchiveEntry() {
Stefan Bodewig5066e9a2010-01-07 09:34:55 +0000147 this("");
Torsten Curdtca165392008-07-10 10:17:44 +0000148 }
149
Stefan Bodewig9b4b4772010-04-19 15:18:05 +0000150 /**
151 * Creates a new zip entry taking some information from the given
152 * file and using the provided name.
153 *
154 * <p>The name will be adjusted to end with a forward slash "/" if
155 * the file is a directory. If the file is not a directory a
156 * potential trailing forward slash will be stripped from the
157 * entry name.</p>
158 */
Sebastian Bazleyfec51a12009-03-31 00:35:56 +0000159 public ZipArchiveEntry(File inputFile, String entryName) {
Stefan Bodewiga1807782009-08-01 19:17:58 +0000160 this(inputFile.isDirectory() && !entryName.endsWith("/") ?
161 entryName + "/" : entryName);
Sebastian Bazley2de1b502009-03-31 13:01:25 +0000162 if (inputFile.isFile()){
163 setSize(inputFile.length());
164 }
Sebastian Bazleye46c02b2009-03-31 10:35:53 +0000165 setTime(inputFile.lastModified());
Sebastian Bazleyfec51a12009-03-31 00:35:56 +0000166 // TODO are there any other fields we can set here?
167 }
168
Torsten Curdtca165392008-07-10 10:17:44 +0000169 /**
Torsten Curdtab9ebfc2009-01-12 11:15:34 +0000170 * Overwrite clone.
Stefan Bodewig463d6eb2009-02-06 17:11:40 +0000171 * @return a cloned copy of this ZipArchiveEntry
Torsten Curdtca165392008-07-10 10:17:44 +0000172 */
Stefan Bodewige53e88a2011-07-25 13:28:31 +0000173 @Override
Torsten Curdtab9ebfc2009-01-12 11:15:34 +0000174 public Object clone() {
Stefan Bodewig463d6eb2009-02-06 17:11:40 +0000175 ZipArchiveEntry e = (ZipArchiveEntry) super.clone();
Torsten Curdtca165392008-07-10 10:17:44 +0000176
Torsten Curdtab9ebfc2009-01-12 11:15:34 +0000177 e.setInternalAttributes(getInternalAttributes());
178 e.setExternalAttributes(getExternalAttributes());
Stefan Bodewig91695e42010-02-16 13:59:03 +0000179 e.setExtraFields(getExtraFields(true));
Torsten Curdtab9ebfc2009-01-12 11:15:34 +0000180 return e;
Torsten Curdtca165392008-07-10 10:17:44 +0000181 }
182
183 /**
Jukka Zitting7b125a32009-12-13 18:31:55 +0000184 * Returns the compression method of this entry, or -1 if the
185 * compression method has not been specified.
186 *
187 * @return compression method
Sebastian Bazley6209f812010-05-10 17:36:40 +0000188 *
Gary D. Gregory2bd0dd42012-04-01 13:02:39 +0000189 * @since 1.1
Jukka Zitting7b125a32009-12-13 18:31:55 +0000190 */
Sebastian Bazleyfb06e3a2011-08-15 15:10:46 +0000191 @Override
Jukka Zitting7b125a32009-12-13 18:31:55 +0000192 public int getMethod() {
193 return method;
194 }
195
196 /**
197 * Sets the compression method of this entry.
198 *
199 * @param method compression method
Sebastian Bazley6209f812010-05-10 17:36:40 +0000200 *
Gary D. Gregory2bd0dd42012-04-01 13:02:39 +0000201 * @since 1.1
Jukka Zitting7b125a32009-12-13 18:31:55 +0000202 */
Sebastian Bazleyfb06e3a2011-08-15 15:10:46 +0000203 @Override
Jukka Zitting7b125a32009-12-13 18:31:55 +0000204 public void setMethod(int method) {
205 if (method < 0) {
206 throw new IllegalArgumentException(
207 "ZIP compression method can not be negative: " + method);
208 }
209 this.method = method;
210 }
211
212 /**
Torsten Curdtca165392008-07-10 10:17:44 +0000213 * Retrieves the internal file attributes.
214 *
Torsten Curdtab9ebfc2009-01-12 11:15:34 +0000215 * @return the internal file attributes
Torsten Curdtca165392008-07-10 10:17:44 +0000216 */
Torsten Curdtab9ebfc2009-01-12 11:15:34 +0000217 public int getInternalAttributes() {
218 return internalAttributes;
Torsten Curdtca165392008-07-10 10:17:44 +0000219 }
220
221 /**
Torsten Curdtab9ebfc2009-01-12 11:15:34 +0000222 * Sets the internal file attributes.
223 * @param value an <code>int</code> value
Torsten Curdtca165392008-07-10 10:17:44 +0000224 */
Torsten Curdtab9ebfc2009-01-12 11:15:34 +0000225 public void setInternalAttributes(int value) {
226 internalAttributes = value;
Torsten Curdtca165392008-07-10 10:17:44 +0000227 }
228
229 /**
Torsten Curdtab9ebfc2009-01-12 11:15:34 +0000230 * Retrieves the external file attributes.
231 * @return the external file attributes
Torsten Curdtca165392008-07-10 10:17:44 +0000232 */
Torsten Curdtab9ebfc2009-01-12 11:15:34 +0000233 public long getExternalAttributes() {
234 return externalAttributes;
235 }
236
237 /**
238 * Sets the external file attributes.
239 * @param value an <code>long</code> value
Torsten Curdtab9ebfc2009-01-12 11:15:34 +0000240 */
241 public void setExternalAttributes(long value) {
242 externalAttributes = value;
243 }
244
245 /**
246 * Sets Unix permissions in a way that is understood by Info-Zip's
247 * unzip command.
248 * @param mode an <code>int</code> value
Torsten Curdtab9ebfc2009-01-12 11:15:34 +0000249 */
250 public void setUnixMode(int mode) {
251 // CheckStyle:MagicNumberCheck OFF - no point
252 setExternalAttributes((mode << SHORT_SHIFT)
253 // MS-DOS read-only attribute
254 | ((mode & 0200) == 0 ? 1 : 0)
255 // MS-DOS directory flag
256 | (isDirectory() ? 0x10 : 0));
257 // CheckStyle:MagicNumberCheck ON
258 platform = PLATFORM_UNIX;
259 }
260
261 /**
262 * Unix permission.
263 * @return the unix permissions
Torsten Curdtab9ebfc2009-01-12 11:15:34 +0000264 */
265 public int getUnixMode() {
266 return platform != PLATFORM_UNIX ? 0 :
267 (int) ((getExternalAttributes() >> SHORT_SHIFT) & SHORT_MASK);
268 }
269
270 /**
Julius Davies11209142013-01-14 21:42:48 +0000271 * Returns true if this entry represents a unix symlink,
272 * in which case the entry's content contains the target path
273 * for the symlink.
274 *
Stefan Bodewigc1fd3172013-03-10 16:37:16 +0000275 * @since 1.5
Julius Davies11209142013-01-14 21:42:48 +0000276 * @return true if the entry represents a unix symlink, false otherwise.
277 */
278 public boolean isUnixSymlink() {
279 return (getUnixMode() & UnixStat.LINK_FLAG) == UnixStat.LINK_FLAG;
280 }
281
282 /**
Torsten Curdtab9ebfc2009-01-12 11:15:34 +0000283 * Platform specification to put into the &quot;version made
284 * by&quot; part of the central file header.
285 *
286 * @return PLATFORM_FAT unless {@link #setUnixMode setUnixMode}
Stefan Bodewig306c3c82012-06-04 04:43:01 +0000287 * has been called, in which case PLATFORM_UNIX will be returned.
Torsten Curdtab9ebfc2009-01-12 11:15:34 +0000288 */
289 public int getPlatform() {
290 return platform;
291 }
292
293 /**
294 * Set the platform (UNIX or FAT).
295 * @param platform an <code>int</code> value - 0 is FAT, 3 is UNIX
Torsten Curdtab9ebfc2009-01-12 11:15:34 +0000296 */
297 protected void setPlatform(int platform) {
298 this.platform = platform;
299 }
300
301 /**
302 * Replaces all currently attached extra fields with the new array.
303 * @param fields an array of extra fields
Torsten Curdtab9ebfc2009-01-12 11:15:34 +0000304 */
305 public void setExtraFields(ZipExtraField[] fields) {
Stefan Bodewige53e88a2011-07-25 13:28:31 +0000306 extraFields = new LinkedHashMap<ZipShort, ZipExtraField>();
Gary D. Gregoryb2a9dd02012-04-01 13:19:03 +0000307 for (ZipExtraField field : fields) {
308 if (field instanceof UnparseableExtraFieldData) {
309 unparseableExtra = (UnparseableExtraFieldData) field;
Stefan Bodewig91695e42010-02-16 13:59:03 +0000310 } else {
Gary D. Gregoryb2a9dd02012-04-01 13:19:03 +0000311 extraFields.put(field.getHeaderId(), field);
Stefan Bodewig91695e42010-02-16 13:59:03 +0000312 }
Torsten Curdtca165392008-07-10 10:17:44 +0000313 }
314 setExtra();
315 }
316
317 /**
Stefan Bodewig91695e42010-02-16 13:59:03 +0000318 * Retrieves all extra fields that have been parsed successfully.
Torsten Curdtab9ebfc2009-01-12 11:15:34 +0000319 * @return an array of the extra fields
Torsten Curdtca165392008-07-10 10:17:44 +0000320 */
Torsten Curdtab9ebfc2009-01-12 11:15:34 +0000321 public ZipExtraField[] getExtraFields() {
Stefan Bodewig91695e42010-02-16 13:59:03 +0000322 return getExtraFields(false);
Torsten Curdtab9ebfc2009-01-12 11:15:34 +0000323 }
Torsten Curdtca165392008-07-10 10:17:44 +0000324
Torsten Curdtab9ebfc2009-01-12 11:15:34 +0000325 /**
Stefan Bodewig91695e42010-02-16 13:59:03 +0000326 * Retrieves extra fields.
327 * @param includeUnparseable whether to also return unparseable
328 * extra fields as {@link UnparseableExtraFieldData} if such data
329 * exists.
330 * @return an array of the extra fields
Sebastian Bazley6209f812010-05-10 17:36:40 +0000331 *
Gary D. Gregory2bd0dd42012-04-01 13:02:39 +0000332 * @since 1.1
Stefan Bodewig91695e42010-02-16 13:59:03 +0000333 */
334 public ZipExtraField[] getExtraFields(boolean includeUnparseable) {
335 if (extraFields == null) {
336 return !includeUnparseable || unparseableExtra == null
337 ? new ZipExtraField[0]
338 : new ZipExtraField[] { unparseableExtra };
339 }
Stefan Bodewige53e88a2011-07-25 13:28:31 +0000340 List<ZipExtraField> result =
341 new ArrayList<ZipExtraField>(extraFields.values());
Stefan Bodewig91695e42010-02-16 13:59:03 +0000342 if (includeUnparseable && unparseableExtra != null) {
343 result.add(unparseableExtra);
344 }
Stefan Bodewige53e88a2011-07-25 13:28:31 +0000345 return result.toArray(new ZipExtraField[0]);
Stefan Bodewig91695e42010-02-16 13:59:03 +0000346 }
347
348 /**
349 * Adds an extra field - replacing an already present extra field
Torsten Curdtab9ebfc2009-01-12 11:15:34 +0000350 * of the same type.
Stefan Bodewig1903db42009-02-18 15:52:42 +0000351 *
352 * <p>If no extra field of the same type exists, the field will be
353 * added as last field.</p>
Torsten Curdtab9ebfc2009-01-12 11:15:34 +0000354 * @param ze an extra field
Torsten Curdtab9ebfc2009-01-12 11:15:34 +0000355 */
356 public void addExtraField(ZipExtraField ze) {
Stefan Bodewig91695e42010-02-16 13:59:03 +0000357 if (ze instanceof UnparseableExtraFieldData) {
358 unparseableExtra = (UnparseableExtraFieldData) ze;
359 } else {
Stefan Bodewig3c5ab4f2010-02-16 14:00:56 +0000360 if (extraFields == null) {
Stefan Bodewige53e88a2011-07-25 13:28:31 +0000361 extraFields = new LinkedHashMap<ZipShort, ZipExtraField>();
Stefan Bodewig3c5ab4f2010-02-16 14:00:56 +0000362 }
363 extraFields.put(ze.getHeaderId(), ze);
Stefan Bodewig91695e42010-02-16 13:59:03 +0000364 }
Torsten Curdtab9ebfc2009-01-12 11:15:34 +0000365 setExtra();
Torsten Curdtca165392008-07-10 10:17:44 +0000366 }
367
368 /**
Stefan Bodewig91695e42010-02-16 13:59:03 +0000369 * Adds an extra field - replacing an already present extra field
Stefan Bodewig1903db42009-02-18 15:52:42 +0000370 * of the same type.
371 *
372 * <p>The new extra field will be the first one.</p>
373 * @param ze an extra field
Stefan Bodewig1903db42009-02-18 15:52:42 +0000374 */
375 public void addAsFirstExtraField(ZipExtraField ze) {
Stefan Bodewig91695e42010-02-16 13:59:03 +0000376 if (ze instanceof UnparseableExtraFieldData) {
377 unparseableExtra = (UnparseableExtraFieldData) ze;
378 } else {
Stefan Bodewig3553cda2011-08-06 13:01:16 +0000379 LinkedHashMap<ZipShort, ZipExtraField> copy = extraFields;
Stefan Bodewige53e88a2011-07-25 13:28:31 +0000380 extraFields = new LinkedHashMap<ZipShort, ZipExtraField>();
Stefan Bodewig3c5ab4f2010-02-16 14:00:56 +0000381 extraFields.put(ze.getHeaderId(), ze);
382 if (copy != null) {
383 copy.remove(ze.getHeaderId());
384 extraFields.putAll(copy);
385 }
Stefan Bodewig91695e42010-02-16 13:59:03 +0000386 }
Stefan Bodewig1903db42009-02-18 15:52:42 +0000387 setExtra();
388 }
389
390 /**
Stefan Bodewig91695e42010-02-16 13:59:03 +0000391 * Remove an extra field.
Torsten Curdtab9ebfc2009-01-12 11:15:34 +0000392 * @param type the type of extra field to remove
Torsten Curdtca165392008-07-10 10:17:44 +0000393 */
Torsten Curdtab9ebfc2009-01-12 11:15:34 +0000394 public void removeExtraField(ZipShort type) {
395 if (extraFields == null) {
Stefan Bodewig2ae5e282009-02-04 08:45:31 +0000396 throw new java.util.NoSuchElementException();
Torsten Curdtab9ebfc2009-01-12 11:15:34 +0000397 }
Stefan Bodewig2ae5e282009-02-04 08:45:31 +0000398 if (extraFields.remove(type) == null) {
Torsten Curdtca165392008-07-10 10:17:44 +0000399 throw new java.util.NoSuchElementException();
400 }
401 setExtra();
402 }
403
404 /**
Stefan Bodewig91695e42010-02-16 13:59:03 +0000405 * Removes unparseable extra field data.
Sebastian Bazley6209f812010-05-10 17:36:40 +0000406 *
Gary D. Gregory2bd0dd42012-04-01 13:02:39 +0000407 * @since 1.1
Stefan Bodewig91695e42010-02-16 13:59:03 +0000408 */
409 public void removeUnparseableExtraFieldData() {
410 if (unparseableExtra == null) {
411 throw new java.util.NoSuchElementException();
412 }
413 unparseableExtra = null;
414 setExtra();
415 }
416
417 /**
Stefan Bodewig2b5fb912009-02-25 16:29:00 +0000418 * Looks up an extra field by its header id.
419 *
420 * @return null if no such field exists.
421 */
422 public ZipExtraField getExtraField(ZipShort type) {
423 if (extraFields != null) {
Stefan Bodewige53e88a2011-07-25 13:28:31 +0000424 return extraFields.get(type);
Stefan Bodewig2b5fb912009-02-25 16:29:00 +0000425 }
426 return null;
427 }
428
429 /**
Stefan Bodewig91695e42010-02-16 13:59:03 +0000430 * Looks up extra field data that couldn't be parsed correctly.
431 *
432 * @return null if no such field exists.
Sebastian Bazley6209f812010-05-10 17:36:40 +0000433 *
Gary D. Gregory2bd0dd42012-04-01 13:02:39 +0000434 * @since 1.1
Stefan Bodewig91695e42010-02-16 13:59:03 +0000435 */
436 public UnparseableExtraFieldData getUnparseableExtraFieldData() {
437 return unparseableExtra;
438 }
439
440 /**
441 * Parses the given bytes as extra field data and consumes any
442 * unparseable data as an {@link UnparseableExtraFieldData}
443 * instance.
Torsten Curdtab9ebfc2009-01-12 11:15:34 +0000444 * @param extra an array of bytes to be parsed into extra fields
445 * @throws RuntimeException if the bytes cannot be parsed
Torsten Curdtab9ebfc2009-01-12 11:15:34 +0000446 * @throws RuntimeException on error
447 */
Stefan Bodewige53e88a2011-07-25 13:28:31 +0000448 @Override
Torsten Curdtab9ebfc2009-01-12 11:15:34 +0000449 public void setExtra(byte[] extra) throws RuntimeException {
450 try {
Stefan Bodewig91695e42010-02-16 13:59:03 +0000451 ZipExtraField[] local =
452 ExtraFieldUtils.parse(extra, true,
453 ExtraFieldUtils.UnparseableExtraField.READ);
Stefan Bodewigdfbdfee2009-03-03 12:42:28 +0000454 mergeExtraFields(local, true);
Christian Grobmeierbcebf922009-04-27 17:22:14 +0000455 } catch (ZipException e) {
Stefan Bodewige53e88a2011-07-25 13:28:31 +0000456 // actually this is not possible as of Commons Compress 1.1
Stefan Bodewig25b59562010-02-12 11:11:34 +0000457 throw new RuntimeException("Error parsing extra fields for entry: "
458 + getName() + " - " + e.getMessage(), e);
Torsten Curdtab9ebfc2009-01-12 11:15:34 +0000459 }
460 }
461
462 /**
Torsten Curdtca165392008-07-10 10:17:44 +0000463 * Unfortunately {@link java.util.zip.ZipOutputStream
Torsten Curdtab9ebfc2009-01-12 11:15:34 +0000464 * java.util.zip.ZipOutputStream} seems to access the extra data
465 * directly, so overriding getExtra doesn't help - we need to
466 * modify super's data directly.
Torsten Curdtca165392008-07-10 10:17:44 +0000467 */
Torsten Curdtab9ebfc2009-01-12 11:15:34 +0000468 protected void setExtra() {
Stefan Bodewig91695e42010-02-16 13:59:03 +0000469 super.setExtra(ExtraFieldUtils.mergeLocalFileDataData(getExtraFields(true)));
Torsten Curdtca165392008-07-10 10:17:44 +0000470 }
Torsten Curdtab9ebfc2009-01-12 11:15:34 +0000471
472 /**
Stefan Bodewigdfbdfee2009-03-03 12:42:28 +0000473 * Sets the central directory part of extra fields.
474 */
475 public void setCentralDirectoryExtra(byte[] b) {
476 try {
Stefan Bodewig91695e42010-02-16 13:59:03 +0000477 ZipExtraField[] central =
478 ExtraFieldUtils.parse(b, false,
479 ExtraFieldUtils.UnparseableExtraField.READ);
Stefan Bodewigdfbdfee2009-03-03 12:42:28 +0000480 mergeExtraFields(central, false);
Christian Grobmeierbcebf922009-04-27 17:22:14 +0000481 } catch (ZipException e) {
Stefan Bodewigdfbdfee2009-03-03 12:42:28 +0000482 throw new RuntimeException(e.getMessage(), e);
483 }
484 }
485
486 /**
Torsten Curdtab9ebfc2009-01-12 11:15:34 +0000487 * Retrieves the extra data for the local file data.
488 * @return the extra data for local file
Torsten Curdtab9ebfc2009-01-12 11:15:34 +0000489 */
490 public byte[] getLocalFileDataExtra() {
491 byte[] extra = getExtra();
Stefan Bodewiga2f978e2013-01-05 19:28:42 +0000492 return extra != null ? extra : EMPTY;
Torsten Curdtab9ebfc2009-01-12 11:15:34 +0000493 }
494
495 /**
496 * Retrieves the extra data for the central directory.
497 * @return the central directory extra data
Torsten Curdtab9ebfc2009-01-12 11:15:34 +0000498 */
499 public byte[] getCentralDirectoryExtra() {
Stefan Bodewig91695e42010-02-16 13:59:03 +0000500 return ExtraFieldUtils.mergeCentralDirectoryData(getExtraFields(true));
Torsten Curdtab9ebfc2009-01-12 11:15:34 +0000501 }
502
503 /**
Torsten Curdtab9ebfc2009-01-12 11:15:34 +0000504 * Get the name of the entry.
505 * @return the entry name
Torsten Curdtab9ebfc2009-01-12 11:15:34 +0000506 */
Stefan Bodewige53e88a2011-07-25 13:28:31 +0000507 @Override
Torsten Curdtab9ebfc2009-01-12 11:15:34 +0000508 public String getName() {
509 return name == null ? super.getName() : name;
510 }
511
512 /**
513 * Is this entry a directory?
514 * @return true if the entry is a directory
Torsten Curdtab9ebfc2009-01-12 11:15:34 +0000515 */
Sebastian Bazleyfb06e3a2011-08-15 15:10:46 +0000516 @Override
Torsten Curdtab9ebfc2009-01-12 11:15:34 +0000517 public boolean isDirectory() {
518 return getName().endsWith("/");
519 }
520
521 /**
522 * Set the name of the entry.
523 * @param name the name to use
524 */
525 protected void setName(String name) {
Stefan Bodewigc75c10f2012-02-28 05:00:26 +0000526 if (name != null && getPlatform() == PLATFORM_FAT
527 && name.indexOf("/") == -1) {
528 name = name.replace('\\', '/');
529 }
Torsten Curdtab9ebfc2009-01-12 11:15:34 +0000530 this.name = name;
531 }
532
533 /**
Stefan Bodewigd7d90da2011-07-21 13:18:36 +0000534 * Gets the uncompressed size of the entry data.
535 * @return the entry size
536 */
Stefan Bodewige53e88a2011-07-25 13:28:31 +0000537 @Override
Stefan Bodewigd7d90da2011-07-21 13:18:36 +0000538 public long getSize() {
539 return size;
540 }
541
542 /**
543 * Sets the uncompressed size of the entry data.
544 * @param size the uncompressed size in bytes
545 * @exception IllegalArgumentException if the specified size is less
546 * than 0
547 */
Stefan Bodewige53e88a2011-07-25 13:28:31 +0000548 @Override
Stefan Bodewigd7d90da2011-07-21 13:18:36 +0000549 public void setSize(long size) {
550 if (size < 0) {
551 throw new IllegalArgumentException("invalid entry size");
552 }
553 this.size = size;
554 }
555
556 /**
Stefan Bodewig15f04cc2011-07-25 18:32:19 +0000557 * Sets the name using the raw bytes and the string created from
558 * it by guessing or using the configured encoding.
559 * @param name the name to use created from the raw bytes using
560 * the guessed or configured encoding
561 * @param rawName the bytes originally read as name from the
562 * archive
Gary D. Gregory2bd0dd42012-04-01 13:02:39 +0000563 * @since 1.2
Stefan Bodewig934a96e2011-07-23 04:33:44 +0000564 */
Stefan Bodewig15f04cc2011-07-25 18:32:19 +0000565 protected void setName(String name, byte[] rawName) {
Stefan Bodewig934a96e2011-07-23 04:33:44 +0000566 setName(name);
567 this.rawName = rawName;
568 }
569
570 /**
571 * Returns the raw bytes that made up the name before it has been
572 * converted using the configured or guessed encoding.
573 *
574 * <p>This method will return null if this instance has not been
575 * read from an archive.</p>
576 *
Gary D. Gregory2bd0dd42012-04-01 13:02:39 +0000577 * @since 1.2
Stefan Bodewig934a96e2011-07-23 04:33:44 +0000578 */
579 public byte[] getRawName() {
580 if (rawName != null) {
581 byte[] b = new byte[rawName.length];
582 System.arraycopy(rawName, 0, b, 0, rawName.length);
583 return b;
584 }
585 return null;
586 }
587
588 /**
Torsten Curdtab9ebfc2009-01-12 11:15:34 +0000589 * Get the hashCode of the entry.
590 * This uses the name as the hashcode.
591 * @return a hashcode.
Torsten Curdtab9ebfc2009-01-12 11:15:34 +0000592 */
Stefan Bodewige53e88a2011-07-25 13:28:31 +0000593 @Override
Torsten Curdtab9ebfc2009-01-12 11:15:34 +0000594 public int hashCode() {
595 // this method has severe consequences on performance. We cannot rely
596 // on the super.hashCode() method since super.getName() always return
597 // the empty string in the current implemention (there's no setter)
598 // so it is basically draining the performance of a hashmap lookup
599 return getName().hashCode();
600 }
601
602 /**
Stefan Bodewig62cb9c42010-03-12 16:06:20 +0000603 * The "general purpose bit" field.
Gary D. Gregory2bd0dd42012-04-01 13:02:39 +0000604 * @since 1.1
Stefan Bodewigc7e51ed2010-02-19 10:55:27 +0000605 */
Stefan Bodewig62cb9c42010-03-12 16:06:20 +0000606 public GeneralPurposeBit getGeneralPurposeBit() {
607 return gpb;
Stefan Bodewigc7e51ed2010-02-19 10:55:27 +0000608 }
609
610 /**
Stefan Bodewig62cb9c42010-03-12 16:06:20 +0000611 * The "general purpose bit" field.
Gary D. Gregory2bd0dd42012-04-01 13:02:39 +0000612 * @since 1.1
Stefan Bodewigc7e51ed2010-02-19 10:55:27 +0000613 */
Stefan Bodewig62cb9c42010-03-12 16:06:20 +0000614 public void setGeneralPurposeBit(GeneralPurposeBit b) {
615 gpb = b;
Stefan Bodewigc7e51ed2010-02-19 10:55:27 +0000616 }
617
618 /**
Stefan Bodewigdfbdfee2009-03-03 12:42:28 +0000619 * If there are no extra fields, use the given fields as new extra
620 * data - otherwise merge the fields assuming the existing fields
621 * and the new fields stem from different locations inside the
622 * archive.
623 * @param f the extra fields to merge
624 * @param local whether the new fields originate from local data
625 */
626 private void mergeExtraFields(ZipExtraField[] f, boolean local)
627 throws ZipException {
628 if (extraFields == null) {
629 setExtraFields(f);
630 } else {
Gary D. Gregoryb2a9dd02012-04-01 13:19:03 +0000631 for (ZipExtraField element : f) {
Stefan Bodewig91695e42010-02-16 13:59:03 +0000632 ZipExtraField existing;
Gary D. Gregoryb2a9dd02012-04-01 13:19:03 +0000633 if (element instanceof UnparseableExtraFieldData) {
Stefan Bodewig91695e42010-02-16 13:59:03 +0000634 existing = unparseableExtra;
635 } else {
Gary D. Gregoryb2a9dd02012-04-01 13:19:03 +0000636 existing = getExtraField(element.getHeaderId());
Stefan Bodewig91695e42010-02-16 13:59:03 +0000637 }
Stefan Bodewigdfbdfee2009-03-03 12:42:28 +0000638 if (existing == null) {
Gary D. Gregoryb2a9dd02012-04-01 13:19:03 +0000639 addExtraField(element);
Stefan Bodewigdfbdfee2009-03-03 12:42:28 +0000640 } else {
641 if (local) {
Gary D. Gregoryb2a9dd02012-04-01 13:19:03 +0000642 byte[] b = element.getLocalFileDataData();
Stefan Bodewigdfbdfee2009-03-03 12:42:28 +0000643 existing.parseFromLocalFileData(b, 0, b.length);
644 } else {
Gary D. Gregoryb2a9dd02012-04-01 13:19:03 +0000645 byte[] b = element.getCentralDirectoryData();
Stefan Bodewigdfbdfee2009-03-03 12:42:28 +0000646 existing.parseFromCentralDirectoryData(b, 0, b.length);
647 }
648 }
649 }
650 setExtra();
651 }
652 }
Christian Grobmeier28c23fb2009-04-17 05:14:49 +0000653
Stefan Bodewig17ffd7f2009-08-01 14:52:15 +0000654 public Date getLastModifiedDate() {
655 return new Date(getTime());
656 }
657
Christian Grobmeier28c23fb2009-04-17 05:14:49 +0000658 /* (non-Javadoc)
659 * @see java.lang.Object#equals(java.lang.Object)
660 */
Stefan Bodewige53e88a2011-07-25 13:28:31 +0000661 @Override
Christian Grobmeier28c23fb2009-04-17 05:14:49 +0000662 public boolean equals(Object obj) {
663 if (this == obj) {
664 return true;
665 }
666 if (obj == null || getClass() != obj.getClass()) {
667 return false;
668 }
669 ZipArchiveEntry other = (ZipArchiveEntry) obj;
Stefan Bodewig5066e9a2010-01-07 09:34:55 +0000670 String myName = getName();
671 String otherName = other.getName();
672 if (myName == null) {
673 if (otherName != null) {
Christian Grobmeier28c23fb2009-04-17 05:14:49 +0000674 return false;
675 }
Stefan Bodewig5066e9a2010-01-07 09:34:55 +0000676 } else if (!myName.equals(otherName)) {
Christian Grobmeier28c23fb2009-04-17 05:14:49 +0000677 return false;
678 }
Stefan Bodewig7be97962010-01-26 05:24:17 +0000679 String myComment = getComment();
680 String otherComment = other.getComment();
681 if (myComment == null) {
Stefan Bodewig4becc322012-06-03 15:30:31 +0000682 myComment = "";
683 }
684 if (otherComment == null) {
685 otherComment = "";
Stefan Bodewig7be97962010-01-26 05:24:17 +0000686 }
687 return getTime() == other.getTime()
Stefan Bodewig4becc322012-06-03 15:30:31 +0000688 && myComment.equals(otherComment)
Stefan Bodewig7be97962010-01-26 05:24:17 +0000689 && getInternalAttributes() == other.getInternalAttributes()
690 && getPlatform() == other.getPlatform()
691 && getExternalAttributes() == other.getExternalAttributes()
692 && getMethod() == other.getMethod()
693 && getSize() == other.getSize()
694 && getCrc() == other.getCrc()
695 && getCompressedSize() == other.getCompressedSize()
696 && Arrays.equals(getCentralDirectoryExtra(),
697 other.getCentralDirectoryExtra())
698 && Arrays.equals(getLocalFileDataExtra(),
Stefan Bodewig62cb9c42010-03-12 16:06:20 +0000699 other.getLocalFileDataExtra())
700 && gpb.equals(other.gpb);
Christian Grobmeier28c23fb2009-04-17 05:14:49 +0000701 }
Torsten Curdtca165392008-07-10 10:17:44 +0000702}