/*******************************************************************************
* Copyright (c) 2006-2012
* Software Technology Group, Dresden University of Technology
* DevBoost GmbH, Berlin, Amtsgericht Charlottenburg, HRB 140026
*
* 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:
* Software Technology Group - TU Dresden, Germany;
* DevBoost GmbH - Berlin, Germany
* - initial API and implementation
******************************************************************************/
package org.reuseware.lacome.layoutlanguage.gmf;
import java.util.Iterator;
import java.util.List;
import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.gmf.runtime.draw2d.ui.mapmode.MapModeUtil;
import org.eclipse.gmf.runtime.notation.Diagram;
import org.eclipse.gmf.runtime.notation.Edge;
import org.eclipse.gmf.runtime.notation.LayoutConstraint;
import org.eclipse.gmf.runtime.notation.Location;
import org.eclipse.gmf.runtime.notation.Node;
import org.eclipse.gmf.runtime.notation.NotationFactory;
import org.eclipse.gmf.runtime.notation.NotationPackage;
import org.eclipse.gmf.runtime.notation.Size;
import org.eclipse.gmf.runtime.notation.View;
import org.reuseware.lacome.Bounds;
import org.reuseware.lacome.CompositionDiagramUtil;
import org.reuseware.lacome.DiagramDescription;
import org.reuseware.lacome.strategy.DiagramMerger;
/**
* Diagram merger that can handle GMF diagrams.
*/
public class GMFMerger implements DiagramMerger {
/**
* @param receivingDiagram the diagram to be extended
* @return true if the first element in the receiving diagram is an instance of
* org.eclipse.gmf.runtime.notation.Diagram.
*/
public boolean canMerge(DiagramDescription receivingDiagram) {
if (!receivingDiagram.getDiagramRoots().isEmpty()) {
return receivingDiagram.getDiagramRoots().get(0) instanceof Diagram;
}
return false;
}
/**
* Universal implementation of merge for all GMF diagrams.
*
* @param contributingDiagrams the GMF diagrams of the contributing fragments
* @param receivingDiagram the GMF diagram of the receiving fragment
* @param additionalDiagrams other GMF diagrams that are processed in another composition
* step of the same composition program execution
*/
public void merge(List<DiagramDescription> contributingDiagrams,
DiagramDescription receivingDiagram, List<DiagramDescription> additionalDiagrams) {
EList<EObject> allDiagrams = new BasicEList<EObject>();
allDiagrams.addAll(receivingDiagram.getDiagramRoots());
for (DiagramDescription contributingDiagram : contributingDiagrams) {
allDiagrams.addAll(contributingDiagram.getDiagramRoots());
}
//diagram composition
for (DiagramDescription sourceDiagram : contributingDiagrams) {
updateCoordinatesOfAllNodes(sourceDiagram);
for (EObject sDiagram : sourceDiagram.getDiagramRoots()) {
for (EObject tDiagram : receivingDiagram.getDiagramRoots()) {
mergeDiagrams(sDiagram, tDiagram, receivingDiagram.getContents(), allDiagrams);
break;
}
}
}
}
/**
* Removes a view if its semantic element was removed.
*
* @param element the element that was target of a cleanup
* @param feature the feature modified during cleanup
* @param removedValue the old value of the feature before cleanup
* @return true if element was removed from diagram
*/
public boolean cleanup(EObject element, EStructuralFeature feature,
EObject removedValue) {
if (NotationPackage.Literals.VIEW__ELEMENT.equals(feature)) {
EcoreUtil.remove(element);
return true;
}
return false;
}
/**
* This method updates the <tt>Location</tt> of the GMF nodes.
*/
private void updateCoordinatesOfAllNodes(DiagramDescription diagram) {
int xsource = diagram.getSourceBounds().getX();
int ysource = diagram.getSourceBounds().getY();
int xtarget = diagram.getTargetBounds().getX();
int ytarget = diagram.getTargetBounds().getY();
int sourceMU = diagram.getSourceBounds().getMeasurementUnit();
int targetMU = diagram.getTargetBounds().getMeasurementUnit();
//convert source diagram units to target diagram units
if (targetMU != Bounds.MU_NULL) {
if (sourceMU != Bounds.MU_NULL) {
if (sourceMU != targetMU) {
if (sourceMU == Bounds.MU_PIXEL) {
xsource = MapModeUtil.getMapMode().LPtoDP(xsource);
ysource = MapModeUtil.getMapMode().LPtoDP(ysource);
} else if (sourceMU == Bounds.MU_HIMETRIC) {
xsource = MapModeUtil.getMapMode().DPtoLP(xsource);
ysource = MapModeUtil.getMapMode().DPtoLP(ysource);
}
}
}
}
//set location attributes of all nodes
for (EObject gmfDiagram : diagram.getDiagramRoots()) {
TreeIterator<EObject> treeIterator = gmfDiagram.eAllContents();
while (treeIterator.hasNext()) {
EObject next = treeIterator.next();
if (next instanceof Node) {
Node node = (Node) next;
LayoutConstraint location = node.getLayoutConstraint();
LayoutConstraint size = node.getLayoutConstraint();
if (location instanceof Location && size instanceof Size) {
org.eclipse.gmf.runtime.notation.Bounds bounds =
NotationFactory.eINSTANCE.createBounds();
int xold = ((Location) location).getX();
int yold = ((Location) location).getY();
//keeps the original size of the node
bounds.setHeight(((Size) size).getHeight());
bounds.setWidth(((Size) size).getWidth());
//sets the new coordinates
bounds.setX(xtarget + xold - xsource);
bounds.setY(ytarget + yold - ysource);
node.setLayoutConstraint(bounds);
}
treeIterator.prune();
}
}
}
}
private void mergeDiagrams(EObject contributingDiagramRoot, EObject receivingDiagramRoot,
EList<EObject> receivingContentRoot, EList<EObject> allDiagrams) {
boolean change = true;
while (change) {
TreeIterator<EObject> diagramElIt = contributingDiagramRoot.eAllContents();
if (!diagramElIt.hasNext()) {
change = false;
} else {
while (diagramElIt.hasNext()) {
change = false;
EObject diagramEl = diagramElIt.next();
EObject element = findSematicModelElement(diagramEl);
List<EObject> replacedElements = CompositionDiagramUtil.getReplacedValues(element);
//for comments (Notes) without semantic element (GMF)
if (element == null && diagramEl instanceof Node) {
Node node = (Node) diagramEl;
if (node.getType().equals("Note")) {
addToSuitableReference(
(View) receivingDiagramRoot, node, replacedElements);
change = true;
}
}
//for nodes with semantic model element
if (receivingContentRoot.contains(EcoreUtil.getRootContainer(element))) {
//is the root the container's view?
if (element.eContainer().equals(findSematicModelElement(receivingDiagramRoot))) {
addToSuitableReference(
(View) receivingDiagramRoot,
(View) diagramEl, replacedElements);
change = true;
} else {
//or is it another element?
for (Iterator<EObject> tDiagramElIt = EcoreUtil.getAllContents(allDiagrams); tDiagramElIt.hasNext();) {
EObject tDiagramEl = tDiagramElIt.next();
EObject tElement = findSematicModelElement(tDiagramEl);
EObject container = element.eContainer();
if (diagramEl instanceof Node) {
Node node = (Node) diagramEl;
if (container.equals(tElement)) {
addToSuitableReference((Node) tDiagramEl, node, replacedElements);
change = true;
break;
}
}
}
}
}
if (change) {
break;
}
}
}
}
}
private EObject findSematicModelElement(EObject diagramElement) {
EObject element = null;
if (diagramElement instanceof View) {
element = ((View) diagramElement).getElement();
}
return element;
}
private void addToSuitableReference(View container, View value, List<EObject> replacedElements) {
EObject parent = findSematicModelElement(container);
if (parent != null) {
for (Object childView : container.getChildren()) {
EObject child = findSematicModelElement((EObject) childView);
//go into children if required - for compartments (see TaiPan example)
if (/*childView instanceof BasicCompartment && */ parent.equals(child)) {
container = (Node) childView;
break;
}
}
}
if (value instanceof Node) {
@SuppressWarnings("unchecked")
EList<Node> persistedChildren = container.getPersistedChildren();
persistedChildren.add((Node) value);
} else if (value instanceof Edge) {
while (!(container instanceof Diagram)) {
container = (View) container.eContainer();
if (container == null) {
return;
}
}
@SuppressWarnings("unchecked")
EList<Edge> persistedEdges = ((Diagram) container).getPersistedEdges();
persistedEdges.add((Edge) value);
} else {
return;
}
}
}