edu.buffalo.cse.green.editor.DiagramEditor.java Source code

Java tutorial

Introduction

Here is the source code for edu.buffalo.cse.green.editor.DiagramEditor.java

Source

/* This file is part of Green.
 *
 * Copyright (C) 2005 The Research Foundation of State University of New York
 * All Rights Under Copyright Reserved, The Research Foundation of S.U.N.Y.
 * 
 * Green is free software, licensed under the terms of the Eclipse
 * Public License, version 1.0.  The license is available at
 * http://www.eclipse.org/legal/epl-v10.html
 */

package edu.buffalo.cse.green.editor;

import static edu.buffalo.cse.green.GreenException.GRERR_FILE_FORMAT;
import static edu.buffalo.cse.green.constants.PaletteStrings.CREATE_RELATIONSHIP_PREFIX_DESCRIPTION;
import static edu.buffalo.cse.green.constants.PaletteStrings.CREATE_RELATIONSHIP_SUFFIX_DESCRIPTION;
import static edu.buffalo.cse.green.constants.PaletteStrings.GROUP_CREATE_RELATIONSHIPS_LABEL;
import static edu.buffalo.cse.green.constants.PaletteStrings.GROUP_CREATE_TYPE_LABEL;
import static edu.buffalo.cse.green.constants.PaletteStrings.GROUP_MAIN_LABEL;
import static edu.buffalo.cse.green.constants.PaletteStrings.GROUP_VISIBILITY_LABEL;
import static edu.buffalo.cse.green.constants.PaletteStrings.NOTE_DESCRIPTION;
import static edu.buffalo.cse.green.constants.PaletteStrings.NOTE_LABEL;
import static edu.buffalo.cse.green.constants.PaletteStrings.SELECTION_DESCRIPTION;
import static edu.buffalo.cse.green.constants.PaletteStrings.SELECTION_LABEL;
import static edu.buffalo.cse.green.constants.PaletteStrings.TOGGLEFISH_DESCRIPTION;
import static edu.buffalo.cse.green.constants.PaletteStrings.TOGGLEFISH_LABEL;
import static edu.buffalo.cse.green.constants.XMLConstants.XML_BENDPOINT;
import static edu.buffalo.cse.green.constants.XMLConstants.XML_BENDPOINTS;
import static edu.buffalo.cse.green.constants.XMLConstants.XML_BENDPOINT_X;
import static edu.buffalo.cse.green.constants.XMLConstants.XML_BENDPOINT_Y;
import static edu.buffalo.cse.green.constants.XMLConstants.XML_GREEN_VERSION;
import static edu.buffalo.cse.green.constants.XMLConstants.XML_NOTE;
import static edu.buffalo.cse.green.constants.XMLConstants.XML_NOTE_HEIGHT;
import static edu.buffalo.cse.green.constants.XMLConstants.XML_NOTE_TEXT;
import static edu.buffalo.cse.green.constants.XMLConstants.XML_NOTE_WIDTH;
import static edu.buffalo.cse.green.constants.XMLConstants.XML_NOTE_X;
import static edu.buffalo.cse.green.constants.XMLConstants.XML_NOTE_Y;
import static edu.buffalo.cse.green.constants.XMLConstants.XML_RELATIONSHIP;
import static edu.buffalo.cse.green.constants.XMLConstants.XML_RELATIONSHIP_CLASS;
import static edu.buffalo.cse.green.constants.XMLConstants.XML_RELATIONSHIP_SOURCE_PROJECT;
import static edu.buffalo.cse.green.constants.XMLConstants.XML_RELATIONSHIP_SOURCE_TYPE;
import static edu.buffalo.cse.green.constants.XMLConstants.XML_RELATIONSHIP_TARGET_PROJECT;
import static edu.buffalo.cse.green.constants.XMLConstants.XML_RELATIONSHIP_TARGET_TYPE;
import static edu.buffalo.cse.green.constants.XMLConstants.XML_TYPE;
import static edu.buffalo.cse.green.constants.XMLConstants.XML_TYPE_HEIGHT;
import static edu.buffalo.cse.green.constants.XMLConstants.XML_TYPE_NAME;
import static edu.buffalo.cse.green.constants.XMLConstants.XML_TYPE_PROJECT;
import static edu.buffalo.cse.green.constants.XMLConstants.XML_TYPE_WIDTH;
import static edu.buffalo.cse.green.constants.XMLConstants.XML_TYPE_X;
import static edu.buffalo.cse.green.constants.XMLConstants.XML_TYPE_Y;
import static edu.buffalo.cse.green.constants.XMLConstants.XML_UML;
import static edu.buffalo.cse.green.preferences.PreferenceInitializer.P_AUTOSAVE;
import static edu.buffalo.cse.green.preferences.PreferenceInitializer.P_DISPLAY_INCREMENTAL_EXPLORER_DIA;
import static edu.buffalo.cse.green.preferences.PreferenceInitializer.P_FORCE_DIA_IN_PROJECT;
import static edu.buffalo.cse.green.preferences.PreferenceInitializer.P_MANHATTAN_ROUTING;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.PrintWriter;
import java.lang.reflect.Constructor;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EventObject;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.Vector;
import java.util.regex.Pattern;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IncrementalProjectBuilder;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.draw2d.BendpointConnectionRouter;
import org.eclipse.draw2d.ConnectionRouter;
import org.eclipse.draw2d.ManhattanConnectionRouter;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.gef.DefaultEditDomain;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.EditPartFactory;
import org.eclipse.gef.KeyHandler;
import org.eclipse.gef.KeyStroke;
import org.eclipse.gef.MouseWheelHandler;
import org.eclipse.gef.MouseWheelZoomHandler;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.commands.CommandStackListener;
import org.eclipse.gef.editparts.ScalableFreeformRootEditPart;
import org.eclipse.gef.editparts.ZoomManager;
import org.eclipse.gef.palette.CombinedTemplateCreationEntry;
import org.eclipse.gef.palette.CreationToolEntry;
import org.eclipse.gef.palette.PaletteEntry;
import org.eclipse.gef.palette.PaletteGroup;
import org.eclipse.gef.palette.PaletteListener;
import org.eclipse.gef.palette.PaletteRoot;
import org.eclipse.gef.palette.PaletteStack;
import org.eclipse.gef.palette.PanningSelectionToolEntry;
import org.eclipse.gef.palette.ToolEntry;
import org.eclipse.gef.requests.BendpointRequest;
import org.eclipse.gef.requests.SimpleFactory;
import org.eclipse.gef.tools.ConnectionCreationTool;
import org.eclipse.gef.ui.actions.ActionRegistry;
import org.eclipse.gef.ui.actions.ZoomInAction;
import org.eclipse.gef.ui.actions.ZoomOutAction;
import org.eclipse.gef.ui.palette.FlyoutPaletteComposite;
import org.eclipse.gef.ui.palette.PaletteViewer;
import org.eclipse.gef.ui.parts.GraphicalEditorWithFlyoutPalette;
import org.eclipse.gef.ui.parts.GraphicalViewerKeyHandler;
import org.eclipse.gef.ui.parts.ScrollingGraphicalViewer;
import org.eclipse.jdt.core.IClassFile;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaModel;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMember;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.internal.ui.JavaPluginImages;
import org.eclipse.jface.action.ActionContributionItem;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IContributionItem;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.IPathEditorInput;
import org.eclipse.ui.IPersistableElement;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.editors.text.ILocationProvider;
import org.eclipse.ui.ide.FileStoreEditorInput;
import org.eclipse.ui.ide.IDE;
import org.eclipse.ui.model.IWorkbenchAdapter;
import org.eclipse.ui.model.WorkbenchAdapter;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.views.contentoutline.IContentOutlinePage;

import edu.buffalo.cse.green.GreenException;
import edu.buffalo.cse.green.PlugIn;
import edu.buffalo.cse.green.constants.PluginConstants;
import edu.buffalo.cse.green.editor.action.ContextAction;
import edu.buffalo.cse.green.editor.action.Submenu;
import edu.buffalo.cse.green.editor.controller.AbstractPart;
import edu.buffalo.cse.green.editor.controller.RelationshipPart;
import edu.buffalo.cse.green.editor.controller.RootPart;
import edu.buffalo.cse.green.editor.model.AbstractModel;
import edu.buffalo.cse.green.editor.model.MemberModel;
import edu.buffalo.cse.green.editor.model.NoteModel;
import edu.buffalo.cse.green.editor.model.RelationshipModel;
import edu.buffalo.cse.green.editor.model.RootModel;
import edu.buffalo.cse.green.editor.model.TypeModel;
import edu.buffalo.cse.green.editor.model.commands.CreateBendpointCommand;
import edu.buffalo.cse.green.editor.save.ISaveFormat;
import edu.buffalo.cse.green.editor.view.RelationshipFigure;
import edu.buffalo.cse.green.editor.view.RootFigure;
import edu.buffalo.cse.green.relationships.RelationshipCache;
import edu.buffalo.cse.green.relationships.RelationshipGroup;
import edu.buffalo.cse.green.relationships.RelationshipRecognizer;
import edu.buffalo.cse.green.relationships.RelationshipSubtype;
import edu.buffalo.cse.green.types.ITypeProperties;
import edu.buffalo.cse.green.xml.XMLConverter;
import edu.buffalo.cse.green.xml.XMLNode;

/**
 * The editor. Displays a UML diagram that represents all the parts of the
 * <code>JavaModel</code> that have been loaded into it. New projects,
 * packages, compilation units, and types can be loaded in. Methods and fields,
 * however, cannot be added in without their parent <code>IType</code>.
 * 
 * @author bcmartin
 * @author hk47
 * @author zgwang
 */

public class DiagramEditor extends GraphicalEditorWithFlyoutPalette
        implements CommandStackListener, ISelectionProvider {
    static {
        _editors = new ArrayList<DiagramEditor>();
    }

    //   private boolean _ignoreMenuSelection = false;

    /**
     * Reference string for the context menu in our editor.
     */
    private static final String UML_CONTEXT_MENU_ID = "#PopupMenu";

    /**
     * A list of all editors currently open.
     */
    private static List<DiagramEditor> _editors;

    /**
     * A list of listeners to the selections in our editor.
     */
    private static ListenerList _selectionChangedListeners = new ListenerList();

    /**
     * The current selection in our editor.
     */
    private IStructuredSelection _selection = StructuredSelection.EMPTY;

    /**
     * Handler for shortcut key presses.
     */
    private KeyHandler _sharedKeyHandler;

    /**
     * Stores a reference to the active editor.
     */
    private static DiagramEditor ACTIVE_EDITOR;

    /**
     * The top-level model displayed in the diagram.
     */
    private RootModel _root;

    /**
     * A reference to the context menu's manager.
     */
    private IMenuManager _menuManager;

    /**
     * Information contained in the current selection.
     */
    private Context _context;

    /**
     * A list of relationships that have changed and have not yet been
     * processed.
     */
    private Set<RelationshipModel> _relationshipChanges;

    /**
     * A list of bendpoints that should be added when loading is complete and
     * the root part is available.
     */
    private List<BendpointInformation> _bendpoints;
    private CompilationUnitMap _cuMap;
    private MenuManager _contextMenu;
    private List<Filter> _filters;
    private static ConnectionRouter CONNECTION_ROUTER;
    private static OutlinePage _outlinePage;

    /**
     * GEF's "root part" different from green's RootPart
     */
    private ScalableFreeformRootEditPart _gefRootPart;

    /**
     * Constructs an instance of the editor.
     */
    public DiagramEditor() {
        updateConnectionRouter();
        _editors.add(this);
        _bendpoints = new ArrayList<BendpointInformation>();
        setEditDomain(new DefaultEditDomain(this));
        getCommandStack().addCommandStackListener(this);
        getCommandStack().setUndoLimit(100);
        _root = new RootModel(); // no project?
        _cuMap = new CompilationUnitMap();
        _filters = new ArrayList<Filter>();
        getPalettePreferences().setPaletteState(FlyoutPaletteComposite.STATE_PINNED_OPEN);
    }

    public Object getAdapter(Class adapter) {
        if (IContentOutlinePage.class.equals(adapter)) {
            return OutlinePage.getInstance();
        }

        return super.getAdapter(adapter);
    }

    /**
     * Updates the connection router based on the user's preference.
     */
    private void updateConnectionRouter() {
        if (PlugIn.getBooleanPreference(P_MANHATTAN_ROUTING)) {
            CONNECTION_ROUTER = new ManhattanConnectionRouter();
        } else {
            CONNECTION_ROUTER = new BendpointConnectionRouter();
        }
    }

    /**
     * @return The selection tool's entry in the palette.
     */
    public static ToolEntry getSelectionTool() {
        return DiagramPaletteFactory.getSelectionTool();
    }

    /**
     * Creates the editor's context menu.
     */
    private void buildMenu(IMenuManager menuManager) {
        Map<String, MenuManager> menus = new HashMap<String, MenuManager>();
        Map<MenuManager, List<ContextAction>> mActions = new HashMap<MenuManager, List<ContextAction>>();

        MenuManager inv = new MenuManager("inv");
        inv.setVisible(false);
        menus.put(Submenu.Invisible.toString(), inv);

        // get all actions that must be added to the menu
        List<ContextAction> actions = new ArrayList<ContextAction>();

        actions.addAll(PlugIn.getActions());

        // moved to PlugIn, after the xml plugins are initialized
        // so that accelerator keys work.
        /*for (Class partClass : PlugIn.getRelationships()) {
           ContextAction action =
        new AlterRelationshipVisibilityAction(partClass);
           actions.add(action);
           action =
        new IncrementalExploreSingleAction(partClass);
           actions.add(action);
        }*/

        // add the actions to their appropriate submenus
        List<ContextAction> lastItems = new ArrayList<ContextAction>();

        for (ContextAction action : actions) {
            // add in menu group if it doesn't exist
            MenuManager submenu = menus.get(action.getPath());

            // initialize the action
            action.calculateEnabled();
            action.setSelectionProvider(this);
            action.setText(action.getLabel());

            // if the submenu doesn't exist, create it
            if (submenu == null) {
                submenu = createMenuGroup(menus, action.getPath());

                // add it to the end if it's a menu item
                if (submenu == null) {
                    lastItems.add(action);
                    continue;
                }

                List<ContextAction> actionList = mActions.get(submenu);

                if (actionList == null) {
                    actionList = new ArrayList<ContextAction>();
                    mActions.put(submenu, actionList);
                }

                actionList.add(action);
            }
        }

        // sort the menu alphabetically
        while (mActions.keySet().size() > 0) {
            String firstAlpha = "zzz";
            MenuManager addMenu = null;

            for (MenuManager menu : mActions.keySet()) {
                if (menu.getMenuText().compareTo(firstAlpha) < 0) {
                    firstAlpha = menu.getMenuText();
                    addMenu = menu;
                }
            }

            _contextMenu.add(addMenu);
            List<ContextAction> actionList = mActions.get(addMenu);
            mActions.remove(addMenu);

            while (actionList.size() > 0) {
                firstAlpha = "zzz";
                ContextAction addAction = null;

                for (ContextAction cAction : actionList) {
                    if (cAction.getLabel().compareTo(firstAlpha) < 0) {
                        firstAlpha = cAction.getLabel();
                        addAction = cAction;
                    }
                }

                addMenu.add(addAction);
                actionList.remove(addAction);
            }
        }

        while (lastItems.size() > 0) {
            String firstAlpha = "zzz";
            ContextAction addAction = null;

            for (ContextAction cAction : lastItems) {
                if (cAction.getLabel().compareTo(firstAlpha) < 0) {
                    firstAlpha = cAction.getLabel();
                    addAction = cAction;
                }
            }

            _contextMenu.add(addAction);
            lastItems.remove(addAction);
        }

        // add quick fixes, if applicable
        IJavaElement element = getContext().getElement();

        if (element instanceof IMember) {
            MemberModel model = (MemberModel) getRootModel().getModelFromElement(element);
            model.appendQuickFixActionsToMenu(_contextMenu);
        }
    }

    /**
     * Creates and returns a menu with the given path.
     * 
     * @param menus - A map of menu prefixes to their corresponding menus.
     * @param path - The path to the desired menu.
     * @return The appropriate menu.
     */
    private MenuManager createMenuGroup(Map<String, MenuManager> menus, Submenu path) {
        StringTokenizer tokens = new StringTokenizer(path.toString(), "/");
        if (!tokens.hasMoreTokens())
            return null;

        // create the base menu
        String menuName = tokens.nextToken();
        MenuManager menu = createMenuGroupHelper(menus, menuName);

        // nest menus using the path
        while (tokens.hasMoreTokens()) {
            menuName += "/" + tokens.nextToken();
            MenuManager submenu = createMenuGroupHelper(menus, menuName);
            menu.add(submenu);
            menu = submenu;
        }

        return menu;
    }

    /**
     * Creates menus if they don't exist until the desired menu path exists.
     * 
     * @param menus - The map of menu prefixes to their corresponding menus.
     * @param menuName - The name of the menu to create.
     * @return The created menu, or the menu if it already existed.
     */
    private MenuManager createMenuGroupHelper(Map<String, MenuManager> menus, String menuName) {
        MenuManager menu = menus.get(menuName);

        if (menu == null) {
            menu = new MenuManager(menuName.substring(menuName.lastIndexOf("/") + 1));
            menus.put(menuName, menu);
        }

        return menu;
    }

    /**
     * Updates the context menu's contents, hiding inappropriate contents.
     * 
     * @param menu - The menu to update.
     */
    private void updateMenuDisplay(IMenuManager menu) {
        AbstractModel selectedModel = _context.getModel();

        for (IContributionItem item : menu.getItems()) {
            if (item instanceof IMenuManager) {
                updateMenuDisplay((IMenuManager) item);
            } else if (item instanceof ActionContributionItem) {
                ActionContributionItem aItem = (ActionContributionItem) item;
                ContextAction action = (ContextAction) aItem.getAction();
                aItem.setVisible(action.isVisible(selectedModel));
            }
        }
    }

    /**
     * Finds the editor that holds the given <code>IJavaProject</code>.
     * 
     * @param project - The project to find.
     */
    public static DiagramEditor findProjectEditor(IJavaProject project) {
        for (int x = 0; x < _editors.size(); x++) {
            DiagramEditor editor = (DiagramEditor) _editors.get(x);
            IJavaProject editorProject = editor.getProject();
            if (editorProject == null)
                continue;

            if (project.getHandleIdentifier().equals(editorProject.getHandleIdentifier())) {
                return editor;
            }
        }

        return null;
    }

    /**
     * Returns the project that the current <code>DiagramEditor</code> is
     * displaying the contents of.
     * 
     * @return The project that's currenly displayed in the editor.
     */
    public IJavaProject getProject() {
        return getRootModel().getProject();
    }

    /**
     * @see org.eclipse.ui.IWorkbenchPart#dispose()
     */
    public void dispose() {
        try {
            markAsSaved();
        } catch (Throwable t) {
            // don't care
        }

        _editors.remove(this);
        getRootModel().dispose();
        if (ACTIVE_EDITOR == this)
            ACTIVE_EDITOR = null;

        super.dispose();
    }

    /**
     * Adds a bendpoint to a relationship at the given location.
     * 
     * @param rModel - The <code>RelationshipsModel</code>.
     * @param location - The location of the bendpoint.
     */
    public void addBendpoint(RelationshipModel rModel, Point location) {
        _bendpoints.add(new BendpointInformation(rModel, location));
    }

    /**
     * @see org.eclipse.ui.ISaveablePart#doSave(org.eclipse.core.runtime.IProgressMonitor)
     */
    public void doSave(IProgressMonitor iMonitor) {
        DiagramEditorFilePolicies.save(this, false);
    }

    /**
     * @see org.eclipse.ui.ISaveablePart#doSaveAs()
     */
    public void doSaveAs() {
        DiagramEditorFilePolicies.save(this, true);
    }

    /**
     * @see org.eclipse.ui.IEditorPart#init(org.eclipse.ui.IEditorSite, org.eclipse.ui.IEditorInput)
     */
    public void init(IEditorSite iSite, IEditorInput iInput) throws PartInitException {
        super.init(iSite, iInput);

        if (iInput instanceof FileStoreEditorInput) {
            URI resourceID = ((FileStoreEditorInput) iInput).getURI();
            setEditorInput(new GreenEditorInput(new File(resourceID)));
        }

        setPartName(getEditorInput().getName());

        // set global actions
        IActionBars bars = getEditorSite().getActionBars();

        for (IContributionItem item : bars.getToolBarManager().getItems()) {
            if (item instanceof ActionContributionItem) {
                // set action handlers (shortcut keys) as appropriate
                ActionContributionItem aItem = (ActionContributionItem) item;
                ContextAction action = (ContextAction) aItem.getAction();
                bars.setGlobalActionHandler(action.getGlobalActionHandler().getId(), action);
            }
        }

        bars.updateActionBars();
    }

    /**
     * @see org.eclipse.gef.ui.parts.GraphicalEditor#initializeGraphicalViewer()
     */
    protected void initializeGraphicalViewer() {
        // associate Green's model and controller with the editor
        getGraphicalViewer().setContents(getRootModel());
        getRootPart().setEditor(this);

        // get the input file to the editor and handle loading from it
        IPathEditorInput input = (IPathEditorInput) getEditorInput();
        DiagramEditorFilePolicies.load(this, new File(input.getPath().toOSString()));

        createContextMenu();

        // create bendpoints (saved) on relationship arcs
        for (BendpointInformation bendpoint : _bendpoints) {
            BendpointRequest request = bendpoint.getBendpointRequest(this);
            if (request == null)
                continue;

            RelationshipFigure rFigure = (RelationshipFigure) request.getSource().getFigure();

            execute(new CreateBendpointCommand(rFigure, request));
        }

        // pretend the editor is saved
        markAsSaved();

        //This code have been replaced with the addition of the flyout
        //palette and PaletteStacks
        //@author zgwang
        //      
        //      getPaletteViewer().addPaletteListener(new PaletteListener() {
        //         /**
        //          * @see org.eclipse.gef.palette.PaletteListener#activeToolChanged(org.eclipse.gef.ui.palette.PaletteViewer, org.eclipse.gef.palette.ToolEntry)
        //          */
        //         public void activeToolChanged(final PaletteViewer palette,
        //               final ToolEntry tool) {
        //            if (!PlugIn.isUserMode()) return;
        //            
        //            final List<RelationshipSubtype> subtypes =
        //               PlugIn.getRelationshipSubtypes(tool.getLabel());
        //            
        //            if (subtypes == null) return;
        //            
        //            if (subtypes.size() < 2) return;
        //            
        //            if (_ignoreMenuSelection) {
        //               _ignoreMenuSelection = false;
        //               return;
        //            }
        //            
        //            Menu menu = new Menu(palette.getControl());
        //            
        //            /* If a relationship with more than one subtype exists, we want
        //             * to display a context menu when the palette tool is clicked so
        //             * that the user can select which subtype to create.
        //             */
        //            for (final RelationshipSubtype subtype : subtypes) {
        //               MenuItem item = new MenuItem(menu, 0);
        //               item.setText(subtype.getLabel());
        //               item.addSelectionListener(new SelectionListener() {
        //                  /**
        //                   * @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent)
        //                   */
        //                  public void widgetSelected(SelectionEvent e) {
        //                     tool.setToolProperty(
        //                           CreationTool.PROPERTY_CREATION_FACTORY,
        //                           new SimpleFactory(
        //                                 subtype.getGroup().getPartClass()));
        //                     _ignoreMenuSelection = true;
        //                     palette.setActiveTool(tool);
        //                  }
        //
        //                  /**
        //                   * @see org.eclipse.swt.events.SelectionListener#widgetDefaultSelected(org.eclipse.swt.events.SelectionEvent)
        //                   */
        //                  public void widgetDefaultSelected(SelectionEvent e) {}
        //               });
        //            }
        //            
        //            // display the menu
        //            menu.setLocation(getSite().getShell().getDisplay().getCursorLocation());
        //            menu.setVisible(true);
        //         }
        //      });

        // save the editor
        doSave(null);

        // makes the palette buttons work
        final PaletteViewer v = getGraphicalViewer().getEditDomain().getPaletteViewer();
        getGraphicalViewer().getEditDomain().getPaletteViewer().addPaletteListener(new PaletteListener() {

            private ToolEntry _lastEntry = v.getActiveTool();

            public void activeToolChanged(PaletteViewer palette, ToolEntry tool) {
                if (tool instanceof PaletteButton) {
                    ((PaletteButton) tool).act();
                    v.setActiveTool(_lastEntry);
                } else
                    _lastEntry = tool;
                //            Shell sh = getSite().getShell();
                //            sh.setCursor(new Cursor(sh.getDisplay(),SWT.CURSOR_UPARROW));
            }

        });

        //doSave(null);

    }

    /**
     * @return The <code>RootPart</code>
     */
    public RootPart getRootPart() {
        // return the graphical viewer, if it exists
        return getGraphicalViewer() == null ? null : (RootPart) getGraphicalViewer().getContents();
    }

    /**
     * @return The <code>UMLRootModel</code>.
     */
    public RootModel getRootModel() {
        return _root;
    }

    public void createContextMenu(IMenuManager m) {
        buildMenu(m);
        updateMenuDisplay(m);
        _menuManager = m;
        m.setVisible(true);
    }

    /**
     * Returns the list of currently open editors.
     */
    public static List<DiagramEditor> getEditors() {
        return _editors;
    }

    /**
     * @see org.eclipse.ui.ISaveablePart#isSaveAsAllowed()
     */
    public boolean isSaveAsAllowed() {
        return true;
    }

    /**
     * Creates the pop-up menu for right-click events.
     */
    private void createContextMenu() {
        _contextMenu = new MenuManager(UML_CONTEXT_MENU_ID);
        _contextMenu.setRemoveAllWhenShown(true);
        _contextMenu.addMenuListener(new IMenuListener() {
            /**
             * @see org.eclipse.jface.action.IMenuListener#menuAboutToShow(org.eclipse.jface.action.IMenuManager)
             */
            public void menuAboutToShow(IMenuManager m) {
                createContextMenu(m);
            }
        });

        // associate the control with the menu
        Control control = getGraphicalViewer().getControl();
        Menu menu = _contextMenu.createContextMenu(control);
        control.setMenu(menu);
    }

    /**
     * Gets the ZoomManager for this.
     * @return zoom manager
     */
    public ZoomManager getZoomManager() {
        return _gefRootPart.getZoomManager();
    }

    /**
     * @see org.eclipse.gef.ui.parts.GraphicalEditor#configureGraphicalViewer()
     */
    protected void configureGraphicalViewer() {
        super.configureGraphicalViewer();
        ScrollingGraphicalViewer viewer = (ScrollingGraphicalViewer) getGraphicalViewer();

        // Scroll-wheel Zoom
        getGraphicalViewer().setProperty(MouseWheelHandler.KeyGenerator.getKey(SWT.MOD1),
                MouseWheelZoomHandler.SINGLETON);

        // associate appropriate handlers with the viewer
        _gefRootPart = new ScalableFreeformRootEditPart();
        ZoomManager zoom = _gefRootPart.getZoomManager();
        List<String> zoomLevels = new ArrayList<String>(3);
        zoomLevels.add(ZoomManager.FIT_ALL);
        zoomLevels.add(ZoomManager.FIT_WIDTH);
        zoomLevels.add(ZoomManager.FIT_HEIGHT);
        zoom.setZoomLevelContributions(zoomLevels);
        IAction zoomIn = new ZoomInAction(_gefRootPart.getZoomManager());
        IAction zoomOut = new ZoomOutAction(_gefRootPart.getZoomManager());
        getActionRegistry().registerAction(zoomIn);
        getActionRegistry().registerAction(zoomOut);

        //These lines don't seem to do anything and will
        //remain commented out until an error occurs.
        //      getSite().getKeyBindingService().registerAction(zoomIn);
        //      getSite().getKeyBindingService().registerAction(zoomOut);

        viewer.setRootEditPart(_gefRootPart);
        viewer.setEditPartFactory(new DiagramPartFactory());
        viewer.setKeyHandler(new GraphicalViewerKeyHandler(viewer).setParent(getCommonKeyHandler()));

        ActionRegistry registry = getActionRegistry();
        List<ContextAction> actions = PlugIn.getActions();

        // register context menu actions
        for (ContextAction action : actions) {
            registry.registerAction(action);
            int accelerator = action.getAccelerator();

            // enabled shortcut keys where appropriate
            if (accelerator != 0) {
                addKeyAction((char) accelerator, action);
            }
        }
    }

    /**
     * Adds a shortcut key mapping to the editor.
     * 
     * @param key - The key to map.
     * @param action - The action to map the key to.
     */
    public void addKeyAction(char key, ContextAction action) {
        _sharedKeyHandler.put(KeyStroke.getPressed(key, key, 0), action);
    }

    /**
     * @see org.eclipse.gef.ui.parts.GraphicalEditorWithPalette#getPaletteRoot()
     */
    protected PaletteRoot getPaletteRoot() {
        PaletteRoot pRoot = DiagramPaletteFactory.createPaletteRoot();
        return pRoot;
    }

    /**
     * @see org.eclipse.ui.IWorkbenchPart#setFocus()
     */
    public void setFocus() {
        ACTIVE_EDITOR = this;

        for (ContextAction action : PlugIn.getActions()) {
            action.setSelectionProvider(this);
        }

        autoSave();
    }

    /**
     * Creates a new editor using the given selection.
     * 
     * @param selection - The selection of elements to add to the editor.
     * @return The editor that was created.
     */
    public static DiagramEditor createEditor(IStructuredSelection selection) throws JavaModelException {

        for (Object element : selection.toArray()) {
            if (element instanceof IJavaElement) {
                return createEditor((IJavaElement) element);
            }
        }

        return null;
    }

    /**
     * Opens a blank editor.
     * 
     * @param element - The element to place the diagram file in.
     * @return a reference to the opened editor, if successful.
     */
    private static DiagramEditor createEditor(IJavaElement element) throws JavaModelException {
        IWorkbenchWindow dwindow = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
        IWorkbenchPage workbenchPage = dwindow.getActivePage();
        IPackageFragment packFrag = null;
        IPath elementPath;

        // get the project itself, if desired (for creating DIA)
        if (element.isReadOnly() || PlugIn.getBooleanPreference(P_FORCE_DIA_IN_PROJECT)) {
            element = element.getAncestor(IJavaElement.JAVA_PROJECT);
        }

        if (element instanceof IJavaProject) {
            IJavaProject project = (IJavaProject) element;
            packFrag = project.getPackageFragments()[0];
        } else if (!(element instanceof IPackageFragment)) {
            packFrag = (IPackageFragment) element.getAncestor(IJavaElement.PACKAGE_FRAGMENT);
        } else {
            packFrag = (IPackageFragment) element;
        }

        // create a path to the diagram file with current extension
        elementPath = packFrag.getPath()
                .append(packFrag.getJavaProject().getElementName() + "." + PluginConstants.GREEN_EXTENSION);

        try {
            IFile diaFile = DiagramEditor.getFileNotExist(element.getJavaProject().getProject(), elementPath);
            DiagramEditor editor = (DiagramEditor) IDE.openEditor(workbenchPage, diaFile, true);
            ACTIVE_EDITOR = editor;
            return editor;
        } catch (CoreException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }

    // -Selection--------------------------------------------------------------------

    /**
     * @see org.eclipse.jface.viewers.ISelectionProvider#addSelectionChangedListener(ISelectionChangedListener listener)
     */
    public void addSelectionChangedListener(ISelectionChangedListener listener) {
        _selectionChangedListeners.add(listener);
    }

    /**
     * @see org.eclipse.jface.viewers.ISelectionProvider#getSelection()
     */
    public ISelection getSelection() {
        // return null since Eclipse uses this to add items to our context menu
        return null;
    }

    /**
     * @return information obtained from the current selection
     */
    public Context getContext() {
        return _context;
    }

    /**
     * @see org.eclipse.jface.viewers.ISelectionProvider#removeSelectionChangedListener(org.eclipse.jface.viewers.ISelectionChangedListener)
     */
    public void removeSelectionChangedListener(ISelectionChangedListener listener) {
        _selectionChangedListeners.remove(listener);
    }

    /**
     * @see org.eclipse.jface.viewers.ISelectionProvider#setSelection(org.eclipse.jface.viewers.ISelection)
     */
    public void setSelection(ISelection selection) {
        if (!(selection instanceof IStructuredSelection))
            return;

        // set the formerly selected part to its original color
        if (_context != null) {
            _context.getPart().setInitialBackgroundColor();
        }

        // set Green's context to the currently selected values
        IStructuredSelection sSelection = (IStructuredSelection) selection;
        if (!(sSelection.getFirstElement() instanceof AbstractPart)) {
            return;
        }

        _selection = sSelection;
        _context = new Context(_selection);

        AbstractPart part = _context.getPart();

        // set the selected part to the selection color
        if ((part != null) && (part.getParent() instanceof AbstractPart)) {
            part.setSelectedBackgroundColor();
        }
    }

    /**
     * @see org.eclipse.ui.ISelectionListener#selectionChanged(org.eclipse.ui.IWorkbenchPart, org.eclipse.jface.viewers.ISelection)
     */
    public void selectionChanged(IWorkbenchPart part, ISelection selection) {
        if (selection.isEmpty()) {
            return;
        }
        super.selectionChanged(part, selection);

        if (part instanceof DiagramEditor) {
            if (_outlinePage != null) {
                _outlinePage.setSelection(selection);
            }
        }

        setSelection(selection);
    }

    // ------------------------------------------------------------------------------
    /**
     * Singleton: gets the key handler for editors. This method is responsible
     * for all of the keyboard shortcuts handled by our editor.
     * 
     * @return The <code>KeyHandler</code>.
     */
    protected KeyHandler getCommonKeyHandler() {
        if (_sharedKeyHandler == null) {
            _sharedKeyHandler = new KeyHandler();
        }

        return _sharedKeyHandler;
    }

    /**
     * Executes a command in this editor.
     */
    public void execute(Command command) {
        getCommandStack().execute(command);
        refresh();
    }

    /**
     * @see org.eclipse.gef.commands.CommandStackListener#commandStackChanged(java.util.EventObject)
     */
    public void commandStackChanged(EventObject event) {
        super.commandStackChanged(event);
        checkDirty();
    }

    /**
     * Checks to see if there were any changes to the editor
     */
    public void checkDirty() {
        firePropertyChange(IEditorPart.PROP_DIRTY);
    }

    /**
     * @see org.eclipse.ui.ISaveablePart#isSaveOnCloseNeeded()
     */
    public boolean isSaveOnCloseNeeded() {
        return isDirty();
    }

    /**
     * @see org.eclipse.ui.ISaveablePart#isDirty()
     */
    public boolean isDirty() {
        return getCommandStack().isDirty();
    }

    /**
     * Creates a file at the specified path and returns it. If the file already
     * exists, a number will be concatenated to the path to create a new file.
     * 
     * @param project
     *            The Java project to create the file in
     * @param path
     *            The path of the file to create
     * @return The created file
     * @throws CoreException
     * @throws IOException
     */
    public static IFile getFileNotExist(IProject project, IPath path) throws CoreException, IOException {
        // create a new path to the file
        IFile file;
        String sPath = path.toOSString();
        int extensionIndex = sPath.lastIndexOf('.');
        String extension = sPath.substring(extensionIndex);
        sPath = sPath.substring(0, extensionIndex);

        int x = 1;

        // mutate the path until it is unique 
        do {
            IPath newPath;

            if (x == 1) {
                newPath = new Path(sPath + extension).removeFirstSegments(1);
            } else {
                newPath = new Path(sPath + "." + x + extension).removeFirstSegments(1);
            }

            file = project.getFile(newPath);
            x++;
        } while (file.isAccessible());

        // create the empty file
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());

        file.create(bais, true, null);
        oos.close();
        baos.close();
        bais.close();

        // notify the workspace that the file has changed
        file.refreshLocal(IResource.DEPTH_INFINITE, null);
        return file;
    }

    /**
     * Returns the file that is currently being edited. This will attempt to see
     * if Eclipse knows about the file. If not, the fallback file will be used
     * instead.
     * 
     * @return The <code>IFile</code> representing the file that is currently
     *         being edited.
     */
    public IFile getCurrentFile() {
        IEditorInput input = getEditorInput();

        if (input instanceof FileEditorInput) {
            FileEditorInput fInput = (FileEditorInput) input;
            return fInput.getFile();
        }

        return null;
    }

    /**
     * Sets the editor's input to the given input.
     * 
     * @param input - The editor's input.
     */
    public void setEditorInput(IEditorInput input) {
        setInput(input);
    }

    /**
     * @see org.eclipse.ui.part.WorkbenchPart#setPartName(java.lang.String)
     */
    public void setPartName(String partName) {
        super.setPartName(partName);
    }

    /**
     * Saves the given string to the current file 
     * 
     * @param contents - The text to save
     * @return true upon success, false otherwise
     */
    public boolean saveFile(String contents) {
        return saveFile(getCurrentFile(), contents);
    }

    /**
     * Refreshes the elements in the current editor
     */
    public void synchronizeCurrentFile() {
        try {
            if (getCurrentFile() == null)
                return;

            getCurrentFile().refreshLocal(IResource.DEPTH_INFINITE, PlugIn.getEmptyProgressMonitor());
        } catch (CoreException e) {
            e.printStackTrace();
        }
    }

    /**
     * Saves the given contents to the given file.
     * 
     * @param iFile - The file to save to.
     * @param contents - The text to save.
     * @return true upon success, false otherwise.
     */
    public boolean saveFile(IFile iFile, String contents) {
        File file = null;

        if (iFile != null) {
            // use the given file
            file = PlugIn.getWorkspaceRoot().getFile(iFile.getFullPath()).getLocation().toFile();
        } else {
            if (getEditorInput() instanceof GreenEditorInput) {
                // Green file format
                GreenEditorInput input = (GreenEditorInput) getEditorInput();
                file = input.getPath().toFile();
            } else {
                // Invalid format
                GreenException.illegalOperation("Illegal editor input type: " + getEditorInput().getClass());
            }
        }

        // write the contents into the file (perform the save)
        try {
            FileWriter fWriter = new FileWriter(file);
            PrintWriter pWriter = new PrintWriter(fWriter);
            pWriter.println(contents);
            pWriter.close();
            fWriter.close();
            synchronizeCurrentFile();
            return true;
        } catch (IOException e) {
            e.printStackTrace();
        }

        return false;
    }

    /**
     * Marks the current editor as being saved (not dirty).
     */
    public void markAsSaved() {
        getCommandStack().markSaveLocation();
        checkDirty();
    }

    /**
     * Refreshes the editor
     */
    public void refresh() {
        refresh(false);
    }

    /**
     * Refreshes the editor.
     * 
     * @param forceUpdateRelationships - Forces the updating of relationships.
     * @author zgwang
     */
    public void refresh(final boolean forceUpdateRelationships) {
        BusyIndicator.showWhile(Display.getCurrent(), new Runnable() {
            /**
             * @see java.lang.Runnable#run()
             */
            public void run() {
                // use Manhattan or normal routing, as desired
                updateConnectionRouter();

                try {
                    // build workspace, if necessary
                    if (getProject() != null) {
                        IWorkspace w = getProject().getUnderlyingResource().getWorkspace();
                        w.build(IncrementalProjectBuilder.INCREMENTAL_BUILD, null);
                    }
                } catch (CoreException e) {
                    GreenException.critical(e);
                }

                // MAYBE look for anonymous inner classes here ---???

                // refresh relationships
                refreshRelationships(forceUpdateRelationships);
                // refresh model
                getRootModel().refresh();
                // refresh figures
                List<DiagramEditor> allEditors = getEditors();
                for (DiagramEditor editor : allEditors) {
                    ((RootFigure) (editor.getRootPart().getFigure())).updateEditor();
                }
            }
        });

        if (_outlinePage != null) {
            _outlinePage.update(this);
        }
    }

    /**
     * @return true if an undo can be performed, false otherwise
     */
    public boolean canUndo() {
        return getCommandStack().canUndo();
    }

    /**
     * Performs an undo
     */
    public void undo() {
        if (!canUndo()) {
            GreenException.illegalOperation("Undo was unsuccessful");
        }

        getCommandStack().undo();
    }

    /**
     * @return true if a redo can be performed, false otherwise
     */
    public boolean canRedo() {
        return getCommandStack().canRedo();
    }

    /**
     * Performs a redo
     */
    public void redo() {
        if (!canRedo()) {
            GreenException.illegalOperation("Redo was unsuccessful");
        }

        getCommandStack().redo();
    }

    /**
     * @return a reference to the context menu's manager
     */
    public IMenuManager getMenuManager() {
        return _menuManager;
    }

    /**
     * Finds all relationships that have the given element as their source.
     * 
     * @param element - The element to find relationships for.
     */
    private void findRelationships(IJavaElement element) {
        long modified;

        // if the element contains errors, quit
        try {
            if (!element.exists() || !element.isStructureKnown())
                return;
        } catch (JavaModelException e) {
            e.printStackTrace();
            return;
        }

        CompilationUnit cu;
        String id = element.getHandleIdentifier();

        // generate AST if necessary - check modification stamp
        Long modifiedStore = _cuMap.getModificationStamp(id);
        IResource resource = element.getResource();

        if (resource == null) {
            if (_cuMap.getCompilationUnit(id) != null) {
                modifiedStore = new Long(0);
            }

            modified = 0;
        } else {
            modified = resource.getModificationStamp();
        }

        // if there isn't an up-to-date AST, create one
        if ((modifiedStore == null) || (modified != modifiedStore)) {
            ASTParser parser = ASTParser.newParser(AST.JLS3);
            parser.setResolveBindings(true);

            if (element instanceof ICompilationUnit) {
                parser.setSource((ICompilationUnit) element);
            } else if (element instanceof IClassFile) {
                // only search through the class if it has source code attached
                IClassFile classFile = (IClassFile) element;

                try {
                    if (classFile.getSource() == null) {
                        return;
                    }
                } catch (JavaModelException e) {
                    e.printStackTrace();
                }

                parser.setSource(classFile);
            } else {
                GreenException.illegalOperation("Illegal element type: " + element.getClass());
            }

            cu = (CompilationUnit) parser.createAST(null);
            _cuMap.put(element, cu);
        } else {
            cu = _cuMap.getCompilationUnit(id);
        }

        // run the recognizers
        for (Class klass : PlugIn.getRelationships()) {
            RelationshipRecognizer recognizer = PlugIn.getRelationshipGroup(klass).getRecognizer();

            // run the recognizer
            recognizer.run(cu, getRootModel().getRelationshipCache());
        }
    }

    public void forceRefreshRelationships() {
        refreshRelationships(true);
    }

    /**
     * Refreshes the relationships in the editor
     * 
     * @param force - If true, will run the relationship recognizers. If false,
     * will run the relationship recognizers only if they are not disabled in
     * the <code>PlugIn</code> instance. 
     */
    private void refreshRelationships(boolean force) {
        List<String> visitedElements = new ArrayList<String>();
        List<String> outdated = new ArrayList<String>();

        if (!force && !PlugIn.isRecognizersEnabled()) {
            return;
        }

        // get a list of all classes and compilation units in the editor
        List<IJavaElement> elements = new ArrayList<IJavaElement>();
        elements.addAll(getRootModel().getElementsOfKind(IJavaElement.COMPILATION_UNIT));
        elements.addAll(getRootModel().getElementsOfKind(IJavaElement.CLASS_FILE));

        // find relationships attached to those elements
        for (IJavaElement element : elements) {
            findRelationships(element);
            visitedElements.add(element.getHandleIdentifier());
        }

        // remove outdated CompilationUnit objects from the map 
        for (String cu : _cuMap.keySet()) {
            if (!(visitedElements.contains(cu))) {
                outdated.add(cu);
            }
        }

        for (String obsolete : outdated) {
            _cuMap.remove(obsolete);
        }

        Set<RelationshipModel> toRemove = new HashSet<RelationshipModel>();
        _relationshipChanges = getRootModel().getRelationshipCache().processChanges();

        // update the relationships as appropriate
        for (RelationshipModel rModel : _relationshipChanges) {
            if (rModel.getRelationships().size() == 0) { // removal
                rModel.removeFromParent();
                toRemove.add(rModel);
            } else {
                rModel.setParent(getRootModel());

                if (rModel.getSourceModel() != null && rModel.getTargetModel() != null) {
                    if (!getRootModel().getRelationships().contains(rModel)) {
                        getRootModel().addChild(rModel);
                        toRemove.add(rModel);
                    }
                }
            }
        }

        // update the cardinality labels of all updated relationships 
        for (RelationshipModel model : toRemove) {
            model.updateCardinality();
        }

        _relationshipChanges.removeAll(toRemove);
    }

    /**
     * Preserves consistency between the editor and the code by automatically
     * saving open <code>CompilationUnit</code>s 
     */
    public void autoSave() {
        if (!PlugIn.getBooleanPreference(P_AUTOSAVE))
            return;

        // save all open compilation units
        IDE.saveAllEditors(new IResource[] { PlugIn.getWorkspaceRoot() }, false);
    }

    /**
     * Gets an AST <code>CompilationUnit</code> from the mapping using the given
     * <code>IJavaElement</code>, which should be either an
     * <code>IClassFile</code> or an <code>ICompilationUnit</code>
     * 
     * @param element - The element to find in the mapping.
     * @return The AST <code>CompilationUnit</code> the represents the structure
     * of the given element
     */
    public CompilationUnit getCompilationUnit(IJavaElement element) {
        return _cuMap.getCompilationUnit(element.getHandleIdentifier());
    }

    /**
     * @return The router used in the current routing scheme.
     */
    public static ConnectionRouter getConnectionRouter() {
        return CONNECTION_ROUTER;
    }

    /**
     * @return The active editor.
     */
    public static DiagramEditor getActiveEditor() {
        return ACTIVE_EDITOR;
    }

    /**
     * @return The currently active tool in the palette.
     */
    public ToolEntry getActiveTool() {
        return getGraphicalViewer().getEditDomain().getPaletteViewer().getActiveTool();
    }

    /**
     * Determines whether or not the given member is filtered.
     * 
     * @param member - the given <code>IMember</code>.
     * @return true if a filter applies to the member, false otherwise.
     * @throws JavaModelException
     */
    public boolean isFiltered(IMember member) throws JavaModelException {
        for (Filter filter : _filters) {
            if (filter.accept(member))
                return true;
        }

        return false;
    }

    public static void setOutlinePage(OutlinePage page) {
        _outlinePage = page;
    }

    public Rectangle getSize() {
        return getGraphicalControl().getBounds();
    }
}

/**
 * Creates the <code>AbstractPart</code>s that correspond to the given
 * <code>AbstractModel</code>s. The parts, in turn, create the figures that
 * represent the models in our editor.
 * 
 * @author bcmartin
 */
class DiagramPartFactory implements EditPartFactory {
    /**
     * @see org.eclipse.gef.EditPartFactory#createEditPart(org.eclipse.gef.EditPart, java.lang.Object)
     */
    public EditPart createEditPart(EditPart context, Object oModel) {
        try {
            // finds the EditPart associated with the created model
            // and instanciates a new EditPart of that type
            AbstractModel model = (AbstractModel) oModel;
            Class editPartClass = model.getPartClass();
            Constructor<?> instance = null;
            Constructor<?>[] constructors = editPartClass.getConstructors();

            // find the empty constructor
            for (Constructor<?> constructor : constructors) {
                if (constructor.getParameterTypes().length == 0) {
                    instance = constructor;
                }
            }

            // throw an error if there is no empty constructor
            if (instance == null) {
                GreenException.illegalOperation("Model has no empty constructor: " + model);
            }

            // connect the model and controller
            AbstractPart part = (AbstractPart) instance.newInstance();
            part.setModel(model);

            // map the model to its corresponding edit part
            RootPart drep;

            if (part instanceof RootPart) {
                drep = (RootPart) part;
            } else {
                AbstractPart aEditPart = (AbstractPart) context;
                drep = aEditPart.getRootPart();
            }

            drep.mapModelToEditPart(model, part);

            return part;
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }
}

/**
 * Sets up the tools in the palette.
 * @author bcmartin
 */
class DiagramPaletteFactory {
    /**
     * A generic selection tool.
     */
    private static ToolEntry SELECTION_TOOL = new PanningSelectionToolEntry(SELECTION_LABEL, SELECTION_DESCRIPTION);

    /**
     * @return The generic selection tool.
     */
    public static ToolEntry getSelectionTool() {
        return SELECTION_TOOL;
    }

    /**
     * Creates the bottom-level palette contents.
     *
     * @return The bottom-level palette contents.
     */
    public static PaletteRoot createPaletteRoot() {
        ImageDescriptor noteIcon = JavaPluginImages.DESC_TOOL_SHOW_SEGMENTS;

        PaletteRoot paletteRoot = new PaletteRoot();

        // create relationship groups
        List<PaletteEntry> categories = new Vector<PaletteEntry>();
        PaletteGroup tools = new PaletteGroup(GROUP_MAIN_LABEL);
        List<ToolEntry> selectEntries = new Vector<ToolEntry>();
        List<ToolEntry> typeEntries = new Vector<ToolEntry>();
        List relEntries = new ArrayList();

        String noteLabel = NOTE_LABEL;
        String noteDesc = NOTE_DESCRIPTION;

        // Selection tool
        selectEntries.add(SELECTION_TOOL);
        paletteRoot.setDefaultEntry(SELECTION_TOOL);
        tools.addAll(selectEntries);

        // Type tools and Note tool
        PaletteGroup typeDrawer = new PaletteGroup(GROUP_CREATE_TYPE_LABEL);
        List<ITypeProperties> properties = new ArrayList<ITypeProperties>();

        // put types in alphabetical order
        for (ITypeProperties prop : PlugIn.getAvailableTypes()) {
            boolean added = false;
            int x = 0;

            for (ITypeProperties cProp : properties) {
                if (prop.getLabel().compareToIgnoreCase(cProp.getLabel()) < 0) {
                    properties.add(x, prop);
                    added = true;
                    break;
                }

                x++;
            }

            if (!added) {
                properties.add(prop);
            }
        }

        // create palette entries for available types
        for (ITypeProperties prop : properties) {
            typeEntries.add(new CombinedTemplateCreationEntry(prop.getLabel(), prop.getDescription(),
                    TypeModel.class, new SimpleFactory(TypeModel.class), prop.getIconDescriptor(),
                    prop.getIconDescriptor()));
        }

        typeEntries.add(new CombinedTemplateCreationEntry(noteLabel, noteDesc, NoteModel.class,
                new SimpleFactory(NoteModel.class), noteIcon, noteIcon));

        typeDrawer.addAll(typeEntries);

        // create container for relationship tools
        PaletteGroup relDrawer = new PaletteGroup(GROUP_CREATE_RELATIONSHIPS_LABEL);
        List<Class> relClasses = new ArrayList<Class>();

        /* Ensure the relationships are added to the palette in the correct
         * order by comparing their names to one another beforehand
         */

        for (Class klass : PlugIn.getRelationships()) {
            boolean added = false;
            int x = 0;

            for (Class comp : relClasses) {
                if (PlugIn.getRelationshipName(klass).compareToIgnoreCase(PlugIn.getRelationshipName(comp)) < 0) {
                    relClasses.add(x, klass);
                    added = true;
                    break;
                }

                x++;
            }

            if (!added) {
                relClasses.add(klass);
            }
        }

        String oldName = null;

        /* Map all relationships to their supertypes (e.g. dependency has more
         * than one flavor.
         */
        for (Class klass : relClasses) {
            RelationshipGroup group = PlugIn.getRelationshipGroup(klass);

            List<RelationshipSubtype> subtypes = PlugIn.getRelationshipSubtypes(group.getName());

            if (group.getName().equals(oldName))
                continue;
            oldName = group.getName();

            if (subtypes == null || subtypes.size() < 2) {
                RelationshipCreationToolEntry entry = new RelationshipCreationToolEntry(group, null);
                relEntries.add(entry);
            } else {
                PaletteStack pstack = new PaletteStack(null, null, null);

                for (RelationshipSubtype type : subtypes) {
                    RelationshipCreationToolEntry entry = new RelationshipCreationToolEntry(type.getGroup(), null);
                    entry.setLabel(type.getLabel() + " " + type.getGroup().getName());
                    pstack.add(entry);
                }
                relEntries.add(pstack);

            }
        }

        relDrawer.addAll(relEntries);

        PaletteGroup visDrawer = new PaletteGroup(GROUP_VISIBILITY_LABEL);
        List<PaletteEntry> visEntries = new ArrayList<PaletteEntry>();
        ImageDescriptor fishIcon = ImageDescriptor.createFromFile(DiagramEditor.class, "fish.gif");
        visEntries.add(new PaletteButton(TOGGLEFISH_LABEL, TOGGLEFISH_DESCRIPTION, fishIcon, fishIcon) {
            {
                setUserModificationPermission(PERMISSION_NO_MODIFICATION);
            }

            public void act() {
                PlugIn.setBooleanPreference(P_DISPLAY_INCREMENTAL_EXPLORER_DIA,
                        !PlugIn.getBooleanPreference(P_DISPLAY_INCREMENTAL_EXPLORER_DIA));
            }
        });
        visDrawer.addAll(visEntries);

        // add the tools into the palette root
        categories.add(tools);
        categories.add(typeDrawer);
        categories.add(relDrawer);
        categories.add(visDrawer);
        paletteRoot.addAll(categories);

        return paletteRoot;
    }
}

/**
 * Our palette tool for creating a relationship.
 * 
 * @author bcmartin
 */
class RelationshipCreationToolEntry extends CreationToolEntry {
    public RelationshipCreationToolEntry(final RelationshipGroup group, final List<RelationshipSubtype> subtypes) {
        super(group.getName(),
                CREATE_RELATIONSHIP_PREFIX_DESCRIPTION + group.getName() + CREATE_RELATIONSHIP_SUFFIX_DESCRIPTION,
                new SimpleFactory(group.getPartClass()), group.getImageDescriptor(), group.getImageDescriptor());

        setToolClass(ConnectionCreationTool.class);
        setUserModificationPermission(PERMISSION_NO_MODIFICATION);
    }
}

/**
 * A class that allows the adding of buttons to the palette instead of tools.
 * 
 * @author dan
 */
abstract class PaletteButton extends ToolEntry {

    /**
     * @param label
     * @param shortDesc
     * @param iconSmall
     * @param iconLarge
     */
    public PaletteButton(String label, String shortDesc, ImageDescriptor iconSmall, ImageDescriptor iconLarge) {
        super(label, shortDesc, iconSmall, iconLarge);
    }

    /**
     * Override to specify what this button will execute when clicked.
     */
    public abstract void act();
}

/**
 * A collection of methods for performing save and load in the editor.
 * 
 * @author bcmartin
 * @author zgwang
 */
class DiagramEditorFilePolicies {
    private static boolean _fileModified;

    /**
     * Indicates to the user that there was an I/O error in Green.
     * 
     * @param file - The file with the invalid format.
     */
    private static void displayInvalidFileFormatError(File file) {
        GreenException.fileException(GRERR_FILE_FORMAT);
    }

    /**
     * Attempts to find the Type in the Java Model
     * 
     * @param projectName - the String name of the project
     * @param path - the IPath of the Compilation Unit containing the type
     * @return the desired <code>IType</code> that is modeled by our diagram
     */
    private static IType extractType(String projectName, String fullyQualifiedTypeName) {
        try {
            IJavaModel jm = JavaCore.create(ResourcesPlugin.getWorkspace().getRoot());
            return jm.getJavaProject(projectName).findType(fullyQualifiedTypeName);
        } catch (JavaModelException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * @param handleId - The handle to the type.
     * @return The type referred to by this handle.
     */
    private static IType extractType(String handleId) {
        IType type = (IType) JavaCore.create(handleId);
        if (type.exists())
            return type;
        return null;
    }

    /**
     * Loads a UML file into an editor. This method delegates the interpretation
     * of the file's contents to a private helper method of the same name; thus
     * this method should never need maintenance.
     * 
     * @param file - The file to be loaded into this editor.
     */
    public static void load(final DiagramEditor editor, final File file) {

        //LOOKINTO [Can be removed if refactoring is done through extension point.] Refactoring in DIA files, this needs to parse Eclipse FQN format

        final XMLNode node, parent;

        char[] fileContents = new char[(int) file.length()];

        // read the file and parse the contents
        try {
            FileReader fReader = null;

            try {
                fReader = new FileReader(file);
            } catch (FileNotFoundException e) {
                // abort;
                GreenException.warn("The file " + file + " was not found.");
                return;
            }

            fReader.read(fileContents);
            fReader.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            return;
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }

        if (fileContents.length < 5) {
            // file is "empty" - no error
            return;
        }

        XMLConverter converter = new XMLConverter(editor);
        node = converter.getDecodedXML(new String(fileContents));

        if (!node.getNodeType().equals("!root")) {
            // invalid file format
            displayInvalidFileFormatError(file);
            return;
        }

        if (node.getChildren().size() == 0) {
            return;
        }

        Queue<XMLNode> queue = new LinkedList<XMLNode>();
        queue.add(node);

        //      System.out.println(" >>>>q node is "+node.toString());
        processXMLNodeRecursively(editor, queue);
        //      System.out.println(" <<<< node is "+node.toString());

        parent = (XMLNode) node.getChild(XML_UML);
        //      System.out.println(" <<<< parent is "+node.toString());

        PlugIn.runWithoutRecognizers(new Runnable() {
            /**
             * @see java.lang.Runnable#run()
             */
            public void run() {
                load(editor, parent);
            }
        });
    }

    private static void processXMLNodeRecursively(final DiagramEditor editor,
            final Queue<XMLNode> queueOfXMLNodes) {
        if (!queueOfXMLNodes.isEmpty()) {
            XMLNode currentNode = queueOfXMLNodes.remove();

            // Get the name for this XML Node

            Collection<String> attributesToModify = new HashSet<String>();
            if (currentNode.getNodeType().equals("type")) {
                attributesToModify.add("name");
            }
            if (currentNode.getNodeType().equals("relationship")) {
                attributesToModify.add("sourceType");
                attributesToModify.add("targetType");
            }

            for (String attributeName : attributesToModify) {
                String resourceNameIncludingProjectName = currentNode.getAttribute(attributeName);
                //System.out.println("   *** ["+attributeName+"] Processing XML node: " + currentNode.toString());
                if (resourceNameIncludingProjectName != null) {
                    ////               System.out.println(attributeName+" attribute is " + resourceNameIncludingProjectName);
                    ////               System.out.println("NodeType is " + currentNode.getNodeType());
                    int begin = resourceNameIncludingProjectName.lastIndexOf("=") + 1;
                    int end = resourceNameIncludingProjectName.lastIndexOf("/");
                    String projectNameInXMLFile = resourceNameIncludingProjectName.substring(begin, end);
                    String resourceNameWithoutProjectName = resourceNameIncludingProjectName.substring(end,
                            resourceNameIncludingProjectName.length());

                    // Get the name of the current Eclipse project folder
                    //               String currentProjectName = editor.getProject().getProject().getName();
                    String currentFileName = editor.getCurrentFile().toString();
                    begin = currentFileName.indexOf("/") + 1;
                    end = currentFileName.indexOf("/", begin);
                    String currentProjectName = currentFileName.substring(begin, end);

                    // If the project names aren't the same, remove the attribute and re-add it with the correct project name
                    if (projectNameInXMLFile.equals(".")) {
                        // Let's fix that.
                        String newResourceNameIncludingNewProjectName = "=" + currentProjectName
                                + resourceNameWithoutProjectName;
                        Map<String, String> attributes = currentNode.getAttributes();
                        //attributes.remove("name");//resourceNameIncludingProjectName);
                        // The put call will (according to JavaDocs) replace existing mapping
                        attributes.put(attributeName, newResourceNameIncludingNewProjectName);
                    }
                }
            }
            queueOfXMLNodes.addAll(currentNode.getChildren());
            processXMLNodeRecursively(editor, queueOfXMLNodes);
        }
    }

    /**
     * Saves the current editor.
     * 
     * @param askForName - Will ask for name if <code>true</code>.
     */
    public static void save(DiagramEditor editor, boolean askForName) {
        IPath filePath = new Path("/noname");

        if (editor.getEditorInput() instanceof GreenEditorInput) {
            GreenEditorInput input = (GreenEditorInput) editor.getEditorInput();
            filePath = new Path(input.getPath().toFile().toString());
        }

        // in case the file is saved outside of Eclipse
        if (editor.getCurrentFile() != null) {
            filePath = PlugIn.getWorkspaceRoot().getFile(editor.getCurrentFile().getFullPath()).getLocation();
        }

        String extension;

        // Open dialog and ask for a name
        if (askForName || !filePath.toFile().exists()) {
            // display the dialog to get the file location
            String fileName = filePath.toString();
            FileDialog dialog = new FileDialog(editor.getSite().getShell(), SWT.SAVE);
            dialog.setFileName(fileName.substring(fileName.lastIndexOf('/') + 1, fileName.lastIndexOf('.')));

            List<String> fExt = new ArrayList<String>();
            List<String> fDesc = new ArrayList<String>();

            // get file extensions
            for (String ext : PlugIn.getSaveFormats()) {
                String sExt = "*." + ext;
                ISaveFormat format = PlugIn.getSaveFormat(ext);

                String desc = format.getDescription();
                if (desc == "null") {
                    desc = "";
                }

                fExt.add(sExt);
                fDesc.add(format.getDescription() + " (" + sExt + ")");
            }

            dialog.setFilterExtensions(fExt.toArray(new String[0]));
            dialog.setFilterNames(fDesc.toArray(new String[0]));
            dialog.setFilterPath(filePath.toOSString());
            fileName = dialog.open();

            // abort if the user pressed cancel
            if (fileName == null)
                return;

            // see if the file path is in the workspace
            filePath = new Path(fileName);
            String workspaceString = PlugIn.getWorkspaceRoot().getLocation().toPortableString();
            String workspaceDir = workspaceString.substring(workspaceString.lastIndexOf('/')) + "/";
            String fileLocation = filePath.toPortableString();

            int wsIndex = fileLocation.indexOf(workspaceDir);

            if (wsIndex == -1) { // not a workspace file path   
                try {
                    filePath.toFile().createNewFile();
                } catch (IOException e) {
                    GreenException.fileException("There was a problem accessing \"" + filePath + "\"");
                    e.printStackTrace();
                    return;
                }

                if (filePath.toString().endsWith("." + PluginConstants.GREEN_EXTENSION) ||
                //Older file type compatibility
                        filePath.toString().endsWith(".dia")) {
                    editor.setEditorInput(new GreenEditorInput(new File(filePath.toOSString())));
                }
            } else { // save file in workspace
                String wsFile = fileLocation.substring(wsIndex + workspaceDir.length());
                String wsProject = wsFile.substring(0, wsFile.indexOf('/'));

                try {
                    IProject project = PlugIn.getWorkspaceRoot().getProject(wsProject);
                    IFile file = DiagramEditor.getFileNotExist(project, new Path(wsFile));

                    editor.setEditorInput(new FileEditorInput(file));

                    filePath = PlugIn.getWorkspaceRoot().getFile(editor.getCurrentFile().getFullPath())
                            .getLocation();
                } catch (CoreException e) {
                    e.printStackTrace();
                    return;
                } catch (IOException e) {
                    e.printStackTrace();
                    return;
                }
            }
        }

        extension = filePath.getFileExtension();
        ISaveFormat format;
        if (extension.equals("dia") || extension.equals("grn"))
            format = PlugIn.getSaveFormat(PluginConstants.GREEN_EXTENSION);
        else
            format = PlugIn.getSaveFormat(extension);

        if (format == null) {
            MessageDialog.openError(editor.getSite().getShell(), GRERR_FILE_FORMAT,
                    GRERR_FILE_FORMAT + ": " + extension);
            return;
        }

        format.saveInformation(editor, filePath.toOSString(), editor.getRootPart().getFigure());
    }

    /**
     * Handles loading of the editor's contents by extracting the file and
     * plugin version. The versions are then compared, warning messages are
     * displayed as appropriate, and the delegate methods are called to perform
     * the loading of the editor's contents.
     * 
     * @param editor - The editor to load the file's contents into.
     * @param base - The node with label XML_UML.
     */
    private static void load(DiagramEditor editor, XMLNode base) {
        final RootModel root = editor.getRootModel();

        String version = base.getAttribute(XML_GREEN_VERSION);
        int pluginVersion = PlugIn.getVersion();
        int fileVersion = version == null ? 20000 : Integer.parseInt(version);

        if (pluginVersion < fileVersion) {
            MessageDialog.openWarning(editor.getSite().getShell(), GreenException.GRERR_FILE_VERSION_TITLE,
                    GreenException.generateVersionWarning(pluginVersion, fileVersion));
            fileVersion = pluginVersion;
        }

        _fileModified = false;
        loadTypes(root, base, fileVersion);
        loadNotes(root, base, fileVersion);
        loadRelationshipModels(editor, root, base, fileVersion);

        if (_fileModified) {
            warnFileModified();
        }
    }

    /**
     * Loads <code>TypeModel</code>s into the diagram.
     * 
     * @param root - The root model to use.
     * @param base - The XML_UML node of the file.
     * @param ver - The version number of the file.
     */
    private static void loadTypes(RootModel root, XMLNode base, int ver) {
        //LOOKINTO [Can be removed if refactoring is done through extension point.] Refactoring in DIA files, this should alter JDT handles...
        if (ver == 20000) {
            // 2.0.0
            for (XMLNode child : base.getChildren()) {
                if (child.getNodeType().equals(XML_TYPE)) {
                    // load in type
                    String proj = child.getAttribute(XML_TYPE_PROJECT);
                    String fqn = child.getAttribute(XML_TYPE_NAME);
                    int height = child.getIntAttribute(XML_TYPE_HEIGHT);
                    int width = child.getIntAttribute(XML_TYPE_WIDTH);
                    int x = child.getIntAttribute(XML_TYPE_X);
                    int y = child.getIntAttribute(XML_TYPE_Y);

                    IType type = extractType(proj, fqn);
                    if (type == null) {
                        _fileModified = true;
                        GreenException.warn("Type does not exist: " + fqn);
                        continue;
                    }

                    TypeModel model = root.createTypeModel(type);
                    model.setLocation(new Point(x, y));
                    model.setSize(new Dimension(width, height));
                }
            }
        } else if (ver <= PlugIn.getVersion()) {
            // 2.1.0 - 3.0.0
            for (XMLNode child : base.getChildren()) {
                if (child.getNodeType().equals(XML_TYPE)) {
                    // load in type
                    String handle = child.getAttribute(XML_TYPE_NAME);
                    int height = child.getIntAttribute(XML_TYPE_HEIGHT);
                    int width = child.getIntAttribute(XML_TYPE_WIDTH);
                    int x = child.getIntAttribute(XML_TYPE_X);
                    int y = child.getIntAttribute(XML_TYPE_Y);

                    IType type = extractType(handle);
                    if (type == null) {
                        _fileModified = true;
                        GreenException.warn("Type does not exist: " + handle);
                        continue;
                    }

                    TypeModel model = root.createTypeModel(type);
                    model.setLocation(new Point(x, y));
                    model.setSize(new Dimension(width, height));
                }
            }
        } else {
            GreenException.warn("loadTypes failed: invalid file version: " + ver);
        }
    }

    /**
     * Loads <code>RelationshipModel</code>s into the diagram.
     * 
     * @param root - The root model to use.
     * @param base - The XML_UML node of the file.
     * @param ver - The version number of the file.
     */
    private static void loadRelationshipModels(DiagramEditor editor, RootModel root, XMLNode base, int ver) {
        // the editor must be forcibly refreshed so that the relationships
        // will appear; we are running with recognizers disabled
        editor.refresh(true);
        RelationshipCache cache = root.getRelationshipCache();

        if (ver == 20000) {
            // 2.0.0
            for (XMLNode relationshipNode : base.getChildren()) {
                if (relationshipNode.getNodeType().equals(XML_RELATIONSHIP)) {
                    String relationshipClass = relationshipNode.getAttribute(XML_RELATIONSHIP_CLASS);
                    String sourceProj = relationshipNode.getAttribute(XML_RELATIONSHIP_SOURCE_PROJECT);
                    String sourceName = relationshipNode.getAttribute(XML_RELATIONSHIP_SOURCE_TYPE);
                    String targetProj = relationshipNode.getAttribute(XML_RELATIONSHIP_TARGET_PROJECT);
                    String targetName = relationshipNode.getAttribute(XML_RELATIONSHIP_TARGET_TYPE);

                    IType sourcetype = extractType(sourceProj, sourceName);
                    IType targettype = extractType(targetProj, targetName);
                    RelationshipModel rModel = cache.getRelationshipModel(sourcetype, targettype,
                            relationshipClass);
                    if (rModel != null) {
                        XMLNode bendpointsNode = relationshipNode.getChild(XML_BENDPOINTS);

                        for (XMLNode bendpointNode : bendpointsNode.getChildren()) {
                            if (bendpointNode.getNodeType().equals(XML_BENDPOINT)) {

                                int x = new Integer(bendpointNode.getAttribute(XML_BENDPOINT_X)).intValue();
                                int y = new Integer(bendpointNode.getAttribute(XML_BENDPOINT_Y)).intValue();

                                editor.addBendpoint(rModel, new Point(x, y));
                            }
                        }
                    } else {//Relationship changed?
                        _fileModified = true;

                    }
                }
            }
        } else if (ver <= PlugIn.getVersion()) {
            // 2.1.0 - 3.0.0
            for (XMLNode relationshipNode : base.getChildren()) {
                if (relationshipNode.getNodeType().equals(XML_RELATIONSHIP)) {
                    String relationshipClass = relationshipNode.getAttribute(XML_RELATIONSHIP_CLASS);
                    String sourceId = relationshipNode.getAttribute(XML_RELATIONSHIP_SOURCE_TYPE);
                    String targetId = relationshipNode.getAttribute(XML_RELATIONSHIP_TARGET_TYPE);

                    IType sourcetype = extractType(sourceId);
                    IType targettype = extractType(targetId);
                    RelationshipModel rModel = cache.getRelationshipModel(sourcetype, targettype,
                            relationshipClass);

                    //               System.out.println(sourceId + "/" + targetId + " Looking for relationship model ("+relationshipClass+"): "+rModel);

                    if (rModel != null) {
                        XMLNode bendpointsNode = relationshipNode.getChild(XML_BENDPOINTS);

                        for (XMLNode bendpointNode : bendpointsNode.getChildren()) {
                            if (bendpointNode.getNodeType().equals(XML_BENDPOINT)) {

                                int x = new Integer(bendpointNode.getAttribute(XML_BENDPOINT_X)).intValue();
                                int y = new Integer(bendpointNode.getAttribute(XML_BENDPOINT_Y)).intValue();

                                editor.addBendpoint(rModel, new Point(x, y));
                            }
                        }
                    } else {
                        _fileModified = true;
                    }
                }
            }
        } else {
            GreenException.warn("loadRelat failed: invalid file version: " + ver);
        }
    }

    /**
     * Loads <code>NoteModel</code>s into the diagram.
     * 
     * @param root - The root model to use.
     * @param base - The XML_UML node of the file.
     * @param ver - The version number of the file.
     */
    private static void loadNotes(RootModel root, XMLNode base, int ver) {
        if (ver <= PlugIn.getVersion()) {
            // 2.0.0 - 3.0.0
            for (XMLNode child : base.getChildren()) {
                if (child.getNodeType().equals(XML_NOTE)) {
                    String text = child.getAttribute(XML_NOTE_TEXT);
                    int height = child.getIntAttribute(XML_NOTE_HEIGHT);
                    int width = child.getIntAttribute(XML_NOTE_WIDTH);
                    int x = child.getIntAttribute(XML_NOTE_X);
                    int y = child.getIntAttribute(XML_NOTE_Y);

                    NoteModel model = new NoteModel();
                    model.setLabel(text);
                    model.setLocation(new Point(x, y));
                    model.setSize(new Dimension(width, height));

                    root.addChild(model);
                }
            }
        } else {
            GreenException.warn("loadNotes failed: invalid file version: " + ver);
        }
    }

    /**
     * Informs the user that the diagram cannot accurately reflect changes made to the code
     * while it was closed.
     * 
     * @author zgwang
     */
    private static void warnFileModified() {
        String warning = "One or more files this diagram refers to have been modified outside of Green's editor.  The diagram may be unable to accurately reflect these changes.";
        MessageDialog.openInformation(PlugIn.getDefaultShell(), "Error", warning);
    }
}

/**
 * A wrapper for a loaded file that exists outside of the
 * <code>IWorkspace</code>.
 * 
 * @author bcmartin
 */
class GreenEditorInput implements IEditorInput, ILocationProvider, IPathEditorInput {
    private File _file;

    public GreenEditorInput(File file) {
        _file = file;
    }

    /**
     * @see org.eclipse.ui.IEditorInput#exists()
     */
    public boolean exists() {
        return _file.exists();
    }

    /**
     * @see org.eclipse.ui.IEditorInput#getImageDescriptor()
     */
    public ImageDescriptor getImageDescriptor() {
        return null;
    }

    /**
     * @see org.eclipse.ui.IEditorInput#getName()
     */
    public String getName() {
        return _file.getName();
    }

    /**
     * @see org.eclipse.ui.IEditorInput#getPersistable()
     */
    public IPersistableElement getPersistable() {
        return null;
    }

    /**
     * @see org.eclipse.ui.IEditorInput#getToolTipText()
     */
    public String getToolTipText() {
        return _file.getAbsolutePath();
    }

    /**
     * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
     */
    public Object getAdapter(Class adapter) {
        if (ILocationProvider.class.equals(adapter)) {
            return this;
        }

        if (IWorkbenchAdapter.class.equals(adapter)) {
            return new WorkbenchAdapter() {
            };
        }

        return Platform.getAdapterManager().getAdapter(this, adapter);
    }

    /**
     * @see org.eclipse.ui.editors.text.ILocationProvider#getPath(java.lang.Object)
     */
    public IPath getPath(Object element) {
        if (element instanceof GreenEditorInput) {
            GreenEditorInput input = (GreenEditorInput) element;
            return Path.fromOSString(input._file.getAbsolutePath());
        }

        return null;
    }

    /**
     * @see org.eclipse.ui.IPathEditorInput#getPath()
     */
    public IPath getPath() {
        return new Path(_file.toString());
    }
}

/**
 * Permits the removal of certain <code>IJavaElement</code> representations from
 * the diagram.
 *
 * @author bcmartin
 */
class Filter {
    private Pattern _pattern;
    private int _flags;
    private int _type;

    public Filter(Pattern pattern, int type, int flags) {
        _pattern = pattern;
        _type = type;
        _flags = flags;
    }

    /**
     * Compares the given member to the filter to see if it applies.
     * 
     * @param element - The <code>IMember</code> to compare.
     * @return true if the filter applies, false otherwise. 
     */
    public boolean accept(IMember element) throws JavaModelException {
        // see if the element type applies
        if ((_type & element.getElementType()) != _type) {
            return false;
        }

        // see if the flags match
        if ((_flags & element.getFlags()) != _flags) {
            return false;
        }

        // see if the text matches
        if (!_pattern.matcher(element.getElementName()).matches()) {
            return false;
        }

        return false;
    }
}

/**
 * Holds bendpoint information and generates bendpoint requests to permit
 * bendpoints to be accurately recreated in the editor.
 * 
 * @author bcmartin
 */
class BendpointInformation {
    private RelationshipModel _rModel;
    private Point _location;

    public BendpointInformation(RelationshipModel rModel, Point absoluteLocation) {
        _rModel = rModel;
        _location = absoluteLocation;
    }

    /**
     * @param editor - The editor.
     * @return A <code>BendpointRequest</code> corresponding to the
     * appropriate <code>RelationshipModel</code> and <code>Point</code>.
     */
    public BendpointRequest getBendpointRequest(DiagramEditor editor) {
        RelationshipPart rPart = (RelationshipPart) editor.getRootPart().getPartFromModel(_rModel);
        if (rPart == null)
            return null;

        //      Point location = new Point();
        //      Point topLeft = rPart.getFigure().getBounds().getTopLeft();
        //      
        //      location.x = _location.x - topLeft.x / 2;
        //      location.y = _location.y - topLeft.y / 2;

        // set the necessary information for the request
        BendpointRequest request = new BendpointRequest();
        request.setIndex(-1); // add to end
        request.setLocation(_location);
        request.setSource(rPart);

        return request;
    }
}

/**
 * Holds a mapping from element handles to their respective resource
 * modification times and <code>CompilationUnit</code>s.
 * 
 * @author bcmartin
 */
class CompilationUnitMap {
    private Map<String, Long> _cuModMap;
    private Map<String, CompilationUnit> _map;

    public CompilationUnitMap() {
        _cuModMap = new HashMap<String, Long>();
        _map = new HashMap<String, CompilationUnit>();
    }

    /**
     * @param id - The handle of the element.
     * @return The <code>CompilationUnit</code> corresponding to the given
     * id.
     */
    public CompilationUnit getCompilationUnit(String id) {
        return _map.get(id);
    }

    /**
     * @param id - The element handle.
     *  
     * @return The stored modification time corresponding to the given
     * handle.
     */
    public Long getModificationStamp(String id) {
        return _cuModMap.get(id);
    }

    /**
     * Removes a handle from the mapping.
     * 
     * @param id - The handle.
     */
    public void remove(String id) {
        _map.remove(id);
        _cuModMap.remove(id);
    }

    /**
     * @return A set of all element handles in the mapping.
     */
    public Set<String> keySet() {
        return new HashSet<String>(_map.keySet());
    }

    /**
     * Maps an element to its corresponding <code>CompilationUnit</code>.
     * 
     * @param element - The element.
     * @param cu - Its corresponding <code>CompilationUnit</code>.
     */
    public void put(IJavaElement element, CompilationUnit cu) {
        String id = element.getHandleIdentifier();

        _map.put(id, cu);

        if (!(element.isReadOnly())) {
            _cuModMap.put(id, element.getResource().getModificationStamp());
        }
    }

    public Object getAdapter(Class required) {
        return null;
    }
}