blob: 189a4d9d9024c42f21236de17cf6eb48ef25ed2c [file] [log] [blame]
/******************************************************************************
* Copyright (c) 2004, 2009 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
****************************************************************************/
package org.eclipse.gmf.runtime.diagram.ui.editpolicies;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.transaction.TransactionalEditingDomain;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.Request;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.commands.UnexecutableCommand;
import org.eclipse.gef.editpolicies.AbstractEditPolicy;
import org.eclipse.gef.requests.ChangeBoundsRequest;
import org.eclipse.gef.requests.CreateRequest;
import org.eclipse.gmf.runtime.common.core.command.CommandResult;
import org.eclipse.gmf.runtime.common.core.command.CompositeCommand;
import org.eclipse.gmf.runtime.common.core.command.FileModificationValidator;
import org.eclipse.gmf.runtime.common.core.command.ICommand;
import org.eclipse.gmf.runtime.diagram.core.commands.AddCommand;
import org.eclipse.gmf.runtime.diagram.core.edithelpers.CreateElementRequestAdapter;
import org.eclipse.gmf.runtime.diagram.core.util.ViewUtil;
import org.eclipse.gmf.runtime.diagram.ui.commands.CommandProxy;
import org.eclipse.gmf.runtime.diagram.ui.commands.CommandUtilities;
import org.eclipse.gmf.runtime.diagram.ui.commands.CreateCommand;
import org.eclipse.gmf.runtime.diagram.ui.commands.CreateOrSelectElementCommand;
import org.eclipse.gmf.runtime.diagram.ui.commands.ICommandProxy;
import org.eclipse.gmf.runtime.diagram.ui.commands.SemanticCreateCommand;
import org.eclipse.gmf.runtime.diagram.ui.editparts.GroupEditPart;
import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart;
import org.eclipse.gmf.runtime.diagram.ui.editparts.LabelEditPart;
import org.eclipse.gmf.runtime.diagram.ui.l10n.DiagramUIMessages;
import org.eclipse.gmf.runtime.diagram.ui.requests.CreateUnspecifiedTypeRequest;
import org.eclipse.gmf.runtime.diagram.ui.requests.CreateViewAndElementRequest;
import org.eclipse.gmf.runtime.diagram.ui.requests.CreateViewRequest;
import org.eclipse.gmf.runtime.diagram.ui.requests.EditCommandRequestWrapper;
import org.eclipse.gmf.runtime.diagram.ui.requests.RefreshConnectionsRequest;
import org.eclipse.gmf.runtime.diagram.ui.requests.RequestConstants;
import org.eclipse.gmf.runtime.emf.commands.core.command.CompositeTransactionalCommand;
import org.eclipse.gmf.runtime.emf.core.util.EObjectAdapter;
import org.eclipse.gmf.runtime.emf.type.core.IElementType;
import org.eclipse.gmf.runtime.emf.type.core.requests.CreateElementRequest;
import org.eclipse.gmf.runtime.emf.type.core.requests.MoveRequest;
import org.eclipse.gmf.runtime.notation.View;
import org.eclipse.swt.widgets.Display;
/**
* This edit policy returns the command that will create a new notational view
* for a the given request.
*
* @author choang
*/
public class CreationEditPolicy extends AbstractEditPolicy {
/**
* @return Command for the REQ_CREATE request. If the request is of type
* CreateViewAndElementRequest then a command to create the
* semantic, and the view will
* @see org.eclipse.gef.EditPolicy#getCommand(org.eclipse.gef.Request)
*/
public Command getCommand(Request request) {
if (understandsRequest(request)) {
if (request instanceof CreateUnspecifiedTypeRequest) {
return getUnspecifiedTypeCreateCommand((CreateUnspecifiedTypeRequest) request);
} else if (request instanceof CreateViewAndElementRequest) {
return getCreateElementAndViewCommand(
(CreateViewAndElementRequest)request);
}
else if (request instanceof CreateViewRequest) {
return getCreateCommand((CreateViewRequest)request);
}
else if (request instanceof ChangeBoundsRequest) {
return getReparentCommand((ChangeBoundsRequest)request);
}
return super.getCommand(request);
}
return null;
}
/** Understands <tt>REQ_CREATE</tt> and <tt>REQ_ADD</tt> request types. */
public boolean understandsRequest(Request request) {
return RequestConstants.REQ_CREATE.equals(request.getType())
|| RequestConstants.REQ_ADD.equals(request.getType())
|| super.understandsRequest(request);
}
/**
* Determines if a semantic reparent is being performed.
* @param element element being reparented
* @param newContext the new container element
* @return <tt>true</tt> if the supplied elemnet's container is
* not equals to the supplied <code>newContext</code>.\; otherwise <tt>false</tt>.
*/
protected boolean shouldReparent( EObject element, EObject newContext ) {
return !(element == null ||
element == newContext ||
element.eContainer() == newContext ||
isContainedIn( element, newContext )
);
}
/*
* This method checks to see element's containment tree already includes
* itself. This is necessary to prevent cyclic graphs in the model that
* cause StackOverflowExceptions.
*/
private boolean isContainedIn( EObject element, EObject newContext )
{
EObject container = newContext.eContainer();
while( container != null ) {
if( container.equals(element) )
return true;
container = container.eContainer();
}
return false;
}
/** Return a command to reparent both the semantic and view elements. */
/**
* return a command to reparent both the semantic and view elements.
* @param request the request
* @return command
*/
protected Command getReparentCommand(ChangeBoundsRequest request) {
Iterator editParts = request.getEditParts().iterator();
View container = (View)getHost().getAdapter(View.class);
EObject context = container == null ? null : ViewUtil.resolveSemanticElement(container);
CompositeCommand cc = new CompositeCommand(DiagramUIMessages.AddCommand_Label);
while ( editParts.hasNext() ) {
EditPart ep = (EditPart)editParts.next();
if ( ep instanceof LabelEditPart ) {
continue;
}
if (ep instanceof GroupEditPart) {
cc.compose(getReparentGroupCommand((GroupEditPart) ep));
}
View view = (View)ep.getAdapter(View.class);
if ( view == null ) {
continue;
}
EObject semantic = ViewUtil.resolveSemanticElement(view);
if ( semantic == null ) {
cc.compose(getReparentViewCommand((IGraphicalEditPart)ep));
}
else if ( context != null && shouldReparent(semantic, context)) {
cc.compose(getReparentCommand((IGraphicalEditPart)ep));
}
}
return cc.isEmpty() ? null : new ICommandProxy(cc.reduce());
}
/**
* Return the command to reparent the supplied group editpart's semantic and
* notation elements.
*
* @param gep
* the groupEP editpart being reparented
* @return A composite command that will reparent both the semantic and
* notation elements of the group.
*/
protected ICommand getReparentGroupCommand(GroupEditPart groupEP) {
CompositeCommand cc = new CompositeCommand(
DiagramUIMessages.AddCommand_Label);
View container = (View) getHost().getModel();
EObject context = ViewUtil.resolveSemanticElement(container);
// semantic
TransactionalEditingDomain editingDomain = ((IGraphicalEditPart) getHost())
.getEditingDomain();
for (Iterator iter = groupEP.getShapeChildren().iterator(); iter
.hasNext();) {
IGraphicalEditPart childEP = (IGraphicalEditPart) iter.next();
EObject element = ViewUtil.resolveSemanticElement((View) childEP
.getModel());
if (element != null) {
Command moveSemanticCmd = getHost().getCommand(
new EditCommandRequestWrapper(new MoveRequest(
editingDomain, context, element)));
if (moveSemanticCmd == null) {
return org.eclipse.gmf.runtime.common.core.command.UnexecutableCommand.INSTANCE;
}
cc.compose(new CommandProxy(moveSemanticCmd));
}
}
// notation
cc.compose(getReparentViewCommand(groupEP));
return cc;
}
/**
* Return the command to reparent the supplied editpart's semantic and notation
* elements.
* @param gep the editpart being reparented
* @return A CompositeCommand2 that will reparent both the semantic and notation elements.
*/
protected ICommand getReparentCommand( IGraphicalEditPart gep ) {
CompositeCommand cc = new CompositeCommand(DiagramUIMessages.AddCommand_Label);
View container = (View)getHost().getModel();
EObject context = ViewUtil.resolveSemanticElement(container);
View view = (View)gep.getModel();
EObject element = ViewUtil.resolveSemanticElement(view);
TransactionalEditingDomain editingDomain = ((IGraphicalEditPart) getHost())
.getEditingDomain();
//
// semantic
if ( element != null ) {
Command moveSemanticCmd =
getHost().getCommand(
new EditCommandRequestWrapper(
new MoveRequest(editingDomain, context, element)));
if (moveSemanticCmd == null) {
return org.eclipse.gmf.runtime.common.core.command.UnexecutableCommand.INSTANCE;
}
cc.compose ( new CommandProxy(moveSemanticCmd) );
}
//
// notation
cc.compose(getReparentViewCommand(gep));
return cc;
}
/**
* Return the command to reparent the supplied editpart's view only.
* @param gep the editpart being reparented
* @return A command to reparent the notation element.
*/
protected ICommand getReparentViewCommand( IGraphicalEditPart gep ) {
View container = (View)getHost().getModel();
View view = (View)gep.getModel();
return new AddCommand(gep.getEditingDomain(), new EObjectAdapter(container),
new EObjectAdapter(view));
}
/**
* Called in response to a <tt>REQ_CREATE</tt> request.
*
* Creates a compound command and populated with the following commands for
* each element to be created: <BR>
* <OL>
* <LI>a {@link CreateCommand}for each of the request's view descriptor.
* </OL>
*
* @param request
* a create request (understands instances of
* {@link CreateViewRequest}).
* @return a command to satify the request; <tt>null</tt> if the request
* is not understood.
*/
protected Command getCreateCommand(CreateViewRequest request) {
TransactionalEditingDomain editingDomain = ((IGraphicalEditPart) getHost())
.getEditingDomain();
CompositeTransactionalCommand cc = new CompositeTransactionalCommand(
editingDomain, DiagramUIMessages.AddCommand_Label);
Iterator descriptors = request.getViewDescriptors().iterator();
while (descriptors.hasNext()) {
CreateViewRequest.ViewDescriptor descriptor =
(CreateViewRequest.ViewDescriptor)descriptors.next();
CreateCommand createCommand =
new CreateCommand(editingDomain,
descriptor,
(View)(getHost().getModel()));
cc.compose(createCommand);
}
return new ICommandProxy(cc.reduce());
}
/**
* Method getCreateElementAndViewCommand.
*
* @param request
* @return Command Which creates the sematnic and the view command for the
* given CreateViewAndElementRequest
*/
protected Command getCreateElementAndViewCommand(CreateViewAndElementRequest request) {
// get the element descriptor
CreateElementRequestAdapter requestAdapter =
request.getViewAndElementDescriptor().getCreateElementRequestAdapter();
// get the semantic request
CreateElementRequest createElementRequest =
(CreateElementRequest) requestAdapter.getAdapter(
CreateElementRequest.class);
if (createElementRequest.getContainer() == null) {
// complete the semantic request by filling in the host's semantic
// element as the context
View view = (View)getHost().getModel();
EObject hostElement = ViewUtil.resolveSemanticElement(view);
if (hostElement == null && view.getElement() == null) {
hostElement = view;
}
// Returns null if host is unresolvable so that trying to create a
// new element in an unresolved shape will not be allowed.
if (hostElement == null) {
return null;
}
createElementRequest.setContainer(hostElement);
}
// get the create element command based on the elementdescriptor's
// request
Command createElementCommand =
getHost().getCommand(
new EditCommandRequestWrapper(
(CreateElementRequest)requestAdapter.getAdapter(
CreateElementRequest.class), request.getExtendedData()));
if (createElementCommand == null) {
return UnexecutableCommand.INSTANCE;
}
if(!createElementCommand.canExecute()){
return createElementCommand;
}
// create the semantic create wrapper command
SemanticCreateCommand semanticCommand =
new SemanticCreateCommand(requestAdapter, createElementCommand);
Command viewCommand = getCreateCommand(request);
Command refreshConnectionCommand =
getHost().getCommand(
new RefreshConnectionsRequest(((List)request.getNewObject())));
// form the compound command and return
CompositeCommand cc = new CompositeCommand(semanticCommand.getLabel());
cc.compose(semanticCommand);
cc.compose(new CommandProxy(viewCommand));
if ( refreshConnectionCommand != null ) {
cc.compose(new CommandProxy(refreshConnectionCommand));
}
return new ICommandProxy(cc);
}
/**
* Gets the command to create a new view (and optionally element) for an
* unspecified type request. This command includes a command to popup a menu
* to prompt the user for the type to be created.
*
* @param request
* the unspecified type request
* @return the command
*/
private Command getUnspecifiedTypeCreateCommand(
final CreateUnspecifiedTypeRequest request) {
final Map createCmds = new HashMap();
List validTypes = new ArrayList();
for (Iterator iter = request.getElementTypes().iterator(); iter
.hasNext();) {
IElementType elementType = (IElementType) iter.next();
Request createRequest = request.getRequestForType(elementType);
if (createRequest != null) {
EditPart target = getHost().getTargetEditPart(createRequest);
if ( target == null ) {
continue;
}
Command individualCmd = target.getCommand(createRequest);
if (individualCmd != null && individualCmd.canExecute()) {
createCmds.put(elementType, individualCmd);
validTypes.add(elementType);
}
}
}
if (createCmds.isEmpty()) {
return null;
} else if (createCmds.size() == 1) {
return (Command) createCmds.values().toArray()[0];
} else {
CreateOrSelectElementCommand selectAndCreateViewCmd = new CreateOrSelectElementCommand(
DiagramUIMessages.CreateCommand_Label, Display.getCurrent()
.getActiveShell(), validTypes) {
private Command _createCmd;
/**
* Execute the command that prompts the user with the popup
* menu, then executes the command prepared for the element
* type that the user selected.
*/
protected CommandResult doExecuteWithResult(
IProgressMonitor progressMonitor, IAdaptable info)
throws ExecutionException {
CommandResult cmdResult = super.doExecuteWithResult(progressMonitor, info);
if (!cmdResult.getStatus().isOK()) {
return cmdResult;
}
IElementType type = (IElementType) cmdResult
.getReturnValue();
_createCmd = (Command) createCmds.get(type);
Assert.isTrue(_createCmd != null && _createCmd.canExecute());
// validate the affected files
IStatus status = validateAffectedFiles(_createCmd);
if (!status.isOK()) {
return new CommandResult(status);
}
_createCmd.execute();
// Set the result in the unspecified type request.
CreateRequest createRequest = request
.getRequestForType(type);
Collection newObject = ((Collection) createRequest
.getNewObject());
request.setNewObject(newObject);
return CommandResult.newOKCommandResult(newObject);
}
protected CommandResult doUndoWithResult(
IProgressMonitor progressMonitor, IAdaptable info)
throws ExecutionException {
if (_createCmd != null && _createCmd.canUndo() ) {
// validate the affected files
IStatus status = validateAffectedFiles(_createCmd);
if (!status.isOK()) {
return new CommandResult(status);
}
_createCmd.undo();
}
return super.doUndoWithResult(progressMonitor, info);
}
protected CommandResult doRedoWithResult(
IProgressMonitor progressMonitor, IAdaptable info)
throws ExecutionException {
if (_createCmd != null && CommandUtilities.canRedo(_createCmd)) {
// validate the affected files
IStatus status = validateAffectedFiles(_createCmd);
if (!status.isOK()) {
return new CommandResult(status);
}
_createCmd.redo();
}
return super.doRedoWithResult(progressMonitor, info);
}
private IStatus validateAffectedFiles(Command command) {
Collection affectedFiles = CommandUtilities
.getAffectedFiles(command);
int fileCount = affectedFiles.size();
if (fileCount > 0) {
return FileModificationValidator
.approveFileModification((IFile[]) affectedFiles
.toArray(new IFile[fileCount]));
}
return Status.OK_STATUS;
}
};
return new ICommandProxy(selectAndCreateViewCmd);
}
}
/** Return the host if this editpolicy understands the supplied request. */
public EditPart getTargetEditPart(Request request) {
return understandsRequest(request) ? getHost() : null;
}
}