applied LGPLv3 and source code formatting.
[vamsas.git] / src / org / apache / tools / zip / ZipEntry.java
1 /*
2  *  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
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *  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  *
17  */
18
19 package org.apache.tools.zip;
20
21 import java.util.Vector;
22 import java.util.zip.ZipException;
23
24 /**
25  * Extension that adds better handling of extra fields and provides access to
26  * the internal and external file attributes.
27  * 
28  */
29 public class ZipEntry extends java.util.zip.ZipEntry implements Cloneable {
30
31   private static final int PLATFORM_UNIX = 3;
32
33   private static final int PLATFORM_FAT = 0;
34
35   private int internalAttributes = 0;
36
37   private int platform = PLATFORM_FAT;
38
39   private long externalAttributes = 0;
40
41   private Vector/* <ZipExtraField> */extraFields = null;
42
43   private String name = null;
44
45   /**
46    * Creates a new zip entry with the specified name.
47    * 
48    * @param name
49    *          the name of the entry
50    * @since 1.1
51    */
52   public ZipEntry(String name) {
53     super(name);
54   }
55
56   /**
57    * Creates a new zip entry with fields taken from the specified zip entry.
58    * 
59    * @param entry
60    *          the entry to get fields from
61    * @since 1.1
62    * @throws ZipException
63    *           on error
64    */
65   public ZipEntry(java.util.zip.ZipEntry entry) throws ZipException {
66     super(entry);
67     byte[] extra = entry.getExtra();
68     if (extra != null) {
69       setExtraFields(ExtraFieldUtils.parse(extra));
70     } else {
71       // initializes extra data to an empty byte array
72       setExtra();
73     }
74   }
75
76   /**
77    * Creates a new zip entry with fields taken from the specified zip entry.
78    * 
79    * @param entry
80    *          the entry to get fields from
81    * @throws ZipException
82    *           on error
83    * @since 1.1
84    */
85   public ZipEntry(ZipEntry entry) throws ZipException {
86     this((java.util.zip.ZipEntry) entry);
87     setInternalAttributes(entry.getInternalAttributes());
88     setExternalAttributes(entry.getExternalAttributes());
89     setExtraFields(entry.getExtraFields());
90   }
91
92   /**
93    * @since 1.9
94    */
95   protected ZipEntry() {
96     super("");
97   }
98
99   /**
100    * Overwrite clone.
101    * 
102    * @return a cloned copy of this ZipEntry
103    * @since 1.1
104    */
105   public Object clone() {
106     ZipEntry e = (ZipEntry) super.clone();
107
108     e.extraFields = extraFields != null ? (Vector) extraFields.clone() : null;
109     e.setInternalAttributes(getInternalAttributes());
110     e.setExternalAttributes(getExternalAttributes());
111     e.setExtraFields(getExtraFields());
112     return e;
113   }
114
115   /**
116    * Retrieves the internal file attributes.
117    * 
118    * @return the internal file attributes
119    * @since 1.1
120    */
121   public int getInternalAttributes() {
122     return internalAttributes;
123   }
124
125   /**
126    * Sets the internal file attributes.
127    * 
128    * @param value
129    *          an <code>int</code> value
130    * @since 1.1
131    */
132   public void setInternalAttributes(int value) {
133     internalAttributes = value;
134   }
135
136   /**
137    * Retrieves the external file attributes.
138    * 
139    * @return the external file attributes
140    * @since 1.1
141    */
142   public long getExternalAttributes() {
143     return externalAttributes;
144   }
145
146   /**
147    * Sets the external file attributes.
148    * 
149    * @param value
150    *          an <code>long</code> value
151    * @since 1.1
152    */
153   public void setExternalAttributes(long value) {
154     externalAttributes = value;
155   }
156
157   /**
158    * Sets Unix permissions in a way that is understood by Info-Zip's unzip
159    * command.
160    * 
161    * @param mode
162    *          an <code>int</code> value
163    * @since Ant 1.5.2
164    */
165   public void setUnixMode(int mode) {
166     setExternalAttributes((mode << 16)
167     // MS-DOS read-only attribute
168         | ((mode & 0200) == 0 ? 1 : 0)
169         // MS-DOS directory flag
170         | (isDirectory() ? 0x10 : 0));
171     platform = PLATFORM_UNIX;
172   }
173
174   /**
175    * Unix permission.
176    * 
177    * @return the unix permissions
178    * @since Ant 1.6
179    */
180   public int getUnixMode() {
181     return (int) ((getExternalAttributes() >> 16) & 0xFFFF);
182   }
183
184   /**
185    * Platform specification to put into the &quot;version made by&quot; part of
186    * the central file header.
187    * 
188    * @return 0 (MS-DOS FAT) unless {@link #setUnixMode setUnixMode} has been
189    *         called, in which case 3 (Unix) will be returned.
190    * 
191    * @since Ant 1.5.2
192    */
193   public int getPlatform() {
194     return platform;
195   }
196
197   /**
198    * Set the platform (UNIX or FAT).
199    * 
200    * @param platform
201    *          an <code>int</code> value - 0 is FAT, 3 is UNIX
202    * @since 1.9
203    */
204   protected void setPlatform(int platform) {
205     this.platform = platform;
206   }
207
208   /**
209    * Replaces all currently attached extra fields with the new array.
210    * 
211    * @param fields
212    *          an array of extra fields
213    * @since 1.1
214    */
215   public void setExtraFields(ZipExtraField[] fields) {
216     extraFields = new Vector();
217     for (int i = 0; i < fields.length; i++) {
218       extraFields.addElement(fields[i]);
219     }
220     setExtra();
221   }
222
223   /**
224    * Retrieves extra fields.
225    * 
226    * @return an array of the extra fields
227    * @since 1.1
228    */
229   public ZipExtraField[] getExtraFields() {
230     if (extraFields == null) {
231       return new ZipExtraField[0];
232     }
233     ZipExtraField[] result = new ZipExtraField[extraFields.size()];
234     extraFields.copyInto(result);
235     return result;
236   }
237
238   /**
239    * Adds an extra fields - replacing an already present extra field of the same
240    * type.
241    * 
242    * @param ze
243    *          an extra field
244    * @since 1.1
245    */
246   public void addExtraField(ZipExtraField ze) {
247     if (extraFields == null) {
248       extraFields = new Vector();
249     }
250     ZipShort type = ze.getHeaderId();
251     boolean done = false;
252     for (int i = 0, fieldsSize = extraFields.size(); !done && i < fieldsSize; i++) {
253       if (((ZipExtraField) extraFields.elementAt(i)).getHeaderId().equals(type)) {
254         extraFields.setElementAt(ze, i);
255         done = true;
256       }
257     }
258     if (!done) {
259       extraFields.addElement(ze);
260     }
261     setExtra();
262   }
263
264   /**
265    * Remove an extra fields.
266    * 
267    * @param type
268    *          the type of extra field to remove
269    * @since 1.1
270    */
271   public void removeExtraField(ZipShort type) {
272     if (extraFields == null) {
273       extraFields = new Vector();
274     }
275     boolean done = false;
276     for (int i = 0, fieldsSize = extraFields.size(); !done && i < fieldsSize; i++) {
277       if (((ZipExtraField) extraFields.elementAt(i)).getHeaderId().equals(type)) {
278         extraFields.removeElementAt(i);
279         done = true;
280       }
281     }
282     if (!done) {
283       throw new java.util.NoSuchElementException();
284     }
285     setExtra();
286   }
287
288   /**
289    * Throws an Exception if extra data cannot be parsed into extra fields.
290    * 
291    * @param extra
292    *          an array of bytes to be parsed into extra fields
293    * @throws RuntimeException
294    *           if the bytes cannot be parsed
295    * @since 1.1
296    * @throws RuntimeException
297    *           on error
298    */
299   public void setExtra(byte[] extra) throws RuntimeException {
300     try {
301       setExtraFields(ExtraFieldUtils.parse(extra));
302     } catch (Exception e) {
303       throw new RuntimeException(e.getMessage());
304     }
305   }
306
307   /**
308    * Unfortunately {@link java.util.zip.ZipOutputStream
309    * java.util.zip.ZipOutputStream} seems to access the extra data directly, so
310    * overriding getExtra doesn't help - we need to modify super's data directly.
311    * 
312    * @since 1.1
313    */
314   protected void setExtra() {
315     super.setExtra(ExtraFieldUtils.mergeLocalFileDataData(getExtraFields()));
316   }
317
318   /**
319    * Retrieves the extra data for the local file data.
320    * 
321    * @return the extra data for local file
322    * @since 1.1
323    */
324   public byte[] getLocalFileDataExtra() {
325     byte[] extra = getExtra();
326     return extra != null ? extra : new byte[0];
327   }
328
329   /**
330    * Retrieves the extra data for the central directory.
331    * 
332    * @return the central directory extra data
333    * @since 1.1
334    */
335   public byte[] getCentralDirectoryExtra() {
336     return ExtraFieldUtils.mergeCentralDirectoryData(getExtraFields());
337   }
338
339   /**
340    * Make this class work in JDK 1.1 like a 1.2 class.
341    * 
342    * <p>
343    * This either stores the size for later usage or invokes setCompressedSize
344    * via reflection.
345    * </p>
346    * 
347    * @param size
348    *          the size to use
349    * @deprecated since 1.7. Use setCompressedSize directly.
350    * @since 1.2
351    */
352   public void setComprSize(long size) {
353     setCompressedSize(size);
354   }
355
356   /**
357    * Get the name of the entry.
358    * 
359    * @return the entry name
360    * @since 1.9
361    */
362   public String getName() {
363     return name == null ? super.getName() : name;
364   }
365
366   /**
367    * Is this entry a directory?
368    * 
369    * @return true if the entry is a directory
370    * @since 1.10
371    */
372   public boolean isDirectory() {
373     return getName().endsWith("/");
374   }
375
376   /**
377    * Set the name of the entry.
378    * 
379    * @param name
380    *          the name to use
381    */
382   protected void setName(String name) {
383     this.name = name;
384   }
385
386   /**
387    * Get the hashCode of the entry. This uses the name as the hashcode.
388    * 
389    * @return a hashcode.
390    * @since Ant 1.7
391    */
392   public int hashCode() {
393     // this method has severe consequences on performance. We cannot rely
394     // on the super.hashCode() method since super.getName() always return
395     // the empty string in the current implemention (there's no setter)
396     // so it is basically draining the performance of a hashmap lookup
397     return getName().hashCode();
398   }
399
400   /**
401    * The equality method. In this case, the implementation returns 'this == o'
402    * which is basically the equals method of the Object class.
403    * 
404    * @param o
405    *          the object to compare to
406    * @return true if this object is the same as <code>o</code>
407    * @since Ant 1.7
408    */
409   public boolean equals(Object o) {
410     return (this == o);
411   }
412
413 }