com.edduarte.protbox.core.registry.PbxFile.java Source code

Java tutorial

Introduction

Here is the source code for com.edduarte.protbox.core.registry.PbxFile.java

Source

/*
 * Copyright 2014 University of Aveiro
 *
 * 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.
 */

package com.edduarte.protbox.core.registry;

import com.edduarte.protbox.core.Constants;
import com.edduarte.protbox.core.FolderOption;
import com.edduarte.protbox.exception.ProtboxException;
import com.edduarte.protbox.utils.Utils;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.swing.*;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * PbxFile is a entry that structures a file in the Registry. Other than having the same
 * variables as {@link PbxEntry}, a PbxFile is also represented by a set of snapshots, each one
 * representing a past state of the file's data, size and last modified date.
 *
 * @author Ed Duarte (<a href="mailto:ed@edduarte.com">ed@edduarte.com</a>)
 * @version 2.0
 */
public final class PbxFile extends PbxEntry implements Serializable {

    private static final Logger logger = LoggerFactory.getLogger(PbxFile.class);

    private static final long serialVersionUID = 1L;

    private SnapshotStack snapshotStack;

    private BackupPolicy backupPolicy;

    PbxFile(final PReg parentRegistry, final PbxFolder parentFolder, final String encodedName,
            final String realName) {

        super(parentRegistry, parentFolder, encodedName, realName);
        this.snapshotStack = new SnapshotStack();
        this.backupPolicy = BackupPolicy.Regular10;
    }

    /**
     * Returns the most recent data for the file represented by this file.
     *
     * @return the most recent data for the file represented by this file, or
     * a empty byte array if this file has no backup data stored
     */
    public Snapshot getLatestSnapshot() {
        Snapshot mostRecentSnapshot = snapshotStack.peek();
        return mostRecentSnapshot != null ? mostRecentSnapshot : Snapshot.empty();
    }

    /**
     * Returns a listing of all snapshots in string representation,
     *
     * @return the most recent data for the file represented by this file, or
     * a empty byte array if this file has no backup data stored
     */
    public List<String> snapshotsToString() {
        return snapshotStack.stream().map(Snapshot::toString).collect(Collectors.toList());
    }

    /**
     * Returns the number of currently stored snapshot data for the file
     * represented by this entry.
     *
     * @return the number of stored snapshots
     */
    int getSnapshotCount() {
        return snapshotStack.size();
    }

    /**
     * Sets the data of the file to the specified data.
     */
    public void createSnapshotFromFile(File snapshotFile, FolderOption fromFolder) throws ProtboxException {
        logger.info("-------------- " + snapshotFile.getName() + " "
                + new Date(snapshotFile.lastModified()).toString());
        try {
            byte[] fileData;

            if (fromFolder.equals(FolderOption.SHARED)) {
                try {
                    fileData = parentRegistry.decrypt(FileUtils.readFileToByteArray(snapshotFile), true);
                } catch (ProtboxException ex) {
                    logger.info(
                            "There was a problem while decrypting. Ignoring file " + snapshotFile.getName() + ".",
                            ex);
                    return;
                }

            } else {
                fileData = FileUtils.readFileToByteArray(snapshotFile);
            }

            long fileSize = snapshotFile.length();
            Date lastModifiedDate = new Date(snapshotFile.lastModified());
            snapshotStack.push(new Snapshot(fileData, fileSize, lastModifiedDate));

        } catch (IOException ex) {
            throw new ProtboxException(ex);
        }
    }

    /**
     * Sets the data of the file to the specified data.
     */
    public void writeSnapshotToFile(int index, File snapshotFile, FolderOption toFolder) throws ProtboxException {
        Snapshot snapshot;
        try {
            snapshot = snapshotStack.remove(index);
            snapshotStack.forcePush(snapshot);

        } catch (IndexOutOfBoundsException ex) {
            return;
        }
        try {
            if (toFolder.equals(FolderOption.SHARED)) {
                FileUtils.writeByteArrayToFile(snapshotFile,
                        parentRegistry.encrypt(snapshot.getSnapshotData(), true));

            } else {
                FileUtils.writeByteArrayToFile(snapshotFile, snapshot.getSnapshotData());
            }

            snapshotFile.setLastModified(snapshot.getLastModifiedDate().getTime());

        } catch (IOException ex) {
            throw new ProtboxException(ex);
        }
    }

    public BackupPolicy getBackupPolicy() {
        return backupPolicy;
    }

    public void setBackupPolicy(BackupPolicy backupPolicy) {
        if (!backupPolicy.equals(BackupPolicy.Ask)) {
            boolean changeBackupPolicy = true;
            if (snapshotStack.size() > backupPolicy.maxBackupSize) {
                changeBackupPolicy = JOptionPane.showConfirmDialog(null,
                        "The number of stored backup copies have been reduced to " + backupPolicy.maxBackupSize
                                + ", and prior backups above that number will be deleted.\n"
                                + "Are you sure you want to change the backup policy?",
                        "Confirm backup policy change", JOptionPane.YES_NO_OPTION,
                        JOptionPane.WARNING_MESSAGE) == JOptionPane.YES_NO_OPTION;
            }

            if (changeBackupPolicy) {
                this.backupPolicy = backupPolicy;
                while (snapshotStack.size() > backupPolicy.maxBackupSize) {
                    snapshotStack.removeLast();
                }
            }
        } else {
            this.backupPolicy = backupPolicy;
        }
    }

    /**
     * Suggests Java Garbage Collector to stop storing this file's data.
     */
    void clearSnapshots() {
        snapshotStack.clear();
        snapshotStack = null;
        System.gc();
    }

    @Override
    public boolean equals(Object obj) {
        return super.equals(obj);
    }

    public enum BackupPolicy {

        None(1), // don't backup this file
        Regular10(10), // backup 10 copies
        Regular50(50), // backup 50 copies
        Regular100(100), // backup 100 copies
        Ask(0); // ask every time

        private int maxBackupSize;

        private BackupPolicy(int maxBackupSize) {
            this.maxBackupSize = maxBackupSize;
        }
    }

    public static final class Snapshot implements Serializable {
        private static final long serialVersionUID = 1L;

        private final byte[] data;

        private final long dataSize;

        private final Date lastModified;

        /**
         * Constructor to instantiate a snapshot, which will be used for backup
         * and restoring purposes.
         */
        private Snapshot(byte[] data, long dataSize, Date lastModified) {
            this.data = data;
            this.dataSize = dataSize;
            this.lastModified = lastModified;
        }

        /**
         * Default constructor to instantiate a empty snapshot, by storing an empty array,
         * a data size of 0 and a last modified date set to the current time.
         */
        private static Snapshot empty() {
            return new Snapshot(new byte[0], 0, Constants.getToday());
        }

        public byte[] getSnapshotData() {
            return data;
        }

        public long getSnapshotSize() {
            return dataSize;
        }

        public Date getLastModifiedDate() {
            return lastModified;
        }

        @Override
        public String toString() {
            return Utils.readableFileSize(getSnapshotSize()) + ", at "
                    + Constants.formatDate(getLastModifiedDate());
        }

        @Override
        public boolean equals(Object o) {
            if (this == o)
                return true;
            if (o == null || getClass() != o.getClass())
                return false;

            Snapshot snapshot = (Snapshot) o;
            return dataSize == snapshot.dataSize && Arrays.equals(data, snapshot.data)
                    && lastModified.equals(snapshot.lastModified);
        }

        @Override
        public int hashCode() {
            int result = Arrays.hashCode(data);
            result = 31 * result + (int) (dataSize ^ (dataSize >>> 32));
            result = 31 * result + lastModified.hashCode();
            return result;
        }
    }

    private final class SnapshotStack extends LinkedList<Snapshot> {

        private SnapshotStack() {
            super();
        }

        @Override
        public void push(Snapshot newSnapshot) {
            if (backupPolicy.equals(BackupPolicy.Ask)) {
                super.push(newSnapshot);
                if (JOptionPane.showConfirmDialog(null,
                        "A new version of '" + realName() + "' has been detected.\n"
                                + "Would you like to backup the previous version?",
                        "Confirm Backup", JOptionPane.YES_NO_OPTION,
                        JOptionPane.WARNING_MESSAGE) == JOptionPane.NO_OPTION) {
                    removeLast();
                }

            } else {
                while (size() > backupPolicy.maxBackupSize) {
                    removeLast();
                }
                forcePush(newSnapshot);
            }
        }

        public void forcePush(Snapshot newSnapshot) {
            super.push(newSnapshot);
        }
    }
}