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