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