/****************************************************************************** * Copyright (c) 2005, 2006 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.tests.runtime.emf.type.core.commands; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import junit.framework.Test; import junit.framework.TestSuite; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.EAnnotation; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EPackage; import org.eclipse.emf.ecore.EReference; import org.eclipse.emf.ecore.EcoreFactory; import org.eclipse.emf.ecore.change.ChangeDescription; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.transaction.RecordingCommand; import org.eclipse.gmf.runtime.common.core.command.ICommand; import org.eclipse.gmf.runtime.common.core.command.ICompositeCommand; import org.eclipse.gmf.runtime.emf.type.core.ElementTypeRegistry; import org.eclipse.gmf.runtime.emf.type.core.IElementType; import org.eclipse.gmf.runtime.emf.type.core.commands.DestroyElementCommand; import org.eclipse.gmf.runtime.emf.type.core.requests.DestroyDependentsRequest; import org.eclipse.gmf.runtime.emf.type.core.requests.DestroyElementRequest; import org.eclipse.gmf.tests.runtime.emf.type.core.AbstractEMFTypeTest; import org.eclipse.gmf.tests.runtime.emf.type.core.employee.Client; import org.eclipse.gmf.tests.runtime.emf.type.core.employee.Customer; import org.eclipse.gmf.tests.runtime.emf.type.core.employee.EmployeeFactory; import org.eclipse.gmf.tests.runtime.emf.type.core.internal.ClientDependentsAdvice; import org.eclipse.gmf.tests.runtime.emf.type.core.internal.DestroyCustomerAdvice; /** * Tests the extensible {@link DestroyElementCommand}. * * @author Christian W. Damus (cdamus) */ public class DestroyElementCommandTest extends AbstractEMFTypeTest { static final String ANNOTATION_SOURCE = "org.eclipse.gmf.tests.runtime.emf.type.core"; //$NON-NLS-1$ private Customer parentCompany; private Customer customer; private Client billieJo; private Client jimBob; private EAnnotation billieJoAnnotation; private EAnnotation jimBobAnnotation; public DestroyElementCommandTest(String name) { super(name); } public static Test suite() { return new TestSuite(DestroyElementCommandTest.class, "DestroyElementCommand Tests"); //$NON-NLS-1$ } /** * Tests just the functionality of the destroy command, itself. */ public void test_destroyCommand_basic() { DestroyElementCommand cmd = new DestroyElementCommand( new DestroyElementRequest(billieJo, false)); execute(cmd); assertDestroyed(billieJo); // annotation was not destroyed assertSame(billieJoAnnotation.eContainer(), customer); // but reference to Billie Jo was cleared assertEquals(0, billieJoAnnotation.getReferences().size()); undo(cmd); assertSame(customer, billieJo.eContainer()); assertSame(billieJoAnnotation.eContainer(), customer); assertTrue(billieJoAnnotation.getReferences().contains(billieJo)); redo(cmd); assertDestroyed(billieJo); assertSame(billieJoAnnotation.eContainer(), customer); assertEquals(0, billieJoAnnotation.getReferences().size()); } /** * Tests that we cannot destroy detached elements. */ public void test_destroyCommand_detached() { Client detachedClient = getEmployeeFactory().createClient(); DestroyElementRequest req = new DestroyElementRequest(detachedClient, false); assertNull(req.getEditHelperContext()); DestroyElementCommand cmd = new DestroyElementCommand(req); assertFalse(cmd.canExecute()); } /** * Tests that dependents are correctly destroyed, as well. */ public void test_destroyCommand_withDependents() { DestroyElementRequest req = new DestroyElementRequest(billieJo, false); IElementType type = ElementTypeRegistry.getInstance().getElementType( req.getEditHelperContext()); assertNotNull(type); ICommand cmd = type.getEditCommand(req); assertNotNull(cmd); assertNoDuplicates(cmd); execute(cmd); assertDestroyed(billieJo); assertDestroyed(billieJoAnnotation); undo(cmd); assertSame(customer, billieJo.eContainer()); assertSame(billieJoAnnotation.eContainer(), customer); assertTrue(billieJoAnnotation.getReferences().contains(billieJo)); redo(cmd); assertDestroyed(billieJo); assertDestroyed(billieJoAnnotation); } /** * Tests that all contents (recursively) are correctly destroyed, as well. * Incidentally tests the destruction of resource roots. */ public void test_destroyCommand_withContainment() { // destroy the parent company (resource root) DestroyElementRequest req = new DestroyElementRequest(parentCompany, false); IElementType type = ElementTypeRegistry.getInstance().getElementType( req.getEditHelperContext()); assertNotNull(type); ICommand cmd = type.getEditCommand(req); assertNotNull(cmd); assertNoDuplicates(cmd); execute(cmd); assertDestroyed(parentCompany); assertDestroyed(customer); assertDestroyed(billieJo); assertDestroyed(billieJoAnnotation); assertDestroyed(jimBob); assertDestroyed(jimBobAnnotation); undo(cmd); assertSame(getResource(), parentCompany.eResource()); assertSame(parentCompany, customer.eContainer()); assertSame(customer, billieJo.eContainer()); assertSame(billieJoAnnotation.eContainer(), customer); assertTrue(billieJoAnnotation.getReferences().contains(billieJo)); assertSame(customer, jimBob.eContainer()); assertSame(jimBobAnnotation.eContainer(), customer); assertTrue(jimBobAnnotation.getReferences().contains(jimBob)); redo(cmd); assertDestroyed(parentCompany); assertDestroyed(customer); assertDestroyed(billieJo); assertDestroyed(billieJoAnnotation); assertDestroyed(jimBob); assertDestroyed(jimBobAnnotation); } /** * Tests that we completely destroy cross-resource-contained objects, also * (removing them from their resource). */ public void test_destroy_crossResourceContained_136738() { // use the Ecore metamodel because EXTLibrary does not have any // cross-resource containment support // create a couple of resources final Resource res1 = getEditingDomain().getResourceSet().createResource( URI.createURI("null://res1.ecore")); //$NON-NLS-1$ final Resource res2 = getEditingDomain().getResourceSet().createResource( URI.createURI("null://res2.ecore")); //$NON-NLS-1$ // the Ecore model final EPackage pkg1 = EcoreFactory.eINSTANCE.createEPackage(); pkg1.setName("package1"); //$NON-NLS-1$ final EClass class1 = EcoreFactory.eINSTANCE.createEClass(); class1.setName("Class1"); //$NON-NLS-1$ final EClass class2 = EcoreFactory.eINSTANCE.createEClass(); class2.setName("Class2"); //$NON-NLS-1$ // establish cross-resource containment RecordingCommand command = new RecordingCommand(getEditingDomain()) { protected void doExecute() { res1.getContents().add(pkg1); pkg1.getEClassifiers().add(class1); pkg1.getEClassifiers().add(class2); class2.getESuperTypes().add(class1); // set a reference feature res2.getContents().add(class2); }}; getEditingDomain().getCommandStack().execute(command); // check that we have cross-resource containment assertSame(pkg1, class2.eContainer()); assertSame(res2, class2.eResource()); DestroyElementRequest req = new DestroyElementRequest(class2, false); IElementType type = ElementTypeRegistry.getInstance().getElementType( req.getEditHelperContext()); assertNotNull(type); ICommand cmd = type.getEditCommand(req); assertNotNull(cmd); assertNoDuplicates(cmd); execute(cmd); assertDestroyed(class2); assertNull(class2.eResource()); undo(cmd); assertSame(pkg1, class2.eContainer()); assertSame(res2, class2.eResource()); assertTrue(class2.getESuperTypes().contains(class1)); // check the reference redo(cmd); assertDestroyed(class2); assertNull(class2.eResource()); } /** * Tests that the element to destroy in a DestroyRequest is the same in the * before advice as it is in the after advice. */ public void test_preserveElementToDestroy_142561() { DestroyElementRequest req = new DestroyElementRequest(parentCompany, false); IElementType type = ElementTypeRegistry.getInstance().getElementType( req.getEditHelperContext()); req.setParameter(DestroyCustomerAdvice.BEFORE, new ArrayList()); req.setParameter(DestroyCustomerAdvice.AFTER, new ArrayList()); assertNotNull(type); ICommand cmd = type.getEditCommand(req); assertNotNull(cmd); assertNoDuplicates(cmd); execute(cmd); assertDestroyed(parentCompany); // verify that the after advice sees the same elements to destroy in the reverse order List before = (List) req.getParameter(DestroyCustomerAdvice.BEFORE); List after = (List) req.getParameter(DestroyCustomerAdvice.AFTER); Collections.reverse(after); assertEquals(before, after); } /** * Verifies the advice on the DestroyDependentsRequest (e.g., * ClientDependentsAdvice) can access the initial element that was requested * to be destroyed. */ public void test_initialElementToBeDestroyed_146559() { DestroyElementRequest req = new DestroyElementRequest(parentCompany, false); IElementType type = ElementTypeRegistry.getInstance().getElementType( req.getEditHelperContext()); assertNotNull(type); ICommand cmd = type.getEditCommand(req); assertNotNull(cmd); assertNoDuplicates(cmd); execute(cmd); assertDestroyed(parentCompany); // verify that the dependents advice sees the same initial element to destroy DestroyDependentsRequest dependentsRequest = (DestroyDependentsRequest) req .getParameter(DestroyElementRequest.DESTROY_DEPENDENTS_REQUEST_PARAMETER); Object initial = dependentsRequest .getParameter(ClientDependentsAdvice.INITIAL); assertEquals(parentCompany, initial); } // // Test framework methods // protected void doModelSetup(Resource resource) { parentCompany = getEmployeeFactory().createCustomer(); parentCompany.setName("GlobalMega"); //$NON-NLS-1$ resource.getContents().add(parentCompany); customer = getEmployeeFactory().createCustomer(); customer.setName("Acme"); //$NON-NLS-1$ parentCompany.getSubsidiaries().add(customer); billieJo = createClient("Billie Jo", "Swanson", customer); //$NON-NLS-1$ //$NON-NLS-2$ billieJoAnnotation = createAnnotation(billieJo); jimBob = createClient("Jim Bob", "Jones", customer); //$NON-NLS-1$ //$NON-NLS-2$ jimBobAnnotation = createAnnotation(jimBob); } protected Client createClient(String firstName, String lastName, Customer _customer) { Client result = EmployeeFactory.eINSTANCE.createClient(); result.setFirstName(firstName); result.setLastName(lastName); _customer.getRepresentatives().add(result); return result; } protected EAnnotation createAnnotation(Client client) { EAnnotation result = EcoreFactory.eINSTANCE.createEAnnotation(); result.setSource(ANNOTATION_SOURCE); result.getReferences().add(client); client.getRepresents().getEAnnotations().add(result); return result; } protected void assertDestroyed(EObject eObject) { assertTrue((eObject.eContainer() == null) || (eObject.eContainer() instanceof ChangeDescription)); // no references exist to any other object. Note that there should not // be a container reference of type ChangeDescription :-) for (Iterator iter = eObject.eClass().getEAllReferences().iterator(); iter.hasNext();) { EReference next = (EReference) iter.next(); assertFalse(eObject.eIsSet(next)); } // in case it was a root object assertFalse(getResource().getContents().contains(eObject)); } /** * Tests that there are no duplicate commands to destroy the same element in * <code>cmd</code>. Used to verify Bugzilla 145763. * * @param cmd * the command to test */ private void assertNoDuplicates(ICommand cmd) { assertNoDuplicatesImpl(cmd, new ArrayList()); } private void assertNoDuplicatesImpl(ICommand cmd, List toDestroy) { if (cmd instanceof DestroyElementCommand) { try { Method getElementToDestroy = DestroyElementCommand.class .getDeclaredMethod("getElementToDestroy", null); //$NON-NLS-1$ getElementToDestroy.setAccessible(true); EObject element = (EObject) getElementToDestroy.invoke( cmd, null); if (toDestroy.contains(element)) { fail("Duplicate destroy command for: " + element); //$NON-NLS-1$ } toDestroy.add(element); } catch (NoSuchMethodException nsme) { fail("Unexpected exception: " + nsme); //$NON-NLS-1$ } catch (IllegalAccessException iae) { fail("Unexpected exception: " + iae); //$NON-NLS-1$ } catch (InvocationTargetException ite) { fail("Unexpected exception: " + ite); //$NON-NLS-1$ } } else if (cmd instanceof ICompositeCommand) { for (Iterator i = ((ICompositeCommand) cmd).iterator(); i.hasNext();) { ICommand next = (ICommand) i.next(); assertNoDuplicatesImpl(next, toDestroy); } } } }