/** * OrbisGIS is a java GIS application dedicated to research in GIScience. * OrbisGIS is developed by the GIS group of the DECIDE team of the * Lab-STICC CNRS laboratory, see <http://www.lab-sticc.fr/>. * * The GIS group of the DECIDE team is located at : * * Laboratoire Lab-STICC – CNRS UMR 6285 * Equipe DECIDE * UNIVERSITÉ DE BRETAGNE-SUD * Institut Universitaire de Technologie de Vannes * 8, Rue Montaigne - BP 561 56017 Vannes Cedex * * OrbisGIS is distributed under GPL 3 license. * * Copyright (C) 2007-2014 CNRS (IRSTV FR CNRS 2488) * Copyright (C) 2015-2017 CNRS (Lab-STICC UMR CNRS 6285) * * 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.geocatalogtree.impl; import java.awt.BorderLayout; import java.awt.event.ActionListener; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.beans.EventHandler; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.atomic.AtomicBoolean; import javax.swing.JComponent; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JScrollPane; import javax.swing.JTree; import javax.swing.KeyStroke; import javax.swing.SwingUtilities; import javax.swing.SwingWorker; import javax.swing.event.TreeExpansionEvent; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.event.TreeWillExpandListener; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.ExpandVetoException; import javax.swing.tree.TreePath; import javax.swing.tree.TreeSelectionModel; import org.h2gis.api.DriverFunction; import org.h2gis.utilities.JDBCUtilities; import org.h2gis.utilities.TableLocation; import org.jooq.impl.DSL; import org.orbisgis.corejdbc.DataManager; import org.orbisgis.corejdbc.DatabaseProgressionListener; import org.orbisgis.corejdbc.StateEvent; import org.orbisgis.dbjobs.api.DatabaseView; import org.orbisgis.dbjobs.api.DriverFunctionContainer; import org.orbisgis.dbjobs.jobs.DropTable; import org.orbisgis.dbjobs.jobs.ExportInFileOperation; import org.orbisgis.geocatalogtree.api.GeoCatalogTreeAction; import org.orbisgis.geocatalogtree.api.GeoCatalogTreeNode; import org.orbisgis.geocatalogtree.api.GeoCatalogTreeNodeImpl; import org.orbisgis.geocatalogtree.api.PopupMenu; import org.orbisgis.geocatalogtree.api.PopupTarget; import org.orbisgis.geocatalogtree.api.TreeNodeFactory; import org.orbisgis.geocatalogtree.icons.GeocatalogIcon; import org.orbisgis.geocatalogtree.impl.jobs.CreateIndex; import org.orbisgis.geocatalogtree.impl.jobs.CreateSpatialIndex; import org.orbisgis.geocatalogtree.impl.jobs.DropColumn; import org.orbisgis.geocatalogtree.impl.jobs.DropIndex; import org.orbisgis.geocatalogtree.impl.nodes.TableAndField; import org.orbisgis.geocatalogtree.impl.nodes.TreeNodeFactoryImpl; import org.orbisgis.sif.components.actions.ActionCommands; import org.orbisgis.sif.components.actions.ActionDockingListener; import org.orbisgis.sif.components.fstree.CustomTreeCellRenderer; import org.orbisgis.sif.components.fstree.TreeNodeBusy; import org.orbisgis.sif.components.resourceTree.AbstractTreeModel; import org.orbisgis.sif.components.resourceTree.TreeSelectionIterable; import org.orbisgis.sif.docking.DockingPanel; import org.orbisgis.sif.docking.DockingPanelParameters; import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Reference; import org.osgi.service.component.annotations.ReferenceCardinality; import org.osgi.service.component.annotations.ReferencePolicy; import org.osgi.service.component.annotations.ReferencePolicyOption; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xnap.commons.i18n.I18n; import org.xnap.commons.i18n.I18nFactory; /** * @author Nicolas Fortin * @author Erwan Bocher */ @Component(service = DockingPanel.class) public class CatalogPanel extends JPanel implements DockingPanel, TreeWillExpandListener, DatabaseView, DatabaseProgressionListener, PopupTarget { private final JTree dbTree = new JTree(new String[0]); private DefaultTreeModel defaultTreeModel; private DockingPanelParameters dockingParameters = new DockingPanelParameters(); private static final I18n I18N = I18nFactory.getI18n(CatalogPanel.class); private static final Logger LOGGER = LoggerFactory.getLogger(CatalogPanel.class); private ActionCommands dockingActions = new ActionCommands(); private ActionCommands popupActions = new ActionCommands(); private DataManager dataManager; private Map<String, Set<TreeNodeFactory>> treeNodeFactories = new HashMap<>(); private TreeNodeFactoryImpl defaultTreeNodeFactory; private AtomicBoolean loadingNodeChildren = new AtomicBoolean(false); private ExecutorService executorService; private DriverFunctionContainer driverFunctionContainer; private Boolean isH2; public CatalogPanel() { super(new BorderLayout()); } @Reference(cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC, policyOption = ReferencePolicyOption.GREEDY) public void addPopupMenu(PopupMenu popupMenu) { popupActions.addActionFactory(popupMenu, this); popupActions.setAccelerators(this); } public void removePopupMenu(PopupMenu popupMenu) { popupActions.removeActionFactory(popupMenu); popupActions.setAccelerators(this); } @Override public List<String> getSelectedSources() { List<String> sources = new ArrayList<>(dbTree.getSelectionCount()); for (GeoCatalogTreeNode treeNode : new TreeSelectionIterable<>(dbTree.getSelectionPaths(), GeoCatalogTreeNode .class)) { if (GeoCatalogTreeNode.NODE_TABLE.equals(treeNode.getNodeType())) { sources.add(new TableLocation(treeNode.getParent().getNodeIdentifier(), treeNode.getNodeIdentifier()) .toString(isH2())); } } return sources; } /** * Check if the database is an H2 database engine. * @return */ private boolean isH2() { if(isH2 == null) { try(Connection connection = dataManager.getDataSource().getConnection()) { isH2 = JDBCUtilities.isH2DataBase(connection.getMetaData()); } catch (SQLException ex) { LOGGER.error(ex.getLocalizedMessage(), ex); return false; } } return isH2; } @Override public JTree getTree() { return dbTree; } @Reference public void setExecutorService(ExecutorService executorService) { this.executorService = executorService; } public void unsetExecutorService(ExecutorService executorService) { this.executorService = null; } public void init() { defaultTreeNodeFactory = new TreeNodeFactoryImpl(dataManager); addTreeNodeFactory(defaultTreeNodeFactory); dbTree.addMouseListener(EventHandler.create(MouseListener.class, this, "onMouseActionOnSourceList", "")); //This method ask the event data as argument //Items can be selected freely dbTree.getSelectionModel().setSelectionMode(TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION); dbTree.setRootVisible(false); dbTree.setShowsRootHandles(true); dbTree.setEditable(true); dbTree.addTreeSelectionListener(new IsEditableHandler(dbTree)); add(new JScrollPane(dbTree)); dockingParameters.setName("geocatalog-tree"); dockingParameters.setTitle(I18N.tr("DB Tree")); dockingParameters.setTitleIcon(GeocatalogIcon.getIcon("db_tree")); dockingParameters.setCloseable(true); // Set the built-in actions to docking frame dockingParameters.setDockActions(dockingActions.getActions()); // Add a listener to put additional actions to this docking dockingActions.addPropertyChangeListener(new ActionDockingListener(dockingParameters)); addActions(); } @Reference public void setDriverFunctionContainer(DriverFunctionContainer driverFunctionContainer) { this.driverFunctionContainer = driverFunctionContainer; } public void unsetDriverFunctionContainer(DriverFunctionContainer driverFunctionContainer) { this.driverFunctionContainer = null; } @Override public void onDatabaseUpdate(String entity, String... identifier) { Set<String> identifiers = new HashSet<>(Arrays.asList(identifier)); try { switch (DB_ENTITY.valueOf(entity)) { case TABLE: Enumeration<TreePath> paths = dbTree.getExpandedDescendants(new TreePath(defaultTreeModel.getRoot())); List<GeoCatalogTreeNode> nodeToUpdate = new ArrayList<>(dbTree.getRowCount()); while(paths != null && paths.hasMoreElements()) { GeoCatalogTreeNode node = (GeoCatalogTreeNode)paths.nextElement().getLastPathComponent(); if(node != null) { if(identifiers.contains(node.getNodeIdentifier())) { nodeToUpdate.add(node); } } } if(!nodeToUpdate.isEmpty() && loadingNodeChildren.compareAndSet(false, true)) { execute(new ReadDB(this, nodeToUpdate, loadingNodeChildren)); } break; } } catch (IllegalArgumentException ex) { // Db entity not managed by CatalogPanel } } private void addActions() { boolean isEmbeddedDataBase = true; try(Connection connection = dataManager.getDataSource().getConnection()) { DatabaseMetaData meta = connection.getMetaData(); isEmbeddedDataBase = JDBCUtilities.isH2DataBase(meta) && !meta.getURL().startsWith("jdbc:h2:tcp:"); } catch (SQLException ex) { LOGGER.error(ex.getLocalizedMessage(), ex); } //Popup:Add if(isEmbeddedDataBase) { GeoCatalogTreeAction addGroup = new GeoCatalogTreeAction(PopupMenu.M_ADD,I18N.tr("Add"), dbTree); addGroup.addNodeTypeFilter(GeoCatalogTreeNode.NODE_SCHEMA); popupActions.addAction(addGroup.setMenuGroup(true).setLogicalGroup(PopupMenu.GROUP_ADD)); //Popup:Add:File GeoCatalogTreeAction addFile = new GeoCatalogTreeAction(PopupMenu.M_ADD_FILE, I18N.tr("File"), I18N.tr ("Add a file from hard drive."), GeocatalogIcon.getIcon("page_white_add"), EventHandler.create (ActionListener.class, this, "onMenuAddLinkedFile"), KeyStroke.getKeyStroke(KeyEvent.VK_O, InputEvent.CTRL_DOWN_MASK), dbTree); addFile.addNodeTypeFilter(GeoCatalogTreeNode.NODE_SCHEMA); popupActions.addAction(addFile.addStroke(KeyStroke.getKeyStroke(KeyEvent.VK_O, InputEvent.CTRL_DOWN_MASK)) .setParent(PopupMenu.M_ADD)); //Popup:Add:Folder GeoCatalogTreeAction addFolder = new GeoCatalogTreeAction(PopupMenu.M_ADD_FOLDER,I18N.tr("Folder"), I18N.tr("Add a set of file from an hard drive folder."), GeocatalogIcon.getIcon("folder_add"),EventHandler.create(ActionListener.class, this,"onMenuAddFilesFromFolder"),KeyStroke.getKeyStroke("ctrl alt O"), dbTree); addFolder.addNodeTypeFilter(GeoCatalogTreeNode.NODE_SCHEMA); popupActions.addAction(addFolder.setParent(PopupMenu.M_ADD)); //Popup:Add:DataBase //popupActions.addAction(new DefaultAction(PopupMenu.M_ADD_DB,I18N.tr("DataBase"), // I18N.tr("Add one or more tables from a DataBase"), // OrbisGISIcon.getIcon("database_add"),EventHandler.create(ActionListener.class, // this,"onMenuAddFromDataBase"),null).setParent(PopupMenu.M_ADD)); } //Popup:Import GeoCatalogTreeAction importGroup = new GeoCatalogTreeAction(PopupMenu.M_IMPORT,I18N.tr("Import"),dbTree); popupActions.addAction(importGroup.addNodeTypeFilter(GeoCatalogTreeNode.NODE_SCHEMA).setMenuGroup(true).setLogicalGroup(PopupMenu.GROUP_IMPORT)); //Popup:Import:File GeoCatalogTreeAction importFile = new GeoCatalogTreeAction(PopupMenu.M_IMPORT_FILE,I18N.tr("File"), I18N.tr("Copy the content of a file from hard drive."), GeocatalogIcon.getIcon("page_white_add"),EventHandler.create(ActionListener.class, this,"onMenuImportFile"),KeyStroke.getKeyStroke("ctrl I"), dbTree); popupActions.addAction(importFile.addNodeTypeFilter(GeoCatalogTreeNode.NODE_SCHEMA).setParent(PopupMenu .M_IMPORT)); GeoCatalogTreeAction importFolder = new GeoCatalogTreeAction(PopupMenu.M_IMPORT_FOLDER,I18N.tr("Folder"), I18N.tr("Add a set of file from an hard drive folder."), GeocatalogIcon.getIcon("folder_add"),EventHandler.create(ActionListener.class, this,"onMenuImportFilesFromFolder"),KeyStroke.getKeyStroke("ctrl alt I"),dbTree); popupActions.addAction(importFolder.addNodeTypeFilter(GeoCatalogTreeNode.NODE_SCHEMA).setParent(PopupMenu .M_IMPORT)); //Popup:Save GeoCatalogTreeAction saveGroup = new GeoCatalogTreeAction(PopupMenu.M_SAVE,I18N.tr("Save"), dbTree); popupActions.addAction(saveGroup.addNodeTypeFilter(GeoCatalogTreeNode.NODE_TABLE).setMenuGroup(true) .setLogicalGroup(PopupMenu.GROUP_ADD)); //Popup:Save:File GeoCatalogTreeAction saveFile = new GeoCatalogTreeAction(PopupMenu.M_SAVE_FILE, I18N.tr("File"), I18N.tr ("Save selected sources in files"), GeocatalogIcon.getIcon("page_white_save"), EventHandler.create (ActionListener.class, this, "onMenuSaveInfile"), KeyStroke.getKeyStroke("ctrl S"), dbTree); popupActions.addAction(saveFile.addNodeTypeFilter(GeoCatalogTreeNode.NODE_TABLE).setParent(PopupMenu.M_SAVE)); //Popup:Save:Db //TODO Add linked table then transfer data //popupActions.addAction(new ActionOnSelection(PopupMenu.M_SAVE_DB,I18N.tr("Database"), // I18N.tr("Save selected sources in a data base"),OrbisGISIcon.getIcon("database_save"), // EventHandler.create(ActionListener.class,this,"onMenuSaveInDB"),getListSelectionModel()).setParent(PopupMenu.M_SAVE)); //Popup:Remove sources GeoCatalogTreeAction dropTable = new GeoCatalogTreeAction(PopupMenu.M_REMOVE, I18N.tr("Remove the source"), I18N.tr("Remove from this list the selected sources."), GeocatalogIcon.getIcon("remove"), EventHandler.create(ActionListener.class, this, "onMenuRemoveSource"), KeyStroke.getKeyStroke (KeyEvent.VK_DELETE, 0), dbTree); popupActions.addAction(dropTable.addNodeTypeFilter(GeoCatalogTreeNode.NODE_TABLE).setLogicalGroup(PopupMenu .GROUP_CLOSE)); // Popup:Remove index GeoCatalogTreeAction dropIndex = new GeoCatalogTreeAction(PopupMenu.M_REMOVE_INDEX, I18N.tr("Drop the index"), I18N.tr("Remove this index"), GeocatalogIcon.getIcon("remove"), EventHandler.create(ActionListener.class, this, "onMenuRemoveIndex"), KeyStroke.getKeyStroke (KeyEvent.VK_DELETE, 0), dbTree); popupActions.addAction(dropIndex.addNodeTypeFilter(GeoCatalogTreeNode.NODE_INDEX).setLogicalGroup(PopupMenu .GROUP_CLOSE)); //Popup:Refresh GeoCatalogTreeAction refresh = new GeoCatalogTreeAction(PopupMenu.M_REFRESH,I18N.tr("Refresh"), I18N.tr("Read the content of the database"), GeocatalogIcon.getIcon("refresh"),EventHandler.create(ActionListener.class, this,"refreshSourceList"),KeyStroke.getKeyStroke("ctrl R"), dbTree); popupActions.addAction(refresh.setLogicalGroup(PopupMenu.GROUP_OPEN)); GeoCatalogTreeAction createIndex = new GeoCatalogTreeAction(PopupMenu.M_CREATE_INDEX, I18N.tr("Create index"), GeocatalogIcon.getIcon("index_alpha"), EventHandler.create(ActionListener.class, this, "onMenuCreateIndex"), dbTree); createIndex.addNodeTypeFilter(GeoCatalogTreeNode.NODE_COLUMN); popupActions.addAction(createIndex); GeoCatalogTreeAction createSpatialIndex = new GeoCatalogTreeAction(PopupMenu.M_CREATE_SPATIAL_INDEX, I18N.tr("Create spatial index"), GeocatalogIcon.getIcon("index_geo"), EventHandler.create(ActionListener.class, this, "onMenuCreateSpatialIndex"), dbTree); createSpatialIndex.addNodeTypeFilter(GeoCatalogTreeNode.NODE_COLUMN); createSpatialIndex.check(GeoCatalogTreeNode.PROP_COLUMN_SPATIAL, true); popupActions.addAction(createSpatialIndex); GeoCatalogTreeAction dropColumn = new GeoCatalogTreeAction(PopupMenu.M_DROP_COLUMN, I18N.tr("Drop column"), GeocatalogIcon.getIcon("remove"), EventHandler.create(ActionListener.class, this, "onMenuDropColumn"), dbTree); popupActions.addAction(dropColumn.addNodeTypeFilter(GeoCatalogTreeNode.NODE_COLUMN).setLogicalGroup(PopupMenu .GROUP_CLOSE)); } @Reference(cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC, policyOption = ReferencePolicyOption.GREEDY) public void addTreeNodeFactory(TreeNodeFactory treeNodeFactory) { for(String nodeType : treeNodeFactory.getParentNodeType()) { Set<TreeNodeFactory> factorySet = treeNodeFactories.get(nodeType); if(factorySet == null) { factorySet = new HashSet<>(); treeNodeFactories.put(nodeType, factorySet); } factorySet.add(treeNodeFactory); } } /** * Add linked file to selected schema */ public void onMenuAddLinkedFile() { driverFunctionContainer.importFile(this, DriverFunction.IMPORT_DRIVER_TYPE.LINK); } /** * User click on create index */ public void onMenuCreateIndex() { List<TableAndField> fields = new ArrayList<TableAndField>(dbTree.getSelectionCount()); for (GeoCatalogTreeNode treeNode : new TreeSelectionIterable<>(dbTree.getSelectionPaths(), GeoCatalogTreeNode.class)) { if (GeoCatalogTreeNode.NODE_COLUMN.equals(treeNode.getNodeType())) { GeoCatalogTreeNode fieldNode = (GeoCatalogTreeNode) treeNode; TableLocation table = TableLocation.parse(fieldNode.getParent().getParent() .getNodeIdentifier()); if (!checkIndexExists(fieldNode, table)) { fields.add(new TableAndField(TableLocation.parse(treeNode.getParent().getParent().getNodeIdentifier()).toString(isH2()), treeNode.getNodeIdentifier())); } } } try { CreateIndex job = CreateIndex.onMenuCreateIndex(dataManager.getDataSource(), fields, this, this, isH2()); if (job != null) { execute(job); } } catch (SQLException ex) { LOGGER.error(ex.getLocalizedMessage(), ex); } } /** * User click on create spatial index */ public void onMenuCreateSpatialIndex() { List<TableAndField> fields = new ArrayList<TableAndField>(dbTree.getSelectionCount()); for (GeoCatalogTreeNode treeNode : new TreeSelectionIterable<>(dbTree.getSelectionPaths(), GeoCatalogTreeNode.class)) { if (GeoCatalogTreeNode.NODE_COLUMN.equals(treeNode.getNodeType())) { GeoCatalogTreeNode fieldNode = (GeoCatalogTreeNode) treeNode; TableLocation table = TableLocation.parse(fieldNode.getParent().getParent() .getNodeIdentifier()); if (!checkIndexExists(fieldNode, table)) { fields.add(new TableAndField(TableLocation.parse(treeNode.getParent().getParent().getNodeIdentifier()).toString(isH2()), treeNode.getNodeIdentifier())); } } } try { CreateSpatialIndex job = CreateSpatialIndex.onMenuCreateSpatialIndex(dataManager.getDataSource(), fields, this, this, isH2()); if (job != null) { execute(job); } } catch (SQLException ex) { LOGGER.error(ex.getLocalizedMessage(), ex); } } /** * User click on dropColumn */ public void onMenuDropColumn() { List<TableAndField> fields = new ArrayList<TableAndField>(dbTree.getSelectionCount()); for (GeoCatalogTreeNode treeNode : new TreeSelectionIterable<>(dbTree.getSelectionPaths(), GeoCatalogTreeNode.class)) { if (GeoCatalogTreeNode.NODE_COLUMN.equals(treeNode.getNodeType())) { fields.add(new TableAndField( TableLocation.parse(treeNode.getParent().getParent().getNodeIdentifier()).toString(isH2()), treeNode.getNodeIdentifier())); } } try { DropColumn job = DropColumn.onMenuDropColumn(dataManager.getDataSource(), fields, this, this); if (job != null) { execute(job); } } catch (SQLException ex) { LOGGER.error(ex.getLocalizedMessage(), ex); } } /** * Check if the field is already indexed * @param fieldNode * @param table */ private boolean checkIndexExists(GeoCatalogTreeNode fieldNode, TableLocation table) { try (Connection connection = dataManager.getDataSource().getConnection()) { DatabaseMetaData databaseMetaData = connection.getMetaData(); try (ResultSet rs = databaseMetaData.getIndexInfo(table.getCatalog(), table.getSchema(), table.getTable(), false, true)) { while (rs.next()) { String columnName = rs.getString("COLUMN_NAME"); if (fieldNode.getNodeIdentifier().equals(columnName)) { // Index already exists LOGGER.error(I18N.tr("This field is already indexed")); return true; } } } } catch (SQLException ex) { LOGGER.error(ex.getLocalizedMessage(), ex); } return false; } /** * Add all files from selected folder recursively */ public void onMenuAddFilesFromFolder() { driverFunctionContainer.addFilesFromFolder(this, DriverFunction.IMPORT_DRIVER_TYPE.LINK); } private String getSelectedSchema() { // Get selected schema String schema = null; Object nodeObj = dbTree.getLastSelectedPathComponent(); if(nodeObj instanceof GeoCatalogTreeNode && GeoCatalogTreeNode.NODE_SCHEMA.equals(((GeoCatalogTreeNode) nodeObj).getNodeType())) { schema = ((GeoCatalogTreeNode) nodeObj).getNodeIdentifier(); } return schema; } /** * Copy file content into a table */ public void onMenuImportFile() { driverFunctionContainer.importFile(this, DriverFunction.IMPORT_DRIVER_TYPE.COPY, getSelectedSchema()); } /** * Copy all files in a folder to tables */ public void onMenuImportFilesFromFolder() { driverFunctionContainer.addFilesFromFolder(this, DriverFunction.IMPORT_DRIVER_TYPE.COPY, getSelectedSchema()); } /** * Export a table into a file. */ public void onMenuSaveInfile() { List<String> sources = new ArrayList<>(dbTree.getSelectionCount()); for(GeoCatalogTreeNode treeNode : new TreeSelectionIterable<>(dbTree.getSelectionPaths(), GeoCatalogTreeNode.class)) { if(GeoCatalogTreeNode.NODE_TABLE.equals(treeNode.getNodeType())) { sources.add(treeNode.getNodeIdentifier()); } } ExportInFileOperation exportJob = ExportInFileOperation.saveInfile(dataManager.getDataSource(), sources, driverFunctionContainer); if(exportJob != null) { execute(exportJob); } } public void onMenuRemoveIndex() { List<String> indexes = new ArrayList<>(dbTree.getSelectionCount()); for(GeoCatalogTreeNode treeNode : new TreeSelectionIterable<>(dbTree.getSelectionPaths(), GeoCatalogTreeNode.class)) { if(GeoCatalogTreeNode.NODE_INDEX.equals(treeNode.getNodeType())) { indexes.add(treeNode.getNodeIdentifier()); } } try { DropIndex job = DropIndex.onMenuDropIndex(dataManager.getDataSource(), indexes, this, this); if (job != null) { execute(job); } } catch (SQLException ex) { LOGGER.error(ex.getLocalizedMessage(), ex); } } /** * Drop selected tables */ public void onMenuRemoveSource() { List<String> sources = new ArrayList<>(dbTree.getSelectionCount()); for(GeoCatalogTreeNode treeNode : new TreeSelectionIterable<>(dbTree.getSelectionPaths(), GeoCatalogTreeNode.class)) { if(GeoCatalogTreeNode.NODE_TABLE.equals(treeNode.getNodeType())) { sources.add(treeNode.getNodeIdentifier()); } } DropTable job = DropTable.onMenuRemoveSource(dataManager.getDataSource(), sources, this, this); if(job != null) { execute(job); } } private void execute(SwingWorker swingWorker) { if(executorService != null) { executorService.execute(swingWorker); } else { swingWorker.execute(); } } @Override public void progressionUpdate(StateEvent state) { // Refresh root node if(state.isUpdateDatabaseStructure() && loadingNodeChildren.compareAndSet(false, true)) { execute(new ReadDB(this, (GeoCatalogTreeNode) defaultTreeModel.getRoot(), loadingNodeChildren)); } } @Override public void refreshSourceList() { if(loadingNodeChildren.compareAndSet(false, true)) { if (!dbTree.isSelectionEmpty()) { List<GeoCatalogTreeNode> nodeToRefresh = new ArrayList<>(dbTree.getSelectionCount()); for (GeoCatalogTreeNode treeNode : new TreeSelectionIterable<>(dbTree.getSelectionPaths(), GeoCatalogTreeNode.class)) { nodeToRefresh.add(treeNode); } if(nodeToRefresh.isEmpty()) { loadingNodeChildren.set(false); } else { execute(new ReadDB(this, nodeToRefresh, loadingNodeChildren)); } } else { // Refresh root node execute(new ReadDB(this, (GeoCatalogTreeNode) defaultTreeModel.getRoot(), loadingNodeChildren)); } } } public void onMouseActionOnSourceList(MouseEvent e) { //Manage selection of items before popping up the menu if (e.isPopupTrigger()) { //Right mouse button under linux and windows //Update selection TreePath path = dbTree.getPathForLocation(e.getX(), e.getY()); TreePath[] selectionPaths = dbTree.getSelectionPaths(); if (selectionPaths != null && path != null){ if (!AbstractTreeModel.contains(selectionPaths, path)) { if (e.isControlDown()) { dbTree.addSelectionPath(path); } else { dbTree.setSelectionPath(path); } } } else { dbTree.setSelectionPath(path); } JPopupMenu popup = new JPopupMenu(); popupActions.copyEnabledActions(popup); if (popup.getComponentCount()>0) { popup.show(e.getComponent(), e.getX(), e.getY()); } } } public void removeTreeNodeFactory(TreeNodeFactory treeNodeFactory) { GeoCatalogTreeNode current = (GeoCatalogTreeNode)defaultTreeModel.getRoot(); while(current != null) { if(treeNodeFactory.equals(current.getFactory())) { GeoCatalogTreeNode removed = current; current = current.getParent(); defaultTreeModel.removeNodeFromParent(removed); } } Set<Map.Entry<String, Set<TreeNodeFactory>>> factorySet = treeNodeFactories.entrySet(); for(Map.Entry<String, Set<TreeNodeFactory>> entry : factorySet) { if(treeNodeFactory.equals(entry.getValue())) { factorySet.remove(entry); } } } @Reference public void setDataManager(DataManager dataManager) { this.dataManager = dataManager; } public void unsetDataManager(DataManager dataManager) { dataManager.removeDatabaseProgressionListener(this); this.dataManager = null; } @Activate public void activate() { init(); new InitTree(this).execute(); } private void initTree() { // Load catalogs try(Connection connection = dataManager.getDataSource().getConnection()) { defaultTreeModel = new GeoCatalogTreeModel(new GeoCatalogTreeNodeImpl(null, "", ""), true); defaultTreeNodeFactory.loadDatabase(DSL.using(connection).meta(), defaultTreeModel); dbTree.setModel(defaultTreeModel); dbTree.setCellRenderer(new CustomTreeCellRenderer(dbTree)); dbTree.expandPath(new TreePath(defaultTreeModel.getRoot())); updateNode((GeoCatalogTreeNode) defaultTreeModel.getRoot()); dbTree.addTreeWillExpandListener(this); dataManager.addDatabaseProgressionListener(this, StateEvent.DB_STATES.STATE_STATEMENT_END); popupActions.setAccelerators(this); dbTree.setDragEnabled(true); dbTree.setTransferHandler(new DBTreeTranferHandler(dbTree)); } catch (SQLException ex) { LOGGER.error(ex.getLocalizedMessage(), ex); } } @Override public void treeWillExpand(TreeExpansionEvent event) throws ExpandVetoException { // Update expanding node Object lastPathComp = event.getPath().getLastPathComponent(); if(lastPathComp instanceof GeoCatalogTreeNode && ((GeoCatalogTreeNode) lastPathComp).getChildCount() == 0) { GeoCatalogTreeNode node = (GeoCatalogTreeNode)lastPathComp; loadingNodeChildren.set(true); new ReadDB(this, node, loadingNodeChildren).execute(); } } /** * Load children of this node * @param node Parent node */ public void updateNode(GeoCatalogTreeNode node) { Set<TreeNodeFactory> factorySet = treeNodeFactories.get(node.getNodeType()); if(factorySet != null) { try(Connection connection = dataManager.getDataSource().getConnection()) { for (TreeNodeFactory factory : factorySet) { factory.updateChildren(node, connection, dbTree); } } catch (SQLException ex) { LOGGER.error(ex.getLocalizedMessage(), ex); } } } @Override public void treeWillCollapse(TreeExpansionEvent event) throws ExpandVetoException { // Nothing to do } @Override public DockingPanelParameters getDockingParameters() { return dockingParameters; } @Override public JComponent getComponent() { return this; } private static class InitTree extends SwingWorker { private CatalogPanel catalogPanel; public InitTree(CatalogPanel catalogPanel) { this.catalogPanel = catalogPanel; } @Override protected Object doInBackground() throws Exception { catalogPanel.initTree(); return null; } } public JTree getDbTree() { return dbTree; } private static class ReadDB extends SwingWorker { private CatalogPanel catalogPanel; private List<GeoCatalogTreeNode> nodes; private AtomicBoolean loadingNodeChildren; public ReadDB(CatalogPanel catalogPanel, List<GeoCatalogTreeNode> nodes, AtomicBoolean loadingNodeChildren) { this.catalogPanel = catalogPanel; this.nodes = nodes; this.loadingNodeChildren = loadingNodeChildren; } public ReadDB(CatalogPanel catalogPanel, GeoCatalogTreeNode node, AtomicBoolean loadingNodeChildren) { this.catalogPanel = catalogPanel; this.nodes = Arrays.asList(node); this.loadingNodeChildren = loadingNodeChildren; } @Override protected Object doInBackground() throws Exception { try { for (GeoCatalogTreeNode node : nodes) { TreeNodeBusy nodeBusy = null; try { if (node.getAllowsChildren() && node.getChildCount() == 0) { nodeBusy = new TreeNodeBusy(); DefaultTreeModel treeModel = (DefaultTreeModel) catalogPanel.getDbTree().getModel(); nodeBusy.setModel(treeModel); // Model change should be done on swing event thread SwingUtilities.invokeAndWait(new InsertBusyNode(treeModel, nodeBusy, node)); nodeBusy.setDoAnimation(true); } catalogPanel.updateNode(node); } finally { if (nodeBusy != null) { nodeBusy.setDoAnimation(false); } } } } finally { loadingNodeChildren.set(false); } return null; } } private static class InsertBusyNode implements Runnable { private DefaultTreeModel treeModel; private TreeNodeBusy nodeBusy; private GeoCatalogTreeNode node; public InsertBusyNode(DefaultTreeModel treeModel, TreeNodeBusy nodeBusy, GeoCatalogTreeNode node) { this.treeModel = treeModel; this.nodeBusy = nodeBusy; this.node = node; } @Override public void run() { treeModel.insertNodeInto(nodeBusy, node, 0); } } /** * Change tree editable state using selected component editable state. */ private static class IsEditableHandler implements TreeSelectionListener { private JTree tree; public IsEditableHandler(JTree tree) { this.tree = tree; } @Override public void valueChanged(TreeSelectionEvent treeSelectionEvent) { TreePath path = treeSelectionEvent.getPath(); Object lastPathComponent = path.getLastPathComponent(); if(lastPathComponent instanceof GeoCatalogTreeNode) { tree.setEditable(((GeoCatalogTreeNode) lastPathComponent).isEditable()); } } } }