org.camunda.bpm.modeler.ui.diagram.editor.Bpmn2Editor.java Source code

Java tutorial

Introduction

Here is the source code for org.camunda.bpm.modeler.ui.diagram.editor.Bpmn2Editor.java

Source

/******************************************************************************* 
 * Copyright (c) 2011 Red Hat, Inc. 
 *  All rights reserved. 
 * This program is made available under the terms of the 
 * Eclipse Public License v1.0 which accompanies this distribution, 
 * and is available at http://www.eclipse.org/legal/epl-v10.html 
 * 
 * Contributors: 
 * Red Hat, Inc. - initial API and implementation 
 *
 * @author Innar Made
 ******************************************************************************/
package org.camunda.bpm.modeler.ui.diagram.editor;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.camunda.bpm.modeler.core.Activator;
import org.camunda.bpm.modeler.core.Bpmn2TabbedPropertySheetPage;
import org.camunda.bpm.modeler.core.ModelHandler;
import org.camunda.bpm.modeler.core.ProxyURIConverterImplExtension;
import org.camunda.bpm.modeler.core.files.FileService;
import org.camunda.bpm.modeler.core.model.Bpmn2ModelerResourceImpl;
import org.camunda.bpm.modeler.core.preferences.Bpmn2Preferences;
import org.camunda.bpm.modeler.core.utils.BusinessObjectUtil;
import org.camunda.bpm.modeler.core.utils.DiagramEditorAdapter;
import org.camunda.bpm.modeler.core.utils.ModelUtil;
import org.camunda.bpm.modeler.core.utils.ModelUtil.Bpmn2DiagramType;
import org.camunda.bpm.modeler.core.utils.ScrollUtil;
import org.camunda.bpm.modeler.core.utils.StyleUtil;
import org.camunda.bpm.modeler.runtime.engine.model.util.ModelResourceFactoryImpl;
import org.camunda.bpm.modeler.ui.views.outline.BaseElementTreeEditPart;
import org.camunda.bpm.modeler.ui.views.outline.Bpmn2EditorOutlinePage;
import org.camunda.bpm.modeler.ui.views.outline.FlowElementTreeEditPart;
import org.camunda.bpm.modeler.ui.wizards.Bpmn2DiagramCreator;
import org.eclipse.bpmn2.BaseElement;
import org.eclipse.bpmn2.util.Bpmn2Resource;
import org.eclipse.bpmn2.util.Bpmn2ResourceImpl;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.emf.common.util.BasicDiagnostic;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EValidator;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.transaction.RecordingCommand;
import org.eclipse.emf.transaction.TransactionalEditingDomain;
import org.eclipse.emf.transaction.TransactionalEditingDomain.Lifecycle;
import org.eclipse.emf.transaction.impl.TransactionalEditingDomainImpl;
import org.eclipse.emf.workspace.util.WorkspaceSynchronizer;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.GraphicalViewer;
import org.eclipse.gef.ui.parts.SelectionSynchronizer;
import org.eclipse.graphiti.mm.algorithms.GraphicsAlgorithm;
import org.eclipse.graphiti.mm.pictograms.Diagram;
import org.eclipse.graphiti.mm.pictograms.PictogramElement;
import org.eclipse.graphiti.services.Graphiti;
import org.eclipse.graphiti.services.IPeService;
import org.eclipse.graphiti.ui.editor.DefaultPaletteBehavior;
import org.eclipse.graphiti.ui.editor.DiagramEditor;
import org.eclipse.graphiti.ui.editor.DiagramEditorInput;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorReference;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.dialogs.SaveAsDialog;
import org.eclipse.ui.ide.IGotoMarker;
import org.eclipse.ui.ide.ResourceUtil;
import org.eclipse.ui.views.contentoutline.IContentOutlinePage;
import org.eclipse.ui.views.properties.IPropertySheetPage;
import org.eclipse.ui.views.properties.tabbed.ITabDescriptorProvider;

/**
 * The BPMN 2 diagram editor
 * 
 * @author nico.rehwaldt
 */
public class Bpmn2Editor extends DiagramEditor implements IPropertyChangeListener, IGotoMarker {

    public static final String EDITOR_ID = "org.camunda.bpm.modeler.ui.bpmn2editor";
    public static final String CONTRIBUTOR_ID = "org.camunda.bpm.modeler.ui.PropertyContributor";

    private static Bpmn2Editor activeEditor;
    private static ITabDescriptorProvider tabDescriptorProvider;

    private ModelHandler modelHandler;

    private Bpmn2Resource bpmnResource;

    private IResourceChangeListener markerChangeListener;
    private Bpmn2EditingDomainListener editingDomainListener;

    private Bpmn2Preferences preferences;

    protected DiagramEditorAdapter editorAdapter;

    private boolean bpmnLoaded = false;
    private DefaultPaletteBehavior paletteBehavior;

    public Bpmn2Editor() {
        editorAdapter = new DiagramEditorAdapter(this);
    }

    public static Bpmn2Editor getActiveEditor() {
        return activeEditor;
    }

    private void setActiveEditor(Bpmn2Editor editor) {
        activeEditor = editor;
        if (activeEditor != null) {
            Bpmn2Preferences.setActiveProject(activeEditor.getProject());
        }
    }

    public Resource getDiagramResource() {
        return getDiagramTypeProvider().getDiagram().eResource();
    }

    protected DiagramEditorAdapter getEditorAdapter() {
        return editorAdapter;
    }

    @Override
    protected DiagramEditorInput convertToDiagramEditorInput(IEditorInput input) throws PartInitException {

        if (input instanceof Bpmn2DiagramEditorInput) {
            return (Bpmn2DiagramEditorInput) input;
        } else {
            return createNewDiagramEditorInput(input, Bpmn2DiagramType.COLLABORATION);
        }
    }

    @Override
    public void init(IEditorSite site, IEditorInput input) throws PartInitException {
        super.init(site, input);

        setActiveEditor(this);
        addListeners();
    }

    protected void addListeners() {
        addMarkerChangeListener();
    }

    @Override
    public Bpmn2EditorUpdateBehavior createUpdateBehavior() {
        return new Bpmn2EditorUpdateBehavior(this);
    }

    @Override
    protected Bpmn2PersistencyBehavior createPersistencyBehavior() {
        return new Bpmn2PersistencyBehavior(this);
    }

    @Override
    protected Bpmn2RefreshBehavior createRefreshBehavior() {
        return new Bpmn2RefreshBehavior(this);
    }

    @Override
    protected DefaultPaletteBehavior createPaletteBehaviour() {
        this.paletteBehavior = super.createPaletteBehaviour();
        return paletteBehavior;
    }

    public DefaultPaletteBehavior getPaletteBehavior() {
        // override for better visibility
        return paletteBehavior;
    }

    @Override
    public PictogramElement[] getPictogramElementsForSelection() {
        // filter out invisible elements when setting selection
        PictogramElement[] pictogramElements = super.getPictogramElementsForSelection();
        if (pictogramElements == null)
            return null;
        ArrayList<PictogramElement> visibleList = new ArrayList<PictogramElement>();
        for (PictogramElement pe : pictogramElements) {
            if (pe.isVisible())
                visibleList.add(pe);
        }
        return visibleList.toArray(new PictogramElement[visibleList.size()]);
    }

    public Bpmn2Preferences getPreferences() {
        if (preferences == null) {
            loadPreferences(getProject());
        }
        return preferences;
    }

    private void loadPreferences(IProject project) {
        preferences = Bpmn2Preferences.getInstance(project);
        preferences.load();
        preferences.getGlobalPreferences().addPropertyChangeListener(this);
    }

    /**
     * ID for tabbed property sheets.
     * 
     * @return the contributor id
     */
    @Override
    public String getContributorId() {
        return CONTRIBUTOR_ID;
    }

    /**
     * Beware, creates a new input and changes this editor
     */
    private Bpmn2DiagramEditorInput createNewDiagramEditorInput(IEditorInput input, Bpmn2DiagramType diagramType) {
        URI modelUri = FileService.getInputUri(input);

        try {
            modelUri = FileService.resolveAsWorkspaceResource(modelUri);
        } catch (CoreException e) {
            throw new RuntimeException("Failed to create diagram", e);
        }

        return Bpmn2DiagramCreator.createDiagramInput(modelUri, diagramType, getEditingDomain());
    }

    @Override
    protected void setInput(IEditorInput input) {

        TransactionalEditingDomain editingDomain = getEditingDomain();
        ResourceSet resourceSet = editingDomain.getResourceSet();

        // get (and init) editing domain listener
        // this allows us to hook into the transaction exception handler to get
        // diagnostics about EMF validation errors
        getEditingDomainListener();

        // configure resource set
        if (!resourceSet.eAdapters().contains(editorAdapter)) {
            resourceSet.eAdapters().add(editorAdapter);

            resourceSet.setURIConverter(new ProxyURIConverterImplExtension());

            resourceSet.getResourceFactoryRegistry().getContentTypeToFactoryMap()
                    .put(Bpmn2ModelerResourceImpl.BPMN2_CONTENT_TYPE_ID, new ModelResourceFactoryImpl());
        }

        // resolve diagram input

        Bpmn2DiagramEditorInput diagramEditorInput = null;

        if (input instanceof Bpmn2DiagramEditorInput) {
            diagramEditorInput = (Bpmn2DiagramEditorInput) input;
        } else {
            diagramEditorInput = createNewDiagramEditorInput(input, Bpmn2DiagramType.COLLABORATION);
        }

        URI modelUri = diagramEditorInput.getModelUri();
        URI diagramUri = diagramEditorInput.getDiagramUri();

        // open and load BPMN 2.0 file

        bpmnResource = (Bpmn2ResourceImpl) resourceSet.createResource(modelUri,
                Bpmn2ModelerResourceImpl.BPMN2_CONTENT_TYPE_ID);
        bpmnResource.setTrackingModification(true);

        // handle the fact that we may deal with
        // virtual, i.e. linked resource files here and get
        // the model name from the actual file location
        IFile file = WorkspaceSynchronizer.getFile(bpmnResource);
        String modelName = Bpmn2DiagramCreator.getModelName(URI.createURI(file.getRawLocationURI().toString()));

        // create graphiti diagram file

        Diagram diagram = Graphiti.getPeCreateService().createDiagram("BPMN2", modelName, true);
        diagram.setGridUnit(0);
        diagram.setActive(true);

        FileService.createDiagramResource(diagramUri, diagram, editingDomain);

        bpmnLoaded = true;

        try {
            bpmnResource.load(null);
        } catch (FileNotFoundException e) {
            bpmnLoaded = false;
        } catch (IOException e) {
            String message = e.getMessage();

            if (message.matches("Resource '[^']+' does not exist.")) {
                bpmnLoaded = false;
            }
        }

        if (!bpmnLoaded) {
            asyncClose();
        }

        // set input
        super.setInput(diagramEditorInput);

        setActiveEditor(this);
    }

    @Override
    public String getTitleToolTip() {
        IFile modelFile = getModelFile();
        if (modelFile != null) {
            IPath location = null;

            if (modelFile.isLinked()) {
                location = modelFile.getRawLocation();
            } else {
                location = modelFile.getFullPath();
            }

            return location.toPortableString();
        } else {
            return super.getTitleToolTip();
        }
    }

    private void asyncClose() {
        Display.getDefault().asyncExec(new Runnable() {

            @Override
            public void run() {
                close();
            }
        });
    }

    public Bpmn2Resource getModelResource() {
        return bpmnResource;
    }

    public boolean isBpmnLoaded() {
        return bpmnLoaded;
    }

    @Override
    public void gotoMarker(IMarker marker) {
        final EObject target = getTargetObject(marker);
        if (target == null) {
            return;
        }

        final PictogramElement pe = getDiagramTypeProvider().getFeatureProvider()
                .getPictogramElementForBusinessObject(target);
        if (pe == null) {
            return;
        }

        selectPictogramElements(new PictogramElement[] { pe });
    }

    private EObject getTargetObject(IMarker marker) {
        final String uriString = marker.getAttribute(EValidator.URI_ATTRIBUTE, null);
        final URI uri = uriString == null ? null : URI.createURI(uriString);
        if (uri == null) {
            return null;
        }
        return getEditingDomain().getResourceSet().getEObject(uri, false);
    }

    private void addMarkerChangeListener() {
        markerChangeListener = new Bpmn2MarkerChangeListener(this);
        addResourceListener(markerChangeListener, IResourceChangeEvent.POST_CHANGE);
    }

    protected void addResourceListener(IResourceChangeListener listener, int eventMask) {
        ResourcesPlugin.getWorkspace().addResourceChangeListener(listener, eventMask);
    }

    private void removeMarkerChangeListener() {
        if (markerChangeListener != null) {
            getModelFile().getWorkspace().removeResourceChangeListener(markerChangeListener);
            markerChangeListener = null;
        }
    }

    public Bpmn2EditingDomainListener getEditingDomainListener() {
        if (editingDomainListener == null) {
            TransactionalEditingDomainImpl editingDomain = (TransactionalEditingDomainImpl) getEditingDomain();
            if (editingDomain == null) {
                return null;
            }
            editingDomainListener = new Bpmn2EditingDomainListener(this);

            Lifecycle domainLifeCycle = (Lifecycle) editingDomain.getAdapter(Lifecycle.class);
            domainLifeCycle.addTransactionalEditingDomainListener(editingDomainListener);
        }
        return editingDomainListener;
    }

    public BasicDiagnostic getDiagnostics() {
        return getEditingDomainListener().getDiagnostics();
    }

    @Override
    @SuppressWarnings("rawtypes")
    public Object getAdapter(Class required) {
        if (required == ITabDescriptorProvider.class) {
            if (tabDescriptorProvider == null) {
                IWorkbenchPage page = getEditorSite().getPage();
                String viewID = "org.eclipse.ui.views.PropertySheet";
                try {
                    page.showView(viewID, null, IWorkbenchPage.VIEW_CREATE);
                    page.showView(viewID, null, IWorkbenchPage.VIEW_ACTIVATE);
                } catch (Exception e) {
                }
            }
            return tabDescriptorProvider;
        }
        if (required == Bpmn2Preferences.class)
            return getPreferences();
        if (required == IPropertySheetPage.class) {
            return new Bpmn2TabbedPropertySheetPage(this);
        }
        if (required == SelectionSynchronizer.class) {
            return getSelectionSynchronizer();
        }
        if (required == IContentOutlinePage.class) {
            if (getDiagramTypeProvider() != null) {
                Bpmn2EditorOutlinePage outlinePage = new Bpmn2EditorOutlinePage(this);
                return outlinePage;
            }
        }

        return super.getAdapter(required);
    }

    @Override
    public void dispose() {

        removeListeners();

        // clear ID mapping tables if no more instances of editor are active
        int instances = 0;
        IEditorSite editorSite = getEditorSite();

        Bpmn2DiagramEditorInput diagramEditorInput = getEditorInput();

        // need to check != null to deal with errors
        if (editorSite != null) {
            IWorkbenchPage[] pages = editorSite.getWorkbenchWindow().getPages();
            for (IWorkbenchPage p : pages) {
                IEditorReference[] refs = p.getEditorReferences();
                instances += refs.length;
            }
        }

        try {
            cleanupDiagramFile(diagramEditorInput);
        } catch (Exception e) {
            Activator.logStatus(new Status(IStatus.WARNING, Activator.PLUGIN_ID,
                    "Could not clean up diagram file: " + e.getMessage(), e));
        }

        try {
            if (modelHandler != null) {
                ModelUtil.clearIDs(modelHandler.getResource(), instances == 0);
            }
            getPreferences().getGlobalPreferences().removePropertyChangeListener(this);

            getEditingDomain().getResourceSet().eAdapters().remove(getEditorAdapter());

            if (instances == 0) {
                setActiveEditor(null);
            }
        } catch (Exception e) {
            Activator.logError(e);
        }

        super.dispose();

        try {
            getPreferences().dispose();
        } catch (Exception e) {
            Activator.logError(e);
        }
    }

    private void removeListeners() {
        removeMarkerChangeListener();
    }

    private void cleanupDiagramFile(Bpmn2DiagramEditorInput editorInput) {

        // not null check --> dealing with previous open diagram failures
        if (editorInput == null) {
            return;
        }

        URI diagramUri = editorInput.getDiagramUri();
        if (diagramUri == null) {
            return;
        }

        File diagramFile = new File(diagramUri.toFileString());
        if (diagramFile.exists()) {
            diagramFile.delete();
        }
    }

    public IPath getModelPath() {
        IResource modelFile = getModelFile();

        if (modelFile != null) {
            return modelFile.getFullPath();
        }

        return null;
    }

    public IProject getProject() {
        IResource modelFile = getModelFile();

        if (modelFile != null) {
            return modelFile.getProject();
        } else {
            return null;
        }
    }

    public IFile getModelFile() {
        return WorkspaceSynchronizer.getUnderlyingFile(bpmnResource);
    }

    public ModelHandler getModelHandler() {
        return modelHandler;
    }

    public void createPartControl(Composite parent) {
        if (getGraphicalViewer() == null) {
            super.createPartControl(parent);
        }
    }

    @Override
    public boolean isSaveAsAllowed() {
        return getModelFile() != null;
    }

    @Override
    public void doSaveAs() {
        IFile oldFile = getModelFile();

        SaveAsDialog saveAsDialog = new SaveAsDialog(getSite().getShell());
        saveAsDialog.setOriginalFile(oldFile);
        saveAsDialog.create();

        if (saveAsDialog.open() == SaveAsDialog.CANCEL) {
            return;
        }

        IPath newFilePath = saveAsDialog.getResult();
        if (newFilePath == null) {
            return;
        }

        URI newURI = URI.createPlatformResourceURI(newFilePath.toString(), true);

        Bpmn2EditorUpdateBehavior updateBehavior = (Bpmn2EditorUpdateBehavior) getUpdateBehavior();
        updateBehavior.getWorkspaceSynchronizerDelegate().handleResourceMoved(bpmnResource, newURI);

        doSave(null);
    }

    public void close() {
        bpmnLoaded = false;

        Display.getDefault().asyncExec(new Runnable() {
            public void run() {
                IWorkbenchPage workbenchPage = getSite().getPage();

                boolean closed = workbenchPage.closeEditor(Bpmn2Editor.this, false);
                if (!closed) {
                    // If close editor fails, try again with explicit editorpart
                    // of the old file
                    IFile oldFile = getModelFile();
                    IEditorPart editorPart = ResourceUtil.findEditor(workbenchPage, oldFile);

                    closed = workbenchPage.closeEditor(editorPart, false);
                }
            }
        });
    }

    @Override
    public Bpmn2DiagramEditorInput getEditorInput() {
        return (Bpmn2DiagramEditorInput) super.getEditorInput();
    }

    // //////////////////////////////////////////////////////////////////////////////
    // Other handlers
    // //////////////////////////////////////////////////////////////////////////////

    // FIXME sometime the last element will be selected randomly, could be related
    // to this function
    @Override
    public void selectionChanged(IWorkbenchPart part, ISelection selection) {

        // make sure we are not able to select the scroll filter

        final ScrollFilteredSelection scrollFiltered = filterScrollElement(selection);
        if (scrollFiltered != null) {
            fixScrollFilteredSelection(selection, scrollFiltered);
            return;
        }

        super.selectionChanged(part, selection);

        updateActions(getSelectionActions()); // usually done in GEF

        EditPart editPart = BusinessObjectUtil.getEditPartForSelection(selection);
        Object pictogramElement = BusinessObjectUtil.getPictogramElementForSelection(selection);

        if (pictogramElement instanceof PictogramElement
                && (editPart instanceof FlowElementTreeEditPart || editPart instanceof BaseElementTreeEditPart)) {
            selectPictogramElements(new PictogramElement[] { (PictogramElement) pictogramElement });
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse
     * .jface.util.PropertyChangeEvent)
     */
    @Override
    public void propertyChange(PropertyChangeEvent event) {

        if (event.getProperty().endsWith(Bpmn2Preferences.PREF_SHAPE_STYLE)) {
            getEditingDomain().getCommandStack().execute(new RecordingCommand(getEditingDomain()) {
                @Override
                protected void doExecute() {
                    IPeService peService = Graphiti.getPeService();
                    TreeIterator<EObject> iter = getDiagramTypeProvider().getDiagram().eAllContents();
                    while (iter.hasNext()) {
                        EObject o = iter.next();
                        if (o instanceof PictogramElement) {
                            PictogramElement pe = (PictogramElement) o;
                            BaseElement be = BusinessObjectUtil.getFirstElementOfType(pe, BaseElement.class);
                            if (be != null) {
                                TreeIterator<EObject> childIter = pe.eAllContents();
                                while (childIter.hasNext()) {
                                    o = childIter.next();
                                    if (o instanceof GraphicsAlgorithm) {
                                        GraphicsAlgorithm ga = (GraphicsAlgorithm) o;
                                        if (peService.getPropertyValue(ga,
                                                Bpmn2Preferences.PREF_SHAPE_STYLE) != null) {
                                            StyleUtil.applyStyle(ga, be);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            });
        }
    }

    // // Scroll fix related stuff ////////////////////////////////////////////

    private void fixScrollFilteredSelection(ISelection selection, ScrollFilteredSelection scrollFiltered) {

        final GraphicalViewer graphicalViewer = getGraphicalViewer();
        IStructuredSelection newSelection = scrollFiltered.getSelection();
        EditPart scrollEditPart = scrollFiltered.getScrollEditPart();

        boolean async = false;

        if (selection instanceof IStructuredSelection) {
            // if first selected element equals scroll edit part update
            // fire the updated selection asynchronously to allow the
            // property panel to pick the change up
            async = ((IStructuredSelection) selection).getFirstElement().equals(scrollEditPart);
        }

        graphicalViewer.deselect(scrollEditPart);

        if (newSelection.isEmpty()) {
            Object diagramEditPart = graphicalViewer.getEditPartRegistry()
                    .get(getDiagramTypeProvider().getDiagram());

            List<?> selectedEditParts = graphicalViewer.getSelectedEditParts();
            if (selectedEditParts.contains(diagramEditPart)) {
                return;
            }

            newSelection = new StructuredSelection(diagramEditPart);
        }

        final ISelection refreshSelection = newSelection;

        if (async) {
            // set the new selection
            Display.getDefault().asyncExec(new Runnable() {
                @Override
                public void run() {
                    graphicalViewer.setSelection(refreshSelection);
                }
            });
        } else {
            graphicalViewer.setSelection(refreshSelection);
        }
    }

    /**
     * Returns the list of filtered elements if the scroll shape was contained in
     * the selection. Returns null otherwise (no filtering required).
     * 
     * @param selection
     * @return
     */
    private ScrollFilteredSelection filterScrollElement(ISelection selection) {
        if (selection instanceof IStructuredSelection) {
            IStructuredSelection structuredSelection = (IStructuredSelection) selection;

            boolean foundScrollShape = false;

            ScrollFilteredSelection filteredSelection = new ScrollFilteredSelection();

            for (Object o : structuredSelection.toArray()) {
                if (o instanceof EditPart) {
                    EditPart editPart = (EditPart) o;
                    PictogramElement element = BusinessObjectUtil.getPictogramElementForEditPart(editPart);
                    if (element != null) {
                        if (ScrollUtil.isScrollShape(element)) {
                            foundScrollShape = true;

                            filteredSelection.setScrollElement(editPart, element);

                            // do not add scroll shape
                            continue;
                        }

                        filteredSelection.add(editPart, element);
                    }
                }
            }

            if (foundScrollShape) {
                return filteredSelection;
            } else {
                return null;
            }
        } else {
            return null;
        }
    }

    /**
     * Helper for filtering the scroll shape from an editor selection.
     * 
     * @author nico.rehwaldt
     */
    private static class ScrollFilteredSelection {

        private ArrayList<EditPart> editParts;
        private ArrayList<PictogramElement> pictogramElements;

        private EditPart scrollEditPart;

        public ScrollFilteredSelection() {

            this.editParts = new ArrayList<EditPart>();
            this.pictogramElements = new ArrayList<PictogramElement>();
        }

        public void add(EditPart editPart, PictogramElement pictogramElement) {
            this.editParts.add(editPart);
            this.pictogramElements.add(pictogramElement);
        }

        public void setScrollElement(EditPart editPart, PictogramElement pictogramElement) {
            this.scrollEditPart = editPart;
        }

        public EditPart getScrollEditPart() {
            return scrollEditPart;
        }

        public IStructuredSelection getSelection() {
            return new StructuredSelection(editParts.toArray());
        }
    }
}