blob: a0073b782c6cb84d06ce8fb802ddd5a74732ee19 [file] [log] [blame]
package org.eclipse.emf.henshin.giraph;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.henshin.interpreter.info.RuleChangeInfo;
import org.eclipse.emf.henshin.model.Action;
import org.eclipse.emf.henshin.model.Edge;
import org.eclipse.emf.henshin.model.Graph;
import org.eclipse.emf.henshin.model.NestedCondition;
import org.eclipse.emf.henshin.model.Node;
import org.eclipse.emf.henshin.model.Rule;
import org.eclipse.emf.henshin.model.staticanalysis.NodeEquivalence;
public class GiraphRuleData {
public static class MatchingStep {
public Node node;
public Edge edge;
public Node verifyEdgeTo;
public Node sendBackTo;
public Node keepMatchesOf;
public boolean isStart;
public boolean isMatching;
public boolean isJoin;
public MatchingStep(Node node, Edge edge, Node sendBackTo, Node verifyEdgeTo, boolean isStart,
boolean isMatching, boolean isJoin) {
this.node = node;
this.edge = edge;
this.verifyEdgeTo = verifyEdgeTo;
this.sendBackTo = sendBackTo;
this.isStart = isStart;
this.isMatching = isMatching;
this.isJoin = isJoin;
}
}
public final Rule rule;
public final RuleChangeInfo changeInfo;
public List<MatchingStep> matchingSteps;
public List<Node> orderedLhsNodes;
public List<Node> requiredNodes;
public Map<Node, NodeEquivalence> requiredNodesEquivalences;
public GiraphRuleData(Rule rule) throws Exception {
this.rule = preprocessPrule(rule);
this.changeInfo = new RuleChangeInfo(this.rule);
this.matchingSteps = generateMatchingSteps();
if (matchingSteps == null) {
throw new RuntimeException("Cannot generate matching data for rule " + rule.getName());
}
generateOrderedLhsNodes();
}
private Rule preprocessPrule(Rule rule) {
// Get the multi-rule:
Rule multiRule = rule.getMultiRules().get(0);
// Make a copy of the multi-rule:
Rule copy = EcoreUtil.copy(multiRule);
copy.setName(rule.getName());
multiRule = null;
rule = null;
// Compute the required nodes:
requiredNodes = new ArrayList<Node>();
for (Node node : copy.getActionNodes(null)) {
Action action = node.getAction();
if (action.getType() == Action.Type.REQUIRE) {
requiredNodes.add(node);
}
}
// Compute the required nodes equivalences:
requiredNodesEquivalences = new HashMap<Node, NodeEquivalence>();
TreeIterator<EObject> it = copy.eAllContents();
while (it.hasNext()) {
EObject obj = it.next();
if (obj instanceof Graph) {
NestedCondition pac = copy.getLhs().getPAC(((Graph) obj).getName());
if (pac != null) {
List<NodeEquivalence> equivalences = NodeEquivalence.computeEquivalences(pac.getConclusion());
for (NodeEquivalence equi : equivalences) {
for (int i = 0; i < equi.size(); i++) {
if (pac.getMappings().getOrigin(equi.get(i)) != null) {
equi.remove(i--);
}
}
if (equi.size() > 1) {
for (Node node : equi) {
requiredNodesEquivalences.put(node, equi);
}
}
}
}
}
}
// Move the required nodes to the LHS:
boolean changed;
do {
changed = false;
for (Node node : copy.getActionNodes(null)) {
Action action = node.getAction();
if (action.getType() == Action.Type.REQUIRE) {
node.setAction(new Action(Action.Type.PRESERVE));
changed = true;
break;
}
}
for (Edge edge : copy.getActionEdges(null)) {
Action action = edge.getAction();
if (action.getType() == Action.Type.REQUIRE) {
edge.setAction(new Action(Action.Type.PRESERVE));
changed = true;
break;
}
}
} while (changed);
// Replace required nodes if necessary:
for (int i = 0; i < requiredNodes.size(); i++) {
Node node = requiredNodes.get(i);
if (!node.getGraph().isLhs()) {
Node lhsNode = ((NestedCondition) node.getGraph().eContainer()).getMappings().getOrigin(node);
requiredNodes.set(i, lhsNode);
if (requiredNodesEquivalences.containsKey(node)) {
requiredNodesEquivalences.put(lhsNode, requiredNodesEquivalences.get(node));
requiredNodesEquivalences.remove(node);
}
for (NodeEquivalence equi : requiredNodesEquivalences.values()) {
for (int j = 0; j < equi.size(); j++) {
if (equi.get(j) == node) {
equi.set(j, lhsNode);
}
}
}
}
}
return copy;
}
private List<MatchingStep> generateMatchingSteps() {
List<Node> nodesToMatch = new ArrayList<Node>(rule.getLhs().getNodes());
List<MatchingStep> result = new ArrayList<GiraphRuleData.MatchingStep>();
while (!nodesToMatch.isEmpty()) {
// The next matching steps:
List<MatchingStep> newSteps = getLongestMatchingSteps(nodesToMatch);
// Find the join node:
Node joinNode = null;
for (int i = 0; i < newSteps.size(); i++) {
for (MatchingStep oldStep : result) {
if (oldStep.node == newSteps.get(i).node) {
joinNode = oldStep.node;
break;
}
}
if (joinNode != null) {
break;
}
}
// In the last old matching step, send the matches to the join node:
if (joinNode != null && !result.isEmpty()) {
result.get(result.size() - 1).sendBackTo = joinNode;
}
// Add all new matching steps until the join node is reached:
for (MatchingStep step : newSteps) {
step.keepMatchesOf = joinNode;
result.add(step);
// Remember new matched nodes:
nodesToMatch.remove(step.node);
if (step.edge != null) {
nodesToMatch.remove(step.edge.getTarget());
}
// Stop if we reached the join node:
if (step.node == joinNode) {
step.edge = null;
step.isJoin = true;
break;
}
}
}
// Any edges which still need to be checked?
List<Edge> missingEdges = new ArrayList<Edge>(rule.getLhs().getEdges());
for (MatchingStep step : result) {
if (step.edge != null) {
missingEdges.remove(step.edge);
}
}
for (Edge edge : missingEdges) {
MatchingStep lastStep = result.get(result.size() - 1);
lastStep.sendBackTo = edge.getSource();
result.add(new MatchingStep(edge.getSource(), // node
edge, // edge
null, // send back to
edge.getTarget(), // verify edge to
false, // is start
false, // is matching
false // is join
));
}
// If the last node is a require node, we need to send the
// matches back to a real node:
if (!result.isEmpty()) {
Node last = result.get(result.size() - 1).node;
if (requiredNodes.contains(last)) {
Node target = null;
for (MatchingStep step : result) {
if (!requiredNodes.contains(step.node)) {
target = step.node;
break;
}
}
if (target != null) {
result.get(result.size() - 1).sendBackTo = target;
result.add(new MatchingStep(target, null, null, null, false, false, false));
}
}
}
return result;
}
private List<MatchingStep> getLongestMatchingSteps(List<Node> nodes) {
List<MatchingStep> result = null;
for (Node start : nodes) {
List<MatchingStep> matchingSteps = getMatchingSteps(rule, start);
if (matchingSteps != null) {
if (result == null || matchingSteps.size() > result.size()) {
result = matchingSteps;
}
}
}
return result;
}
private List<MatchingStep> getMatchingSteps(Rule rule, Node start) {
List<MatchingStep> matchingSteps = new ArrayList<MatchingStep>();
if (start.getOutgoing().isEmpty()) {
matchingSteps.add(new MatchingStep(start, // node
null, // edge
null, // send back to
null, // verify edge to
true, // is start
true, // is matching
false // is join
));
}
Deque<Edge> edgeQueue = new ArrayDeque<Edge>();
edgeQueue.addAll(start.getOutgoing());
Set<Node> lockedNodes = new HashSet<Node>();
Set<Edge> visitedEdges = new HashSet<Edge>();
while (!edgeQueue.isEmpty()) {
Edge edge = edgeQueue.pop();
boolean isMatching = lockedNodes.add(edge.getSource());
// Add the next matching step:
Node sendBackTo = null;
if (lockedNodes.contains(edge.getTarget()) && !edgeQueue.isEmpty()) {
sendBackTo = edgeQueue.getFirst().getSource();
}
Node verifyEdgeTo = null;
if (lockedNodes.contains(edge.getTarget())) {
verifyEdgeTo = edge.getTarget();
}
matchingSteps.add(new MatchingStep(edge.getSource(), // node
edge, // edge
sendBackTo, // send back to
verifyEdgeTo, // verify edge to
lockedNodes.size() == 1, // is start
isMatching, // is matching
false // is join
));
visitedEdges.add(edge);
if (edge.getTarget().getOutgoing().isEmpty()) {
// Leaf:
if (!lockedNodes.contains(edge.getTarget())) {
sendBackTo = null;
if (!edgeQueue.isEmpty()) {
sendBackTo = edgeQueue.getFirst().getSource();
}
matchingSteps.add(new MatchingStep(edge.getTarget(), null, sendBackTo, null, false, true, false));
lockedNodes.add(edge.getTarget());
}
} else {
// Intermediate node:
for (Edge succ : edge.getTarget().getOutgoing()) {
if (!visitedEdges.contains(succ) && !edgeQueue.contains(succ)) {
edgeQueue.push(succ);
}
}
}
lockedNodes.add(edge.getSource());
}
return matchingSteps;
}
private void generateOrderedLhsNodes() {
orderedLhsNodes = new ArrayList<Node>();
for (MatchingStep step : matchingSteps) {
if (step.node != null && !orderedLhsNodes.contains(step.node)) {
orderedLhsNodes.add(step.node);
}
}
// Sort all require node equivalences:
for (NodeEquivalence equi : requiredNodesEquivalences.values()) {
Collections.sort(equi, new Comparator<Node>() {
@Override
public int compare(Node x, Node y) {
return orderedLhsNodes.indexOf(x) - orderedLhsNodes.indexOf(y);
}
});
}
}
}