Repository: commons-compress
Updated Branches:
  refs/heads/compress-2.0 7c8c870c0 -> 1a377507a


experimenting with Java8 - embrace BasicFileAttributes and Optional


Project: http://git-wip-us.apache.org/repos/asf/commons-compress/repo
Commit: http://git-wip-us.apache.org/repos/asf/commons-compress/commit/c6eed140
Tree: http://git-wip-us.apache.org/repos/asf/commons-compress/tree/c6eed140
Diff: http://git-wip-us.apache.org/repos/asf/commons-compress/diff/c6eed140

Branch: refs/heads/compress-2.0
Commit: c6eed140b0b4579fd11ade0b66ada704bb116eca
Parents: 7c8c870
Author: Stefan Bodewig <bode...@apache.org>
Authored: Sun Mar 27 15:48:30 2016 +0200
Committer: Stefan Bodewig <bode...@apache.org>
Committed: Sun Mar 27 15:48:30 2016 +0200

----------------------------------------------------------------------
 .../compress2/archivers/ArchiveEntry.java       |  71 +++--
 .../archivers/ArchiveEntryParameters.java       | 318 ++++++++++++++++---
 .../archivers/spi/SimpleArchiveEntry.java       |  82 ++++-
 .../compress2/formats/ar/ArArchiveEntry.java    |  28 +-
 .../compress2/formats/ar/ArArchiveInput.java    |  10 +-
 .../compress2/formats/ar/ArArchiveOutput.java   |  22 +-
 .../archivers/ArchiveEntryParametersTest.java   | 129 ++++++--
 .../compress2/formats/ar/RoundTripTest.java     |   5 +-
 8 files changed, 510 insertions(+), 155 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/commons-compress/blob/c6eed140/src/main/java/org/apache/commons/compress2/archivers/ArchiveEntry.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/commons/compress2/archivers/ArchiveEntry.java 
b/src/main/java/org/apache/commons/compress2/archivers/ArchiveEntry.java
index bd4b713..e2573ce 100644
--- a/src/main/java/org/apache/commons/compress2/archivers/ArchiveEntry.java
+++ b/src/main/java/org/apache/commons/compress2/archivers/ArchiveEntry.java
@@ -18,15 +18,36 @@
  */
 package org.apache.commons.compress2.archivers;
 
-import java.time.Instant;
-import java.util.Map;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.nio.file.attribute.PosixFilePermission;
+import java.util.Optional;
 import java.util.Set;
 
 /**
  * Represents an entry of an archive.
+ *
+ * <p>The scope of {@link BasicFileAttributes} attributes really
+ * supported by an {@link ArchiveEntry} depends on the format. All
+ * formats support the mandatory attributes of {@link
+ * java.nio.file.attribute.BasicFileAttributes}.</p>
  * @Immutable
  */
-public interface ArchiveEntry {
+public interface ArchiveEntry extends BasicFileAttributes {
+
+    /**
+     * The supported types of files, maps to the {@code isFoo} methods
+     * in {@link BasicFileAttributes}.
+     */
+    public static enum FileType {
+        /** A regular file. */
+        REGULAR_FILE,
+        /** A directory. */
+        DIR,
+        /** A symbolic link. */
+        SYMLINK,
+        /** Something that is neither a regular file nor a directory nor a 
symbolic link. */
+        OTHER
+    };
 
     /** Special value indicating that the size is unknown */
     static final long SIZE_UNKNOWN = -1;
@@ -42,30 +63,42 @@ public interface ArchiveEntry {
     String getName();
 
     /**
-     * Gets the uncompressed size of this entry. May be -1 ({@link 
#SIZE_UNKNOWN}) if the size is unknown.
-     * 
-     * @return the uncompressed size of this entry.
+     * Provides information about the owner.
+     *
+     * @return information about the entry's owner or {@link Optional#empty} 
if the format doesn't support owner information
      */
-    long getSize();
+    Optional<OwnerInformation> getOwnerInformation();
 
     /**
-     * Returns true if this entry refers to a directory.
-     * 
-     * @return true if this entry refers to a directory.
+     * The "mode" associated with the entry.
+     *
+     * <p>Many formats support a mode attribute that is inspired by
+     * the Unix stat(2) system call. Some even extend it beyond {@code
+     * st_mode} which is only 16 bits, therefore a long is used to
+     * accomodate these formats.</p>
+     *
+     * @return the format-specific mode if the format supports modes.
      */
-    boolean isDirectory();
+    Optional<Long> getMode();
 
     /**
-     * Gets the last modified date/time of this entry.
-     * 
-     * @return the last modified date/time of this entry.
+     * Permissions associated with the the entry.
+     * @return the set of recognized permissions or {@link Optional#empty} if 
the format doesn't support permissions.
      */
-    Instant getLastModified();
+    Optional<Set<PosixFilePermission>> getPermissions();
 
     /**
-     * Provides information about the owner.
-     *
-     * @return information about the entry's owner or null if the format 
doesn't support owner information
+     * The type of entry.
+     * @return the type of the entry.
      */
-    OwnerInformation getOwnerInformation();
+    default FileType getType() {
+        if (isRegularFile()) {
+            return FileType.REGULAR_FILE;
+        } else if (isDirectory()) {
+            return FileType.DIR;
+        } else if (isSymbolicLink()) {
+            return FileType.SYMLINK;
+        }
+        return FileType.OTHER;
+    }
 }

http://git-wip-us.apache.org/repos/asf/commons-compress/blob/c6eed140/src/main/java/org/apache/commons/compress2/archivers/ArchiveEntryParameters.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/commons/compress2/archivers/ArchiveEntryParameters.java
 
b/src/main/java/org/apache/commons/compress2/archivers/ArchiveEntryParameters.java
index 64a0d88..3147242 100644
--- 
a/src/main/java/org/apache/commons/compress2/archivers/ArchiveEntryParameters.java
+++ 
b/src/main/java/org/apache/commons/compress2/archivers/ArchiveEntryParameters.java
@@ -19,21 +19,33 @@
 package org.apache.commons.compress2.archivers;
 
 import java.io.File;
-import java.time.Instant;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.nio.file.attribute.FileTime;
+import java.nio.file.attribute.PosixFileAttributes;
+import java.nio.file.attribute.PosixFilePermission;
+import java.util.EnumSet;
+import java.util.Optional;
+import java.util.Set;
 
 /**
  * A parameter object useful for creating new ArchiveEntries.
  * @NotThreadSafe
  */
-public class ArchiveEntryParameters {
+public class ArchiveEntryParameters implements ArchiveEntry {
 
     private static final char SLASH = '/';
 
     private String name;
     private long size = ArchiveEntry.SIZE_UNKNOWN;
-    private boolean dirFlag = false;
-    private Instant lastModified;
-    private OwnerInformation owner;
+    private Object fileKey;
+    private FileType type = FileType.REGULAR_FILE;
+    private FileTime lastModified, created, lastAccess;
+    private Optional<OwnerInformation> owner = Optional.empty();
+    private Optional<Long> mode = Optional.empty();
+    private Optional<Set<PosixFilePermission>> permissions = Optional.empty();
 
     /**
      * Creates parameters as a copy of an existing entry.
@@ -43,10 +55,11 @@ public class ArchiveEntryParameters {
     public static ArchiveEntryParameters copyOf(ArchiveEntry otherEntry) {
         return new ArchiveEntryParameters()
             .withName(otherEntry.getName())
-            .asDirectory(otherEntry.isDirectory())
-            .withSize(otherEntry.getSize())
-            .withLastModified(otherEntry.getLastModified())
-            .withOwnerInformation(otherEntry.getOwnerInformation());
+            .withAttributes(otherEntry)
+            .withType(otherEntry.getType())
+            .withOwnerInformation(otherEntry.getOwnerInformation())
+            .withMode(otherEntry.getMode())
+            .withPermissions(otherEntry.getPermissions());
     }
 
     /**
@@ -54,12 +67,21 @@ public class ArchiveEntryParameters {
      * @param file the File to read information from
      * @return parameters populated from the file instance
      */
-    public static ArchiveEntryParameters fromFile(File file) {
-        return new ArchiveEntryParameters()
-            .withName(file.getName())
-            .asDirectory(file.isDirectory())
-            .withSize(file.exists() ? file.length() : 
ArchiveEntry.SIZE_UNKNOWN)
-            .withLastModified(Instant.ofEpochMilli(file.lastModified()));
+    public static ArchiveEntryParameters fromFile(File file) throws 
IOException {
+        Path path = file.toPath();
+        ArchiveEntryParameters params = new ArchiveEntryParameters()
+            .withName(file.getName());
+        if (file.exists()) {
+            params = params
+                .withAttributes(Files.readAttributes(path, 
BasicFileAttributes.class));
+            try {
+                params = params.withPermissions(Files.readAttributes(path, 
PosixFileAttributes.class)
+                                                .permissions());
+            } catch (UnsupportedOperationException ex) {
+                // file system without support for POSIX attributes
+            }
+        }
+        return params;
     }
 
     /**
@@ -77,8 +99,48 @@ public class ArchiveEntryParameters {
     }
 
     /**
+     * Sets the creation time of the entry.
+     * @param creationTime the creation time for the entry to build
+     * @return the parameters object
+     */
+    public ArchiveEntryParameters withCreationTime(FileTime creationTime) {
+        this.created = creationTime;
+        return this;
+    }
+
+    /**
+     * Sets the last access time of the entry.
+     * @param lastAccessTime the last access time for the entry to build
+     * @return the parameters object
+     */
+    public ArchiveEntryParameters withLastAccessTime(FileTime lastAccessTime) {
+        this.lastAccess = lastAccessTime;
+        return this;
+    }
+
+    /**
+     * Sets the last modified time of the entry.
+     * @param lastModifiedTime the last modified time for the entry to build
+     * @return the parameters object
+     */
+    public ArchiveEntryParameters withLastModifiedTime(FileTime 
lastModifiedTime) {
+        this.lastModified = lastModifiedTime;
+        return this;
+    }
+
+    /**
+     * Sets the file key of the entry.
+     * @param key the file key for the entry to build
+     * @return the parameters object
+     */
+    public ArchiveEntryParameters withFileKey(Object key) {
+        this.fileKey = key;
+        return this;
+    }
+
+    /**
      * Sets the size of the entry.
-     * @param size the size of the entry to build
+     * @param size the size for the entry to build
      * @return the parameters object
      */
     public ArchiveEntryParameters withSize(long size) {
@@ -87,22 +149,22 @@ public class ArchiveEntryParameters {
     }
 
     /**
-     * Marks the entry to build as a directory.
-     * @param b whether the entry is supposed to represent a directory
+     * Sets the type of the entry.
+     * @param type the type for the entry to build
      * @return the parameters object
      */
-    public ArchiveEntryParameters asDirectory(boolean b) {
-        this.dirFlag = b;
+    public ArchiveEntryParameters withType(FileType type) {
+        this.type = type;
         return this;
     }
 
     /**
-     * Sets the last modified date/time of the entry.
-     * @param lastModified the last modified date/time of the entry to build
+     * Sets the owner information of the entry.
+     * @param owner the owner information for the entry to build
      * @return the parameters object
      */
-    public ArchiveEntryParameters withLastModified(Instant lastModified) {
-        this.lastModified = lastModified;
+    public ArchiveEntryParameters 
withOwnerInformation(Optional<OwnerInformation> owner) {
+        this.owner = owner;
         return this;
     }
 
@@ -112,56 +174,182 @@ public class ArchiveEntryParameters {
      * @return the parameters object
      */
     public ArchiveEntryParameters withOwnerInformation(OwnerInformation owner) 
{
-        this.owner = owner;
+        return withOwnerInformation(Optional.ofNullable(owner));
+    }
+
+    /**
+     * Sets the "mode" of the entry.
+     * @param mode the mode for the entry to build
+     * @return the parameters object
+     */
+    public ArchiveEntryParameters withMode(Optional<Long> mode) {
+        this.mode = mode;
         return this;
     }
 
     /**
-     * Gets the configured name.
-     *
-     * <p>The name will use '/' as directory separator and end with a '/' if 
and only if the entry represents a
-     * directory.</p>
-     *
-     * @return the normalized name
+     * Sets the "mode" of the entry.
+     * @param mode the mode for the entry to build
+     * @return the parameters object
      */
-    public String getName() {
-        return normalize(name, dirFlag);
+    public ArchiveEntryParameters withMode(long mode) {
+        return withMode(Optional.of(mode));
     }
 
     /**
-     * Gets the configured size or {@link #SIZE_UNKNOWN}) if the size is not 
configured.
-     * 
-     * @return the configured size
+     * Sets the permissions of the entry.
+     * @param permissions the permissions for the entry to build
+     * @return the parameters object
      */
-    public long getSize() {
-        return dirFlag ? 0 : size;
+    public ArchiveEntryParameters 
withPermissions(Optional<Set<PosixFilePermission>> permissions) {
+        this.permissions = permissions;
+        return this;
     }
 
     /**
-     * Returns true if parameters are configured to represent a directory.
-     * 
-     * @return true if this parameters refer to a directory.
+     * Sets the permissions of the entry.
+     * @param permissions the permissions for the entry to build
+     * @return the parameters object
      */
-    public boolean isDirectory() {
-        return dirFlag;
+    public ArchiveEntryParameters withPermissions(Set<PosixFilePermission> 
permissions) {
+        return withPermissions(Optional.ofNullable(permissions));
     }
 
     /**
-     * Gets the configured last modified date/time.
-     * 
-     * @return the configured last modified date/time or null if no date was 
configured.
+     * Sets the basic attributes.
+     * @param attributes the attributes of the entry to build
+     * @return the parameters object
      */
-    public Instant getLastModified() {
+    public ArchiveEntryParameters withAttributes(BasicFileAttributes 
attributes) {
+        if (attributes.isRegularFile()) {
+            type = FileType.REGULAR_FILE;
+        } else if (attributes.isDirectory()) {
+            type = FileType.DIR;
+        } else if (attributes.isSymbolicLink()) {
+            type = FileType.SYMLINK;
+        } else {
+            type = FileType.OTHER;
+        }
+        return withCreationTime(attributes.creationTime())
+            .withFileKey(attributes.fileKey())
+            .withLastAccessTime(attributes.lastAccessTime())
+            .withLastModifiedTime(attributes.lastModifiedTime())
+            .withSize(attributes.size());
+    }
+
+    @Override
+    public String getName() {
+        return normalize(name, isDirectory());
+    }
+
+    @Override
+    public FileTime creationTime() {
+        return created;
+    }
+
+    @Override
+    public Object fileKey() {
+        return fileKey;
+    }
+
+    @Override
+    public boolean isDirectory() {
+        return type == FileType.DIR;
+    }
+
+    @Override
+    public boolean isOther() {
+        return type == FileType.OTHER;
+    }
+
+    @Override
+    public boolean isRegularFile() {
+        return type == FileType.REGULAR_FILE;
+    }
+
+    @Override
+    public boolean isSymbolicLink() {
+        return type == FileType.SYMLINK;
+    }
+
+    @Override
+    public FileTime lastAccessTime() {
+        return lastAccess;
+    }
+
+    @Override
+    public FileTime lastModifiedTime() {
         return lastModified;
     }
 
+    @Override
+    public long size() {
+        return type == FileType.DIR ? 0 : size;
+    }
+
+    @Override
+    public Optional<OwnerInformation> getOwnerInformation() {
+        return owner;
+    }
+
+    @Override
+    public Optional<Long> getMode() {
+        return mode.isPresent() ? mode : permissions.map(p -> 
modeFromPermissions(p, type));
+    }
+
+    @Override
+    public Optional<Set<PosixFilePermission>> getPermissions() {
+        return permissions.isPresent() ? permissions
+            : mode.map(ArchiveEntryParameters::permissionsFromMode);
+    }
+
+    @Override
+    public FileType getType() {
+        return type;
+    }
+
     /**
-     * Gets the configured information about the owner.
-     *
-     * @return information about the entry's owner or null if no information 
was configured
+     * Translates a set of permissons into a Unix stat(2) {@code st_mode} 
result
+     * @param permissions the permissions
+     * @param type the file type
+     * @return the "mode"
      */
-    public OwnerInformation getOwnerInformation() {
-        return owner;
+    public static long modeFromPermissions(Set<PosixFilePermission> 
permissions, FileType type) {
+        long mode;
+        switch (type) {
+        case SYMLINK:
+            mode = 012;
+            break;
+        case REGULAR_FILE:
+            mode = 010;
+            break;
+        case DIR:
+            mode = 004;
+            break;
+        default:
+            // OTHER could be a character or block device, a socket or a FIFO 
- so don't set anything
+            mode = 0;
+            break;
+        }
+        mode <<= 3;
+        mode <<= 3; // we don't support sticky, setuid, setgid
+        mode |= modeFromPermissions(permissions, "OWNER");
+        mode <<= 3;
+        mode |= modeFromPermissions(permissions, "GROUP");
+        mode <<= 3;
+        mode |= modeFromPermissions(permissions, "OTHERS");
+        return mode;
+    }
+
+    /**
+     * Translates a Unix stat(2) {@code st_mode} compatible value into a set 
of permissions.
+     */
+    public static Set<PosixFilePermission> permissionsFromMode(long mode) {
+        Set<PosixFilePermission> permissions = 
EnumSet.noneOf(PosixFilePermission.class);
+        addPermissions(permissions, "OTHERS", mode);
+        addPermissions(permissions, "GROUP", mode >> 3);
+        addPermissions(permissions, "OWNER", mode >> 6);
+        return permissions;
     }
 
     private static String normalize(String name, boolean dirFlag) {
@@ -179,4 +367,30 @@ public class ArchiveEntryParameters {
         }
         return name;
     }
+
+    private static long modeFromPermissions(Set<PosixFilePermission> 
permissions, String prefix) {
+        long mode = 0;
+        if (permissions.contains(PosixFilePermission.valueOf(prefix + 
"_READ"))) {
+            mode |= 4;
+        }
+        if (permissions.contains(PosixFilePermission.valueOf(prefix + 
"_WRITE"))) {
+            mode |= 2;
+        }
+        if (permissions.contains(PosixFilePermission.valueOf(prefix + 
"_EXECUTE"))) {
+            mode |= 1;
+        }
+        return mode;
+    }
+
+    private static void addPermissions(Set<PosixFilePermission> permissions, 
String prefix, long mode) {
+        if ((mode & 1) == 1) {
+            permissions.add(PosixFilePermission.valueOf(prefix + "_EXECUTE"));
+        }
+        if ((mode & 2) == 2) {
+            permissions.add(PosixFilePermission.valueOf(prefix + "_WRITE"));
+        }
+        if ((mode & 4) == 4) {
+            permissions.add(PosixFilePermission.valueOf(prefix + "_READ"));
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/commons-compress/blob/c6eed140/src/main/java/org/apache/commons/compress2/archivers/spi/SimpleArchiveEntry.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/commons/compress2/archivers/spi/SimpleArchiveEntry.java
 
b/src/main/java/org/apache/commons/compress2/archivers/spi/SimpleArchiveEntry.java
index dbe04da..ac1294d 100644
--- 
a/src/main/java/org/apache/commons/compress2/archivers/spi/SimpleArchiveEntry.java
+++ 
b/src/main/java/org/apache/commons/compress2/archivers/spi/SimpleArchiveEntry.java
@@ -18,9 +18,12 @@
  */
 package org.apache.commons.compress2.archivers.spi;
 
-import java.time.Instant;
+import java.nio.file.attribute.FileTime;
+import java.nio.file.attribute.PosixFilePermission;
+import java.util.Collections;
 import java.util.Objects;
-
+import java.util.Optional;
+import java.util.Set;
 import org.apache.commons.compress2.archivers.ArchiveEntry;
 import org.apache.commons.compress2.archivers.ArchiveEntryParameters;
 import org.apache.commons.compress2.archivers.OwnerInformation;
@@ -32,9 +35,12 @@ import 
org.apache.commons.compress2.archivers.OwnerInformation;
 public class SimpleArchiveEntry implements ArchiveEntry {
     private final String name;
     private final long size;
-    private final boolean dirFlag;
-    private final Instant lastModified;
-    private final OwnerInformation owner;
+    private final ArchiveEntry.FileType type;
+    private final Object fileKey;
+    private final FileTime lastModified, lastAccess, created;
+    private final Optional<OwnerInformation> owner;
+    private final Optional<Set<PosixFilePermission>> permissions;
+    private final Optional<Long> mode;
 
     /**
      * Creates a SimpleArchiveEntry from a parameter object.
@@ -42,10 +48,15 @@ public class SimpleArchiveEntry implements ArchiveEntry {
      */
     public SimpleArchiveEntry(ArchiveEntryParameters params) {
         this.name = params.getName();
-        this.size = params.getSize();
-        this.dirFlag = params.isDirectory();
-        this.lastModified = params.getLastModified();
+        this.size = params.size();
+        this.type = params.getType();
+        this.fileKey = params.fileKey();
+        this.lastModified = params.lastModifiedTime();
+        this.lastAccess = params.lastAccessTime();
+        this.created = params.creationTime();
         this.owner = params.getOwnerInformation();
+        this.permissions = 
params.getPermissions().map(Collections::unmodifiableSet);
+        this.mode = params.getMode();
     }
 
     @Override
@@ -54,26 +65,66 @@ public class SimpleArchiveEntry implements ArchiveEntry {
     }
 
     @Override
-    public long getSize() {
+    public long size() {
         return size;
     }
 
     @Override
     public boolean isDirectory() {
-        return dirFlag;
+        return type == FileType.DIR;
+    }
+
+    @Override
+    public boolean isRegularFile() {
+        return type == FileType.REGULAR_FILE;
+    }
+
+    @Override
+    public boolean isSymbolicLink() {
+        return type == FileType.SYMLINK;
     }
 
     @Override
-    public Instant getLastModified() {
+    public boolean isOther() {
+        return type == FileType.OTHER;
+    }
+
+    @Override
+    public FileTime lastModifiedTime() {
         return lastModified;
     }
 
     @Override
-    public OwnerInformation getOwnerInformation() {
+    public FileTime lastAccessTime() {
+        return lastAccess;
+    }
+
+    @Override
+    public FileTime creationTime() {
+        return created;
+    }
+
+    @Override
+    public Object fileKey() {
+        return fileKey;
+    }
+
+    @Override
+    public Optional<OwnerInformation> getOwnerInformation() {
         return owner;
     }
 
     @Override
+    public Optional<Long> getMode() {
+        return mode;
+    }
+
+    @Override
+    public Optional<Set<PosixFilePermission>> getPermissions() {
+        return permissions;
+    }
+
+    @Override
     public int hashCode() {
         final int prime = 31;
         int result = 1;
@@ -92,8 +143,13 @@ public class SimpleArchiveEntry implements ArchiveEntry {
         SimpleArchiveEntry other = (SimpleArchiveEntry) obj;
         return Objects.equals(name, other.name)
             && size == other.size
-            && dirFlag == other.dirFlag
+            && type == other.type
+            && Objects.equals(fileKey, other.fileKey)
             && Objects.equals(lastModified, other.lastModified)
+            && Objects.equals(lastAccess, other.lastAccess)
+            && Objects.equals(created, other.created)
+            && Objects.equals(mode, other.mode)
+            && Objects.equals(permissions, other.permissions)
             && Objects.equals(owner, other.owner);
     }
 }

http://git-wip-us.apache.org/repos/asf/commons-compress/blob/c6eed140/src/main/java/org/apache/commons/compress2/formats/ar/ArArchiveEntry.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/commons/compress2/formats/ar/ArArchiveEntry.java 
b/src/main/java/org/apache/commons/compress2/formats/ar/ArArchiveEntry.java
index 9233bc4..584d9fe 100644
--- a/src/main/java/org/apache/commons/compress2/formats/ar/ArArchiveEntry.java
+++ b/src/main/java/org/apache/commons/compress2/formats/ar/ArArchiveEntry.java
@@ -57,37 +57,17 @@ public class ArArchiveEntry extends SimpleArchiveEntry {
     /** The trailer for each entry */
     public static final String TRAILER = "`\012";
 
-    // TODO revisit once the permissions stuff is sorted out
-    private final int mode;
-    private static final int DEFAULT_MODE = 33188; // = (octal) 0100644 
-
     /**
-     * Creates an ArArchiveEntry from a parameter object.
-     * @param params the parameters describing the archive entry.
+     * The default mode (a world readable, owner writable regular file).
      */
-    public ArArchiveEntry(ArchiveEntryParameters params) {
-        this(params, DEFAULT_MODE);
-    }
+    public static final long DEFAULT_MODE = 0100644l;
 
     /**
-     * Creates an ArArchiveEntry from a parameter object and an octal mode.
+     * Creates an ArArchiveEntry from a parameter object.
      * @param params the parameters describing the archive entry.
-     * @param mode the file/dir mode of the entry
      */
-    public ArArchiveEntry(ArchiveEntryParameters params, int mode) {
+    public ArArchiveEntry(ArchiveEntryParameters params) {
         super(params);
-        this.mode = mode;
-    }
-
-    // TODO revisit once the permissions stuff is sorted out
-    public int getMode() {
-        return mode;
-    }
-
-    // TODO revisit once the permissions stuff is sorted out
-    @Override
-    public boolean equals(Object obj) {
-        return super.equals(obj) && mode == ((ArArchiveEntry) obj).mode;
     }
 
 }

http://git-wip-us.apache.org/repos/asf/commons-compress/blob/c6eed140/src/main/java/org/apache/commons/compress2/formats/ar/ArArchiveInput.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/commons/compress2/formats/ar/ArArchiveInput.java 
b/src/main/java/org/apache/commons/compress2/formats/ar/ArArchiveInput.java
index 8e90f97..8a62f8a 100644
--- a/src/main/java/org/apache/commons/compress2/formats/ar/ArArchiveInput.java
+++ b/src/main/java/org/apache/commons/compress2/formats/ar/ArArchiveInput.java
@@ -26,7 +26,7 @@ import java.nio.ByteBuffer;
 import java.nio.channels.Channels;
 import java.nio.channels.ReadableByteChannel;
 import java.nio.charset.StandardCharsets;
-import java.time.Instant;
+import java.nio.file.attribute.FileTime;
 
 import org.apache.commons.compress2.archivers.ArchiveEntryParameters;
 import org.apache.commons.compress2.archivers.OwnerInformation;
@@ -87,7 +87,7 @@ public class ArArchiveInput extends 
AbstractArchiveInput<ArArchiveEntry> {
     @Override
     public ArArchiveEntry next() throws IOException {
         if (currentEntry != null) {
-            final long entryEnd = entryOffset + currentEntry.getSize();
+            final long entryEnd = entryOffset + currentEntry.size();
             IOUtils.skip(wrappedStream, entryEnd - offset);
             currentEntry = null;
         }
@@ -166,8 +166,8 @@ public class ArArchiveInput extends 
AbstractArchiveInput<ArArchiveEntry> {
 
         currentEntry = new ArArchiveEntry(new 
ArchiveEntryParameters().withName(temp).withSize(len)
                                           .withOwnerInformation(new 
OwnerInformation(userId, asInt(ID_BUF, true)))
-                                          
.withLastModified(Instant.ofEpochSecond(asLong(LAST_MODIFIED_BUF))),
-                                          asInt(FILE_MODE_BUF, 8));
+                                          
.withLastModifiedTime(FileTime.fromMillis(asLong(LAST_MODIFIED_BUF) * 1000))
+                                          .withMode(asInt(FILE_MODE_BUF, 8)));
         return currentEntry;
     }
 
@@ -248,7 +248,7 @@ public class ArArchiveInput extends 
AbstractArchiveInput<ArArchiveEntry> {
     public int read(byte[] b, final int off, final int len) throws IOException 
{
         int toRead = len;
         if (currentEntry != null) {
-            final long entryEnd = entryOffset + currentEntry.getSize();
+            final long entryEnd = entryOffset + currentEntry.size();
             if (len > 0 && entryEnd > offset) {
                 toRead = (int) Math.min(len, entryEnd - offset);
             } else {

http://git-wip-us.apache.org/repos/asf/commons-compress/blob/c6eed140/src/main/java/org/apache/commons/compress2/formats/ar/ArArchiveOutput.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/commons/compress2/formats/ar/ArArchiveOutput.java 
b/src/main/java/org/apache/commons/compress2/formats/ar/ArArchiveOutput.java
index 7cd7639..92eb9aa 100644
--- a/src/main/java/org/apache/commons/compress2/formats/ar/ArArchiveOutput.java
+++ b/src/main/java/org/apache/commons/compress2/formats/ar/ArArchiveOutput.java
@@ -22,6 +22,7 @@ import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.nio.channels.WritableByteChannel;
 import java.nio.charset.StandardCharsets;
+import java.util.Optional;
 
 import org.apache.commons.compress2.archivers.ArchiveEntryParameters;
 import org.apache.commons.compress2.archivers.OwnerInformation;
@@ -95,8 +96,8 @@ public class ArArchiveOutput extends 
AbstractArchiveOutput<ArArchiveEntry> {
         if (prevEntry == null) {
             writeArchiveHeader();
         } else {
-            if (prevEntry.getSize() != entryOffset) {
-                throw new IOException("length does not match entry (" + 
prevEntry.getSize() + " != " + entryOffset);
+            if (prevEntry.size() != entryOffset) {
+                throw new IOException("length does not match entry (" + 
prevEntry.size() + " != " + entryOffset);
             }
 
             if (haveUnclosedEntry) {
@@ -154,7 +155,7 @@ public class ArArchiveOutput extends 
AbstractArchiveOutput<ArArchiveEntry> {
         }
 
         offset = fill(offset, 16, (byte) ' ');
-        final String m = "" + (pEntry.getLastModified().getEpochSecond());
+        final String m = "" + (pEntry.lastModifiedTime().toMillis() / 1000);
         if (m.length() > 12) {
             throw new IOException("modified too long");
         }
@@ -175,7 +176,7 @@ public class ArArchiveOutput extends 
AbstractArchiveOutput<ArArchiveEntry> {
         offset += write(g);
 
         offset = fill(offset, 40, (byte) ' ');
-        final String fm = "" + Integer.toString(pEntry.getMode(), 8);
+        final String fm = "" + 
Integer.toString(pEntry.getMode().orElse(0l).intValue(), 8);
         if (fm.length() > 8) {
             throw new IOException("filemode too long");
         }
@@ -183,7 +184,7 @@ public class ArArchiveOutput extends 
AbstractArchiveOutput<ArArchiveEntry> {
 
         offset = fill(offset, 48, (byte) ' ');
         final String s =
-            String.valueOf(pEntry.getSize()
+            String.valueOf(pEntry.size()
                            + (mustAppendName ? n.length() : 0));
         if (s.length() > 10) {
             throw new IOException("size too long");
@@ -215,6 +216,9 @@ public class ArArchiveOutput extends 
AbstractArchiveOutput<ArArchiveEntry> {
 
     @Override
     public ArArchiveEntry createEntry(ArchiveEntryParameters params) {
+        if (!params.getMode().isPresent()) {
+            params = params.withMode(ArArchiveEntry.DEFAULT_MODE);
+        }
         return new ArArchiveEntry(params);
     }
 
@@ -228,12 +232,12 @@ public class ArArchiveOutput extends 
AbstractArchiveOutput<ArArchiveEntry> {
         finished = true;
     }
 
-    private int getUserId(OwnerInformation info) {
-        return info == null ? 0 : info.getUserId();
+    private int getUserId(Optional<OwnerInformation> info) {
+        return info.map(OwnerInformation::getUserId).orElse(0);
     }
 
-    private int getGroupId(OwnerInformation info) {
-        return info == null ? 0 : info.getGroupId();
+    private int getGroupId(Optional<OwnerInformation> info) {
+        return info.map(OwnerInformation::getGroupId).orElse(0);
     }
 
     private class CurrentChannel implements WritableByteChannel {

http://git-wip-us.apache.org/repos/asf/commons-compress/blob/c6eed140/src/test/java/org/apache/commons/compress2/archivers/ArchiveEntryParametersTest.java
----------------------------------------------------------------------
diff --git 
a/src/test/java/org/apache/commons/compress2/archivers/ArchiveEntryParametersTest.java
 
b/src/test/java/org/apache/commons/compress2/archivers/ArchiveEntryParametersTest.java
index 38305de..c6f81b3 100644
--- 
a/src/test/java/org/apache/commons/compress2/archivers/ArchiveEntryParametersTest.java
+++ 
b/src/test/java/org/apache/commons/compress2/archivers/ArchiveEntryParametersTest.java
@@ -19,11 +19,20 @@
 package org.apache.commons.compress2.archivers;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 
 import java.io.File;
 import java.io.IOException;
+import java.nio.file.attribute.FileTime;
+import java.nio.file.attribute.PosixFilePermission;
 import java.time.Duration;
 import java.time.Instant;
+import java.util.EnumSet;
+import java.util.Optional;
+import java.util.Set;
+
 import org.junit.Test;
 
 public class ArchiveEntryParametersTest {
@@ -31,17 +40,25 @@ public class ArchiveEntryParametersTest {
     @Test
     public void defaultValues() {
         ArchiveEntryParameters p = new ArchiveEntryParameters();
-        assertEquals(null, p.getName());
-        assertEquals(-1, p.getSize());
-        assertEquals(false, p.isDirectory());
-        assertEquals(null, p.getLastModified());
-        assertEquals(null, p.getOwnerInformation());
+        assertNull(p.getName());
+        assertEquals(-1, p.size());
+        assertFalse(p.isDirectory());
+        assertFalse(p.isSymbolicLink());
+        assertFalse(p.isOther());
+        assertTrue(p.isRegularFile());
+        assertNull(p.lastModifiedTime());
+        assertNull(p.lastAccessTime());
+        assertNull(p.creationTime());
+        assertNull(p.fileKey());
+        assertFalse(p.getOwnerInformation().isPresent());
+        assertFalse(p.getPermissions().isPresent());
+        assertEquals(ArchiveEntry.FileType.REGULAR_FILE, p.getType());
     }
 
     @Test
     public void shouldAddTrailingSlashForDirectories() {
         ArchiveEntryParameters p = new ArchiveEntryParameters()
-            .withName("foo").asDirectory(true);
+            .withName("foo").withType(ArchiveEntry.FileType.DIR);
         assertEquals("foo/", p.getName());
         p.withName("foo/");
         assertEquals("foo/", p.getName());
@@ -52,7 +69,7 @@ public class ArchiveEntryParametersTest {
     @Test
     public void shouldStripTrailingSlashForNonDirectories() {
         ArchiveEntryParameters p = new ArchiveEntryParameters()
-            .withName("foo").asDirectory(false);
+            .withName("foo").withType(ArchiveEntry.FileType.REGULAR_FILE);
         assertEquals("foo", p.getName());
         p.withName("foo/");
         assertEquals("foo", p.getName());
@@ -63,28 +80,63 @@ public class ArchiveEntryParametersTest {
     @Test
     public void sizeShouldBe0ForDirectories() {
         ArchiveEntryParameters p = new ArchiveEntryParameters()
-            .asDirectory(true);
-        assertEquals(0, p.getSize());
+            .withType(ArchiveEntry.FileType.DIR);
+        assertEquals(0, p.size());
         p.withSize(42);
-        assertEquals(0, p.getSize());
+        assertEquals(0, p.size());
     }
 
     @Test
     public void copyActuallyCopies() {
-        final Instant d = Instant.now();
+        final FileTime d1 = FileTime.from(Instant.now());
+        final FileTime d2 = FileTime.from(Instant.now());
         final OwnerInformation o = new OwnerInformation(17, 4);
         ArchiveEntryParameters p = ArchiveEntryParameters.copyOf(new 
ArchiveEntry() {
+                @Override
                 public String getName() {return "baz";}
-                public long getSize() {return 42;}
+                @Override
+                public long size() {return 42;}
+                @Override
                 public boolean isDirectory() {return false;}
-                public Instant getLastModified() {return d;}
-                public OwnerInformation getOwnerInformation() {return o;}
+                @Override
+                public boolean isSymbolicLink() {return false;}
+                @Override
+                public boolean isOther() {return false;}
+                @Override
+                public boolean isRegularFile() {return true;}
+                @Override
+                public FileTime lastModifiedTime() {return d1;}
+                @Override
+                public FileTime lastAccessTime() {return null;}
+                @Override
+                public FileTime creationTime() {return d2;}
+                @Override
+                public Optional<OwnerInformation> getOwnerInformation() 
{return Optional.of(o);}
+                @Override
+                public Optional<Long> getMode() { return Optional.of(4711l); }
+                @Override
+                public Optional<Set<PosixFilePermission>> getPermissions() {
+                    return 
Optional.of(EnumSet.of(PosixFilePermission.OWNER_READ));
+                }
+                @Override
+                public Object fileKey() {
+                    return "foo";
+                }
             });
         assertEquals("baz", p.getName());
-        assertEquals(42, p.getSize());
-        assertEquals(false, p.isDirectory());
-        assertEquals(d, p.getLastModified());
-        assertEquals(o, p.getOwnerInformation());
+        assertEquals(42, p.size());
+        assertFalse(p.isDirectory());
+        assertFalse(p.isSymbolicLink());
+        assertFalse(p.isOther());
+        assertTrue(p.isRegularFile());
+        assertEquals(d1, p.lastModifiedTime());
+        assertEquals(d2, p.creationTime());
+        assertNull(p.lastAccessTime());
+        assertEquals("foo", p.fileKey());
+        assertEquals(ArchiveEntry.FileType.REGULAR_FILE, p.getType());
+        assertEquals(o, p.getOwnerInformation().get());
+        assertEquals(4711l, p.getMode().get().longValue());
+        
assertTrue(p.getPermissions().get().contains(PosixFilePermission.OWNER_READ));
     }
 
     @Test
@@ -96,10 +148,10 @@ public class ArchiveEntryParametersTest {
         ArchiveEntryParameters p = ArchiveEntryParameters.fromFile(f);
         assert p.getName().endsWith("suf");
         assert p.getName().startsWith("pre");
-        assertEquals(0, p.getSize());
-        assertEquals(false, p.isDirectory());
-        assertWithinTwoSecondsOf(d, p.getLastModified());
-        assertEquals(null, p.getOwnerInformation());
+        assertEquals(0, p.size());
+        assertEquals(ArchiveEntry.FileType.REGULAR_FILE, p.getType());
+        assertWithinTwoSecondsOf(d, p.lastModifiedTime());
+        assertFalse(p.getOwnerInformation().isPresent());
     }
 
     @Test
@@ -113,10 +165,10 @@ public class ArchiveEntryParametersTest {
         ArchiveEntryParameters p = ArchiveEntryParameters.fromFile(f);
         assert p.getName().endsWith("suf/");
         assert p.getName().startsWith("pre");
-        assertEquals(0, p.getSize());
-        assertEquals(true, p.isDirectory());
-        assertWithinTwoSecondsOf(d, p.getLastModified());
-        assertEquals(null, p.getOwnerInformation());
+        assertEquals(0, p.size());
+        assertEquals(ArchiveEntry.FileType.DIR, p.getType());
+        assertWithinTwoSecondsOf(d, p.lastModifiedTime());
+        assertFalse(p.getOwnerInformation().isPresent());
     }
 
     @Test
@@ -124,10 +176,29 @@ public class ArchiveEntryParametersTest {
         File f = File.createTempFile("pre", "suf");
         assert f.delete();
         ArchiveEntryParameters p = ArchiveEntryParameters.fromFile(f);
-        assertEquals(-1, p.getSize());
+        assertEquals(-1, p.size());
+    }
+
+    @Test
+    public void getModeConstructsModeFromPermissions() {
+        ArchiveEntryParameters p = new ArchiveEntryParameters()
+            .withPermissions(EnumSet.of(PosixFilePermission.OWNER_READ, 
PosixFilePermission.OWNER_WRITE, PosixFilePermission.OWNER_EXECUTE));
+        assertEquals("100700", Long.toString(p.getMode().get(), 8));
+    }
+
+    @Test
+    public void getPermissionsConstructsPermissionsFromMode() {
+        ArchiveEntryParameters p = new ArchiveEntryParameters()
+            .withMode(0100753l);
+        Set<PosixFilePermission> s = p.getPermissions().get();
+        assertEquals(EnumSet.of(PosixFilePermission.OWNER_READ, 
PosixFilePermission.OWNER_WRITE,
+                                PosixFilePermission.OWNER_EXECUTE, 
PosixFilePermission.GROUP_READ,
+                                PosixFilePermission.GROUP_EXECUTE, 
PosixFilePermission.OTHERS_WRITE,
+                                PosixFilePermission.OTHERS_EXECUTE),
+                     s);
     }
 
-    private static void assertWithinTwoSecondsOf(Instant expected, Instant 
actual) {
-        assert Math.abs(Duration.between(expected, actual).getSeconds()) < 2;
+    private static void assertWithinTwoSecondsOf(Instant expected, FileTime 
actual) {
+        assert Math.abs(Duration.between(expected, 
actual.toInstant()).getSeconds()) < 2;
     }
 }

http://git-wip-us.apache.org/repos/asf/commons-compress/blob/c6eed140/src/test/java/org/apache/commons/compress2/formats/ar/RoundTripTest.java
----------------------------------------------------------------------
diff --git 
a/src/test/java/org/apache/commons/compress2/formats/ar/RoundTripTest.java 
b/src/test/java/org/apache/commons/compress2/formats/ar/RoundTripTest.java
index 250c505..491c964 100644
--- a/src/test/java/org/apache/commons/compress2/formats/ar/RoundTripTest.java
+++ b/src/test/java/org/apache/commons/compress2/formats/ar/RoundTripTest.java
@@ -186,10 +186,7 @@ public class RoundTripTest {
         try {
             uri = url.toURI();
         } catch (java.net.URISyntaxException ex) {
-//          throw new IOException(ex); // JDK 1.6+
-            IOException ioe = new IOException();
-            ioe.initCause(ex);
-            throw ioe;
+            throw new IOException(ex);
         }
         return new File(uri);
     }

Reply via email to