001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: */
018:
019: package org.apache.tools.zip;
020:
021: import java.util.Vector;
022: import java.util.zip.ZipException;
023:
024: /**
025: * Extension that adds better handling of extra fields and provides
026: * access to the internal and external file attributes.
027: *
028: */
029: public class ZipEntry extends java.util.zip.ZipEntry implements
030: Cloneable {
031:
032: private static final int PLATFORM_UNIX = 3;
033: private static final int PLATFORM_FAT = 0;
034:
035: private int internalAttributes = 0;
036: private int platform = PLATFORM_FAT;
037: private long externalAttributes = 0;
038: private Vector/*<ZipExtraField>*/extraFields = null;
039: private String name = null;
040:
041: /**
042: * Creates a new zip entry with the specified name.
043: * @param name the name of the entry
044: * @since 1.1
045: */
046: public ZipEntry(String name) {
047: super (name);
048: }
049:
050: /**
051: * Creates a new zip entry with fields taken from the specified zip entry.
052: * @param entry the entry to get fields from
053: * @since 1.1
054: * @throws ZipException on error
055: */
056: public ZipEntry(java.util.zip.ZipEntry entry) throws ZipException {
057: super (entry);
058: byte[] extra = entry.getExtra();
059: if (extra != null) {
060: setExtraFields(ExtraFieldUtils.parse(extra));
061: } else {
062: // initializes extra data to an empty byte array
063: setExtra();
064: }
065: }
066:
067: /**
068: * Creates a new zip entry with fields taken from the specified zip entry.
069: * @param entry the entry to get fields from
070: * @throws ZipException on error
071: * @since 1.1
072: */
073: public ZipEntry(ZipEntry entry) throws ZipException {
074: this ((java.util.zip.ZipEntry) entry);
075: setInternalAttributes(entry.getInternalAttributes());
076: setExternalAttributes(entry.getExternalAttributes());
077: setExtraFields(entry.getExtraFields());
078: }
079:
080: /**
081: * @since 1.9
082: */
083: protected ZipEntry() {
084: super ("");
085: }
086:
087: /**
088: * Overwrite clone.
089: * @return a cloned copy of this ZipEntry
090: * @since 1.1
091: */
092: public Object clone() {
093: ZipEntry e = (ZipEntry) super .clone();
094:
095: e.extraFields = extraFields != null ? (Vector) extraFields
096: .clone() : null;
097: e.setInternalAttributes(getInternalAttributes());
098: e.setExternalAttributes(getExternalAttributes());
099: e.setExtraFields(getExtraFields());
100: return e;
101: }
102:
103: /**
104: * Retrieves the internal file attributes.
105: *
106: * @return the internal file attributes
107: * @since 1.1
108: */
109: public int getInternalAttributes() {
110: return internalAttributes;
111: }
112:
113: /**
114: * Sets the internal file attributes.
115: * @param value an <code>int</code> value
116: * @since 1.1
117: */
118: public void setInternalAttributes(int value) {
119: internalAttributes = value;
120: }
121:
122: /**
123: * Retrieves the external file attributes.
124: * @return the external file attributes
125: * @since 1.1
126: */
127: public long getExternalAttributes() {
128: return externalAttributes;
129: }
130:
131: /**
132: * Sets the external file attributes.
133: * @param value an <code>long</code> value
134: * @since 1.1
135: */
136: public void setExternalAttributes(long value) {
137: externalAttributes = value;
138: }
139:
140: /**
141: * Sets Unix permissions in a way that is understood by Info-Zip's
142: * unzip command.
143: * @param mode an <code>int</code> value
144: * @since Ant 1.5.2
145: */
146: public void setUnixMode(int mode) {
147: setExternalAttributes((mode << 16)
148: // MS-DOS read-only attribute
149: | ((mode & 0200) == 0 ? 1 : 0)
150: // MS-DOS directory flag
151: | (isDirectory() ? 0x10 : 0));
152: platform = PLATFORM_UNIX;
153: }
154:
155: /**
156: * Unix permission.
157: * @return the unix permissions
158: * @since Ant 1.6
159: */
160: public int getUnixMode() {
161: return (int) ((getExternalAttributes() >> 16) & 0xFFFF);
162: }
163:
164: /**
165: * Platform specification to put into the "version made
166: * by" part of the central file header.
167: *
168: * @return 0 (MS-DOS FAT) unless {@link #setUnixMode setUnixMode}
169: * has been called, in which case 3 (Unix) will be returned.
170: *
171: * @since Ant 1.5.2
172: */
173: public int getPlatform() {
174: return platform;
175: }
176:
177: /**
178: * Set the platform (UNIX or FAT).
179: * @param platform an <code>int</code> value - 0 is FAT, 3 is UNIX
180: * @since 1.9
181: */
182: protected void setPlatform(int platform) {
183: this .platform = platform;
184: }
185:
186: /**
187: * Replaces all currently attached extra fields with the new array.
188: * @param fields an array of extra fields
189: * @since 1.1
190: */
191: public void setExtraFields(ZipExtraField[] fields) {
192: extraFields = new Vector();
193: for (int i = 0; i < fields.length; i++) {
194: extraFields.addElement(fields[i]);
195: }
196: setExtra();
197: }
198:
199: /**
200: * Retrieves extra fields.
201: * @return an array of the extra fields
202: * @since 1.1
203: */
204: public ZipExtraField[] getExtraFields() {
205: if (extraFields == null) {
206: return new ZipExtraField[0];
207: }
208: ZipExtraField[] result = new ZipExtraField[extraFields.size()];
209: extraFields.copyInto(result);
210: return result;
211: }
212:
213: /**
214: * Adds an extra fields - replacing an already present extra field
215: * of the same type.
216: * @param ze an extra field
217: * @since 1.1
218: */
219: public void addExtraField(ZipExtraField ze) {
220: if (extraFields == null) {
221: extraFields = new Vector();
222: }
223: ZipShort type = ze.getHeaderId();
224: boolean done = false;
225: for (int i = 0, fieldsSize = extraFields.size(); !done
226: && i < fieldsSize; i++) {
227: if (((ZipExtraField) extraFields.elementAt(i))
228: .getHeaderId().equals(type)) {
229: extraFields.setElementAt(ze, i);
230: done = true;
231: }
232: }
233: if (!done) {
234: extraFields.addElement(ze);
235: }
236: setExtra();
237: }
238:
239: /**
240: * Remove an extra fields.
241: * @param type the type of extra field to remove
242: * @since 1.1
243: */
244: public void removeExtraField(ZipShort type) {
245: if (extraFields == null) {
246: extraFields = new Vector();
247: }
248: boolean done = false;
249: for (int i = 0, fieldsSize = extraFields.size(); !done
250: && i < fieldsSize; i++) {
251: if (((ZipExtraField) extraFields.elementAt(i))
252: .getHeaderId().equals(type)) {
253: extraFields.removeElementAt(i);
254: done = true;
255: }
256: }
257: if (!done) {
258: throw new java.util.NoSuchElementException();
259: }
260: setExtra();
261: }
262:
263: /**
264: * Throws an Exception if extra data cannot be parsed into extra fields.
265: * @param extra an array of bytes to be parsed into extra fields
266: * @throws RuntimeException if the bytes cannot be parsed
267: * @since 1.1
268: * @throws RuntimeException on error
269: */
270: public void setExtra(byte[] extra) throws RuntimeException {
271: try {
272: setExtraFields(ExtraFieldUtils.parse(extra));
273: } catch (Exception e) {
274: throw new RuntimeException(e.getMessage());
275: }
276: }
277:
278: /**
279: * Unfortunately {@link java.util.zip.ZipOutputStream
280: * java.util.zip.ZipOutputStream} seems to access the extra data
281: * directly, so overriding getExtra doesn't help - we need to
282: * modify super's data directly.
283: *
284: * @since 1.1
285: */
286: protected void setExtra() {
287: super .setExtra(ExtraFieldUtils
288: .mergeLocalFileDataData(getExtraFields()));
289: }
290:
291: /**
292: * Retrieves the extra data for the local file data.
293: * @return the extra data for local file
294: * @since 1.1
295: */
296: public byte[] getLocalFileDataExtra() {
297: byte[] extra = getExtra();
298: return extra != null ? extra : new byte[0];
299: }
300:
301: /**
302: * Retrieves the extra data for the central directory.
303: * @return the central directory extra data
304: * @since 1.1
305: */
306: public byte[] getCentralDirectoryExtra() {
307: return ExtraFieldUtils
308: .mergeCentralDirectoryData(getExtraFields());
309: }
310:
311: /**
312: * Make this class work in JDK 1.1 like a 1.2 class.
313: *
314: * <p>This either stores the size for later usage or invokes
315: * setCompressedSize via reflection.</p>
316: * @param size the size to use
317: * @deprecated since 1.7.
318: * Use setCompressedSize directly.
319: * @since 1.2
320: */
321: public void setComprSize(long size) {
322: setCompressedSize(size);
323: }
324:
325: /**
326: * Get the name of the entry.
327: * @return the entry name
328: * @since 1.9
329: */
330: public String getName() {
331: return name == null ? super .getName() : name;
332: }
333:
334: /**
335: * Is this entry a directory?
336: * @return true if the entry is a directory
337: * @since 1.10
338: */
339: public boolean isDirectory() {
340: return getName().endsWith("/");
341: }
342:
343: /**
344: * Set the name of the entry.
345: * @param name the name to use
346: */
347: protected void setName(String name) {
348: this .name = name;
349: }
350:
351: /**
352: * Get the hashCode of the entry.
353: * This uses the name as the hashcode.
354: * @return a hashcode.
355: * @since Ant 1.7
356: */
357: public int hashCode() {
358: // this method has severe consequences on performance. We cannot rely
359: // on the super.hashCode() method since super.getName() always return
360: // the empty string in the current implemention (there's no setter)
361: // so it is basically draining the performance of a hashmap lookup
362: return getName().hashCode();
363: }
364:
365: /**
366: * The equality method. In this case, the implementation returns 'this == o'
367: * which is basically the equals method of the Object class.
368: * @param o the object to compare to
369: * @return true if this object is the same as <code>o</code>
370: * @since Ant 1.7
371: */
372: public boolean equals(Object o) {
373: return (this == o);
374: }
375:
376: }
|