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(final 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(final java.util.zip.ZipEntry entry) throws ZipException { 116 super(entry); 117 setName(entry.getName()); 118 final 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(final 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 final 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(final File inputFile, final 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 final 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(final 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(final 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(final 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(final 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(final 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(final ZipExtraField[] fields) { 324 final List<ZipExtraField> newFields = new ArrayList<>(); 325 for (final 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(final 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(final ZipExtraField[] src){ 389 return copyOf(src, src.length); 390 } 391 392 private ZipExtraField[] copyOf(final ZipExtraField[] src, final int length) { 393 final 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(final 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(final 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 final ZipExtraField[] copy = extraFields; 453 final 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(final ZipShort type) { 468 if (extraFields == null) { 469 throw new java.util.NoSuchElementException(); 470 } 471 472 final List<ZipExtraField> newResult = new ArrayList<>(); 473 for (final 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(final ZipShort type) { 505 if (extraFields != null) { 506 for (final 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(final byte[] extra) throws RuntimeException { 536 try { 537 final ZipExtraField[] local = 538 ExtraFieldUtils.parse(extra, true, 539 ExtraFieldUtils.UnparseableExtraField.READ); 540 mergeExtraFields(local, true); 541 } catch (final ZipException e) { 542 // actually this is not possible as of Commons Compress 1.1 543 throw new RuntimeException("Error parsing extra fields for entry: " //NOSONAR 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(final byte[] b) { 563 try { 564 final ZipExtraField[] central = 565 ExtraFieldUtils.parse(b, false, 566 ExtraFieldUtils.UnparseableExtraField.READ); 567 mergeExtraFields(central, false); 568 } catch (final ZipException e) { 569 throw new RuntimeException(e.getMessage(), e); //NOSONAR 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 final 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 * @throws IllegalArgumentException if the specified size is less 638 * than 0 639 */ 640 @Override 641 public void setSize(final 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(final String name, final 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 final 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(final 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(final ZipExtraField[] f, final boolean local) 722 throws ZipException { 723 if (extraFields == null) { 724 setExtraFields(f); 725 } else { 726 for (final 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 final byte[] b = element.getLocalFileDataData(); 738 existing.parseFromLocalFileData(b, 0, b.length); 739 } else { 740 final 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 @Override 758 public Date getLastModifiedDate() { 759 return new Date(getTime()); 760 } 761 762 /* (non-Javadoc) 763 * @see java.lang.Object#equals(java.lang.Object) 764 */ 765 @Override 766 public boolean equals(final Object obj) { 767 if (this == obj) { 768 return true; 769 } 770 if (obj == null || getClass() != obj.getClass()) { 771 return false; 772 } 773 final ZipArchiveEntry other = (ZipArchiveEntry) obj; 774 final String myName = getName(); 775 final String otherName = other.getName(); 776 if (myName == null) { 777 if (otherName != null) { 778 return false; 779 } 780 } else if (!myName.equals(otherName)) { 781 return false; 782 } 783 String myComment = getComment(); 784 String otherComment = other.getComment(); 785 if (myComment == null) { 786 myComment = ""; 787 } 788 if (otherComment == null) { 789 otherComment = ""; 790 } 791 return getTime() == other.getTime() 792 && myComment.equals(otherComment) 793 && getInternalAttributes() == other.getInternalAttributes() 794 && getPlatform() == other.getPlatform() 795 && getExternalAttributes() == other.getExternalAttributes() 796 && getMethod() == other.getMethod() 797 && getSize() == other.getSize() 798 && getCrc() == other.getCrc() 799 && getCompressedSize() == other.getCompressedSize() 800 && Arrays.equals(getCentralDirectoryExtra(), 801 other.getCentralDirectoryExtra()) 802 && Arrays.equals(getLocalFileDataExtra(), 803 other.getLocalFileDataExtra()) 804 && gpb.equals(other.gpb); 805 } 806 807 /** 808 * Sets the "version made by" field. 809 * @param versionMadeBy "version made by" field 810 * @since 1.11 811 */ 812 public void setVersionMadeBy(final int versionMadeBy) { 813 this.versionMadeBy = versionMadeBy; 814 } 815 816 /** 817 * Sets the "version required to expand" field. 818 * @param versionRequired "version required to expand" field 819 * @since 1.11 820 */ 821 public void setVersionRequired(final int versionRequired) { 822 this.versionRequired = versionRequired; 823 } 824 825 /** 826 * The "version required to expand" field. 827 * @return "version required to expand" field 828 * @since 1.11 829 */ 830 public int getVersionRequired() { 831 return versionRequired; 832 } 833 834 /** 835 * The "version made by" field. 836 * @return "version made by" field 837 * @since 1.11 838 */ 839 public int getVersionMadeBy() { 840 return versionMadeBy; 841 } 842 843 /** 844 * The content of the flags field. 845 * @return content of the flags field 846 * @since 1.11 847 */ 848 public int getRawFlag() { 849 return rawFlag; 850 } 851 852 /** 853 * Sets the content of the flags field. 854 * @param rawFlag content of the flags field 855 * @since 1.11 856 */ 857 public void setRawFlag(final int rawFlag) { 858 this.rawFlag = rawFlag; 859 } 860}