blob: 936a587bc789f8923c9df43c14eebae16fcf49cc [file] [log] [blame]
/**
* <copyright>
* OCL2AC is developed by Nebras Nassar based on an initial version developed by Thorsten Arendt and Jan Steffen Becker.
* </copyright>
*/
package org.eclipse.emf.henshin.ocl2ac.ocl2gc.util;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecore.util.EcoreUtil.Copier;
import graph.Attribute;
import graph.Edge;
import graph.Graph;
import graph.Node;
import morphisms.Mapping;
import morphisms.Morphism;
import morphisms.MorphismsPackage;
import morphisms.Pair;
public class JointPairs {
/**
* for finding the nodes which have to be overlapped. Compute the set of
* jointly surjective pairs that commute with a given span.
*
* @param span
* A span of morphisms
* @return the set of jointly surjective pairs that commute with the span
*
*/
private static Pair pushout = null;
/**
* for now it should be called after calling the method getJointPairs
*
* @return
*/
public static Pair getPushout() {
return pushout;
}
public static Set<Pair> getCommutingPairs(Pair span) {
Map<Node, Node> nodeMap = new HashMap<>(); // which nodes have to be
// joined
for (Mapping mA : span.getA().getMappings()) {
for (Mapping mB : span.getB().getMappings()) {
if (mA.getOrigin() == mB.getOrigin()) {
nodeMap.put(mA.getImage(), mB.getImage());
}
}
}
return getJointPairs(span.getA().getCodomain(), span.getB().getCodomain(), nodeMap);
}
/**
* Compute the set of jointly surjective pairs of inclusions from two
* graphs.
*
* @param graphA
* @param graphB
* @return the set of jointly surjective inclusions
*/
public static Set<Pair> getJointInclusions(Graph graphA, Graph graphB) {
Map<Node, Node> nodeMap = new HashMap<>(); // which nodes have to be
// joined
for (Node a : graphA.getNodes()) {
for (Node b : graphB.getNodes()) {
for (String name : a.getNames()) {
if (b.getNames().contains(name)) {
nodeMap.put(a, b);
break;
}
}
}
}
return getJointPairs(graphA, graphB, nodeMap);
}
/**
* Compute a set of jointly surjective pairs.
*
* @param graphA
* @param graphB
* @param jointNodes
* A map of nodes that must be joined, i.e. have the same image.
* @return
*/
private static Set<Pair> getJointPairs(Graph graphA, Graph graphB, Map<Node, Node> jointNodes) {
Copier copier = new EcoreUtil.Copier();
// create the graph of the first pair as a copy of graph A
Graph graph = (Graph) copier.copy(graphA);
copier.copyReferences();
// Integrate graph B into the new graph
// Calculate existing images for nodes
Map<Node, Node> nodeMap = new HashMap<>(); // which nodes from codomain
// B are already in the new
// graph
for (Entry<Node, Node> e : jointNodes.entrySet()) {
nodeMap.put(e.getValue(), (Node) copier.get(e.getKey()));
}
// copy remaining nodes
for (Node node : graphB.getNodes()) {
Node image = nodeMap.get(node);
if (image == null) { // copy the whole node
image = EcoreUtil.copy(node);
nodeMap.put(node, image);
graph.getNodes().add(image);
} else { // copy attributes only TODO: handle whole set of common
// subtypes
EClass jointType = getCommonSubtype(node.getType(), image.getType());
if (jointType == null) {
return Collections.emptySet();
}
image.setType(jointType);
// supporting attribute values
for (Attribute a : node.getAttributes()) {
Attribute copy = EcoreUtil.copy(a);
copy.setValue(a.getValue());
copy.setOp(a.getOp());
copy.setType(a.getType());
image.getAttributes().add(copy);
}
image.addNames(node.getNames());
}
}
// copy remaining edges
copyEdges: for (Edge edge : graphB.getEdges()) {
// leave out edges that are already present
for (Edge e : graph.getEdges()) {
if (edge.getType() == e.getType() && nodeMap.get(edge.getSource()) == e.getSource()
&& nodeMap.get(edge.getTarget()) == e.getTarget()) {
continue copyEdges;
}
}
Edge image = EcoreUtil.copy(edge);
image.setSource(nodeMap.get(edge.getSource()));
image.setTarget(nodeMap.get(edge.getTarget()));
graph.getEdges().add(image);
}
Pair initial = MorphismsPackage.eINSTANCE.getMorphismsFactory().createPair();
Morphism morphA = MorphismsPackage.eINSTANCE.getMorphismsFactory().createMorphism();
morphA.setDomain(graphA);
morphA.setCodomain(graph);
// Compute the initial morphisms
for (Node node : graphA.getNodes()) {
Mapping mapping = MorphismsPackage.eINSTANCE.getMorphismsFactory().createMapping();
mapping.setOrigin(node);
mapping.setImage((Node) copier.get(node));
morphA.getMappings().add(mapping);
}
initial.setA(morphA);
Morphism morphB = MorphismsPackage.eINSTANCE.getMorphismsFactory().createMorphism();
morphB.setDomain(graphB);
morphB.setCodomain(graph);
for (Node node : graphB.getNodes()) {
Mapping mapping = MorphismsPackage.eINSTANCE.getMorphismsFactory().createMapping();
mapping.setOrigin(node);
mapping.setImage(nodeMap.get(node));
morphB.getMappings().add(mapping);
}
initial.setB(morphB);
pushout = initial;
Set<Pair> result = new HashSet<>();
result.add(initial);
// calculate all other pairs from the first pair
collectJointPairs(initial, result, 0);
return result;
}
/**
* Calculate all jointly surjective pairs from the first pair. I.e. this is
* the pair with the biggest co-domain. This is done by recursively
* calculating all epimorphisms e: I->K from the co-domain I of the initial
* pair such that f ang g are injective.
*
* a b A ---> I <--- B \ | / \ |e / f\ | /g \ | / \ | / JVL K
*
* @param initial
* the first pair (i.e. the pair with the biggest graph)
* @param result
* @param nodeIndex
*/
private static void collectJointPairs(Pair initial, Set<Pair> result, int nodeIndex) {
initial.getA().getDomain().getNodes();
List<Node> nodes = initial.getA().getDomain().getNodes();
if (nodeIndex >= nodes.size()) {
return;
}
Node a = nodes.get(nodeIndex);
for (Node b : initial.getB().getDomain().getNodes()) {
for (Pair joined : join(initial, a, b)) {
if (joined != null) {
result.add(joined);
collectJointPairs(joined, result, nodeIndex + 1);
}
}
}
collectJointPairs(initial, result, nodeIndex + 1);
}
// TODO handling attributes are not yet complete.
// we merge if two nodes of the same type have the same attribute values or the attribute values are not yet set.
private static boolean isAttributeValueIn(Attribute attA, Node image) {
for (Attribute attB : image.getAttributes()) {
if (attA.getType().getName().equals(attB.getType().getName())) {
if (attA.getValue().equals(attB.getValue()) || attA.getValue()==null || attB.getValue() ==null)
return true;
}
}
return false;
}
/**
* Calculate some injective jointly surjective pair from some injective
* jointly surjective pair by joining nodeA and nodeB. If nodeA and nodeB
* are not type compatible or one of them already has a pre-image in both
* graphs, null is returned.
*
* @param pair
* @param nodeA
* some node from the co-domain of the span that has a pre-image
* in graph A
* @param nodeB
* some node from the co-domain of the span that has a pre-image
* in graph B
* @return the jointly surjective pair resulting from merging nodeA and
* nodeB or null
*/
private static Collection<Pair> join(Pair pair, Node nodeA, Node nodeB) {
EClass jt = getCommonSubtype(nodeA.getType(), nodeB.getType());
if (jt != null) {
// if (nodeA.getAttributes().size() != nodeB.getAttributes().size()) {
// return Collections.emptySet();
// } else {
// TODO handling attributes are not yet complete.
for (Attribute attA : nodeA.getAttributes()) {
if (!isAttributeValueIn(attA, nodeB))
return Collections.emptySet();
}
// }
}
Mapping mappingA = getOutgoingMapping(pair.getA(), nodeA);
Mapping mappingB = getOutgoingMapping(pair.getB(), nodeB);
if (getIncomingMapping(pair.getB(), mappingA.getImage()) != null
|| getIncomingMapping(pair.getA(), mappingB.getImage()) != null) {
return Collections.emptySet();
}
Collection<Pair> pairs = new LinkedList<Pair>();
for (EClass jointType : getCommonSubtypes(nodeA.getType(), nodeB.getType(),
pair.getA().getCodomain().getTypegraph())) {
Copier copier = new EcoreUtil.Copier();
Pair result = (Pair) copier.copy(pair);
Graph graph = (Graph) copier.copy(pair.getA().getCodomain());
copier.copyReferences();
mappingA = (Mapping) copier.get(mappingA);
mappingB = (Mapping) copier.get(mappingB);
Node remaining = mappingA.getImage();
Node removed = mappingB.getImage();
remaining.setType(jointType);
remaining.getAttributes().addAll(removed.getAttributes()); // TODO
// handle
// multiple
// attributes
remaining.addNames(removed.getNames());
mappingB.setImage(remaining);
// do not copy multiple edges
Iterator<Edge> edgeIterator = removed.getIncoming().iterator();
moveIncoming:
// for (Edge edge: removed.getIncoming()) {
while (edgeIterator.hasNext()) {
Edge edge = edgeIterator.next();
for (Edge e : remaining.getIncoming()) {
if (edge.getType() == e.getType() && edge.getSource() == e.getSource()) {
edge.setSource(null);
graph.getEdges().remove(edge);
continue moveIncoming;
}
}
edgeIterator.remove();
edge.setTarget(remaining);
}
edgeIterator = removed.getOutgoing().iterator();
moveOutgoing: while (edgeIterator.hasNext()) {
Edge edge = edgeIterator.next();
for (Edge e : remaining.getOutgoing()) {
if (edge.getType() == e.getType() && edge.getTarget() == e.getTarget()) {
edge.setTarget(null);
graph.getEdges().remove(edge);
continue moveOutgoing;
}
}
edgeIterator.remove();
edge.setSource(remaining);
}
graph.getNodes().remove(removed);
pairs.add(result);
}
return pairs;
}
private static Mapping getOutgoingMapping(Morphism morphism, Node node) {
for (Mapping mapping : morphism.getMappings()) {
if (mapping.getOrigin() == node) {
return mapping;
}
}
return null;
}
private static Mapping getIncomingMapping(Morphism morphism, Node node) {
for (Mapping mapping : morphism.getMappings()) {
if (mapping.getImage() == node) {
return mapping;
}
}
return null;
}
/**
*
*
* @param a
* @param b
* @return the class that is a super class of both classes
*/
private static EClass getCommonSubtype(EClass a, EClass b) {
if (a == b)
return a;
if (a.getEAllSuperTypes().contains(b)) {
return a;
} else if (b.getEAllSuperTypes().contains(a)) {
return b;
} else {
return null;
}
}
/**
*
* @param a
* @param b
* @param typeGraph
* @return the classes from typeGraph that is a super class of both classes
*/
private static List<EClass> getCommonSubtypes(EClass a, EClass b, EPackage typeGraph) {
if (a == b)
return Collections.singletonList(a);
if (a.getEAllSuperTypes().contains(b)) {
return Collections.singletonList(a);
} else if (b.getEAllSuperTypes().contains(a)) {
return Collections.singletonList(b);
} else {
List<EClass> result = new BasicEList<EClass>();
for (EClassifier e : typeGraph.getEClassifiers()) {
if (e instanceof EClass) {
List<EClass> superTypes = ((EClass) e).getEAllSuperTypes();
if (superTypes.contains(a) && superTypes.contains(b)) {
result.add((EClass) e);
}
}
}
return result;
}
}
}