blob: 03685d181afca204ee197edc02a3908665eb4ef0 [file] [log] [blame]
package com.intellij.dupLocator.treeHash;
import com.intellij.dupLocator.*;
import com.intellij.dupLocator.util.DuplocatorUtil;
import com.intellij.dupLocator.util.PsiFragment;
import com.intellij.openapi.components.PathMacroManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.usageView.UsageInfo;
import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;
import gnu.trove.TIntObjectHashMap;
import gnu.trove.TIntObjectProcedure;
import gnu.trove.TObjectIntHashMap;
import gnu.trove.TObjectIntIterator;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.*;
/**
* Created by IntelliJ IDEA.
* User: db
* Date: Mar 26, 2004
* Time: 7:11:30 PM
* To change this template use File | Settings | File Templates.
*/
public class DuplocatorHashCallback implements FragmentsCollector {
private static final Logger LOG = Logger.getInstance("#com.intellij.dupLocator.treeHash.DuplocatorHashCallback");
private TIntObjectHashMap<List<List<PsiFragment>>> myDuplicates;
private final int myBound;
private boolean myReadOnly = false;
private final int myDiscardCost;
public DuplocatorHashCallback(int bound, int discardCost) {
myDuplicates = new TIntObjectHashMap<List<List<PsiFragment>>>();
myBound = bound;
myDiscardCost = discardCost;
}
@SuppressWarnings({"UnusedDeclaration"})
public DuplocatorHashCallback(final int bound, final int discardCost, final boolean readOnly) {
this(bound, discardCost);
myReadOnly = readOnly;
}
public DuplocatorHashCallback(int lowerBound) {
this(lowerBound, 0);
}
@SuppressWarnings({"UnusedDeclaration"})
public void setReadOnly(final boolean readOnly) {
myReadOnly = readOnly;
}
// used in TeamCity
@SuppressWarnings("UnusedParameters")
public void add(int hash, int cost, PsiFragment frag, NodeSpecificHasher visitor) {
forceAdd(hash, cost, frag);
}
private void forceAdd(int hash, int cost, PsiFragment frag) {
if (frag == null) { //fake fragment
myDuplicates.put(hash, new ArrayList<List<PsiFragment>>());
return;
}
frag.setCost(cost);
List<List<PsiFragment>> fragments = myDuplicates.get(hash);
if (fragments == null) {
if (!myReadOnly) { //do not add new hashcodes
List<List<PsiFragment>> list = new ArrayList<List<PsiFragment>>();
List<PsiFragment> listf = new ArrayList<PsiFragment>();
listf.add(frag);
list.add(listf);
myDuplicates.put(hash, list);
}
return;
}
boolean found = false;
final PsiElement[] elements = frag.getElements();
int discardCost = 0;
if (myDiscardCost >= 0) {
discardCost = myDiscardCost;
}
else {
final DuplocatorState state = DuplocatorUtil.getDuplocatorState(frag);
if (state != null) {
discardCost = state.getDiscardCost();
}
}
for (Iterator<List<PsiFragment>> i = fragments.iterator(); i.hasNext() && !found; ) {
List<PsiFragment> fi = i.next();
PsiFragment aFrag = fi.get(0);
if (aFrag.isEqual(elements, discardCost)) {
boolean skip = false;
for (Iterator<PsiFragment> frags = fi.iterator(); frags.hasNext() && !skip; ) {
skip = frag.intersectsWith(frags.next());
if (skip) {
frags.remove();
}
}
fi.add(frag);
found = true;
}
}
if (!found) {
List<PsiFragment> newFrags = new ArrayList<PsiFragment>();
newFrags.add(frag);
fragments.add(newFrags);
}
}
@Override
public void add(int hash, int cost, PsiFragment frag) {
int bound;
if (myBound >= 0) {
bound = myBound;
}
else {
final DuplocatorState duplocatorState = DuplocatorUtil.getDuplocatorState(frag);
if (duplocatorState == null) {
return;
}
bound = duplocatorState.getLowerBound();
}
if (cost >= bound) {
forceAdd(hash, cost, frag);
}
}
public DupInfo getInfo() {
final TObjectIntHashMap<PsiFragment[]> duplicateList = new TObjectIntHashMap<PsiFragment[]>();
myDuplicates.forEachEntry(new TIntObjectProcedure<List<List<PsiFragment>>>() {
public boolean execute(final int hash, final List<List<PsiFragment>> listList) {
for (List<PsiFragment> list : listList) {
final int len = list.size();
if (len > 1) {
PsiFragment[] filtered = new PsiFragment[len];
int idx = 0;
for (final PsiFragment fragment : list) {
fragment.markDuplicate();
filtered[idx++] = fragment;
}
duplicateList.put(filtered, hash);
}
}
return true;
}
});
myDuplicates = null;
for (TObjectIntIterator<PsiFragment[]> dups = duplicateList.iterator(); dups.hasNext(); ) {
dups.advance();
PsiFragment[] fragments = dups.key();
LOG.assertTrue(fragments.length > 1);
boolean nested = false;
for (PsiFragment fragment : fragments) {
if (fragment.isNested()) {
nested = true;
break;
}
}
if (nested) {
dups.remove();
}
}
final Object[] duplicates = duplicateList.keys();
Arrays.sort(duplicates, new Comparator<Object>() {
public int compare(Object x, Object y) {
return ((PsiFragment[])y)[0].getCost() - ((PsiFragment[])x)[0].getCost();
}
});
return new DupInfo() {
private final TIntObjectHashMap<GroupNodeDescription> myPattern2Description = new TIntObjectHashMap<GroupNodeDescription>();
public int getPatterns() {
return duplicates.length;
}
public int getPatternCost(int number) {
return ((PsiFragment[])duplicates[number])[0].getCost();
}
public int getPatternDensity(int number) {
return ((PsiFragment[])duplicates[number]).length;
}
public PsiFragment[] getFragmentOccurences(int pattern) {
return (PsiFragment[])duplicates[pattern];
}
public UsageInfo[] getUsageOccurences(int pattern) {
PsiFragment[] occs = getFragmentOccurences(pattern);
UsageInfo[] infos = new UsageInfo[occs.length];
for (int i = 0; i < infos.length; i++) {
infos[i] = occs[i].getUsageInfo();
}
return infos;
}
public int getFileCount(final int pattern) {
if (myPattern2Description.containsKey(pattern)) {
return myPattern2Description.get(pattern).getFilesCount();
}
return cacheGroupNodeDescription(pattern).getFilesCount();
}
private GroupNodeDescription cacheGroupNodeDescription(final int pattern) {
final Set<PsiFile> files = new HashSet<PsiFile>();
final PsiFragment[] occurencies = getFragmentOccurences(pattern);
for (PsiFragment occurency : occurencies) {
final PsiFile file = occurency.getFile();
if (file != null) {
files.add(file);
}
}
final int fileCount = files.size();
final PsiFile psiFile = occurencies[0].getFile();
DuplicatesProfile profile = DuplicatesProfileCache.getProfile(this, pattern);
String comment = profile != null ? profile.getComment(this, pattern) : "";
final GroupNodeDescription description = new GroupNodeDescription(fileCount, psiFile != null ? psiFile.getName() : "unknown", comment);
myPattern2Description.put(pattern, description);
return description;
}
@Nullable
public String getTitle(int pattern) {
if (getFileCount(pattern) == 1) {
if (myPattern2Description.containsKey(pattern)) {
return myPattern2Description.get(pattern).getTitle();
}
return cacheGroupNodeDescription(pattern).getTitle();
}
return null;
}
@Nullable
public String getComment(int pattern) {
if (getFileCount(pattern) == 1) {
if (myPattern2Description.containsKey(pattern)) {
return myPattern2Description.get(pattern).getComment();
}
return cacheGroupNodeDescription(pattern).getComment();
}
return null;
}
public int getHash(final int i) {
return duplicateList.get((PsiFragment[])duplicates[i]);
}
};
}
@SuppressWarnings({"HardCodedStringLiteral"})
public void report(String path, final Project project) throws IOException {
int[] hashCodes = myDuplicates.keys();
FileWriter fileWriter = null;
//fragments
try {
fileWriter = new FileWriter(path + File.separator + "fragments.xml");
PrettyPrintWriter writer = new PrettyPrintWriter(fileWriter);
writer.startNode("root");
for (int hash : hashCodes) {
List<List<PsiFragment>> dupList = myDuplicates.get(hash);
writer.startNode("hash");
writer.addAttribute("val", String.valueOf(hash));
for (final List<PsiFragment> psiFragments : dupList) {
writeFragments(psiFragments, writer, project, false);
}
writer.endNode();
}
writer.endNode(); //root node
writer.flush();
}
finally {
if (fileWriter != null) {
fileWriter.close();
}
}
fileWriter = null;
//duplicates
try {
fileWriter = new FileWriter(path + File.separator + "duplicates.xml");
PrettyPrintWriter writer = new PrettyPrintWriter(fileWriter);
writer.startNode("root");
final DupInfo info = getInfo();
final int patterns = info.getPatterns();
for (int i = 0; i < patterns; i++) {
writer.startNode("duplicate");
writer.addAttribute("cost", String.valueOf(info.getPatternCost(i)));
writer.addAttribute("hash", String.valueOf(info.getHash(i)));
writeFragments(Arrays.asList(info.getFragmentOccurences(i)), writer, project, true);
writer.endNode();
}
writer.endNode(); //root node
writer.flush();
}
finally {
if (fileWriter != null) {
fileWriter.close();
}
}
}
@SuppressWarnings({"HardCodedStringLiteral"})
private static void writeFragments(final List<PsiFragment> psiFragments,
final PrettyPrintWriter writer,
Project project,
final boolean shouldWriteOffsets) {
final PathMacroManager macroManager = PathMacroManager.getInstance(project);
final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(project);
for (PsiFragment fragment : psiFragments) {
final PsiFile psiFile = fragment.getFile();
final VirtualFile virtualFile = psiFile != null ? psiFile.getVirtualFile() : null;
if (virtualFile != null) {
writer.startNode("fragment");
writer.addAttribute("file", macroManager.collapsePath(virtualFile.getUrl()));
if (shouldWriteOffsets) {
final Document document = documentManager.getDocument(psiFile);
LOG.assertTrue(document != null);
int startOffset = fragment.getStartOffset();
final int line = document.getLineNumber(startOffset);
writer.addAttribute("line", String.valueOf(line));
final int lineStartOffset = document.getLineStartOffset(line);
if (StringUtil.isEmptyOrSpaces(document.getText().substring(lineStartOffset, startOffset))) {
startOffset = lineStartOffset;
}
writer.addAttribute("start", String.valueOf(startOffset));
writer.addAttribute("end", String.valueOf(fragment.getEndOffset()));
if (fragment.containsMultipleFragments()) {
final int[][] offsets = fragment.getOffsets();
for (int[] offset : offsets) {
writer.startNode("offset");
writer.addAttribute("start", String.valueOf(offset[0]));
writer.addAttribute("end", String.valueOf(offset[1]));
writer.endNode();
}
}
}
writer.endNode();
}
}
}
}