/** * <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); }