msi.gama.gui.swt.dialogs.PickWorkspaceDialog.java Source code

Java tutorial

Introduction

Here is the source code for msi.gama.gui.swt.dialogs.PickWorkspaceDialog.java

Source

/*********************************************************************************************
 * 
 * 
 * 'PickWorkspaceDialog.java', in plugin 'msi.gama.application', is part of the source code of the
 * GAMA modeling and simulation platform.
 * (c) 2007-2014 UMI 209 UMMISCO IRD/UPMC & Partners
 * 
 * Visit https://code.google.com/p/gama-platform/ for license information and developers contact.
 * 
 * 
 **********************************************************************************************/
package msi.gama.gui.swt.dialogs;

// import java.awt.GridLayout;
import java.io.*;
import java.util.*;
import java.util.List;
import java.util.prefs.*;
import msi.gama.gui.swt.WorkspaceModelsManager;
import org.eclipse.jface.dialogs.*;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CLabel;
import org.eclipse.swt.layout.*;
import org.eclipse.swt.widgets.*;

/**
 * Dialog that lets/forces a user to enter/select a workspace that will be used when saving all
 * configuration files and settings. This dialog is shown at startup of the GUI just after the
 * splash screen has shown. Inspired by http://hexapixel.com/2009/01/12/rcp-workspaces
 */
public class PickWorkspaceDialog extends TitleAreaDialog {

    /*
     * The name of the file that tells us that the workspace directory belongs to our application
     */
    private static final String WS_IDENTIFIER = ".gama_application_workspace";
    // private static final String VERSION_IDENTIFIER = ".gama_version_" +
    // Platform.getProduct().getDefiningBundle().getVersion().toString();
    private static final String MODEL_IDENTIFIER = WorkspaceModelsManager.getCurrentGamaStampString();
    private static final String keyWorkspaceRootDir = "wsRootDir";
    private static final String keyRememberWorkspace = "wsRemember";
    private static final String keyLastUsedWorkspaces = "wsLastUsedWorkspaces";

    /*
     * This are our preferences we will be using as the IPreferenceStore is not available yet
     */
    // FIX: Removed the static reference in case it was causing trouble. Issue 240.

    // static Preferences preferences = Preferences.userRoot().node("gama");
    /* Various dialog messages */
    private static final String strMsg = "Your workspace is where settings and files of your Gama models will be stored.";
    private static final String strInfo = "Please select a directory that will be the workspace root";
    private static final String strError = "You must set a directory";

    /* Our controls */
    protected Combo workspacePathCombo;
    protected List<String> lastUsedWorkspaces;
    protected Button rememberWorkspaceButton;

    /* Used as separator when we save the last used workspace locations */
    private static final String splitChar = "#";
    /* Max number of entries in the history box */
    private static final int maxHistory = 20;

    /* Whatever the user picks ends up on this variable */
    private String selectedWorkspaceRootLocation;
    private boolean cloning = false;

    /**
     * Creates a new workspace dialog with a specific image as title-area image.
     * 
     * @param switchWorkspace true if we're using this dialog as a switch workspace dialog
     * @param wizardImage Image to show
     */
    public PickWorkspaceDialog() {
        super(Display.getDefault().getActiveShell());
        // setTitleImage(IGamaIcons.GAMA_ICON.image());
    }

    @Override
    protected void configureShell(final Shell newShell) {
        super.configureShell(newShell);
        newShell.setText("GAMA Models Workspace");
    }

    public static Preferences getNode() {
        try {
            if (Preferences.userRoot().nodeExists("gama")) {
                return Preferences.userRoot().node("gama");
            }
        } catch (BackingStoreException e1) {
            e1.printStackTrace();
        }
        Preferences p = Preferences.userRoot().node("gama");
        try {
            p.flush();
        } catch (BackingStoreException e) {
            e.printStackTrace();
        }
        return p;
    }

    /** Returns whether the user selected "remember workspace" in the preferences */
    public static boolean isRememberWorkspace() {
        return getNode().getBoolean(keyRememberWorkspace, false);
    }

    /**
     * Returns the last set workspace directory from the preferences
     * 
     * @return null if none
     */
    public static String getLastSetWorkspaceDirectory() {
        return getNode().get(keyWorkspaceRootDir, null);
    }

    @Override
    protected Control createDialogArea(final Composite parent) {
        setTitle("Choose a Workspace to store your models, settings, etc.");
        setMessage(strMsg);

        try {
            Composite inner = new Composite(parent, SWT.NONE);
            GridLayout l = new GridLayout(5, false);
            // double[][] layout =
            // new double[][] {
            // { 5, LatticeConstants.PREFERRED, 5, 250, 5, LatticeConstants.PREFERRED, 5 },
            // { 5, LatticeConstants.PREFERRED, 5, LatticeConstants.PREFERRED, 40 } };
            inner.setLayout(l);
            inner.setLayoutData(
                    new GridData(GridData.FILL_VERTICAL | GridData.VERTICAL_ALIGN_END | GridData.GRAB_HORIZONTAL));

            /* Label on the left */
            CLabel label = new CLabel(inner, SWT.NONE);
            label.setText("GAMA Models Workspace");
            label.setLayoutData(new GridData());

            /* Combo in the middle */
            workspacePathCombo = new Combo(inner, SWT.BORDER);
            workspacePathCombo.setLayoutData(new GridData());
            String wsRoot = getNode().get(keyWorkspaceRootDir, "");
            if (wsRoot == null || wsRoot.length() == 0) {
                wsRoot = getWorkspacePathSuggestion();
            }
            workspacePathCombo.setText(wsRoot);

            /* Checkbox below */
            rememberWorkspaceButton = new Button(inner, SWT.CHECK);
            rememberWorkspaceButton.setText("Remember workspace");
            rememberWorkspaceButton.setLayoutData(new GridData());
            rememberWorkspaceButton.setSelection(getNode().getBoolean(keyRememberWorkspace, false));

            String lastUsed = getNode().get(keyLastUsedWorkspaces, "");
            lastUsedWorkspaces = new ArrayList<String>();
            if (lastUsed != null) {
                String[] all = lastUsed.split(splitChar);
                for (String str : all) {
                    lastUsedWorkspaces.add(str);
                }
            }
            for (String last : lastUsedWorkspaces) {
                workspacePathCombo.add(last);
            }

            /* Browse button on the right */
            Button browse = new Button(inner, SWT.PUSH);
            browse.setText("Browse...");
            browse.setLayoutData(new GridData());
            browse.addListener(SWT.Selection, new Listener() {

                @Override
                public void handleEvent(final Event event) {
                    DirectoryDialog dd = new DirectoryDialog(getParentShell());
                    dd.setText("Select Workspace Root");
                    dd.setMessage(strInfo);
                    dd.setFilterPath(workspacePathCombo.getText());
                    String pick = dd.open();
                    if (pick == null) {
                        if (workspacePathCombo.getText().length() == 0) {
                            setMessage(strError, IMessageProvider.ERROR);
                        }
                    } else {
                        setMessage(strMsg);
                        workspacePathCombo.setText(pick);
                    }
                }
            });
            return inner;
        } catch (RuntimeException err) {
            err.printStackTrace();
            return null;
        }
    }

    /**
     * Returns whatever path the user selected in the dialog.
     * 
     * @return Path
     */
    public String getSelectedWorkspaceLocation() {
        return getSelectedWorkspaceRootLocation();
    }

    /* Suggests a default path based on the user.home/GAMA directory location */
    private String getWorkspacePathSuggestion() {
        StringBuffer buf = new StringBuffer();

        String uHome = System.getProperty("user.home");
        if (uHome == null) {
            uHome = "c:";
        }

        buf.append(uHome).append(File.separator).append("gama_workspace");

        return buf.toString();
    }

    @Override
    protected void createButtonsForButtonBar(final Composite parent) {

        /* Clone workspace needs a lot of checks */
        Button clone = createButton(parent, IDialogConstants.IGNORE_ID, "Clone current workspace", false);
        clone.addListener(SWT.Selection, new Listener() {

            @Override
            public void handleEvent(final Event arg0) {
                cloneCurrentWorkspace();
                // try {
                // String txt = workspacePathCombo.getText();
                // File workspaceDirectory = new File(txt);
                // if ( !workspaceDirectory.exists() ) {
                // MessageDialog.openError(Display.getDefault().getActiveShell(), "Error",
                // "The path entered does not exist. Please enter a valid path.");
                // return;
                // }
                //
                // if ( !workspaceDirectory.canRead() ) {
                // MessageDialog
                // .openError(Display.getDefault().getActiveShell(), "Error",
                // "The currently entered workspace path is not readable. Please check file system permissions.");
                // return;
                // }
                //
                // // check for workspace file (empty indicator that it's a workspace)
                // File wsFile = new File(txt + File.separator + WS_IDENTIFIER);
                // if ( !wsFile.exists() ) {
                // boolean b =
                // MessageDialog.openConfirm(Display.getDefault().getActiveShell(), "Confirm cloning",
                // "A new workspace will be created at location " + txt +
                // " and the current workspace contents will be copied to it. Confirm ?");
                // if ( !b ) { return; }
                // if ( !checkAndCreateWorkspaceRoot(txt) ) { return; }
                // } else {
                // boolean b =
                // MessageDialog
                // .openConfirm(
                // Display.getDefault().getActiveShell(),
                // "Existing workspace",
                // "The path entered is a path to an existing workspace. All its contents will be erased and replaced by the current workspace contents. Proceed anyway ?");
                // if ( !b ) { return; }
                // }
                //
                // DirectoryDialog dd = new DirectoryDialog(Display.getDefault().getActiveShell());
                // dd.setFilterPath(txt);
                // String directory = dd.open();
                // if ( directory == null ) { return; }
                //
                // File targetDirectory = new File(directory);
                // if ( targetDirectory.getAbsolutePath().equals(workspaceDirectory.getAbsolutePath()) ) {
                // MessageDialog.openError(Display.getDefault().getActiveShell(), "Error",
                // "Source and target workspaces are the same");
                // return;
                // }
                //
                // // recursive check, if new directory is a subdirectory of
                // // our workspace, that's a big no-no or we'll
                // // create directories forever
                // if ( isTargetSubdirOfDir(workspaceDirectory, targetDirectory) ) {
                // MessageDialog.openError(Display.getDefault().getActiveShell(), "Error",
                // "The path entered is a subdirectory of the current workspace");
                // return;
                // }
                //
                // try {
                // copyFiles(workspaceDirectory, targetDirectory);
                // } catch (Exception err) {
                // MessageDialog.openError(Display.getDefault().getActiveShell(), "Error",
                // "There was an error cloning the workspace: " + err.getMessage());
                // return;
                // }
                //
                // boolean setActive =
                // MessageDialog.openConfirm(Display.getDefault().getActiveShell(), "Workspace Cloned",
                // "Would you like to set the newly cloned workspace to be the active one?");
                // if ( setActive ) {
                // workspacePathCombo.setText(directory);
                // }
                // } catch (Exception err) {
                // MessageDialog.openError(Display.getDefault().getActiveShell(), "Error",
                // "There was an internal error, please check the logs");
                // err.printStackTrace();
                // }
            }
        });
        createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL, true);
        createButton(parent, IDialogConstants.CANCEL_ID, IDialogConstants.CANCEL_LABEL, false);
    }

    /* Checks whether a target directory is a subdirectory of ourselves */
    private boolean isTargetSubdirOfDir(final File source, final File target) {
        List<File> subdirs = new ArrayList<File>();
        getAllSubdirectoriesOf(source, subdirs);
        return subdirs.contains(target);
    }

    /* Helper for above */
    private void getAllSubdirectoriesOf(final File target, final List<File> buffer) {
        File[] files = target.listFiles();
        if (files == null || files.length == 0) {
            return;
        }

        for (File f : files) {
            if (f.isDirectory()) {
                buffer.add(f);
                getAllSubdirectoriesOf(f, buffer);
            }
        }
    }

    /**
     * This function will copy files or directories from one location to another. note that the
     * source and the destination must be mutually exclusive. This function can not be used to copy
     * a directory to a sub directory of itself. The function will also have problems if the
     * destination files already exist.
     * 
     * @param src -- A File object that represents the source for the copy
     * @param dest -- A File object that represents the destination for the copy.
     * @throws IOException if unable to copy.
     */
    public static void copyFiles(final File src, final File dest) throws IOException {
        /* Check to ensure that the source is valid... */
        if (!src.exists()) {
            throw new IOException("Can not find source: " + src.getAbsolutePath());
        } else if (!src.canRead()) { // check to ensure we have rights to the
            // source...
            throw new IOException("Cannot read: " + src.getAbsolutePath() + ". Check file permissions.");
        }
        /* Is this a directory copy? */
        if (src.isDirectory()) {
            /* Does the destination already exist? */
            if (!dest.exists()) {
                /* If not we need to make it exist if possible */
                if (!dest.mkdirs()) {
                    throw new IOException("Could not create direcotry: " + dest.getAbsolutePath());
                }
            }
            /* Get a listing of files... */
            String list[] = src.list();
            /* Copy all the files in the list. */
            for (int i = 0; i < list.length; i++) {
                File dest1 = new File(dest, list[i]);
                File src1 = new File(src, list[i]);
                copyFiles(src1, dest1);
            }
        } else {
            /* This was not a directory, so lets just copy the file */
            FileInputStream fin = null;
            FileOutputStream fout = null;
            byte[] buffer = new byte[4096];
            int bytesRead;
            try {
                /* Open the files for input and output */
                fin = new FileInputStream(src);
                fout = new FileOutputStream(dest);
                /* While bytesRead indicates a successful read, lets write... */
                while ((bytesRead = fin.read(buffer)) >= 0) {
                    fout.write(buffer, 0, bytesRead);
                }
            } catch (IOException e) {
                IOException wrapper = new IOException(
                        "Unable to copy file: " + src.getAbsolutePath() + "to" + dest.getAbsolutePath());
                wrapper.initCause(e);
                wrapper.setStackTrace(e.getStackTrace());
                throw wrapper;
                /* Ensure that the files are closed (if they were open). */
            } finally {
                if (fin != null) {
                    fin.close();
                }
                if (fout != null) {
                    fout.close();
                }
            }
        }
    }

    protected void cloneCurrentWorkspace() {
        // Some checks first
        String newLocation = workspacePathCombo.getText();
        String currentLocation = getNode().get(keyWorkspaceRootDir, "");
        if (currentLocation.isEmpty()) {
            MessageDialog.openError(Display.getDefault().getActiveShell(), "Error",
                    "No current workspace exists. Can only clone from an existing workspace");
            return;
        }
        File workspaceDirectory = new File(currentLocation);
        File targetDirectory = new File(newLocation);
        if (targetDirectory.getAbsolutePath().equals(workspaceDirectory.getAbsolutePath())) {
            MessageDialog.openError(Display.getDefault().getActiveShell(), "Error",
                    "Please enter a different location for the new workspace. A workspace cannot be cloned into itself.");
            return;
        }
        // recursive check, if new directory is a subdirectory of
        // our workspace, that's a big no-no or we'll
        // create directories forever
        if (isTargetSubdirOfDir(workspaceDirectory, targetDirectory)) {
            MessageDialog.openError(Display.getDefault().getActiveShell(), "Error",
                    "The path entered is a subdirectory of the current workspace. A workspace cannot be cloned in one of its sub-directories");
            return;
        }
        // If the checks are ok, we set "cloning" to true and do as if ok was pressed.
        cloning = true;
        try {
            okPressed();
        } finally {
            cloning = false;
        }
    }

    @Override
    protected void okPressed() {
        String str = workspacePathCombo.getText();
        // GuiUtils.debug("Directory to create " + str);
        if (str.length() == 0) {
            setMessage(strError, IMessageProvider.ERROR);
            return;
        }

        String ret = checkWorkspaceDirectory(getParentShell(), str, true, true, cloning);
        if (ret != null) {
            setMessage(ret, IMessageProvider.ERROR);
            return;
        }
        // GuiUtils.debug("Directory to create (after check " + str);
        /* Save it so we can show it in combo later */
        lastUsedWorkspaces.remove(str);

        if (!lastUsedWorkspaces.contains(str)) {
            lastUsedWorkspaces.add(0, str);
        }

        /* Deal with the max history */
        if (lastUsedWorkspaces.size() > maxHistory) {
            List<String> remove = new ArrayList<String>();
            for (int i = maxHistory; i < lastUsedWorkspaces.size(); i++) {
                remove.add(lastUsedWorkspaces.get(i));
            }

            lastUsedWorkspaces.removeAll(remove);
        }

        /* Create a string concatenation of all our last used workspaces */
        StringBuffer buf = new StringBuffer();
        for (int i = 0; i < lastUsedWorkspaces.size(); i++) {
            buf.append(lastUsedWorkspaces.get(i));
            if (i != lastUsedWorkspaces.size() - 1) {
                buf.append(splitChar);
            }
        }

        /* Save them onto our preferences */
        getNode().putBoolean(keyRememberWorkspace, rememberWorkspaceButton.getSelection());
        getNode().put(keyLastUsedWorkspaces, buf.toString());
        try {
            getNode().flush();
        } catch (BackingStoreException e) {
            e.printStackTrace();
        }

        /* Now create it */
        boolean ok = checkAndCreateWorkspaceRoot(str);
        if (!ok) {
            // GuiUtils.debug("Problem creating " + str);
            setMessage("No workspace could be created at location " + str + ", please check the error log");
            return;
        }

        /* Here we set the location so that we can later fetch it again */
        setSelectedWorkspaceRootLocation(str);

        /* And on our preferences as well */
        // GuiUtils.debug("Writing " + str + " in the preferences");
        if (cloning) {
            String previousLocation = getNode().get(keyWorkspaceRootDir, "");
            File workspaceDirectory = new File(previousLocation);
            File targetDirectory = new File(str);
            try {
                copyFiles(workspaceDirectory, targetDirectory);
            } catch (Exception err) {
                MessageDialog.openError(Display.getDefault().getActiveShell(), "Error",
                        "There was an error cloning the workspace: " + err.getMessage());
                return;
            }
        }
        getNode().put(keyWorkspaceRootDir, str);
        try {
            getNode().flush();
        } catch (BackingStoreException e) {
            e.printStackTrace();
        }

        super.okPressed();
    }

    /**
     * Ensures a workspace directory is OK in regards of reading/writing, etc. This method will get
     * called externally as well.
     * 
     * @param parentShell Shell parent shell
     * @param workspaceLocation Directory the user wants to use
     * @param askCreate Whether to ask if to create the workspace or not in this location if it does
     *            not exist already
     * @param fromDialog Whether this method was called from our dialog or from somewhere else just
     *            to check a location
     * @return null if everything is ok, or an error message if not
     */
    public static String checkWorkspaceDirectory(final Shell parentShell, final String workspaceLocation,
            final boolean askCreate, final boolean fromDialog, final boolean cloning) {
        File f = new File(workspaceLocation);
        if (!f.exists()) {
            if (askCreate) {
                boolean create = MessageDialog.openConfirm(parentShell, "New Directory",
                        workspaceLocation + " does not exist. Would you like to create a new workspace here"
                                + (cloning ? ", copy the contents of the current workspace into it, " : "")
                                + " and restart the application ?");
                if (create) {
                    try {
                        f.mkdirs();
                        File wsDot = new File(workspaceLocation + File.separator + WS_IDENTIFIER);
                        wsDot.createNewFile();
                        File dotFile = new File(workspaceLocation + File.separator + MODEL_IDENTIFIER);
                        dotFile.createNewFile();
                    } catch (RuntimeException err) {
                        // GuiUtils
                        // .debug("Error creating directories, please check folder permissions");
                        err.printStackTrace();
                        return "Error creating directories, please check folder permissions";
                    } catch (IOException er) {
                        er.printStackTrace();
                        return "Error creating directories, please check folder permissions";
                    }
                }

                if (!f.exists()) {
                    return "The selected directory does not exist";
                } else {
                    return null;
                }
            }
        }

        if (!f.canRead()) {
            // GuiUtils.debug("The selected directory is not readable");
            return "The selected directory is not readable";
        }

        if (!f.isDirectory()) {
            // GuiUtils.debug("The selected path is not a directory");
            return "The selected path is not a directory";
        }

        File wsTest = new File(workspaceLocation + File.separator + WS_IDENTIFIER);
        if (fromDialog) {
            if (!wsTest.exists()) {
                boolean create = MessageDialog.openConfirm(parentShell, "New Workspace", "The directory '"
                        + wsTest.getAbsolutePath()
                        + "' exists but is not identified as a GAMA workspace. \n\nWould you like to use it anyway ?");
                if (create) {
                    try {
                        f.mkdirs();
                        File wsDot = new File(workspaceLocation + File.separator + WS_IDENTIFIER);
                        wsDot.createNewFile();
                    } catch (Exception err) {
                        // GuiUtils
                        // .debug("Error creating directories, please check folder permissions");
                        return "Error creating directories, please check folder permissions";
                    }
                } else {
                    // GuiUtils.debug("Please select a directory for your workspace");
                    return "Please select a directory for your workspace";
                }

                if (!wsTest.exists()) {
                    // GuiUtils.debug("The selected directory does not exist");
                    return "The selected directory does not exist";
                }

                return null;
            }
        } else {
            if (!wsTest.exists()) {
                return "The selected directory is not a workspace directory";
            }
        }
        File dotFile = new File(workspaceLocation + File.separator + MODEL_IDENTIFIER);
        if (!dotFile.exists()) {
            if (fromDialog) {
                boolean create = MessageDialog.openConfirm(Display.getDefault().getActiveShell(),
                        "Outdated version of the models library",
                        "The workspace contains an old version of the models library. Do you want to proceed anyway ?");
                if (create) {
                    try {
                        dotFile.createNewFile();
                    } catch (IOException e) {
                        return "Error updating the models library";
                    }
                    return null;
                }
            }

            return "models";
        } else if (cloning) {
            boolean b = MessageDialog.openConfirm(Display.getDefault().getActiveShell(), "Existing workspace",
                    "The path entered is a path to an existing workspace. All its contents will be erased and replaced by the current workspace contents. Proceed anyway ?");
            if (!b) {
                return "no clone";
            }
        }
        return null;
    }

    /**
     * Checks to see if a workspace exists at a given directory string, and if not, creates it. Also
     * puts our identifying file inside that workspace.
     * 
     * @param wsRoot Workspace root directory as string
     * @return true if all checks and creations succeeded, false if there was a problem
     */
    public static boolean checkAndCreateWorkspaceRoot(final String wsRoot) {
        try {
            File fRoot = new File(wsRoot);
            if (!fRoot.exists()) {
                // GuiUtils.debug("Folder " + wsRoot + " does not exist");
                return false;
            }

            File dotFile = new File(wsRoot + File.separator + WS_IDENTIFIER);
            if (!dotFile.exists()) {
                boolean created = dotFile.createNewFile();
                if (!created) {
                    return false;
                }
                dotFile = new File(wsRoot + File.separator + MODEL_IDENTIFIER);
                dotFile.createNewFile();
            }

            return true;
        } catch (Exception err) {
            err.printStackTrace();
            return false;
        }
    }

    private String getSelectedWorkspaceRootLocation() {
        return selectedWorkspaceRootLocation;
    }

    private void setSelectedWorkspaceRootLocation(final String selectedWorkspaceRootLocation) {
        this.selectedWorkspaceRootLocation = selectedWorkspaceRootLocation;
    }

}