org.dataconservancy.packaging.tool.model.ipm.FileInfo.java Source code

Java tutorial

Introduction

Here is the source code for org.dataconservancy.packaging.tool.model.ipm.FileInfo.java

Source

package org.dataconservancy.packaging.tool.model.ipm;

/*
 * Copyright 2015 Johns Hopkins University
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileTime;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.codec.binary.Hex;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.output.NullOutputStream;
import org.dataconservancy.dcs.model.DetectedFormat;
import org.dataconservancy.dcs.util.ContentDetectionService;

/**
 * Information about a file or directory.
 */
public class FileInfo {

    public enum Algorithm {
        SHA1, MD5
    }

    private URI location;
    private String name;
    private List<String> formats;
    private Map<Algorithm, String> checksums;
    private FileInfoAttributes fileAttributes;

    /**
     * Default constructor that should be used in most cases. Will read the file at the path location and load the necessary file attributes.
     * @param path The path to the file.
     */
    public FileInfo(Path path) {
        location = path.toUri();
        name = path.getFileName().toString();

        try {
            fileAttributes = new FileInfoAttributes(Files.readAttributes(path, BasicFileAttributes.class));
            if (fileAttributes.isRegularFile()) {
                checksums = new HashMap<>();
                formats = new ArrayList<>();

                MessageDigest md5 = null;
                MessageDigest sha1 = null;

                try {
                    md5 = MessageDigest.getInstance("MD5");
                    sha1 = MessageDigest.getInstance("SHA-1");
                } catch (NoSuchAlgorithmException e) {
                    throw new RuntimeException(e.getMessage(), e);
                }

                try (ChecksumCalculatingInputStream in = new ChecksumCalculatingInputStream(
                        Files.newInputStream(path), Arrays.asList(md5, sha1))) {

                    IOUtils.copy(in, new NullOutputStream());

                    in.digests().forEach((md, digest) -> {
                        String encoded = Hex.encodeHexString(digest);
                        if (md.getAlgorithm().equals("MD5")) {
                            checksums.put(Algorithm.MD5, encoded);
                        }

                        if (md.getAlgorithm().equals("SHA-1")) {
                            checksums.put(Algorithm.SHA1, encoded);
                        }
                    });
                }

                List<DetectedFormat> fileFormats = ContentDetectionService.getInstance()
                        .detectFormats(path.toFile());
                for (DetectedFormat format : fileFormats) {
                    if (format.getId() != null && !format.getId().isEmpty()) {
                        formats.add(createFormatURIString(format));
                    }

                    if (format.getMimeType() != null && !format.getMimeType().isEmpty()) {
                        formats.add(format.getMimeType());
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * Constructor to use when loading existing file information.
     * @param path The path of the File info
     * @param fileAttributes The basic file attibutes of the file.
     * @param formats The list of formats
     * @param checksums The checksum map
     */
    public FileInfo(Path path, BasicFileAttributes fileAttributes, List<String> formats,
            Map<Algorithm, String> checksums) {
        location = path.toUri();
        name = path.getFileName().toString();

        this.fileAttributes = new FileInfoAttributes(fileAttributes);
        this.formats = formats;
        this.checksums = checksums;
    }

    /**
     * Constructor that creates an essentially empty FileInfo object. Only location and name will be set.
     * Note: FileAttributes will not be created with this constructor and must be set manually.
     * @param location The location of the File system entity referenced by this FileInfo object.
     * @param name The name of the File System Entity referenced by this FileInfo object.
     */
    public FileInfo(URI location, String name) {
        this.location = location;
        this.name = name;
    }

    public FileInfo(URI location, String name, FileTime creationTime, FileTime modifiedTime, boolean isFile,
            boolean isDirectory, long size, List<String> formats, Map<Algorithm, String> checksums) {
        this.location = location;
        this.name = name;

        this.formats = formats;
        this.checksums = checksums;

        fileAttributes = new FileInfoAttributes();
        fileAttributes.setCreationTime(creationTime);
        fileAttributes.setLastModifiedTime(modifiedTime);
        fileAttributes.setIsRegularFile(isFile);
        fileAttributes.setIsDirectory(isDirectory);
        fileAttributes.setSize(size);
    }

    /**
     * @return Location of file or directory.
     */
    public URI getLocation() {
        return location;
    }

    /**
     * @param location the location of a file or directory.
     */
    public void setLocation(URI location) {
        this.location = location;
    }

    /**
     * @param algorithm The algorithm of the checksum either MD5 or SHA1
     * @return Checksum in known format of the file or null if directory.
     */
    public String getChecksum(Algorithm algorithm) {
        String value = null;
        if (checksums != null) {
            value = checksums.get(algorithm);
        }
        return value;
    }

    /**
     * Add a checksum for this FileInfo object.
     * @param algorithm The algorithm of the FileInfo object.
     * @param value The value of checksum.
     */
    public void addChecksum(Algorithm algorithm, String value) {
        if (checksums == null) {
            checksums = new HashMap<>();
        }

        checksums.put(algorithm, value);
    }

    /**
     * Sets the map of checksums
     * @param checksumMap The map of checksums for the FileInfo object.
     */
    public void setChecksums(Map<Algorithm, String> checksumMap) {
        this.checksums = checksumMap;
    }

    /**
     * @return Name of the file.
     */
    public String getName() {
        return name;
    }

    /**
     * @return List of formats for the file.
     */
    public List<String> getFormats() {
        return formats;
    }

    /**
     * Add a new format for this FileInfo object.
     * @param format The format to add.
     */
    public void addFormat(String format) {
        if (formats == null) {
            formats = new ArrayList<>();
        }

        formats.add(format);
    }

    /**
     * Sets the list of formats for the File backing this FileInfo object.
     * @param formats The list of formats to add.
     */
    public void setFormats(List<String> formats) {
        this.formats = formats;
    }

    /**
     * @return Size of the file or -1 if directory.
     */
    public long getSize() {
        long size = -1;
        if (fileAttributes != null) {
            size = fileAttributes.size();
        }

        return size;
    }

    /**
     * Sets the size of the File backing this FileInfo object.
     * @param size The size in bytes of the file backing this FileInfo object.
     */
    public void setSize(long size) {
        if (fileAttributes == null) {
            fileAttributes = new FileInfoAttributes();
        }

        fileAttributes.setSize(size);
    }

    /**
     * @return Whether or not a file is being described.
     */
    public boolean isFile() {
        boolean isFile = false;
        if (fileAttributes != null) {
            isFile = fileAttributes.isRegularFile();
        }

        return isFile;
    }

    /**
     * Sets whether or not the FileInfo describes a file on the file system.
     * @param isFile True if the FileInfo object describes a file, false otherwise.
     */
    public void setIsFile(boolean isFile) {
        if (fileAttributes == null) {
            fileAttributes = new FileInfoAttributes();
        }

        fileAttributes.setIsRegularFile(isFile);
    }

    /**
     * @return Whether or not a directory is being described.
     */
    public boolean isDirectory() {
        boolean isDirectory = false;
        if (fileAttributes != null) {
            isDirectory = fileAttributes.isDirectory();
        }

        return isDirectory;
    }

    /**
     * Sets whether or not the FileInfo describes a directory on the file system.
     * @param isDirectory True if the FileInfo object describes a directory, false otherwise.
     */
    public void setIsDirectory(boolean isDirectory) {
        if (fileAttributes == null) {
            fileAttributes = new FileInfoAttributes();
        }

        fileAttributes.setIsDirectory(isDirectory);
    }

    /**
     * @return Creation time of file.
     */
    public FileTime getCreationTime() {
        FileTime creationTime = null;
        if (fileAttributes != null) {
            creationTime = fileAttributes.creationTime();
        }

        return creationTime;
    }

    /**
     * Sets the time this file was created.
     * @param creationTime The FileTime representing when this file was created.
     */
    public void setCreationTime(FileTime creationTime) {
        if (fileAttributes == null) {
            fileAttributes = new FileInfoAttributes();
        }

        fileAttributes.setCreationTime(creationTime);
    }

    /**
     * @return Last modification time of file.
     */
    public FileTime getLastModifiedTime() {
        FileTime modifiedTime = null;
        if (fileAttributes != null) {
            modifiedTime = fileAttributes.lastModifiedTime();
        }

        return modifiedTime;
    }

    /**
     * Sets the time this file was last modified.
     * @param modifiedTime The FileTime representing when this file was last modified.
     */
    public void setLastModifiedTime(FileTime modifiedTime) {
        if (fileAttributes == null) {
            fileAttributes = new FileInfoAttributes();
        }

        fileAttributes.setLastModifiedTime(modifiedTime);
    }

    /**
     * Converts format id from the DcsFormat objects into formatURI string with qualifying namespace. Only applicable
     * to pronom format identifier at this point.
     * @param format the DetectedFormat object
     * @return a formatURI string with qualifying namespace
     */
    private String createFormatURIString(DetectedFormat format) {
        String formatString = "";
        if (format.getId() != null && !format.getId().isEmpty()) {
            formatString = "info:pronom/" + format.getId();
        }

        return formatString;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (!(o instanceof FileInfo)) {
            return false;
        }

        FileInfo fileInfo = (FileInfo) o;

        if (location != null ? !location.equals(fileInfo.location) : fileInfo.location != null) {
            return false;
        }
        if (name != null ? !name.equals(fileInfo.name) : fileInfo.name != null) {
            return false;
        }
        if (formats != null ? !formats.equals(fileInfo.formats) : fileInfo.formats != null) {
            return false;
        }
        if (checksums != null ? !checksums.equals(fileInfo.checksums) : fileInfo.checksums != null) {
            return false;
        }

        return true;

    }

    @Override
    public int hashCode() {
        int result = location != null ? location.hashCode() : 0;
        result = 31 * result + (name != null ? name.hashCode() : 0);
        result = 31 * result + (formats != null ? formats.hashCode() : 0);
        result = 31 * result + (checksums != null ? checksums.hashCode() : 0);
        return result;
    }

    @Override
    public String toString() {
        return "FileInfo [location=" + location + ", name=" + name + ", formats=" + formats + ", checksums="
                + checksums + ", fileAttributes=" + fileAttributes + "]";
    }

    private class FileInfoAttributes implements BasicFileAttributes {
        private FileTime lastModifiedTime;
        private FileTime creationTime;
        private boolean isRegularFile;
        private boolean isDirectory;
        private boolean isSymbolicLink;
        private long size;

        public FileInfoAttributes() {

        }

        public FileInfoAttributes(BasicFileAttributes superAttributes) {
            lastModifiedTime = superAttributes.lastModifiedTime();
            creationTime = superAttributes.creationTime();
            isRegularFile = superAttributes.isRegularFile();
            isDirectory = superAttributes.isDirectory();
            isSymbolicLink = superAttributes.isSymbolicLink();
            size = superAttributes.size();
        }

        @Override
        public FileTime lastModifiedTime() {
            return lastModifiedTime;
        }

        public void setLastModifiedTime(FileTime lastModifiedTime) {
            this.lastModifiedTime = lastModifiedTime;
        }

        @Override
        public FileTime lastAccessTime() {
            return null;
        }

        @Override
        public FileTime creationTime() {
            return creationTime;
        }

        public void setCreationTime(FileTime creationTime) {
            this.creationTime = creationTime;
        }

        @Override
        public boolean isRegularFile() {
            return isRegularFile;
        }

        public void setIsRegularFile(boolean regularFile) {
            this.isRegularFile = regularFile;
        }

        @Override
        public boolean isDirectory() {
            return isDirectory;
        }

        public void setIsDirectory(boolean isDirectory) {
            this.isDirectory = isDirectory;
        }

        @Override
        public boolean isSymbolicLink() {
            return isSymbolicLink;
        }

        public void setIsSymbolicLink(boolean symbolicLink) {
            this.isSymbolicLink = symbolicLink;
        }

        @Override
        public boolean isOther() {
            return false;
        }

        @Override
        public long size() {
            return size;
        }

        public void setSize(long size) {
            this.size = size;
        }

        @Override
        public Object fileKey() {
            return null;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }
            if (!(o instanceof FileInfoAttributes)) {
                return false;
            }

            FileInfoAttributes that = (FileInfoAttributes) o;

            if (isRegularFile != that.isRegularFile) {
                return false;
            }
            if (isDirectory != that.isDirectory) {
                return false;
            }
            if (isSymbolicLink != that.isSymbolicLink) {
                return false;
            }
            if (size != that.size) {
                return false;
            }
            if (lastModifiedTime != null ? !lastModifiedTime.equals(that.lastModifiedTime)
                    : that.lastModifiedTime != null) {
                return false;
            }
            return !(creationTime != null ? !creationTime.equals(that.creationTime) : that.creationTime != null);
        }

        @Override
        public int hashCode() {
            int result = lastModifiedTime != null ? lastModifiedTime.hashCode() : 0;
            result = 31 * result + (creationTime != null ? creationTime.hashCode() : 0);
            result = 31 * result + (isRegularFile ? 1 : 0);
            result = 31 * result + (isDirectory ? 1 : 0);
            result = 31 * result + (isSymbolicLink ? 1 : 0);
            result = 31 * result + (int) (size ^ (size >>> 32));
            return result;
        }

        @Override
        public String toString() {
            return "FileInfoAttributes [lastModifiedTime=" + lastModifiedTime + ", creationTime=" + creationTime
                    + ", isRegularFile=" + isRegularFile + ", isDirectory=" + isDirectory + ", isSymbolicLink="
                    + isSymbolicLink + ", size=" + size + "]";
        }
    }

    static class ChecksumCalculatingInputStream extends FilterInputStream {

        private Collection<MessageDigest> digests;

        protected ChecksumCalculatingInputStream(InputStream in, Collection<MessageDigest> digests) {
            super(in);
            this.digests = digests;
        }

        @Override
        public int read() throws IOException {
            int b = super.read();

            if (b > -1) {
                digests.forEach(md -> md.update((byte) b));
            }

            return b;
        }

        @Override
        public int read(byte[] b) throws IOException {
            return this.read(b, 0, b.length);
        }

        @Override
        public int read(byte[] b, int off, int len) throws IOException {
            int count = super.read(b, off, len);

            if (count > -1) {
                digests.forEach(md -> md.update(b, off, count));
            }

            return count;
        }

        Map<MessageDigest, byte[]> digests() {
            return digests.stream().collect(HashMap::new, (map, md) -> map.put(md, md.digest()), HashMap::putAll);
        }
    }
}