001: /*
002: * Copyright 2001-2004 The Apache Software Foundation
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: *
016: */
017: package org.apache.tools.zip;
018:
019: import java.lang.reflect.InvocationTargetException;
020: import java.lang.reflect.Method;
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: * @author Stefan Bodewig
029: * @version $Revision: 1.12.2.3 $
030: */
031: public class ZipEntry extends java.util.zip.ZipEntry implements
032: Cloneable {
033:
034: private static final int PLATFORM_UNIX = 3;
035: private static final int PLATFORM_FAT = 0;
036:
037: private int internalAttributes = 0;
038: private int platform = PLATFORM_FAT;
039: private long externalAttributes = 0;
040: private Vector<ZipExtraField> extraFields = new Vector<ZipExtraField>();
041: private String name = null;
042:
043: /**
044: * Creates a new zip entry with the specified name.
045: *
046: * @since 1.1
047: */
048: public ZipEntry(String name) {
049: super (name);
050: }
051:
052: /**
053: * Creates a new zip entry with fields taken from the specified zip entry.
054: *
055: * @since 1.1
056: */
057: public ZipEntry(java.util.zip.ZipEntry entry) throws ZipException {
058: /*
059: * REVISIT: call super(entry) instead of this stuff in Ant2,
060: * "copy constructor" has not been available in JDK 1.1
061: */
062: super (entry.getName());
063:
064: setComment(entry.getComment());
065: setMethod(entry.getMethod());
066: setTime(entry.getTime());
067:
068: long size = entry.getSize();
069: if (size > 0) {
070: setSize(size);
071: }
072: long cSize = entry.getCompressedSize();
073: if (cSize > 0) {
074: setComprSize(cSize);
075: }
076: long crc = entry.getCrc();
077: if (crc > 0) {
078: setCrc(crc);
079: }
080:
081: byte[] extra = entry.getExtra();
082: if (extra != null) {
083: setExtraFields(ExtraFieldUtils.parse(extra));
084: } else {
085: // initializes extra data to an empty byte array
086: setExtra();
087: }
088: }
089:
090: /**
091: * Creates a new zip entry with fields taken from the specified zip entry.
092: *
093: * @since 1.1
094: */
095: public ZipEntry(ZipEntry entry) throws ZipException {
096: this ((java.util.zip.ZipEntry) entry);
097: setInternalAttributes(entry.getInternalAttributes());
098: setExternalAttributes(entry.getExternalAttributes());
099: setExtraFields(entry.getExtraFields());
100:
101: }
102:
103: /**
104: * @since 1.9
105: */
106: protected ZipEntry() {
107: super ("");
108: }
109:
110: /**
111: * Overwrite clone
112: *
113: * @since 1.1
114: */
115: @SuppressWarnings("unchecked")
116: @Override
117: public Object clone() {
118: try {
119: ZipEntry e = (ZipEntry) super .clone();
120:
121: e.setName(getName());
122: e.setComment(getComment());
123: e.setMethod(getMethod());
124: e.setTime(getTime());
125: long size = getSize();
126: if (size > 0) {
127: e.setSize(size);
128: }
129: long cSize = getCompressedSize();
130: if (cSize > 0) {
131: e.setComprSize(cSize);
132: }
133: long crc = getCrc();
134: if (crc > 0) {
135: e.setCrc(crc);
136: }
137: e.extraFields = (Vector<ZipExtraField>) extraFields.clone();
138: e.setInternalAttributes(getInternalAttributes());
139: e.setExternalAttributes(getExternalAttributes());
140: e.setExtraFields(getExtraFields());
141: return e;
142: } catch (Throwable t) {
143: // in JDK 1.1 ZipEntry is not Cloneable, so super.clone declares
144: // to throw CloneNotSupported - since JDK 1.2 it is overridden to
145: // not throw that exception
146: return null;
147: }
148: }
149:
150: /**
151: * Retrieves the internal file attributes.
152: *
153: * @since 1.1
154: */
155: public int getInternalAttributes() {
156: return internalAttributes;
157: }
158:
159: /**
160: * Sets the internal file attributes.
161: *
162: * @since 1.1
163: */
164: public void setInternalAttributes(int value) {
165: internalAttributes = value;
166: }
167:
168: /**
169: * Retrieves the external file attributes.
170: *
171: * @since 1.1
172: */
173: public long getExternalAttributes() {
174: return externalAttributes;
175: }
176:
177: public boolean isReadOnly() {
178: return (externalAttributes & 1) == 1;
179: }
180:
181: /**
182: * Sets the external file attributes.
183: *
184: * @since 1.1
185: */
186: public void setExternalAttributes(long value) {
187: externalAttributes = value;
188: }
189:
190: /**
191: * Sets Unix permissions in a way that is understood by Info-Zip's
192: * unzip command.
193: *
194: * @since Ant 1.5.2
195: */
196: public void setUnixMode(int mode) {
197: setExternalAttributes((mode << 16)
198: // MS-DOS read-only attribute
199: | ((mode & 0200) == 0 ? 1 : 0)
200: // MS-DOS directory flag
201: | (isDirectory() ? 0x10 : 0));
202: platform = PLATFORM_UNIX;
203: }
204:
205: /**
206: * Unix permission.
207: *
208: * @since Ant 1.6
209: */
210: public int getUnixMode() {
211: return (int) ((getExternalAttributes() >> 16) & 0xFFFF);
212: }
213:
214: /**
215: * Platform specification to put into the "version made
216: * by" part of the central file header.
217: *
218: * @return 0 (MS-DOS FAT) unless {@link #setUnixMode setUnixMode}
219: * has been called, in which case 3 (Unix) will be returned.
220: *
221: * @since Ant 1.5.2
222: */
223: public int getPlatform() {
224: return platform;
225: }
226:
227: /**
228: * @since 1.9
229: */
230: protected void setPlatform(int platform) {
231: this .platform = platform;
232: }
233:
234: /**
235: * Replaces all currently attached extra fields with the new array.
236: *
237: * @since 1.1
238: */
239: public void setExtraFields(ZipExtraField[] fields) {
240: extraFields.removeAllElements();
241: for (int i = 0; i < fields.length; i++) {
242: extraFields.add(fields[i]);
243: }
244: setExtra();
245: }
246:
247: /**
248: * Retrieves extra fields.
249: *
250: * @since 1.1
251: */
252: public ZipExtraField[] getExtraFields() {
253: ZipExtraField[] result = new ZipExtraField[extraFields.size()];
254: extraFields.copyInto(result);
255: return result;
256: }
257:
258: /**
259: * Adds an extra fields - replacing an already present extra field
260: * of the same type.
261: *
262: * @since 1.1
263: */
264: public void addExtraField(ZipExtraField ze) {
265: ZipShort type = ze.getHeaderId();
266: boolean done = false;
267: for (int i = 0; !done && i < extraFields.size(); i++) {
268: if (extraFields.elementAt(i).getHeaderId().equals(type)) {
269: extraFields.setElementAt(ze, i);
270: done = true;
271: }
272: }
273: if (!done) {
274: extraFields.add(ze);
275: }
276: setExtra();
277: }
278:
279: /**
280: * Remove an extra fields.
281: *
282: * @since 1.1
283: */
284: public void removeExtraField(ZipShort type) {
285: boolean done = false;
286: for (int i = 0; !done && i < extraFields.size(); i++) {
287: if (extraFields.elementAt(i).getHeaderId().equals(type)) {
288: extraFields.removeElementAt(i);
289: done = true;
290: }
291: }
292: if (!done) {
293: throw new java.util.NoSuchElementException();
294: }
295: setExtra();
296: }
297:
298: /**
299: * Throws an Exception if extra data cannot be parsed into extra fields.
300: *
301: * @since 1.1
302: */
303: @Override
304: public void setExtra(byte[] extra) throws RuntimeException {
305: try {
306: setExtraFields(ExtraFieldUtils.parse(extra));
307: } catch (Exception e) {
308: throw new RuntimeException(e.getMessage());
309: }
310: }
311:
312: /**
313: * Unfortunately {@link java.util.zip.ZipOutputStream
314: * java.util.zip.ZipOutputStream} seems to access the extra data
315: * directly, so overriding getExtra doesn't help - we need to
316: * modify super's data directly.
317: *
318: * @since 1.1
319: */
320: protected void setExtra() {
321: super .setExtra(ExtraFieldUtils
322: .mergeLocalFileDataData(getExtraFields()));
323: }
324:
325: /**
326: * Retrieves the extra data for the local file data.
327: *
328: * @since 1.1
329: */
330: public byte[] getLocalFileDataExtra() {
331: byte[] extra = getExtra();
332: return extra != null ? extra : new byte[0];
333: }
334:
335: /**
336: * Retrieves the extra data for the central directory.
337: *
338: * @since 1.1
339: */
340: public byte[] getCentralDirectoryExtra() {
341: return ExtraFieldUtils
342: .mergeCentralDirectoryData(getExtraFields());
343: }
344:
345: /**
346: * Helper for JDK 1.1 <-> 1.2 incompatibility.
347: *
348: * @since 1.2
349: */
350: private Long compressedSize = null;
351:
352: /**
353: * Make this class work in JDK 1.1 like a 1.2 class.
354: *
355: * <p>This either stores the size for later usage or invokes
356: * setCompressedSize via reflection.</p>
357: *
358: * @since 1.2
359: */
360: public void setComprSize(long size) {
361: if (haveSetCompressedSize()) {
362: //System.out.println("performSetCompressedSize");
363: performSetCompressedSize(this , size);
364: } else {
365: compressedSize = new Long(size);
366: }
367: }
368:
369: /**
370: * Override to make this class work in JDK 1.1 like a 1.2 class.
371: *
372: * @since 1.2
373: */
374: @Override
375: public long getCompressedSize() {
376: if (compressedSize != null) {
377: // has been set explicitly and we are running in a 1.1 VM
378: return compressedSize;
379: }
380: return super .getCompressedSize();
381: }
382:
383: /**
384: * @since 1.9
385: */
386: @Override
387: public String getName() {
388: return name == null ? super .getName() : name;
389: }
390:
391: /**
392: * @since 1.10
393: */
394: @Override
395: public boolean isDirectory() {
396: return getName().endsWith("/");
397: }
398:
399: protected void setName(String name) {
400: this .name = name;
401: }
402:
403: /**
404: * Helper for JDK 1.1
405: *
406: * @since 1.2
407: */
408: private static Method setCompressedSizeMethod = null;
409:
410: /**
411: * Helper for JDK 1.1
412: *
413: * @since 1.2
414: */
415: private static Object lockReflection = new Object();
416:
417: /**
418: * Helper for JDK 1.1
419: *
420: * @since 1.2
421: */
422: private static boolean triedToGetMethod = false;
423:
424: /**
425: * Are we running JDK 1.2 or higher?
426: *
427: * @since 1.2
428: */
429: private static boolean haveSetCompressedSize() {
430: checkSCS();
431: return setCompressedSizeMethod != null;
432: }
433:
434: /**
435: * Invoke setCompressedSize via reflection.
436: *
437: * @since 1.2
438: */
439: private static void performSetCompressedSize(ZipEntry ze, long size) {
440: //Long[] s = {new Long(size)};
441: try {
442: setCompressedSizeMethod.invoke(ze, size);
443: } catch (InvocationTargetException ite) {
444: Throwable nested = ite.getTargetException();
445: throw new RuntimeException(
446: "Exception setting the compressed size " + "of "
447: + ze + ": " + nested.getMessage());
448: } catch (Throwable other) {
449: throw new RuntimeException(
450: "Exception setting the compressed size " + "of "
451: + ze + ": " + other.getMessage());
452: }
453: }
454:
455: /**
456: * Try to get a handle to the setCompressedSize method.
457: *
458: * @since 1.2
459: */
460: private static void checkSCS() {
461: if (!triedToGetMethod) {
462: synchronized (lockReflection) {
463: triedToGetMethod = true;
464: try {
465: setCompressedSizeMethod = java.util.zip.ZipEntry.class
466: .getMethod("setCompressedSize",
467: new Class[] { Long.TYPE });
468: } catch (NoSuchMethodException nse) {
469: }
470: }
471: }
472: }
473: }
|