| /****************************************************************************** |
| * Copyright (c) 2004, 2007 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; |
| |
| // 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; |
| } |
| } |