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