net.refractions.udig.tools.edit.EditBlackboardUtil.java Source code

Java tutorial

Introduction

Here is the source code for net.refractions.udig.tools.edit.EditBlackboardUtil.java

Source

/* uDig - User Friendly Desktop Internet GIS client
 * http://udig.refractions.net
 * (C) 2004, Refractions Research Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation;
 * version 2.1 of the License.
 *
 * This library 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
 * Lesser General Public License for more details.
 */
package net.refractions.udig.tools.edit;

import java.awt.geom.AffineTransform;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import net.refractions.udig.project.ILayer;
import net.refractions.udig.project.ILayerListener;
import net.refractions.udig.project.IMap;
import net.refractions.udig.project.Interaction;
import net.refractions.udig.project.LayerEvent;
import net.refractions.udig.project.command.UndoableComposite;
import net.refractions.udig.project.command.UndoableMapCommand;
import net.refractions.udig.project.internal.EditManager;
import net.refractions.udig.project.internal.Layer;
import net.refractions.udig.project.internal.impl.EditManagerImpl;
import net.refractions.udig.project.internal.render.RenderPackage;
import net.refractions.udig.project.internal.render.ViewportModel;
import net.refractions.udig.project.render.displayAdapter.IMapDisplayListener;
import net.refractions.udig.project.render.displayAdapter.MapDisplayEvent;
import net.refractions.udig.project.ui.ApplicationGIS;
import net.refractions.udig.project.ui.tool.IToolContext;
import net.refractions.udig.project.ui.tool.Tool;
import net.refractions.udig.tool.edit.internal.Messages;
import net.refractions.udig.tools.edit.support.EditBlackboard;
import net.refractions.udig.tools.edit.support.EditGeom;
import net.refractions.udig.tools.edit.support.PrimitiveShape;
import net.refractions.udig.ui.PlatformGIS;

import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.Command;
import org.eclipse.core.commands.CommandEvent;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.commands.ICommandListener;
import org.eclipse.core.commands.IHandler;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.impl.AdapterImpl;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.commands.ICommandService;
import org.geotools.data.FeatureEvent;
import org.geotools.data.FeatureSource;
import org.geotools.data.FeatureStore;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.factory.GeoTools;
import org.geotools.feature.FeatureCollection;
import org.geotools.feature.FeatureIterator;
import org.geotools.referencing.CRS;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.filter.Filter;
import org.opengis.filter.FilterFactory;
import org.opengis.filter.Id;
import org.opengis.filter.identity.Identifier;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.operation.MathTransform;

import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.geom.Geometry;

/**
 * To delete soon EditManager will have an editblackboard.
 * 
 * @author jones
 * @since 1.1.0
 */
public class EditBlackboardUtil {
    private static final MathTransform IDENTITY;
    static {
        MathTransform tmp = null;
        try {
            tmp = CRS.findMathTransform(DefaultGeographicCRS.WGS84, DefaultGeographicCRS.WGS84);
        } catch (FactoryException e) {
            // can't happen
        }

        IDENTITY = tmp;
    }

    private volatile static ViewportModelListener listener;

    public static final EditBlackboard EMPTY_BLACKBOARD = new EditBlackboard(0, 0,
            AffineTransform.getTranslateInstance(0, 0), IDENTITY);

    public static final String EDIT_BLACKBOARD_KEY = "EDIT_BLACKBOARD_KEY_839834"; //$NON-NLS-1$

    private static Lock blackboardLock = new ReentrantLock();

    public static EditBlackboard getEditBlackboard(IToolContext context, ILayer layer2) {
        if (layer2 == null || !ApplicationGIS.getOpenMaps().contains(layer2.getMap()))
            return EMPTY_BLACKBOARD;
        ILayer layer = layer2;

        EditBlackboard editBlackBoard;
        blackboardLock.lock();
        try {
            EditManager editManager = ((EditManager) context.getEditManager());
            if (editManager.getEditLayer() != null && editManager.isEditLayerLocked()) {
                layer = context.getEditManager().getEditLayer();
            }

            editBlackBoard = getEditBlackBoardFromLayer(layer);

            if (editBlackBoard == null) {

                MathTransform layerToMapTransform;
                try {
                    layerToMapTransform = layer.layerToMapTransform();
                } catch (IOException e) {
                    EditPlugin.log("", e); //$NON-NLS-1$
                    layerToMapTransform = IDENTITY;
                }
                editBlackBoard = new EditBlackboard(context.getMapDisplay().getWidth(),
                        context.getMapDisplay().getHeight(), context.worldToScreenTransform(), layerToMapTransform);

                final EditBlackboard bb = editBlackBoard;

                context.getViewportPane().addPaneListener(new IMapDisplayListener() {

                    public void sizeChanged(MapDisplayEvent event) {
                        if (event.getOldSize() != null && event.getOldSize().width != event.getSize().width)
                            bb.setWidth(event.getSize().width);
                        if (event.getOldSize() != null && event.getOldSize().height != event.getSize().height)
                            bb.setHeight(event.getSize().height);
                    }

                });

                layer.getBlackboard().put(EDIT_BLACKBOARD_KEY, editBlackBoard);

            }
            enableViewportListener((ViewportModel) context.getViewportModel());

            //Vitalus: moved to EditToolHandler.enableListeners().
            //            enableClearBlackboardCommand(context);

            // disabled until I fix the events
            //            enableLayerChangeEventListener(layer, editBlackBoard);
        } finally {
            blackboardLock.unlock();
        }

        if (dirtyAreas.get(layer) != null) {
            openDataChangedDialog(layer, dirtyAreas.get(layer));
        }
        editBlackBoard.setToScreenTransform(context.worldToScreenTransform());
        return editBlackBoard;

    }

    private static Map<ILayer, ILayerListener> layerListenerMap = new HashMap<ILayer, ILayerListener>();
    private static Map<ILayer, Envelope> dirtyAreas = new HashMap<ILayer, Envelope>();

    /**
     * Adds a listener so that the editblackboards can be notified when the data in the feature
     * store are modified.
     */
    private static synchronized void enableLayerChangeEventListener(final ILayer layer,
            final EditBlackboard editBlackboard) {
        ILayerListener listener = layerListenerMap.get(layer);
        if (listener == null) {
            listener = new ILayerListener() {
                public void refresh(LayerEvent event) {
                    if (event.getSource() != layer) {
                        layer.removeListener(this);
                        return;
                    }
                    if (event.getType() != LayerEvent.EventType.EDIT_EVENT)
                        return;

                    EditManager editManager = (EditManager) layer.getMap().getEditManager();
                    ILayer editlayer = editManager.getSelectedLayer();
                    if (editManager.getEditLayer() != null && editManager.isEditLayerLocked()) {
                        editlayer = editManager.getEditLayer();
                    }
                    EditState currentEditState = (EditState) layer.getMap().getBlackboard()
                            .get(EditToolHandler.EDITSTATE);
                    if (editlayer == layer && (currentEditState == EditState.COMMITTING
                            || EditManagerListener.committing == editManager))
                        return;

                    FeatureEvent editEvent = (FeatureEvent) event.getNewValue();

                    if (editEvent == null)
                        return;

                    Envelope dirtyArea = dirtyAreas.get(editlayer);
                    if (dirtyArea == null) {
                        dirtyArea = editEvent.getBounds();
                    } else {
                        dirtyArea.expandToInclude(editEvent.getBounds());
                    }

                    dirtyAreas.put(editlayer, dirtyArea);

                    if (editlayer == layer && layer.getMap() == ApplicationGIS.getActiveMap()) {
                        openDataChangedDialog(editlayer, dirtyArea);
                    }
                }
            };
            layerListenerMap.put(layer, listener);
        }

        layer.addListener(listener);

    }

    static volatile Dialog dialog;

    public static void openDataChangedDialog(final ILayer layer, final Envelope dirtyArea) {
        Display d = Display.getCurrent();
        if (d == null)
            d = Display.getDefault();

        Condition condition = blackboardLock.newCondition();
        try {
            blackboardLock.lock();
            if (dialog != null) {
                // we're in the display thread, which means that the viewport is repainting since the dialog has any other input blocked.
                // so just return
                if (Display.getCurrent() != null)
                    return;
                // the issue is being resolved we should wait for it to be resolved.
                while (dialog != null) {
                    try {
                        condition.await(500, TimeUnit.MILLISECONDS);
                    } catch (InterruptedException e) {
                        return;
                    }
                }
                return;
            }
        } finally {
            blackboardLock.unlock();
        }

        PlatformGIS.syncInDisplayThread(d, new Runnable() {
            public void run() {
                try {
                    blackboardLock.lock();
                    if (dirtyAreas.get(layer) == null)
                        return;

                    dialog = new Dialog(Display.getCurrent().getActiveShell()) {
                        private final int UPDATE = 1;
                        private final int CLEAR = 2;
                        private final int IGNORE = 3;

                        public Control createDialogArea(Composite parent) {
                            Composite comp = new Composite(parent, SWT.NONE);
                            comp.setLayout(new GridLayout(2, false));
                            Display display = Display.getCurrent();
                            Label label = new Label(comp, SWT.NONE);
                            label.setBackground(display.getSystemColor(SWT.COLOR_WIDGET_BACKGROUND));
                            label.setForeground(display.getSystemColor(SWT.COLOR_WIDGET_FOREGROUND));
                            label.setImage(Dialog.getImage(Dialog.DLG_IMG_WARNING));

                            label.setLayoutData(new GridData(SWT.FILL, SWT.NONE, true, false));
                            Text text = new Text(comp, SWT.WRAP | SWT.READ_ONLY);
                            text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
                            text.setBackground(display.getSystemColor(SWT.COLOR_WIDGET_BACKGROUND));
                            text.setForeground(display.getSystemColor(SWT.COLOR_WIDGET_FOREGROUND));
                            text.setText(Messages.EditBlackboardUtil_data_changed);
                            return comp;
                        }

                        public void createButtonsForButtonBar(Composite composite) {
                            createButton(composite, UPDATE, Messages.EditBlackboardUtil_update, false);
                            createButton(composite, CLEAR, Messages.EditBlackboardUtil_clear, true);
                            createButton(composite, IGNORE, Messages.EditBlackboardUtil_ignore, false);
                        }

                        public void buttonPressed(int button) {
                            switch (button) {
                            case UPDATE: {
                                boolean ok = MessageDialog.openConfirm(getParentShell(),
                                        Messages.EditBlackboardUtil_Update_Selection,
                                        Messages.EditBlackboardUtil_update_selection_confirmation);
                                if (ok) {
                                    try {
                                        ProgressMonitorDialog d = new ProgressMonitorDialog(this.getParentShell());

                                        blackboardLock.unlock();
                                        d.run(false, false, new IRunnableWithProgress() {
                                            public void run(IProgressMonitor monitor) {
                                                updateFeatures(layer, monitor, dirtyArea);
                                            }
                                        });
                                        okPressed();
                                    } catch (Exception e) {
                                        EditPlugin.log("", e); //$NON-NLS-1$
                                    }
                                }
                                break;
                            }
                            case CLEAR: {
                                boolean ok = MessageDialog.openConfirm(getParentShell(),
                                        Messages.EditBlackboardUtil_clear_selection,
                                        Messages.EditBlackboardUtil_changes_will_be_lost);
                                if (ok) {
                                    blackboardLock.unlock();
                                    (getEditBlackBoardFromLayer(layer)).clear();
                                    layer.getMap().getBlackboard().put(EditToolHandler.CURRENT_SHAPE, null);
                                    ((EditManager) layer.getMap().getEditManager()).setEditFeature(null, null);
                                    okPressed();
                                }
                                break;
                            }
                            case IGNORE:
                                boolean ok = MessageDialog.openConfirm(getParentShell(),
                                        Messages.EditBlackboardUtil_ignore_change,
                                        Messages.EditBlackboardUtil_changes_will_be_overwritten);
                                if (ok) {
                                    okPressed();
                                }
                                break;

                            default:
                                cancelPressed();
                                break;
                            }
                        }

                        public void okPressed() {
                            dirtyAreas.remove(layer);
                            super.okPressed();
                        }
                    };
                    dialog.setBlockOnOpen(true);
                    dialog.open();
                } finally {
                    blackboardLock.unlock();
                    dialog = null;
                }

            }
        });
    }

    /**
     * Updates the features in the "dirty Area" so that the {@link EditGeom}s reflect the actual state of the stored features.  
     * Any changes to the {@link EditGeom} will be lost. 
     *
     * @param layer that needs to be updated.
     * @param monitor progress monitor
     * @param dirtyArea area that needs to be updated.
     */
    public static void updateFeatures(ILayer layer, IProgressMonitor monitor, Envelope dirtyArea) {
        EditBlackboard bb = getEditBlackBoardFromLayer(layer);
        List<EditGeom> geoms = bb.getGeoms();
        monitor.beginTask(Messages.EditBlackboardUtil_updating_selected_features, geoms.size());

        PrimitiveShape shape = (PrimitiveShape) layer.getMap().getBlackboard().get(EditToolHandler.CURRENT_SHAPE);
        EditManager editManager = (EditManager) layer.getMap().getEditManager();

        FilterFactory factory = CommonFactoryFinder.getFilterFactory(GeoTools.getDefaultHints());
        Set<Identifier> ids = new HashSet<Identifier>();
        for (EditGeom geom : geoms) {
            ids.add(factory.featureId(geom.getFeatureIDRef().get()));
        }
        Id fidFilter = factory.id(ids);
        Filter filter = factory.and(fidFilter, layer.createBBoxFilter(dirtyArea, new NullProgressMonitor()));

        try {
            FeatureSource<SimpleFeatureType, SimpleFeature> fs = layer.getResource(FeatureSource.class, monitor);
            FeatureCollection<SimpleFeatureType, SimpleFeature> results = fs.getFeatures(filter);
            FeatureIterator<SimpleFeature> reader = results.features();
            try {
                int read = 0;
                boolean selectedFound = false;
                List<EditGeom> toRemove = new ArrayList<EditGeom>();
                while (reader.hasNext()) {
                    int count = geoms.size() - read;
                    monitor.setTaskName(MessageFormat.format(Messages.EditBlackboardUtil_count_remaining,
                            new Object[] { count }));
                    read++;
                    SimpleFeature feature = reader.next();

                    for (EditGeom geom : geoms) {
                        if (feature.getID().equals(geom.getFeatureIDRef().get())) {
                            toRemove.add(geom);
                        }
                    }

                    Map<Geometry, EditGeom> mapping = bb.addGeometry((Geometry) feature.getDefaultGeometry(),
                            feature.getID());
                    if (feature.getID().equals(shape.getEditGeom().getFeatureIDRef().get())) {
                        editManager.setEditFeature(feature, (Layer) layer);
                        layer.getMap().getBlackboard().put(EditToolHandler.CURRENT_SHAPE,
                                mapping.values().iterator().next().getShell());
                        selectedFound = true;
                    }
                    monitor.worked(1);
                }
                if (!selectedFound) {
                    layer.getMap().getBlackboard().put(EditToolHandler.CURRENT_SHAPE, null);
                    editManager.setEditFeature(null, null);
                }
                bb.removeGeometries(toRemove);
            } finally {
                reader.close();
                monitor.done();
            }
        } catch (IOException e) {
            EditPlugin.log("", e); //$NON-NLS-1$
        }
    }

    /**
     * Returns the Editblackboard from the layer if it is on the layer or null if it hasn't been created yet.
     * {@link #getEditBlackboard(IToolContext, ILayer)} will create and initialize the blackbaord
     *
     * @param layer
     * @return
     */
    private static EditBlackboard getEditBlackBoardFromLayer(ILayer layer) {
        blackboardLock.lock();
        try {
            return (EditBlackboard) layer.getBlackboard().get(EDIT_BLACKBOARD_KEY);
        } finally {
            blackboardLock.unlock();
        }
    }

    /**
     * Command handler for the command "net.refractions.udig.tool.edit.clearAction"
     *  to clear EditBlackboard.
     */
    static IHandler clearEditBlackboardHandler;

    /**
     * Listener for "net.refractions.udig.tool.edit.clearAction" command.
     */
    static ICommandListener clearEditBlackboardCommandListener;

    /**
     * Sets the command handler for the "ESC" button. The handler clears current edit blackboard.
     *  <p>
     *  Called from <code>EditToolHandler.enableListeners()</code>.
     *  
     * @param context
     */
    static synchronized void enableClearBlackboardCommand(final IToolContext context) {
        if (clearEditBlackboardHandler == null) {
            clearEditBlackboardHandler = new AbstractHandler() {

                public Object execute(ExecutionEvent event) throws ExecutionException {
                    Tool tool = ApplicationGIS.getToolManager().getActiveTool();

                    if (tool instanceof AbstractEditTool) {
                        EditToolHandler editToolHandler = ((AbstractEditTool) tool).getHandler();
                        List<Behaviour> behaviours = editToolHandler.getCancelBehaviours();
                        UndoableComposite compositeCommand = new UndoableComposite();
                        for (Behaviour behaviour : behaviours) {
                            if (behaviour.isValid(editToolHandler)) {
                                UndoableMapCommand command = behaviour.getCommand(editToolHandler);
                                if (command != null)
                                    compositeCommand.getCommands().add(command);
                            }
                        }
                        if (!compositeCommand.getCommands().isEmpty())
                            editToolHandler.getContext().sendASyncCommand(compositeCommand);
                    }

                    return null;
                }
            };

        }
        ICommandService service = (ICommandService) PlatformUI.getWorkbench().getAdapter(ICommandService.class);

        Command command = service.getCommand("net.refractions.udig.tool.edit.clearAction"); //$NON-NLS-1$
        command.setHandler(clearEditBlackboardHandler);

        if (clearEditBlackboardCommandListener == null) {
            clearEditBlackboardCommandListener = new ICommandListener() {

                public void commandChanged(CommandEvent commandEvent) {
                    if (commandEvent.isHandledChanged()) {
                        commandEvent.getCommand().removeCommandListener(this);
                        clearEditBlackboardCommandListener = null;

                        IMap map = ApplicationGIS.getActiveMap();
                        resetBlackboards(map);
                    }
                }

            };
            command.addCommandListener(clearEditBlackboardCommandListener);
        }
    }

    /**
     *  Removes a command handler for the "ESC" button.
     *  <p>
     *  Called from <code>EditToolHandler.disableListeners()</code>.
     */
    static synchronized void disableClearBlackboardCommand() {
        ICommandService service = (ICommandService) PlatformUI.getWorkbench().getAdapter(ICommandService.class);
        Command command = service.getCommand("net.refractions.udig.tool.edit.clearAction"); //$NON-NLS-1$

        if (clearEditBlackboardCommandListener != null) {
            command.removeCommandListener(clearEditBlackboardCommandListener);
            clearEditBlackboardCommandListener = null;
        }

        command.setHandler(null);

    }

    /**
     * Disables listeners so that they will not get events.
     * Should be called when tool is disabled or when a blackboard is no longer required.
     * 
     * @see #getEditBlackboard(IToolContext, ILayer) (it enables listeners).
     */
    public static void doneListening() {
        disableViewportListener();
        //        disableLayerEvents();
    }

    /**
     * Disables the listeners listening to layers for edit events.
     * @see #enableLayerChangeEventListener(ILayer, EditBlackboard)
     */
    public static synchronized void disableLayerEvents() {
        Collection<Map.Entry<ILayer, ILayerListener>> entries = layerListenerMap.entrySet();
        for (Map.Entry<ILayer, ILayerListener> entry : entries) {
            entry.getKey().removeListener(entry.getValue());
        }
        layerListenerMap.clear();
    }

    /**
     * Returns the selectedLayer or if it is not editable or not visible then the edit layer will be
     * 
     * @return
     */
    public static ILayer findEditLayer(IToolContext context) {
        ILayer layer = null;
        IMap map = context.getMap();
        // The selected layer will become the edit layer! If it's a featureStore and editable of
        // course.
        ILayer selectedLayer = ((EditManagerImpl) map.getEditManager()).getSelectedLayer();
        if (isEditable(selectedLayer)) {
            return selectedLayer;
        }
        // Otherwise we'll fall back to the current editLayer
        if (isEditable(map.getEditManager().getEditLayer()))
            return map.getEditManager().getEditLayer();
        // If all else fails we'll iterate through the layer and find the first layer eligable for
        // editing.
        // We should really never get here though.
        for (Iterator<ILayer> iter = map.getMapLayers().iterator(); iter.hasNext();) {
            layer = iter.next();
            if (isEditable(layer))
                break;
        }
        return layer;
    }

    /**
     * Checks that a layer has a FeatureStore, is editable and is visible.
     */
    private static boolean isEditable(ILayer layer) {
        return layer != null && layer.hasResource(FeatureStore.class) && layer.getInteraction(Interaction.EDIT)
                && layer.isVisible(); //$NON-NLS-1$
    }

    @SuppressWarnings("unchecked")
    private synchronized static void enableViewportListener(ViewportModel model) {
        disableViewportListener();
        listener = new ViewportModelListener(model);

        listener.model.eAdapters().add(listener);

    }

    private synchronized static void disableViewportListener() {
        if (listener != null)
            listener.model.eAdapters().remove(listener);
        listener = null;
    }

    /** 
     * Listens for changes to the viewport model and transforms the edit blackboards to the new CRS or bounds
     * 
     */
    static class ViewportModelListener extends AdapterImpl {
        ViewportModel model;
        private ILayer layer;

        ViewportModelListener(ViewportModel model) {
            this.model = model;
            this.layer = model.getMap().getEditManager().getSelectedLayer();
        }

        @Override
        public void notifyChanged(Notification msg) {
            if (listener == null) {
                model.eAdapters().remove(listener);
                listener = this;
                return;
            }
            switch (msg.getFeatureID(ViewportModel.class)) {
            case RenderPackage.VIEWPORT_MODEL__BOUNDS: {
                EditBlackboard editBlackBoard = getEditBlackBoardFromLayer(layer);
                if (editBlackBoard == null) {
                    return; // cannot notify edit blackboard
                }
                editBlackBoard.setToScreenTransform(model.worldToScreenTransform());
                break;
            }
            case RenderPackage.VIEWPORT_MODEL__CRS: {
                EditBlackboard editBlackBoard = getEditBlackBoardFromLayer(layer);
                if (editBlackBoard == null) {
                    return; // cannot notify edit blackboard
                }
                try {
                    editBlackBoard.setMapLayerTransform(layer.mapToLayerTransform());
                } catch (IOException e) {
                    EditPlugin.log("", e); //$NON-NLS-1$
                }
                editBlackBoard.setToScreenTransform(model.worldToScreenTransform());
                break;
            }
            }
        }
    }

    /**
     *  clears edit blackboard for all layers in a given map... Critical to prevent memory leaks
     */
    public static void resetBlackboards(IMap map) {
        if (map != null) {
            blackboardLock.lock();
            try {
                List<ILayer> layers = map.getMapLayers();
                for (ILayer layer : layers) {
                    EditBlackboard blackboard = getEditBlackBoardFromLayer(layer);
                    if (blackboard != null) {
                        blackboard.clear();
                        layer.getBlackboard().put(EDIT_BLACKBOARD_KEY, null);
                    }
                }
            } finally {
                blackboardLock.unlock();
            }
        }
    }

}