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