Skip to content

Commit

Permalink
Find references to fields via getters/setters (#1561)
Browse files Browse the repository at this point in the history
Signed-off-by: Snjezana Peco <[email protected]>
  • Loading branch information
snjeza authored Oct 8, 2020
1 parent eba0282 commit ece3235
Show file tree
Hide file tree
Showing 5 changed files with 188 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,16 @@
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jdt.core.IAnnotation;
import org.eclipse.jdt.core.IClassFile;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IField;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMemberValuePair;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.ITypeRoot;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.search.IJavaSearchConstants;
Expand All @@ -31,6 +37,7 @@
import org.eclipse.jdt.core.search.SearchParticipant;
import org.eclipse.jdt.core.search.SearchPattern;
import org.eclipse.jdt.core.search.SearchRequestor;
import org.eclipse.jdt.internal.corext.codemanipulation.GetterSetterUtil;
import org.eclipse.jdt.ls.core.internal.JDTUtils;
import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin;
import org.eclipse.jdt.ls.core.internal.preferences.PreferenceManager;
Expand All @@ -55,47 +62,100 @@ private IJavaSearchScope createSearchScope() throws JavaModelException {
}

public List<Location> findReferences(ReferenceParams param, IProgressMonitor monitor) {

final List<Location> locations = new ArrayList<>();
try {
IJavaElement elementToSearch = JDTUtils.findElementAtSelection(JDTUtils.resolveTypeRoot(param.getTextDocument().getUri()), param.getPosition().getLine(), param.getPosition().getCharacter(), this.preferenceManager, monitor);

ITypeRoot typeRoot = JDTUtils.resolveTypeRoot(param.getTextDocument().getUri());
if (typeRoot == null) {
return locations;
}
IJavaElement elementToSearch = JDTUtils.findElementAtSelection(typeRoot, param.getPosition().getLine(), param.getPosition().getCharacter(), this.preferenceManager, monitor);
if (elementToSearch == null) {
int offset = JsonRpcHelpers.toOffset(typeRoot.getBuffer(), param.getPosition().getLine(), param.getPosition().getCharacter());
elementToSearch = typeRoot.getElementAt(offset);
}
if (elementToSearch == null) {
return locations;
}

boolean includeClassFiles = preferenceManager.isClientSupportsClassFileContent();
SearchEngine engine = new SearchEngine();
SearchPattern pattern = SearchPattern.createPattern(elementToSearch, IJavaSearchConstants.REFERENCES);

engine.search(pattern, new SearchParticipant[] { SearchEngine.getDefaultSearchParticipant() }, createSearchScope(), new SearchRequestor() {

@Override
public void acceptSearchMatch(SearchMatch match) throws CoreException {
Object o = match.getElement();
if (o instanceof IJavaElement) {
IJavaElement element = (IJavaElement) o;
ICompilationUnit compilationUnit = (ICompilationUnit) element.getAncestor(IJavaElement.COMPILATION_UNIT);
Location location = null;
if (compilationUnit != null) {
location = JDTUtils.toLocation(compilationUnit, match.getOffset(), match.getLength());
} else if (includeClassFiles) {
IClassFile cf = (IClassFile) element.getAncestor(IJavaElement.CLASS_FILE);
if (cf != null && cf.getSourceRange() != null) {
location = JDTUtils.toLocation(cf, match.getOffset(), match.getLength());
}
}
if (location != null) {
locations.add(location);
search(elementToSearch, locations, monitor);
if (preferenceManager.getPreferences().isIncludeGetterSetter() && elementToSearch instanceof IField) { // IField
IField field = (IField) elementToSearch;
IMethod getter = GetterSetterUtil.getGetter(field);
if (getter != null) {
search(getter, locations, monitor);
}
IMethod setter = GetterSetterUtil.getSetter(field);
if (setter != null) {
search(setter, locations, monitor);
}
String builderName = getBuilderName(field);
IType builder = field.getJavaProject().findType(builderName);
if (builder != null) {
String fieldSignature = field.getTypeSignature();
for (IMethod method : builder.getMethods()) {
String[] parameters = method.getParameterTypes();
if (parameters.length == 1 && field.getElementName().equals(method.getElementName()) && fieldSignature.equals(parameters[0])) {
search(method, locations, monitor);
}
}
}
}, monitor);

}
} catch (CoreException e) {
JavaLanguageServerPlugin.logException("Find references failure ", e);
}
return locations;
}

private String getBuilderName(IField field) {
IType declaringType = field.getDeclaringType();
IAnnotation annotation = declaringType.getAnnotation("Builder");
if (annotation == null || !annotation.exists()) {
annotation = declaringType.getAnnotation("lombok.Builder");
}
if (annotation != null && annotation.exists()) {
try {
for (IMemberValuePair pair : annotation.getMemberValuePairs()) {
if (pair.getValueKind() == IMemberValuePair.K_STRING) {
String memberName = pair.getMemberName();
Object value = pair.getValue();
if ("builderClassName".equals(memberName) && value instanceof String && !((String) value).isEmpty()) {
return declaringType.getFullyQualifiedName() + "." + (String) value;
}
}
}
} catch (JavaModelException e) {
JavaLanguageServerPlugin.logException(e.getMessage(), e);
}
}
return declaringType.getFullyQualifiedName() + "." + declaringType.getElementName() + "Builder";
}

private void search(IJavaElement elementToSearch, final List<Location> locations, IProgressMonitor monitor) throws CoreException, JavaModelException {
boolean includeClassFiles = preferenceManager.isClientSupportsClassFileContent();
SearchEngine engine = new SearchEngine();
SearchPattern pattern = SearchPattern.createPattern(elementToSearch, IJavaSearchConstants.REFERENCES);
engine.search(pattern, new SearchParticipant[] { SearchEngine.getDefaultSearchParticipant() }, createSearchScope(), new SearchRequestor() {

@Override
public void acceptSearchMatch(SearchMatch match) throws CoreException {
Object o = match.getElement();
if (o instanceof IJavaElement) {
IJavaElement element = (IJavaElement) o;
ICompilationUnit compilationUnit = (ICompilationUnit) element.getAncestor(IJavaElement.COMPILATION_UNIT);
Location location = null;
if (compilationUnit != null) {
location = JDTUtils.toLocation(compilationUnit, match.getOffset(), match.getLength());
} else if (includeClassFiles) {
IClassFile cf = (IClassFile) element.getAncestor(IJavaElement.CLASS_FILE);
if (cf != null && cf.getSourceRange() != null) {
location = JDTUtils.toLocation(cf, match.getOffset(), match.getLength());
}
}
if (location != null) {
locations.add(location);
}
}
}
}, monitor);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ public class Preferences {
* Specifies the folder path to the JDK .
*/
public static final String JAVA_HOME = "java.home";
/**
* Preference key used to include getter, setter and builder/constructor when
* finding references.
*/
public static final String JAVA_REFERENCES_INCLUDE_GETTERSETTER = "java.references.includeGetterSetter";
/**
* Specifies Java Execution Environments.
*/
Expand Down Expand Up @@ -434,6 +439,7 @@ public class Preferences {
private boolean generateToStringListArrayContents;
private int generateToStringLimitElements;
private List<String> preferredContentProviderIds;
private boolean includeGetterSetter;

private String mavenUserSettings;

Expand Down Expand Up @@ -630,6 +636,7 @@ public Preferences() {
staticImportOnDemandThreshold = IMPORTS_STATIC_ONDEMANDTHRESHOLD_DEFAULT;
referencedLibraries = JAVA_PROJECT_REFERENCED_LIBRARIES_DEFAULT;
resourceFilters = JAVA_RESOURCE_FILTERS_DEFAULT;
includeGetterSetter = false;
}

/**
Expand Down Expand Up @@ -874,7 +881,8 @@ public static Preferences createFrom(Map<String, Object> configuration) {
prefs.setFileHeaderTemplate(fileHeader);
List<String> typeComment = getList(configuration, JAVA_TEMPLATES_TYPECOMMENT);
prefs.setTypeCommentTemplate(typeComment);

boolean includeGetterSetter = getBoolean(configuration, JAVA_REFERENCES_INCLUDE_GETTERSETTER, false);
prefs.setIncludeGetterSetter(includeGetterSetter);
return prefs;
}

Expand Down Expand Up @@ -1461,4 +1469,13 @@ public Preferences setTypeCommentTemplate(List<String> typeCommentTemplate) {
this.typeCommentTemplate = typeCommentTemplate;
return this;
}

public Preferences setIncludeGetterSetter(boolean includeGetterSetter) {
this.includeGetterSetter = includeGetterSetter;
return this;
}

public boolean isIncludeGetterSetter() {
return this.includeGetterSetter;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package org.ref;

public class Apple {
private String name;

public Apple(String name) {
this.name = name;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public static <T> AppleBuilder<T> builder() {
return new AppleBuilder<T>();
}

public static class AppleBuilder<T> {
private String name;

private AppleBuilder() {
}

public AppleBuilder<T> name(String name) {
this.name = name;
return this;
}

@java.lang.Override
public String toString() {
return "AppleBuilder(name = " + name + ")";
}

public Apple build() {
return new Apple(name);
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.ref;

public class Test {
public static void main(String[] args) {
Apple apple = Apple.builder().name("Hello World!").build();
System.out.println(apple.getName());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
import org.eclipse.jdt.ls.core.internal.WorkspaceHelper;
import org.eclipse.jdt.ls.core.internal.managers.AbstractProjectsManagerBasedTest;
import org.eclipse.jdt.ls.core.internal.preferences.PreferenceManager;
import org.eclipse.jdt.ls.core.internal.preferences.Preferences;
import org.eclipse.lsp4j.Location;
import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.ReferenceContext;
Expand All @@ -49,7 +48,7 @@ public void setup() throws Exception{
importProjects("eclipse/hello");
project = WorkspaceHelper.getProject("hello");
preferenceManager = mock(PreferenceManager.class);
when(preferenceManager.getPreferences()).thenReturn(new Preferences());
when(preferenceManager.getPreferences()).thenReturn(preferences);
handler = new ReferencesHandler(preferenceManager);
}

Expand Down Expand Up @@ -81,4 +80,32 @@ public void testReference(){
assertEquals(refereeUri, l.getUri());
}

@Test
public void testIncludeGetterSetter() {
boolean includeGetterSetter = preferenceManager.getPreferences().isIncludeGetterSetter();
try {
URI uri = project.getFile("src/org/ref/Apple.java").getRawLocationURI();
String fileURI = ResourceUtils.fixURI(uri);
ReferenceParams param = new ReferenceParams();
param.setPosition(new Position(3, 18));
param.setContext(new ReferenceContext(true));
param.setTextDocument(new TextDocumentIdentifier(fileURI));
List<Location> references = handler.findReferences(param, monitor);
assertNotNull("findReferences should not return null", references);
assertEquals(3, references.size());
preferenceManager.getPreferences().setIncludeGetterSetter(true);
references = handler.findReferences(param, monitor);
assertNotNull("findReferences should not return null", references);
assertEquals(5, references.size());
Location l = references.get(0);
String refereeUri = ResourceUtils.fixURI(project.getFile("src/org/ref/Apple.java").getRawLocationURI());
assertEquals(refereeUri, l.getUri());
l = references.get(4);
refereeUri = ResourceUtils.fixURI(project.getFile("src/org/ref/Test.java").getRawLocationURI());
assertEquals(refereeUri, l.getUri());
} finally {
preferenceManager.getPreferences().setIncludeGetterSetter(includeGetterSetter);
}
}

}

0 comments on commit ece3235

Please sign in to comment.