blob: 1c336d27f6512f1f28f6a60343de77e47cde8a0a [file] [log] [blame]
package org.eclipse.emf.henshin.sam.invcheck.algorithm;
import java.util.ArrayList;
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.Set;
import java.util.Stack;
import java.util.Vector;
import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.henshin.sam.invcheck.InvariantCheckerUtil;
import org.eclipse.emf.henshin.sam.invcheck.SubgraphIterator;
import org.eclipse.emf.henshin.sam.invcheck.adapter.GCNACAdapter;
import org.eclipse.emf.henshin.sam.invcheck.adapter.SamGraphInvCheckGraphAdapter;
import org.eclipse.emf.henshin.sam.invcheck.algorithm.IsomorphicPartPartialMatcher.MatchingState.BackTrackReason;
import org.eclipse.emf.henshin.sam.invcheck.algorithm.IsomorphicPartPartialMatcher.MatchingState.MatchingOperation;
import org.eclipse.emf.henshin.sam.invcheck.filter.RuleApplicationFilter;
import org.eclipse.emf.henshin.sam.invcheck.nac.GraphWithNacs;
import org.eclipse.emf.henshin.sam.invcheck.nac.MatchWithNacs;
import org.eclipse.emf.henshin.sam.invcheck.nac.NacFactory;
import org.eclipse.emf.henshin.sam.invcheck.nac.NegativeApplicationCondition;
import org.eclipse.emf.henshin.sam.model.samannotation.AnnotatedElem;
import org.eclipse.emf.henshin.sam.model.samannotation.Annotation;
import org.eclipse.emf.henshin.sam.model.samgraph.Edge;
import org.eclipse.emf.henshin.sam.model.samgraph.Graph;
import org.eclipse.emf.henshin.sam.model.samgraph.Node;
import org.eclipse.emf.henshin.sam.model.samgraph.SamgraphPackage;
import org.eclipse.emf.henshin.sam.model.samrules.RuleGraph;
import org.eclipse.emf.henshin.sam.model.samrules.SamrulesPackage;
import org.eclipse.emf.henshin.sam.model.samtrace.Match;
import org.eclipse.emf.henshin.sam.model.samtrace.SamtraceFactory;
import org.eclipse.emf.henshin.sam.model.samtypegraph.EdgeDirection;
import org.eclipse.emf.henshin.sam.model.samtypegraph.EdgeType;
import org.eclipse.emf.henshin.sam.model.samtypegraph.NodeType;
/**
* Finds matchings between a set of graph items (current subgraph) and a host
* graph. <br>
* <br>
* Is used six times: <br>
* <ul>
* <li>To find intersections between the right part of a rule and a
* <a href="../../../../../doc/glossary.html">forbidden property</a> in order to
* construct a counterexample. ({@link MatchSubgprahFilter})</li>
* <li>To translate NACs from a forbidden subgraph to a
* <a href="../../../../../doc/glossary.html">merged graph</a> when constructing
* a counterexample. ({@link NACTranslator})</li>
* <li>To ensure the applicability of a rule to a
* <a href="../../../../../doc/glossary.html">source graph pattern</a>. (
* {@link RuleApplicationFilter})</li>
* <li>To find a forbidden property in a source graph pattern. (
* {@link HybridPropertyFilter})</li>
* <li>To find the left part of a higher-priority rule in a source graph
* pattern. ({@link HybridGraphRuleFilter})</li>
* <li>To find the left part of an urgent rule in a
* <a href="../../../../../doc/glossary.html">target graph pattern</a>. (
* {@link HybridUrgentGraphRuleFilter})</li>
* </ul>
* <br>
* The IPM matches only positive components. When matching NACs, separate IPM
* instances are launched and filled with positive representations of the NACs.
* <br>
* <br>
* The IPM can be executed in different modes: <br />
* <dl>
* <dt>MATCH_CALLs</dt>
* <dd>try to match the current subgraph to the host graph. NACs are matched
* with separate NAC_MATCH_CALLs.</dd>
* <dt>NAC_MATCH_CALLs</dt>
* <dd>try to match the current subgraph (being the positive representation of a
* NAC including its positive interface) to the positive representation of its
* NAC counterpart (host graph).</dd>
* <dt>CHECK_NAC_CALLs</dt>
* <dd>try to find a NAC (its positive representation, to be exact) as positive
* part in the host graph after the NAC has been matched to another NAC. Finding
* such a positive occurrence of a NAC would obviously result in failure of the
* current matching process.</dd>
* <dt>NAC_TRANSLATION_CALLs</dt>
* <dd>find all partial matchings of the positive representation of a NAC
* (current subgraph) in a merged graph (host graph). If the NAC can be matched
* completely, the result will be empty.</dd>
* </dl>
*
* @author jfd
*/
public class IsomorphicPartPartialMatcher implements AlgorithmComponent {
private static class MatchingStateFactory {
/**
* The checkNodesStates array stores all available
* <code>MatchingStates</code> for checking nodes.
*/
private MatchingState[] checkNodeStates;
/**
* the index of the next CHECK_NODE <code>MatchingState</code>.
*/
private int checkNodeStatesIndex = 0;
/**
* The checkEdgeStates array stores all available
* <code>MatchingStates</code> for checking edges.
*/
private MatchingState[] checkEdgeStates;
/**
* the index of the next CHECK_EDGE <code>MatchingState</code>.
*/
private int checkEdgeStatesIndex = 0;
/**
* Initializes the factory with the specified number of
* <code>MatchingStates</code> for node and edge matching operations,
* respectively.
*
* @param non
* number of <code>MatchingStates</code> to create for
* matching <code>Nodes</code>.
* @param noe
* number of <code>MatchingStates</code> to create for
* matching <code>Edges</code>.
*/
public MatchingStateFactory(final int non, final int noe) {
checkNodeStates = new MatchingState[non];
checkEdgeStates = new MatchingState[noe];
checkNodeStatesIndex = non - 1;
checkEdgeStatesIndex = noe - 1;
for (int i = 0; i < non; i++) {
checkNodeStates[i] = new MatchingState(MatchingOperation.CHECK_NODE);
}
for (int i = 0; i < noe; i++) {
checkEdgeStates[i] = new MatchingState(MatchingOperation.CHECK_EDGE);
}
}
/**
* Same as <code>new MatchingStateFactory(n,n)</code>
*
* @see MatchingStateFactory#MatchingStateFactory(int, int)
* @param n
*/
public MatchingStateFactory(final int n) {
this(n, n);
}
/**
* Same as <code>new MatchingStateFactory(20,20);</code>
*
* @see MatchingStateFactory#MatchingStateFactory(int, int)
*/
public MatchingStateFactory() {
this(20, 20);
}
/**
* Returns a new <code>MatchingState</code> for checking
* <code>Nodes</code>.<br />
* If the factory's internal array still contains a
* <code>MathcingState</code> this one is returned. A new one will be
* created, otherwise.
*
* @return a <code>MatchingState</code> for matching <code>Nodes</code>.
*/
public MatchingState getCheckNodeState() {
MatchingState result;
if (checkNodeStatesIndex >= 0) {
result = checkNodeStates[checkNodeStatesIndex];
checkNodeStates[checkNodeStatesIndex] = null;
checkNodeStatesIndex--;
cleanMatchingState(result);
} else {
result = new MatchingState(MatchingOperation.CHECK_NODE);
}
return result;
}
/**
* Returns a new <code>MatchingState</code> for checking
* <code>Edges</code>.<br />
* If the factory's internal array still contains a
* <code>MathcingState</code> this one is returned. A new one will be
* created, otherwise.
*
* @return a <code>MatchingState</code> for matching <code>Edges</code>.
*/
public MatchingState getCheckEdgeState() {
MatchingState result;
if (checkEdgeStatesIndex >= 0) {
result = checkEdgeStates[checkEdgeStatesIndex];
checkEdgeStates[checkEdgeStatesIndex] = null;
checkEdgeStatesIndex--;
cleanMatchingState(result);
} else {
result = new MatchingState(MatchingOperation.CHECK_EDGE);
}
return result;
}
public void releaseMatchingState(MatchingState ms) {
switch (ms.matchOp) {
case CHECK_NODE:
if (checkNodeStatesIndex < checkNodeStates.length - 1) {
checkNodeStatesIndex++;
checkNodeStates[checkNodeStatesIndex] = ms;
}
break;
case CHECK_EDGE:
if (checkEdgeStatesIndex < checkEdgeStates.length - 1) {
checkEdgeStatesIndex++;
checkEdgeStates[checkEdgeStatesIndex] = ms;
}
break;
}
}
private void cleanMatchingState(MatchingState ms) {
ms.backtrack = BackTrackReason.NONE;
ms.currentEdge = null;
ms.currentNode = null;
ms.edgeIterator = null;
ms.mappedEdgeIterator = null;
ms.mappedToNode = null;
}
}
/**
* The graph the current subgraph will be matched to.
*/
private Graph hostGraph;
/**
* A set of graph items representing the current subgraph to be matched to
* the host graph.
*/
private Set<AnnotatedElem> currentSubGraph;
/**
* The data structure controlling the execution and order of the matching
* process.
*/
private Stack<MatchingState> stack = new Stack<MatchingState>();
/**
* A data structure storing the node matching, edge matching and NAC
* matching the matcher has currenty built.
*/
private MatchWithNacs currentMatching;
/**
* The order of the matching process for the current graphs.
*/
private LinkedList<MatchingState> matchingHistory = new LinkedList<MatchingState>();
/**
* Stores the match mode that is currently active to control the way the
* matchers works.
*/
private MatchMode mode = MatchMode.MATCH_CALL;
/**
* A list storing one matching for each matching step. Is used only within a
* NAC translation call.
*/
private LinkedList<MatchWithNacs> iterativeMatchings = new LinkedList<MatchWithNacs>();
/**
* A list preventing certain edges from being tried as "mapped" edges when
* executing a NAC translation call.
*/
private List<Edge> blacklist = new LinkedList<Edge>();
/**
* A list preventing certain nodes from being tried as "mapped" edges when
* executing a NAC translation call.
*/
private List<Node> nodeBlacklist = new LinkedList<Node>();
/**
* Stores the node degrees of the current subgraph's nodes.
*/
private Map<Node, Integer> nodeDegrees;
/**
* Stores the number of types in the host graph
*/
private Map<NodeType, Integer> typeCount;
/**
* Saves the nacs in the subgraph
*/
private Set<NegativeApplicationCondition> nacs = null;
/**
* Iterates over the nacs in the subgraph
*/
private Iterator<NegativeApplicationCondition> nacIterator = null;
/**
* Only used in nac match call or nac translation call.
*/
private Match nacInterfaceMatch = null;
/**
* Gets the graph or pattern the current subgraph will be matched to.
*
* @return The host graph
*/
public Graph getHostGraph() {
return this.hostGraph;
}
/**
* Sets the graph or pattern the current subgraph will be matched to and
* calculates the number of each node type.
*
* @param value
* The new host graph
*/
public void setHostGraph(final Graph value) {
this.hostGraph = value;
this.typeCount = calculateTypeCount();
}
/**
* Calculates the type count.
*/
private Map<NodeType, Integer> calculateTypeCount() {
Map<NodeType, Integer> result = new HashMap<NodeType, Integer>();
if (this.hostGraph == null) {
return result;
}
for (Node n : this.hostGraph.getNodes()) {
NodeType type = n.getInstanceOf();
if (result.containsKey(type)) {
result.put(type, result.get(type) + 1);
} else {
result.put(type, 1);
}
}
return result;
}
/**
* Indicates a failure to match NACs. New positive matching is required.
*/
boolean positiveFailure = false;
/**
* This attribute doesn't represent a GraphRule, it only represents the
* right handed side of a GraphRule. More generally speaking, it represents
* the graph containing the current subgraph (which might be the same
* graph).
*/
private Graph pattern;
/**
* Gets the graph containing the current subgraph.
*
* @return The graph containing the current subgraph
*/
public Graph getPattern() {
return this.pattern;
}
/**
* Sets the graph containing the current subgraph.
*
* @param value
* the graph containing the current subgraph
*/
public void setPattern(Graph value) {
this.pattern = value;
}
private Iterator<AnnotatedElem> startNodeIter;
/*
* /** This is an experimental field to save matchings where a nac in the
* pattern (or a part of its translation to the host, to be more exact) does
* not have an equivalent matching NAC in the host. Consequently, the
* implication host => pattern cannot be assured as the absence of the nac
* in the pattern cannot be assured in the host. We then save the positive
* match with the translated NACs for such cases and try to analyse the case
* with context generation in another filter. This means, that we treat the
* translated unmatched NACs as positive elements (including the original
* positive elements) and try to find forbidden patterns (without NACs) in
* the graph.
*
* (Actually, this is not really necessary. We could also check the
* translated elements directly for the forbidden patterns. However, this
* would then be part of the IPM which is probably not really desirable.
* That is what the next field is for.)
*/
/*
* private Map<Match, RuleGraph> incompleteNacMatchings; private Set<Graph>
* restrictingConstraints;
*
* public Map<Match, RuleGraph> getIncompleteMatchings() { return
* this.incompleteNacMatchings; }
*
* public void setRestrictingConstraint(Set<Graph> restrictingConstraints) {
* this.restrictingConstraints = restrictingConstraints; }
*
* public void addRestrictingConstraint(Graph restrictingConstraint) { if
* (this.restrictingConstraints == null) { this.restrictingConstraints = new
* HashSet<Graph>(); }
* this.restrictingConstraints.add(restrictingConstraint); }
*/
private MatchingStateFactory theFactory;
/**
* A <code>StartConfiguration</code> encapsulates all information that is
* required to start the matching of a connected component of the
* {@link IsomorphicPartPartialMatcher#currentSubGraph}.
*
* @author bb
*
*/
private static class StartConfiguration {
/**
* the current connected component's start node
*/
public Node startNode = null;
/**
* the {@link IsomorphicPartPartialMatcher#matchingHistory
* matchingHistories} index at the time this
* <code>StartConfiguration</code> has been created.
*/
public int historyIndex = 0;
/**
* The <code>Iterator</code> of <code>Nodes</code> contained in the
* hostgraph. The <code>Iterator</code> will be reset each time a match
* of a previous connected component has to be revoked.
*/
public Iterator<Node> hostNodeIter = null;
}
private List<StartConfiguration> connectedComponents = null;
private int connectedComponentIndex = 0;
/**
* Resets this <code>IsomorphicPartMatcher</code> instance.<br />
* In particular this method clears the {@link #stack} or creates an empty
* one and sets the fields {@link #currentMatching}, {@link #startNode} and
* {@link #hostNodeIter} as well as certain flags to null/their default
* value.
*/
public void reset() {
if (stack == null) {
stack = new Stack<MatchingState>();
} else
stack.clear();
if (theFactory == null) {
theFactory = new MatchingStateFactory();
}
if (matchingHistory == null) {
matchingHistory = new LinkedList<MatchingState>();
} else {
for (MatchingState ms : matchingHistory) {
theFactory.releaseMatchingState(ms);
}
matchingHistory.clear();
}
if (connectedComponents == null) {
this.connectedComponents = new ArrayList<StartConfiguration>();
} else {
this.connectedComponents.clear();
}
this.connectedComponents.add(new StartConfiguration());
this.connectedComponentIndex = 0;
this.iterativeMatchings.clear();
this.currentMatching = null;
this.nodeDegrees = null;
this.startNodeIter = null;
this.blacklist.clear();
this.nodeBlacklist = null;
this.nacIterator = null;
this.nacs = null;
this.positiveFailure = false;
this.nacInterfaceMatch = null;
}
private void addFirstCheckNACState() {
// if there is an check edge state with currentEdge == null, remove this
// entry
if (stack.size() > 0) {
if (stack.peek().matchOp == MatchingOperation.CHECK_EDGE && stack.peek().backtrack == BackTrackReason.NONE
&& stack.peek().currentEdge == null) {
stack.pop();
}
}
if (nacs == null) {
nacs = new HashSet<NegativeApplicationCondition>();
for (AnnotatedElem gI : currentSubGraph) {
if (SamgraphPackage.eINSTANCE.getEdge().isSuperTypeOf(gI.eClass())) {
if (InvariantCheckerUtil.isNegated((Edge) gI)) {
Edge e = (Edge) gI;
NegativeApplicationCondition nac = GCNACAdapter
.getInstance(InvariantCheckerUtil.getHighestCondition(e));
nacs.add(nac);
}
} else {
if (InvariantCheckerUtil.isNegated((Node) gI)) {
Node n = (Node) gI;
NegativeApplicationCondition nac = GCNACAdapter
.getInstance(InvariantCheckerUtil.getHighestCondition(n));
nacs.add(nac);
}
}
}
}
nacIterator = nacs.iterator();
MatchingState ms = new MatchingState(MatchingOperation.CHECK_NAC);
ms.currentNAC = nacIterator.next();
stack.push(ms);
}
/**
* Returns the next matching for the actual values of: {@link #pattern},
* {@link #hostGraph} and {@link #currentSubGraph}.<br />
* <p>
* The first invocation of this method after {@link #reset()} has been
* invoked, initializes the <code>IsomorphicPartMatcher</code>'s internal
* bookkeeping and returns the first matching of the
* {@link #currentSubGraph| current subgraph} in the {@link #hostGraph| host
* graph}. Following invocations invalidate the last matched element and
* start a backtracking procedure to find the next matching. If no more
* matching could be found the method returns <code>null</code>
* </p>
* The execution of this method might create additional instances of the
* matcher and execute them.
* <p>
* elements from the {@link #pattern| graph pattern} are returned as the
* keys and elements from the {@link #hostGraph| host graph} are returned as
* the values.
* </p>
* <br>
* A matching for a MATCH_CALL or a CHECK_NAC_CALL must fulfill the
* following conditions:<br>
* <ul>
* <li>All nodes from the current subgraph have been matched.</li>
* <li>All edges from the current subgraph have been matched.</li>
* <li>All NACs from the current subgraph have been matched (by using
* separate NAC_MATCH_CALLs).</li>
* </ul>
* <br>
* A matching for a NAC_MATCH_CALL can be correct without having matched all
* nodes and edges from the subgraph. This requires that the host graph is
* already completely matched so that the subgraph contains more the same,
* but more elements and thus is stricter that the host graph.<br>
* <br>
* Matchings for a NAC_TRANSLATION_CALL are returned as a list of partial
* matchings, see {@link #getIterativeMatchings()}.<br>
* <br>
* If no matching could be found, null will be returned.
*
* @return A <code>Match</code> representing a matching of {@link #pattern}
* in {@link #hostGraph} w.r.t to the {@link #currentSubGraph} or
* <code>null</code>.
*/
public Match getNextMatching() {
// When executing a NAC translation call, we only search for partial
// matches. Thus, the compareTypes method is not applicable in that
// case.
if (mode != MatchMode.NAC_TRANSLATION_CALL && !compareTypes(currentSubGraph, hostGraph)) {
return null;
}
if (mode != MatchMode.NAC_TRANSLATION_CALL
&& posSizeOfSubgraph(currentSubGraph) > this.hostGraph.getEdges().size()
+ this.hostGraph.getNodes().size()) {
return null;
}
if (mode == MatchMode.MATCH_CALL && numberOfNacs(currentSubGraph) > 0
&& (hostGraph.eClass() != SamrulesPackage.eINSTANCE.getRuleGraph()
|| ((RuleGraph) hostGraph).getCondition() == null)) {
return null;
}
MatchingState ms;
// System.out.println("===");
boolean reentrantCall = false;
boolean triedAllHostNodes = false;
if (this.currentMatching == null) {
this.currentMatching = NacFactory.eINSTANCE.createMatchWithNacs();
}
if (this.theFactory == null) {
this.theFactory = new MatchingStateFactory();
}
if (this.connectedComponents == null) {
this.connectedComponents = new ArrayList<StartConfiguration>();
this.connectedComponents.add(new StartConfiguration());
}
if (this.currentSubGraph == null) {
throw new IllegalStateException("initialize the object's current subgraph before invoking getNextMatching");
} else if (this.nodeDegrees == null) {
/*
* compute the node degrees of the current subgraph's nodes. We
* cannot use Node.getIncomingEdge().size() etc. as the this returns
* a value that is only valid for the complete pattern. But our
* search is restricted to the currentSubgraph attribute and so have
* to be the node's degrees.
*/
this.nodeDegrees = new HashMap<Node, Integer>(this.currentSubGraph.size());
for (Iterator<AnnotatedElem> iter = this.currentSubGraph.iterator(); iter.hasNext();) {
final AnnotatedElem gi = iter.next();
if (SamgraphPackage.eINSTANCE.getNode().isSuperTypeOf(gi.eClass())) {
if (!InvariantCheckerUtil.isNegated((Node) gi) && !this.nodeDegrees.containsKey(gi)) {
this.nodeDegrees.put((Node) gi, 0);
}
} else if (!InvariantCheckerUtil.isNegated((Edge) gi)) {
final Edge edge = (Edge) gi;
if (this.nodeDegrees.containsKey(edge.getSource())) {
this.nodeDegrees.put(edge.getSource(), this.nodeDegrees.get(edge.getSource()) + 1);
} else
this.nodeDegrees.put(edge.getSource(), 1);
if (this.nodeDegrees.containsKey(edge.getTarget())) {
this.nodeDegrees.put(edge.getTarget(), this.nodeDegrees.get(edge.getTarget()) + 1);
} else
this.nodeDegrees.put(edge.getTarget(), 1);
}
}
}
if ((mode == MatchMode.MATCH_CALL || mode == MatchMode.NAC_MATCH_CALL)
&& (this.stack != null && this.stack.size() > 0)) {
/*
* The method is invoked after a previous match has been found.
* Thus, we invalidate the stack's topmost entry, to start the
* matcher's backtracking functionality. This is only applicable
* when executing a match call (as for example when executing the
* MatchSubgraphFilter).
*/
reentrantCall = true;
handleReentrantCall();
}
/*
* The matching process will continue while there are unmatched nodes,
* edges or NACs in the current subgraph. The process may end
* prematurely in the case of a NAC match call when the host graph is
* found to be a stricter NAC.
*/
while ((reentrantCall
|| (((InvariantCheckerUtil.positiveSize(this.currentMatching) < posSizeOfSubgraph(currentSubGraph))
|| (this.currentMatching.getNacMatching().size() < numberOfNacs(currentSubGraph)))
&& (triedAllHostNodes == false)))) {
if (!reentrantCall && mode == MatchMode.MATCH_CALL
&& InvariantCheckerUtil.positiveSize(this.currentMatching) == posSizeOfSubgraph(currentSubGraph)
&& numberOfNacs(currentSubGraph) > 0) {
if (nacIterator == null && !positiveFailure) {
// positive matching is complete, build first check nac
// state
addFirstCheckNACState();
}
if (positiveFailure) {
positiveFailure = false;
}
}
reentrantCall = false;
if (stack.size() > 0) {
ms = stack.peek();
// if (mode != MatchMode.NAC_TRANSLATION_CALL || mode ==
// MatchMode.NAC_MATCH_CALL) {
// System.out.println("now checking: " + ms.toString() + " " +
// ms.backtrack);
// }
switch (ms.matchOp) {
case CHECK_NODE:
checkNode(ms);
break;
case CHECK_EDGE:
checkEdge(ms);
break;
case CHECK_NAC:
checkNAC();
break;
}
} else {
// Stack is empty, mode is MATCH_CALL, NAC_MATCH_CALL or
// NAC_TRANSLATION_CALL.
// Have to find an initial node.
// Will happen in two cases: 1) first call of gNM for a specific
// subgraph or 2) stack has been completely removed because of
// backtracking.
triedAllHostNodes = createInitialNodePair();
if (triedAllHostNodes && connectedComponentIndex > 0) {
for (int i = connectedComponents
.get(connectedComponentIndex - 1).historyIndex; i < connectedComponents
.get(connectedComponentIndex).historyIndex; i++) {
if (i < matchingHistory.size())
stack.push(matchingHistory.get(i));
}
stack.peek().backtrack = BackTrackReason.FAILURE;
this.connectedComponentIndex--;
triedAllHostNodes = false;
}
if (this.connectedComponents.get(connectedComponentIndex).startNode == null) {
return null;
}
}
}
return triedAllHostNodes ? null : this.currentMatching.copy();
}
/**
* This method determines the number of NACs contained in the given
* subgraph.
*
* @param currentSubGraph
* the graph whose NACs will be counted
* @return the number of NACs
*/
private int numberOfNacs(Set<AnnotatedElem> currentSubGraph) {
Set<NegativeApplicationCondition> nacs = new HashSet<NegativeApplicationCondition>();
for (AnnotatedElem gI : currentSubGraph) {
if (SamgraphPackage.eINSTANCE.getEdge().isSuperTypeOf(gI.eClass())) {
if (InvariantCheckerUtil.isNegated((Edge) gI)) {
Edge e = (Edge) gI;
NegativeApplicationCondition nac = GCNACAdapter
.getInstance(InvariantCheckerUtil.getHighestCondition(e));
nacs.add(nac);
}
} else {
if (InvariantCheckerUtil.isNegated((Node) gI)) {
Node n = (Node) gI;
NegativeApplicationCondition nac = GCNACAdapter
.getInstance(InvariantCheckerUtil.getHighestCondition(n));
nacs.add(nac);
}
}
}
return nacs.size();
}
/**
* This method determines the number of positive graph items in a given
* subgraph.
*
* @param currentSubGraph
* the subgraph whose positive items will be counted
* @return the number of positive items
*/
private int posSizeOfSubgraph(Set<AnnotatedElem> currentSubGraph) {
int count = 0;
for (AnnotatedElem gI : currentSubGraph) {
if (SamgraphPackage.eINSTANCE.getEdge().isSuperTypeOf(gI.eClass())) {
if (!InvariantCheckerUtil.isNegated((Edge) gI)) {
count++;
}
} else {
if (!InvariantCheckerUtil.isNegated((Node) gI)) {
count++;
}
}
}
return count;
}
/**
* This method creates an arbitrary pair of two nodes and pushes an
* corresponding {@link MatchingState} instance onto the {@link #stack}.
* <br />
* If the {@link #startNode} field is empty the {@link #currentSubGraph}'s
* first node is selected. The {@link #startNode} is then tried to match to
* the {@link #hostGraph}'s next {@link Node}.
*
* @return <code>false</code> if the {@link #hostGraph} has unvisited nodes.
* <code>true></code> otherwise.
*/
private boolean createInitialNodePair() {
// System.out.println("createInitialNodePair");
boolean triedAllHostNodes;
if (this.currentMatching.getSize() > this.connectedComponents.get(connectedComponentIndex).historyIndex
&& this.currentMatching.getSize() < this.currentSubGraph.size()) {
this.connectedComponentIndex++;
if (this.connectedComponents.size() > this.connectedComponentIndex) {
this.connectedComponents.get(this.connectedComponentIndex).hostNodeIter = null;
} else {
final StartConfiguration tmpConf = new StartConfiguration();
tmpConf.historyIndex = this.currentMatching.getSize();
tmpConf.startNode = null;
tmpConf.hostNodeIter = null;
this.connectedComponents.add(tmpConf);
}
}
if (this.connectedComponents.get(connectedComponentIndex).startNode == null) {
if (mode == MatchMode.NAC_TRANSLATION_CALL && connectedComponentIndex == 0) {
return true;
}
findStartNode();
}
if (this.connectedComponents.get(connectedComponentIndex).hostNodeIter == null) {
this.connectedComponents.get(connectedComponentIndex).hostNodeIter = this.hostGraph.getNodes().iterator();
}
Node next = null;
triedAllHostNodes = false;
// iterate through nodes of hostGraph
// iterator is resetted for each subgraph (input)
while (this.connectedComponents.get(connectedComponentIndex).hostNodeIter.hasNext() && next == null) {
next = this.connectedComponents.get(connectedComponentIndex).hostNodeIter.next();
if (currentMatching.getNodeMatching().containsValue(next) == false) {
final MatchingState newms = theFactory.getCheckNodeState();
newms.currentNode = this.connectedComponents.get(connectedComponentIndex).startNode;
newms.mappedToNode = next;
// types, negated are not checked, this is done in the checkNode
// method
stack.push(newms);
triedAllHostNodes = false;
}
}
if (next == null) {
triedAllHostNodes = true;
}
return triedAllHostNodes;
}
/**
* Identifies the first not negative {@link Node} in the
* {@link #currentSubGraph}.<br />
* This node is not directly returned but the {@link #startNode} field is
* accordingly set.
*/
private void findStartNode() {
if (startNodeIter == null) {
startNodeIter = this.currentSubGraph.iterator();
}
while (this.connectedComponents.get(connectedComponentIndex).startNode == null && startNodeIter.hasNext()) {
AnnotatedElem next = startNodeIter.next();
if (next instanceof Node && !this.currentMatching.getNodeMatching().containsKey((Node) next)) {
Node n = (Node) next;
if (!InvariantCheckerUtil.isNegated(n)) {
this.connectedComponents.get(connectedComponentIndex).startNode = n;
}
}
}
}
/**
* This method is invoked if the {@link #getNextMatching()} method is not
* invoked the first time.<br />
* The method prepares the {@link #stack} to start the backtracking
* functionality to find a new matching.
*
* Matching has been found and returned, then call of getNextMatching on
* same subgraph calls this
*/
private void handleReentrantCall() {
boolean stop = false;
this.currentMatching.getNacMatching().clear();
this.nacIterator = null;
while (this.stack.size() > 0 && !stop) {
// remove all check nac states.
if (this.stack.peek().matchOp == MatchingOperation.CHECK_NAC) {
this.stack.pop();
} else {
stop = true;
}
}
if (this.stack.size() > 0 && this.stack.peek().matchOp == MatchingOperation.CHECK_EDGE
&& this.stack.peek().currentEdge == null) {
/*
* If the last entry of the stack is a CHECK_EDGE entry, but it's
* currentEdge field is set to null, this entry has not lead to a
* matching and thus has to be removed from the stack.
*/
this.stack.pop();
}
if (this.stack.size() > 0) {
// Initiates the backtracking.
this.stack.peek().backtrack = BackTrackReason.FAILURE;
}
}
public void handleNACMatchCall(Match interfaceMatching, List<Node> interfaceBlacklist) {
this.nodeBlacklist = interfaceBlacklist;
mode = MatchMode.NAC_MATCH_CALL;
this.nacInterfaceMatch = interfaceMatching;
}
/**
* This method prepares the IPM instance for a NAC translation call,
* starting from (negative) nodes that do not belong to the NAC interface.
*
* @param startNode
* the start node
*/
public void handleNacTranslationCall(Node startNode, List<Edge> edgeBlacklist, List<Node> nodeBlacklist,
Match nacInterfaceMatch) {
mode = MatchMode.NAC_TRANSLATION_CALL;
this.blacklist = edgeBlacklist;
this.nodeBlacklist = nodeBlacklist;
this.nacInterfaceMatch = nacInterfaceMatch;
if (this.currentMatching == null) {
this.currentMatching = NacFactory.eINSTANCE.createMatchWithNacs();
}
this.connectedComponents.get(0).startNode = startNode;
createInitialNodePair();
// System.out.println("inner nac translation call: " + startNode);
}
/**
* This method will handle a CHECK_NODE matching state. It will contain both
* the current node (from the current subgraph) and the corresponding
* counterpart (to be checked) from the host graph. The nature of the state
* might be: <br>
* <br>
* <dl>
* <dt>Failure</dt>
* <dd>Remove the current matching state from the stack. Remove both nodes
* from the current matching. Set the nature of the previous state to
* failure.</dd>
* <dt>Backtrack</dt>
* <dd>Remove the current matching state from the stack. Set the nature of
* the previous state to backtrack unless the IPM executes a NAC_MATCH_CALL
* or a CHECK_NAC_CALL.</dd>
* <dt>None</dt>
* <dd>Check {@link #degreePredicate(Node, Node)},
* {@link #negatedPredicate(Node, Node)} and
* {@link #typePredicate(Node, Node)} for the current nodes. If successful,
* add the nodes to the current matching and add a new CHECK_EDGE matching
* state to the stack. Otherwise remove the current matching state from the
* stack and set the previous state to failure.</dd>
* </dl>
* <br>
* A CHECK_NODE state usually results from a successfully handled CHECK_EDGE
* state. It is also the first matching state on the stack when executing a
* MATCH_CALL.
*
* @param ms
* the current matching state
*/
private void checkNode(final MatchingState ms) {
if (ms.backtrack == BackTrackReason.FAILURE) {
// Remove wrong/invalid matchingState from stack.
stack.pop();
this.currentMatching.getNodeMatching().remove(ms.currentNode);
this.matchingHistory.remove(ms);
theFactory.releaseMatchingState(ms);
if (stack.size() > 0) {
// If the node matching is invalid, the edge matching leading to
// it has to be removed, too.
final MatchingState tmpMS = stack.peek();
tmpMS.backtrack = BackTrackReason.FAILURE;
}
} else if (ms.backtrack == BackTrackReason.BACKTRACK) {
// backtracking, remove stack until another chance can be found
stack.pop();
if (stack.size() > 0) {
stack.peek().backtrack = BackTrackReason.BACKTRACK;
}
} else {
// check predicates, build new checkEdge state if applicable
final Node expectedNode = ms.currentNode;
final Node foundNode = ms.mappedToNode;
if (nacInterfaceNodePredicate(expectedNode, foundNode) && degreePredicate(foundNode, expectedNode)
&& typePredicate(expectedNode, foundNode) && negatedPredicate(expectedNode, foundNode)
&& ((!currentMatching.getNodeMatching().containsKey(expectedNode)
&& !currentMatching.getNodeMatching().containsValue(foundNode))
|| (foundNode == currentMatching.getNodeMatching().get(expectedNode)))) {
this.currentMatching.getNodeMatching().put(expectedNode, foundNode);
this.matchingHistory.add(ms);
if (this.mode == MatchMode.NAC_TRANSLATION_CALL) {
iterativeMatchings.add(currentMatching.copy());
}
MatchingState newms = theFactory.getCheckEdgeState();
newms.currentNode = expectedNode;
newms.mappedToNode = foundNode;
newms.currentNAC = ms.currentNAC;
newms.mappedToNAC = ms.mappedToNAC;
stack.push(newms);
// next call should be checkEdge
} else {
// cannot match nodes, remove from stack
// next call should be checkEdge
stack.pop();
if (stack.size() > 0) {
stack.peek().backtrack = BackTrackReason.FAILURE;
}
}
}
}
/**
* Returns an unmatched edge in the current subgraph that leads to or comes
* from the current node in the matching state. This method will try to
* return positive edges first.
*
* An edge will be returned if it fulfills all of the following conditions:
* <ul>
* <li>It is not contained in the current matching, thus being unmatched
* </li>
* <li>It is contained in the edgeIterator of the current matching state,
* which iterates over the edges from the currentNode of the current state
* </li>
* <li>It is contained in the current subgraph (and the pattern)</li>
* <li>It is contained in the current subgraph (and the pattern)</li>
* </ul>
* and additionally fulfills at least one of the following conditions:
* <ul>
* <li>It is positive (i.e. does not belong to a NAC) and there are
* unchecked edges left</li>
* <li>It is negative (i.e. belongs to a NAC), all positive edges have been
* checked and the <a href="../../../../../doc/glossary.html">positive NAC
* interface</a> has been matched</li>
* <li>The IPM executes a NAC_TRANSLATION_CALL</li>
* </ul>
*
* When executed the first time, this method will try to return a positive
* edge as the matching of NACs currently assumes that the positive NAC
* interfaces have already been matched. When all edges have been checked,
* the iterator will be reset and the ignoreNegativeEdges flag will be set
* to true. When iterating over the edges for the second time, negative
* edges will also be checked.
*
* @param ms
* the current matching state
* @return the unmatched edge
*/
private Edge getNextUnmatchedEdge(MatchingState ms) {
if (ms != null) {
if (ms.edgeIterator == null) {
ms.edgeIterator = iteratorOfEdges(ms.currentNode);
}
while (ms.edgeIterator.hasNext()) {
final Edge nextEdge = ms.edgeIterator.next();
/*
* "unmatched", thus not contained in currentMatching but in the
* subgraph Only positive edges should be considered.
*/
if (this.currentMatching != null && !this.currentMatching.getEdgeMatching().containsKey(nextEdge)
&& this.currentSubGraph.contains(nextEdge) && !InvariantCheckerUtil.isNegated(nextEdge)) {
return nextEdge;
}
}
}
return null;
}
/**
* This method will handle a CHECK_EDGE matching state. It might contain the
* current edge from the current subgraph and might also contain the
* corresponding counterpart (which might be checked) from the host graph.
* The nature of the state might be: <br>
* <br>
* <dl>
* <dt>Failure</dt>
* <dd>If the state is the last state in the matching history, remove it
* from the stack and remove both edges from the current matching. Set the
* state to NONE so that the next call will try to find a new matching edge.
* If not, rebuild the stack. Set the nature of the topmost state to FAILURE
* so that the next call will evaluate the states leading to the current
* failure.</dd>
* <dt>Backtrack</dt>
* <dd>If another unmatched edge going from or to the current node can be
* found, put it on the stack as a new CHECK_EDGE state. If not, remove the
* state from the stack and set the previous state to BACKTRACK in order to
* continue the backtracking process.</dd>
* <dt>None</dt>
* <dd>If the current edge is not set, find one by calling
* {@link #getNextUnmatchedEdge(MatchingState)}. Call
* {@link #findMatchingEdge(MatchingState)} to find a corresponding
* counterpart to the current edge in the host graph. This method will
* handle the predicates to be checked.</dd>
* </dl>
* <br>
* When executing a normal (NONE) CHECK_EDGE call, the following scenarios
* might occur: <br>
* <ul>
* <li>A matching edge will be found by
* {@link #findMatchingEdge(MatchingState)}. The implications of this will
* be explained in said method.</li>
* <li>There is no unmatched edge left to be returned by
* {@link #getNextUnmatchedEdge(MatchingState)}. The matching state will be
* removed from the stack and the previous state will be set to BACKTRACK.
* </li>
* <li>There was no matching edge to be found and the IPM is in NAC_MATCH
* mode. If the host graph NAC is already completely matched, it is stricter
* than the NAC in the current subgraph. In that case, the
* {@link #stricterNac} flag will be set and the matching process will
* terminate in the next iteration of the main loop.</li>
* <li>There was no matching edge to be found and the IPM is in
* NAC_TRANSLATION mode. As we look for incomplete matching, we cannot
* simply return with FAILURE but need to check other edges by using
* {@link #getNextUnmatchedEdge(MatchingState)}. If there is an unmatched
* edge, a new matching state will be put on the stack. If not, remove the
* state from the stack and set the previous state to FAILURE.</li>
* <li>There was no matching edge and the IPM is in standard MATCH mode.
* Remove the state from the stack and set the prevoius state to FAILURE.
* </li>
* </ul>
*
* <br>
* A CHECK_EDGE state is the first matching state on the stack when
* executing a MATCH_NAC_CALL, a CHECK_NAC_CALL or a NAC_TRANSLATION_CALL
* starting from the <a href="../../../../../doc/glossary.html">positive NAC
* interface</a> as these calls have predefined starting nodes from the
* current subgraph with corresponding counterparts from the host graph.
*
* @param ms
* the current matching state
*/
private void checkEdge(final MatchingState ms) {
if (ms.backtrack == BackTrackReason.FAILURE) {
if (this.matchingHistory != null && !this.matchingHistory.isEmpty()
&& this.matchingHistory.getLast() != null && ms != this.matchingHistory.getLast()) {
// where is the current ms in the history?
final int index = this.matchingHistory.indexOf(ms);
if (index > -1) {
/*
* current matching state is not the latest state in the
* history, return all matchingStates coming after the
* current state from the history to the stack Stack has to
* be rebuilt, because the wrong matching is probably not on
* the stack, but in the history. So we have to check
* previous matchings because otherwise we might pop
* everything from the stack without finding the error.
*/
for (Iterator<MatchingState> msIter = this.matchingHistory.listIterator(index + 1); msIter
.hasNext(); this.stack.push(msIter.next()))
;
if (stack.size() > 0) {
stack.peek().backtrack = BackTrackReason.FAILURE;
}
}
} else {
// matchingState was the latest state in the history or there
// was no history
// find another edge that can be mapped to the current edge
// next call should be checkEdge with the same matching state to
// find another
this.currentMatching.getEdgeMatching().remove(ms.currentEdge);
this.matchingHistory.remove(ms);
}
ms.backtrack = BackTrackReason.NONE;
} else if (ms.backtrack == BackTrackReason.BACKTRACK) {
// we are in backtracking mode. This means we have to find a new
// currentEdge for the current MatchingState to be matched
// reset the iterator looking for unmatched edges
ms.edgeIterator = null;
final Edge nue = this.getNextUnmatchedEdge(ms);
if (nue == null) {
// no unmatched edge from the node in question, go back
stack.pop();
if (stack.size() > 0) {
stack.peek().backtrack = BackTrackReason.BACKTRACK;
}
} else {
// there is another edge, next call is checkEdge
final MatchingState newMS = theFactory.getCheckEdgeState();
newMS.currentEdge = nue;
newMS.currentNode = ms.currentNode;
newMS.mappedToNode = ms.mappedToNode;
newMS.currentNAC = ms.currentNAC;
newMS.mappedToNAC = ms.mappedToNAC;
// NAC translation call
if (mode == MatchMode.NAC_TRANSLATION_CALL) {
newMS.edgeIterator = ms.edgeIterator;
}
stack.push(newMS);
}
} else {
// default case, try to find a matching edge
if (ms.currentEdge == null) {
ms.currentEdge = this.getNextUnmatchedEdge(ms);
if (ms.currentEdge == null) {
// We did not find any unmatched edge, following we track
// back to find further unmatched subgraph elements.
stack.pop();
if (stack.size() > 0) {
// this is the only point where backtracking (not due to
// failure) is activated
// assuming we tried to match an edge that did not
// exist, the next call is checkNode
stack.peek().backtrack = BackTrackReason.BACKTRACK;
}
return;
}
}
if (ms.mappedEdgeIterator == null) {
ms.mappedEdgeIterator = iteratorOfEdges(ms.mappedToNode);
}
boolean foundMatch = findMatchingEdge(ms);
if (foundMatch == false) {
// we did not find a match for at least one Edge
if (mode != MatchMode.NAC_TRANSLATION_CALL) {
stack.pop();
if (stack.size() > 0) {
stack.peek().backtrack = BackTrackReason.FAILURE;
}
} else {
/*
* When in NAC translation mode, we only search for
* incomplete matchings. So we can only "fail" when there
* are no unmatched edges left on a certain node in the
* current subgraph.
*/
Edge nue = getNextUnmatchedEdge(ms);
if (nue == null) {
stack.pop();
if (stack.size() > 0) {
stack.peek().backtrack = BackTrackReason.FAILURE;
}
} else {
final MatchingState newMS = new MatchingState(MatchingOperation.CHECK_EDGE);
newMS.currentEdge = nue;
newMS.currentNode = ms.currentNode;
newMS.mappedToNode = ms.mappedToNode;
newMS.currentNAC = ms.currentNAC;
newMS.mappedToNAC = ms.mappedToNAC;
// nacTC
if (mode == MatchMode.NAC_TRANSLATION_CALL) {
newMS.edgeIterator = ms.edgeIterator;
}
stack.push(newMS);
}
}
}
}
}
/**
* This method finds an edge in the host graph for the current edge of the
* subgraph contained in the given matching state.
*
* For an edge to be matched to the current edge, it must not be contained
* as a value in the current matching. Additionally, the predicates
* {@link #typePredicate(Edge, Edge), {@link #negatedPredicate(Edge, Edge)}}
* and {@link #directionPredicate(Edge, Edge, Node, Node)} need to be
* checked.<br>
* <br>
* If these conditions hold, the edges will be matched and added to the
* current matching. Depending on the nature of the edges, one of the
* following will happen:
*
* <ul>
* <li>The edges are negated (i.e. belonging to NACs). A CHECK_NAC state
* will be put on the stack. This will result in the execution of the
* {@link #checkNAC()} method, which might result in the execution of a
* NAC_MATCH_CALL.</li>
* <li>The edges are positive and the node at the other end of the current
* edge is unmatched. A CHECK_NODE state with both nodes is put on the
* stack.</li>
* <li>The edges are positive and the node at the other end of the current
* edge has already been matched to the node at the other end of the mapped
* edge. This means that we do not need to match the nodes by creating a
* CHECK_NODE state. Instead, just search for unmatched edges going to or
* from the current node by putting a new CHECK_EDGE state on the stack.
* </li>
* </ul>
* <br>
* If no matching edge can be found, false will be returned. The calling
* method ({@link #checkNode(MatchingState)}) will handle this result.
*
* @param ms
* the current matching state
* @return true if there is a matching edge, false otherwise
*/
private boolean findMatchingEdge(final MatchingState ms) {
final Iterator<Edge> theIterator = ms.mappedEdgeIterator;
while (theIterator.hasNext()) {
final Edge foundEdge = theIterator.next();
// the blacklist condition is only used in NAC translation calls
if (!currentMatching.getEdgeMatching().containsValue(foundEdge) && !blacklist.contains(foundEdge)) {
// predicates
if (typePredicate(ms.currentEdge, foundEdge) && negatedPredicate(ms.currentEdge, foundEdge)
&& directionPredicate(ms.currentEdge, foundEdge, ms.currentNode, ms.mappedToNode)) {
// get the nodes at the other side of the edge
final Node newCurrentNode = ms.currentEdge.getSource() == ms.currentNode
? ms.currentEdge.getTarget() : ms.currentEdge.getSource();
final Node newFoundNode = foundEdge.getSource() == ms.mappedToNode ? foundEdge.getTarget()
: foundEdge.getSource();
if (!this.currentMatching.getNodeMatching().containsKey(newCurrentNode)) {
// the nodes on the other side of the edge are unmatched
this.currentMatching.getEdgeMatching().put(ms.currentEdge, foundEdge);
this.matchingHistory.add(ms);
final MatchingState newMS = theFactory.getCheckNodeState();
newMS.currentNode = newCurrentNode;
newMS.mappedToNode = newFoundNode;
newMS.currentNAC = ms.currentNAC;
newMS.mappedToNAC = ms.mappedToNAC;
newMS.backtrack = BackTrackReason.NONE;
// next call is check node
stack.push(newMS);
return true;
} else if (this.currentMatching.getNodeMatching().get(newCurrentNode) == newFoundNode) {
/*
* The nodes on the other side of the edge are already
* matched, so we found some kind of cycle. Therefore we
* will try to find another unmatched edge for the
* current matching state with its current node.
*/
this.currentMatching.getEdgeMatching().put(ms.currentEdge, foundEdge);
this.matchingHistory.add(ms);
if (this.mode == MatchMode.NAC_TRANSLATION_CALL) {
this.iterativeMatchings.add(this.currentMatching.copy());
}
final MatchingState newMS = theFactory.getCheckEdgeState();
newMS.currentNode = ms.currentNode;
newMS.mappedToNode = ms.mappedToNode;
newMS.currentNAC = ms.currentNAC;
newMS.mappedToNAC = ms.mappedToNAC;
// next call is check edge
stack.push(newMS);
return true;
}
} // if (type ...
} // if (!currentMatching
} // while...
return false;
}
private void checkNAC() {
MatchingState ms = stack.peek();
if (ms.backtrack == BackTrackReason.FAILURE) {
// remove nac matching
currentMatching.getNacMatching().remove(ms.currentNAC);
nacIterator = null;
positiveFailure = true;
stack.pop();
if (stack.size() > 0) {
stack.peek().backtrack = BackTrackReason.FAILURE;
}
} else if (ms.backtrack == BackTrackReason.BACKTRACK) {
stack.pop();
if (stack.size() > 0) {
stack.peek().backtrack = BackTrackReason.BACKTRACK;
}
} else {
// check whether bound items in NACs are part of the positive
// matching so a translation is not necessary
boolean translate = true;
for (NegativeApplicationCondition nac : SamGraphInvCheckGraphAdapter.getInstance(hostGraph).getNacs()) {
boolean allIn = true;
if (nac.getAnnotations().isEmpty()) {
allIn = false;
}
for (Annotation an : nac.getAnnotations()) {
if (!currentMatching.getNodeMatching().containsValue(an.getTarget())
&& !currentMatching.getEdgeMatching().containsValue(an.getTarget())) {
allIn = false;
}
}
if (allIn) {
translate = false;
}
}
// translate = true;
NACTranslator nacT1 = new NACTranslator();
nacT1.setMergedGraph((RuleGraph) hostGraph);
nacT1.setInitialMatching(this.currentMatching.copy());
nacT1.setPattern(pattern);
if (nacT1.checkFullNacExistence()) {
ms.backtrack = BackTrackReason.FAILURE;
return;
}
// ms.translatedGraph = (RuleGraph) pattern;
// if (ms.translatedGraph == null) {
if (translate) {
// if a translation is necessary, it should be done to the union
// of the positive part of the pattern and the NAC interface in
// the
// host graph.
NACTranslator nacT = new NACTranslator();
// NACTranslatorOp nacT = new NACTranslatorOp();
nacT.setMergedGraph((RuleGraph) hostGraph);
nacT.setInitialMatching(this.currentMatching.copy());
nacT.setPattern(pattern);
if (nacT.checkFullNacExistence()) {
ms.backtrack = BackTrackReason.FAILURE;
return;
}
nacT.setInitialMatching(null);
nacT.setMergedGraph(null);
nacT.setPattern(null);
// Match translation =
// InvariantCheckingUtil.copyAsRuleGraph(hostGraph); // full
// translation
// translation to the minimum graph that includes the nac
// interface and the pattern:
Match translation = InvariantCheckerUtil.createMinimalTranslation(hostGraph, currentMatching);
Graph translated = null;
if (translation.getNodeMatching().size() > 0) {
translated = (Graph) translation.getNodeMatching().get(0).getValue().eContainer();
}
if (translated == null && translation.getEdgeMatching().size() > 0) {
translated = (Graph) translation.getEdgeMatching().get(0).getValue().eContainer();
}
// check whether host graph has necessary NACs to imply pattern
// NACs, by type comparison
/*
* Map<NodeType, Integer> nodeTypes = new HashMap<NodeType,
* Integer>(); for (NegativeApplicationCondition nac :
* SamGraphInvCheckGraphAdapter.getInstance(hostGraph).getNacs()
* ) { for (Node n : nac.getNodes()) { if
* (nodeTypes.containsKey(n.getInstanceOf())) {
* nodeTypes.put(n.getInstanceOf(),
* nodeTypes.get(n.getInstanceOf()) + 1); } else {
* nodeTypes.put(n.getInstanceOf(), 1); } } for (Annotation an :
* nac.getAnnotations()) { if
* (an.getSource().equals(InvariantCheckingCore.NAC_BOUND_ITEM))
* { if (SamgraphPackage.eINSTANCE.getNode().isSuperTypeOf(an.
* getTarget().eClass())) { Node n = (Node) an.getTarget(); if
* (nodeTypes.containsKey(n.getInstanceOf())) {
* nodeTypes.put(n.getInstanceOf(),
* nodeTypes.get(n.getInstanceOf()) + 1); } else {
* nodeTypes.put(n.getInstanceOf(), 1); } } } } // comparing one
* NAC should be enough break; } for (Map.Entry<NodeType,
* Integer> entry : nodeTypes.entrySet()) { boolean found =
* false; for (Node n : ms.currentNAC.getNodes()) { if
* (n.getInstanceOf() == entry.getKey()) { found = true; break;
* } } if (!found) { for (Node n : pattern.getNodes()) { if
* (!this.currentMatching.getNodeMatching().contains(n) &&
* translation.getNodeMatching().get(n) != null &&
* n.getInstanceOf() == entry.getKey()) { found = true; break; }
* } } if (!found) { ms.backtrack = BackTrackReason.FAILURE;
* return; } }
*
*/
nacT.setMergedGraph((RuleGraph) translated);
nacT.setPattern(pattern);
Match initialMatching = SamtraceFactory.eINSTANCE.createMatch();
for (Map.Entry<Node, Node> entry : currentMatching.getNodeMatching()) {
initialMatching.getNodeMatching().put(entry.getKey(),
translation.getNodeMatching().get(entry.getValue()));
}
for (Map.Entry<Edge, Edge> entry : currentMatching.getEdgeMatching()) {
initialMatching.getEdgeMatching().put(entry.getKey(),
translation.getEdgeMatching().get(entry.getValue()));
}
nacT.setInitialMatching(initialMatching);
// print debug
ms.translatedGraph = nacT.translateSingleNac(ms.currentNAC);
if (ms.translatedGraph == null) {
// positive occurrence of nac from current subgraph/pattern
// in host graph
// => abort, look for new positive matching
ms.backtrack = BackTrackReason.FAILURE;
return;
}
ms.translation = translation;
} else {
ms.translatedGraph = (RuleGraph) pattern;
}
GraphWithNacs tmp = SamGraphInvCheckGraphAdapter.getInstance(ms.translatedGraph);
if (ms.currentNACIter == null) {
ms.currentNACIter = tmp.getNacs().iterator(); // SamGraphInvCheckGraphAdapter.getInstance(ms.translatedGraph).getNacs().iterator();
}
if (ms.stack == null) {
ms.stack = new Stack<NacMatchingState>();
}
if (ms.nacMatching == null) {
ms.nacMatching = NacFactory.eINSTANCE.createMatchWithNacs();
}
while (ms.nacMatching.getNacMatching().size() < tmp.getNacs().size()) {
if (ms.stack.size() > 0) {
// System.out.println(ms.stack.peek());
boolean triedAll = checkSingleNAC(ms);
if (triedAll) {
// tried all nacs from the host graph to fit in the nac
// translated from
// pattern to host.
// now try to find forbidden graphs
// (restrictingConstraints) in the nac (nac + pos.
// elements)
// boolean found = false;
/*
* if (this.restrictingConstraints != null) {
* IsomorphicPartPartialMatcher subIpm = new
* IsomorphicPartPartialMatcher(); Match m =
* SubgraphIterator.fullNacToGraph(ms.stack.peek().
* currentNAC, true); Graph newHost = null; for (Node n
* : m.getNodeMatching().values()) { newHost = (Graph)
* n.eContainer(); break; }
* subIpm.setHostGraph(newHost);
*
* for (Graph constraint : this.restrictingConstraints)
* { subIpm.setPattern(constraint);
* subIpm.setCurrentSubGraph(SubgraphIterator.
* graphToSubGraph(constraint)); subIpm.reset(); if
* (subIpm.getNextMatching() != null) { found = true;
* ms.nacMatching.getNacMatching().put(ms.stack.peek().
* currentNAC, null); break; } } } else {
*/
// this happens when there is no equivalent NAC in the
// host graph for one (specific) NAC
// in the translation of the current NAC to the host
// graph
ms.backtrack = BackTrackReason.FAILURE;
// System.out.println("nac match failure");
return;
// }
}
} else {
if (ms.currentNACIter.hasNext()) {
NegativeApplicationCondition next = ms.currentNACIter.next();
NacMatchingState newNMS = new NacMatchingState();
newNMS.currentNAC = next;
ms.stack.push(newNMS);
}
}
}
// add main nac matching
currentMatching.getNacMatching().put(ms.currentNAC, ms.mappedToNAC);
// successful matching: put next nac matching on the main stack
if (nacIterator.hasNext()) {
NegativeApplicationCondition next = nacIterator.next();
MatchingState newMS = new MatchingState(MatchingOperation.CHECK_NAC);
newMS.currentNAC = next;
stack.push(newMS);
// cleanup, trying to save some memory
ms.nacMatching = null;
ms.stack.clear();
ms.translatedGraph = null;
if (ms.translation != null) {
ms.translation.clear();
}
ms.translation = null;
}
}
}
private boolean checkSingleNAC(MatchingState ms) {
NacMatchingState nacms = ms.stack.peek();
if (nacms.backtrack == BackTrackReason.FAILURE) {
ms.stack.pop();
if (ms.stack.size() > 0) {
ms.stack.peek().backtrack = BackTrackReason.FAILURE;
}
return false;
} else if (nacms.backtrack == BackTrackReason.BACKTRACK) {
ms.stack.pop();
if (ms.stack.size() > 0) {
ms.stack.peek().backtrack = BackTrackReason.BACKTRACK;
}
return false;
} else {
if (nacms.mappedNACIter == null) {
nacms.mappedNACIter = SamGraphInvCheckGraphAdapter.getInstance(hostGraph).getNacs().iterator();
}
if (nacms.mappedNACIter.hasNext()) {
NegativeApplicationCondition mappedToNAC = nacms.mappedNACIter.next();
// match successful: next nacmatchingstate, add to
// currentMatching
// match unsuccessful: leave on stack, try next turn other nac
if (nacSizePredicate(nacms.currentNAC, mappedToNAC)
&& nacInterfacePredicate(nacms.currentNAC, mappedToNAC)) {
IsomorphicPartPartialMatcher subIPM = new IsomorphicPartPartialMatcher();
// translate the current NAC (subgraph) to a positive
// representation and save the mapping
Match newHostNacMatch = SubgraphIterator.fullNacToGraph(nacms.currentNAC, false);
// actually, the current NAC representation might be the
// same if we just check another mappedNAC for the same
// currentNAC.
// so we could save the representation instead of
// calculating it again to save some time.
Graph newHost = null;
for (Edge e : newHostNacMatch.getEdgeMatching().keySet()) {
if (newHostNacMatch.getEdgeMatching().get(e) != null) {
// this is the new (positive) graph representing the
// NAC and its positive interface nodes
newHost = (Graph) newHostNacMatch.getEdgeMatching().get(e).eContainer();
break;
}
}
if (newHost == null) {
for (Node n : newHostNacMatch.getNodeMatching().keySet()) {
if (newHostNacMatch.getNodeMatching().get(n) != null) {
// this is the new (positive) graph representing
// the NAC and its positive interface nodes
newHost = (Graph) newHostNacMatch.getNodeMatching().get(n).eContainer();
break;
}
}
}
// translate the "mapped" NAC (host graph) to a positive
// representation and save the mapping
Match newPatternNacMatch = SubgraphIterator.fullNacToGraph(mappedToNAC, true);
Graph newPattern = null;
for (Edge e : newPatternNacMatch.getEdgeMatching().keySet()) {
if (newPatternNacMatch.getEdgeMatching().get(e) != null) {
// this is the (positive) graph representing the NAC
// and its positive interface nodes
newPattern = (Graph) newPatternNacMatch.getEdgeMatching().get(e).eContainer();
break;
}
}
if (newPattern == null) {
for (Node n : newPatternNacMatch.getNodeMatching().keySet()) {
if (newPatternNacMatch.getNodeMatching().get(n) != null) {
// this is the (positive) graph representing the
// NAC and its positive interface nodes
newPattern = (Graph) newPatternNacMatch.getNodeMatching().get(n).eContainer();
break;
}
}
}
Set<AnnotatedElem> subgraph = SubgraphIterator.graphToSubGraph(newPattern);
// the current NAC will be the current subgraph of the new
// IPM instance
subIPM.setCurrentSubGraph(subgraph);
subIPM.setPattern(newPattern);
// the "mapped" NAC will be the host graph of the new IPM
// instance
subIPM.setHostGraph(newHost);
List<Node> interfaceBlacklist = new LinkedList<Node>();
// build matching of the interface nodes
Match interfaceMatching = SamtraceFactory.eINSTANCE.createMatch();
// for (Map.Entry<Node, Node> entry :
// currentMatching.getNodeMatching()) {
// check whether this works
for (Node n : hostGraph.getNodes()) {
Node nodeInHostNac = null;
Node nodeInPatternNac;
if (ms.translation != null) {
// Node nodeInTranslatedHost =
// ms.translation.getNodeMatching().get(currentMatching.getNodeMatching().get(entry.getKey()));
// // for translation
Node nodeInTranslatedHost = ms.translation.getNodeMatching().get(n); // for
// translation
// nodeInPatternNac =
// newPatternNacMatch.getNodeMatching().get(currentMatching.getNodeMatching().get(entry.getKey()));
// // for translation
nodeInPatternNac = newPatternNacMatch.getNodeMatching().get(n); // for
// translation
nodeInHostNac = newHostNacMatch.getNodeMatching().get(nodeInTranslatedHost); // for
// translation
} else {
for (Map.Entry<Node, Node> entry : currentMatching.getNodeMatching()) {
if (entry.getValue() == n) {
nodeInHostNac = newHostNacMatch.getNodeMatching().get(entry.getKey()); // only
// for
// non-translation
break;
}
}
// nodeInHostNac =
// newHostNacMatch.getNodeMatching().get(entry.getKey());
// // only for non-translation
// nodeInHostNac =
// newHostNacMatch.getNodeMatching().get(n); // only
// for non-translation
// nodeInPatternNac =
// newPatternNacMatch.getNodeMatching().get(currentMatching.getNodeMatching().get(entry.getKey()));
// // for non-translation
nodeInPatternNac = newPatternNacMatch.getNodeMatching().get(n); // for
// non-translation
}
// Node nodeInHostNac =
// newHostNacMatch.getNodeMatching().get(entry.getKey());
// // only for non-translation
// Node nodeInPatternNac =
// newPatternNacMatch.getNodeMatching().get(currentMatching.getNodeMatching().get(entry.getKey()));
// // for non-translation
if (nodeInPatternNac != null && nodeInHostNac != null) {
interfaceMatching.getNodeMatching().put(nodeInPatternNac, nodeInHostNac);
} else if (nodeInPatternNac != null && nodeInHostNac == null) {
// interfaces do not comply with positive matching,
// try another nac
return false;
} else if (nodeInPatternNac == null && nodeInHostNac != null) {
// this is the case where we can map a negative node
// from a partial NAC to a positive node
// from a total NAC. Consequently, we have an empty
// blacklist.
// interfaceBlacklist.add(nodeInHostNac);
}
}
// initialize initial matching (positive interface nodes of
// the NACs)
// subIPM.handleNACMatchCall(ms.translation,
// newPatternNacMatch, newHostNacMatch);
subIPM.handleNACMatchCall(interfaceMatching, interfaceBlacklist);
Match nacMatching = subIPM.getNextMatching();
if (nacMatching != null) {
// System.out.println("matched Nac");
// found matching. If we just matched our "main" nac,
// save the result.
// not sure this works for partial NACs. Probably does
// not matter.
if (nacms.currentNAC.getNodes().size() == ms.currentNAC.getNodes().size()) {
if (nacms.currentNAC.getEdges().size() == ms.currentNAC.getEdges().size()) {
ms.mappedToNAC = mappedToNAC;
}
}
// found matching, build next state on the stack
if (ms.currentNACIter.hasNext()) {
NegativeApplicationCondition next = ms.currentNACIter.next();
NacMatchingState newNMS = new NacMatchingState();
newNMS.currentNAC = next;
ms.stack.push(newNMS);
}
// System.out.println("matched single nac");
ms.nacMatching.getNacMatching().put(nacms.currentNAC, mappedToNAC);
// todo: add matchings for nodes and edges.
// Strictly speaking, this is unnecessary. For the
// moment, at least.
return false;
} else {
// no match: leave state on stack, try another nac next
// turn
return false;
}
} else {
// predicates do not match; leave state on stack, try
// another nac next turn
return false;
}
} else {
return true;
}
}
}
/**
* This comment is old, as the old checkNAC method does not exist any more.
*
* This method handles a CHECK_NAC matching state. Such a state is
* initialized when {@link #findMatchingEdge(MatchingState)} matches two
* negative <a href="../../../../../doc/glossary.html">NAC interface
* edges</a> to each other. The NACs containing the edges will then be
* matched by a separate IPM instance executing a NAC_MATCH_CALL. It is
* important to note that such a state will only be created from within the
* {@link #findMatchingEdge(MatchingState)} method after handling a
* CHECK_EDGE state. Furthermore, CHECK_NAC states will only be created in
* MATCH_CALLs, but not in NAC_TRANSLATION_CALLs, CHECK_NAC_CALLs or
* NAC_MATCH_CALLs as these calls will work with positive elements only
* (some of which will represent negative items). <br>
* <br>
* The nature of the state might be:
* <dl>
* <dt>Failure</dt>
* <dd>Remove the state from the stack. Remove both NACs from the matching.
* Remove all items in the NACs from the matching. Set the previous state
* (will be a CHECK_EDGE state with two NAC interface edges) to FAILURE.
* </dd>
* <dt>Backtrack</dt>
* <dd>Remove the state from the stack. Set the previous state to BACKTRACK,
* continuing the backtracking process.</dd>
* <dt>None</dt>
* <dd>Try to match the NACs by executing a separate IPM instance with a
* NAC_MATCH_CALL. To prepare this call, the following steps will be
* executed:</dd>
* </dl>
* <ol>
* <li>Check the
* {@link #nacSizePredicate(NegativeApplicationCondition, NegativeApplicationCondition)}
* , the
* {@link #nacInterfacePredicate(NegativeApplicationCondition, NegativeApplicationCondition)}
* and the {@link #mappedNacInterface(NegativeApplicationCondition)}
* predicate. If any of these conditions do not hold, remove the state from
* the stack and set the previous state to FAILURE.</li>
* <li>Create a new IPM instance. Create a
* <a href="../../../../../doc/glossary.html">positive representation</a>
* for the current NAC (from the current subgraph) and set it as the new
* IPM's current subgraph and pattern.</li>
* <li>Create a positive representation for the mapped NAC (from the host
* graph) and set it as the new IPM's host graph.</li>
* <li>Call
* {@link #handleNACMatchCall(NegativeApplicationCondition, Match, Match, Match)}
* , passing the current NAC, the match mapping it to its positive
* representation, the match mapping the mapped NAC to its positive
* representation and the current matching. This method will translate the
* current matching to the new subgraph and host graph so that the
* NAC_MATCH_CALL will have the positive NAC interface as starting points
* which will already be matched.</li>
* <li>Execute the new IPM instance by calling {@link #getNextMatching()}.
* </li>
* <li>If there is a result, call
* {@link #completeNACMatching(MatchingState, NegativeApplicationCondition, NegativeApplicationCondition)}
* . This method will issue a CHECK_NAC_CALL in another IPM instance, which
* will ensure that the NAC does not illegally exist as a positive part of
* the unmatched host graph. It it does exist or if the result of the
* NAC_MATCH_CALL is null, the NACs can not be matched. In that case remove
* the state from the stack and set the previous state (will be the
* CHECK_EDGE state containing two NAC interface edges) to FAILURE.</li>
* <li>Add the two NACs to the current matching.</li>
* <li>The matching returned by the NAC_MATCH_CALL matches only the positive
* representations of the NACs to each other. Therefore we need to translate
* the matching to our original NACs in the current subgraph and host graph.
* </li>
* <li>Add the matching of the graph items in the NACs to the current
* matching. If a CHECK_NAC state with FAILURE is handled, all the items
* will be removed from the current matching. So basically, the matching of
* the graph items is tied to the CHECK_NAC state.
* <li>Create a new CHECK_EDGE state and put it on the stack. The nodes will
* be the same as the nodes of the CHECK_EDGE stack that originally lead to
* the matching of two negative edges and to this CHECK_NAC call. These
* nodes will usually be part of the
* <a href="../../../../../doc/glossary.html">positive NAC interface</a> As
* the NAC items were matched by the NAC_MATCH_CALL, the matching process
* will continue with the positive part of the current subgraph.</li>
* </ol>
*
*/
/**
* This predicate checks whether the interfaces of two NACs contain the same
* types, which is a necessary requirement for the NACs to be matched.
*
* @param currentNAC
* the current NAC
* @param mappedToNAC
* the "mapped" NAC
* @return true if the the interfaces contain the same number of items for
* every type, false otherwise
*/
private boolean nacInterfacePredicate(NegativeApplicationCondition currentNAC,
NegativeApplicationCondition mappedToNAC) {
if (true) {
return true;
}
HashMap<EdgeType, Integer> typeMapC = new HashMap<EdgeType, Integer>();
HashMap<EdgeType, Integer> typeMapM = new HashMap<EdgeType, Integer>();
for (Edge e1 : currentNAC.getEdges()) {
if (e1.partOfNacInterface()) {
if (typeMapC.containsKey(e1.getInstanceOf())) {
typeMapC.put(e1.getInstanceOf(), typeMapC.get(e1.getInstanceOf()) + 1);
} else {
typeMapC.put(e1.getInstanceOf(), 1);
}
}
}
for (Edge e1 : mappedToNAC.getEdges()) {
if (e1.partOfNacInterface()) {
if (typeMapM.containsKey(e1.getInstanceOf())) {
typeMapM.put(e1.getInstanceOf(), typeMapM.get(e1.getInstanceOf()) + 1);
} else {
typeMapM.put(e1.getInstanceOf(), 1);
}
}
}
for (EdgeType s : typeMapM.keySet()) {
if (!typeMapC.containsKey(s)) {
return false;
} else if (typeMapM.get(s) > typeMapC.get(s)) {
return false;
}
}
return true;
}
/**
* This predicate compares the sizes of the NAC, ensuring a necessary
* requirement for the NACs to be matched.
*
* @param currentNAC
* the current NAC
* @param mappedToNAC
* the "mapped" NAC
* @return false if the current NAC is smaller than the "mapped" NAC, true
* otherwise
*/
private boolean nacSizePredicate(NegativeApplicationCondition currentNAC,
NegativeApplicationCondition mappedToNAC) {
if (currentNAC.getNodes().size() < mappedToNAC.getNodes().size()) {
// return false;
}
return true;
}
/**
* Compare the types of two edges.
*
* @param e1
* one edge
* @param e2
* another edge
* @return true if the edges have the same type, false otherwise
*/
private boolean typePredicate(final Edge e1, final Edge e2) {
if (e1 != null && e2 != null) {
return (e1.getInstanceOf() == null || e2.getInstanceOf() == null
|| e1.getInstanceOf().equals(e2.getInstanceOf()));
} else if (e1 == e2) {
return true;
}
return false;
}
/**
* Compare the types of two nodes.
*
* @param n1
* one node
* @param n2
* another node
* @return true if the nodes have the same type, false otherwise
*/
private boolean typePredicate(final Node n1, final Node n2) {
if (n1 != null && n2 != null) {
return n1.getInstanceOf() == null || n2.getInstanceOf() == null
|| n1.getInstanceOf().equals(n2.getInstanceOf());
} else if (n1 == n2) {
return true;
}
return false;
}
/**
* Compare the direction of two edges.
*
* @param e1
* one edge
* @param e2
* another edge
* @param n1
* a node adjacent to e1 (source or target)
* @param n2
* a node adjacent to e2 (source or target)
* @return true if the edges have the same direction, false otherwise
*/
private boolean directionPredicate(final Edge e1, final Edge e2, final Node n1, final Node n2) {
if (e1 != null && e2 != null) {
return e1.getInstanceOf().getDirection() == e2.getInstanceOf().getDirection()
&& (e1.getInstanceOf().getDirection() == EdgeDirection.UNDIRECTED
|| ((e1.getSource() == n1 && e2.getSource() == n2)
|| (e1.getTarget() == n1 && e2.getTarget() == n2)));
} else if (e1 == e2) {
return true;
}
return false;
}
/**
* Check whether the edges are both negative or both positive.
*
* @param e1
* one edge
* @param e2
* another edge
* @return true if both edges are negative or positive, false otherwise
*/
private boolean negatedPredicate(final Edge e1, final Edge e2) {
if (e1 != null && e2 != null) {
return (InvariantCheckerUtil.isNegated(e1) == InvariantCheckerUtil.isNegated(e2));
} else if (e1 == e2) {
return true;
}
return false;
}
/**
* Check whether the nodes are both negative or both positive.
*
* @param n1
* a node
* @param n2
* another node
* @return true if both nodes are negative or positive, false otherwise
*/
private boolean negatedPredicate(final Node n1, final Node n2) {
if (mode == MatchMode.NAC_TRANSLATION_CALL) {
return true;
}
if (n1 != null && n2 != null) {
return InvariantCheckerUtil.isNegated(n1) == InvariantCheckerUtil.isNegated(n2);
} else if (n1 == n2) {
return true;
}
return false;
}
/**
* This method builds all incomplete matchings of the given graphs. It is
* used by the NACTranslator component. It is executed in the context of a
* NAC_TRANSLATION_CALL. If the subgraph can be found in the host graph,
* null is returned.
*
* @return a list of matchings or null, if a complete matching can be found
*/
public LinkedList<Match> getIterativeMatchings() {
if (this.getNextMatching() != null) {
return null;
} else {
LinkedList<Match> result = new LinkedList<Match>();
for (Match gm : iterativeMatchings) {
result.add(gm.copy());
}
return result;
}
}
private boolean nacInterfaceNodePredicate(Node expected, Node found) {
if (mode == MatchMode.NAC_MATCH_CALL) {
if (this.nacInterfaceMatch.getNodeMatching().containsKey(expected)) {
return this.nacInterfaceMatch.getNodeMatching().get(expected) == found;
} else {
// the first part of the condition is probably obsolete
return !this.nacInterfaceMatch.getNodeMatching().containsValue(found) && !nodeBlacklist.contains(found);
}
} else if (mode == MatchMode.NAC_TRANSLATION_CALL) {
if (this.nacInterfaceMatch.getNodeMatching().containsKey(expected)) {
return this.nacInterfaceMatch.getNodeMatching().get(expected) == found;
} else {
return !this.nodeBlacklist.contains(found);
}
} else {
return true;
}
}
/**
* This predicate returns true if the degree of the first node is greater or
* equal the second node's degree.<br />
* A node's degree is specified as the sum of the numbers of its in- and
* outgoing edges.
*
* @param n1
* the first node
* @param n2
* the second node
* @return true if the first node's degree is greater or equal the second
* node's degree.
*/
private boolean degreePredicate(final Node n1, final Node n2) {
if (n1 != null && n2 != null) {
// addition to negative nodes
if (mode == MatchMode.NAC_TRANSLATION_CALL || mode == MatchMode.NAC_MATCH_CALL) {
return true;
}
if (InvariantCheckerUtil.isNegated(n1) && InvariantCheckerUtil.isNegated(n2)) {
return true;
}
if (this.nodeDegrees == null || !this.nodeDegrees.containsKey(n2)) {
return false;
}
final int degree = this.nodeDegrees.get(n2);
return (n1.getIncoming().size() + n1.getOutgoing().size()) >= degree;
}
return false;
}
/**
* This method returns a <code>Vector</code> containing all matchings
* between the currently set
* {@link IsomorphicPartPartialMatcher#setCurrentSubGraph(Set) subgraph} and
* the set property attribute of this object. Note, that you have to invoke
* this method for each subgraph of a rule Graph to compute all partial
* matchings between rule and property.
*
* @return a <code>{@link Vector Vector}</code> containing all matchings
* between the property and the rule restricted to the current
* subgraph of this object.
* @see IsomorphicPartPartialMatcher#setCurrentSubGraph(Set)
* @see IsomorphicPartPartialMatcher#setHostGraph(Graph)
* @see IsomorphicPartPartialMatcher#setPattern(Graph)
* @see org.eclipse.emf.henshin.sam.invcheck.SubgraphIterator
*/
public Vector<Match> findAllMatchings() {
Vector<Match> results = new Vector<Match>();
if (this.currentSubGraph != null && this.pattern != null && this.hostGraph != null) {
this.reset();
Match gm = this.getNextMatching();
while (gm != null) {
results.add(gm);
gm = this.getNextMatching();
}
}
return results;
}
/**
* Get the currentSubGraph attribute of the IsomorphicPartMatcher object
*
* @return The currentSubGraph value
*/
public Set<AnnotatedElem> getCurrentSubGraph() {
return currentSubGraph;
}
/**
* Sets the currentSubGraph attribute of the IsomorphicPartMatcher object.
* <br />
* Changing an <code>IsomorphicPartMatcher</code>'s subgraph attribute,
* invalidates the <code>IsomorphicPartMatcher</code>'s internal state. You
* have to invoke {@link #reset()} prior to invoking
* {@link #getNextMatching()} to receive valid matches.
*
* @param subGraph
* The new currentSubGraph value
*/
public void setCurrentSubGraph(Set<AnnotatedElem> subGraph) {
if (this.currentSubGraph != subGraph) {
this.currentSubGraph = subGraph;
}
}
/**
* Builds an iterator for all nodes coming from or going to the given node.
*
* @param n
* a node
* @return an iterator of edges
*/
private Iterator<Edge> iteratorOfEdges(Node n) {
EList<Edge> all = new BasicEList<Edge>();
for (Edge e : n.getIncoming()) {
all.add(e);
}
for (Edge e : n.getOutgoing()) {
if ((e.getSource() == e.getTarget()) == false)
all.add(e);
}
return all.iterator();
}
/**
* In a first step this method computes a <code>Map</code> that contains the
* frequency of types in the <code>Set</code> s. Afterwards each entry in
* this <code>Map</code> gets compared to the corresponding value of the
* given <code>Graph</code>. Contains the <code>Set</code> more elements of
* a type then the <code>Graph</code> the method returns false. In this case
* there is no need to compute all matchings, as none exists.
*
* @param s
* @param g
* @return
*/
private boolean compareTypes(Set<AnnotatedElem> s, Graph g) {
// compute types in host graph
if (s != null && g != null) {
Map<NodeType, Integer> m = SubgraphIterator.computeTypesInSubgraph(s);
for (Iterator<Map.Entry<NodeType, Integer>> iter = m.entrySet().iterator(); iter.hasNext();) {
Map.Entry<NodeType, Integer> entry = iter.next();
Integer i = entry.getValue();
// if (i.intValue() > g.getNumberOfType(entry.getKey())) {
if (i.intValue() > 0 && !this.typeCount.containsKey(entry.getKey())) {
return false;
} else if (i.intValue() > this.typeCount.get(entry.getKey())) {
return false;
}
}
return true;
}
return false;
}
public enum MatchMode {
MATCH_CALL, NAC_MATCH_CALL, NAC_TRANSLATION_CALL;
}
public static class NacMatchingState {
public NegativeApplicationCondition currentNAC;
public Iterator<NegativeApplicationCondition> mappedNACIter;
BackTrackReason backtrack;
public NacMatchingState() {
}
@Override
public String toString() {
StringBuffer sb = new StringBuffer(20);
sb.append("Single NAC matching");
sb.append(": ");
sb.append(this.currentNAC.getNodes().size());
return sb.toString();
}
}
public static class MatchingState {
public Node currentNode;
public Node mappedToNode;
public Iterator<Edge> edgeIterator;
public Iterator<Edge> mappedEdgeIterator;
public Edge currentEdge;
public NegativeApplicationCondition currentNAC;
public NegativeApplicationCondition mappedToNAC;
public RuleGraph translatedGraph; // only for nac matching
public Iterator<NegativeApplicationCondition> currentNACIter; // only
// for
// nac
// matching
public MatchWithNacs nacMatching; // only for nac matching
public Stack<NacMatchingState> stack; // only for nac matching
public Match translation; // only for nac matching
BackTrackReason backtrack;
final MatchingOperation matchOp;
public MatchingState(MatchingOperation mo) {
this.matchOp = mo;
}
enum MatchingOperation {
CHECK_EDGE, CHECK_NODE, CHECK_NAC;
}
enum BackTrackReason {
NONE, FAILURE, BACKTRACK;
}
@Override
public String toString() {
StringBuffer sb = new StringBuffer(20);
sb.append(this.matchOp);
sb.append(" ").append(this.currentNode != null ? this.currentNode.getName() : "null");
sb.append("->").append(this.mappedToNode != null ? this.mappedToNode.getName() : "null");
if (matchOp == MatchingOperation.CHECK_EDGE) {
sb.append(this.currentEdge != null ? this.currentEdge : "null");
}
if (matchOp == MatchingOperation.CHECK_NAC) {
/*
* for (Edge e : this.currentNAC.getEdges()) { sb.append(e);
* sb.append(","); } sb.append("--"); for (Edge e :
* this.mappedToNAC.getEdges()) { sb.append(e); sb.append(",");
* }
*/
}
return sb.toString();
}
}
public class TestAdapter {
public class TestAdapterMatchingStateFactory extends MatchingStateFactory {
TestAdapterMatchingStateFactory(final int a, final int b) {
super(a, b);
}
};
public void setStartNode(Node n) {
connectedComponents.get(0).startNode = n;
}
public void pushCheckNodeMatchingState(Node current, Node mappedTo) {
if (stack != null) {
MatchingState ms = new MatchingState(MatchingOperation.CHECK_NODE);
ms.currentNode = current;
ms.mappedToNode = mappedTo;
stack.push(ms);
}
}
public void setHostNodeIter(Iterator<Node> iter) {
connectedComponents.get(0).hostNodeIter = iter;
}
public boolean testTypePredicate(Edge e1, Edge e2) {
return typePredicate(e1, e2);
}
public boolean testDirectionPredicate(Edge e1, Edge e2, Node n1, Node n2) {
return directionPredicate(e1, e2, n1, n2);
}
public boolean testNegatedPredicate(Edge e1, Edge e2) {
return negatedPredicate(e1, e2);
}
public TestAdapterMatchingStateFactory getFactory() {
return new TestAdapterMatchingStateFactory(5, 5);
}
}
}
/*
* $Log$ Revision 1.4 2007/06/25 17:48:22 basilb fixed a lot of these bugs
* testing does not find but evaluating ;)
*
* Revision 1.3 2007/01/11 14:44:23 basilb even more usage of generics
*
* Revision 1.2 2007/01/03 09:27:46 basilb removed compile errors caused by
* wrong import declarations; introduced empty plugin class to ensure correct
* loading
*
*/