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.sevenz; 019 020import java.util.Arrays; 021import java.util.Calendar; 022import java.util.Collections; 023import java.util.Date; 024import java.util.Iterator; 025import java.util.LinkedList; 026import java.util.Objects; 027import java.util.TimeZone; 028 029import org.apache.commons.compress.archivers.ArchiveEntry; 030 031/** 032 * An entry in a 7z archive. 033 * 034 * @NotThreadSafe 035 * @since 1.6 036 */ 037public class SevenZArchiveEntry implements ArchiveEntry { 038 private String name; 039 private boolean hasStream; 040 private boolean isDirectory; 041 private boolean isAntiItem; 042 private boolean hasCreationDate; 043 private boolean hasLastModifiedDate; 044 private boolean hasAccessDate; 045 private long creationDate; 046 private long lastModifiedDate; 047 private long accessDate; 048 private boolean hasWindowsAttributes; 049 private int windowsAttributes; 050 private boolean hasCrc; 051 private long crc, compressedCrc; 052 private long size, compressedSize; 053 private Iterable<? extends SevenZMethodConfiguration> contentMethods; 054 static final SevenZArchiveEntry[] EMPTY_SEVEN_Z_ARCHIVE_ENTRY_ARRAY = new SevenZArchiveEntry[0]; 055 056 public SevenZArchiveEntry() { 057 } 058 059 /** 060 * Get this entry's name. 061 * 062 * <p>This method returns the raw name as it is stored inside of the archive.</p> 063 * 064 * @return This entry's name. 065 */ 066 @Override 067 public String getName() { 068 return name; 069 } 070 071 /** 072 * Set this entry's name. 073 * 074 * @param name This entry's new name. 075 */ 076 public void setName(final String name) { 077 this.name = name; 078 } 079 080 /** 081 * Whether there is any content associated with this entry. 082 * @return whether there is any content associated with this entry. 083 */ 084 public boolean hasStream() { 085 return hasStream; 086 } 087 088 /** 089 * Sets whether there is any content associated with this entry. 090 * @param hasStream whether there is any content associated with this entry. 091 */ 092 public void setHasStream(final boolean hasStream) { 093 this.hasStream = hasStream; 094 } 095 096 /** 097 * Return whether or not this entry represents a directory. 098 * 099 * @return True if this entry is a directory. 100 */ 101 @Override 102 public boolean isDirectory() { 103 return isDirectory; 104 } 105 106 /** 107 * Sets whether or not this entry represents a directory. 108 * 109 * @param isDirectory True if this entry is a directory. 110 */ 111 public void setDirectory(final boolean isDirectory) { 112 this.isDirectory = isDirectory; 113 } 114 115 /** 116 * Indicates whether this is an "anti-item" used in differential backups, 117 * meaning it should delete the same file from a previous backup. 118 * @return true if it is an anti-item, false otherwise 119 */ 120 public boolean isAntiItem() { 121 return isAntiItem; 122 } 123 124 /** 125 * Sets whether this is an "anti-item" used in differential backups, 126 * meaning it should delete the same file from a previous backup. 127 * @param isAntiItem true if it is an anti-item, false otherwise 128 */ 129 public void setAntiItem(final boolean isAntiItem) { 130 this.isAntiItem = isAntiItem; 131 } 132 133 /** 134 * Returns whether this entry has got a creation date at all. 135 * @return whether the entry has got a creation date 136 */ 137 public boolean getHasCreationDate() { 138 return hasCreationDate; 139 } 140 141 /** 142 * Sets whether this entry has got a creation date at all. 143 * @param hasCreationDate whether the entry has got a creation date 144 */ 145 public void setHasCreationDate(final boolean hasCreationDate) { 146 this.hasCreationDate = hasCreationDate; 147 } 148 149 /** 150 * Gets the creation date. 151 * @throws UnsupportedOperationException if the entry hasn't got a 152 * creation date. 153 * @return the creation date 154 */ 155 public Date getCreationDate() { 156 if (hasCreationDate) { 157 return ntfsTimeToJavaTime(creationDate); 158 } 159 throw new UnsupportedOperationException( 160 "The entry doesn't have this timestamp"); 161 } 162 163 /** 164 * Sets the creation date using NTFS time (100 nanosecond units 165 * since 1 January 1601) 166 * @param ntfsCreationDate the creation date 167 */ 168 public void setCreationDate(final long ntfsCreationDate) { 169 this.creationDate = ntfsCreationDate; 170 } 171 172 /** 173 * Sets the creation date, 174 * @param creationDate the creation date 175 */ 176 public void setCreationDate(final Date creationDate) { 177 hasCreationDate = creationDate != null; 178 if (hasCreationDate) { 179 this.creationDate = javaTimeToNtfsTime(creationDate); 180 } 181 } 182 183 /** 184 * Returns whether this entry has got a last modified date at all. 185 * @return whether this entry has got a last modified date at all 186 */ 187 public boolean getHasLastModifiedDate() { 188 return hasLastModifiedDate; 189 } 190 191 /** 192 * Sets whether this entry has got a last modified date at all. 193 * @param hasLastModifiedDate whether this entry has got a last 194 * modified date at all 195 */ 196 public void setHasLastModifiedDate(final boolean hasLastModifiedDate) { 197 this.hasLastModifiedDate = hasLastModifiedDate; 198 } 199 200 /** 201 * Gets the last modified date. 202 * @throws UnsupportedOperationException if the entry hasn't got a 203 * last modified date. 204 * @return the last modified date 205 */ 206 @Override 207 public Date getLastModifiedDate() { 208 if (hasLastModifiedDate) { 209 return ntfsTimeToJavaTime(lastModifiedDate); 210 } 211 throw new UnsupportedOperationException( 212 "The entry doesn't have this timestamp"); 213 } 214 215 /** 216 * Sets the last modified date using NTFS time (100 nanosecond 217 * units since 1 January 1601) 218 * @param ntfsLastModifiedDate the last modified date 219 */ 220 public void setLastModifiedDate(final long ntfsLastModifiedDate) { 221 this.lastModifiedDate = ntfsLastModifiedDate; 222 } 223 224 /** 225 * Sets the last modified date, 226 * @param lastModifiedDate the last modified date 227 */ 228 public void setLastModifiedDate(final Date lastModifiedDate) { 229 hasLastModifiedDate = lastModifiedDate != null; 230 if (hasLastModifiedDate) { 231 this.lastModifiedDate = javaTimeToNtfsTime(lastModifiedDate); 232 } 233 } 234 235 /** 236 * Returns whether this entry has got an access date at all. 237 * @return whether this entry has got an access date at all. 238 */ 239 public boolean getHasAccessDate() { 240 return hasAccessDate; 241 } 242 243 /** 244 * Sets whether this entry has got an access date at all. 245 * @param hasAcessDate whether this entry has got an access date at all. 246 */ 247 public void setHasAccessDate(final boolean hasAcessDate) { 248 this.hasAccessDate = hasAcessDate; 249 } 250 251 /** 252 * Gets the access date. 253 * @throws UnsupportedOperationException if the entry hasn't got a 254 * access date. 255 * @return the access date 256 */ 257 public Date getAccessDate() { 258 if (hasAccessDate) { 259 return ntfsTimeToJavaTime(accessDate); 260 } 261 throw new UnsupportedOperationException( 262 "The entry doesn't have this timestamp"); 263 } 264 265 /** 266 * Sets the access date using NTFS time (100 nanosecond units 267 * since 1 January 1601) 268 * @param ntfsAccessDate the access date 269 */ 270 public void setAccessDate(final long ntfsAccessDate) { 271 this.accessDate = ntfsAccessDate; 272 } 273 274 /** 275 * Sets the access date, 276 * @param accessDate the access date 277 */ 278 public void setAccessDate(final Date accessDate) { 279 hasAccessDate = accessDate != null; 280 if (hasAccessDate) { 281 this.accessDate = javaTimeToNtfsTime(accessDate); 282 } 283 } 284 285 /** 286 * Returns whether this entry has windows attributes. 287 * @return whether this entry has windows attributes. 288 */ 289 public boolean getHasWindowsAttributes() { 290 return hasWindowsAttributes; 291 } 292 293 /** 294 * Sets whether this entry has windows attributes. 295 * @param hasWindowsAttributes whether this entry has windows attributes. 296 */ 297 public void setHasWindowsAttributes(final boolean hasWindowsAttributes) { 298 this.hasWindowsAttributes = hasWindowsAttributes; 299 } 300 301 /** 302 * Gets the windows attributes. 303 * @return the windows attributes 304 */ 305 public int getWindowsAttributes() { 306 return windowsAttributes; 307 } 308 309 /** 310 * Sets the windows attributes. 311 * @param windowsAttributes the windows attributes 312 */ 313 public void setWindowsAttributes(final int windowsAttributes) { 314 this.windowsAttributes = windowsAttributes; 315 } 316 317 /** 318 * Returns whether this entry has got a crc. 319 * 320 * <p>In general entries without streams don't have a CRC either.</p> 321 * @return whether this entry has got a crc. 322 */ 323 public boolean getHasCrc() { 324 return hasCrc; 325 } 326 327 /** 328 * Sets whether this entry has got a crc. 329 * @param hasCrc whether this entry has got a crc. 330 */ 331 public void setHasCrc(final boolean hasCrc) { 332 this.hasCrc = hasCrc; 333 } 334 335 /** 336 * Gets the CRC. 337 * @deprecated use getCrcValue instead. 338 * @return the CRC 339 */ 340 @Deprecated 341 public int getCrc() { 342 return (int) crc; 343 } 344 345 /** 346 * Sets the CRC. 347 * @deprecated use setCrcValue instead. 348 * @param crc the CRC 349 */ 350 @Deprecated 351 public void setCrc(final int crc) { 352 this.crc = crc; 353 } 354 355 /** 356 * Gets the CRC. 357 * @since 1.7 358 * @return the CRC 359 */ 360 public long getCrcValue() { 361 return crc; 362 } 363 364 /** 365 * Sets the CRC. 366 * @since 1.7 367 * @param crc the CRC 368 */ 369 public void setCrcValue(final long crc) { 370 this.crc = crc; 371 } 372 373 /** 374 * Gets the compressed CRC. 375 * @deprecated use getCompressedCrcValue instead. 376 * @return the compressed CRC 377 */ 378 @Deprecated 379 int getCompressedCrc() { 380 return (int) compressedCrc; 381 } 382 383 /** 384 * Sets the compressed CRC. 385 * @deprecated use setCompressedCrcValue instead. 386 * @param crc the CRC 387 */ 388 @Deprecated 389 void setCompressedCrc(final int crc) { 390 this.compressedCrc = crc; 391 } 392 393 /** 394 * Gets the compressed CRC. 395 * @since 1.7 396 * @return the CRC 397 */ 398 long getCompressedCrcValue() { 399 return compressedCrc; 400 } 401 402 /** 403 * Sets the compressed CRC. 404 * @since 1.7 405 * @param crc the CRC 406 */ 407 void setCompressedCrcValue(final long crc) { 408 this.compressedCrc = crc; 409 } 410 411 /** 412 * Get this entry's file size. 413 * 414 * @return This entry's file size. 415 */ 416 @Override 417 public long getSize() { 418 return size; 419 } 420 421 /** 422 * Set this entry's file size. 423 * 424 * @param size This entry's new file size. 425 */ 426 public void setSize(final long size) { 427 this.size = size; 428 } 429 430 /** 431 * Get this entry's compressed file size. 432 * 433 * @return This entry's compressed file size. 434 */ 435 long getCompressedSize() { 436 return compressedSize; 437 } 438 439 /** 440 * Set this entry's compressed file size. 441 * 442 * @param size This entry's new compressed file size. 443 */ 444 void setCompressedSize(final long size) { 445 this.compressedSize = size; 446 } 447 448 /** 449 * Sets the (compression) methods to use for entry's content - the 450 * default is LZMA2. 451 * 452 * <p>Currently only {@link SevenZMethod#COPY}, {@link 453 * SevenZMethod#LZMA2}, {@link SevenZMethod#BZIP2} and {@link 454 * SevenZMethod#DEFLATE} are supported when writing archives.</p> 455 * 456 * <p>The methods will be consulted in iteration order to create 457 * the final output.</p> 458 * 459 * @param methods the methods to use for the content 460 * @since 1.8 461 */ 462 public void setContentMethods(final Iterable<? extends SevenZMethodConfiguration> methods) { 463 if (methods != null) { 464 final LinkedList<SevenZMethodConfiguration> l = new LinkedList<>(); 465 methods.forEach(l::addLast); 466 contentMethods = Collections.unmodifiableList(l); 467 } else { 468 contentMethods = null; 469 } 470 } 471 472 /** 473 * Sets the (compression) methods to use for entry's content - the 474 * default is LZMA2. 475 * 476 * <p>Currently only {@link SevenZMethod#COPY}, {@link 477 * SevenZMethod#LZMA2}, {@link SevenZMethod#BZIP2} and {@link 478 * SevenZMethod#DEFLATE} are supported when writing archives.</p> 479 * 480 * <p>The methods will be consulted in iteration order to create 481 * the final output.</p> 482 * 483 * @param methods the methods to use for the content 484 * @since 1.22 485 */ 486 public void setContentMethods(SevenZMethodConfiguration... methods) { 487 setContentMethods(Arrays.asList(methods)); 488 } 489 490 /** 491 * Gets the (compression) methods to use for entry's content - the 492 * default is LZMA2. 493 * 494 * <p>Currently only {@link SevenZMethod#COPY}, {@link 495 * SevenZMethod#LZMA2}, {@link SevenZMethod#BZIP2} and {@link 496 * SevenZMethod#DEFLATE} are supported when writing archives.</p> 497 * 498 * <p>The methods will be consulted in iteration order to create 499 * the final output.</p> 500 * 501 * @since 1.8 502 * @return the methods to use for the content 503 */ 504 public Iterable<? extends SevenZMethodConfiguration> getContentMethods() { 505 return contentMethods; 506 } 507 508 @Override 509 public int hashCode() { 510 final String n = getName(); 511 return n == null ? 0 : n.hashCode(); 512 } 513 514 @Override 515 public boolean equals(final Object obj) { 516 if (this == obj) { 517 return true; 518 } 519 if (obj == null || getClass() != obj.getClass()) { 520 return false; 521 } 522 final SevenZArchiveEntry other = (SevenZArchiveEntry) obj; 523 return 524 Objects.equals(name, other.name) && 525 hasStream == other.hasStream && 526 isDirectory == other.isDirectory && 527 isAntiItem == other.isAntiItem && 528 hasCreationDate == other.hasCreationDate && 529 hasLastModifiedDate == other.hasLastModifiedDate && 530 hasAccessDate == other.hasAccessDate && 531 creationDate == other.creationDate && 532 lastModifiedDate == other.lastModifiedDate && 533 accessDate == other.accessDate && 534 hasWindowsAttributes == other.hasWindowsAttributes && 535 windowsAttributes == other.windowsAttributes && 536 hasCrc == other.hasCrc && 537 crc == other.crc && 538 compressedCrc == other.compressedCrc && 539 size == other.size && 540 compressedSize == other.compressedSize && 541 equalSevenZMethods(contentMethods, other.contentMethods); 542 } 543 544 /** 545 * Converts NTFS time (100 nanosecond units since 1 January 1601) 546 * to Java time. 547 * @param ntfsTime the NTFS time in 100 nanosecond units 548 * @return the Java time 549 */ 550 public static Date ntfsTimeToJavaTime(final long ntfsTime) { 551 final Calendar ntfsEpoch = Calendar.getInstance(); 552 ntfsEpoch.setTimeZone(TimeZone.getTimeZone("GMT+0")); 553 ntfsEpoch.set(1601, 0, 1, 0, 0, 0); 554 ntfsEpoch.set(Calendar.MILLISECOND, 0); 555 final long realTime = ntfsEpoch.getTimeInMillis() + (ntfsTime / (10*1000)); 556 return new Date(realTime); 557 } 558 559 /** 560 * Converts Java time to NTFS time. 561 * @param date the Java time 562 * @return the NTFS time 563 */ 564 public static long javaTimeToNtfsTime(final Date date) { 565 final Calendar ntfsEpoch = Calendar.getInstance(); 566 ntfsEpoch.setTimeZone(TimeZone.getTimeZone("GMT+0")); 567 ntfsEpoch.set(1601, 0, 1, 0, 0, 0); 568 ntfsEpoch.set(Calendar.MILLISECOND, 0); 569 return ((date.getTime() - ntfsEpoch.getTimeInMillis())* 1000 * 10); 570 } 571 572 private boolean equalSevenZMethods(final Iterable<? extends SevenZMethodConfiguration> c1, 573 final Iterable<? extends SevenZMethodConfiguration> c2) { 574 if (c1 == null) { 575 return c2 == null; 576 } 577 if (c2 == null) { 578 return false; 579 } 580 final Iterator<? extends SevenZMethodConfiguration> i2 = c2.iterator(); 581 for (SevenZMethodConfiguration element : c1) { 582 if (!i2.hasNext()) { 583 return false; 584 } 585 if (!element.equals(i2.next())) { 586 return false; 587 } 588 } 589 return !i2.hasNext(); 590 } 591}