blob: 014f7dfd9958857f2f966cddc43ebe06b275d3b5 [file] [log] [blame]
package org.eclipse.emf.henshin.sam.invcheck;
import java.util.BitSet;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
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.samtrace.Match;
import org.eclipse.emf.henshin.sam.model.samtrace.SamtraceFactory;
import org.eclipse.emf.henshin.sam.model.samtypegraph.NodeType;
public class MatchIterator implements Iterator<Match> {
//not threadsafe
private Node[][] hostNodes;
private Node[][] targetNodes;
private BitSet[] hostNodesBS;
BitSet[] targetNodesBS;
private int[][] combinations;
private int[] maximalCardinality;
private boolean cachedHasNext;
private boolean cacheValid = false;
private boolean innerCachedHasNext;
private boolean innerCacheValid = false;
private boolean innerValid = false;
private Map<Node, Node> preMatch;
private Match currentResult = null;
public boolean hasNext() {
if (this.cacheValid) {
return this.cachedHasNext;
}
while(!innerHasNext()) {
this.innerValid = false;
this.innerCacheValid = false;
if (!incrementBitSets(hostNodesBS)) {
this.cacheValid = true;
this.cachedHasNext = false;
this.currentResult = null;
return false;
}
}
innerNext();
buildCurrentResult();
this.cachedHasNext = true;
this.cacheValid = true;
return true;
}
private boolean innerHasNext() {
if (innerCacheValid) {
return this.innerCachedHasNext;
}
if (!innerValid) {
return initializeInner();
} else {
if (incrementCrossProducts(combinations)) {
this.innerCachedHasNext = true;
this.innerCacheValid = true;
//buildInnerResult();
return true;
}
}
this.innerCacheValid = true;
return false;
}
private void buildCurrentResult() {
Match match = SamtraceFactory.eINSTANCE.createMatch();
for (int i = 0; i <= hostNodesBS.length - 1; i++) {
int k = 0;
for (int j = hostNodesBS[i].nextSetBit(0); j != -1; j = hostNodesBS[i].nextSetBit(j+1)) {
Node key = hostNodes[i][j];
int originalValue = this.combinations[i][k];
int valueIndex = this.combinations[i][k];
//Node value = targetNodes[i][valueIndex];
// iterate over array until current index, increment for each value equal or lower
// note: this step could be optimized by saving for each integer the number of indexes
// below that value in a map. Then, instead of iterating over the left side of the array,
// the number by which the index must be incremented can be fetched from the map.
for (int n = 0; n < k; n++) {
if (originalValue >= this.combinations[i][n]) {
valueIndex++;
}
}
Node value = targetNodes[i][valueIndex];
while (match.getNodeMatching().containsValue(value)) {
valueIndex = valueIndex + 1;
value = targetNodes[i][valueIndex];
}
match.getNodeMatching().put(key, value);
k = k + 1;
}
}
match.getNodeMatching().putAll(this.preMatch);
this.currentResult = match;
}
private void innerNext() {
if (!this.innerHasNext()) {
throw new NoSuchElementException("end of iterator has been reached. no more matches available");
}
this.innerCacheValid = false;
}
private boolean incrementCrossProducts(int[][] c) {
for (int i = 0; i <= c.length - 1; i++) {
if (!hasMaxCardinality(c[i], i)) {
incrementCrossProduct(c[i], i);
return true;
} else {
incrementCrossProduct(c[i], i);
}
}
return false;
}
private boolean hasMaxCardinality(int[] a, int index) {
for (int i = 0; i <= a.length - 1; i++) {
if (a[i] < this.maximalCardinality[index] - i) {
return false;
}
}
return true;
}
private void incrementCrossProduct(int[] p, int index) {
for (int i = 0; i <= p.length - 1; i++) {
if (p[i] == this.maximalCardinality[index] - i) {
p[i] = 0;
} else {
p[i] = p[i] + 1;
return;
}
}
}
/**
* initialize inner iterator such that first result is valid
*
* @return whether match is possible
*/
private boolean initializeInner() {
combinations = new int[hostNodesBS.length][];
for (int i = 0; i <= hostNodesBS.length - 1; i++) {
int currentCardinality = hostNodesBS[i].cardinality(); // how many bits are set of this type?
if (currentCardinality > this.maximalCardinality[i] + 1) {
return false;
}
int[] crossProduct = new int[currentCardinality];
combinations[i] = crossProduct;
}
this.innerCacheValid = true;
this.innerCachedHasNext = true;
this.innerValid = true;
return true;
}
public Match next() {
if (!this.hasNext()) {
throw new NoSuchElementException("end of iterator has been reached. no more matches available");
}
this.cacheValid = false;
return currentResult;
}
public MatchIterator(Graph host, Graph pattern) {
this(host, pattern, null, null);
}
public MatchIterator(Graph host, Graph pattern, Map<Node, Node> preMatch, Collection<Node> blacklist) {
if (preMatch == null) {
this.preMatch = new HashMap<Node, Node>();
} else {
this.preMatch = preMatch;
}
if (blacklist == null) {
blacklist = new LinkedList<Node>();
}
// restrict to node types present in both graphs
//Map<NodeType, Integer> typeCount = new HashMap<NodeType, Integer>();
Map<NodeType, List<Node>> typedNodesHost = new HashMap<NodeType, List<Node>>();
Map<NodeType, List<Node>> typedNodesPattern = new HashMap<NodeType, List<Node>>();
for (Node node : host.getNodes()) {
if (!this.preMatch.containsKey(node)) {
if (typedNodesHost.containsKey(node.getInstanceOf())) {
typedNodesHost.get(node.getInstanceOf()).add(node);
} else {
typedNodesHost.put(node.getInstanceOf(), new LinkedList<Node>());
typedNodesHost.get(node.getInstanceOf()).add(node);
}
}
}
for (Node node : pattern.getNodes()) {
// check whether this type is relevant for match
if (!blacklist.contains(node) && !this.preMatch.containsValue(node) && typedNodesHost.containsKey(node.getInstanceOf())) {
if (typedNodesPattern.containsKey(node.getInstanceOf())) {
typedNodesPattern.get(node.getInstanceOf()).add(node);
} else {
typedNodesPattern.put(node.getInstanceOf(), new LinkedList<Node>());
typedNodesPattern.get(node.getInstanceOf()).add(node);
}
}
}
List<NodeType> toBeRemoved = new LinkedList<NodeType>();
for (NodeType nt : typedNodesHost.keySet()) {
if (!typedNodesPattern.containsKey(nt)) {
toBeRemoved.add(nt);
}
}
for (NodeType nt : toBeRemoved) {
typedNodesHost.remove(nt);
}
if (typedNodesHost.size() == 0 || typedNodesPattern.size() == 0) {
this.cachedHasNext = false;
this.cacheValid = true;
return;
}
this.hostNodes = new Node[typedNodesHost.keySet().size()][];
this.hostNodesBS = new BitSet[typedNodesHost.keySet().size()];
Map<NodeType, Integer> typeMap = new HashMap<NodeType, Integer>();
int typeIndex = 0;
for (NodeType nodeType : typedNodesHost.keySet()) {
typeMap.put(nodeType, typeIndex);
this.hostNodes[typeIndex] = new Node[typedNodesHost.get(nodeType).size()];
int nodeIndex = 0;
for (Node n : typedNodesHost.get(nodeType)) {
this.hostNodes[typeIndex][nodeIndex] = n;
nodeIndex++;
}
this.hostNodesBS[typeIndex] = new BitSet(typedNodesHost.get(nodeType).size());
typeIndex++;
}
this.maximalCardinality = new int[typedNodesPattern.size()];
this.targetNodes = new Node[typedNodesPattern.keySet().size()][];
this.targetNodesBS = new BitSet[typedNodesPattern.keySet().size()];
Map<NodeType, Integer> typeMapPattern = new HashMap<NodeType, Integer>();
int typeIndexP = 0;
for (NodeType nodeType : typedNodesPattern.keySet()) {
typeMapPattern.put(nodeType, typeIndexP);
this.targetNodes[typeIndexP] = new Node[typedNodesPattern.get(nodeType).size()];
int nodeIndex = 0;
for (Node n : typedNodesPattern.get(nodeType)) {
this.targetNodes[typeIndexP][nodeIndex] = n;
nodeIndex++;
}
this.targetNodesBS[typeIndexP] = new BitSet(typedNodesPattern.get(nodeType).size());
this.maximalCardinality[typeIndexP] = typedNodesPattern.get(nodeType).size() - 1;
typeIndexP++;
}
incrementBitSets(hostNodesBS);
// repeat for edges and edge types?
}
private void incrementBitSet(final BitSet bs) {
final int firstClear = bs.nextClearBit(0);
bs.set(firstClear);
for (int i = firstClear - 1; i >= 0; i--) {
bs.clear(i);
}
}
private boolean incrementBitSets(final BitSet[] bss) {
for (int i = 0; i <= bss.length - 1; i++) {
if (bss[i].cardinality() == hostNodes[i].length) {
bss[i].clear();
} else {
incrementBitSet(bss[i]);
return true;
}
}
return false;
}
}
/*
* $Log$ Revision 1.2 2007/01/03 09:27:47 basilb removed compile errors caused
* by wrong import declarations; introduced empty plugin class to ensure correct
* loading
*
*/