/* * Copyright (c) 2006, 2010 Borland Software 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: * bblajer - initial API and implementation */ package org.eclipse.gmf.tests.lite.gef; import java.util.Collection; import junit.framework.Assert; import junit.framework.AssertionFailedError; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.draw2d.geometry.Point; import org.eclipse.emf.codegen.ecore.genmodel.GenFeature; import org.eclipse.emf.common.command.CompoundCommand; import org.eclipse.emf.common.notify.Adapter; import org.eclipse.emf.common.notify.Notification; import org.eclipse.emf.common.notify.impl.AdapterImpl; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EPackage; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.emf.edit.command.AddCommand; import org.eclipse.emf.edit.command.SetCommand; import org.eclipse.emf.transaction.TransactionalEditingDomain; import org.eclipse.emf.transaction.util.TransactionUtil; import org.eclipse.emf.workspace.EMFCommandOperation; import org.eclipse.gef.EditPart; import org.eclipse.gef.RequestConstants; import org.eclipse.gef.commands.Command; import org.eclipse.gef.requests.ChangeBoundsRequest; import org.eclipse.gmf.codegen.gmfgen.GenCompartment; import org.eclipse.gmf.codegen.gmfgen.GenLink; import org.eclipse.gmf.codegen.gmfgen.GenNode; import org.eclipse.gmf.runtime.notation.Diagram; import org.eclipse.gmf.runtime.notation.DrawerStyle; import org.eclipse.gmf.runtime.notation.Edge; import org.eclipse.gmf.runtime.notation.Node; import org.eclipse.gmf.runtime.notation.NotationPackage; import org.eclipse.gmf.runtime.notation.View; import org.eclipse.gmf.tests.lite.gen.LiteGeneratorConfiguration; import org.eclipse.gmf.tests.rt.GeneratedCanvasTest; public class DiagramNodeCloneMoveTest extends GeneratedCanvasTest { public DiagramNodeCloneMoveTest(String name) { super(name, new LiteGeneratorConfiguration()); } public void testClone() throws Exception { GenNode nodeB = getSetup().getGenModel().getNodeB(); assertTrue("Incorrect Setup: passed node has no compartments", nodeB.getCompartments().size() > 0); GenCompartment genCompartment = nodeB.getCompartments().get(0); assertTrue("Incorrect Setup: passed node has no children", genCompartment.getChildNodes().size() > 0); GenNode childNode = genCompartment.getChildNodes().get(0); Node nodeBInstance = getCanvasInstance().getNodeB(); Diagram diagram = nodeBInstance.getDiagram(); Node clonedB = checkClone(nodeBInstance, diagram, diagram, nodeB, nodeB); //Check child Node level1Child = createNodeIndirect(childNode, getCanvasInstance().getNodeBCompartment()); assertNotNull("Level1 ChildNode was not created", level1Child); Node clonedLevel1Child = checkClone(level1Child, getCanvasInstance().getNodeBCompartment(), getCanvasInstance().getNodeBCompartment(), childNode, childNode); Node clonedBCompartment = (Node) findChildView(clonedB, genCompartment); //Finally, check cloning to another element. checkClone(clonedLevel1Child, clonedBCompartment, getCanvasInstance().getNodeBCompartment(), childNode, childNode); } public void testMove() throws Exception { GenNode nodeB = getSetup().getGenModel().getNodeB(); assertTrue("Incorrect Setup: passed node has no compartments", nodeB.getCompartments().size() > 0); GenCompartment genCompartment = nodeB.getCompartments().get(0); assertTrue("Incorrect Setup: passed node has no children", genCompartment.getChildNodes().size() > 0); GenNode childNode = genCompartment.getChildNodes().get(0); Node nodeBInstance = getCanvasInstance().getNodeB(); Diagram diagram = nodeBInstance.getDiagram(); Node clonedB = checkClone(nodeBInstance, diagram, diagram, nodeB, nodeB); Node level1Child = createNodeIndirect(childNode, getCanvasInstance().getNodeBCompartment()); assertNotNull("Level1 ChildNode was not created", level1Child); Node clonedBCompartment = (Node) findChildView(clonedB, genCompartment); checkMove(level1Child, clonedBCompartment, getCanvasInstance().getNodeBCompartment(), childNode, childNode); } public void testCloneWithVisualIDChange() throws Exception { GenNode nodeA = getSetup().getGenModel().getNodeA(); GenNode nodeB = getSetup().getGenModel().getNodeB(); GenCompartment genCompartment = nodeA.getCompartments().get(0); assertNotNull("Node A has no compartments", genCompartment); GenNode nodeBAsChildOfA = genCompartment.getChildNodes().get(0); assertNotNull("Node A compartment has no children", nodeBAsChildOfA); assertEquals("Incorrect child", nodeB.getModelFacet().getMetaClass().getEcoreClass().getName(), nodeBAsChildOfA.getModelFacet().getMetaClass().getEcoreClass().getName()); Node nodeAInstance = getCanvasInstance().getNodeA(); Node compartmentA = (Node) findChildView(nodeAInstance, genCompartment); assertNotNull("Compartment not found", compartmentA); Node nodeBInstance = getCanvasInstance().getNodeB(); Diagram diagram = nodeBInstance.getDiagram(); checkClone(nodeBInstance, compartmentA, diagram, nodeB, nodeBAsChildOfA); } public void testMoveWithVisualIDChange() throws Exception { GenNode nodeA = getSetup().getGenModel().getNodeA(); GenNode nodeB = getSetup().getGenModel().getNodeB(); GenCompartment genCompartment = nodeA.getCompartments().get(0); assertNotNull("Node A has no compartments", genCompartment); GenNode nodeBAsChildOfA = genCompartment.getChildNodes().get(0); assertNotNull("Node A compartment has no children", nodeBAsChildOfA); assertSame("Incorrect child", nodeB.getModelFacet().getMetaClass(), nodeBAsChildOfA.getModelFacet().getMetaClass()); Node nodeAInstance = getCanvasInstance().getNodeA(); Node compartmentA = (Node) findChildView(nodeAInstance, genCompartment); assertNotNull("Compartment not found", compartmentA); Node nodeBInstance = getCanvasInstance().getNodeB(); Diagram diagram = nodeBInstance.getDiagram(); checkMove(nodeBInstance, compartmentA, diagram, nodeB, nodeBAsChildOfA); } private Node checkClone(Node node, View newContainer, View originalContainer, GenNode originalGenNode, GenNode expectedGenNode) { assertEquals(originalGenNode.getVisualID(), Integer.parseInt(node.getType())); EditPart nodeEP = findEditPart(node); assertNotNull(nodeEP); assertTrue(nodeEP.isActive()); checkContainment(node, originalContainer, originalGenNode.getModelFacet().getContainmentMetaFeature(), originalGenNode.getModelFacet().getChildMetaFeature()); Node clone = cloneOrMoveNode(node, newContainer, true); assertNotNull("Clone command returned null", clone); assertEquals(expectedGenNode.getVisualID(), Integer.parseInt(clone.getType())); EditPart clonedEP = findEditPart(clone); assertNotNull("Failed to find the edit part for the cloned node", clonedEP); assertTrue(clonedEP.isActive()); checkContainment(clone, newContainer, expectedGenNode.getModelFacet().getContainmentMetaFeature(), expectedGenNode.getModelFacet().getChildMetaFeature()); assertTrue(nodeEP.isActive()); //make sure clone has not affected the original node. checkContainment(node, originalContainer, originalGenNode.getModelFacet().getContainmentMetaFeature(), originalGenNode.getModelFacet().getChildMetaFeature()); return clone; } private Node checkMove(Node node, View newContainer, View originalContainer, GenNode originalGenNode, GenNode expectedGenNode) { assertEquals(originalGenNode.getVisualID(), Integer.parseInt(node.getType())); EditPart nodeEP = findEditPart(node); assertNotNull(nodeEP); assertTrue(nodeEP.isActive()); checkContainment(node, originalContainer, originalGenNode.getModelFacet().getContainmentMetaFeature(), originalGenNode.getModelFacet().getChildMetaFeature()); Node moved = cloneOrMoveNode(node, newContainer, false); assertNotNull("Move command returned null", moved); assertEquals(expectedGenNode.getVisualID(), Integer.parseInt(moved.getType())); EditPart movedEP = findEditPart(moved); assertNotNull("Failed to find the edit part for the cloned node", movedEP); assertTrue(movedEP.isActive()); checkContainment(moved, newContainer, expectedGenNode.getModelFacet().getContainmentMetaFeature(), expectedGenNode.getModelFacet().getChildMetaFeature()); assertFalse(nodeEP.isActive()); //make sure clone has removed the original node. assertFalse(originalContainer.getChildren().contains(node)); //Do not check that the node is uncontained. Implementation may move the node by actually placing the same instance to another place. // checkContainment(node, null, originalGenNode.getModelFacet().getContainmentMetaFeature(), originalGenNode.getModelFacet().getChildMetaFeature()); return moved; } private void checkContainment(View child, View parent, GenFeature containmentMetaFeature, GenFeature childMetaFeature) { assertSame("Incorrect notation-model containment", parent, child.eContainer()); EObject childEObject = child.getElement(); if (parent == null) { assertNull("Incorrect domain-model containment", childEObject.eContainer()); return; } EObject parentEObject = parent.getElement(); assertSame("Incorrect domain-model containment", parentEObject, childEObject.eContainer()); EStructuralFeature containmentFeature = parentEObject.eClass().getEStructuralFeature(containmentMetaFeature.getEcoreFeature().getName()); assertNotNull("Failed to find containment feature", containmentFeature); assertSame("Incorrect domain-model containment feature", containmentFeature, childEObject.eContainmentFeature()); if (childMetaFeature != null) { EStructuralFeature childFeature = parentEObject.eClass().getEStructuralFeature(childMetaFeature.getEcoreFeature().getName()); assertNotNull("Failed to find child feature", childFeature); assertTrue("Child feature not set", ((Collection<?>)parentEObject.eGet(childFeature)).contains(childEObject)); } } private Node cloneOrMoveNode(Node node, View container, boolean isCloneNotMove) { final Object[] resultHolder = new Object[1]; EditPart containerEP = findEditPart(container); Adapter adapter = new AdapterImpl() { public void notifyChanged(Notification msg) { super.notifyChanged(msg); if (msg.getEventType() == Notification.ADD) { resultHolder[0] = msg.getNewValue(); } } public boolean isAdapterForType(Object type) { return true; } }; EditPart nodeEP = findEditPart(node); assertNotNull("No edit part available for the node being cloned", nodeEP); Command cmd = null; if (!isCloneNotMove) { ChangeBoundsRequest orphanRequest = new ChangeBoundsRequest(RequestConstants.REQ_ORPHAN); orphanRequest.setLocation(new Point(0, 0)); orphanRequest.setEditParts(nodeEP); cmd = nodeEP.getCommand(orphanRequest); } ChangeBoundsRequest cloneRequest = new ChangeBoundsRequest(isCloneNotMove ? RequestConstants.REQ_CLONE : RequestConstants.REQ_ADD); cloneRequest.setLocation(new Point(0,0)); cloneRequest.setEditParts(nodeEP); cmd = merge(cmd, containerEP.getCommand(cloneRequest)); Assert.assertNotNull("No command is available for request", cmd); //$NON-NLS-1$ container.eAdapters().add(adapter); try { execute(cmd); } catch (Exception e) { e.printStackTrace(); Assert.fail("Node creation failure: " + e.getLocalizedMessage()); //$NON-NLS-1$ } finally { container.eAdapters().remove(adapter); } assertTrue("Failed to create notation model Node", resultHolder[0] instanceof Node); //$NON-NLS-1$ return (Node) resultHolder[0]; } /** * The setup we use does not provide palette, so direct node creation is impossible. * Workaround by creating the node in the domain model. * Works only if the created domain node is in the same EPackage as the container node. */ protected Node createNodeIndirect(GenNode nodeType, View notationContainer) { final Object[] resultHolder = new Object[1]; Adapter adapter = new AdapterImpl() { public void notifyChanged(Notification msg) { super.notifyChanged(msg); if (msg.getEventType() == Notification.ADD) { resultHolder[0] = msg.getNewValue(); } } public boolean isAdapterForType(Object type) { return true; } }; EObject container = notationContainer.getElement(); assertNotNull(container); TransactionalEditingDomain editingDomain = TransactionUtil.getEditingDomain(container); EPackage ePackage = container.eClass().getEPackage(); EObject createdElement = ePackage.getEFactoryInstance().create((EClass) ePackage.getEClassifier(nodeType.getDomainMetaClass().getEcoreClass().getName())); CompoundCommand compoundCommand = new CompoundCommand(); EStructuralFeature containmentFeature = container.eClass().getEStructuralFeature(nodeType.getModelFacet().getContainmentMetaFeature().getEcoreFeature().getName()); assertNotNull(containmentFeature); compoundCommand.append(AddCommand.create(editingDomain, container, containmentFeature, createdElement)); EStructuralFeature childFeature = container.eClass().getEStructuralFeature(nodeType.getModelFacet().getChildMetaFeature().getEcoreFeature().getName()); if (childFeature != null && !childFeature.isDerived() && childFeature != containmentFeature) { compoundCommand.append(AddCommand.create(editingDomain, container, childFeature, createdElement)); } assertTrue(compoundCommand.canExecute()); notationContainer.eAdapters().add(adapter); try { new EMFCommandOperation(editingDomain, compoundCommand).execute(new NullProgressMonitor(), null); } catch (Exception e) { e.printStackTrace(); Assert.fail("Node creation failure: " + e.getLocalizedMessage()); //$NON-NLS-1$ } finally { notationContainer.eAdapters().remove(adapter); } assertTrue("Failed to indirectly create notation model Node", resultHolder[0] instanceof Node); //$NON-NLS-1$ return (Node) resultHolder[0]; } private Command merge(Command one, Command another) { if (one == null) { return another; } if (another == null) { return one; } return one.chain(another); } public void testNodeMultiplicity() throws Exception { Node writer = getCanvasInstance().getNodeA(); final Node book = getCanvasInstance().getNodeB(); GenNode writerGenNode = getSetup().getGenModel().getNodeA(); GenCompartment brochuresCompartment = writerGenNode.getCompartments().get(0); final GenNode brochuresGenNode = brochuresCompartment.getChildNodes().get(0); final View writerCompartment = findChildView(writer, brochuresCompartment); cloneOrMoveNode(book, writerCompartment, true); final Node secondBrochure = createNode(brochuresGenNode, writerCompartment); shouldFail("Should not be possible to create a third node here", new Runnable() { public void run() { createNode(brochuresGenNode, writerCompartment); } }); shouldFail("Should not be possible to clone a third node here", new Runnable() { public void run() { cloneOrMoveNode(secondBrochure, writerCompartment, true); } }); shouldFail("Should not be possible to move a third node here", new Runnable() { public void run() { cloneOrMoveNode(book, writerCompartment, false); } }); } public void testLinkMultiplicity() throws Exception { final Node writer = getCanvasInstance().getNodeA(); final Node book = getCanvasInstance().getNodeB(); final GenLink opinionGenLink = getSetup().getGenModel().getLinkC(); Edge opinion1 = getCanvasInstance().getLinkByClass(); Edge opinion2 = createLink(opinionGenLink, writer, book); assertNotNull(opinion1); assertNotNull(opinion2); shouldFail("Should not be possible to create a third link", new Runnable() { public void run() { Edge opinion3 = createLink(opinionGenLink, writer, book); assertNotNull(opinion3); } }); } public void testLinkTargetFeatureInverseMultiplicity() throws Exception { final Node writer1 = getCanvasInstance().getNodeA(); final Node book = getCanvasInstance().getNodeB(); final GenLink opinionGenLink = getSetup().getGenModel().getLinkC(); Edge opinion1 = getCanvasInstance().getLinkByClass(); assertNotNull(opinion1); final Node writer2 = createNode(getSetup().getGenModel().getNodeA(), writer1.getDiagram()); Edge opinion2 = createLink(opinionGenLink, writer2, book); assertNotNull(opinion2); final Node writer3 = createNode(getSetup().getGenModel().getNodeA(), writer1.getDiagram()); shouldFail("Should not be possible to create a third incoming link to book: Book::opinions multiplicity is 2", new Runnable() { public void run() { Edge opinion3 = createLink(opinionGenLink, writer3, book); assertNotNull(opinion3); } }); } private void shouldFail(String msg, Runnable r) { try { r.run(); } catch (AssertionFailedError e) { return; } fail(msg); } public void testCompartmentCollapsibility() throws Exception { GenNode nodeB = getSetup().getGenModel().getNodeB(); assertTrue("Incorrect Setup: passed node has no compartments", nodeB.getCompartments().size() > 0); GenCompartment genCompartment = nodeB.getCompartments().get(0); assertTrue("Incorrect Setup: passed node has no children", genCompartment.getChildNodes().size() > 0); GenNode childNode = genCompartment.getChildNodes().get(0); Node nodeBInstance = createNode(nodeB, getDiagram()); Node nodeBCompartment = (Node) findChildView(nodeBInstance, genCompartment); assertNotNull("Failed to find the compartment", nodeBCompartment); DrawerStyle drawerStyle = (DrawerStyle) nodeBCompartment.getStyle(NotationPackage.eINSTANCE.getDrawerStyle()); assertNotNull("Drawer style not added automatically", drawerStyle); assertFalse("Compartment should be expanded by default", drawerStyle.isCollapsed()); Node level1Child = createNode(childNode, nodeBCompartment); assertNotNull("Child not created", level1Child); assertTrue(nodeBCompartment.getChildren().contains(level1Child)); EditPart compartmentEP = findEditPart(nodeBCompartment); assertNotNull("Edit part for compartment is missing", compartmentEP); EditPart childEP = findEditPart(level1Child); assertNotNull("Edit part for child is missing", childEP); assertTrue("Edit part for child is inactive", childEP.isActive()); assertSame("Unexpected parent of the child edit part", compartmentEP, childEP.getParent()); TransactionalEditingDomain domain = TransactionUtil.getEditingDomain(drawerStyle); org.eclipse.emf.common.command.Command command = SetCommand.create(domain, drawerStyle, NotationPackage.eINSTANCE.getDrawerStyle_Collapsed(), Boolean.TRUE); assertTrue("Failed to obtain command to collapse the compartment", command != null && command.canExecute()); new EMFCommandOperation(domain, command).execute(new NullProgressMonitor(), null); assertTrue("Compartment failed to collapse", drawerStyle.isCollapsed()); assertNotNull("Collapsing compartment should not have removed the view", level1Child.eResource()); assertFalse("Collapsing compartment should have removed the child edit part", childEP.isActive()); childEP = findEditPart(level1Child); assertNull("Collapsing compartment should have made the child edit part go away", childEP); command = SetCommand.create(domain, drawerStyle, NotationPackage.eINSTANCE.getDrawerStyle_Collapsed(), Boolean.FALSE); assertTrue("Failed to obtain command to expand the compartment", command != null && command.canExecute()); new EMFCommandOperation(domain, command).execute(new NullProgressMonitor(), null); assertFalse("Compartment failed to expand", drawerStyle.isCollapsed()); assertNotNull("Expanding compartment should not have done anything to the view", level1Child.eResource()); childEP = findEditPart(level1Child); assertTrue("Expanding compartment should have made the child edit part to reappear", childEP.isActive()); } public void testCompartmentExpandsOnAddingToIt() throws Exception { GenNode nodeB = getSetup().getGenModel().getNodeB(); assertTrue("Incorrect Setup: passed node has no compartments", nodeB.getCompartments().size() > 0); GenCompartment genCompartment = nodeB.getCompartments().get(0); assertTrue("Incorrect Setup: passed node has no children", genCompartment.getChildNodes().size() > 0); GenNode childNode = genCompartment.getChildNodes().get(0); Node nodeBInstance = createNode(nodeB, getDiagram()); Node nodeBCompartment = (Node) findChildView(nodeBInstance, genCompartment); assertNotNull("Failed to find the compartment", nodeBCompartment); DrawerStyle drawerStyle = (DrawerStyle) nodeBCompartment.getStyle(NotationPackage.eINSTANCE.getDrawerStyle()); assertNotNull("Drawer style not added automatically", drawerStyle); TransactionalEditingDomain domain = TransactionUtil.getEditingDomain(drawerStyle); //Collapse the compartment org.eclipse.emf.common.command.Command command = SetCommand.create(domain, drawerStyle, NotationPackage.eINSTANCE.getDrawerStyle_Collapsed(), Boolean.TRUE); assertTrue("Failed to obtain command to collapse the compartment", command != null && command.canExecute()); new EMFCommandOperation(domain, command).execute(new NullProgressMonitor(), null); assertTrue("Compartment failed to collapse", drawerStyle.isCollapsed()); //1. Create a child, check that the compartment has expanded in response. Node child = createNode(childNode, nodeBCompartment); assertNotNull("Child not created", child); assertTrue(nodeBCompartment.getChildren().contains(child)); assertFalse("Compartment failed to expand automatically on adding a new child", drawerStyle.isCollapsed()); Node nodeBClone = cloneOrMoveNode(nodeBInstance, getDiagram(), true); assertNotNull("Failed to clone node", nodeBClone); View compartmentClone = findChildView(nodeBClone, genCompartment); assertNotNull("Cloning node failed to clone its child compartment", compartmentClone); Node childClone = (Node) findChildView(compartmentClone, childNode); //Collapse the compartment again. command = SetCommand.create(domain, drawerStyle, NotationPackage.eINSTANCE.getDrawerStyle_Collapsed(), Boolean.TRUE); assertTrue("Failed to obtain command to collapse the compartment", command != null && command.canExecute()); new EMFCommandOperation(domain, command).execute(new NullProgressMonitor(), null); assertTrue("Compartment failed to collapse", drawerStyle.isCollapsed()); //2. Clone a node onto the compartment, check the compartment has expanded in response. Node anotherChild = cloneOrMoveNode(childClone, nodeBCompartment, true); assertNotNull("Clone not created", anotherChild); assertTrue(nodeBCompartment.getChildren().contains(anotherChild)); assertFalse("Compartment failed to expand automatically on adding a new child", drawerStyle.isCollapsed()); //Collapse the compartment again. command = SetCommand.create(domain, drawerStyle, NotationPackage.eINSTANCE.getDrawerStyle_Collapsed(), Boolean.TRUE); assertTrue("Failed to obtain command to collapse the compartment", command != null && command.canExecute()); new EMFCommandOperation(domain, command).execute(new NullProgressMonitor(), null); assertTrue("Compartment failed to collapse", drawerStyle.isCollapsed()); //3. Move a node onto the compartment, check the compartment has expanded in response. anotherChild = cloneOrMoveNode(childClone, nodeBCompartment, false); assertNotNull("Clone not created", anotherChild); assertTrue(nodeBCompartment.getChildren().contains(anotherChild)); assertFalse("Compartment failed to expand automatically on adding a new child", drawerStyle.isCollapsed()); } }