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