org.orbisgis.mapeditorapi.MapElement.java Source code

Java tutorial

Introduction

Here is the source code for org.orbisgis.mapeditorapi.MapElement.java

Source

/**
 * 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-2016 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.mapeditorapi;

import java.beans.EventHandler;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import javax.swing.JOptionPane;
import javax.swing.undo.UndoManager;
import javax.swing.undo.UndoableEdit;

import org.apache.commons.io.FilenameUtils;
import org.orbisgis.corejdbc.DataManager;
import org.orbisgis.corejdbc.TableEditEvent;
import org.orbisgis.corejdbc.TableEditListener;
import org.orbisgis.coremap.layerModel.ILayer;
import org.orbisgis.coremap.layerModel.LayerCollectionEvent;
import org.orbisgis.coremap.layerModel.LayerException;
import org.orbisgis.coremap.layerModel.LayerListenerAdapter;
import org.orbisgis.coremap.layerModel.LayerListenerEvent;
import org.orbisgis.coremap.layerModel.MapContext;
import org.orbisgis.coremap.layerModel.OwsMapContext;
import org.orbisgis.coremap.layerModel.SelectionEvent;
import org.orbisgis.commons.progress.ProgressMonitor;
import org.orbisgis.editorjdbc.EditorUndoableEdit;
import org.orbisgis.sif.UIFactory;
import org.h2gis.utilities.JDBCUtilities;
import org.orbisgis.sif.edition.AbstractEditableElement;
import org.orbisgis.sif.edition.EditableElement;
import org.orbisgis.sif.edition.EditorManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xnap.commons.i18n.I18n;
import org.xnap.commons.i18n.I18nFactory;

/**
 * MapElement is an editable document that contains a Map Context. It is shared by Toc, MapEditor and some other components.
 * The source code, functionality is mainly provided by GeocognitionMapContext
 */
public final class MapElement extends AbstractEditableElement implements TableEditListener {
    public static final String EDITABLE_TYPE = "MapContext";
    private static final Logger LOGGER = LoggerFactory.getLogger("gui." + MapElement.class);
    private static final I18n I18N = I18nFactory.getI18n(MapElement.class);

    // The following events does not change the MapElement modification state.
    private Set<String> ignoredModificationEvents = new HashSet<String>(Arrays.asList(new String[] {
            MapContext.PROP_ACTIVELAYER, MapContext.PROP_SELECTEDLAYERS, MapContext.PROP_SELECTEDSTYLES }));

    private MapContext mapContext;
    private String mapId;
    private PropertyChangeListener mapContextPropertyUpdateListener = EventHandler
            .create(PropertyChangeListener.class, this, "onMapUpdate", "");
    private LayerUpdateListener layerUpdateListener = new LayerUpdateListener();
    private File mapContextFile;
    // All edits on all layers are accumulated here
    private UndoManager mapUndoManager = new UndoManager();

    public MapElement(MapContext mapContext, File mapContextFile) {
        if (mapContext == null) {
            throw new IllegalArgumentException("MapContext argument cannot be null");
        }
        this.mapContext = mapContext;
        this.mapContextFile = mapContextFile;
        mapId = String.valueOf(mapContext.getIdTime());
    }

    /**
     * Constructor that read the provided map context file
     *
     * @param mapContextFile Xml file
     * @param manager        Where to register MapContext URI
     */
    public MapElement(File mapContextFile, DataManager manager) {
        mapContext = new OwsMapContext(manager);
        try {
            mapContext.read(new FileInputStream(mapContextFile));
            mapContext.setLocation(mapContextFile.toURI());
        } catch (FileNotFoundException | IllegalArgumentException ex) {
            LOGGER.error(I18N.tr("The saved map context cannot be read, starting with an empty map context."), ex);
        }
        this.mapContextFile = mapContextFile;
        mapId = String.valueOf(mapContext.getIdTime());
    }

    /**
     * @return UndoManager of layer's table
     */
    public UndoManager getMapUndoManager() {
        return mapUndoManager;
    }

    /**
     * Use the EditorManager service and search for the first available editable map.
     *
     * @return The map context or null if it is not found.
     */
    public static MapElement fetchFirstMapElement(EditorManager editorManager) {
        for (EditableElement editable : editorManager.getEditableElements()) {
            if (editable instanceof MapElement) {
                return (MapElement) editable;
            }
        }
        return null;
    }

    public File getMapContextFile() {
        return mapContextFile;
    }

    /**
     * Set this element as modified
     */
    public void onMapUpdate(PropertyChangeEvent evt) {
        if (!ignoredModificationEvents.contains(evt.getPropertyName())) {
            setModified(true);
        }
    }

    /**
     * Update the modified state
     *
     * @param modified
     */
    public void setModified(Boolean modified) {
        this.modified = modified;
    }

    private boolean hasTemporaryTables() {
        try {
            try (Connection connection = mapContext.getDataManager().getDataSource().getConnection()) {
                for (ILayer layer : mapContext.getLayers()) {
                    String table = layer.getTableReference();
                    if (table != null && !table.isEmpty()) {
                        if (JDBCUtilities.isTemporaryTable(connection, table)) {
                            return true;
                        }
                    }
                }
            }
        } catch (SQLException ex) {
            LOGGER.error(I18N.tr("Error while checking temporary table"));
        }
        return false;
    }

    @Override
    public void save() throws UnsupportedOperationException {
        // If a layer hold a not well known source then alert the user
        boolean doSave = true;
        if (hasTemporaryTables()) {
            int response = JOptionPane.showConfirmDialog(UIFactory.getMainFrame(),
                    I18N.tr("Some layers use temporary"
                            + " table, are you sure to save this map and loose layers with temporary tables ?"),
                    I18N.tr("Temporary layers data source"), JOptionPane.YES_NO_OPTION,
                    JOptionPane.WARNING_MESSAGE);
            if (response == JOptionPane.NO_OPTION) {
                doSave = false;
            }
        }
        if (doSave) {
            // Save MapContext
            try {
                //Create folders if needed
                File parentFolder = mapContextFile.getParentFile();
                if (!parentFolder.exists()) {
                    parentFolder.mkdirs();
                }
                mapContext.write(new FileOutputStream(mapContextFile));
            } catch (FileNotFoundException ex) {
                throw new UnsupportedOperationException(ex);
            }
            if (doSave) {
                setModified(false);
            }
        }
    }

    @Override
    public void open(ProgressMonitor progressMonitor) throws UnsupportedOperationException {
        setModified(false);
        try {
            mapContext.open(progressMonitor);
            mapContext.addPropertyChangeListener(mapContextPropertyUpdateListener);
            setListeners(mapContext.getLayerModel());
        } catch (LayerException ex) {
            LOGGER.error(I18N.tr("Unable to load the map context"), ex);
        } catch (IllegalStateException ex) {
            LOGGER.error(I18N.tr("Unable to load the map context"), ex);
        }
    }

    @Override
    public void close(ProgressMonitor progressMonitor) throws UnsupportedOperationException {
        mapContext.close(progressMonitor);
        mapContext.removePropertyChangeListener(mapContextPropertyUpdateListener);
        removeListeners(mapContext.getLayerModel());
    }

    private void setListeners(ILayer layer) {
        if (layer == null) {
            return;
        }
        layer.addLayerListener(layerUpdateListener);
        if (layer.getTableReference() != null) {
            mapContext.getDataManager().addTableEditListener(layer.getTableReference(), this, false);
        }
        ILayer[] layers = layer.getLayersRecursively();
        if (layers != null) {
            for (ILayer subLayer : layers) {
                setListeners(subLayer);
            }
        }
    }

    @Override
    public void tableChange(TableEditEvent event) {
        if (event.getUndoableEdit() != null) {
            mapUndoManager.addEdit(new EditorUndoableEdit(event.getUndoableEdit()));
        }
    }

    private void removeListeners(ILayer layer) {
        if (layer == null) {
            return;
        }
        layer.removeLayerListener(layerUpdateListener);
        if (layer.getTableReference() != null) {
            mapContext.getDataManager().removeTableEditListener(layer.getTableReference(), this);
        }
        ILayer[] layers = layer.getLayersRecursively();
        if (layers != null) {
            for (ILayer subLayer : layers) {
                removeListeners(subLayer);
            }
        }
    }

    @Override
    public String getId() {
        return mapId;
    }

    /**
     * Return the edited map context
     *
     * @return
     */
    public MapContext getMapContext() {
        return mapContext;
    }

    @Override
    public Object getObject() throws UnsupportedOperationException {
        return mapContext;
    }

    @Override
    public String getTypeId() {
        return EDITABLE_TYPE;
    }

    @Override
    public String toString() {
        return I18N.tr("MapContext - {0}", FilenameUtils.getBaseName(mapContextFile.getName()));
    }

    /**
     * Set the editable map as modified when the layer model change
     */
    private class LayerUpdateListener extends LayerListenerAdapter {

        @Override
        public void nameChanged(LayerListenerEvent e) {
            setModified(true);
        }

        @Override
        public void visibilityChanged(LayerListenerEvent e) {
            setModified(true);
        }

        @Override
        public void styleChanged(LayerListenerEvent e) {
            setModified(true);
        }

        @Override
        public void layerAdded(LayerCollectionEvent e) {
            for (final ILayer layer : e.getAffected()) {
                setListeners(layer);
            }
            setModified(true);
        }

        @Override
        public void layerRemoved(LayerCollectionEvent e) {
            for (final ILayer layer : e.getAffected()) {
                removeListeners(layer);
            }
            setModified(true);
        }

        @Override
        public void layerMoved(LayerCollectionEvent e) {
            setModified(true);
        }

        @Override
        public void selectionChanged(SelectionEvent e) {
            // row selection is not serialised in MapContext
        }

    }
}