mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
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:
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user