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 */ 018package org.apache.commons.compress.archivers.zip; 019 020import org.apache.commons.compress.archivers.ArchiveEntry; 021 022import java.io.File; 023import java.util.ArrayList; 024import java.util.Arrays; 025import java.util.Date; 026import java.util.List; 027import java.util.zip.ZipException; 028 029/** 030 * Extension that adds better handling of extra fields and provides 031 * access to the internal and external file attributes. 032 * 033 * <p>The extra data is expected to follow the recommendation of 034 * <a href="http://www.pkware.com/documents/casestudies/APPNOTE.TXT">APPNOTE.TXT</a>:</p> 035 * <ul> 036 * <li>the extra byte array consists of a sequence of extra fields</li> 037 * <li>each extra fields starts by a two byte header id followed by 038 * a two byte sequence holding the length of the remainder of 039 * data.</li> 040 * </ul> 041 * 042 * <p>Any extra data that cannot be parsed by the rules above will be 043 * consumed as "unparseable" extra data and treated differently by the 044 * methods of this class. Versions prior to Apache Commons Compress 045 * 1.1 would have thrown an exception if any attempt was made to read 046 * or write extra data not conforming to the recommendation.</p> 047 * 048 * @NotThreadSafe 049 */ 050public class ZipArchiveEntry extends java.util.zip.ZipEntry 051 implements ArchiveEntry { 052 053 public static final int PLATFORM_UNIX = 3; 054 public static final int PLATFORM_FAT = 0; 055 public static final int CRC_UNKNOWN = -1; 056 private static final int SHORT_MASK = 0xFFFF; 057 private static final int SHORT_SHIFT = 16; 058 private static final byte[] EMPTY = new byte[0]; 059 060 /** 061 * The {@link java.util.zip.ZipEntry} base class only supports 062 * the compression methods STORED and DEFLATED. We override the 063 * field so that any compression methods can be used. 064 * <p> 065 * The default value -1 means that the method has not been specified. 066 * 067 * @see <a href="https://issues.apache.org/jira/browse/COMPRESS-93" 068 * >COMPRESS-93</a> 069 */ 070 private int method = ZipMethod.UNKNOWN_CODE; 071 072 /** 073 * The {@link java.util.zip.ZipEntry#setSize} method in the base 074 * class throws an IllegalArgumentException if the size is bigger 075 * than 2GB for Java versions < 7. Need to keep our own size 076 * information for Zip64 support. 077 */ 078 private long size = SIZE_UNKNOWN; 079 080 private int internalAttributes = 0; 081 private int versionRequired; 082 private int versionMadeBy; 083 private int platform = PLATFORM_FAT; 084 private int rawFlag; 085 private long externalAttributes = 0; 086 private ZipExtraField[] extraFields; 087 private UnparseableExtraFieldData unparseableExtra = null; 088 private String name = null; 089 private byte[] rawName = null; 090 private GeneralPurposeBit gpb = new GeneralPurposeBit(); 091 private static final ZipExtraField[] noExtraFields = new ZipExtraField[0]; 092 093 /** 094 * Creates a new zip entry with the specified name. 095 * 096 * <p>Assumes the entry represents a directory if and only if the 097 * name ends with a forward slash "/".</p> 098 * 099 * @param name the name of the entry 100 */ 101 public ZipArchiveEntry(String name) { 102 super(name); 103 setName(name); 104 } 105 106 /** 107 * Creates a new zip entry with fields taken from the specified zip entry. 108 * 109 * <p>Assumes the entry represents a directory if and only if the 110 * name ends with a forward slash "/".</p> 111 * 112 * @param entry the entry to get fields from 113 * @throws ZipException on error 114 */ 115 public ZipArchiveEntry(java.util.zip.ZipEntry entry) throws ZipException { 116 super(entry); 117 setName(entry.getName()); 118 byte[] extra = entry.getExtra(); 119 if (extra != null) { 120 setExtraFields(ExtraFieldUtils.parse(extra, true, 121 ExtraFieldUtils 122 .UnparseableExtraField.READ)); 123 } else { 124 // initializes extra data to an empty byte array 125 setExtra(); 126 } 127 setMethod(entry.getMethod()); 128 this.size = entry.getSize(); 129 } 130 131 /** 132 * Creates a new zip entry with fields taken from the specified zip entry. 133 * 134 * <p>Assumes the entry represents a directory if and only if the 135 * name ends with a forward slash "/".</p> 136 * 137 * @param entry the entry to get fields from 138 * @throws ZipException on error 139 */ 140 public ZipArchiveEntry(ZipArchiveEntry entry) throws ZipException { 141 this((java.util.zip.ZipEntry) entry); 142 setInternalAttributes(entry.getInternalAttributes()); 143 setExternalAttributes(entry.getExternalAttributes()); 144 setExtraFields(getAllExtraFieldsNoCopy()); 145 setPlatform(entry.getPlatform()); 146 GeneralPurposeBit other = entry.getGeneralPurposeBit(); 147 setGeneralPurposeBit(other == null ? null : 148 (GeneralPurposeBit) other.clone()); 149 } 150 151 /** 152 */ 153 protected ZipArchiveEntry() { 154 this(""); 155 } 156 157 /** 158 * Creates a new zip entry taking some information from the given 159 * file and using the provided name. 160 * 161 * <p>The name will be adjusted to end with a forward slash "/" if 162 * the file is a directory. If the file is not a directory a 163 * potential trailing forward slash will be stripped from the 164 * entry name.</p> 165 * @param inputFile file to create the entry from 166 * @param entryName name of the entry 167 */ 168 public ZipArchiveEntry(File inputFile, String entryName) { 169 this(inputFile.isDirectory() && !entryName.endsWith("/") ? 170 entryName + "/" : entryName); 171 if (inputFile.isFile()){ 172 setSize(inputFile.length()); 173 } 174 setTime(inputFile.lastModified()); 175 // TODO are there any other fields we can set here? 176 } 177 178 /** 179 * Overwrite clone. 180 * @return a cloned copy of this ZipArchiveEntry 181 */ 182 @Override 183 public Object clone() { 184 ZipArchiveEntry e = (ZipArchiveEntry) super.clone(); 185 186 e.setInternalAttributes(getInternalAttributes()); 187 e.setExternalAttributes(getExternalAttributes()); 188 e.setExtraFields(getAllExtraFieldsNoCopy()); 189 return e; 190 } 191 192 /** 193 * Returns the compression method of this entry, or -1 if the 194 * compression method has not been specified. 195 * 196 * @return compression method 197 * 198 * @since 1.1 199 */ 200 @Override 201 public int getMethod() { 202 return method; 203 } 204 205 /** 206 * Sets the compression method of this entry. 207 * 208 * @param method compression method 209 * 210 * @since 1.1 211 */ 212 @Override 213 public void setMethod(int method) { 214 if (method < 0) { 215 throw new IllegalArgumentException( 216 "ZIP compression method can not be negative: " + method); 217 } 218 this.method = method; 219 } 220 221 /** 222 * Retrieves the internal file attributes. 223 * 224 * <p><b>Note</b>: {@link ZipArchiveInputStream} is unable to fill 225 * this field, you must use {@link ZipFile} if you want to read 226 * entries using this attribute.</p> 227 * 228 * @return the internal file attributes 229 */ 230 public int getInternalAttributes() { 231 return internalAttributes; 232 } 233 234 /** 235 * Sets the internal file attributes. 236 * @param value an <code>int</code> value 237 */ 238 public void setInternalAttributes(int value) { 239 internalAttributes = value; 240 } 241 242 /** 243 * Retrieves the external file attributes. 244 * 245 * <p><b>Note</b>: {@link ZipArchiveInputStream} is unable to fill 246 * this field, you must use {@link ZipFile} if you want to read 247 * entries using this attribute.</p> 248 * 249 * @return the external file attributes 250 */ 251 public long getExternalAttributes() { 252 return externalAttributes; 253 } 254 255 /** 256 * Sets the external file attributes. 257 * @param value an <code>long</code> value 258 */ 259 public void setExternalAttributes(long value) { 260 externalAttributes = value; 261 } 262 263 /** 264 * Sets Unix permissions in a way that is understood by Info-Zip's 265 * unzip command. 266 * @param mode an <code>int</code> value 267 */ 268 public void setUnixMode(int mode) { 269 // CheckStyle:MagicNumberCheck OFF - no point 270 setExternalAttributes((mode << SHORT_SHIFT) 271 // MS-DOS read-only attribute 272 | ((mode & 0200) == 0 ? 1 : 0) 273 // MS-DOS directory flag 274 | (isDirectory() ? 0x10 : 0)); 275 // CheckStyle:MagicNumberCheck ON 276 platform = PLATFORM_UNIX; 277 } 278 279 /** 280 * Unix permission. 281 * @return the unix permissions 282 */ 283 public int getUnixMode() { 284 return platform != PLATFORM_UNIX ? 0 : 285 (int) ((getExternalAttributes() >> SHORT_SHIFT) & SHORT_MASK); 286 } 287 288 /** 289 * Returns true if this entry represents a unix symlink, 290 * in which case the entry's content contains the target path 291 * for the symlink. 292 * 293 * @since 1.5 294 * @return true if the entry represents a unix symlink, false otherwise. 295 */ 296 public boolean isUnixSymlink() { 297 return (getUnixMode() & UnixStat.LINK_FLAG) == UnixStat.LINK_FLAG; 298 } 299 300 /** 301 * Platform specification to put into the "version made 302 * by" part of the central file header. 303 * 304 * @return PLATFORM_FAT unless {@link #setUnixMode setUnixMode} 305 * has been called, in which case PLATFORM_UNIX will be returned. 306 */ 307 public int getPlatform() { 308 return platform; 309 } 310 311 /** 312 * Set the platform (UNIX or FAT). 313 * @param platform an <code>int</code> value - 0 is FAT, 3 is UNIX 314 */ 315 protected void setPlatform(int platform) { 316 this.platform = platform; 317 } 318 319 /** 320 * Replaces all currently attached extra fields with the new array. 321 * @param fields an array of extra fields 322 */ 323 public void setExtraFields(ZipExtraField[] fields) { 324 List<ZipExtraField> newFields = new ArrayList<ZipExtraField>(); 325 for (ZipExtraField field : fields) { 326 if (field instanceof UnparseableExtraFieldData) { 327 unparseableExtra = (UnparseableExtraFieldData) field; 328 } else { 329 newFields.add( field); 330 } 331 } 332 extraFields = newFields.toArray(new ZipExtraField[newFields.size()]); 333 setExtra(); 334 } 335 336 /** 337 * Retrieves all extra fields that have been parsed successfully. 338 * 339 * <p><b>Note</b>: The set of extra fields may be incomplete when 340 * {@link ZipArchiveInputStream} has been used as some extra 341 * fields use the central directory to store additional 342 * information.</p> 343 * 344 * @return an array of the extra fields 345 */ 346 public ZipExtraField[] getExtraFields() { 347 return getParseableExtraFields(); 348 } 349 350 /** 351 * Retrieves extra fields. 352 * @param includeUnparseable whether to also return unparseable 353 * extra fields as {@link UnparseableExtraFieldData} if such data 354 * exists. 355 * @return an array of the extra fields 356 * 357 * @since 1.1 358 */ 359 public ZipExtraField[] getExtraFields(boolean includeUnparseable) { 360 return includeUnparseable ? 361 getAllExtraFields() : 362 getParseableExtraFields(); 363 } 364 365 private ZipExtraField[] getParseableExtraFieldsNoCopy() { 366 if (extraFields == null) { 367 return noExtraFields; 368 } 369 return extraFields; 370 } 371 372 private ZipExtraField[] getParseableExtraFields() { 373 final ZipExtraField[] parseableExtraFields = getParseableExtraFieldsNoCopy(); 374 return (parseableExtraFields == extraFields) ? copyOf(parseableExtraFields) : parseableExtraFields; 375 } 376 377 /** 378 * Get all extra fields, including unparseable ones. 379 * @return An array of all extra fields. Not necessarily a copy of internal data structures, hence private method 380 */ 381 private ZipExtraField[] getAllExtraFieldsNoCopy() { 382 if (extraFields == null) { 383 return getUnparseableOnly(); 384 } 385 return unparseableExtra != null ? getMergedFields() : extraFields; 386 } 387 388 private ZipExtraField[] copyOf(ZipExtraField[] src){ 389 return copyOf(src, src.length); 390 } 391 392 private ZipExtraField[] copyOf(ZipExtraField[] src, int length) { 393 ZipExtraField[] cpy = new ZipExtraField[length]; 394 System.arraycopy(src, 0, cpy, 0, Math.min(src.length, length)); 395 return cpy; 396 } 397 398 private ZipExtraField[] getMergedFields() { 399 final ZipExtraField[] zipExtraFields = copyOf(extraFields, extraFields.length + 1); 400 zipExtraFields[extraFields.length] = unparseableExtra; 401 return zipExtraFields; 402 } 403 404 private ZipExtraField[] getUnparseableOnly() { 405 return unparseableExtra == null ? noExtraFields : new ZipExtraField[] { unparseableExtra }; 406 } 407 408 private ZipExtraField[] getAllExtraFields() { 409 final ZipExtraField[] allExtraFieldsNoCopy = getAllExtraFieldsNoCopy(); 410 return (allExtraFieldsNoCopy == extraFields) ? copyOf( allExtraFieldsNoCopy) : allExtraFieldsNoCopy; 411 } 412 /** 413 * Adds an extra field - replacing an already present extra field 414 * of the same type. 415 * 416 * <p>If no extra field of the same type exists, the field will be 417 * added as last field.</p> 418 * @param ze an extra field 419 */ 420 public void addExtraField(ZipExtraField ze) { 421 if (ze instanceof UnparseableExtraFieldData) { 422 unparseableExtra = (UnparseableExtraFieldData) ze; 423 } else { 424 if (extraFields == null) { 425 extraFields = new ZipExtraField[]{ ze}; 426 } else { 427 if (getExtraField(ze.getHeaderId())!= null){ 428 removeExtraField(ze.getHeaderId()); 429 } 430 final ZipExtraField[] zipExtraFields = copyOf(extraFields, extraFields.length + 1); 431 zipExtraFields[zipExtraFields.length -1] = ze; 432 extraFields = zipExtraFields; 433 } 434 } 435 setExtra(); 436 } 437 438 /** 439 * Adds an extra field - replacing an already present extra field 440 * of the same type. 441 * 442 * <p>The new extra field will be the first one.</p> 443 * @param ze an extra field 444 */ 445 public void addAsFirstExtraField(ZipExtraField ze) { 446 if (ze instanceof UnparseableExtraFieldData) { 447 unparseableExtra = (UnparseableExtraFieldData) ze; 448 } else { 449 if (getExtraField(ze.getHeaderId()) != null){ 450 removeExtraField(ze.getHeaderId()); 451 } 452 ZipExtraField[] copy = extraFields; 453 int newLen = extraFields != null ? extraFields.length + 1: 1; 454 extraFields = new ZipExtraField[newLen]; 455 extraFields[0] = ze; 456 if (copy != null){ 457 System.arraycopy(copy, 0, extraFields, 1, extraFields.length - 1); 458 } 459 } 460 setExtra(); 461 } 462 463 /** 464 * Remove an extra field. 465 * @param type the type of extra field to remove 466 */ 467 public void removeExtraField(ZipShort type) { 468 if (extraFields == null) { 469 throw new java.util.NoSuchElementException(); 470 } 471 472 List<ZipExtraField> newResult = new ArrayList<ZipExtraField>(); 473 for (ZipExtraField extraField : extraFields) { 474 if (!type.equals(extraField.getHeaderId())){ 475 newResult.add( extraField); 476 } 477 } 478 if (extraFields.length == newResult.size()) { 479 throw new java.util.NoSuchElementException(); 480 } 481 extraFields = newResult.toArray(new ZipExtraField[newResult.size()]); 482 setExtra(); 483 } 484 485 /** 486 * Removes unparseable extra field data. 487 * 488 * @since 1.1 489 */ 490 public void removeUnparseableExtraFieldData() { 491 if (unparseableExtra == null) { 492 throw new java.util.NoSuchElementException(); 493 } 494 unparseableExtra = null; 495 setExtra(); 496 } 497 498 /** 499 * Looks up an extra field by its header id. 500 * 501 * @param type the header id 502 * @return null if no such field exists. 503 */ 504 public ZipExtraField getExtraField(ZipShort type) { 505 if (extraFields != null) { 506 for (ZipExtraField extraField : extraFields) { 507 if (type.equals(extraField.getHeaderId())) { 508 return extraField; 509 } 510 } 511 } 512 return null; 513 } 514 515 /** 516 * Looks up extra field data that couldn't be parsed correctly. 517 * 518 * @return null if no such field exists. 519 * 520 * @since 1.1 521 */ 522 public UnparseableExtraFieldData getUnparseableExtraFieldData() { 523 return unparseableExtra; 524 } 525 526 /** 527 * Parses the given bytes as extra field data and consumes any 528 * unparseable data as an {@link UnparseableExtraFieldData} 529 * instance. 530 * @param extra an array of bytes to be parsed into extra fields 531 * @throws RuntimeException if the bytes cannot be parsed 532 * @throws RuntimeException on error 533 */ 534 @Override 535 public void setExtra(byte[] extra) throws RuntimeException { 536 try { 537 ZipExtraField[] local = 538 ExtraFieldUtils.parse(extra, true, 539 ExtraFieldUtils.UnparseableExtraField.READ); 540 mergeExtraFields(local, true); 541 } catch (ZipException e) { 542 // actually this is not possible as of Commons Compress 1.1 543 throw new RuntimeException("Error parsing extra fields for entry: " 544 + getName() + " - " + e.getMessage(), e); 545 } 546 } 547 548 /** 549 * Unfortunately {@link java.util.zip.ZipOutputStream 550 * java.util.zip.ZipOutputStream} seems to access the extra data 551 * directly, so overriding getExtra doesn't help - we need to 552 * modify super's data directly. 553 */ 554 protected void setExtra() { 555 super.setExtra(ExtraFieldUtils.mergeLocalFileDataData(getAllExtraFieldsNoCopy())); 556 } 557 558 /** 559 * Sets the central directory part of extra fields. 560 * @param b an array of bytes to be parsed into extra fields 561 */ 562 public void setCentralDirectoryExtra(byte[] b) { 563 try { 564 ZipExtraField[] central = 565 ExtraFieldUtils.parse(b, false, 566 ExtraFieldUtils.UnparseableExtraField.READ); 567 mergeExtraFields(central, false); 568 } catch (ZipException e) { 569 throw new RuntimeException(e.getMessage(), e); 570 } 571 } 572 573 /** 574 * Retrieves the extra data for the local file data. 575 * @return the extra data for local file 576 */ 577 public byte[] getLocalFileDataExtra() { 578 byte[] extra = getExtra(); 579 return extra != null ? extra : EMPTY; 580 } 581 582 /** 583 * Retrieves the extra data for the central directory. 584 * @return the central directory extra data 585 */ 586 public byte[] getCentralDirectoryExtra() { 587 return ExtraFieldUtils.mergeCentralDirectoryData(getAllExtraFieldsNoCopy()); 588 } 589 590 /** 591 * Get the name of the entry. 592 * @return the entry name 593 */ 594 @Override 595 public String getName() { 596 return name == null ? super.getName() : name; 597 } 598 599 /** 600 * Is this entry a directory? 601 * @return true if the entry is a directory 602 */ 603 @Override 604 public boolean isDirectory() { 605 return getName().endsWith("/"); 606 } 607 608 /** 609 * Set the name of the entry. 610 * @param name the name to use 611 */ 612 protected void setName(String name) { 613 if (name != null && getPlatform() == PLATFORM_FAT 614 && !name.contains("/")) { 615 name = name.replace('\\', '/'); 616 } 617 this.name = name; 618 } 619 620 /** 621 * Gets the uncompressed size of the entry data. 622 * 623 * <p><b>Note</b>: {@link ZipArchiveInputStream} may create 624 * entries that return {@link #SIZE_UNKNOWN SIZE_UNKNOWN} as long 625 * as the entry hasn't been read completely.</p> 626 * 627 * @return the entry size 628 */ 629 @Override 630 public long getSize() { 631 return size; 632 } 633 634 /** 635 * Sets the uncompressed size of the entry data. 636 * @param size the uncompressed size in bytes 637 * @exception IllegalArgumentException if the specified size is less 638 * than 0 639 */ 640 @Override 641 public void setSize(long size) { 642 if (size < 0) { 643 throw new IllegalArgumentException("invalid entry size"); 644 } 645 this.size = size; 646 } 647 648 /** 649 * Sets the name using the raw bytes and the string created from 650 * it by guessing or using the configured encoding. 651 * @param name the name to use created from the raw bytes using 652 * the guessed or configured encoding 653 * @param rawName the bytes originally read as name from the 654 * archive 655 * @since 1.2 656 */ 657 protected void setName(String name, byte[] rawName) { 658 setName(name); 659 this.rawName = rawName; 660 } 661 662 /** 663 * Returns the raw bytes that made up the name before it has been 664 * converted using the configured or guessed encoding. 665 * 666 * <p>This method will return null if this instance has not been 667 * read from an archive.</p> 668 * 669 * @return the raw name bytes 670 * @since 1.2 671 */ 672 public byte[] getRawName() { 673 if (rawName != null) { 674 byte[] b = new byte[rawName.length]; 675 System.arraycopy(rawName, 0, b, 0, rawName.length); 676 return b; 677 } 678 return null; 679 } 680 681 /** 682 * Get the hashCode of the entry. 683 * This uses the name as the hashcode. 684 * @return a hashcode. 685 */ 686 @Override 687 public int hashCode() { 688 // this method has severe consequences on performance. We cannot rely 689 // on the super.hashCode() method since super.getName() always return 690 // the empty string in the current implemention (there's no setter) 691 // so it is basically draining the performance of a hashmap lookup 692 return getName().hashCode(); 693 } 694 695 /** 696 * The "general purpose bit" field. 697 * @return the general purpose bit 698 * @since 1.1 699 */ 700 public GeneralPurposeBit getGeneralPurposeBit() { 701 return gpb; 702 } 703 704 /** 705 * The "general purpose bit" field. 706 * @param b the general purpose bit 707 * @since 1.1 708 */ 709 public void setGeneralPurposeBit(GeneralPurposeBit b) { 710 gpb = b; 711 } 712 713 /** 714 * If there are no extra fields, use the given fields as new extra 715 * data - otherwise merge the fields assuming the existing fields 716 * and the new fields stem from different locations inside the 717 * archive. 718 * @param f the extra fields to merge 719 * @param local whether the new fields originate from local data 720 */ 721 private void mergeExtraFields(ZipExtraField[] f, boolean local) 722 throws ZipException { 723 if (extraFields == null) { 724 setExtraFields(f); 725 } else { 726 for (ZipExtraField element : f) { 727 ZipExtraField existing; 728 if (element instanceof UnparseableExtraFieldData) { 729 existing = unparseableExtra; 730 } else { 731 existing = getExtraField(element.getHeaderId()); 732 } 733 if (existing == null) { 734 addExtraField(element); 735 } else { 736 if (local) { 737 byte[] b = element.getLocalFileDataData(); 738 existing.parseFromLocalFileData(b, 0, b.length); 739 } else { 740 byte[] b = element.getCentralDirectoryData(); 741 existing.parseFromCentralDirectoryData(b, 0, b.length); 742 } 743 } 744 } 745 setExtra(); 746 } 747 } 748 749 /** 750 * Wraps {@link java.util.zip.ZipEntry#getTime} with a {@link Date} as the 751 * entry's last modified date. 752 * 753 * <p>Changes to the implementation of {@link java.util.zip.ZipEntry#getTime} 754 * leak through and the returned value may depend on your local 755 * time zone as well as your version of Java.</p> 756 */ 757 public Date getLastModifiedDate() { 758 return new Date(getTime()); 759 } 760 761 /* (non-Javadoc) 762 * @see java.lang.Object#equals(java.lang.Object) 763 */ 764 @Override 765 public boolean equals(Object obj) { 766 if (this == obj) { 767 return true; 768 } 769 if (obj == null || getClass() != obj.getClass()) { 770 return false; 771 } 772 ZipArchiveEntry other = (ZipArchiveEntry) obj; 773 String myName = getName(); 774 String otherName = other.getName(); 775 if (myName == null) { 776 if (otherName != null) { 777 return false; 778 } 779 } else if (!myName.equals(otherName)) { 780 return false; 781 } 782 String myComment = getComment(); 783 String otherComment = other.getComment(); 784 if (myComment == null) { 785 myComment = ""; 786 } 787 if (otherComment == null) { 788 otherComment = ""; 789 } 790 return getTime() == other.getTime() 791 && myComment.equals(otherComment) 792 && getInternalAttributes() == other.getInternalAttributes() 793 && getPlatform() == other.getPlatform() 794 && getExternalAttributes() == other.getExternalAttributes() 795 && getMethod() == other.getMethod() 796 && getSize() == other.getSize() 797 && getCrc() == other.getCrc() 798 && getCompressedSize() == other.getCompressedSize() 799 && Arrays.equals(getCentralDirectoryExtra(), 800 other.getCentralDirectoryExtra()) 801 && Arrays.equals(getLocalFileDataExtra(), 802 other.getLocalFileDataExtra()) 803 && gpb.equals(other.gpb); 804 } 805 806 /** 807 * Sets the "version made by" field. 808 * @param versionMadeBy "version made by" field 809 * @since 1.11 810 */ 811 public void setVersionMadeBy(int versionMadeBy) { 812 this.versionMadeBy = versionMadeBy; 813 } 814 815 /** 816 * Sets the "version required to expand" field. 817 * @param versionRequired "version required to expand" field 818 * @since 1.11 819 */ 820 public void setVersionRequired(int versionRequired) { 821 this.versionRequired = versionRequired; 822 } 823 824 /** 825 * The "version required to expand" field. 826 * @return "version required to expand" field 827 * @since 1.11 828 */ 829 public int getVersionRequired() { 830 return versionRequired; 831 } 832 833 /** 834 * The "version made by" field. 835 * @return "version made by" field 836 * @since 1.11 837 */ 838 public int getVersionMadeBy() { 839 return versionMadeBy; 840 } 841 842 /** 843 * The content of the flags field. 844 * @return content of the flags field 845 * @since 1.11 846 */ 847 public int getRawFlag() { 848 return rawFlag; 849 } 850 851 /** 852 * Sets the content of the flags field. 853 * @param rawFlag content of the flags field 854 * @since 1.11 855 */ 856 public void setRawFlag(int rawFlag) { 857 this.rawFlag = rawFlag; 858 } 859}