1 /* java.util.zip.ZipEntry
2    Copyright (C) 2001, 2002 Free Software Foundation, Inc.
3 
4 This file is part of GNU Classpath.
5 
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10 
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 USA.
20 
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25 
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version. */
37 
38 
39 package java.util.zip;
40 
41 import java.util.Calendar;
42 import java.util.Date;
43 
44 /**
45  * This class represents a member of a zip archive.  ZipFile and
46  * ZipInputStream will give you instances of this class as information
47  * about the members in an archive.  On the other hand ZipOutputStream
48  * needs an instance of this class to create a new member.
49  *
50  * @author Jochen Hoenicke
51  */
52 public class ZipEntry implements ZipConstants, Cloneable
53 {
54   private static int KNOWN_SIZE   = 1;
55   private static int KNOWN_CSIZE  = 2;
56   private static int KNOWN_CRC    = 4;
57   private static int KNOWN_TIME   = 8;
58 
59   private static Calendar cal;
60 
61   private String name;
62   private int size;
63   private int compressedSize;
64   private int crc;
65   private int dostime;
66   private short known = 0;
67   private short method = -1;
68   private byte[] extra = null;
69   private String comment = null;
70 
71   int flags;              /* used by ZipOutputStream */
72   int offset;             /* used by ZipFile and ZipOutputStream */
73 
74 
75   /**
76    * Compression method.  This method doesn't compress at all.
77    */
78   public final static int STORED      =  0;
79   /**
80    * Compression method.  This method uses the Deflater.
81    */
82   public final static int DEFLATED    =  8;
83 
84   /**
85    * Creates a zip entry with the given name.
86    * @param name the name. May include directory components separated
87    * by '/'.
88    *
89    * @exception NullPointerException when name is null.
90    * @exception IllegalArgumentException when name is bigger then 65535 chars.
91    */
ZipEntry(String name)92   public ZipEntry(String name)
93   {
94     int length = name.length();
95     if (length > 65535)
96       throw new IllegalArgumentException("name length is " + length);
97     this.name = name;
98   }
99 
100   /**
101    * Creates a copy of the given zip entry.
102    * @param e the entry to copy.
103    */
ZipEntry(ZipEntry e)104   public ZipEntry(ZipEntry e)
105   {
106     name = e.name;
107     known = e.known;
108     size = e.size;
109     compressedSize = e.compressedSize;
110     crc = e.crc;
111     dostime = e.dostime;
112     method = e.method;
113     extra = e.extra;
114     comment = e.comment;
115   }
116 
setDOSTime(int dostime)117   final void setDOSTime(int dostime)
118   {
119     this.dostime = dostime;
120     known |= KNOWN_TIME;
121   }
122 
getDOSTime()123   final int getDOSTime()
124   {
125     if ((known & KNOWN_TIME) == 0)
126       return 0;
127     else
128       return dostime;
129   }
130 
131   /**
132    * Creates a copy of this zip entry.
133    */
134   /**
135    * Clones the entry.
136    */
clone()137   public Object clone()
138   {
139     try
140       {
141 	// The JCL says that the `extra' field is also copied.
142 	ZipEntry clone = (ZipEntry) super.clone();
143 	if (extra != null)
144 	  clone.extra = (byte[]) extra.clone();
145 	return clone;
146       }
147     catch (CloneNotSupportedException ex)
148       {
149 	throw new InternalError();
150       }
151   }
152 
153   /**
154    * Returns the entry name.  The path components in the entry are
155    * always separated by slashes ('/').
156    */
getName()157   public String getName()
158   {
159     return name;
160   }
161 
162   /**
163    * Sets the time of last modification of the entry.
164    * @time the time of last modification of the entry.
165    */
setTime(long time)166   public void setTime(long time)
167   {
168     Calendar cal = getCalendar();
169     synchronized (cal)
170       {
171 	cal.setTime(new Date(time*1000L));
172 	dostime = (cal.get(cal.YEAR) - 1980 & 0x7f) << 25
173 	  | (cal.get(cal.MONTH) + 1) << 21
174 	  | (cal.get(cal.DAY_OF_MONTH)) << 16
175 	  | (cal.get(cal.HOUR_OF_DAY)) << 11
176 	  | (cal.get(cal.MINUTE)) << 5
177 	  | (cal.get(cal.SECOND)) >> 1;
178       }
179     dostime = (int) (dostime / 1000L);
180     this.known |= KNOWN_TIME;
181   }
182 
183   /**
184    * Gets the time of last modification of the entry.
185    * @return the time of last modification of the entry, or -1 if unknown.
186    */
getTime()187   public long getTime()
188   {
189     if ((known & KNOWN_TIME) == 0)
190       return -1;
191 
192     int sec = 2 * (dostime & 0x1f);
193     int min = (dostime >> 5) & 0x3f;
194     int hrs = (dostime >> 11) & 0x1f;
195     int day = (dostime >> 16) & 0x1f;
196     int mon = ((dostime >> 21) & 0xf) - 1;
197     int year = ((dostime >> 25) & 0x7f) + 1980; /* since 1900 */
198 
199     try
200       {
201 	cal = getCalendar();
202 	synchronized (cal)
203 	  {
204 	    cal.set(year, mon, day, hrs, min, sec);
205 	    return cal.getTime().getTime();
206 	  }
207       }
208     catch (RuntimeException ex)
209       {
210 	/* Ignore illegal time stamp */
211 	known &= ~KNOWN_TIME;
212 	return -1;
213       }
214   }
215 
getCalendar()216   private static synchronized Calendar getCalendar()
217   {
218     if (cal == null)
219       cal = Calendar.getInstance();
220 
221     return cal;
222   }
223 
224   /**
225    * Sets the size of the uncompressed data.
226    * @exception IllegalArgumentException if size is not in 0..0xffffffffL
227    */
setSize(long size)228   public void setSize(long size)
229   {
230     if ((size & 0xffffffff00000000L) != 0)
231 	throw new IllegalArgumentException();
232     this.size = (int) size;
233     this.known |= KNOWN_SIZE;
234   }
235 
236   /**
237    * Gets the size of the uncompressed data.
238    * @return the size or -1 if unknown.
239    */
getSize()240   public long getSize()
241   {
242     return (known & KNOWN_SIZE) != 0 ? size & 0xffffffffL : -1L;
243   }
244 
245   /**
246    * Sets the size of the compressed data.
247    * @exception IllegalArgumentException if size is not in 0..0xffffffffL
248    */
setCompressedSize(long csize)249   public void setCompressedSize(long csize)
250   {
251     if ((csize & 0xffffffff00000000L) != 0)
252 	throw new IllegalArgumentException();
253     this.compressedSize = (int) csize;
254     this.known |= KNOWN_CSIZE;
255   }
256 
257   /**
258    * Gets the size of the compressed data.
259    * @return the size or -1 if unknown.
260    */
getCompressedSize()261   public long getCompressedSize()
262   {
263     return (known & KNOWN_CSIZE) != 0 ? compressedSize & 0xffffffffL : -1L;
264   }
265 
266   /**
267    * Sets the crc of the uncompressed data.
268    * @exception IllegalArgumentException if crc is not in 0..0xffffffffL
269    */
setCrc(long crc)270   public void setCrc(long crc)
271   {
272     if ((crc & 0xffffffff00000000L) != 0)
273 	throw new IllegalArgumentException();
274     this.crc = (int) crc;
275     this.known |= KNOWN_CRC;
276   }
277 
278   /**
279    * Gets the crc of the uncompressed data.
280    * @return the crc or -1 if unknown.
281    */
getCrc()282   public long getCrc()
283   {
284     return (known & KNOWN_CRC) != 0 ? crc & 0xffffffffL : -1L;
285   }
286 
287   /**
288    * Sets the compression method.  Only DEFLATED and STORED are
289    * supported.
290    * @exception IllegalArgumentException if method is not supported.
291    * @see ZipOutputStream#DEFLATED
292    * @see ZipOutputStream#STORED
293    */
setMethod(int method)294   public void setMethod(int method)
295   {
296     if (method != ZipOutputStream.STORED
297 	&& method != ZipOutputStream.DEFLATED)
298 	throw new IllegalArgumentException();
299     this.method = (short) method;
300   }
301 
302   /**
303    * Gets the compression method.
304    * @return the compression method or -1 if unknown.
305    */
getMethod()306   public int getMethod()
307   {
308     return method;
309   }
310 
311   /**
312    * Sets the extra data.
313    * @exception IllegalArgumentException if extra is longer than 0xffff bytes.
314    */
setExtra(byte[] extra)315   public void setExtra(byte[] extra)
316   {
317     if (extra == null)
318       {
319 	this.extra = null;
320 	return;
321       }
322 
323     if (extra.length > 0xffff)
324       throw new IllegalArgumentException();
325     this.extra = extra;
326     try
327       {
328 	int pos = 0;
329 	while (pos < extra.length)
330 	  {
331 	    int sig = (extra[pos++] & 0xff)
332 	      | (extra[pos++] & 0xff) << 8;
333 	    int len = (extra[pos++] & 0xff)
334 	      | (extra[pos++] & 0xff) << 8;
335 	    if (sig == 0x5455)
336 	      {
337 		/* extended time stamp */
338 		int flags = extra[pos];
339 		if ((flags & 1) != 0)
340 		  {
341 		    long time = ((extra[pos+1] & 0xff)
342 			    | (extra[pos+2] & 0xff) << 8
343 			    | (extra[pos+3] & 0xff) << 16
344 			    | (extra[pos+4] & 0xff) << 24);
345 		    setTime(time);
346 		  }
347 	      }
348 	    pos += len;
349 	  }
350       }
351     catch (ArrayIndexOutOfBoundsException ex)
352       {
353 	/* be lenient */
354 	return;
355       }
356   }
357 
358   /**
359    * Gets the extra data.
360    * @return the extra data or null if not set.
361    */
getExtra()362   public byte[] getExtra()
363   {
364     return extra;
365   }
366 
367   /**
368    * Sets the entry comment.
369    * @exception IllegalArgumentException if comment is longer than 0xffff.
370    */
setComment(String comment)371   public void setComment(String comment)
372   {
373     if (comment != null && comment.length() > 0xffff)
374       throw new IllegalArgumentException();
375     this.comment = comment;
376   }
377 
378   /**
379    * Gets the comment.
380    * @return the comment or null if not set.
381    */
getComment()382   public String getComment()
383   {
384     return comment;
385   }
386 
387   /**
388    * Gets true, if the entry is a directory.  This is solely
389    * determined by the name, a trailing slash '/' marks a directory.
390    */
isDirectory()391   public boolean isDirectory()
392   {
393     int nlen = name.length();
394     return nlen > 0 && name.charAt(nlen - 1) == '/';
395   }
396 
397   /**
398    * Gets the string representation of this ZipEntry.  This is just
399    * the name as returned by getName().
400    */
toString()401   public String toString()
402   {
403     return name;
404   }
405 
406   /**
407    * Gets the hashCode of this ZipEntry.  This is just the hashCode
408    * of the name.  Note that the equals method isn't changed, though.
409    */
hashCode()410   public int hashCode()
411   {
412     return name.hashCode();
413   }
414 }
415