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}