/** * 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.geocatalog.impl; import org.h2gis.api.DriverFunction; import org.h2gis.utilities.JDBCUtilities; import org.orbisgis.commons.utils.CollectionUtils; import org.orbisgis.corejdbc.DataManager; 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.geocatalog.api.GeoCatalogMenu; import org.orbisgis.geocatalog.api.PopupMenu; import org.orbisgis.geocatalog.api.PopupTarget; import org.orbisgis.geocatalog.api.TitleActionBar; import org.orbisgis.geocatalog.icons.GeocatalogIcon; import org.orbisgis.geocatalog.impl.actions.ActionOnSelection; import org.orbisgis.geocatalog.impl.filters.IFilter; import org.orbisgis.geocatalog.impl.filters.factories.NameContains; import org.orbisgis.geocatalog.impl.filters.factories.NameNotContains; import org.orbisgis.geocatalog.impl.filters.factories.SourceTypeIs; import org.orbisgis.geocatalog.impl.renderer.DataSourceListCellRenderer; import org.orbisgis.sif.common.ContainerItemProperties; import org.orbisgis.sif.components.actions.ActionCommands; import org.orbisgis.sif.components.actions.ActionDockingListener; import org.orbisgis.sif.components.actions.DefaultAction; import org.orbisgis.sif.components.filter.DefaultActiveFilter; import org.orbisgis.sif.components.filter.FilterFactoryManager; 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.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xnap.commons.i18n.I18n; import org.xnap.commons.i18n.I18nFactory; import javax.swing.*; import java.awt.BorderLayout; import java.awt.event.*; import java.beans.EventHandler; import java.net.URI; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.SQLException; import java.util.*; import java.util.concurrent.ExecutorService; /** * This is the GeoCatalog panel. That Panel show the list of available * DataSource * * This is connected with the DataSource model. */ @Component(service = DockingPanel.class, immediate = true) public class Catalog extends JPanel implements DockingPanel, TitleActionBar, PopupTarget, DatabaseView { //The UID must be incremented when the serialization is not compatible with the new version of this class private static final long serialVersionUID = 1L; private static final I18n I18N = I18nFactory.getI18n(Catalog.class); private static final Logger LOGGER = LoggerFactory.getLogger(Catalog.class); private DockingPanelParameters dockingParameters = new DockingPanelParameters(); /* * !< GeoCatalog docked panel properties */ private JList<ContainerItemProperties> sourceList; private SourceListModel sourceListContent; //The factory shown when the user click on new factory button private static final String DEFAULT_FILTER_FACTORY = "name_contains"; private FilterFactoryManager<IFilter,DefaultActiveFilter> filterFactoryManager; private ActionCommands dockingActions = new ActionCommands(); private ActionCommands popupActions = new ActionCommands(); private DriverFunctionContainer driverFunctionContainer; private DataManager dataManager; private ExecutorService executorService = null; /** * For the Unit test purpose * * @return The source list instance */ public JList<ContainerItemProperties> getSourceList() { return sourceList; } @Reference(cardinality = ReferenceCardinality.OPTIONAL, policy = ReferencePolicy.DYNAMIC) public void setExecutorService(ExecutorService executorService) { this.executorService = executorService; } public void unsetExecutorService(ExecutorService executorService) { this.executorService = null; } @Reference public void setDriverFunctionContainer(DriverFunctionContainer driverFunctionContainer) { this.driverFunctionContainer = driverFunctionContainer; } public void unsetDriverFunctionContainer(DriverFunctionContainer driverFunctionContainer) { this.driverFunctionContainer = null; } /** * Default constructor */ public Catalog() { super(new BorderLayout()); } @Reference public void setDataManager(DataManager dataManager) { this.dataManager = dataManager; } public void unsetDataManager(DataManager dataManager) { this.dataManager = null; } /** * Initialise panel */ @Activate public void init() { dockingParameters.setName("geocatalog"); dockingParameters.setTitle(I18N.tr("GeoCatalog")); dockingParameters.setTitleIcon(GeocatalogIcon.getIcon("geocatalog")); dockingParameters.setCloseable(true); //Add the Source List in a Scroll Pane, //then add the scroll pane in this panel add(new JScrollPane(makeSourceList()), BorderLayout.CENTER); //Init the filter factory manager filterFactoryManager = new FilterFactoryManager<>(); //Set the factory that must be shown when the user click on add filter button filterFactoryManager.setDefaultFilterFactory(DEFAULT_FILTER_FACTORY); //Set listener on filter change event, this event will update the filters FilterFactoryManager.FilterChangeListener refreshFilterListener = EventHandler.create(FilterFactoryManager.FilterChangeListener.class, sourceListContent, //target of event "setFilters", //target method "source.getFilters" //target method argument ); filterFactoryManager.getEventFilterChange().addListener(sourceListContent,refreshFilterListener); filterFactoryManager.getEventFilterFactoryChange().addListener(sourceListContent,refreshFilterListener); //Add the filter list at the top of the geocatalog add(filterFactoryManager.makeFilterPanel(false), BorderLayout.NORTH); //Create an action to add a new filter dockingActions.addAction(new DefaultAction(GeoCatalogMenu.M_ADD_FILTER, I18N.tr("Add filter"), I18N.tr("Add a new data source filter"), GeocatalogIcon.getIcon("add_filter"), EventHandler.create(ActionListener.class, filterFactoryManager, "onAddFilter"), null)); // 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)); //Add the geocatalog specific filters registerFilterFactories(); // Register built-ins popup actions createPopupActions(); popupActions.setAccelerators(sourceList); } @Reference(cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC) public void addGeoCatalogMenu(GeoCatalogMenu geoCatalogMenu) { dockingActions.addActionFactory(geoCatalogMenu, this); } public void removeGeoCatalogMenu(GeoCatalogMenu geoCatalogMenu) { dockingActions.removeActionFactory(geoCatalogMenu); } @Reference(cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC) public void addPopupMenu(PopupMenu popupMenu) { popupActions.addActionFactory(popupMenu, this); } public void removePopupMenu(PopupMenu popupMenu) { popupActions.removeActionFactory(popupMenu); } /** * Get the actions related to frame title. * @return actions related to frame title. */ public ActionCommands getActionsDocking() { return dockingActions; } /** * Get the actions of PopupMenu related to Source list items. * @return actions of PopupMenu related to Source list items. */ public ActionCommands getActionsPopup() { return popupActions; } /** * DataSource URI drop. Currently used on file drop by the {@link SourceListTransferHandler}. * * @param uriDrop Uniform Resource Identifier */ public void onDropURI(List<URI> uriDrop) { for (URI uri : uriDrop) { try { dataManager.registerDataSource(uri); refreshSourceList(); } catch (SQLException ex) { LOGGER.error("Cannot load dropped data source", ex); } } } /** * For JUnit purpose, return the filter factory manager * * @return Instance of filterFactoryManager */ public FilterFactoryManager<IFilter,DefaultActiveFilter> getFilterFactoryManager() { return filterFactoryManager; } /** * Add the built-ins filter factory */ private void registerFilterFactories() { filterFactoryManager.registerFilterFactory(new NameContains()); filterFactoryManager.registerFilterFactory(new NameNotContains()); filterFactoryManager.registerFilterFactory(new SourceTypeIs()); } /** * The user click on the source list control * * @param e The mouse event fired by the LI */ public void onMouseActionOnSourceList(MouseEvent e) { //Manage selection of items before popping up the menu if (e.isPopupTrigger()) { //Right mouse button under linux and windows int itemUnderMouse = -1; //Item under the position of the mouse event //Find the Item under the position of the mouse cursor for (int i = 0; i < sourceListContent.getSize(); i++) { //If the coordinate of the cursor cover the cell bounding box if (sourceList.getCellBounds(i, i).contains(e.getPoint())) { itemUnderMouse = i; break; } } //Retrieve all selected items index int[] selectedItems = sourceList.getSelectedIndices(); //If there are a list item under the mouse if ((selectedItems != null) && (itemUnderMouse != -1)) { //If the item under the mouse was not previously selected if (!CollectionUtils.contains(selectedItems, itemUnderMouse)) { //Control must be pushed to add the list item to the selection if (e.isControlDown()) { sourceList.addSelectionInterval(itemUnderMouse, itemUnderMouse); } else { //Unselect the other items and select only the item under the mouse sourceList.setSelectionInterval(itemUnderMouse, itemUnderMouse); } } } else if (itemUnderMouse == -1) { //Unselect all items sourceList.clearSelection(); } //Selection are ready, now create the popup menu JPopupMenu popup = new JPopupMenu(); popupActions.copyEnabledActions(popup); if (popup.getComponentCount()>0) { popup.show(e.getComponent(), e.getX(), e.getY()); } } } /** * The user click on the menu item called "Import/File" The user wants to * open a file using the geocatalog. It will open a panel dedicated to * the selection of the wanted files. This panel will then return the * selected files. */ public void onMenuImportFile() { driverFunctionContainer.importFile(this, DriverFunction.IMPORT_DRIVER_TYPE.COPY); } /** * The user click on the menu item called "Add/File" The user wants to * open a file using the geocatalog. It will open a panel dedicated to * the selection of the wanted files. This panel will then return the * selected files. */ public void onMenuAddLinkedFile() { driverFunctionContainer.importFile(this, DriverFunction.IMPORT_DRIVER_TYPE.LINK); } /** * Connect to a database and add one or more tables in the geocatalog. */ public void onMenuAddFromDataBase() { /* SourceManager sm = getDataManager().getSourceManager(); TableImportPanel tableImportPanel = new TableImportPanel(sm); tableImportPanel.setVisible(true); */ } /** * The user can remove added source from the geocatalog */ public void onMenuRemoveSource() { List<String> sources = new ArrayList<>(); List<ContainerItemProperties> selectedValues = getSourceList().getSelectedValuesList(); for (ContainerItemProperties source : selectedValues) { sources.add(source.getKey()); } if(!sources.isEmpty()) { DropTable dropTable = DropTable.onMenuRemoveSource(dataManager.getDataSource(), sources, this, this); if(dropTable != null) { executeJob(dropTable); } } } private void executeJob(SwingWorker worker) { if(executorService == null) { worker.execute(); } else { executorService.execute(worker); } } /** * The user can export a source in a file. */ public void onMenuSaveInfile() { List<String> sources = Arrays.asList(getSelectedSources()); ExportInFileOperation exportJob = ExportInFileOperation.saveInfile(dataManager.getDataSource(), sources, driverFunctionContainer); if(exportJob != null) { executeJob(exportJob); } } /** * The user can save a source in a database */ public void onMenuSaveInDB() { /* DataManager dm = Services.getService(DataManager.class); SourceManager sm = dm.getSourceManager(); String[] res = getSelectedSources(); TableExportPanel tableExportPanel = new TableExportPanel(res, sm); tableExportPanel.setVisible(true); */ } /** * The user can load several files from a folder */ public void onMenuAddFilesFromFolder() { driverFunctionContainer.addFilesFromFolder(this, DriverFunction.IMPORT_DRIVER_TYPE.LINK); } /** * The user can copy several files from a folder */ public void onMenuImportFilesFromFolder() { driverFunctionContainer.addFilesFromFolder(this, DriverFunction.IMPORT_DRIVER_TYPE.COPY); } private void createPopupActions() { 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) { popupActions.addAction(new DefaultAction(PopupMenu.M_ADD,I18N.tr("Add")).setMenuGroup(true).setLogicalGroup(PopupMenu.GROUP_ADD)); //Popup:Add:File popupActions.addAction(new DefaultAction(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) ).addStroke(KeyStroke.getKeyStroke(KeyEvent.VK_O, InputEvent.CTRL_DOWN_MASK)).setParent(PopupMenu.M_ADD)); //Popup:Add:Folder popupActions.addAction(new DefaultAction(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")).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 popupActions.addAction(new DefaultAction(PopupMenu.M_IMPORT,I18N.tr("Import")).setMenuGroup(true).setLogicalGroup(PopupMenu.GROUP_IMPORT)); //Popup:Import:File popupActions.addAction(new DefaultAction(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")).setParent(PopupMenu.M_IMPORT)); popupActions.addAction(new DefaultAction(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")).setParent(PopupMenu.M_IMPORT)); //Popup:Save popupActions.addAction(new ActionOnSelection( PopupMenu.M_SAVE,I18N.tr("Save"), true, getListSelectionModel() ).setLogicalGroup(PopupMenu.GROUP_ADD)); //Popup:Save:File popupActions.addAction(new ActionOnSelection(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"),getListSelectionModel()). setKeyStroke(KeyStroke.getKeyStroke("ctrl S")).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 popupActions.addAction(new ActionOnSelection(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"),getListSelectionModel()) .setKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0)).setLogicalGroup(PopupMenu.GROUP_CLOSE)); //Popup:Refresh popupActions.addAction(new DefaultAction(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")).setLogicalGroup(PopupMenu.GROUP_OPEN)); } @Override public void onDatabaseUpdate(String entity, String... identifier) { refreshSourceList(); } @Override public void refreshSourceList() { sourceListContent.onDataManagerChange(); } /** * Create the Source List ui component */ private JList makeSourceList() { sourceList = new JList<>(); //Set the list content renderer sourceList.setCellRenderer(new DataSourceListCellRenderer(sourceList)); //Add mouse listener for popup menu sourceList.addMouseListener(EventHandler.create(MouseListener.class, this, "onMouseActionOnSourceList", "")); //This method ask the event data as argument //Create the list content manager sourceListContent = new SourceListModel(dataManager); //Replace the default model by the GeoCatalog model sourceList.setModel(sourceListContent); SourceListTransferHandler transferHandler = new SourceListTransferHandler(dataManager); //Call the method this.onDropURI when the user drop uri(like files) on the list control transferHandler.getDropListenerHandler().addListener(this, EventHandler.create(SourceListTransferHandler.DropUriListener.class, this, "onDropURI", "uriList")); sourceList.setTransferHandler(transferHandler); sourceList.setDragEnabled(true); //Attach the content to the DataSource instance sourceListContent.setListeners(); return sourceList; } /** * Free listeners, Catalog must not be reachable to let the Garbage * Collector free this instance */ public void dispose() { //Remove listeners linked with the source list content filterFactoryManager.getEventFilterChange().clearListeners(); filterFactoryManager.getEventFilterFactoryChange().clearListeners(); sourceListContent.dispose(); } @Override public String[] getSelectedSources() { List<ContainerItemProperties> selectedValues = getSourceList().getSelectedValuesList(); String[] sources = new String[selectedValues.size()]; int i=0; for (ContainerItemProperties source : selectedValues) { sources[i++] = source.getKey(); } return sources; } /** * Give information on the behaviour of this panel related to the * current docking system * * @return The panel parameter instance */ @Override public DockingPanelParameters getDockingParameters() { return dockingParameters; } @Override public JComponent getComponent() { return this; } @Override public ListSelectionModel getListSelectionModel() { return sourceList.getSelectionModel(); } }