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