/* 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.EventObject;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
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();
_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;
}
});
}
/**
* @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);
}
// 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 XMLConverter converter = new XMLConverter();
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;
}
node = converter.getDecodedXML(new String(fileContents));
if (!node.getName().equals("!root")) {
// invalid file format
displayInvalidFileFormatError(file);
return;
}
if (node.getChildren().size() == 0) { return; }
parent = (XMLNode) node.getChild(XML_UML);
PlugIn.runWithoutRecognizers(new Runnable() {
/**
* @see java.lang.Runnable#run()
*/
public void run() {
load(editor, parent);
}
});
}
/**
* 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);
loadRelat(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.getName().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.getName().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 loadRelat(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.getName().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.getName().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.getName().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);
if (rModel != null) {
XMLNode bendpointsNode =
relationshipNode.getChild(
XML_BENDPOINTS);
for (XMLNode bendpointNode
: bendpointsNode.getChildren()) {
if (bendpointNode.getName().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.getName().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;
}
}