blob: 9f74ffb15083aefd9ddc66d3562d7cde44854f01 [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.utils.henshin.simplification;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
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.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.henshin.model.And;
import org.eclipse.emf.henshin.model.Attribute;
import org.eclipse.emf.henshin.model.BinaryFormula;
import org.eclipse.emf.henshin.model.Edge;
import org.eclipse.emf.henshin.model.Formula;
import org.eclipse.emf.henshin.model.Graph;
import org.eclipse.emf.henshin.model.HenshinFactory;
import org.eclipse.emf.henshin.model.NestedCondition;
import org.eclipse.emf.henshin.model.Node;
import org.eclipse.emf.henshin.model.Or;
import org.eclipse.emf.henshin.model.UnaryFormula;
public class EquivalencesSimplifier {
private Formula formula;
private HenshinFactory factory;
public EquivalencesSimplifier(Formula formula) {
this.formula = formula;
this.factory = HenshinFactory.eINSTANCE;
}
public boolean simplify() {
if (simplifyExists()) {
return simplify();
}
return true;
}
private boolean insert(EObject container, Formula oldContent, Formula newContent) {
if (container instanceof NestedCondition) {
NestedCondition constraint = (NestedCondition) container;
constraint.getConclusion().setFormula(newContent);
return true;
}
if (container instanceof Graph) {
Graph qlc = (Graph) container;
qlc.setFormula(newContent);
return true;
}
if (container instanceof Formula) {
Formula f = (Formula) container;
if (f instanceof UnaryFormula) {
UnaryFormula not = (UnaryFormula) f;
not.setChild(newContent);
return true;
}
if (f instanceof BinaryFormula) {
BinaryFormula binaryFormula = (BinaryFormula) f;
if (binaryFormula.getLeft() == oldContent) {
binaryFormula.setLeft(newContent);
return true;
}
if (binaryFormula.getRight() == oldContent) {
binaryFormula.setRight(newContent);
return true;
}
}
}
return false;
}
public boolean simplifyExists() {
TreeIterator<EObject> iter = formula.eAllContents();
while (iter.hasNext()) {
EObject eObject = iter.next();
if (isExistsCondition(eObject)) {
NestedCondition outerCondition = (NestedCondition) eObject;
if (isExistsCondition(outerCondition.getConclusion().getFormula())) {
NestedCondition innerCondition = (NestedCondition) outerCondition.getConclusion().getFormula();
if (isLeaf(innerCondition)) {
Graph outerGraph = outerCondition.getConclusion();
Graph innerGraph = innerCondition.getConclusion();
// E1.c
if (isSubGraph(outerGraph, innerGraph)) {
outerCondition.setConclusion(innerGraph);
System.out.println("(1) Equivalence E1.c");
return true;
}
if (isSubGraph(innerGraph, outerGraph)) {
outerCondition.getConclusion().setFormula(null);
System.out.println("(2) Equivalence E1.c");
return true;
}
// E1.b
if (areClanDisjoint(innerGraph, outerGraph)) {
EList<Node> innerNodes = innerGraph.getNodes();
innerGraph.getNodes().clear();
outerGraph.getNodes().addAll(innerNodes);
EList<Edge> innerEdges = innerGraph.getEdges();
innerGraph.getEdges().clear();
outerGraph.getEdges().addAll(innerEdges);
outerCondition.getConclusion().setFormula(null);
System.out.println("Equivalence E1.b");
return true;
}
// E1.a
if (haveIntersection(innerGraph, outerGraph)) {
EList<Intersection> intersections = getIntersections(outerGraph, innerGraph);
EObject container = outerCondition.eContainer();
if (intersections.size() == 1) {
NestedCondition condition = factory.createNestedCondition();
condition.setConclusion(glue(outerGraph, intersections.get(0), innerGraph));
insert(container, outerCondition, condition);
System.out.println("(1) Equivalence E1.a");
return true;
}
// TODO manage or
Or orFormula = factory.createOr();
Or orRight = null;
for (Intersection intersection : intersections) {
NestedCondition condition = factory.createNestedCondition();
condition.setConclusion(glue(outerCondition.getConclusion(), intersection,
innerCondition.getConclusion()));
orFormula.setLeft(condition);
// condition.getConclusion().setFormula(null);
orFormula.setLeft(condition);
orRight = factory.createOr();
orFormula.setRight(orRight);
orFormula = orRight;
// TODO call the henshin cleaner to refactor
// having or onleft only
}
insert(container, outerCondition, orFormula);
System.out.println("(2) Equivalence E1.a");
return true;
}
// E3 case 1
if (hasOnlyOneNode(innerGraph)) {
Node innerNode = innerGraph.getNodes().get(0);
if (hasTwoNames(innerNode)) {
String name1 = getNames(innerNode.getName()).get(0);
String name2 = getNames(innerNode.getName()).get(1);
if (containsExactlyOne(outerGraph, name1, name2)) {
rename(outerGraph, name1, name2);
outerCondition.getConclusion().setFormula(null);
System.out.println("Equivalence E3'");
return true;
}
}
}
}
System.err.println("not a leaf");
}
if (isAndFormula(outerCondition.getConclusion().getFormula())) {
And andFormula = (And) outerCondition.getConclusion().getFormula();
// if (andFormula.getArguments().size() == 2) {
if (isExistsCondition(andFormula.getLeft()) && isExistsCondition(andFormula.getRight())) {
NestedCondition cond1 = (NestedCondition) andFormula.getLeft();
NestedCondition cond2 = (NestedCondition) andFormula.getRight();
if (isLeaf(cond1) && isLeaf(cond2)) {
Graph gr = outerCondition.getConclusion();
Graph gr1 = cond1.getConclusion();
Graph gr2 = cond2.getConclusion();
if (hasOnlyOneNode(gr) && hasOnlyOneNode(gr2)) {
Node node = gr.getNodes().get(0);
Node node2 = gr2.getNodes().get(0);
if (haveSameTypes(node, node2) && hasOneName(node) && hasTwoNames(node2)) {
String name = node.getName();
String name1 = node2.getName().split("=")[0];
String name2 = node2.getName().split("=")[1];
if (name.equals(name1) || name.equals(name2)) {
if (containsExactlyOne(gr1, name1, name2)) {
rename(gr1, name1, name2);
outerCondition.getConclusion().setFormula(cond1);
System.out.println("Equivalence E3");
return true;
}
}
}
}
if (hasOnlyOneNode(gr) && hasOnlyOneNode(gr1)) {
Node node = gr.getNodes().get(0);
Node node1 = gr1.getNodes().get(0);
if (haveSameTypes(node, node1) && hasOneName(node) && hasTwoNames(node1)) {
String name = node.getName();
String name1 = node1.getName().split("=")[0];
String name2 = node1.getName().split("=")[1];
if (name.equals(name1) || name.equals(name2)) {
if (containsExactlyOne(gr2, name1, name2)) {
rename(gr2, name1, name2);
outerCondition.getConclusion().setFormula(cond2);
System.out.println("Equivalence E3");
return true;
}
}
}
}
System.out.println("=> haveRecurringNodes: " + haveRecurringNodes(gr1, gr2));
System.out.println(
"=> containsEachRecurringNode: " + containsEachRecurringNode(gr, gr1, gr2));
if (haveRecurringNodes(gr1, gr2)) {
if (containsEachRecurringNode(gr, gr1, gr2)
|| containsNoRecurringNodeType(gr, gr1, gr2)) {
if (haveIntersection(gr1, gr2)) {
EList<Intersection> intersections = getIntersections(gr1, gr2);
if (intersections.size() == 1) {
NestedCondition condition = factory.createNestedCondition();
condition.setConclusion((glue(gr1, intersections.get(0), gr2)));
// condition.getConclusion().setFormula(null);
outerCondition.getConclusion().setFormula(condition);
System.out.println("Equivalence E2.a");
return true;
}
// TODO Manager OR
Or or = factory.createOr();
Or orRight = null;
for (Intersection intersection : intersections) {
NestedCondition condition = factory.createNestedCondition();
condition.setConclusion((glue(gr1, intersection, gr2)));
// condition.getConclusion().setFormula(null);
or.setLeft(condition);
orRight = factory.createOr();
or.setRight(orRight);
or = orRight;
// TODO Test it and call cleaner
}
outerCondition.getConclusion().setFormula(or);
System.out.println("Equivalence E2.a");
return true;
}
}
}
}
}
// }
}
}
if (isAndFormula(eObject)) {
And andFormula = (And) eObject;
// if (andFormula.getArguments().size() == 2) {
if (isExistsCondition(andFormula.getLeft()) && isExistsCondition(andFormula.getRight())) {
NestedCondition cond1 = (NestedCondition) andFormula.getLeft();
NestedCondition cond2 = (NestedCondition) andFormula.getRight();
if (isLeaf(cond1) && isLeaf(cond2)) {
Graph gr1 = cond1.getConclusion();
Graph gr2 = cond2.getConclusion();
if (areClanDisjoint(gr1, gr2) && areNodeNameDisjoint(gr1, gr2)) {
EList<Node> nodes = gr2.getNodes();
gr2.getNodes().clear();
gr1.getNodes().addAll(nodes);
EList<Edge> edges = gr2.getEdges();
gr2.getEdges().clear();
gr1.getEdges().addAll(edges);
EObject container = andFormula.eContainer();
insert(container, andFormula, cond1);
System.out.println("Equivalence E2.b");
return true;
}
}
}
// }
}
}
System.err.println("return false");
return false;
}
/**
*
* @param gr1
* @param gr2
* @return
*/
private boolean haveRecurringNodes(Graph gr1, Graph gr2) {
EList<Node> recurringNodes = collectRecurringNodes(gr1, gr2);
return (!recurringNodes.isEmpty());
}
/**
*
* @param gr
* @param gr1
* @param gr2
* @return
*/
private boolean containsEachRecurringNode(Graph gr, Graph gr1, Graph gr2) {
EList<Node> recurringNodes = collectRecurringNodes(gr1, gr2);
for (Node recurringNode : recurringNodes) {
boolean occurs = false;
String nodeName = recurringNode.getName();
EClass type = recurringNode.getType();
for (Node node : gr.getNodes()) {
if (node.getName().equals(nodeName) && node.getType() == type) {
occurs = true;
break;
}
}
if (!occurs) {
return false;
}
}
return true;
}
/**
*
* @param gr
* @param gr1
* @param gr2
* @return
*/
private boolean containsNoRecurringNodeType(Graph gr, Graph gr1, Graph gr2) {
EList<Node> recurringNodes = collectRecurringNodes(gr1, gr2);
boolean occurs = false;
for (Node recurringNode : recurringNodes) {
EClass type = recurringNode.getType();
for (Node node : gr.getNodes()) {
if (node.getType() == type) {
occurs = true;
break;
}
}
}
return !occurs;
}
private EList<Node> collectRecurringNodes(Graph gr1, Graph gr2) {
EList<Node> recurringNodes = new BasicEList<Node>();
for (Node node1 : gr1.getNodes()) {
String nodeName = node1.getName();
EClass type = node1.getType();
for (Node node2 : gr2.getNodes()) {
if (node2.getName().equals(nodeName) && node2.getType() == type) {
recurringNodes.add(node1);
break;
}
}
}
return recurringNodes;
}
/**
*
* @param graph
* @param name1
* @param name2
*/
private void rename(Graph graph, String name1, String name2) {
String newName = name1 + "=" + name2;
for (Node node : graph.getNodes()) {
if (node.getName().equals(name1) || node.getName().equals(name2)) {
node.setName(newName);
}
}
}
/**
*
*
* @param graph
* @param name1
* @param name2
* @return
*/
private boolean containsExactlyOne(Graph graph, String name1, String name2) {
boolean exists1 = false;
boolean exists2 = false;
for (Node node : graph.getNodes()) {
if (node.getName().equals(name1))
exists1 = true;
if (node.getName().equals(name2))
exists2 = true;
}
return ((exists1 && !exists2) || (!exists1 && exists2));
}
/**
*
*
* @param node
* @return
*/
private boolean hasTwoNames(Node node) {
return (node.getName().split("=").length == 2);
}
/**
*
*
* @param node
* @return
*/
private boolean hasOneName(Node node) {
return (node.getName().split("=").length == 1);
}
/**
* OK
*
* @param node1
* @param node2
* @return
*/
private boolean haveSameTypes(Node node1, Node node2) {
return (node1.getType() == node2.getType());
}
/**
*
*
* @param graph
* @return
*/
private boolean hasOnlyOneNode(Graph graph) {
return (graph.getNodes().size() == 1 && graph.getEdges().isEmpty());
}
/**
* OK
*
* @param gr1
* @param intersection
* @param gr2
* @return
*/
private Graph glue(Graph gr1, Intersection intersection, Graph gr2) {
Graph graph = intersection.getSourceGraph();
for (Node node : gr1.getNodes()) {
if (!intersection.containsSource(node)) {
Node nodeCopy = EcoreUtil.copy(node);
intersection.addNodeMapping(new NodeMapping(nodeCopy, node));
graph.getNodes().add(nodeCopy);
}
}
for (Node node : gr2.getNodes()) {
if (!intersection.containsTarget(node)) {
Node nodeCopy = EcoreUtil.copy(node);
intersection.addNodeMapping(new NodeMapping(nodeCopy, node));
graph.getNodes().add(nodeCopy);
}
}
for (Edge edge : gr1.getEdges()) {
if (!intersection.containsTarget(edge)) {
Edge edgeCopy = EcoreUtil.copy(edge);
edgeCopy.setSource(intersection.getSourceNode1(edge.getSource()));
edgeCopy.setTarget(intersection.getSourceNode1(edge.getTarget()));
graph.getEdges().add(edgeCopy);
}
}
for (Edge edge : gr2.getEdges()) {
if (!intersection.containsTarget(edge)) {
Edge edgeCopy = EcoreUtil.copy(edge);
edgeCopy.setSource(intersection.getSourceNode(edge.getSource()));
edgeCopy.setTarget(intersection.getSourceNode(edge.getTarget()));
graph.getEdges().add(edgeCopy);
}
}
return graph;
}
private EList<Intersection> getIntersections(Graph gr1, Graph gr2) {
EList<Intersection> intersections = new BasicEList<Intersection>();
EList<Graph> subGraphs = new BasicEList<Graph>();
fillSubGraphs(subGraphs, gr1);
for (Graph graph : subGraphs) {
if (isSubGraph(graph, gr2)) {
intersections.add(getIntersection(graph, gr2));
}
}
return intersections;
}
private Intersection getIntersection(Graph subGraph, Graph graph) {
Intersection intersection = new Intersection();
for (Node n1 : subGraph.getNodes()) {
for (Node n2 : graph.getNodes()) {
if (n1.getType() == n2.getType() && n1.getName().equals(n2.getName())) {
intersection.addNodeMapping(new NodeMapping(n1, n2));
}
}
}
for (Edge e1 : subGraph.getEdges()) {
Node src1 = e1.getSource();
NodeMapping srcMapping = getMapping(intersection.getNodeMappings(), src1);
Node tgt1 = e1.getTarget();
NodeMapping tgtMapping = getMapping(intersection.getNodeMappings(), tgt1);
for (Edge e2 : graph.getEdges()) {
Node src2 = e2.getSource();
Node tgt2 = e2.getTarget();
if (e1.getType() == e2.getType() && srcMapping.getTargetNode() == src2
&& tgtMapping.getTargetNode() == tgt2) {
intersection.addEdgeMapping(new EdgeMapping(e1, e2));
break;
}
}
}
return intersection;
}
private void fillSubGraphs(EList<Graph> subGraphs, Graph graph) {
EList<Edge> edges = cloneEdges(graph);
for (Edge edge : edges) {
graph.getEdges().remove(edge);
Graph gr = EcoreUtil.copy(graph);
graph.getEdges().add(edge);
testAndFill(subGraphs, gr);
}
EList<Node> nodes = cloneNodes(graph);
for (Node node : nodes) {
if (!isConnected(node, graph)) {
graph.getNodes().remove(node);
Graph gr = EcoreUtil.copy(graph);
graph.getNodes().add(node);
if (!isEmptyGraph(gr)) {
testAndFill(subGraphs, gr);
}
}
}
}
private boolean isEmptyGraph(Graph graph) {
return (graph.getNodes().isEmpty() && graph.getEdges().isEmpty());
}
private boolean isConnected(Node node, Graph graph) {
for (Edge edge : graph.getEdges()) {
if (edge.getSource() == node)
return true;
if (edge.getTarget() == node)
return true;
}
return false;
}
private void testAndFill(EList<Graph> graphs, Graph graph) {
if (!contains(graphs, graph)) {
graphs.add(graph);
fillSubGraphs(graphs, graph);
}
}
private boolean contains(EList<Graph> graphs, Graph graph) {
for (Graph gr : graphs) {
if (isSameGraph(gr, graph)) {
return true;
}
}
return false;
}
private boolean isSameGraph(Graph gr1, Graph gr2) {
return (isSubGraph(gr1, gr2) && isSubGraph(gr2, gr1));
}
private EList<Node> cloneNodes(Graph graph) {
EList<Node> nodes = new BasicEList<Node>();
for (Node node : graph.getNodes()) {
nodes.add(node);
}
return nodes;
}
private EList<Edge> cloneEdges(Graph graph) {
EList<Edge> edges = new BasicEList<Edge>();
for (Edge edge : graph.getEdges()) {
edges.add(edge);
}
return edges;
}
/**
*
*
* @param gr1
* @param gr2
* @return
*/
private boolean areNodeNameDisjoint(Graph gr1, Graph gr2) {
List<String> names1 = getNodeNames(gr1);
List<String> names2 = getNodeNames(gr2);
for (String str1 : names1) {
for (String str2 : names2) {
if (str1.equals(str2)) {
return false;
}
}
}
return true;
}
private List<String> getNodeNames(Graph graph) {
List<String> names = new ArrayList<String>();
for (Node node : graph.getNodes()) {
names.addAll(getNames(node.getName()));
}
return names;
}
private List<String> getNames(String name) {
String[] names = name.split("=");
return Arrays.asList(names);
}
/**
*
*
* @param gr1
* @param gr2
* @return
*/
private boolean haveIntersection(Graph gr1, Graph gr2) {
EList<Intersection> intersections = getIntersections(gr1, gr2);
return !intersections.isEmpty();
}
/**
*
* @param gr1
* @param gr2
* @return
*/
private boolean areClanDisjoint(Graph gr1, Graph gr2) {
for (Node n1 : gr1.getNodes()) {
EList<EClass> clan1 = getClan(n1.getType());
for (Node n2 : gr2.getNodes()) {
if (clan1.contains(n2.getType())) {
return false;
}
}
}
for (Node n2 : gr2.getNodes()) {
EList<EClass> clan2 = getClan(n2.getType());
for (Node n1 : gr1.getNodes()) {
if (clan2.contains(n1.getType())) {
return false;
}
}
}
return true;
}
private EList<EClass> getClan(EClass eClass) {
EList<EClass> eClasses = new BasicEList<EClass>();
eClasses.add(eClass);
eClasses.addAll(getAllSubclasses(eClass));
return eClasses;
}
private EList<EClass> getAllSubclasses(EClass eClass) {
EList<EClass> eClasses = new BasicEList<EClass>();
EPackage ePackage = eClass.getEPackage();
TreeIterator<EObject> iter = ePackage.eAllContents();
while (iter.hasNext()) {
EObject eObject = iter.next();
if (eObject instanceof EClass) {
EClass clazz = (EClass) eObject;
if (clazz.getEAllSuperTypes().contains(eClass)) {
eClasses.add(clazz);
}
}
}
return eClasses;
}
/**
*
*
* @param graphA
* @param graphB
* @return
*/
public boolean isSubGraph(Graph graphA, Graph graphB) {
HashMap<Node, Node> mappingNodeA2NodeB = new HashMap<Node, Node>();
for (Node nodeA : graphA.getNodes()) {
for (Node nodeB : graphB.getNodes()) {
if (nodeA.getType() == nodeB.getType()) {
if (getNames(nodeB.getName()).containsAll(getNames(nodeA.getName()))) {
// TODO check it attribute
if (nodeB.getAttributes().containsAll(nodeA.getAttributes())) {
boolean attISO = true;
for (Attribute a : nodeA.getAttributes()) {
if (getBy(a, nodeB.getAttributes()) == null)
attISO = false;
}
if (attISO)
mappingNodeA2NodeB.put(nodeA, nodeB);
}
}
}
}
}
if (graphA.getNodes().size() != mappingNodeA2NodeB.size()) {
return false;
}
for (Edge edgeA : graphA.getEdges()) {
Node srcEdgeA = edgeA.getSource();
Node tgtEdgeA = edgeA.getTarget();
boolean edgeIsMapped = false;
for (Edge edgeB : graphB.getEdges()) {
Node srcEdgeB = edgeB.getSource();
Node tgtEdgeB = edgeB.getTarget();
if (edgeA.getType() == edgeB.getType() && mappingNodeA2NodeB.get(srcEdgeA) == srcEdgeB
&& mappingNodeA2NodeB.get(tgtEdgeA) == tgtEdgeB) {
edgeIsMapped = true;
break;
}
}
if (!edgeIsMapped) {
return false;
}
}
return true;
}
/**
*
*
* @param a
* @param ls
* @return
*/
private Attribute getBy(Attribute a, EList<Attribute> ls) {
for (Attribute b : ls) {
if (b.getType() == a.getType() && b.getValue() == a.getValue()) {
return b;
}
}
return null;
}
private NodeMapping getMapping(EList<NodeMapping> nodeMappings, Node node) {
for (NodeMapping nm : nodeMappings) {
if (nm.getSourceNode() == node) {
return nm;
}
}
return null;
}
private boolean isLeaf(NestedCondition nestedCondition) {
if (nestedCondition.getConclusion() == null)
return true;
else if (nestedCondition.getConclusion() != null && nestedCondition.getConclusion().getFormula() == null)
return true;
return false;
}
private boolean isAndFormula(EObject eObject) {
if (eObject instanceof And)
return true;
else
return false;
}
private boolean isExistsCondition(EObject eObject) {
if (eObject instanceof NestedCondition)
return true;
else
return false;
}
}