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