IDEA-25984: Preliminary results won't stick in UniqueResultsSearch's internal map causing low memory conditions. We'll have lightweight descriptors there instead.

This commit is contained in:
Maxim Shafirov
2010-02-02 21:55:19 +03:00
parent 9b271b5bd7
commit 2333416c75
4 changed files with 74 additions and 44 deletions
@@ -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<PsiElement, PsiEle
public static DefinitionsSearch INSTANCE = new DefinitionsSearch();
public static Query<PsiElement> search(PsiElement definitionsOf) {
return new UniqueResultsQuery<PsiElement>(INSTANCE.createQuery(definitionsOf));
return INSTANCE.createUniqueResultsQuery(definitionsOf);
}
}
@@ -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<PsiReference, ReferencesSearch.SearchParameters> {
private static final ReferencesSearch INSTANCE = new ReferencesSearch();
private static final TObjectHashingStrategy<PsiReference> HASHING_STRATEGY = new TObjectHashingStrategy<PsiReference>() {
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<PsiReference, ReferenceDescriptor> MAPPER = new Function<PsiReference, ReferenceDescriptor>() {
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<PsiReference, Refer
}
public static Query<PsiReference> search(@NotNull SearchParameters parameters) {
return INSTANCE.createUniqueResultsQuery(parameters, HASHING_STRATEGY);
//noinspection unchecked
return INSTANCE.createUniqueResultsQuery(parameters, TObjectHashingStrategy.CANONICAL, MAPPER);
}
}
@@ -57,7 +57,7 @@ public class QueryFactory<Result, Parameters> {
* @param parameters of the search
*/
public final Query<Result> createUniqueResultsQuery(@NotNull Parameters parameters) {
return new UniqueResultsQuery<Result>(createQuery(parameters));
return new UniqueResultsQuery<Result, Result>(createQuery(parameters));
}
/**
@@ -66,6 +66,18 @@ public class QueryFactory<Result, Parameters> {
* @param hashingStrategy strategy to factor results
*/
public final Query<Result> createUniqueResultsQuery(@NotNull Parameters parameters, TObjectHashingStrategy<Result> hashingStrategy) {
return new UniqueResultsQuery<Result>(createQuery(parameters), hashingStrategy);
return new UniqueResultsQuery<Result, Result>(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 <T> Query<Result> createUniqueResultsQuery(@NotNull Parameters parameters,
TObjectHashingStrategy<T> hashingStrategy,
Function<Result, T> mapper) {
return new UniqueResultsQuery<Result, T>(createQuery(parameters), hashingStrategy, mapper);
}
}
@@ -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<T> implements Query<T> {
public class UniqueResultsQuery<T, M> implements Query<T> {
private final Query<T> myOriginal;
private final TObjectHashingStrategy<T> myHashingStrategy;
private final TObjectHashingStrategy<M> myHashingStrategy;
private final Function<T, M> myMapper;
public final static Function ID = new Function() {
public Object fun(Object o) {
return o;
}
};
public UniqueResultsQuery(final Query<T> original) {
myOriginal = original;
//noinspection unchecked
myHashingStrategy = TObjectHashingStrategy.CANONICAL;
this(original, TObjectHashingStrategy.CANONICAL, ID);
}
public UniqueResultsQuery(final Query<T> original, TObjectHashingStrategy<T> hashingStrategy) {
public UniqueResultsQuery(final Query<T> original, TObjectHashingStrategy<M> hashingStrategy) {
//noinspection unchecked
this(original, hashingStrategy, ID);
}
public UniqueResultsQuery(final Query<T> original, TObjectHashingStrategy<M> hashingStrategy, Function<T, M> mapper) {
myOriginal = original;
myHashingStrategy = hashingStrategy;
myMapper = mapper;
}
public T findFirst() {
return myOriginal.findFirst();
}
private boolean doForEach(@NotNull final Processor<T> consumer, @Nullable Ref<Set<T>> outProcessed) {
final Set<T> processedElements = new ConcurrentHashSet<T>(myHashingStrategy);
if (outProcessed != null) {
outProcessed.set(processedElements);
}
public boolean forEach(@NotNull final Processor<T> consumer) {
final Set<M> processedElements = new ConcurrentHashSet<M>(myHashingStrategy);
return myOriginal.forEach(new Processor<T>() {
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<T> consumer) {
return doForEach(consumer, null);
}
@NotNull
public Collection<T> findAll() {
Ref<Set<T>> refProcessed = new Ref<Set<T>>();
doForEach(new Processor<T>() {
public boolean process(final T t) {
return true;
}
}, refProcessed);
return refProcessed.get();
final CommonProcessors.CollectProcessor<T> processor = new CommonProcessors.CollectProcessor<T>();
forEach(processor);
return processor.getResults();
}
public T[] toArray(final T[] a) {