ca.phon.app.workspace.ExtractProjectArchiveTask.java Source code

Java tutorial

Introduction

Here is the source code for ca.phon.app.workspace.ExtractProjectArchiveTask.java

Source

/*
 * Phon - An open source tool for research in phonology.
 * Copyright (C) 2005 - 2015, Gregory Hedlund <ghedlund@mun.ca> and Yvan Rose <yrose@mun.ca>
 * Dept of Linguistics, Memorial University <https://phon.ca>
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package ca.phon.app.workspace;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Enumeration;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import org.apache.commons.io.FileUtils;

import ca.phon.ui.CommonModuleFrame;
import ca.phon.ui.nativedialogs.NativeDialogEvent;
import ca.phon.ui.nativedialogs.NativeDialogListener;
import ca.phon.ui.nativedialogs.NativeDialogs;
import ca.phon.util.Tuple;
import ca.phon.worker.PhonTask;
import ca.phon.workspace.Workspace;

/**
 * Task to extract a .phon or .zip project archive
 * into the current workspace.
 * 
 * The property <code>PhonTask.PROGRESS_PROP</code>
 * can be watched extraction progress.
 */
public class ExtractProjectArchiveTask extends PhonTask {

    // check if we should upgrade the project after extraction
    private boolean checkForUpgrade = true;

    private File archiveFile;

    private final static Logger LOGGER = Logger.getLogger(ExtractProjectArchiveTask.class.getName());

    private enum ZippedProjectType {
        NOT_A_PROJECT, // used when not project.xml is found
        PROJECT_BASE_INCLUDED, PROJECT_BASE_NOT_INCLUDED
    };

    private File destDir;

    // cache variable for zip detection
    private Tuple<ZippedProjectType, String> zipDetect = null;

    public ExtractProjectArchiveTask(File archive) {
        this.archiveFile = archive;
    }

    private Tuple<ZippedProjectType, String> getZipDetect() {
        Tuple<ZippedProjectType, String> retVal = zipDetect;

        if (retVal == null) {
            zipDetect = detectZippedProjectType(archiveFile);
            retVal = zipDetect;
        }

        return retVal;
    }

    public File getDestDir() {
        if (destDir == null) {
            File destBaseDir = Workspace.userWorkspaceFolder();

            /*
             *  we will support two types of project structure:
             *  
             *  1) Project base included
             *     e.g., 
             *     English-Smith/
             *        project.xml
             *        Corpus1/
             *        Corpus2/
             *        etc.
             *  
             *  2) Project base not included
             *     e.g.,
             *     project.xml
             *     Corpus1/
             *     Corpus2/
             *     etc.
             *  
             *  In the later case, the name of the archive file
             *  (without extension) is used as the new project 
             *  folder name.
             */
            Tuple<ZippedProjectType, String> zipDetect = getZipDetect();
            ZippedProjectType zippedProjectType = zipDetect.getObj1();

            //         if(zippedProjectType == ZippedProjectType.NOT_A_PROJECT) {
            //            IllegalArgumentException ex
            //                = new IllegalArgumentException("'" + archiveFile.getAbsolutePath() + "' does not contain a Phon project.");
            //            super.err = ex;
            //            super.setStatus(TaskStatus.ERROR);
            //            return;
            if (zippedProjectType != ZippedProjectType.NOT_A_PROJECT) {
                destDir = destBaseDir;
                if (zippedProjectType == ZippedProjectType.PROJECT_BASE_NOT_INCLUDED) {
                    String archiveName = archiveFile.getName();
                    int lastDotIdx = archiveName.lastIndexOf('.');
                    String destName = (lastDotIdx >= 0 ? archiveName.substring(0, lastDotIdx) : archiveName);
                    destDir = new File(destBaseDir, destName);
                } else if (zippedProjectType == ZippedProjectType.PROJECT_BASE_INCLUDED) {
                    destDir = new File(destBaseDir, zipDetect.getObj2());
                }
            }

            // increment name if necessary
            int fileNum = 1;
            String baseDestDir = destDir.getAbsolutePath();
            while (destDir.exists()) {
                String newDestDir = baseDestDir + "(" + (fileNum++) + ")";
                destDir = new File(newDestDir);
            }
        }
        return destDir;
    }

    @Override
    public void performTask() {
        super.setStatus(TaskStatus.RUNNING);

        try {
            extractArchive(archiveFile, getDestDir());

            // check if archive is in phon workspace
            File archiveParent = archiveFile.getParentFile();
            File phonWorkspace = Workspace.userWorkspaceFolder();
            File backupLoc = new File(phonWorkspace, "backups");
            if (!backupLoc.exists()) {
                backupLoc.mkdirs();
            }
            if (phonWorkspace.getAbsolutePath().equals(archiveParent.getAbsolutePath())) {
                // move archive file to ${workspace}/backups/
                File backupFile = new File(backupLoc, archiveFile.getName());

                FileUtils.copyFile(archiveFile, backupFile);
                archiveFile.delete();

                NativeDialogs.showMessageDialog(CommonModuleFrame.getCurrentFrame(), new NativeDialogListener() {

                    @Override
                    public void nativeDialogEvent(NativeDialogEvent event) {

                    }
                }, null, "File moved",
                        "Moved " + archiveFile.getName() + " to " + backupFile.getParentFile().getAbsolutePath());
            }
        } catch (IOException e) {
            LOGGER.severe(e.getMessage());
            super.err = e;
            super.setStatus(TaskStatus.ERROR);
            return;
        }

        super.setStatus(TaskStatus.FINISHED);
    }

    /**
     * Get zipped project type.
     * 
     * @param archive
     * @return the detected archive type and the project root folder name
     * in the zip file - only valid if detected type is ZippedProjectType.PROJECT_BASE_INCLUDED
     */
    private Tuple<ZippedProjectType, String> detectZippedProjectType(File archive) {
        ZippedProjectType projType = ZippedProjectType.NOT_A_PROJECT;
        String projRoot = null;

        try (ZipFile zip = new ZipFile(archive)) {
            ZipEntry entry = null;
            Enumeration<? extends ZipEntry> entries = zip.entries();
            while (entries.hasMoreElements()) {
                entry = entries.nextElement();

                if (entry.getName().contains("project.xml")) {
                    if (entry.getName().equals("project.xml")) {
                        projType = ZippedProjectType.PROJECT_BASE_NOT_INCLUDED;
                        projRoot = "";
                    } else {
                        projType = ZippedProjectType.PROJECT_BASE_INCLUDED;

                        File f = new File(entry.getName());
                        File rootFile = f.getParentFile();
                        projRoot = rootFile.getName();
                    }
                    break;
                }
            }
        } catch (IOException e) {
            LOGGER.warning(e.getMessage());
        }

        return new Tuple<ZippedProjectType, String>(projType, projRoot);
    }

    /**
     * Extract zip file
     * 
     * @param archive
     * @param outdir
     */
    private final static int ZIP_BUFFER = 2048;

    /**
     * Extract contents of a zip file to the destination directory.
     */
    private void extractArchive(File archive, File destDir) throws IOException {

        BufferedOutputStream out = null;
        BufferedInputStream in = null;

        try (ZipFile zip = new ZipFile(archive)) {
            // create output directory if it does not exist
            if (destDir.exists() && !destDir.isDirectory()) {
                throw new IOException("'" + destDir.getAbsolutePath() + "' is not a directory.");
            }

            if (!destDir.exists()) {
                setProperty(STATUS_PROP, "Creating output directory");
                destDir.mkdirs();
            }

            Tuple<ZippedProjectType, String> zipDetect = getZipDetect();
            ZipEntry entry = null;
            Enumeration<? extends ZipEntry> entries = zip.entries();
            while (entries.hasMoreElements()) {

                if (isShutdown()) {
                    return;
                }

                entry = entries.nextElement();

                String entryName = entry.getName();
                File outFile = null;
                if (zipDetect.getObj1() == ZippedProjectType.PROJECT_BASE_INCLUDED) {
                    // dest dir has already b
                    outFile = new File(destDir, entryName.replaceFirst(zipDetect.getObj2(), ""));
                } else {
                    outFile = new File(destDir, entryName);
                }

                if (entry.isDirectory()) {
                    if (!outFile.exists())
                        outFile.mkdirs();
                } else {
                    LOGGER.info("Extracting: " + entry);
                    setProperty(STATUS_PROP, "Extracting: " + entry);

                    in = new BufferedInputStream(zip.getInputStream(entry));
                    int count = -1;
                    byte data[] = new byte[ZIP_BUFFER];

                    if (outFile.exists()) {
                        LOGGER.warning("Overwriting file '" + outFile.getAbsolutePath() + "'");
                    }

                    File parentFile = outFile.getParentFile();

                    if (!parentFile.exists())
                        parentFile.mkdirs();

                    FileOutputStream fos = new FileOutputStream(outFile);
                    out = new BufferedOutputStream(fos, ZIP_BUFFER);
                    while ((count = in.read(data, 0, ZIP_BUFFER)) != -1) {
                        out.write(data, 0, count);
                    }
                    out.flush();
                    out.close();
                    in.close();
                }
            }
        } catch (IOException e) {
            LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e);
            throw e;
        }

        setProperty(STATUS_PROP, "Finished");
    }

}