forked from XWxiaowei/JavaCode
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathZipEntry.java
More file actions
594 lines (555 loc) · 18.5 KB
/
ZipEntry.java
File metadata and controls
594 lines (555 loc) · 18.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
/*
* Copyright (c) 1995, 2015, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package java.util.zip;
import static java.util.zip.ZipUtils.*;
import java.nio.file.attribute.FileTime;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import static java.util.zip.ZipConstants64.*;
/**
* This class is used to represent a ZIP file entry.
*
* @author David Connelly
*/
public
class ZipEntry implements ZipConstants, Cloneable {
String name; // entry name
long xdostime = -1; // last modification time (in extended DOS time,
// where milliseconds lost in conversion might
// be encoded into the upper half)
FileTime mtime; // last modification time, from extra field data
FileTime atime; // last access time, from extra field data
FileTime ctime; // creation time, from extra field data
long crc = -1; // crc-32 of entry data
long size = -1; // uncompressed size of entry data
long csize = -1; // compressed size of entry data
int method = -1; // compression method
int flag = 0; // general purpose flag
byte[] extra; // optional extra field data for entry
String comment; // optional comment string for entry
/**
* Compression method for uncompressed entries.
*/
public static final int STORED = 0;
/**
* Compression method for compressed (deflated) entries.
*/
public static final int DEFLATED = 8;
/**
* DOS time constant for representing timestamps before 1980.
*/
static final long DOSTIME_BEFORE_1980 = (1 << 21) | (1 << 16);
/**
* Approximately 128 years, in milliseconds (ignoring leap years etc).
*
* This establish an approximate high-bound value for DOS times in
* milliseconds since epoch, used to enable an efficient but
* sufficient bounds check to avoid generating extended last modified
* time entries.
*
* Calculating the exact number is locale dependent, would require loading
* TimeZone data eagerly, and would make little practical sense. Since DOS
* times theoretically go to 2107 - with compatibility not guaranteed
* after 2099 - setting this to a time that is before but near 2099
* should be sufficient.
*/
private static final long UPPER_DOSTIME_BOUND =
128L * 365 * 24 * 60 * 60 * 1000;
/**
* Creates a new zip entry with the specified name.
*
* @param name
* The entry name
*
* @throws NullPointerException if the entry name is null
* @throws IllegalArgumentException if the entry name is longer than
* 0xFFFF bytes
*/
public ZipEntry(String name) {
Objects.requireNonNull(name, "name");
if (name.length() > 0xFFFF) {
throw new IllegalArgumentException("entry name too long");
}
this.name = name;
}
/**
* Creates a new zip entry with fields taken from the specified
* zip entry.
*
* @param e
* A zip Entry object
*
* @throws NullPointerException if the entry object is null
*/
public ZipEntry(ZipEntry e) {
Objects.requireNonNull(e, "entry");
name = e.name;
xdostime = e.xdostime;
mtime = e.mtime;
atime = e.atime;
ctime = e.ctime;
crc = e.crc;
size = e.size;
csize = e.csize;
method = e.method;
flag = e.flag;
extra = e.extra;
comment = e.comment;
}
/**
* Creates a new un-initialized zip entry
*/
ZipEntry() {}
/**
* Returns the name of the entry.
* @return the name of the entry
*/
public String getName() {
return name;
}
/**
* Sets the last modification time of the entry.
*
* <p> If the entry is output to a ZIP file or ZIP file formatted
* output stream the last modification time set by this method will
* be stored into the {@code date and time fields} of the zip file
* entry and encoded in standard {@code MS-DOS date and time format}.
* The {@link java.util.TimeZone#getDefault() default TimeZone} is
* used to convert the epoch time to the MS-DOS data and time.
*
* @param time
* The last modification time of the entry in milliseconds
* since the epoch
*
* @see #getTime()
* @see #getLastModifiedTime()
*/
public void setTime(long time) {
this.xdostime = javaToExtendedDosTime(time);
// Avoid setting the mtime field if time is in the valid
// range for a DOS time
if (xdostime != DOSTIME_BEFORE_1980 && time <= UPPER_DOSTIME_BOUND) {
this.mtime = null;
} else {
this.mtime = FileTime.from(time, TimeUnit.MILLISECONDS);
}
}
/**
* Returns the last modification time of the entry.
*
* <p> If the entry is read from a ZIP file or ZIP file formatted
* input stream, this is the last modification time from the {@code
* date and time fields} of the zip file entry. The
* {@link java.util.TimeZone#getDefault() default TimeZone} is used
* to convert the standard MS-DOS formatted date and time to the
* epoch time.
*
* @return The last modification time of the entry in milliseconds
* since the epoch, or -1 if not specified
*
* @see #setTime(long)
* @see #setLastModifiedTime(FileTime)
*/
public long getTime() {
if (mtime != null) {
return mtime.toMillis();
}
return (xdostime != -1) ? extendedDosToJavaTime(xdostime) : -1;
}
/**
* Sets the last modification time of the entry.
*
* <p> When output to a ZIP file or ZIP file formatted output stream
* the last modification time set by this method will be stored into
* zip file entry's {@code date and time fields} in {@code standard
* MS-DOS date and time format}), and the extended timestamp fields
* in {@code optional extra data} in UTC time.
*
* @param time
* The last modification time of the entry
* @return This zip entry
*
* @throws NullPointerException if the {@code time} is null
*
* @see #getLastModifiedTime()
* @since 1.8
*/
public ZipEntry setLastModifiedTime(FileTime time) {
this.mtime = Objects.requireNonNull(time, "lastModifiedTime");
this.xdostime = javaToExtendedDosTime(time.to(TimeUnit.MILLISECONDS));
return this;
}
/**
* Returns the last modification time of the entry.
*
* <p> If the entry is read from a ZIP file or ZIP file formatted
* input stream, this is the last modification time from the zip
* file entry's {@code optional extra data} if the extended timestamp
* fields are present. Otherwise the last modification time is read
* from the entry's {@code date and time fields}, the {@link
* java.util.TimeZone#getDefault() default TimeZone} is used to convert
* the standard MS-DOS formatted date and time to the epoch time.
*
* @return The last modification time of the entry, null if not specified
*
* @see #setLastModifiedTime(FileTime)
* @since 1.8
*/
public FileTime getLastModifiedTime() {
if (mtime != null)
return mtime;
if (xdostime == -1)
return null;
return FileTime.from(getTime(), TimeUnit.MILLISECONDS);
}
/**
* Sets the last access time of the entry.
*
* <p> If set, the last access time will be stored into the extended
* timestamp fields of entry's {@code optional extra data}, when output
* to a ZIP file or ZIP file formatted stream.
*
* @param time
* The last access time of the entry
* @return This zip entry
*
* @throws NullPointerException if the {@code time} is null
*
* @see #getLastAccessTime()
* @since 1.8
*/
public ZipEntry setLastAccessTime(FileTime time) {
this.atime = Objects.requireNonNull(time, "lastAccessTime");
return this;
}
/**
* Returns the last access time of the entry.
*
* <p> The last access time is from the extended timestamp fields
* of entry's {@code optional extra data} when read from a ZIP file
* or ZIP file formatted stream.
*
* @return The last access time of the entry, null if not specified
* @see #setLastAccessTime(FileTime)
* @since 1.8
*/
public FileTime getLastAccessTime() {
return atime;
}
/**
* Sets the creation time of the entry.
*
* <p> If set, the creation time will be stored into the extended
* timestamp fields of entry's {@code optional extra data}, when
* output to a ZIP file or ZIP file formatted stream.
*
* @param time
* The creation time of the entry
* @return This zip entry
*
* @throws NullPointerException if the {@code time} is null
*
* @see #getCreationTime()
* @since 1.8
*/
public ZipEntry setCreationTime(FileTime time) {
this.ctime = Objects.requireNonNull(time, "creationTime");
return this;
}
/**
* Returns the creation time of the entry.
*
* <p> The creation time is from the extended timestamp fields of
* entry's {@code optional extra data} when read from a ZIP file
* or ZIP file formatted stream.
*
* @return the creation time of the entry, null if not specified
* @see #setCreationTime(FileTime)
* @since 1.8
*/
public FileTime getCreationTime() {
return ctime;
}
/**
* Sets the uncompressed size of the entry data.
*
* @param size the uncompressed size in bytes
*
* @throws IllegalArgumentException if the specified size is less
* than 0, is greater than 0xFFFFFFFF when
* <a href="package-summary.html#zip64">ZIP64 format</a> is not supported,
* or is less than 0 when ZIP64 is supported
* @see #getSize()
*/
public void setSize(long size) {
if (size < 0) {
throw new IllegalArgumentException("invalid entry size");
}
this.size = size;
}
/**
* Returns the uncompressed size of the entry data.
*
* @return the uncompressed size of the entry data, or -1 if not known
* @see #setSize(long)
*/
public long getSize() {
return size;
}
/**
* Returns the size of the compressed entry data.
*
* <p> In the case of a stored entry, the compressed size will be the same
* as the uncompressed size of the entry.
*
* @return the size of the compressed entry data, or -1 if not known
* @see #setCompressedSize(long)
*/
public long getCompressedSize() {
return csize;
}
/**
* Sets the size of the compressed entry data.
*
* @param csize the compressed size to set to
*
* @see #getCompressedSize()
*/
public void setCompressedSize(long csize) {
this.csize = csize;
}
/**
* Sets the CRC-32 checksum of the uncompressed entry data.
*
* @param crc the CRC-32 value
*
* @throws IllegalArgumentException if the specified CRC-32 value is
* less than 0 or greater than 0xFFFFFFFF
* @see #getCrc()
*/
public void setCrc(long crc) {
if (crc < 0 || crc > 0xFFFFFFFFL) {
throw new IllegalArgumentException("invalid entry crc-32");
}
this.crc = crc;
}
/**
* Returns the CRC-32 checksum of the uncompressed entry data.
*
* @return the CRC-32 checksum of the uncompressed entry data, or -1 if
* not known
*
* @see #setCrc(long)
*/
public long getCrc() {
return crc;
}
/**
* Sets the compression method for the entry.
*
* @param method the compression method, either STORED or DEFLATED
*
* @throws IllegalArgumentException if the specified compression
* method is invalid
* @see #getMethod()
*/
public void setMethod(int method) {
if (method != STORED && method != DEFLATED) {
throw new IllegalArgumentException("invalid compression method");
}
this.method = method;
}
/**
* Returns the compression method of the entry.
*
* @return the compression method of the entry, or -1 if not specified
* @see #setMethod(int)
*/
public int getMethod() {
return method;
}
/**
* Sets the optional extra field data for the entry.
*
* <p> Invoking this method may change this entry's last modification
* time, last access time and creation time, if the {@code extra} field
* data includes the extensible timestamp fields, such as {@code NTFS tag
* 0x0001} or {@code Info-ZIP Extended Timestamp}, as specified in
* <a href="http://www.info-zip.org/doc/appnote-19970311-iz.zip">Info-ZIP
* Application Note 970311</a>.
*
* @param extra
* The extra field data bytes
*
* @throws IllegalArgumentException if the length of the specified
* extra field data is greater than 0xFFFF bytes
*
* @see #getExtra()
*/
public void setExtra(byte[] extra) {
setExtra0(extra, false);
}
/**
* Sets the optional extra field data for the entry.
*
* @param extra
* the extra field data bytes
* @param doZIP64
* if true, set size and csize from ZIP64 fields if present
*/
void setExtra0(byte[] extra, boolean doZIP64) {
if (extra != null) {
if (extra.length > 0xFFFF) {
throw new IllegalArgumentException("invalid extra field length");
}
// extra fields are in "HeaderID(2)DataSize(2)Data... format
int off = 0;
int len = extra.length;
while (off + 4 < len) {
int tag = get16(extra, off);
int sz = get16(extra, off + 2);
off += 4;
if (off + sz > len) // invalid data
break;
switch (tag) {
case EXTID_ZIP64:
if (doZIP64) {
// LOC extra zip64 entry MUST include BOTH original
// and compressed file size fields.
// If invalid zip64 extra fields, simply skip. Even
// it's rare, it's possible the entry size happens to
// be the magic value and it "accidently" has some
// bytes in extra match the id.
if (sz >= 16) {
size = get64(extra, off);
csize = get64(extra, off + 8);
}
}
break;
case EXTID_NTFS:
if (sz < 32) // reserved 4 bytes + tag 2 bytes + size 2 bytes
break; // m[a|c]time 24 bytes
int pos = off + 4; // reserved 4 bytes
if (get16(extra, pos) != 0x0001 || get16(extra, pos + 2) != 24)
break;
mtime = winTimeToFileTime(get64(extra, pos + 4));
atime = winTimeToFileTime(get64(extra, pos + 12));
ctime = winTimeToFileTime(get64(extra, pos + 20));
break;
case EXTID_EXTT:
int flag = Byte.toUnsignedInt(extra[off]);
int sz0 = 1;
// The CEN-header extra field contains the modification
// time only, or no timestamp at all. 'sz' is used to
// flag its presence or absence. But if mtime is present
// in LOC it must be present in CEN as well.
if ((flag & 0x1) != 0 && (sz0 + 4) <= sz) {
mtime = unixTimeToFileTime(get32(extra, off + sz0));
sz0 += 4;
}
if ((flag & 0x2) != 0 && (sz0 + 4) <= sz) {
atime = unixTimeToFileTime(get32(extra, off + sz0));
sz0 += 4;
}
if ((flag & 0x4) != 0 && (sz0 + 4) <= sz) {
ctime = unixTimeToFileTime(get32(extra, off + sz0));
sz0 += 4;
}
break;
default:
}
off += sz;
}
}
this.extra = extra;
}
/**
* Returns the extra field data for the entry.
*
* @return the extra field data for the entry, or null if none
*
* @see #setExtra(byte[])
*/
public byte[] getExtra() {
return extra;
}
/**
* Sets the optional comment string for the entry.
*
* <p>ZIP entry comments have maximum length of 0xffff. If the length of the
* specified comment string is greater than 0xFFFF bytes after encoding, only
* the first 0xFFFF bytes are output to the ZIP file entry.
*
* @param comment the comment string
*
* @see #getComment()
*/
public void setComment(String comment) {
this.comment = comment;
}
/**
* Returns the comment string for the entry.
*
* @return the comment string for the entry, or null if none
*
* @see #setComment(String)
*/
public String getComment() {
return comment;
}
/**
* Returns true if this is a directory entry. A directory entry is
* defined to be one whose name ends with a '/'.
* @return true if this is a directory entry
*/
public boolean isDirectory() {
return name.endsWith("/");
}
/**
* Returns a string representation of the ZIP entry.
*/
public String toString() {
return getName();
}
/**
* Returns the hash code value for this entry.
*/
public int hashCode() {
return name.hashCode();
}
/**
* Returns a copy of this entry.
*/
public Object clone() {
try {
ZipEntry e = (ZipEntry)super.clone();
e.extra = (extra == null) ? null : extra.clone();
return e;
} catch (CloneNotSupportedException e) {
// This should never happen, since we are Cloneable
throw new InternalError(e);
}
}
}