/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.cpd;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
public class MatchAlgorithm {
private final static int MOD = 37;
private int lastHash;
private int lastMod = 1;
private List matches;
private Map source;
private Tokens tokens;
private List code;
private CPDListener cpdListener;
private int min;
public MatchAlgorithm(Map sourceCode, Tokens tokens, int min) {
this(sourceCode, tokens, min, new CPDNullListener());
}
public MatchAlgorithm(Map sourceCode, Tokens tokens, int min, CPDListener listener) {
this.source = sourceCode;
this.tokens = tokens;
this.code = tokens.getTokens();
this.min = min;
this.cpdListener = listener;
for (int i = 0; i < min; i++) {
lastMod *= MOD;
}
}
public void setListener(CPDListener listener) {
this.cpdListener = listener;
}
public void findMatches() {
cpdListener.phaseUpdate(CPDListener.HASH);
Map markGroups = hash();
cpdListener.phaseUpdate(CPDListener.MATCH);
MatchCollector coll = new MatchCollector(this);
for (Iterator i = markGroups.values().iterator(); i.hasNext();) {
Object o = i.next();
if (o instanceof List) {
Collections.reverse((List) o);
coll.collect(min, (List) o);
}
i.remove();
}
cpdListener.phaseUpdate(CPDListener.GROUPING);
matches = coll.getMatches();
coll = null;
for (Iterator i = matches(); i.hasNext();) {
Match match = (Match) i.next();
for (Iterator occurrences = match.iterator(); occurrences.hasNext();) {
TokenEntry mark = (TokenEntry) occurrences.next();
match.setLineCount(tokens.getLineCount(mark, match));
if (!occurrences.hasNext()) {
int start = mark.getBeginLine();
int end = start + match.getLineCount() - 1;
SourceCode sourceCode = (SourceCode) source.get(mark.getTokenSrcID());
match.setSourceCodeSlice(sourceCode.getSlice(start, end));
}
}
}
cpdListener.phaseUpdate(CPDListener.DONE);
}
private Map hash() {
Map markGroups = new HashMap(tokens.size());
for (int i = code.size() - 1; i >= 0; i--) {
TokenEntry token = (TokenEntry) code.get(i);
if (token != TokenEntry.EOF) {
int last = tokenAt(min, token).getIdentifier();
lastHash = MOD * lastHash + token.getIdentifier() - lastMod * last;
token.setHashCode(lastHash);
Object o = markGroups.get(token);
if (o == null) {
markGroups.put(token, token);
} else if (o instanceof TokenEntry) {
List l = new ArrayList();
l.add(o);
l.add(token);
markGroups.put(token, l);
} else {
List l = (List) o;
l.add(token);
}
} else {
lastHash = 0;
for (int end = Math.max(0, i - min + 1); i > end; i--) {
token = (TokenEntry) code.get(i - 1);
lastHash = MOD * lastHash + token.getIdentifier();
if (token == TokenEntry.EOF) {
break;
}
}
}
}
return markGroups;
}
public Iterator matches() {
return matches.iterator();
}
public TokenEntry tokenAt(int offset, TokenEntry m) {
return (TokenEntry) code.get(offset + m.getIndex());
}
}