blob: 34863439f88086c02cd11db300ab3111c487882f [file] [log] [blame]
/**
* <copyright>
* Copyright (c) 2010-2014 Henshin developers. 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
* </copyright>
*/
package org.eclipse.emf.henshin.interpreter.info;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.henshin.interpreter.impl.EngineImpl;
import org.eclipse.emf.henshin.interpreter.matching.constraints.AttributeConstraint;
import org.eclipse.emf.henshin.interpreter.matching.constraints.BinaryConstraint;
import org.eclipse.emf.henshin.interpreter.matching.constraints.ContainmentConstraint;
import org.eclipse.emf.henshin.interpreter.matching.constraints.DanglingConstraint;
import org.eclipse.emf.henshin.interpreter.matching.constraints.PathConstraint;
import org.eclipse.emf.henshin.interpreter.matching.constraints.ReferenceConstraint;
import org.eclipse.emf.henshin.interpreter.matching.constraints.UnaryConstraint;
import org.eclipse.emf.henshin.interpreter.matching.constraints.Variable;
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.Mapping;
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.UnaryFormula;
import org.eclipse.emf.henshin.model.staticanalysis.PathFinder;
public class VariableInfo {
// variables which represent nodes when they are first introduced, e.g. if a
// mapped node occurs in the LHS and in one condition, only the variable
// representing the LHS node will be in this collection
private Collection<Variable> mainVariables;
// node-variable pair
private Map<Node, Variable> node2variable;
// variable-node pair
private Map<Variable, Node> variable2node;
// map between a graph and all variables corresponding to nodes of that
// graph
private Map<Graph, List<Variable>> graph2variables;
// map between a key variable and its main variable, e.g. there is a mapping
// chain from the node belonging to the main variable to the key variable
private Map<Variable, Variable> variable2mainVariable;
private Rule rule;
private EngineImpl engine;
public VariableInfo(RuleInfo ruleInfo, EngineImpl engine) {
this.rule = ruleInfo.getRule();
this.engine = engine;
this.node2variable = new HashMap<Node, Variable>();
this.variable2node = new HashMap<Variable, Node>();
this.graph2variables = new HashMap<Graph, List<Variable>>();
this.variable2mainVariable = new HashMap<Variable, Variable>();
createVariables(rule.getLhs(), null);
for (Node node : rule.getLhs().getNodes()) {
if (rule.getMappings().getImage(node,rule.getRhs())==null)
createDanglingConstraints(node);
}
mainVariables = variable2mainVariable.values();
}
private void createVariables(Graph g, Collection<Mapping> mappings) {
List<Variable> variables = new ArrayList<Variable>();
for (Node node : g.getNodes()) {
EClass type = node.getType();
Variable var = new Variable(type);
variables.add(var);
node2variable.put(node, var);
variable2node.put(var, node);
Variable mainVariable = var;
if (mappings != null) {
for (Mapping mapping : mappings) {
if (node == mapping.getImage()) {
mainVariable = variable2mainVariable.get(node2variable.get(mapping
.getOrigin()));
}
}
}
variable2mainVariable.put(var, mainVariable);
}
for (Node node : g.getNodes()) {
createConstraints(node);
}
graph2variables.put(g, variables);
createVariables(g.getFormula());
}
private void createVariables(Formula formula) {
if (formula instanceof BinaryFormula) {
createVariables(((BinaryFormula) formula).getLeft());
createVariables(((BinaryFormula) formula).getRight());
} else if (formula instanceof UnaryFormula)
createVariables(((UnaryFormula) formula).getChild());
else if (formula instanceof NestedCondition) {
NestedCondition nc = (NestedCondition) formula;
createVariables(nc.getConclusion(), nc.getMappings());
}
}
private void createConstraints(Node node) {
Variable var = node2variable.get(node);
// User-defined node constraints:
UnaryConstraint userConstraint = engine.createUserConstraints(node);
if (userConstraint != null){
var.userConstraints.add(userConstraint);
}
// Outgoing edges:
for (Edge edge : node.getOutgoing()) {
Variable target = node2variable.get(edge.getTarget());
ReferenceConstraint constraint;
String index = edge.getIndex();
if (index!=null && index.length()>0) {
if (rule.getParameter(index)!=null) {
constraint = new ReferenceConstraint(target, edge.getType(), index, false);
var.requiresFinalCheck = true;
} else {
try {
Number constant = (Number) engine.getScriptEngine().eval(index);
constraint = new ReferenceConstraint(target, edge.getType(), constant, true);
} catch (Exception e) {
throw new RuntimeException("Error evaluating index expression: " + index);
}
}
} else {
constraint = new ReferenceConstraint(target, edge.getType(), null, true);
}
var.referenceConstraints.add(constraint);
BinaryConstraint binaryUserConstraint = engine.createUserConstraints(edge);
if (binaryUserConstraint != null){
var.binaryUserConstraints.put(constraint, binaryUserConstraint);
}
}
// Incoming edges:
for (Edge edge : node.getIncoming()) {
if (edge.getType().isContainment()) {
Variable target = node2variable.get(edge.getSource());
ContainmentConstraint constraint = new ContainmentConstraint(target);
var.containmentConstraints.add(constraint);
} else if (edge.getType().getEOpposite() != null) {
Variable target = node2variable.get(edge.getSource());
ReferenceConstraint constraint = new ReferenceConstraint(target, edge.getType().getEOpposite(), null, true);
var.referenceConstraints.add(constraint);
}
}
// Attributes:
for (Attribute attribute : node.getAttributes()) {
String value = attribute.getValue();
AttributeConstraint constraint;
if (rule.getParameter(value)!=null) {
constraint = new AttributeConstraint(attribute.getType(), value, false);
} else {
Object constant = engine.evalAttributeExpression(attribute, rule);
constraint = new AttributeConstraint(attribute.getType(), constant, true);
}
var.attributeConstraints.add(constraint);
UnaryConstraint unaryUserConstraint = engine.createUserConstraints(attribute);
if (unaryUserConstraint != null){
var.attributeUserConstraints.put(constraint, unaryUserConstraint);
}
}
// Path constraints:
if (!rule.getLhs().getPACs().isEmpty()) {
for (Node target : node.getGraph().getNodes()) {
if (node==target) continue;
for (Entry<List<EReference>,Integer> entry : PathFinder.findReferencePaths(node, target, true, true).entrySet()) {
if (entry.getKey().size() > 1) { // only paths of length > 1
Variable targetVar = node2variable.get(target);
PathConstraint constraint = new PathConstraint(targetVar, entry.getKey(), entry.getValue());
var.pathConstraints.add(constraint);
}
}
}
}
}
private void createDanglingConstraints(Node node) {
Variable var = node2variable.get(node);
DanglingConstraint constraint =
new DanglingConstraint(getEdgeCounts(node, false), getEdgeCounts(node, true));
var.danglingConstraints.add(constraint);
}
private Map<EReference, Integer> getEdgeCounts(Node node, boolean incoming) {
Collection<Edge> edges = incoming ? node.getIncoming() : node.getOutgoing();
Collection<Edge> oppositeEdges = incoming ? node.getOutgoing() : node.getIncoming();
if (edges.size() == 0)
return null;
Map<EReference, Integer> edgeCount = new HashMap<EReference, Integer>();
for (Edge edge : edges) {
EReference type = edge.getType();
Integer count = edgeCount.get(type);
if (count == null) {
count = ONE;
} else {
count = count + 1;
}
edgeCount.put(type, count);
}
for (Edge edge : oppositeEdges) {
if (edge.getType().getEOpposite() == null)
continue;
EReference oppType = edge.getType().getEOpposite();
if (incoming) {
// opposite edges are outgoing from the PoV of the node
Node remoteNode = edge.getTarget();
if (remoteNode.getOutgoing(oppType, node) == null) {
Integer count = edgeCount.get(oppType);
if (count == null) {
count = ONE;
} else {
count = count + 1;
}
edgeCount.put(oppType, count);
}
} else {
// opposite edges are incoming from the PoV of the node
Node remoteNode = edge.getSource();
if (node.getOutgoing(oppType, remoteNode) == null) {
Integer count = edgeCount.get(oppType);
if (count == null) {
count = ONE;
} else {
count = count + 1;
}
edgeCount.put(oppType, count);
}
}
}
return edgeCount;
}
public Node getVariableForNode(Variable variable) {
return variable2node.get(variable);
}
public Collection<Variable> getDependendVariables(Variable mainVariable) {
Collection<Variable> dependendVariables = new HashSet<Variable>();
for (Variable var : variable2mainVariable.keySet()) {
if (variable2mainVariable.get(var) == mainVariable)
dependendVariables.add(var);
}
return dependendVariables;
}
public Collection<Variable> getMainVariables() {
return mainVariables;
}
/**
* @return the graph2variables
*/
public Map<Graph, List<Variable>> getGraph2variables() {
return graph2variables;
}
/**
* @return the node2variable
*/
public Map<Node, Variable> getNode2variable() {
// TODO: change Solution to get rid of this method
return node2variable;
}
private static final Integer ONE = new Integer(1);
}