org.orbisgis.view.components.fstree.TreeNodeFolder.java Source code

Java tutorial

Introduction

Here is the source code for org.orbisgis.view.components.fstree.TreeNodeFolder.java

Source

/*
 * OrbisGIS is a GIS application dedicated to scientific spatial simulation.
 * This cross-platform GIS is developed at French IRSTV institute and is able to
 * manipulate and create vector and raster spatial information. 
 * 
 * OrbisGIS is distributed under GPL 3 license. It is produced by the "Atelier SIG"
 * team of the IRSTV Institute <http://www.irstv.fr/> CNRS FR 2488.
 * 
 * Copyright (C) 2007-2012 IRSTV (FR CNRS 2488)
 * 
 * This file is part of OrbisGIS.
 * 
 * OrbisGIS 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.
 * 
 * OrbisGIS 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
 * OrbisGIS. If not, see <http://www.gnu.org/licenses/>.
 * 
 * For more information, please consult: <http://www.orbisgis.org/>
 * or contact directly:
 * info_at_ orbisgis.org
 */
package org.orbisgis.view.components.fstree;

import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.event.ActionListener;
import java.beans.EventHandler;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;
import javax.swing.TransferHandler.TransferSupport;
import javax.swing.tree.MutableTreeNode;
import javax.swing.tree.TreeNode;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.log4j.Logger;
import org.orbisgis.core.Services;
import org.orbisgis.progress.ProgressMonitor;
import org.orbisgis.sif.UIFactory;
import org.orbisgis.view.background.BackgroundJob;
import org.orbisgis.view.background.BackgroundManager;
import org.orbisgis.view.icons.OrbisGISIcon;
import org.orbisgis.view.util.MenuCommonFunctions;
import org.xnap.commons.i18n.I18n;
import org.xnap.commons.i18n.I18nFactory;

/**
 * Represent a folder in the file system.
 * @author Nicolas Fortin
 */
public class TreeNodeFolder extends AbstractTreeNodeContainer
        implements PopupTreeNode, TreeNodePath, DropDestinationTreeNode, DragTreeNode {
    private File folderPath;
    private static final Logger LOGGER = Logger.getLogger("gui." + TreeNodeFolder.class);
    private static final I18n I18N = I18nFactory.getI18n(TreeNodeFolder.class);
    private TreeNodeFileFactoryManager factoryManager;

    /**
     * @param folderPath
     * @param factoryManager 
     * @throws IllegalArgumentException If the provided path represent a file
     */
    public TreeNodeFolder(File folderPath, TreeNodeFileFactoryManager factoryManager) {
        this.factoryManager = factoryManager;
        this.folderPath = folderPath;
        if (!folderPath.isDirectory()) {
            throw new IllegalArgumentException("The file path must be a directory");
        }
        setLabel(folderPath.getName());
    }

    @Override
    public void setParent(MutableTreeNode mtn) {
        super.setParent(mtn);
        // Only sub folder can be renamed
        setEditable(mtn instanceof TreeNodeFolder);
    }

    /**
     * Read the file system and insert the new files and folders
     */
    public void updateTree() {
        List<String> fsList;
        try {
            fsList = new ArrayList<String>(Arrays.asList(getFilePath().list()));
        } catch (SecurityException ex) {
            LOGGER.error(I18N.tr("Cannot list the directory content"), ex);
            return;
        }
        // Find deleted sub-elements, and update existing sub-folders 
        List<MutableTreeNode> childrenToRemove = new ArrayList<MutableTreeNode>(children.size());
        for (MutableTreeNode child : children) {
            if (child instanceof TreeNodePath) {
                String childFileName = ((TreeNodePath) child).getFilePath().getName();
                if (!fsList.contains(childFileName)) {
                    childrenToRemove.add(child);
                } else {
                    fsList.remove(childFileName);
                    if (child instanceof TreeNodeFolder) {
                        ((TreeNodeFolder) child).updateTree();
                    }
                }
            }
        }
        // Effective children removal
        for (MutableTreeNode child : childrenToRemove) {
            model.removeNodeFromParent(child);
        }
        // Add the new children, and update new sub-folders 
        for (String childPath : fsList) {
            File newChild = new File(getFilePath(), childPath);
            if (newChild.isDirectory()) {
                TreeNodeFolder subDir = new TreeNodeFolder(newChild, factoryManager);
                model.insertNodeInto(subDir, this, getChildCount());
                subDir.updateTree();
            } else {
                AbstractTreeNode child = factoryManager.create(newChild);
                if (child != null) {
                    model.insertNodeInto(child, this, getChildCount());
                }
            }
        }
    }

    @Override
    public void setUserObject(Object o) {
        //User set the folder name
        File curPath = getFilePath();
        File dest = new File(curPath.getParentFile(), o.toString());
        if (dest.exists()) {
            LOGGER.error("The specified folder already exists");
            return;
        }
        if (curPath.renameTo(dest)) {
            folderPath = dest;
            setLabel(folderPath.getName());
        } else {
            LOGGER.error("The specified folder name is not correct");
        }
    }

    /**
     * File&Folder deletion
     */
    public void onDeleteFolder() {
        if (getFilePath().exists()) {
            int result = JOptionPane.showConfirmDialog(UIFactory.getMainFrame(),
                    I18N.tr("Are you sure you want to delete permanently the selected files ?"),
                    I18N.tr("Remove the files"), JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE);
            if (result == JOptionPane.YES_OPTION) {
                try {
                    FileUtils.deleteDirectory(getFilePath());
                } catch (IOException ex) {
                    LOGGER.error(I18N.tr("Cannot remove the folder {0}", getFilePath().getName()), ex);
                }
            } else {
                return;
            }
        }
        model.removeNodeFromParent(this);
    }

    /**
     * Copy the folder path to the ClipBoard
     */
    public void onCopyPath() {
        StringSelection pathString = new StringSelection(getFilePath().getAbsolutePath());
        try {
            Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
            clipboard.setContents(pathString, pathString);
            LOGGER.info(I18N.tr("The path {0} has been copied to the clipboard", getFilePath().getAbsolutePath()));
        } catch (Throwable ex) {
            LOGGER.error(I18N.tr("Could not copy the folder path to the clipboard"), ex);
        }
    }

    @Override
    public void feedPopupMenu(JPopupMenu menu) {
        if (menu.getComponentCount() > 0) {
            menu.addSeparator();
        }
        // Open the folder
        JMenuItem copyPathMenu = new JMenuItem(I18N.tr("Copy the path"));
        copyPathMenu.setToolTipText(I18N.tr("Copy the folder path in the clipboard"));
        copyPathMenu.setActionCommand("TreeNodeFolder:CopyPath");
        copyPathMenu.addActionListener(EventHandler.create(ActionListener.class, this, "onCopyPath"));
        MenuCommonFunctions.updateOrInsertMenuItem(menu, copyPathMenu);
        // Read the file system to update the tree
        JMenuItem updateMenu = new JMenuItem(I18N.tr("Update"), OrbisGISIcon.getIcon("arrow_refresh"));
        updateMenu.setToolTipText(I18N.tr("Update the content of this folder from the file system"));
        updateMenu.setActionCommand("Update");
        updateMenu.addActionListener(EventHandler.create(ActionListener.class, this, "updateTree"));
        MenuCommonFunctions.updateOrInsertMenuItem(menu, updateMenu);
        // Add a new Sub Folder
        JMenuItem newSubFolder = new JMenuItem(I18N.tr("New folder"), OrbisGISIcon.getIcon("folder_add"));
        newSubFolder.setToolTipText(I18N.tr("Create a sub-folder"));
        newSubFolder.setActionCommand("TreeNodeFolder:newSubFolder");
        newSubFolder.addActionListener(EventHandler.create(ActionListener.class, this, "onNewSubFolder"));
        MenuCommonFunctions.updateOrInsertMenuItem(menu, newSubFolder);
        // Remove the folder
        //The root folder cannot be removed
        if (parent instanceof TreeNodeFolder) {
            JMenuItem folderRemove = new JMenuItem(I18N.tr("Delete"), OrbisGISIcon.getIcon("remove"));
            folderRemove.setToolTipText(I18N.tr("Remove permanently the folder"));
            folderRemove.setActionCommand("delete");
            folderRemove.addActionListener(EventHandler.create(ActionListener.class, this, "onDeleteFolder"));
            MenuCommonFunctions.updateOrInsertMenuItem(menu, folderRemove, true);
        }
    }

    /**
     * Create a sub folder, the folder name is given through an input dialog
     */
    public void onNewSubFolder() {
        String folderName = JOptionPane.showInputDialog(UIFactory.getMainFrame(), I18N.tr("Enter the folder name"),
                I18N.tr("Folder"));
        if (folderName != null) {
            File newFolderPath = getUniqueFileName(new File(getFilePath(), folderName));
            try {
                if (!newFolderPath.mkdir()) {
                    LOGGER.error(I18N.tr("The folder creation has failed"));
                } else {
                    updateTree();
                }
            } catch (Throwable ex) {
                LOGGER.error(I18N.tr("The folder creation has failed"), ex);
            }
        }
    }

    @Override
    public File getFilePath() {
        if (parent instanceof TreeNodePath) {
            File parentPath = ((TreeNodePath) parent).getFilePath();
            return new File(parentPath, folderPath.getName());
        } else {
            return folderPath;
        }
    }

    /**
     * Return the children of this folder
     * @return 
     */
    public List<AbstractTreeNode> getChildren() {
        List<AbstractTreeNode> childrenRet = new ArrayList<AbstractTreeNode>(getChildCount());
        for (MutableTreeNode child : children) {
            childrenRet.add((AbstractTreeNode) child);
        }
        return childrenRet;
    }

    @Override
    public boolean canImport(TransferSupport ts) {
        return ts.isDataFlavorSupported(TransferableNodePaths.PATHS_FLAVOR)
                || ts.isDataFlavorSupported(TransferableFileContent.FILE_CONTENT_FLAVOR);
    }

    /**
     * Extract all components of the path to compare with the second argument
     * @param path
     * @param compPath Absolute path
     * @return 
     */
    private static boolean hasAParent(File path, String compPath) {
        File parent = path.getParentFile();
        while (parent != null) {
            if (compPath.equals(parent.getAbsolutePath())) {
                return true;
            }
            parent = parent.getParentFile();
        }
        return false;
    }

    /**
     * Extract all components of the path to compare with the second argument
     * @param path
     * @param paths Set of Absolute path
     * @return 
     */
    private static boolean hasAParentInPathList(File path, Set<String> paths) {
        File parent = path.getParentFile();
        while (parent != null) {
            if (paths.contains(parent.getAbsolutePath())) {
                return true;
            }
            parent = parent.getParentFile();
        }
        return false;
    }

    /**
     * Return the appropriate file path to not overwrite existing files
     * @param basePath Path of the file
     * @return Non existing file path
     */
    private static File getUniqueFileName(final File fullPath) {
        int cpt = 0;
        final String fileNameWithExt = fullPath.getName();
        final File basePath = fullPath.getParentFile();
        final String extension = FilenameUtils.getExtension(fileNameWithExt);
        final String baseName = FilenameUtils.getBaseName(fileNameWithExt);
        File fileName = fullPath;
        while (fileName.exists()) {
            String newFileName = baseName + "_" + (cpt++) + "." + extension;
            fileName = new File(basePath, newFileName);
        }
        return fileName;
    }

    /**
     * Create a file from a Reader instance in this folder
     * @param ts
     * @param flavor
     * @return 
     */
    private boolean importReader(Transferable tf, DataFlavor flavor) {
        try {
            // From this transferable, a file can be created
            Object transferData = tf.getTransferData(flavor);
            if (!(transferData instanceof Reader)) {
                return false;
            }
            BufferedReader br = new BufferedReader((Reader) transferData);
            File fileName;
            if (transferData instanceof TransferableFileContent) {
                // The filename is given by the drag source
                fileName = new File(getFilePath(), ((TransferableFileContent) transferData).getFileNameHint());
            } else {
                // The filename must be given by the user
                String contentFileName = JOptionPane.showInputDialog(UIFactory.getMainFrame(),
                        I18N.tr("Enter the folder name"), I18N.tr("Folder"));
                if (contentFileName != null) {
                    fileName = new File(getFilePath(), contentFileName);
                } else {
                    return false;
                }
            }
            // If the file exists found a new one
            fileName = getUniqueFileName(fileName);
            FileWriter writer = new FileWriter(fileName);
            String res = br.readLine();
            while (res != null) {
                writer.write(res + "\n");
                res = br.readLine();
            }
            writer.close();
            return true;
        } catch (UnsupportedFlavorException ex) {
            LOGGER.error(ex.getLocalizedMessage(), ex);
            return false;
        } catch (IOException ex) {
            LOGGER.error(ex.getLocalizedMessage(), ex);
            return false;
        }
    }

    @Override
    public boolean importData(TransferSupport ts) {
        BackgroundManager bm = Services.getService(BackgroundManager.class);
        if (ts.isDataFlavorSupported(TransferableNodePaths.PATHS_FLAVOR)) {
            // Move Nodes and Move Files
            bm.nonBlockingBackgroundOperation(new DropTransferable(ts.getTransferable(), this));
            return true;
        } else {
            DataFlavor[] flavors = ts.getDataFlavors();
            for (DataFlavor flavor : flavors) {
                if (flavor.isRepresentationClassReader()) {
                    bm.nonBlockingBackgroundOperation(new DropTransferable(ts.getTransferable(), this));
                    return true;
                }
            }
            return false;
        }
    }

    @Override
    public boolean completeTransferable(TransferableList transferable) {
        if (parent instanceof TreeNodeFolder) {
            transferable.addTransferable(new TransferableNodePaths(this));
            return true;
        } else {
            return false;
        }
    }

    private void transferTreeNodePath(List<TreeNodePath> nodePath) {
        // Moved paths set
        Set<String> paths = new HashSet<String>();
        for (TreeNodePath treeNode : nodePath) {
            File treeFilePath = treeNode.getFilePath();
            paths.add(treeFilePath.getAbsolutePath());
        }
        // Process folder and files moves
        for (TreeNodePath treeNode : nodePath) {
            // Ignore if a parent path is already on the list
            // or if this folder node is the child of a transfered path
            File treeFilePath = treeNode.getFilePath();
            if (!hasAParentInPathList(treeFilePath, paths)
                    && !hasAParent(getFilePath(), treeFilePath.getAbsolutePath())) {
                try {
                    File dest = new File(getFilePath(), treeFilePath.getName());
                    if (!dest.exists()) {
                        // Move the folder
                        FileUtils.moveToDirectory(treeFilePath, getFilePath(), false);
                    } else {
                        LOGGER.warn(I18N.tr("Destination file {0} already exists, cannot move {1}", dest,
                                treeFilePath));
                    }
                } catch (IOException ex) {
                    LOGGER.error(ex.getLocalizedMessage(), ex);
                    return;
                }
            }
        }
    }

    /**
     * Process drop operation on this node
     */
    private static class DropTransferable implements BackgroundJob {
        Transferable transferable;
        TreeNodeFolder folderNode;

        public DropTransferable(Transferable transferable, TreeNodeFolder folderNode) {
            this.transferable = transferable;
            this.folderNode = folderNode;
        }

        @Override
        public void run(ProgressMonitor pm) {

            if (transferable.isDataFlavorSupported(TransferableNodePaths.PATHS_FLAVOR)) {
                // Move Nodes and Move Files
                List<TreeNodePath> nodePath = new ArrayList<TreeNodePath>();
                try {
                    Object objTrans = transferable.getTransferData(TransferableNodePaths.PATHS_FLAVOR);
                    if (objTrans instanceof List) {
                        nodePath = (List<TreeNodePath>) objTrans;
                    }
                } catch (UnsupportedFlavorException ex) {
                    LOGGER.error(ex.getLocalizedMessage(), ex);
                    return;
                } catch (IOException ex) {
                    LOGGER.error(ex.getLocalizedMessage(), ex);
                    return;
                }
                folderNode.transferTreeNodePath(nodePath);
            } else {
                DataFlavor[] flavors = transferable.getTransferDataFlavors();
                for (DataFlavor flavor : flavors) {
                    if (flavor.isRepresentationClassReader()) {
                        folderNode.importReader(transferable, flavor);
                        break;
                    }
                }
            }
            SwingUtilities.invokeLater(new UpdateTree(folderNode));
        }

        @Override
        public String getTaskName() {
            return I18N.tr("Drop the content in the folder..");
        }

    }

    /**
     * Update the whole folder tree
     */
    private static class UpdateTree implements Runnable {
        TreeNodeFolder folderNode;

        public UpdateTree(TreeNodeFolder folderNode) {
            this.folderNode = folderNode;
        }

        @Override
        public void run() {
            // Retrieve the top folder
            TreeNodeFolder topFolder = folderNode;
            TreeNode cursor = folderNode;
            while (cursor instanceof TreeNodeFolder) {
                topFolder = (TreeNodeFolder) cursor;
                cursor = cursor.getParent();
            }
            topFolder.updateTree();
        }
    }
}