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.util.Calendar;
021import java.util.Collections;
022import java.util.Date;
023import java.util.LinkedList;
024import java.util.TimeZone;
025
026import org.apache.commons.compress.archivers.ArchiveEntry;
027
028/**
029 * An entry in a 7z archive.
030 *
031 * @NotThreadSafe
032 * @since 1.6
033 */
034public class SevenZArchiveEntry implements ArchiveEntry {
035    private String name;
036    private boolean hasStream;
037    private boolean isDirectory;
038    private boolean isAntiItem;
039    private boolean hasCreationDate;
040    private boolean hasLastModifiedDate;
041    private boolean hasAccessDate;
042    private long creationDate;
043    private long lastModifiedDate;
044    private long accessDate;
045    private boolean hasWindowsAttributes;
046    private int windowsAttributes;
047    private boolean hasCrc;
048    private long crc, compressedCrc;
049    private long size, compressedSize;
050    private Iterable<? extends SevenZMethodConfiguration> contentMethods;
051
052    public SevenZArchiveEntry() {
053    }
054
055    /**
056     * Get this entry's name.
057     *
058     * <p>This method returns the raw name as it is stored inside of the archive.</p>
059     *
060     * @return This entry's name.
061     */
062    @Override
063    public String getName() {
064        return name;
065    }
066
067    /**
068     * Set this entry's name.
069     *
070     * @param name This entry's new name.
071     */
072    public void setName(final String name) {
073        this.name = name;
074    }
075
076    /**
077     * Whether there is any content associated with this entry.
078     * @return whether there is any content associated with this entry.
079     */
080    public boolean hasStream() {
081        return hasStream;
082    }
083
084    /**
085     * Sets whether there is any content associated with this entry.
086     * @param hasStream whether there is any content associated with this entry.
087     */
088    public void setHasStream(final boolean hasStream) {
089        this.hasStream = hasStream;
090    }
091
092    /**
093     * Return whether or not this entry represents a directory.
094     *
095     * @return True if this entry is a directory.
096     */
097    @Override
098    public boolean isDirectory() {
099        return isDirectory;
100    }
101
102    /**
103     * Sets whether or not this entry represents a directory.
104     *
105     * @param isDirectory True if this entry is a directory.
106     */
107    public void setDirectory(final boolean isDirectory) {
108        this.isDirectory = isDirectory;
109    }
110
111    /**
112     * Indicates whether this is an "anti-item" used in differential backups,
113     * meaning it should delete the same file from a previous backup.
114     * @return true if it is an anti-item, false otherwise
115     */
116    public boolean isAntiItem() {
117        return isAntiItem;
118    }
119
120    /**
121     * Sets whether this is an "anti-item" used in differential backups,
122     * meaning it should delete the same file from a previous backup.
123     * @param isAntiItem true if it is an anti-item, false otherwise
124     */
125    public void setAntiItem(final boolean isAntiItem) {
126        this.isAntiItem = isAntiItem;
127    }
128
129    /**
130     * Returns whether this entry has got a creation date at all.
131     * @return whether the entry has got a creation date
132     */
133    public boolean getHasCreationDate() {
134        return hasCreationDate;
135    }
136
137    /**
138     * Sets whether this entry has got a creation date at all.
139     * @param hasCreationDate whether the entry has got a creation date
140     */
141    public void setHasCreationDate(final boolean hasCreationDate) {
142        this.hasCreationDate = hasCreationDate;
143    }
144
145    /**
146     * Gets the creation date.
147     * @throws UnsupportedOperationException if the entry hasn't got a
148     * creation date.
149     * @return the creation date
150     */
151    public Date getCreationDate() {
152        if (hasCreationDate) {
153            return ntfsTimeToJavaTime(creationDate);
154        }
155        throw new UnsupportedOperationException(
156                "The entry doesn't have this timestamp");
157    }
158
159    /**
160     * Sets the creation date using NTFS time (100 nanosecond units
161     * since 1 January 1601)
162     * @param ntfsCreationDate the creation date
163     */
164    public void setCreationDate(final long ntfsCreationDate) {
165        this.creationDate = ntfsCreationDate;
166    }
167
168    /**
169     * Sets the creation date,
170     * @param creationDate the creation date
171     */
172    public void setCreationDate(final Date creationDate) {
173        hasCreationDate = creationDate != null;
174        if (hasCreationDate) {
175            this.creationDate = javaTimeToNtfsTime(creationDate);
176        }
177    }
178
179    /**
180     * Returns whether this entry has got a last modified date at all.
181     * @return whether this entry has got a last modified date at all
182     */
183    public boolean getHasLastModifiedDate() {
184        return hasLastModifiedDate;
185    }
186
187    /**
188     * Sets whether this entry has got a last modified date at all.
189     * @param hasLastModifiedDate whether this entry has got a last
190     * modified date at all
191     */
192    public void setHasLastModifiedDate(final boolean hasLastModifiedDate) {
193        this.hasLastModifiedDate = hasLastModifiedDate;
194    }
195
196    /**
197     * Gets the last modified date.
198     * @throws UnsupportedOperationException if the entry hasn't got a
199     * last modified date.
200     * @return the last modified date
201     */
202    @Override
203    public Date getLastModifiedDate() {
204        if (hasLastModifiedDate) {
205            return ntfsTimeToJavaTime(lastModifiedDate);
206        }
207        throw new UnsupportedOperationException(
208                "The entry doesn't have this timestamp");
209    }
210
211    /**
212     * Sets the last modified date using NTFS time (100 nanosecond
213     * units since 1 January 1601)
214     * @param ntfsLastModifiedDate the last modified date
215     */
216    public void setLastModifiedDate(final long ntfsLastModifiedDate) {
217        this.lastModifiedDate = ntfsLastModifiedDate;
218    }
219
220    /**
221     * Sets the last modified date,
222     * @param lastModifiedDate the last modified date
223     */
224    public void setLastModifiedDate(final Date lastModifiedDate) {
225        hasLastModifiedDate = lastModifiedDate != null;
226        if (hasLastModifiedDate) {
227            this.lastModifiedDate = javaTimeToNtfsTime(lastModifiedDate);
228        }
229    }
230
231    /**
232     * Returns whether this entry has got an access date at all.
233     * @return whether this entry has got an access date at all.
234     */
235    public boolean getHasAccessDate() {
236        return hasAccessDate;
237    }
238
239    /**
240     * Sets whether this entry has got an access date at all.
241     * @param hasAcessDate whether this entry has got an access date at all.
242     */
243    public void setHasAccessDate(final boolean hasAcessDate) {
244        this.hasAccessDate = hasAcessDate;
245    }
246
247    /**
248     * Gets the access date.
249     * @throws UnsupportedOperationException if the entry hasn't got a
250     * access date.
251     * @return the access date
252     */
253    public Date getAccessDate() {
254        if (hasAccessDate) {
255            return ntfsTimeToJavaTime(accessDate);
256        }
257        throw new UnsupportedOperationException(
258                "The entry doesn't have this timestamp");
259    }
260
261    /**
262     * Sets the access date using NTFS time (100 nanosecond units
263     * since 1 January 1601)
264     * @param ntfsAccessDate the access date
265     */
266    public void setAccessDate(final long ntfsAccessDate) {
267        this.accessDate = ntfsAccessDate;
268    }
269
270    /**
271     * Sets the access date,
272     * @param accessDate the access date
273     */
274    public void setAccessDate(final Date accessDate) {
275        hasAccessDate = accessDate != null;
276        if (hasAccessDate) {
277            this.accessDate = javaTimeToNtfsTime(accessDate);
278        }
279    }
280
281    /**
282     * Returns whether this entry has windows attributes.
283     * @return whether this entry has windows attributes.
284     */
285    public boolean getHasWindowsAttributes() {
286        return hasWindowsAttributes;
287    }
288
289    /**
290     * Sets whether this entry has windows attributes.
291     * @param hasWindowsAttributes whether this entry has windows attributes.
292     */
293    public void setHasWindowsAttributes(final boolean hasWindowsAttributes) {
294        this.hasWindowsAttributes = hasWindowsAttributes;
295    }
296
297    /**
298     * Gets the windows attributes.
299     * @return the windows attributes
300     */
301    public int getWindowsAttributes() {
302        return windowsAttributes;
303    }
304
305    /**
306     * Sets the windows attributes.
307     * @param windowsAttributes the windows attributes
308     */
309    public void setWindowsAttributes(final int windowsAttributes) {
310        this.windowsAttributes = windowsAttributes;
311    }
312
313    /**
314     * Returns whether this entry has got a crc.
315     *
316     * <p>In general entries without streams don't have a CRC either.</p>
317     * @return whether this entry has got a crc.
318     */
319    public boolean getHasCrc() {
320        return hasCrc;
321    }
322
323    /**
324     * Sets whether this entry has got a crc.
325     * @param hasCrc whether this entry has got a crc.
326     */
327    public void setHasCrc(final boolean hasCrc) {
328        this.hasCrc = hasCrc;
329    }
330
331    /**
332     * Gets the CRC.
333     * @deprecated use getCrcValue instead.
334     * @return the CRC
335     */
336    @Deprecated
337    public int getCrc() {
338        return (int) crc;
339    }
340
341    /**
342     * Sets the CRC.
343     * @deprecated use setCrcValue instead.
344     * @param crc the CRC
345     */
346    @Deprecated
347    public void setCrc(final int crc) {
348        this.crc = crc;
349    }
350
351    /**
352     * Gets the CRC.
353     * @since Compress 1.7
354     * @return the CRC
355     */
356    public long getCrcValue() {
357        return crc;
358    }
359
360    /**
361     * Sets the CRC.
362     * @since Compress 1.7
363     * @param crc the CRC
364     */
365    public void setCrcValue(final long crc) {
366        this.crc = crc;
367    }
368
369    /**
370     * Gets the compressed CRC.
371     * @deprecated use getCompressedCrcValue instead.
372     * @return the compressed CRC
373     */
374    @Deprecated
375    int getCompressedCrc() {
376        return (int) compressedCrc;
377    }
378
379    /**
380     * Sets the compressed CRC.
381     * @deprecated use setCompressedCrcValue instead.
382     * @param crc the CRC
383     */
384    @Deprecated
385    void setCompressedCrc(final int crc) {
386        this.compressedCrc = crc;
387    }
388
389    /**
390     * Gets the compressed CRC.
391     * @since Compress 1.7
392     * @return the CRC
393     */
394    long getCompressedCrcValue() {
395        return compressedCrc;
396    }
397
398    /**
399     * Sets the compressed CRC.
400     * @since Compress 1.7
401     * @param crc the CRC
402     */
403    void setCompressedCrcValue(final long crc) {
404        this.compressedCrc = crc;
405    }
406
407    /**
408     * Get this entry's file size.
409     *
410     * @return This entry's file size.
411     */
412    @Override
413    public long getSize() {
414        return size;
415    }
416
417    /**
418     * Set this entry's file size.
419     *
420     * @param size This entry's new file size.
421     */
422    public void setSize(final long size) {
423        this.size = size;
424    }
425
426    /**
427     * Get this entry's compressed file size.
428     *
429     * @return This entry's compressed file size.
430     */
431    long getCompressedSize() {
432        return compressedSize;
433    }
434
435    /**
436     * Set this entry's compressed file size.
437     *
438     * @param size This entry's new compressed file size.
439     */
440    void setCompressedSize(final long size) {
441        this.compressedSize = size;
442    }
443
444    /**
445     * Sets the (compression) methods to use for entry's content - the
446     * default is LZMA2.
447     *
448     * <p>Currently only {@link SevenZMethod#COPY}, {@link
449     * SevenZMethod#LZMA2}, {@link SevenZMethod#BZIP2} and {@link
450     * SevenZMethod#DEFLATE} are supported when writing archives.</p>
451     *
452     * <p>The methods will be consulted in iteration order to create
453     * the final output.</p>
454     *
455     * @param methods the methods to use for the content
456     * @since 1.8
457     */
458    public void setContentMethods(final Iterable<? extends SevenZMethodConfiguration> methods) {
459        if (methods != null) {
460            final LinkedList<SevenZMethodConfiguration> l = new LinkedList<>();
461            for (final SevenZMethodConfiguration m : methods) {
462                l.addLast(m);
463            }
464            contentMethods = Collections.unmodifiableList(l);
465        } else {
466            contentMethods = null;
467        }
468    }
469
470    /**
471     * Gets the (compression) methods to use for entry's content - the
472     * default is LZMA2.
473     *
474     * <p>Currently only {@link SevenZMethod#COPY}, {@link
475     * SevenZMethod#LZMA2}, {@link SevenZMethod#BZIP2} and {@link
476     * SevenZMethod#DEFLATE} are supported when writing archives.</p>
477     *
478     * <p>The methods will be consulted in iteration order to create
479     * the final output.</p>
480     *
481     * @since 1.8
482     * @return the methods to use for the content
483     */
484    public Iterable<? extends SevenZMethodConfiguration> getContentMethods() {
485        return contentMethods;
486    }
487
488    /**
489     * Converts NTFS time (100 nanosecond units since 1 January 1601)
490     * to Java time.
491     * @param ntfsTime the NTFS time in 100 nanosecond units
492     * @return the Java time
493     */
494    public static Date ntfsTimeToJavaTime(final long ntfsTime) {
495        final Calendar ntfsEpoch = Calendar.getInstance();
496        ntfsEpoch.setTimeZone(TimeZone.getTimeZone("GMT+0"));
497        ntfsEpoch.set(1601, 0, 1, 0, 0, 0);
498        ntfsEpoch.set(Calendar.MILLISECOND, 0);
499        final long realTime = ntfsEpoch.getTimeInMillis() + (ntfsTime / (10*1000));
500        return new Date(realTime);
501    }
502
503    /**
504     * Converts Java time to NTFS time.
505     * @param date the Java time
506     * @return the NTFS time
507     */
508    public static long javaTimeToNtfsTime(final Date date) {
509        final Calendar ntfsEpoch = Calendar.getInstance();
510        ntfsEpoch.setTimeZone(TimeZone.getTimeZone("GMT+0"));
511        ntfsEpoch.set(1601, 0, 1, 0, 0, 0);
512        ntfsEpoch.set(Calendar.MILLISECOND, 0);
513        return ((date.getTime() - ntfsEpoch.getTimeInMillis())* 1000 * 10);
514    }
515}