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