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}