diff --git a/platform/lang-api/src/com/intellij/psi/search/searches/DefinitionsSearch.java b/platform/lang-api/src/com/intellij/psi/search/searches/DefinitionsSearch.java index b2b4c1f9e2c3..1d141b19c265 100644 --- a/platform/lang-api/src/com/intellij/psi/search/searches/DefinitionsSearch.java +++ b/platform/lang-api/src/com/intellij/psi/search/searches/DefinitionsSearch.java @@ -18,7 +18,6 @@ package com.intellij.psi.search.searches; import com.intellij.psi.PsiElement; import com.intellij.util.Query; -import com.intellij.util.UniqueResultsQuery; /** * The search is used in two IDE navigation functions namely Go To Implementation (Ctrl+Alt+B) and @@ -31,6 +30,6 @@ public class DefinitionsSearch extends ExtensibleQueryFactory search(PsiElement definitionsOf) { - return new UniqueResultsQuery(INSTANCE.createQuery(definitionsOf)); + return INSTANCE.createUniqueResultsQuery(definitionsOf); } } diff --git a/platform/lang-api/src/com/intellij/psi/search/searches/ReferencesSearch.java b/platform/lang-api/src/com/intellij/psi/search/searches/ReferencesSearch.java index 0ce81f718980..c4551018f374 100644 --- a/platform/lang-api/src/com/intellij/psi/search/searches/ReferencesSearch.java +++ b/platform/lang-api/src/com/intellij/psi/search/searches/ReferencesSearch.java @@ -20,6 +20,7 @@ import com.intellij.psi.PsiFile; import com.intellij.psi.PsiReference; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.search.SearchScope; +import com.intellij.util.Function; import com.intellij.util.Query; import gnu.trove.TObjectHashingStrategy; import org.jetbrains.annotations.NotNull; @@ -29,21 +30,38 @@ import org.jetbrains.annotations.NotNull; */ public class ReferencesSearch extends ExtensibleQueryFactory { private static final ReferencesSearch INSTANCE = new ReferencesSearch(); - private static final TObjectHashingStrategy HASHING_STRATEGY = new TObjectHashingStrategy() { - public int computeHashCode(final PsiReference object) { - if (object == null) return 0; - final PsiElement element = object.getElement(); - final PsiFile file = element.getContainingFile(); - return file.hashCode() + 31 * (element.getTextOffset() + object.getRangeInElement().getStartOffset()); + + private static class ReferenceDescriptor { + private final PsiFile file; + private final int offset; + + private ReferenceDescriptor(PsiFile file, int offset) { + this.file = file; + this.offset = offset; } - public boolean equals(final PsiReference o1, final PsiReference o2) { - if (o1 == o2) return true; - if (o1 == null || o2 == null) return false; - final PsiElement e1 = o1.getElement(); - final PsiElement e2 = o2.getElement(); - return e1.getManager().areElementsEquivalent(e1.getContainingFile(), e2.getContainingFile()) && - e1.getTextOffset() + o1.getRangeInElement().getStartOffset() == e2.getTextOffset() + o2.getRangeInElement().getStartOffset(); + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof ReferenceDescriptor)) return false; + + ReferenceDescriptor that = (ReferenceDescriptor)o; + + if (offset != that.offset) return false; + return file.equals(that.file); + } + + @Override + public int hashCode() { + return 31 * file.hashCode() + offset; + } + } + + private static final Function MAPPER = new Function() { + public ReferenceDescriptor fun(PsiReference psiReference) { + final PsiElement element = psiReference.getElement(); + final PsiFile file = element.getContainingFile(); + return new ReferenceDescriptor(file, element.getTextOffset() + psiReference.getRangeInElement().getStartOffset()); } }; @@ -97,6 +115,7 @@ public class ReferencesSearch extends ExtensibleQueryFactory search(@NotNull SearchParameters parameters) { - return INSTANCE.createUniqueResultsQuery(parameters, HASHING_STRATEGY); + //noinspection unchecked + return INSTANCE.createUniqueResultsQuery(parameters, TObjectHashingStrategy.CANONICAL, MAPPER); } } diff --git a/platform/util/src/com/intellij/util/QueryFactory.java b/platform/util/src/com/intellij/util/QueryFactory.java index 5ad023fa64de..ca2d756b82f5 100644 --- a/platform/util/src/com/intellij/util/QueryFactory.java +++ b/platform/util/src/com/intellij/util/QueryFactory.java @@ -57,7 +57,7 @@ public class QueryFactory { * @param parameters of the search */ public final Query createUniqueResultsQuery(@NotNull Parameters parameters) { - return new UniqueResultsQuery(createQuery(parameters)); + return new UniqueResultsQuery(createQuery(parameters)); } /** @@ -66,6 +66,18 @@ public class QueryFactory { * @param hashingStrategy strategy to factor results */ public final Query createUniqueResultsQuery(@NotNull Parameters parameters, TObjectHashingStrategy hashingStrategy) { - return new UniqueResultsQuery(createQuery(parameters), hashingStrategy); + return new UniqueResultsQuery(createQuery(parameters), hashingStrategy); + } + + /** @return query to perform the search. Obtained results are mapped to whatever objects that are automatically filtered wrt. equals() + * relation. Storing mapped objects instead of original elements may be wise wrt to memory consumption. + * @param parameters of the search + * @param hashingStrategy strategy to factor results + * @param mapper function that maps results to their mapping counterparts. + */ + public final Query createUniqueResultsQuery(@NotNull Parameters parameters, + TObjectHashingStrategy hashingStrategy, + Function mapper) { + return new UniqueResultsQuery(createQuery(parameters), hashingStrategy, mapper); } } diff --git a/platform/util/src/com/intellij/util/UniqueResultsQuery.java b/platform/util/src/com/intellij/util/UniqueResultsQuery.java index 98259c4d4703..577405af4895 100644 --- a/platform/util/src/com/intellij/util/UniqueResultsQuery.java +++ b/platform/util/src/com/intellij/util/UniqueResultsQuery.java @@ -16,62 +16,62 @@ package com.intellij.util; -import com.intellij.openapi.util.Ref; import com.intellij.util.containers.ConcurrentHashSet; import gnu.trove.TObjectHashingStrategy; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import java.util.*; +import java.util.Collection; +import java.util.Iterator; +import java.util.Set; /** * @author max */ -public class UniqueResultsQuery implements Query { +public class UniqueResultsQuery implements Query { private final Query myOriginal; - private final TObjectHashingStrategy myHashingStrategy; + private final TObjectHashingStrategy myHashingStrategy; + private final Function myMapper; + + public final static Function ID = new Function() { + public Object fun(Object o) { + return o; + } + }; public UniqueResultsQuery(final Query original) { - myOriginal = original; //noinspection unchecked - myHashingStrategy = TObjectHashingStrategy.CANONICAL; + this(original, TObjectHashingStrategy.CANONICAL, ID); } - public UniqueResultsQuery(final Query original, TObjectHashingStrategy hashingStrategy) { + public UniqueResultsQuery(final Query original, TObjectHashingStrategy hashingStrategy) { + //noinspection unchecked + this(original, hashingStrategy, ID); + } + + public UniqueResultsQuery(final Query original, TObjectHashingStrategy hashingStrategy, Function mapper) { myOriginal = original; myHashingStrategy = hashingStrategy; + myMapper = mapper; } public T findFirst() { return myOriginal.findFirst(); } - private boolean doForEach(@NotNull final Processor consumer, @Nullable Ref> outProcessed) { - final Set processedElements = new ConcurrentHashSet(myHashingStrategy); - if (outProcessed != null) { - outProcessed.set(processedElements); - } + public boolean forEach(@NotNull final Processor consumer) { + final Set processedElements = new ConcurrentHashSet(myHashingStrategy); return myOriginal.forEach(new Processor() { public boolean process(final T t) { - return !processedElements.add(t) || consumer.process(t); + return !processedElements.add(myMapper.fun(t)) || consumer.process(t); } }); } - public boolean forEach(@NotNull final Processor consumer) { - return doForEach(consumer, null); - } - @NotNull public Collection findAll() { - Ref> refProcessed = new Ref>(); - doForEach(new Processor() { - public boolean process(final T t) { - return true; - } - }, refProcessed); - - return refProcessed.get(); + final CommonProcessors.CollectProcessor processor = new CommonProcessors.CollectProcessor(); + forEach(processor); + return processor.getResults(); } public T[] toArray(final T[] a) {