OSDN Git Service

a635a5fc92d2b422457707f361b455c2a69e31fa
[charactermanaj/CharacterManaJ.git] / src / org / apache / tools / zip / ZipEntry.java
1 /*\r
2  *  Licensed to the Apache Software Foundation (ASF) under one or more\r
3  *  contributor license agreements.  See the NOTICE file distributed with\r
4  *  this work for additional information regarding copyright ownership.\r
5  *  The ASF licenses this file to You under the Apache License, Version 2.0\r
6  *  (the "License"); you may not use this file except in compliance with\r
7  *  the License.  You may obtain a copy of the License at\r
8  *\r
9  *      http://www.apache.org/licenses/LICENSE-2.0\r
10  *\r
11  *  Unless required by applicable law or agreed to in writing, software\r
12  *  distributed under the License is distributed on an "AS IS" BASIS,\r
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
14  *  See the License for the specific language governing permissions and\r
15  *  limitations under the License.\r
16  *\r
17  */\r
18 \r
19 package org.apache.tools.zip;\r
20 \r
21 import java.util.ArrayList;\r
22 import java.util.LinkedHashMap;\r
23 import java.util.List;\r
24 import java.util.zip.ZipException;\r
25 \r
26 /**\r
27  * Extension that adds better handling of extra fields and provides\r
28  * access to the internal and external file attributes.\r
29  *\r
30  * <p>The extra data is expected to follow the recommendation of\r
31  * {@link http://www.pkware.com/documents/casestudies/APPNOTE.TXT\r
32  * APPNOTE.txt}:</p>\r
33  * <ul>\r
34  *   <li>the extra byte array consists of a sequence of extra fields</li>\r
35  *   <li>each extra fields starts by a two byte header id followed by\r
36  *   a two byte sequence holding the length of the remainder of\r
37  *   data.</li>\r
38  * </ul>\r
39  *\r
40  * <p>Any extra data that cannot be parsed by the rules above will be\r
41  * consumed as "unparseable" extra data and treated differently by the\r
42  * methods of this class.  Versions prior to Apache Commons Compress\r
43  * 1.1 would have thrown an exception if any attempt was made to read\r
44  * or write extra data not conforming to the recommendation.</p>\r
45  *\r
46  */\r
47 @SuppressWarnings({"unchecked", "rawtypes"})\r
48 public class ZipEntry extends java.util.zip.ZipEntry implements Cloneable {\r
49 \r
50     public static final int PLATFORM_UNIX = 3;\r
51     public static final int PLATFORM_FAT  = 0;\r
52     private static final int SHORT_MASK = 0xFFFF;\r
53     private static final int SHORT_SHIFT = 16;\r
54 \r
55     private int internalAttributes = 0;\r
56     private int platform = PLATFORM_FAT;\r
57     private long externalAttributes = 0;\r
58         private LinkedHashMap/*<ZipShort, ZipExtraField>*/ extraFields = null;\r
59     private UnparseableExtraFieldData unparseableExtra = null;\r
60     private String name = null;\r
61 \r
62     /**\r
63      * Creates a new zip entry with the specified name.\r
64      * @param name the name of the entry\r
65      * @since 1.1\r
66      */\r
67     public ZipEntry(String name) {\r
68         super(name);\r
69     }\r
70 \r
71     /**\r
72      * Creates a new zip entry with fields taken from the specified zip entry.\r
73      * @param entry the entry to get fields from\r
74      * @since 1.1\r
75      * @throws ZipException on error\r
76      */\r
77     public ZipEntry(java.util.zip.ZipEntry entry) throws ZipException {\r
78         super(entry);\r
79         byte[] extra = entry.getExtra();\r
80         if (extra != null) {\r
81             setExtraFields(ExtraFieldUtils.parse(extra, true,\r
82                                                  ExtraFieldUtils\r
83                                                  .UnparseableExtraField.READ));\r
84         } else {\r
85             // initializes extra data to an empty byte array\r
86             setExtra();\r
87         }\r
88     }\r
89 \r
90     /**\r
91      * Creates a new zip entry with fields taken from the specified zip entry.\r
92      * @param entry the entry to get fields from\r
93      * @throws ZipException on error\r
94      * @since 1.1\r
95      */\r
96     public ZipEntry(ZipEntry entry) throws ZipException {\r
97         this((java.util.zip.ZipEntry) entry);\r
98         setInternalAttributes(entry.getInternalAttributes());\r
99         setExternalAttributes(entry.getExternalAttributes());\r
100         setExtraFields(entry.getExtraFields(true));\r
101     }\r
102 \r
103     /**\r
104      * @since 1.9\r
105      */\r
106     protected ZipEntry() {\r
107         super("");\r
108     }\r
109 \r
110     /**\r
111      * Overwrite clone.\r
112      * @return a cloned copy of this ZipEntry\r
113      * @since 1.1\r
114      */\r
115     public Object clone() {\r
116         ZipEntry e = (ZipEntry) super.clone();\r
117 \r
118         e.setInternalAttributes(getInternalAttributes());\r
119         e.setExternalAttributes(getExternalAttributes());\r
120         e.setExtraFields(getExtraFields(true));\r
121         return e;\r
122     }\r
123 \r
124     /**\r
125      * Retrieves the internal file attributes.\r
126      *\r
127      * @return the internal file attributes\r
128      * @since 1.1\r
129      */\r
130     public int getInternalAttributes() {\r
131         return internalAttributes;\r
132     }\r
133 \r
134     /**\r
135      * Sets the internal file attributes.\r
136      * @param value an <code>int</code> value\r
137      * @since 1.1\r
138      */\r
139     public void setInternalAttributes(int value) {\r
140         internalAttributes = value;\r
141     }\r
142 \r
143     /**\r
144      * Retrieves the external file attributes.\r
145      * @return the external file attributes\r
146      * @since 1.1\r
147      */\r
148     public long getExternalAttributes() {\r
149         return externalAttributes;\r
150     }\r
151 \r
152     /**\r
153      * Sets the external file attributes.\r
154      * @param value an <code>long</code> value\r
155      * @since 1.1\r
156      */\r
157     public void setExternalAttributes(long value) {\r
158         externalAttributes = value;\r
159     }\r
160 \r
161     /**\r
162      * Sets Unix permissions in a way that is understood by Info-Zip's\r
163      * unzip command.\r
164      * @param mode an <code>int</code> value\r
165      * @since Ant 1.5.2\r
166      */\r
167     public void setUnixMode(int mode) {\r
168         // CheckStyle:MagicNumberCheck OFF - no point\r
169         setExternalAttributes((mode << SHORT_SHIFT)\r
170                               // MS-DOS read-only attribute\r
171                               | ((mode & 0200) == 0 ? 1 : 0)\r
172                               // MS-DOS directory flag\r
173                               | (isDirectory() ? 0x10 : 0));\r
174         // CheckStyle:MagicNumberCheck ON\r
175         platform = PLATFORM_UNIX;\r
176     }\r
177 \r
178     /**\r
179      * Unix permission.\r
180      * @return the unix permissions\r
181      * @since Ant 1.6\r
182      */\r
183     public int getUnixMode() {\r
184         return platform != PLATFORM_UNIX ? 0 :\r
185             (int) ((getExternalAttributes() >> SHORT_SHIFT) & SHORT_MASK);\r
186     }\r
187 \r
188     /**\r
189      * Platform specification to put into the &quot;version made\r
190      * by&quot; part of the central file header.\r
191      *\r
192      * @return PLATFORM_FAT unless {@link #setUnixMode setUnixMode}\r
193      * has been called, in which case PLATORM_UNIX will be returned.\r
194      *\r
195      * @since Ant 1.5.2\r
196      */\r
197     public int getPlatform() {\r
198         return platform;\r
199     }\r
200 \r
201     /**\r
202      * Set the platform (UNIX or FAT).\r
203      * @param platform an <code>int</code> value - 0 is FAT, 3 is UNIX\r
204      * @since 1.9\r
205      */\r
206     protected void setPlatform(int platform) {\r
207         this.platform = platform;\r
208     }\r
209 \r
210     /**\r
211      * Replaces all currently attached extra fields with the new array.\r
212      * @param fields an array of extra fields\r
213      * @since 1.1\r
214      */\r
215         public void setExtraFields(ZipExtraField[] fields) {\r
216         extraFields = new LinkedHashMap();\r
217         for (int i = 0; i < fields.length; i++) {\r
218             if (fields[i] instanceof UnparseableExtraFieldData) {\r
219                 unparseableExtra = (UnparseableExtraFieldData) fields[i];\r
220             } else {\r
221                 extraFields.put(fields[i].getHeaderId(), fields[i]);\r
222             }\r
223         }\r
224         setExtra();\r
225     }\r
226 \r
227     /**\r
228      * Retrieves all extra fields that have been parsed successfully.\r
229      * @return an array of the extra fields\r
230      */\r
231     public ZipExtraField[] getExtraFields() {\r
232         return getExtraFields(false);\r
233     }\r
234 \r
235     /**\r
236      * Retrieves extra fields.\r
237      * @param includeUnparseable whether to also return unparseable\r
238      * extra fields as {@link UnparseableExtraFieldData} if such data\r
239      * exists.\r
240      * @return an array of the extra fields\r
241      * @since 1.1\r
242      */\r
243         public ZipExtraField[] getExtraFields(boolean includeUnparseable) {\r
244         if (extraFields == null) {\r
245             return !includeUnparseable || unparseableExtra == null\r
246                 ? new ZipExtraField[0]\r
247                 : new ZipExtraField[] { unparseableExtra };\r
248         }\r
249         List result = new ArrayList(extraFields.values());\r
250         if (includeUnparseable && unparseableExtra != null) {\r
251             result.add(unparseableExtra);\r
252         }\r
253         return (ZipExtraField[]) result.toArray(new ZipExtraField[0]);\r
254     }\r
255 \r
256     /**\r
257      * Adds an extra field - replacing an already present extra field\r
258      * of the same type.\r
259      *\r
260      * <p>If no extra field of the same type exists, the field will be\r
261      * added as last field.</p>\r
262      * @param ze an extra field\r
263      * @since 1.1\r
264      */\r
265         public void addExtraField(ZipExtraField ze) {\r
266         if (ze instanceof UnparseableExtraFieldData) {\r
267             unparseableExtra = (UnparseableExtraFieldData) ze;\r
268         } else {\r
269             if (extraFields == null) {\r
270                 extraFields = new LinkedHashMap();\r
271             }\r
272             extraFields.put(ze.getHeaderId(), ze);\r
273         }\r
274         setExtra();\r
275     }\r
276 \r
277     /**\r
278      * Adds an extra field - replacing an already present extra field\r
279      * of the same type.\r
280      *\r
281      * <p>The new extra field will be the first one.</p>\r
282      * @param ze an extra field\r
283      * @since 1.1\r
284      */\r
285         public void addAsFirstExtraField(ZipExtraField ze) {\r
286         if (ze instanceof UnparseableExtraFieldData) {\r
287             unparseableExtra = (UnparseableExtraFieldData) ze;\r
288         } else {\r
289             LinkedHashMap copy = extraFields;\r
290             extraFields = new LinkedHashMap();\r
291             extraFields.put(ze.getHeaderId(), ze);\r
292             if (copy != null) {\r
293                 copy.remove(ze.getHeaderId());\r
294                 extraFields.putAll(copy);\r
295             }\r
296         }\r
297         setExtra();\r
298     }\r
299 \r
300     /**\r
301      * Remove an extra field.\r
302      * @param type the type of extra field to remove\r
303      * @since 1.1\r
304      */\r
305     public void removeExtraField(ZipShort type) {\r
306         if (extraFields == null) {\r
307             throw new java.util.NoSuchElementException();\r
308         }\r
309         if (extraFields.remove(type) == null) {\r
310             throw new java.util.NoSuchElementException();\r
311         }\r
312         setExtra();\r
313     }\r
314 \r
315     /**\r
316      * Removes unparseable extra field data.\r
317      */\r
318     public void removeUnparseableExtraFieldData() {\r
319         if (unparseableExtra == null) {\r
320             throw new java.util.NoSuchElementException();\r
321         }\r
322         unparseableExtra = null;\r
323         setExtra();\r
324     }\r
325 \r
326     /**\r
327      * Looks up an extra field by its header id.\r
328      *\r
329      * @return null if no such field exists.\r
330      */\r
331     public ZipExtraField getExtraField(ZipShort type) {\r
332         if (extraFields != null) {\r
333             return (ZipExtraField) extraFields.get(type);\r
334         }\r
335         return null;\r
336     }\r
337 \r
338     /**\r
339      * Looks up extra field data that couldn't be parsed correctly.\r
340      *\r
341      * @return null if no such field exists.\r
342      */\r
343     public UnparseableExtraFieldData getUnparseableExtraFieldData() {\r
344         return unparseableExtra;\r
345     }\r
346 \r
347     /**\r
348      * Parses the given bytes as extra field data and consumes any\r
349      * unparseable data as an {@link UnparseableExtraFieldData}\r
350      * instance.\r
351      * @param extra an array of bytes to be parsed into extra fields\r
352      * @throws RuntimeException if the bytes cannot be parsed\r
353      * @since 1.1\r
354      * @throws RuntimeException on error\r
355      */\r
356     public void setExtra(byte[] extra) throws RuntimeException {\r
357         try {\r
358             ZipExtraField[] local =\r
359                 ExtraFieldUtils.parse(extra, true,\r
360                                       ExtraFieldUtils.UnparseableExtraField.READ);\r
361             mergeExtraFields(local, true);\r
362         } catch (Exception e) {\r
363             // actually this is not be possible as of Ant 1.8.1\r
364             throw new RuntimeException("Error parsing extra fields for entry: "\r
365                                        + getName() + " - " + e.getMessage(), e);\r
366         }\r
367     }\r
368 \r
369     /**\r
370      * Unfortunately {@link java.util.zip.ZipOutputStream\r
371      * java.util.zip.ZipOutputStream} seems to access the extra data\r
372      * directly, so overriding getExtra doesn't help - we need to\r
373      * modify super's data directly.\r
374      *\r
375      * @since 1.1\r
376      */\r
377     protected void setExtra() {\r
378         super.setExtra(ExtraFieldUtils.mergeLocalFileDataData(getExtraFields(true)));\r
379     }\r
380 \r
381     /**\r
382      * Sets the central directory part of extra fields.\r
383      */\r
384     public void setCentralDirectoryExtra(byte[] b) {\r
385         try {\r
386             ZipExtraField[] central =\r
387                 ExtraFieldUtils.parse(b, false,\r
388                                       ExtraFieldUtils.UnparseableExtraField.READ);\r
389             mergeExtraFields(central, false);\r
390         } catch (Exception e) {\r
391             throw new RuntimeException(e.getMessage(), e);\r
392         }\r
393     }\r
394 \r
395     /**\r
396      * Retrieves the extra data for the local file data.\r
397      * @return the extra data for local file\r
398      * @since 1.1\r
399      */\r
400     public byte[] getLocalFileDataExtra() {\r
401         byte[] extra = getExtra();\r
402         return extra != null ? extra : new byte[0];\r
403     }\r
404 \r
405     /**\r
406      * Retrieves the extra data for the central directory.\r
407      * @return the central directory extra data\r
408      * @since 1.1\r
409      */\r
410     public byte[] getCentralDirectoryExtra() {\r
411         return ExtraFieldUtils.mergeCentralDirectoryData(getExtraFields(true));\r
412     }\r
413 \r
414     /**\r
415      * Make this class work in JDK 1.1 like a 1.2 class.\r
416      *\r
417      * <p>This either stores the size for later usage or invokes\r
418      * setCompressedSize via reflection.</p>\r
419      * @param size the size to use\r
420      * @deprecated since 1.7.\r
421      *             Use setCompressedSize directly.\r
422      * @since 1.2\r
423      */\r
424     public void setComprSize(long size) {\r
425         setCompressedSize(size);\r
426     }\r
427 \r
428     /**\r
429      * Get the name of the entry.\r
430      * @return the entry name\r
431      * @since 1.9\r
432      */\r
433     public String getName() {\r
434         return name == null ? super.getName() : name;\r
435     }\r
436 \r
437     /**\r
438      * Is this entry a directory?\r
439      * @return true if the entry is a directory\r
440      * @since 1.10\r
441      */\r
442     public boolean isDirectory() {\r
443         return getName().endsWith("/");\r
444     }\r
445 \r
446     /**\r
447      * Set the name of the entry.\r
448      * @param name the name to use\r
449      */\r
450     protected void setName(String name) {\r
451         this.name = name;\r
452     }\r
453 \r
454     /**\r
455      * Get the hashCode of the entry.\r
456      * This uses the name as the hashcode.\r
457      * @return a hashcode.\r
458      * @since Ant 1.7\r
459      */\r
460     public int hashCode() {\r
461         // this method has severe consequences on performance. We cannot rely\r
462         // on the super.hashCode() method since super.getName() always return\r
463         // the empty string in the current implemention (there's no setter)\r
464         // so it is basically draining the performance of a hashmap lookup\r
465         return getName().hashCode();\r
466     }\r
467 \r
468     /**\r
469      * The equality method. In this case, the implementation returns 'this == o'\r
470      * which is basically the equals method of the Object class.\r
471      * @param o the object to compare to\r
472      * @return true if this object is the same as <code>o</code>\r
473      * @since Ant 1.7\r
474      */\r
475     public boolean equals(Object o) {\r
476         return (this == o);\r
477     }\r
478 \r
479     /**\r
480      * If there are no extra fields, use the given fields as new extra\r
481      * data - otherwise merge the fields assuming the existing fields\r
482      * and the new fields stem from different locations inside the\r
483      * archive.\r
484      * @param f the extra fields to merge\r
485      * @param local whether the new fields originate from local data\r
486      */\r
487     private void mergeExtraFields(ZipExtraField[] f, boolean local)\r
488         throws ZipException {\r
489         if (extraFields == null) {\r
490             setExtraFields(f);\r
491         } else {\r
492             for (int i = 0; i < f.length; i++) {\r
493                 ZipExtraField existing;\r
494                 if (f[i] instanceof UnparseableExtraFieldData) {\r
495                     existing = unparseableExtra;\r
496                 } else {\r
497                     existing = getExtraField(f[i].getHeaderId());\r
498                 }\r
499                 if (existing == null) {\r
500                     addExtraField(f[i]);\r
501                 } else {\r
502                     if (local\r
503                         || !(existing\r
504                              instanceof CentralDirectoryParsingZipExtraField)) {\r
505                         byte[] b = f[i].getLocalFileDataData();\r
506                         existing.parseFromLocalFileData(b, 0, b.length);\r
507                     } else {\r
508                         byte[] b = f[i].getCentralDirectoryData();\r
509                         ((CentralDirectoryParsingZipExtraField) existing)\r
510                             .parseFromCentralDirectoryData(b, 0, b.length);\r
511                     }\r
512                 }\r
513             }\r
514             setExtra();\r
515         }\r
516     }\r
517 }\r