org.lh.dmlj.schema.editor.SchemaEditor.java Source code

Java tutorial

Introduction

Here is the source code for org.lh.dmlj.schema.editor.SchemaEditor.java

Source

/**
 * Copyright (C) 2014  Luc Hermans
 * 
 * This program 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.
 * 
 * This program 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 this program.  If
 * not, see <http://www.gnu.org/licenses/>.
 * 
 * Contact information: kozzeluc@gmail.com.
 */
package org.lh.dmlj.schema.editor;

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

import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IResourceDeltaVisitor;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Insets;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl;
import org.eclipse.gef.DefaultEditDomain;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.EditPartViewer;
import org.eclipse.gef.GraphicalViewer;
import org.eclipse.gef.SnapToGeometry;
import org.eclipse.gef.SnapToGrid;
import org.eclipse.gef.commands.CommandStack;
import org.eclipse.gef.commands.CommandStackEvent;
import org.eclipse.gef.commands.CommandStackEventListener;
import org.eclipse.gef.editparts.ScalableFreeformRootEditPart;
import org.eclipse.gef.editparts.ZoomListener;
import org.eclipse.gef.editparts.ZoomManager;
import org.eclipse.gef.palette.CombinedTemplateCreationEntry;
import org.eclipse.gef.palette.ConnectionCreationToolEntry;
import org.eclipse.gef.palette.MarqueeToolEntry;
import org.eclipse.gef.palette.PaletteDrawer;
import org.eclipse.gef.palette.PaletteRoot;
import org.eclipse.gef.palette.PaletteToolbar;
import org.eclipse.gef.palette.PanningSelectionToolEntry;
import org.eclipse.gef.palette.ToolEntry;
import org.eclipse.gef.print.PrintGraphicalViewerOperation;
import org.eclipse.gef.requests.SimpleFactory;
import org.eclipse.gef.rulers.RulerProvider;
import org.eclipse.gef.tools.AbstractTool;
import org.eclipse.gef.ui.actions.ActionRegistry;
import org.eclipse.gef.ui.actions.DeleteAction;
import org.eclipse.gef.ui.actions.PrintAction;
import org.eclipse.gef.ui.actions.RedoAction;
import org.eclipse.gef.ui.actions.SelectAllAction;
import org.eclipse.gef.ui.actions.UndoAction;
import org.eclipse.gef.ui.actions.ZoomInAction;
import org.eclipse.gef.ui.actions.ZoomOutAction;
import org.eclipse.gef.ui.palette.PaletteViewerProvider;
import org.eclipse.gef.ui.parts.GraphicalEditorWithFlyoutPalette;
import org.eclipse.gef.ui.parts.GraphicalViewerKeyHandler;
import org.eclipse.gef.ui.parts.ScrollingGraphicalViewer;
import org.eclipse.gef.ui.parts.SelectionSynchronizer;
import org.eclipse.gef.ui.rulers.RulerComposite;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.commands.ActionHandler;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.swt.SWT;
import org.eclipse.swt.printing.PrintDialog;
import org.eclipse.swt.printing.Printer;
import org.eclipse.swt.printing.PrinterData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorReference;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.IPartListener;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchPartSite;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.actions.WorkspaceModifyOperation;
import org.eclipse.ui.dialogs.SaveAsDialog;
import org.eclipse.ui.handlers.IHandlerService;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.views.contentoutline.IContentOutlinePage;
import org.eclipse.ui.views.properties.IPropertySheetPage;
import org.eclipse.ui.views.properties.tabbed.ITabbedPropertySheetPageContributor;
import org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetPage;
import org.lh.dmlj.schema.Connector;
import org.lh.dmlj.schema.DiagramLabel;
import org.lh.dmlj.schema.Schema;
import org.lh.dmlj.schema.SchemaPackage;
import org.lh.dmlj.schema.SystemOwner;
import org.lh.dmlj.schema.editor.command.SetZoomLevelCommand;
import org.lh.dmlj.schema.editor.command.infrastructure.IModelChangeListener;
import org.lh.dmlj.schema.editor.command.infrastructure.IModelChangeProvider;
import org.lh.dmlj.schema.editor.command.infrastructure.ModelChangeDispatcher;
import org.lh.dmlj.schema.editor.outline.OutlinePage;
import org.lh.dmlj.schema.editor.palette.IChainedSetPlaceHolder;
import org.lh.dmlj.schema.editor.palette.IIndexedSetPlaceHolder;
import org.lh.dmlj.schema.editor.palette.IMultipleMemberSetPlaceHolder;
import org.lh.dmlj.schema.editor.part.SchemaDiagramEditPartFactory;
import org.lh.dmlj.schema.editor.preference.PreferenceConstants;
import org.lh.dmlj.schema.editor.ruler.SchemaEditorRulerProvider;

public class SchemaEditor extends GraphicalEditorWithFlyoutPalette
        implements CommandStackEventListener, ITabbedPropertySheetPageContributor {

    public static final String ID = "org.lh.dmlj.schema.editor.schemaeditor";

    private static final EAttribute ATTRIBUTE_SHOW_GRID = SchemaPackage.eINSTANCE.getDiagramData_ShowGrid();
    private static final EAttribute ATTRIBUTE_SHOW_RULERS = SchemaPackage.eINSTANCE.getDiagramData_ShowRulers();
    private static final EAttribute ATTRIBUTE_ZOOM_LEVEL = SchemaPackage.eINSTANCE.getDiagramData_ZoomLevel();

    // This class listens to changes to the file system in the workspace, and
    // makes changes accordingly.
    // 1) An open, saved file gets deleted -> close the editor
    // 2) An open file gets renamed or moved -> change the editor's input
    // accordingly
    class ResourceTracker implements IResourceChangeListener, IResourceDeltaVisitor {
        public void resourceChanged(IResourceChangeEvent event) {
            IResourceDelta delta = event.getDelta();
            try {
                if (delta != null) {
                    delta.accept(this);
                }
            } catch (CoreException exception) {
                // What should be done here?
            }
        }

        public boolean visit(IResourceDelta delta) {
            if (delta == null || !delta.getResource().equals(((IFileEditorInput) getEditorInput()).getFile())) {
                return true;
            }

            if (delta.getKind() == IResourceDelta.REMOVED) {
                Display display = getSite().getShell().getDisplay();
                if ((IResourceDelta.MOVED_TO & delta.getFlags()) == 0) { // if
                    // the
                    // file
                    // was
                    // deleted
                    // NOTE: The case where an open, unsaved file is deleted is
                    // being handled by the
                    // PartListener added to the Workbench in the initialize()
                    // method.
                    display.asyncExec(new Runnable() {
                        public void run() {
                            if (!isDirty()) {
                                closeEditor(false);
                            }
                        }
                    });
                } else { // else if it was moved or renamed
                    final IFile newFile = ResourcesPlugin.getWorkspace().getRoot().getFile(delta.getMovedToPath());
                    display.asyncExec(new Runnable() {
                        public void run() {
                            superSetInput(new FileEditorInput(newFile));
                        }
                    });
                }
            } else if (delta.getKind() == IResourceDelta.CHANGED) {
                if (!editorSaving) {
                    // the file was overwritten somehow (could have been
                    // replaced by another
                    // version in the respository)
                    final IFile newFile = ResourcesPlugin.getWorkspace().getRoot().getFile(delta.getFullPath());
                    Display display = getSite().getShell().getDisplay();
                    display.asyncExec(new Runnable() {
                        public void run() {
                            setInput(new FileEditorInput(newFile));
                            getCommandStack().flush();
                        }
                    });
                }
            }
            return false;
        }
    }

    private IPartListener partListener = new IPartListener() {
        // If an open, unsaved file was deleted, query the user to either do a
        // "Save As" or close the editor.
        public void partActivated(IWorkbenchPart part) {
            if (part != SchemaEditor.this) {
                return;
            }
            if (!((IFileEditorInput) getEditorInput()).getFile().exists()) {
                Shell shell = getSite().getShell();
                String title = "File Deleted";
                String message = "The file has been deleted from the file system.  Do you "
                        + "want to save your changes or close the editor without " + "saving ?";
                String[] buttons = { "Save", "Close" };
                MessageDialog dialog = new MessageDialog(shell, title, null, message, MessageDialog.QUESTION,
                        buttons, 0);
                if (dialog.open() == 0) {
                    if (!performSaveAs()) {
                        partActivated(part);
                    }
                } else {
                    closeEditor(false);
                }
            }
        }

        public void partBroughtToTop(IWorkbenchPart part) {
        }

        public void partClosed(IWorkbenchPart part) {
        }

        public void partDeactivated(IWorkbenchPart part) {
        }

        public void partOpened(IWorkbenchPart part) {
        }
    };

    private boolean editorSaving = false;
    private SchemaEditorRulerProvider horizontalRulerProvider;
    private ModelChangeDispatcher modelChangeDispatcher = new ModelChangeDispatcher();
    private IModelChangeListener modelChangeListener;
    private OutlinePage outlinePage;
    private PaletteRoot palette;
    private ResourceTracker resourceListener = new ResourceTracker();
    private RulerComposite rulerComp;
    private Schema schema;
    private SelectionSynchronizer selectionSynchronizer;
    private URI uri;
    private SchemaEditorRulerProvider verticalRulerProvider;
    private IResource workspaceResource;

    public SchemaEditor() {
        super();
    }

    private void activePaletteToolChanged(ToolEntry tool) {
        ModifiedPaletteViewerProvider paletteViewerProvider = (ModifiedPaletteViewerProvider) getPaletteViewerProvider();
        paletteViewerProvider.selectTool(tool);
    }

    private void closeEditor(final boolean save) {
        final Display display = PlatformUI.getWorkbench().getDisplay();
        display.asyncExec(new Runnable() {
            public void run() {
                getSite().getPage().closeEditor(SchemaEditor.this, save);
            }
        });
    }

    @Override
    public void commandStackChanged(EventObject event) {
        firePropertyChange(IEditorPart.PROP_DIRTY);
        super.commandStackChanged(event);
    }

    @Override
    public void stackChanged(CommandStackEvent event) {
        modelChangeDispatcher.dispatch(event);
    }

    @Override
    protected void configureGraphicalViewer() {

        super.configureGraphicalViewer();

        // create the root edit part...
        ScalableFreeformRootEditPart root = new ScalableFreeformRootEditPart();

        // set clipping strategy for connection layer
        /*ConnectionLayer connectionLayer = 
           (ConnectionLayer) root.getLayer(LayerConstants.CONNECTION_LAYER);
        connectionLayer.setClippingStrategy(new ViewportAwareConnectionLayerClippingStrategy(connectionLayer));*/

        List<String> zoomLevels = new ArrayList<>(3);
        zoomLevels.add(ZoomManager.FIT_ALL);
        zoomLevels.add(ZoomManager.FIT_WIDTH);
        zoomLevels.add(ZoomManager.FIT_HEIGHT);
        root.getZoomManager().setZoomLevelContributions(zoomLevels);

        IAction zoomIn = new ZoomInAction(root.getZoomManager());
        IAction zoomOut = new ZoomOutAction(root.getZoomManager());
        getActionRegistry().registerAction(zoomIn);
        getActionRegistry().registerAction(zoomOut);

        IHandlerService handlerService = (IHandlerService) PlatformUI.getWorkbench()
                .getService(IHandlerService.class);
        handlerService.activateHandler(zoomIn.getActionDefinitionId(), new ActionHandler(zoomIn));
        handlerService.activateHandler(zoomOut.getActionDefinitionId(), new ActionHandler(zoomOut));

        // configure the graphical viewer...
        GraphicalViewer viewer = getGraphicalViewer();
        viewer.setRootEditPart(root);
        viewer.setEditPartFactory(new SchemaDiagramEditPartFactory(this));
        viewer.setSelectionManager(new ModifiedSelectionManager(viewer));
        viewer.setKeyHandler(new GraphicalViewerKeyHandler(viewer));

        // left (vertical) ruler properties            
        verticalRulerProvider = new SchemaEditorRulerProvider(schema.getDiagramData().getVerticalRuler(), this);
        getGraphicalViewer().setProperty(RulerProvider.PROPERTY_VERTICAL_RULER, verticalRulerProvider);

        // top (horizontal) ruler properties
        horizontalRulerProvider = new SchemaEditorRulerProvider(schema.getDiagramData().getHorizontalRuler(), this);
        getGraphicalViewer().setProperty(RulerProvider.PROPERTY_HORIZONTAL_RULER, horizontalRulerProvider);

        // ruler visibility (currently, the rulers are always visible)
        getGraphicalViewer().setProperty(RulerProvider.PROPERTY_RULER_VISIBILITY,
                schema.getDiagramData().isShowRulers());

        // Snap to Geometry property
        getGraphicalViewer().setProperty(SnapToGeometry.PROPERTY_SNAP_ENABLED,
                schema.getDiagramData().isSnapToGeometry());

        // Grid properties
        getGraphicalViewer().setProperty(SnapToGrid.PROPERTY_GRID_ENABLED,
                new Boolean(schema.getDiagramData().isSnapToGrid()));
        getGraphicalViewer().setProperty(SnapToGrid.PROPERTY_GRID_VISIBLE,
                new Boolean(schema.getDiagramData().isShowGrid()));
        // Set the grid spacing; the value that we need (for a spacing of half a
        // centimeter) is somewhere between 18 and 19 (pixels); 19 seems to be 
        // the better choice over 18 but is not exactly what we need.
        // todo: store the grid spacing in the model.
        getGraphicalViewer().setProperty(SnapToGrid.PROPERTY_GRID_SPACING, new Dimension(19, 19));

        // configure the zoom manager with the zoom level stored in the schema
        // and attach a zoom listener to change the model via the command stack 
        // whenever the user zooms in or out...
        final ZoomManager manager = (ZoomManager) getGraphicalViewer().getProperty(ZoomManager.class.toString());
        if (manager != null) {

            // get the zoom level from the model and set the zoom managers level
            // to it (it will be ignored if out of range)
            double zoomLevel = schema.getDiagramData().getZoomLevel();
            manager.setZoom(zoomLevel);

            // make sure the zoom level in the model matches the value in the 
            // zoom level combo; if there is a mismatch, connection endpoints 
            // and bendpoints will go crazy
            if (manager.getZoom() != zoomLevel) {
                // the zoom level is probably 'Page', 'Width' or 'Height'; for
                // now, we will adjust the zoom level in the model (the file IS
                // marked as dirty), but it would be better if we could just
                // 'select' the right zoom level.            
                SetZoomLevelCommand command = new SetZoomLevelCommand(schema, manager.getZoom(), false);
                getCommandStack().execute(command);
            }

            // make sure we are informed of zoom changes
            manager.addZoomListener(new ZoomListener() {
                @Override
                public void zoomChanged(double zoom) {
                    if (zoom != schema.getDiagramData().getZoomLevel()) {
                        SetZoomLevelCommand command = new SetZoomLevelCommand(schema, zoom);
                        getCommandStack().execute(command);
                    }
                }
            });
        }
        // Scroll-wheel Zoom
        /*getGraphicalViewer().setProperty(MouseWheelHandler.KeyGenerator.getKey(SWT.MOD1),
                           MouseWheelZoomHandler.SINGLETON);*/

        // add a listener to the command stack to have the model change dispatcher dispatch the  
        // command stack event to inform all of its listeners of a model change 'event' - a command 
        // annotated with @ModelChange should always leave the model in a consistent state after its 
        // execute(), redo() or undo() method has been called (like any other command actually; the 
        // annotation is merely used to extract the kind [category] of model change)
        getCommandStack().addCommandStackEventListener(this);

        // attach a model change listener to respond to changes to rulers&guides, grid visibility 
        // and zoom level
        modelChangeListener = new IModelChangeListener() {

            @Override
            public void afterAddItem(EObject owner, EReference reference, Object item) {
            }

            @Override
            public void afterMoveItem(EObject oldOwner, EReference reference, Object item, EObject newOwner) {
            }

            @Override
            public void afterRemoveItem(EObject owner, EReference reference, Object item) {
            }

            @Override
            public void afterSetFeatures(EObject owner, EStructuralFeature[] features) {
                if (owner == schema.getDiagramData() && features.length == 1
                        && features[0] == ATTRIBUTE_SHOW_RULERS) {

                    // the rulers are to be shown or hidden
                    boolean showRulers = schema.getDiagramData().isShowRulers();
                    getGraphicalViewer().setProperty(RulerProvider.PROPERTY_RULER_VISIBILITY,
                            Boolean.valueOf(showRulers));
                } else if (owner == schema.getDiagramData() && features.length == 1
                        && features[0] == ATTRIBUTE_SHOW_GRID) {

                    // the grid has to be shown or hidden
                    boolean showGrid = schema.getDiagramData().isShowGrid();
                    getGraphicalViewer().setProperty(SnapToGrid.PROPERTY_GRID_VISIBLE, Boolean.valueOf(showGrid));
                } else if (owner == schema.getDiagramData() && features.length == 1
                        && features[0] == ATTRIBUTE_ZOOM_LEVEL) {

                    // the zoom level has changed; it is important to set the manager's zoom level
                    // only when it's different from the current model value (to avoid assertion
                    // errors regarding the 'dispatching' indicator in the model change dispatcher,
                    // meaning we've put a command on the command stack while dispatching a command
                    // stack event)
                    if (manager != null && schema.getDiagramData().getZoomLevel() != manager.getZoom()) {

                        manager.setZoom(schema.getDiagramData().getZoomLevel());
                    }
                }
            }

        };
        modelChangeDispatcher.addModelChangeListener(modelChangeListener);

    }

    @SuppressWarnings("unchecked")
    @Override
    protected void createActions() {
        ActionRegistry registry = getActionRegistry();
        IAction action;

        action = new UndoAction(this);
        registry.registerAction(action);
        getStackActions().add(action.getId());

        action = new RedoAction(this);
        registry.registerAction(action);
        getStackActions().add(action.getId());

        action = new SelectAllAction(this);
        registry.registerAction(action);

        action = new DeleteAction((IWorkbenchPart) this);
        registry.registerAction(action);
        getSelectionActions().add(action.getId());

        action = new PrintAction(this) {
            public void run() {
                GraphicalViewer viewer;
                viewer = (GraphicalViewer) getWorkbenchPart().getAdapter(GraphicalViewer.class);

                PrintDialog dialog = new PrintDialog(viewer.getControl().getShell(), SWT.NULL);
                PrinterData data = dialog.open();

                if (data != null) {
                    // create a print operation
                    PrintGraphicalViewerOperation op = new PrintGraphicalViewerOperation(new Printer(data), viewer);
                    // set the printmargins; margins are stored in pels (logical pixels; 
                    // 72 pels == 1 inch)
                    IPreferenceStore store = Plugin.getDefault().getPreferenceStore();
                    int topMargin = (int) (store.getInt(PreferenceConstants.TOP_MARGIN));
                    int leftMargin = (int) (store.getInt(PreferenceConstants.LEFT_MARGIN));
                    int bottomMargin = (int) (store.getInt(PreferenceConstants.BOTTOM_MARGIN));
                    int rightMargin = (int) (store.getInt(PreferenceConstants.RIGHT_MARGIN));
                    Insets printMargin = new Insets(topMargin, leftMargin, bottomMargin, rightMargin);
                    op.setPrintMargin(printMargin);
                    // run the print operation
                    op.run(getWorkbenchPart().getTitle());
                }
            }
        };
        registry.registerAction(action);
    }

    @Override
    protected void createGraphicalViewer(Composite parent) {
        rulerComp = new RulerComposite(parent, SWT.NONE);
        super.createGraphicalViewer(rulerComp);
        ScrollingGraphicalViewer graphicalViewer = (ScrollingGraphicalViewer) getGraphicalViewer();
        rulerComp.setGraphicalViewer(graphicalViewer);
    }

    @Override
    protected PaletteViewerProvider createPaletteViewerProvider() {
        return new ModifiedPaletteViewerProvider(this);
    }

    public void dispose() {
        getCommandStack().removeCommandStackEventListener(this);
        ModifiedPaletteViewerProvider paletteViewerProvider = (ModifiedPaletteViewerProvider) getPaletteViewerProvider();
        paletteViewerProvider.dispose();
        hookActivePaletteViewerToEditDomain();
        verticalRulerProvider.dispose();
        horizontalRulerProvider.dispose();
        modelChangeDispatcher.dispose();
        getSite().getWorkbenchWindow().getPartService().removePartListener(partListener);
        partListener = null;
        ((IFileEditorInput) getEditorInput()).getFile().getWorkspace()
                .removeResourceChangeListener(resourceListener);
        super.dispose();
    }

    @Override
    public void doSave(IProgressMonitor monitor) {
        editorSaving = true;
        // Serialize the model
        ResourceSet resourceSet = new ResourceSetImpl();
        Resource resource = resourceSet.createResource(uri);
        resource.getContents().add(schema);
        try {
            resource.save(null);
        } catch (IOException e) {
            e.printStackTrace();
            Status status = new Status(IStatus.ERROR, ID, "An exception occurred while saving the file", e);
            ErrorDialog.openError(getSite().getShell(), "Exception", e.getMessage(), status);
        }

        // refresh the resource in the workspace to avoid 'Resource is out of 
        // sync with the file system' messages
        try {
            workspaceResource.refreshLocal(IResource.DEPTH_ZERO, null);
        } catch (Throwable e) {
            e.printStackTrace(); // just log whatever problem we encounter
        }

        // Update the editor state to indicate that the contents have been saved 
        // and notify all listeners about the change in state
        getCommandStack().markSaveLocation();
        firePropertyChange(PROP_DIRTY);

        editorSaving = false;
    }

    @Override
    public void doSaveAs() {
        SaveAsDialog dialog = new SaveAsDialog(getSite().getShell());
        dialog.setOriginalFile(((IFileEditorInput) getEditorInput()).getFile());
        dialog.open();
        IPath path = dialog.getResult();
        if (path == null) {
            return;
        }
        IFile iFile = ResourcesPlugin.getWorkspace().getRoot().getFile(path);
        super.setInput(new FileEditorInput(iFile));
        File file = iFile.getLocation().toFile();
        uri = URI.createFileURI(file.getAbsolutePath());
        doSave(null);
        // refresh the resource in the workspace to avoid 'Resource is out of 
        // sync with the file system' messages
        try {
            iFile.refreshLocal(IResource.DEPTH_ZERO, null);
            // we should probably select the file in the package explorer too
        } catch (Throwable e) {
            Status status = new Status(IStatus.ERROR, ID, "An exception occurred while saving the file", e);
            ErrorDialog.openError(getSite().getShell(), "Exception", e.getMessage(), status);
        }
        setPartName(iFile.getName());
        firePropertyChange(PROP_INPUT);
    }

    void fireActivePaletteToolChanged(SchemaEditor source, ToolEntry tool) {
        Assert.isTrue(source == this, "event fired by another editor, this is NOT expected");
        for (SchemaEditor anEditor : getAllEditorsForInput(getEditorInput())) {
            if (anEditor != source) {
                anEditor.activePaletteToolChanged(tool);
            }
        }
    }

    public Object getAdapter(@SuppressWarnings("rawtypes") Class type) {

        if (type == ZoomManager.class) {
            String key = ZoomManager.class.toString();
            return getGraphicalViewer().getProperty(key);
        } else if (type == IPropertySheetPage.class) {
            return new TabbedPropertySheetPage(this);
        } else if (type == IContentOutlinePage.class) {
            outlinePage = new OutlinePage(this);
            return outlinePage;
        } else if (type == CommandStack.class) {
            // the command stack is accessible by anybody else but only for executing commands -
            // implementing the IModelChangeListener is the preferred way to catch up with model 
            // changes
            Assert.isNotNull(getEditDomain(), "edit domain not yet set");
            return getCommandStack();
        } else if (type == GraphicalViewer.class) {
            return getGraphicalViewer();
        } else if (type == IModelChangeProvider.class) {
            return modelChangeDispatcher;
        } else if (type == ActionRegistry.class) {
            return getActionRegistry();
        } else if (type == DefaultEditDomain.class) {
            Assert.isNotNull(getEditDomain(), "edit domain not yet set");
            return getEditDomain();
        } else if (type == SelectionSynchronizer.class) {
            return getSelectionSynchronizer();
        }

        return super.getAdapter(type);
    }

    private List<SchemaEditor> getAllEditorsForInput(IEditorInput editorInput) {
        List<SchemaEditor> editors = new ArrayList<>();
        for (IWorkbenchWindow workbenchWindow : PlatformUI.getWorkbench().getWorkbenchWindows()) {
            for (IWorkbenchPage workbenchPage : workbenchWindow.getPages()) {
                for (IEditorReference editorReference : workbenchPage.getEditorReferences()) {
                    try {
                        if (editorReference.getEditorInput().equals(editorInput)) {
                            SchemaEditor schemaEditor = (SchemaEditor) editorReference.getEditor(true);
                            if (schemaEditor != null) {
                                editors.add(schemaEditor);
                            }
                        }
                    } catch (PartInitException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }
        return editors;
    }

    private SchemaEditor getFirstEditorForSameInput(IEditorInput editorInput) {
        for (IWorkbenchWindow workbenchWindow : PlatformUI.getWorkbench().getWorkbenchWindows()) {
            for (IWorkbenchPage workbenchPage : workbenchWindow.getPages()) {
                for (IEditorReference editorReference : workbenchPage.getEditorReferences()) {
                    try {
                        if (editorReference.getEditorInput().equals(editorInput)) {
                            SchemaEditor schemaEditor = (SchemaEditor) editorReference.getEditor(true);
                            if (schemaEditor != null && schemaEditor != this) {
                                return schemaEditor;
                            }
                        }
                    } catch (PartInitException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }
        return null;
    }

    @Override
    public String getContributorId() {
        return getSite().getId();
    }

    @Override
    protected Control getGraphicalControl() {
        return rulerComp;
    }

    @Override
    protected PaletteRoot getPaletteRoot() {

        if (palette != null) {
            return palette;
        }

        SchemaEditor firstEditorForSameInput = getFirstEditorForSameInput(getEditorInput());
        if (firstEditorForSameInput != null) {
            palette = firstEditorForSameInput.palette;
            return palette;
        }

        palette = new PaletteRoot();

        // selection tool
        ToolEntry tool = new PanningSelectionToolEntry();

        // label creation tool
        ImageDescriptor label16 = ImageDescriptor
                .createFromImage(Plugin.getDefault().getImage("icons/label16.GIF"));
        ImageDescriptor label24 = ImageDescriptor
                .createFromImage(Plugin.getDefault().getImage("icons/label24.GIF"));
        CombinedTemplateCreationEntry labelCreationTool = new CombinedTemplateCreationEntry("Label",
                "Add diagram label", new SimpleFactory(DiagramLabel.class), label16, label24);

        // chained set creation tool
        ImageDescriptor chainedSet16 = ImageDescriptor
                .createFromImage(Plugin.getDefault().getImage("icons/chainedSet16.gif"));
        ImageDescriptor chainedSet24 = ImageDescriptor
                .createFromImage(Plugin.getDefault().getImage("icons/chainedSet24.gif"));
        ConnectionCreationToolEntry chainedSetCreationTool = new ConnectionCreationToolEntry("Chained Set",
                "Add chained set", new SimpleFactory(IChainedSetPlaceHolder.class), chainedSet16, chainedSet24);
        chainedSetCreationTool.setToolProperty(AbstractTool.PROPERTY_UNLOAD_WHEN_FINISHED, true);

        // multiple-member set tool
        ImageDescriptor multipleMemberSet16 = ImageDescriptor
                .createFromImage(Plugin.getDefault().getImage("icons/multipleMemberSet16.gif"));
        ImageDescriptor multipleMemberSet24 = ImageDescriptor
                .createFromImage(Plugin.getDefault().getImage("icons/multipleMemberSet24.gif"));
        ConnectionCreationToolEntry multipleMemberSetCreationTool = new ConnectionCreationToolEntry(
                "Multiple-member Set", "Add member record type to chained set",
                new SimpleFactory(IMultipleMemberSetPlaceHolder.class), multipleMemberSet16, multipleMemberSet24);
        multipleMemberSetCreationTool.setToolProperty(AbstractTool.PROPERTY_UNLOAD_WHEN_FINISHED, true);

        // indexed set creation tool
        ImageDescriptor indexedSet16 = ImageDescriptor
                .createFromImage(Plugin.getDefault().getImage("icons/indexedSet16.gif"));
        ImageDescriptor indexedSet24 = ImageDescriptor
                .createFromImage(Plugin.getDefault().getImage("icons/indexedSet24.gif"));
        ConnectionCreationToolEntry indexedSetCreationTool = new ConnectionCreationToolEntry("Indexed Set",
                "Add user owned indexed set", new SimpleFactory(IIndexedSetPlaceHolder.class), indexedSet16,
                indexedSet24);
        indexedSetCreationTool.setToolProperty(AbstractTool.PROPERTY_UNLOAD_WHEN_FINISHED, true);

        // index creation tool
        ImageDescriptor index16 = ImageDescriptor
                .createFromImage(Plugin.getDefault().getImage("icons/index16.gif"));
        ImageDescriptor index24 = ImageDescriptor
                .createFromImage(Plugin.getDefault().getImage("icons/index24.gif"));
        CombinedTemplateCreationEntry indexCreationTool = new CombinedTemplateCreationEntry("Index",
                "Add index to record", new SimpleFactory(SystemOwner.class), index16, index24);

        // connector creation tool  
        ImageDescriptor connector16 = ImageDescriptor
                .createFromImage(Plugin.getDefault().getImage("icons/connector16.GIF"));
        ImageDescriptor connector24 = ImageDescriptor
                .createFromImage(Plugin.getDefault().getImage("icons/connector24.GIF"));
        CombinedTemplateCreationEntry connectorCreationTool = new CombinedTemplateCreationEntry("Connector",
                "Add connectors to connection", new SimpleFactory(Connector.class), connector16, connector24);

        // Tools toolbar
        PaletteToolbar toolbar = new PaletteToolbar("Tools");
        toolbar.add(tool);
        toolbar.add(new MarqueeToolEntry());
        palette.add(toolbar);

        // General drawer
        PaletteDrawer createGeneralItemsDrawer = new PaletteDrawer("General");
        createGeneralItemsDrawer.add(labelCreationTool);
        palette.add(createGeneralItemsDrawer);

        // Sets drawer
        PaletteDrawer createSetItemsDrawer = new PaletteDrawer("Sets");
        createSetItemsDrawer.add(chainedSetCreationTool);
        createSetItemsDrawer.add(multipleMemberSetCreationTool);
        createSetItemsDrawer.add(indexedSetCreationTool);
        createSetItemsDrawer.add(indexCreationTool);
        createSetItemsDrawer.add(connectorCreationTool);
        palette.add(createSetItemsDrawer);

        // the selection tool is the default entry
        palette.setDefaultEntry(tool);

        return palette;
    }

    public Schema getSchema() {
        return schema;
    }

    @Override
    protected SelectionSynchronizer getSelectionSynchronizer() {
        if (selectionSynchronizer == null)
            selectionSynchronizer = new SelectionSynchronizer() {

                @Override
                protected EditPart convert(EditPartViewer viewer, EditPart part) {
                    if (outlinePage != null && outlinePage.canConvertEditPart(viewer, part)) {
                        // make sure the most relevant edit part is selected in the outline page
                        return outlinePage.convert(viewer, part);
                    } else {
                        // this request is not for the outline page, so have the standard selection
                        // synchronizer's pick the edit part
                        return super.convert(viewer, part);
                    }
                }
            };
        return selectionSynchronizer;
    }

    private void hookActivePaletteViewerToEditDomain() {
        // unless the workbench is closing, we need to make sure that editors for the same diagram
        // (editor input) have a functioning palette; we need to hook an active palette viewer to 
        // the edit domain 
        if (!PlatformUI.getWorkbench().isClosing()) {
            SchemaEditor editor = getFirstEditorForSameInput(getEditorInput());
            if (editor != null) {
                ((ModifiedPaletteViewerProvider) editor.getPaletteViewerProvider()).hookPaletteViewer();
            }
        }
    }

    @Override
    protected void initializeGraphicalViewer() {
        super.initializeGraphicalViewer();
        getGraphicalViewer().setContents(schema);
    }

    @Override
    public boolean isDirty() {
        // if the workbench is NOT closing, refer to the super's method
        if (!PlatformUI.getWorkbench().isClosing()) {
            return super.isDirty();
        }
        // make sure we get to see exactly 1 line if the editor is dirty, and not more
        List<SchemaEditor> openEditors = getAllEditorsForInput(getEditorInput());
        if (openEditors.isEmpty() || openEditors.get(0) == this) {
            return super.isDirty();
        } else {
            return false;
        }
    }

    @Override
    public boolean isSaveAsAllowed() {
        return true;
    }

    private boolean performSaveAs() {
        SaveAsDialog dialog = new SaveAsDialog(getSite().getWorkbenchWindow().getShell());
        dialog.setOriginalFile(((IFileEditorInput) getEditorInput()).getFile());
        dialog.open();
        IPath path = dialog.getResult();

        if (path == null) {
            return false;
        }

        IWorkspace workspace = ResourcesPlugin.getWorkspace();
        final IFile file = workspace.getRoot().getFile(path);

        if (!file.exists()) {
            WorkspaceModifyOperation op = new WorkspaceModifyOperation() {
                public void execute(final IProgressMonitor monitor) {
                    // Serialize the model
                    ResourceSet resourceSet = new ResourceSetImpl();
                    URI tmpURI = URI.createFileURI(file.getLocation().toFile().getAbsolutePath());
                    Resource resource = resourceSet.createResource(tmpURI);
                    resource.getContents().add(schema);
                    try {
                        resource.save(null);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            };
            try {
                new ProgressMonitorDialog(getSite().getWorkbenchWindow().getShell()).run(false, true, op);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        try {
            try {
                IContainer container = file.getParent();
                if (container != null) {
                    container.refreshLocal(IResource.DEPTH_INFINITE, null);
                }
            } catch (Throwable e) {
                e.printStackTrace(); // just log whatever problem we encounter
            }
            superSetInput(new FileEditorInput(file));
            try {
                if (workspaceResource != null) {
                    workspaceResource.refreshLocal(IResource.DEPTH_ZERO, null);
                }
            } catch (Throwable e) {
                e.printStackTrace(); // just log whatever problem we encounter
            }
            getCommandStack().markSaveLocation();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return true;
    }

    protected void setInput(IEditorInput input) {

        Plugin.logDebug(Plugin.DebugItem.CALLING_METHOD);

        superSetInput(input);

        if (schema != null) {
            // Always keep the same Schema object to avoid trouble (note that if the file was
            // overwritten with something else, there is a mismatch between the Schema object and
            // the file contents).  We can't show a message because there is a chance that the user
            // will get to see it while importing a schema.
            /*MessageDialog.openWarning(getSite().getShell(), "Warning", "The file holding the " +
                           "diagram for schema " + schema.getName() + " version " +
                           schema.getVersion() + " (" + getTitle() + ") seems to have " +
                           "changed; these changes are NOT reflected in the diagram, " +
                           "so make sure you fix this situation.");*/
            return;
        }

        SchemaEditor firstEditorForSameInput = getFirstEditorForSameInput(input);
        if (firstEditorForSameInput != null) {
            setEditDomain(firstEditorForSameInput.getEditDomain());
            schema = firstEditorForSameInput.getSchema();
        } else {
            setEditDomain(new DefaultEditDomain(null));
            ResourceSet resourceSet = new ResourceSetImpl();
            resourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put("schema",
                    new XMIResourceFactoryImpl());
            Resource resource = resourceSet.getResource(uri, true);
            schema = (Schema) resource.getContents().get(0);
        }

        if (!editorSaving) {
            if (outlinePage != null) {
                outlinePage.setSchema(schema);
            }
        }

    }

    @Override
    protected void setSite(IWorkbenchPartSite site) {
        super.setSite(site);
        getSite().getWorkbenchWindow().getPartService().addPartListener(partListener);
    }

    private void superSetInput(IEditorInput input) {
        // The workspace never changes for an editor. So, removing and re-adding the
        // resourceListener is not necessary. But it is being done here for the sake of proper 
        // implementation. Plus, the resourceListener needs to be added to the workspace the first 
        // time around.
        if (getEditorInput() != null) {
            IFile file = ((IFileEditorInput) getEditorInput()).getFile();
            file.getWorkspace().removeResourceChangeListener(resourceListener);
        }

        super.setInput(input);

        if (getEditorInput() != null) {
            IFile file = ((IFileEditorInput) getEditorInput()).getFile();
            workspaceResource = ResourcesPlugin.getWorkspace().getRoot().findMember(file.getFullPath());
            uri = URI.createFileURI(file.getLocation().toFile().getAbsolutePath());
            file.getWorkspace().addResourceChangeListener(resourceListener);
            setPartName(file.getName());
        }
    }

}