/* * Copyright (c) 2013, the Dart project authors. * * Licensed under the Eclipse Public License v1.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at * * http://www.eclipse.org/legal/epl-v10.html * * Unless required by applicable law or agreed to in writing, software distributed under the License * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing permissions and limitations under * the License. */ package com.google.dart.engine.internal.search; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.common.util.concurrent.Uninterruptibles; import com.google.dart.engine.element.ClassElement; import com.google.dart.engine.element.CompilationUnitElement; import com.google.dart.engine.element.ConstructorElement; import com.google.dart.engine.element.Element; import com.google.dart.engine.element.FunctionElement; import com.google.dart.engine.element.FunctionTypeAliasElement; import com.google.dart.engine.element.ImportElement; import com.google.dart.engine.element.LibraryElement; import com.google.dart.engine.element.LocalVariableElement; import com.google.dart.engine.element.MethodElement; import com.google.dart.engine.element.ParameterElement; import com.google.dart.engine.element.PropertyAccessorElement; import com.google.dart.engine.element.PropertyInducingElement; import com.google.dart.engine.element.TypeParameterElement; import com.google.dart.engine.element.VariableElement; import com.google.dart.engine.element.angular.AngularElement; import com.google.dart.engine.index.Index; import com.google.dart.engine.index.Location; import com.google.dart.engine.index.LocationWithData; import com.google.dart.engine.index.Relationship; import com.google.dart.engine.index.RelationshipCallback; import com.google.dart.engine.internal.element.member.Member; import com.google.dart.engine.internal.index.IndexConstants; import com.google.dart.engine.internal.index.NameElementImpl; import com.google.dart.engine.internal.search.listener.CountingSearchListener; import com.google.dart.engine.internal.search.listener.FilteredSearchListener; import com.google.dart.engine.internal.search.listener.GatheringSearchListener; import com.google.dart.engine.internal.search.listener.NameMatchingSearchListener; import com.google.dart.engine.internal.search.scope.LibrarySearchScope; import com.google.dart.engine.search.MatchKind; import com.google.dart.engine.search.MatchQuality; import com.google.dart.engine.search.SearchEngine; import com.google.dart.engine.search.SearchFilter; import com.google.dart.engine.search.SearchListener; import com.google.dart.engine.search.SearchMatch; import com.google.dart.engine.search.SearchPattern; import com.google.dart.engine.search.SearchScope; import com.google.dart.engine.type.Type; import com.google.dart.engine.utilities.source.SourceRange; import java.util.Collections; import java.util.List; import java.util.Set; import java.util.concurrent.CountDownLatch; /** * Implementation of {@link SearchEngine}. * * @coverage dart.engine.search */ public class SearchEngineImpl implements SearchEngine { /** * Instances of the class <code>RelationshipCallbackImpl</code> implement a callback that can be * used to report results to a search listener. */ private static class RelationshipCallbackImpl implements RelationshipCallback { private final SearchScope scope; /** * The kind of matches that are represented by the results that will be provided to this * callback. */ private MatchKind matchKind; /** * The search listener that should be notified when results are found. */ private SearchListener listener; /** * Initialize a newly created callback to report matches of the given kind to the given listener * when results are found. * * @param scope the {@link SearchScope} to return matches from, may be {@code null} to return * all matches * @param matchKind the kind of matches that are represented by the results * @param listener the search listener that should be notified when results are found */ public RelationshipCallbackImpl(SearchScope scope, MatchKind matchKind, SearchListener listener) { this.scope = scope; this.matchKind = matchKind; this.listener = listener; } @Override public void hasRelationships(Element element, Relationship relationship, Location[] locations) { for (Location location : locations) { Element targetElement = location.getElement(); // check scope if (scope != null && !scope.encloses(targetElement)) { continue; } SourceRange range = new SourceRange(location.getOffset(), location.getLength()); // TODO(scheglov) IndexConstants.DYNAMIC for MatchQuality.NAME MatchQuality quality = MatchQuality.EXACT; // MatchQuality quality = element.getResource() != IndexConstants.DYNAMIC // ? MatchQuality.EXACT : MatchQuality.NAME; SearchMatch match = new SearchMatch(quality, matchKind, targetElement, range); match.setQualified(relationship == IndexConstants.IS_REFERENCED_BY_QUALIFIED || relationship == IndexConstants.IS_INVOKED_BY_QUALIFIED); listener.matchFound(match); } listener.searchComplete(); } } /** * The interface <code>SearchRunner</code> defines the behavior of objects that can be used to * perform an asynchronous search. */ private interface SearchRunner { /** * Perform an asynchronous search, passing the results to the given listener. * * @param listener the listener to which search results should be passed @ if the results could * not be computed */ public void performSearch(SearchListener listener); } /** * Apply the given filter to the given listener. * * @param filter the filter to be used before passing matches on to the listener, or {@code null} * if all matches should be passed on * @param listener the listener that will only be given matches that pass the filter * @return a search listener that will pass to the given listener any matches that pass the given * filter */ private static SearchListener applyFilter(SearchFilter filter, SearchListener listener) { if (filter == null) { return listener; } return new FilteredSearchListener(filter, listener); } /** * Apply the given pattern to the given listener. * * @param pattern the pattern to be used before passing matches on to the listener, or * {@code null} if all matches should be passed on * @param listener the listener that will only be given matches that match the pattern * @return a search listener that will pass to the given listener any matches that match the given * pattern */ private static SearchListener applyPattern(SearchPattern pattern, SearchListener listener) { if (pattern == null) { return listener; } return new NameMatchingSearchListener(pattern, listener); } private static Element[] createElements(SearchScope scope) { if (scope instanceof LibrarySearchScope) { return ((LibrarySearchScope) scope).getLibraries(); } return new Element[] {IndexConstants.UNIVERSE}; } private static RelationshipCallback newCallback(MatchKind matchKind, SearchScope scope, SearchListener listener) { return new RelationshipCallbackImpl(scope, matchKind, listener); } /** * The index used to respond to the search requests. */ private Index index; /** * Initialize a newly created search engine to use the given index. * * @param index the index used to respond to the search requests */ public SearchEngineImpl(Index index) { this.index = index; } @Override public Set<Type> searchAssignedTypes(PropertyInducingElement variable, SearchScope scope) { PropertyAccessorElement setter = variable.getSetter(); int numRequests = (setter != null ? 2 : 0) + 2; // find locations final List<Location> locations = Lists.newArrayList(); final CountDownLatch latch = new CountDownLatch(numRequests); class Callback implements RelationshipCallback { @Override public void hasRelationships(Element element, Relationship relationship, Location[] locs) { Collections.addAll(locations, locs); latch.countDown(); } } if (setter != null) { index.getRelationships(setter, IndexConstants.IS_REFERENCED_BY_QUALIFIED, new Callback()); index.getRelationships(setter, IndexConstants.IS_REFERENCED_BY_UNQUALIFIED, new Callback()); } index.getRelationships(variable, IndexConstants.IS_REFERENCED_BY, new Callback()); index.getRelationships(variable, IndexConstants.IS_DEFINED_BY, new Callback()); Uninterruptibles.awaitUninterruptibly(latch); // get types from locations Set<Type> types = Sets.newHashSet(); for (Location location : locations) { // check scope if (scope != null) { Element targetElement = location.getElement(); if (!scope.encloses(targetElement)) { continue; } } // we need data if (!(location instanceof LocationWithData<?>)) { continue; } LocationWithData<?> locationWithData = (LocationWithData<?>) location; // add type Object data = locationWithData.getData(); if (data instanceof Type) { Type type = (Type) data; types.add(type); } } // done return types; } @Override public List<SearchMatch> searchDeclarations(final String name, final SearchScope scope, final SearchFilter filter) { return gatherResults(new SearchRunner() { @Override public void performSearch(SearchListener listener) { searchDeclarations(name, scope, filter, listener); } }); } @Override public void searchDeclarations(String name, SearchScope scope, SearchFilter filter, SearchListener listener) { assert listener != null; listener = applyFilter(filter, listener); index.getRelationships( new NameElementImpl(name), IndexConstants.IS_DEFINED_BY, newCallback(MatchKind.NAME_DECLARATION, scope, listener)); } @Override public List<SearchMatch> searchFunctionDeclarations(final SearchScope scope, final SearchPattern pattern, final SearchFilter filter) { return gatherResults(new SearchRunner() { @Override public void performSearch(SearchListener listener) { searchFunctionDeclarations(scope, pattern, filter, listener); } }); } @Override public void searchFunctionDeclarations(SearchScope scope, SearchPattern pattern, SearchFilter filter, SearchListener listener) { assert listener != null; Element[] elements = createElements(scope); listener = applyPattern(pattern, listener); listener = applyFilter(filter, listener); listener = new CountingSearchListener(elements.length, listener); for (Element element : elements) { index.getRelationships( element, IndexConstants.DEFINES_FUNCTION, newCallback(MatchKind.FUNCTION_DECLARATION, scope, listener)); } } @Override public List<SearchMatch> searchQualifiedMemberReferences(final String name, final SearchScope scope, final SearchFilter filter) { return gatherResults(new SearchRunner() { @Override public void performSearch(SearchListener listener) { searchQualifiedMemberReferences(name, scope, filter, listener); } }); } @Override public void searchQualifiedMemberReferences(String name, SearchScope scope, SearchFilter filter, SearchListener listener) { assert listener != null; listener = applyFilter(filter, listener); listener = new CountingSearchListener(10, listener); index.getRelationships( new NameElementImpl(name), IndexConstants.IS_REFERENCED_BY_QUALIFIED_RESOLVED, newCallback(MatchKind.NAME_REFERENCE_RESOLVED, scope, listener)); index.getRelationships( new NameElementImpl(name), IndexConstants.IS_REFERENCED_BY_QUALIFIED_UNRESOLVED, newCallback(MatchKind.NAME_REFERENCE_UNRESOLVED, scope, listener)); // granular resolved operations index.getRelationships( new NameElementImpl(name), IndexConstants.NAME_IS_INVOKED_BY_RESOLVED, newCallback(MatchKind.NAME_INVOCATION_RESOLVED, scope, listener)); index.getRelationships( new NameElementImpl(name), IndexConstants.NAME_IS_READ_BY_RESOLVED, newCallback(MatchKind.NAME_READ_RESOLVED, scope, listener)); index.getRelationships( new NameElementImpl(name), IndexConstants.NAME_IS_READ_WRITTEN_BY_RESOLVED, newCallback(MatchKind.NAME_READ_WRITE_RESOLVED, scope, listener)); index.getRelationships( new NameElementImpl(name), IndexConstants.NAME_IS_WRITTEN_BY_RESOLVED, newCallback(MatchKind.NAME_WRITE_RESOLVED, scope, listener)); // granular unresolved operations index.getRelationships( new NameElementImpl(name), IndexConstants.NAME_IS_INVOKED_BY_UNRESOLVED, newCallback(MatchKind.NAME_INVOCATION_UNRESOLVED, scope, listener)); index.getRelationships( new NameElementImpl(name), IndexConstants.NAME_IS_READ_BY_UNRESOLVED, newCallback(MatchKind.NAME_READ_UNRESOLVED, scope, listener)); index.getRelationships( new NameElementImpl(name), IndexConstants.NAME_IS_READ_WRITTEN_BY_UNRESOLVED, newCallback(MatchKind.NAME_READ_WRITE_UNRESOLVED, scope, listener)); index.getRelationships( new NameElementImpl(name), IndexConstants.NAME_IS_WRITTEN_BY_UNRESOLVED, newCallback(MatchKind.NAME_WRITE_UNRESOLVED, scope, listener)); } @Override public List<SearchMatch> searchReferences(final Element element, final SearchScope scope, final SearchFilter filter) { return gatherResults(new SearchRunner() { @Override public void performSearch(SearchListener listener) { searchReferences(element, scope, filter, listener); } }); } @Override public void searchReferences(Element element, SearchScope scope, SearchFilter filter, SearchListener listener) { if (element == null) { listener.searchComplete(); return; } if (element instanceof Member) { element = ((Member) element).getBaseElement(); } switch (element.getKind()) { case ANGULAR_COMPONENT: case ANGULAR_CONTROLLER: case ANGULAR_FORMATTER: case ANGULAR_PROPERTY: case ANGULAR_SCOPE_PROPERTY: case ANGULAR_SELECTOR: searchReferences((AngularElement) element, scope, filter, listener); return; case CLASS: searchReferences((ClassElement) element, scope, filter, listener); return; case COMPILATION_UNIT: searchReferences((CompilationUnitElement) element, scope, filter, listener); return; case CONSTRUCTOR: searchReferences((ConstructorElement) element, scope, filter, listener); return; case FIELD: case TOP_LEVEL_VARIABLE: searchReferences((PropertyInducingElement) element, scope, filter, listener); return; case FUNCTION: searchReferences((FunctionElement) element, scope, filter, listener); return; case GETTER: case SETTER: searchReferences((PropertyAccessorElement) element, scope, filter, listener); return; case IMPORT: searchReferences((ImportElement) element, scope, filter, listener); return; case LIBRARY: searchReferences((LibraryElement) element, scope, filter, listener); return; case LOCAL_VARIABLE: searchReferences((LocalVariableElement) element, scope, filter, listener); return; case METHOD: searchReferences((MethodElement) element, scope, filter, listener); return; case PARAMETER: searchReferences((ParameterElement) element, scope, filter, listener); return; case FUNCTION_TYPE_ALIAS: searchReferences((FunctionTypeAliasElement) element, scope, filter, listener); return; case TYPE_PARAMETER: searchReferences((TypeParameterElement) element, scope, filter, listener); return; default: listener.searchComplete(); return; } } @Override public List<SearchMatch> searchSubtypes(final ClassElement type, final SearchScope scope, final SearchFilter filter) { return gatherResults(new SearchRunner() { @Override public void performSearch(SearchListener listener) { searchSubtypes(type, scope, filter, listener); } }); } @Override public void searchSubtypes(ClassElement type, SearchScope scope, SearchFilter filter, SearchListener listener) { assert listener != null; listener = applyFilter(filter, listener); listener = new CountingSearchListener(3, listener); index.getRelationships( type, IndexConstants.IS_EXTENDED_BY, newCallback(MatchKind.EXTENDS_REFERENCE, scope, listener)); index.getRelationships( type, IndexConstants.IS_MIXED_IN_BY, newCallback(MatchKind.WITH_REFERENCE, scope, listener)); index.getRelationships( type, IndexConstants.IS_IMPLEMENTED_BY, newCallback(MatchKind.IMPLEMENTS_REFERENCE, scope, listener)); } @Override public List<SearchMatch> searchTypeDeclarations(final SearchScope scope, final SearchPattern pattern, final SearchFilter filter) { return gatherResults(new SearchRunner() { @Override public void performSearch(SearchListener listener) { searchTypeDeclarations(scope, pattern, filter, listener); } }); } @Override public void searchTypeDeclarations(SearchScope scope, SearchPattern pattern, SearchFilter filter, SearchListener listener) { assert listener != null; Element[] elements = createElements(scope); listener = applyPattern(pattern, listener); listener = applyFilter(filter, listener); listener = new CountingSearchListener(elements.length * 3, listener); for (Element element : elements) { index.getRelationships( element, IndexConstants.DEFINES_CLASS, newCallback(MatchKind.CLASS_DECLARATION, scope, listener)); index.getRelationships( element, IndexConstants.DEFINES_CLASS_ALIAS, newCallback(MatchKind.CLASS_ALIAS_DECLARATION, scope, listener)); index.getRelationships( element, IndexConstants.DEFINES_FUNCTION_TYPE, newCallback(MatchKind.FUNCTION_TYPE_DECLARATION, scope, listener)); } } @Override public List<SearchMatch> searchVariableDeclarations(final SearchScope scope, final SearchPattern pattern, final SearchFilter filter) { return gatherResults(new SearchRunner() { @Override public void performSearch(SearchListener listener) { searchVariableDeclarations(scope, pattern, filter, listener); } }); } @Override public void searchVariableDeclarations(SearchScope scope, SearchPattern pattern, SearchFilter filter, SearchListener listener) { assert listener != null; Element[] elements = createElements(scope); listener = applyPattern(pattern, listener); listener = applyFilter(filter, listener); listener = new CountingSearchListener(elements.length, listener); for (Element element : elements) { index.getRelationships( element, IndexConstants.DEFINES_VARIABLE, newCallback(MatchKind.VARIABLE_DECLARATION, scope, listener)); } } /** * Use the given runner to perform the given number of asynchronous searches, then wait until the * search has completed and return the results that were produced. * * @param runner the runner used to perform an asynchronous search * @return the results that were produced @ if the results of at least one of the searched could * not be computed */ private List<SearchMatch> gatherResults(SearchRunner runner) { GatheringSearchListener listener = new GatheringSearchListener(); runner.performSearch(listener); while (!listener.isComplete()) { Thread.yield(); } return listener.getMatches(); } private void searchReferences(AngularElement element, SearchScope scope, SearchFilter filter, SearchListener listener) { assert listener != null; listener = applyFilter(filter, listener); listener = new CountingSearchListener(2, listener); index.getRelationships( element, IndexConstants.ANGULAR_REFERENCE, newCallback(MatchKind.ANGULAR_REFERENCE, scope, listener)); index.getRelationships( element, IndexConstants.ANGULAR_CLOSING_TAG_REFERENCE, newCallback(MatchKind.ANGULAR_CLOSING_TAG_REFERENCE, scope, listener)); } private void searchReferences(ClassElement type, SearchScope scope, SearchFilter filter, SearchListener listener) { assert listener != null; listener = applyFilter(filter, listener); index.getRelationships( type, IndexConstants.IS_REFERENCED_BY, newCallback(MatchKind.TYPE_REFERENCE, scope, listener)); } private void searchReferences(CompilationUnitElement unit, SearchScope scope, SearchFilter filter, SearchListener listener) { assert listener != null; listener = applyFilter(filter, listener); index.getRelationships( unit, IndexConstants.IS_REFERENCED_BY, newCallback(MatchKind.UNIT_REFERENCE, scope, listener)); } private void searchReferences(ConstructorElement constructor, SearchScope scope, SearchFilter filter, SearchListener listener) { assert listener != null; listener = applyFilter(filter, listener); listener = new CountingSearchListener(2, listener); index.getRelationships( constructor, IndexConstants.IS_DEFINED_BY, newCallback(MatchKind.CONSTRUCTOR_DECLARATION, scope, listener)); index.getRelationships( constructor, IndexConstants.IS_REFERENCED_BY, newCallback(MatchKind.CONSTRUCTOR_REFERENCE, scope, listener)); } private void searchReferences(FunctionElement function, SearchScope scope, SearchFilter filter, SearchListener listener) { assert listener != null; listener = applyFilter(filter, listener); listener = new CountingSearchListener(2, listener); index.getRelationships( function, IndexConstants.IS_REFERENCED_BY, newCallback(MatchKind.FUNCTION_REFERENCE, scope, listener)); index.getRelationships( function, IndexConstants.IS_INVOKED_BY, newCallback(MatchKind.FUNCTION_EXECUTION, scope, listener)); } private void searchReferences(FunctionTypeAliasElement alias, SearchScope scope, SearchFilter filter, SearchListener listener) { assert listener != null; listener = applyFilter(filter, listener); index.getRelationships( alias, IndexConstants.IS_REFERENCED_BY, newCallback(MatchKind.FUNCTION_TYPE_REFERENCE, scope, listener)); } private void searchReferences(ImportElement imp, SearchScope scope, SearchFilter filter, SearchListener listener) { assert listener != null; listener = applyFilter(filter, listener); index.getRelationships( imp, IndexConstants.IS_REFERENCED_BY, newCallback(MatchKind.IMPORT_REFERENCE, scope, listener)); } private void searchReferences(LibraryElement library, SearchScope scope, SearchFilter filter, SearchListener listener) { assert listener != null; listener = applyFilter(filter, listener); index.getRelationships( library, IndexConstants.IS_REFERENCED_BY, newCallback(MatchKind.LIBRARY_REFERENCE, scope, listener)); } private void searchReferences(MethodElement method, SearchScope scope, SearchFilter filter, SearchListener listener) { assert listener != null; listener = applyFilter(filter, listener); // TODO(scheglov) use "5" when add named matches listener = new CountingSearchListener(4, listener); // exact matches index.getRelationships( method, IndexConstants.IS_INVOKED_BY_UNQUALIFIED, newCallback(MatchKind.METHOD_INVOCATION, scope, listener)); index.getRelationships( method, IndexConstants.IS_INVOKED_BY_QUALIFIED, newCallback(MatchKind.METHOD_INVOCATION, scope, listener)); index.getRelationships( method, IndexConstants.IS_REFERENCED_BY_UNQUALIFIED, newCallback(MatchKind.METHOD_REFERENCE, scope, listener)); index.getRelationships( method, IndexConstants.IS_REFERENCED_BY_QUALIFIED, newCallback(MatchKind.METHOD_REFERENCE, scope, listener)); // TODO(scheglov) // inexact matches // index.getRelationships( // new Element(IndexConstants.DYNAMIC, method.getElementName()), // IndexConstants.IS_INVOKED_BY_QUALIFIED, // newCallback(MatchKind.METHOD_INVOCATION, listener)); } private void searchReferences(ParameterElement parameter, SearchScope scope, SearchFilter filter, SearchListener listener) { assert listener != null; listener = applyFilter(filter, listener); listener = new CountingSearchListener(5, listener); index.getRelationships( parameter, IndexConstants.IS_READ_BY, newCallback(MatchKind.VARIABLE_READ, scope, listener)); index.getRelationships( parameter, IndexConstants.IS_READ_WRITTEN_BY, newCallback(MatchKind.VARIABLE_READ_WRITE, scope, listener)); index.getRelationships( parameter, IndexConstants.IS_WRITTEN_BY, newCallback(MatchKind.VARIABLE_WRITE, scope, listener)); index.getRelationships( parameter, IndexConstants.IS_REFERENCED_BY, newCallback(MatchKind.NAMED_PARAMETER_REFERENCE, scope, listener)); index.getRelationships( parameter, IndexConstants.IS_INVOKED_BY, newCallback(MatchKind.FUNCTION_EXECUTION, scope, listener)); } private void searchReferences(PropertyAccessorElement accessor, SearchScope scope, SearchFilter filter, SearchListener listener) { assert listener != null; listener = applyFilter(filter, listener); listener = new CountingSearchListener(2, listener); index.getRelationships( accessor, IndexConstants.IS_REFERENCED_BY_QUALIFIED, newCallback(MatchKind.PROPERTY_ACCESSOR_REFERENCE, scope, listener)); index.getRelationships( accessor, IndexConstants.IS_REFERENCED_BY_UNQUALIFIED, newCallback(MatchKind.PROPERTY_ACCESSOR_REFERENCE, scope, listener)); } private void searchReferences(PropertyInducingElement field, SearchScope scope, SearchFilter filter, SearchListener listener) { assert listener != null; PropertyAccessorElement getter = field.getGetter(); PropertyAccessorElement setter = field.getSetter(); int numRequests = (getter != null ? 4 : 0) + (setter != null ? 2 : 0) + 2; listener = applyFilter(filter, listener); listener = new CountingSearchListener(numRequests, listener); if (getter != null) { index.getRelationships( getter, IndexConstants.IS_REFERENCED_BY_QUALIFIED, newCallback(MatchKind.FIELD_READ, scope, listener)); index.getRelationships( getter, IndexConstants.IS_REFERENCED_BY_UNQUALIFIED, newCallback(MatchKind.FIELD_READ, scope, listener)); index.getRelationships( getter, IndexConstants.IS_INVOKED_BY_QUALIFIED, newCallback(MatchKind.FIELD_INVOCATION, scope, listener)); index.getRelationships( getter, IndexConstants.IS_INVOKED_BY_UNQUALIFIED, newCallback(MatchKind.FIELD_INVOCATION, scope, listener)); } if (setter != null) { index.getRelationships( setter, IndexConstants.IS_REFERENCED_BY_QUALIFIED, newCallback(MatchKind.FIELD_WRITE, scope, listener)); index.getRelationships( setter, IndexConstants.IS_REFERENCED_BY_UNQUALIFIED, newCallback(MatchKind.FIELD_WRITE, scope, listener)); } index.getRelationships( field, IndexConstants.IS_REFERENCED_BY, newCallback(MatchKind.FIELD_REFERENCE, scope, listener)); index.getRelationships( field, IndexConstants.IS_REFERENCED_BY_QUALIFIED, newCallback(MatchKind.FIELD_REFERENCE, scope, listener)); } private void searchReferences(TypeParameterElement typeParameter, SearchScope scope, SearchFilter filter, SearchListener listener) { assert listener != null; listener = applyFilter(filter, listener); index.getRelationships( typeParameter, IndexConstants.IS_REFERENCED_BY, newCallback(MatchKind.TYPE_PARAMETER_REFERENCE, scope, listener)); } private void searchReferences(VariableElement variable, SearchScope scope, SearchFilter filter, SearchListener listener) { assert listener != null; listener = applyFilter(filter, listener); listener = new CountingSearchListener(4, listener); index.getRelationships( variable, IndexConstants.IS_READ_BY, newCallback(MatchKind.VARIABLE_READ, scope, listener)); index.getRelationships( variable, IndexConstants.IS_READ_WRITTEN_BY, newCallback(MatchKind.VARIABLE_READ_WRITE, scope, listener)); index.getRelationships( variable, IndexConstants.IS_WRITTEN_BY, newCallback(MatchKind.VARIABLE_WRITE, scope, listener)); index.getRelationships( variable, IndexConstants.IS_INVOKED_BY, newCallback(MatchKind.FUNCTION_EXECUTION, scope, listener)); } }