/******************************************************************************* * <copyright> * * Copyright (c) 2005, 2012 SAP AG. * 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: * SAP AG - initial API, implementation and documentation * mwenz - Bug 351053 - Remove the need for WorkspaceCommandStackImpl * cbrand - Bug 377475 - Fix AbstractCustomFeature.execute and canExecute * * </copyright> * *******************************************************************************/ package org.eclipse.graphiti.ui.tests; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import org.easymock.EasyMock; import org.eclipse.draw2d.geometry.Rectangle; import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; import org.eclipse.emf.edit.provider.ComposedAdapterFactory; import org.eclipse.emf.transaction.TransactionalEditingDomain; import org.eclipse.emf.transaction.impl.TransactionalEditingDomainImpl; import org.eclipse.emf.workspace.WorkspaceEditingDomainFactory; import org.eclipse.gef.commands.Command; import org.eclipse.graphiti.dt.IDiagramTypeProvider; import org.eclipse.graphiti.features.IFeatureProvider; import org.eclipse.graphiti.features.context.IAddContext; import org.eclipse.graphiti.features.context.IContext; import org.eclipse.graphiti.features.context.ICustomContext; import org.eclipse.graphiti.features.context.IDeleteContext; import org.eclipse.graphiti.features.context.IDirectEditingContext; import org.eclipse.graphiti.features.custom.AbstractCustomFeature; import org.eclipse.graphiti.features.impl.AbstractAddShapeFeature; import org.eclipse.graphiti.features.impl.AbstractDirectEditingFeature; import org.eclipse.graphiti.internal.command.CommandContainer; import org.eclipse.graphiti.internal.command.DirectEditingFeatureCommandWithContext; import org.eclipse.graphiti.internal.command.GenericFeatureCommandWithContext; import org.eclipse.graphiti.internal.command.ICommand; import org.eclipse.graphiti.mm.pictograms.PictogramElement; import org.eclipse.graphiti.tb.IContextEntry; import org.eclipse.graphiti.tb.IToolBehaviorProvider; import org.eclipse.graphiti.tests.reuse.GFAbstractTestCase; import org.eclipse.graphiti.ui.features.DefaultDeleteFeature; import org.eclipse.graphiti.ui.internal.command.AddModelObjectCommand; import org.eclipse.graphiti.ui.internal.command.ContextEntryCommand; import org.eclipse.graphiti.ui.internal.command.GefCommandWrapper; import org.eclipse.graphiti.ui.internal.config.IConfigurationProviderInternal; import org.eclipse.graphiti.ui.internal.editor.GFCommandStack; import org.eclipse.graphiti.ui.platform.IConfigurationProvider; import org.eclipse.graphiti.ui.services.GraphitiUi; import org.eclipse.jface.viewers.StructuredSelection; import org.junit.After; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; @SuppressWarnings("restriction") public class CommandStackTest extends GFAbstractTestCase { private TransactionalEditingDomain editingDomain; private GFCommandStack commandStack; private IConfigurationProvider configurationProvider; @BeforeClass public static void prepareClass() { } @Before public void beforeTest() { editingDomain = GraphitiUi.getEmfService().createResourceSetAndEditingDomain(); configurationProvider = initConfigurationProviderForHasDoneChangesTests(editingDomain); commandStack = new GFCommandStack(configurationProvider, editingDomain); } @After public void afterTest() { editingDomain.dispose(); } /** * Tests that direct editing features that do not change anything don't make * the command stack dirty. This functionality is e.g. needed when switching * to direct editing directly after creating a new object and the user * simply takes over the default name; the null change must not end up in * the command stack because that would cause an undo that does nothing and * the user needs to press undo twice in order to undo the create (without * any feedback for the first undo). */ @Test public void testHasDoneChangesForDirectEditing() { TestDirectEditingFeature feature = new TestDirectEditingFeature(configurationProvider.getFeatureProvider()); IDirectEditingContext context = EasyMock.createNiceMock(IDirectEditingContext.class); EasyMock.replay(context); DirectEditingFeatureCommandWithContext featureCommand = new DirectEditingFeatureCommandWithContext(feature, context, "Initial", null); executeAndCheck(featureCommand, commandStack, editingDomain); } @Test public void testHasDoneChangesForCustomFeature() { TestCustomFeature feature = new TestCustomFeature(configurationProvider.getFeatureProvider()); ICustomContext context = EasyMock.createNiceMock(ICustomContext.class); EasyMock.replay(context); GenericFeatureCommandWithContext featureCommand = new GenericFeatureCommandWithContext(feature, context); CommandContainer commandContainer = new CommandContainer(configurationProvider.getFeatureProvider()); commandContainer.add(featureCommand); executeAndCheck(commandContainer, commandStack, editingDomain); } @Test public void testHasDoneChangesForAddObject() { IAddContext context = EasyMock.createNiceMock(IAddContext.class); EasyMock.replay(context); // Feature is set via EasyMock in init method AddModelObjectCommand featureCommand = new AddModelObjectCommand(configurationProvider, null, new StructuredSelection(new Object()), new Rectangle()); executeAndCheck(featureCommand, commandStack); } @Test public void testHasDoneChangesForContextEntry() { IContext context = EasyMock.createNiceMock(IContext.class); EasyMock.replay(context); IContextEntry contextEntry = EasyMock.createNiceMock(IContextEntry.class); EasyMock.expect(contextEntry.getFeature()).andReturn(new TestDeleteFeature(configurationProvider.getFeatureProvider())).anyTimes(); EasyMock.expect(contextEntry.getContext()).andReturn(context).anyTimes(); EasyMock.replay(contextEntry); ContextEntryCommand featureCommand = new ContextEntryCommand(contextEntry); executeAndCheck(featureCommand, commandStack); editingDomain.dispose(); } @Test public void testCanUsePlainTransactionalCommandStack() { // Test for https://bugs.eclipse.org/bugs/show_bug.cgi?id=351053 // Create an editing domain with a plain transactional command stack (Graphiti services create a // GFWorkspaceCommandStackImpl which is also a Workspace command stack) ResourceSet resourceSet = new ResourceSetImpl(); TransactionalEditingDomainImpl editingDomain = new TransactionalEditingDomainImpl(new ComposedAdapterFactory( ComposedAdapterFactory.Descriptor.Registry.INSTANCE), resourceSet); WorkspaceEditingDomainFactory.INSTANCE.mapResourceSet(editingDomain); // Create command stack using this editing domain GFCommandStack commandStack = new GFCommandStack(configurationProvider, editingDomain); // Create test feature and context TestStandardTransactionalEditingDomainFeature feature = new TestStandardTransactionalEditingDomainFeature( configurationProvider.getFeatureProvider()); ICustomContext context = EasyMock.createNiceMock(ICustomContext.class); EasyMock.replay(context); // Prepare execution and do it GenericFeatureCommandWithContext featureCommand = new GenericFeatureCommandWithContext(feature, context); CommandContainer commandContainer = new CommandContainer(configurationProvider.getFeatureProvider()); commandContainer.add(featureCommand); GefCommandWrapper commandWrapper = new GefCommandWrapper(featureCommand, editingDomain); commandStack.execute(commandWrapper); // Check assertTrue("Feature was not executed", feature.wasExecuted); // Clean-up local stuff editingDomain.dispose(); } private IConfigurationProvider initConfigurationProviderForHasDoneChangesTests(TransactionalEditingDomain editingDomain) { IToolBehaviorProvider toolBehaviorProvider = EasyMock.createNiceMock(IToolBehaviorProvider.class); EasyMock.replay(toolBehaviorProvider); IDiagramTypeProvider diagramTypeProvider = EasyMock.createNiceMock(IDiagramTypeProvider.class); IFeatureProvider featureProvider = EasyMock.createNiceMock(IFeatureProvider.class); EasyMock.expect(featureProvider.getDiagramTypeProvider()).andReturn(diagramTypeProvider).anyTimes(); EasyMock.expect(featureProvider.getAddFeature((IAddContext) EasyMock.notNull())) .andReturn(new TestAddObjectFeature(featureProvider)).anyTimes(); EasyMock.replay(featureProvider); EasyMock.expect(diagramTypeProvider.getCurrentToolBehaviorProvider()).andReturn(toolBehaviorProvider).anyTimes(); EasyMock.expect(diagramTypeProvider.getFeatureProvider()).andReturn(featureProvider).anyTimes(); EasyMock.replay(diagramTypeProvider); IConfigurationProvider configurationProvider = EasyMock.createNiceMock(IConfigurationProviderInternal.class); EasyMock.expect(configurationProvider.getDiagramTypeProvider()).andReturn(diagramTypeProvider).anyTimes(); EasyMock.replay(configurationProvider); return configurationProvider; } private void executeAndCheck(ICommand featureCommand, GFCommandStack commandStack, TransactionalEditingDomain editingDomain) { GefCommandWrapper commandWrapper = new GefCommandWrapper(featureCommand, editingDomain); commandStack.execute(commandWrapper); assertNull("Direct editing command must not appear in command stack", commandStack.getUndoCommand()); } private void executeAndCheck(Command command, GFCommandStack commandStack) { commandStack.execute(command); assertNull("Direct editing command must not appear in command stack", commandStack.getUndoCommand()); } private class TestDirectEditingFeature extends AbstractDirectEditingFeature { public TestDirectEditingFeature(IFeatureProvider featureProvider) { super(featureProvider); } public int getEditingType() { return TYPE_TEXT; } public String getInitialValue(IDirectEditingContext context) { return "Initial"; } @Override public void setValue(String value, IDirectEditingContext context) { // Do nothing } } private class TestCustomFeature extends AbstractCustomFeature { public TestCustomFeature(IFeatureProvider fp) { super(fp); } @Override public boolean canExecute(ICustomContext context) { return true; } public void execute(ICustomContext context) { // Do nothing } @Override public boolean hasDoneChanges() { return false; } } private class TestAddObjectFeature extends AbstractAddShapeFeature { public TestAddObjectFeature(IFeatureProvider fp) { super(fp); } public boolean canAdd(IAddContext context) { return true; } public PictogramElement add(IAddContext context) { // Do nothing return null; } @Override public boolean hasDoneChanges() { return false; } } private class TestDeleteFeature extends DefaultDeleteFeature { public TestDeleteFeature(IFeatureProvider fp) { super(fp); } @Override public boolean canDelete(IDeleteContext context) { return true; } @Override public void delete(IDeleteContext context) { // Do nothing } @Override public boolean hasDoneChanges() { return false; } } private class TestStandardTransactionalEditingDomainFeature extends AbstractCustomFeature { public boolean wasExecuted = false; public TestStandardTransactionalEditingDomainFeature(IFeatureProvider fp) { super(fp); } @Override public boolean canExecute(ICustomContext context) { return true; } public void execute(ICustomContext context) { wasExecuted = true; } } }