Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

show generate toString()/hashCode() and equals() quick fixes on demand #2213

Merged
merged 2 commits into from
Sep 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@

package org.eclipse.jdt.ls.core.internal.handlers;

import java.util.stream.Stream;

import org.apache.commons.lang3.StringUtils;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
Expand All @@ -38,14 +36,15 @@
import org.eclipse.jdt.ls.core.internal.corrections.DiagnosticsHelper;
import org.eclipse.jdt.ls.core.internal.handlers.JdtDomModels.LspVariableBinding;
import org.eclipse.jdt.ls.core.internal.preferences.Preferences;
import org.eclipse.jdt.ls.core.internal.text.correction.CodeActionUtility;
import org.eclipse.jdt.ls.core.internal.text.correction.SourceAssistProcessor;
import org.eclipse.lsp4j.CodeActionParams;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.WorkspaceEdit;
import org.eclipse.text.edits.TextEdit;

public class GenerateToStringHandler {
private static final String METHODNAME_TOSTRING = "toString";
public static final String METHODNAME_TOSTRING = "toString";
public static final String DEFAULT_TEMPLATE = "${object.className} [${member.name()}=${member.value}, ${otherMembers}]";

// For test purpose
Expand Down Expand Up @@ -79,7 +78,7 @@ public static CheckToStringResponse checkToStringStatus(IType type, IProgressMon
if (typeBinding != null) {
response.type = type.getTypeQualifiedName();
response.fields = JdtDomModels.getDeclaredFields(typeBinding, false);
response.exists = Stream.of(typeBinding.getDeclaredMethods()).anyMatch(method -> method.getName().equals(METHODNAME_TOSTRING) && method.getParameterTypes().length == 0);
response.exists = CodeActionUtility.hasMethod(type, METHODNAME_TOSTRING);
}
} catch (JavaModelException e) {
JavaLanguageServerPlugin.logException("Failed to check toString status", e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@
import org.eclipse.text.edits.TextEdit;

public class HashCodeEqualsHandler {
private static final String METHODNAME_HASH_CODE = "hashCode";
private static final String METHODNAME_EQUALS = "equals";
public static final String METHODNAME_HASH_CODE = "hashCode";
public static final String METHODNAME_EQUALS = "equals";

// For test purpose
public static CheckHashCodeEqualsResponse checkHashCodeEqualsStatus(CodeActionParams params) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Stream;

import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
import org.eclipse.jdt.core.dom.BodyDeclaration;
Expand Down Expand Up @@ -128,4 +131,32 @@ public static String getTypeName(AbstractTypeDeclaration node) {
}
return null;
}

public static boolean hasMethod(IType type, String methodName, Class... parameterTypes) {
if (type == null) {
return false;
}
try {
return Stream.of(type.getMethods()).anyMatch(method -> {
if (!method.getElementName().equals(methodName)) {
return false;
}
if (method.getParameterTypes().length != parameterTypes.length) {
return false;
}
String[] parameterTypeNames = method.getParameterTypes();
if (parameterTypes.length != parameterTypeNames.length) {
return false;
}
for (int i = 0; i < parameterTypeNames.length; i++) {
if (parameterTypes[i].getName().equals(parameterTypeNames[i])) {
return false;
}
}
return true;
});
} catch (JavaModelException e) {
return false;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
import org.eclipse.jdt.ls.core.internal.handlers.GenerateConstructorsHandler.CheckConstructorsResponse;
import org.eclipse.jdt.ls.core.internal.handlers.GenerateDelegateMethodsHandler;
import org.eclipse.jdt.ls.core.internal.handlers.GenerateToStringHandler;
import org.eclipse.jdt.ls.core.internal.handlers.HashCodeEqualsHandler;
import org.eclipse.jdt.ls.core.internal.handlers.JdtDomModels.LspVariableBinding;
import org.eclipse.jdt.ls.core.internal.handlers.OrganizeImportsHandler;
import org.eclipse.jdt.ls.core.internal.handlers.CodeActionHandler.CodeActionData;
Expand Down Expand Up @@ -180,10 +181,11 @@ public List<Either<Command, CodeAction>> getSourceActionCommands(CodeActionParam
JavaLanguageServerPlugin.logException("Failed to generate Getter and Setter source action", e);
}

boolean hashCodeAndEqualsExists = CodeActionUtility.hasMethod(type, HashCodeEqualsHandler.METHODNAME_HASH_CODE) && CodeActionUtility.hasMethod(type, HashCodeEqualsHandler.METHODNAME_EQUALS, Object.class);
// Generate hashCode() and equals()
if (supportsHashCodeEquals(context, type, monitor)) {
// Generate QuickAssist
if (isInTypeDeclaration) {
if (isInTypeDeclaration && !hashCodeAndEqualsExists) {
Optional<Either<Command, CodeAction>> quickAssistHashCodeEquals = getHashCodeEqualsAction(params, JavaCodeActionKind.QUICK_ASSIST);
addSourceActionCommand($, params.getContext(), quickAssistHashCodeEquals);
}
Expand All @@ -194,6 +196,7 @@ public List<Either<Command, CodeAction>> getSourceActionCommands(CodeActionParam

}

boolean toStringExists = CodeActionUtility.hasMethod(type, GenerateToStringHandler.METHODNAME_TOSTRING);
// Generate toString()
if (supportsGenerateToString(type)) {
boolean nonStaticFields = true;
Expand All @@ -204,7 +207,7 @@ public List<Either<Command, CodeAction>> getSourceActionCommands(CodeActionParam
}
if (nonStaticFields) {
// Generate QuickAssist
if (isInTypeDeclaration) {
if (isInTypeDeclaration && !toStringExists) {
Optional<Either<Command, CodeAction>> generateToStringQuickAssist = getGenerateToStringAction(params, JavaCodeActionKind.QUICK_ASSIST);
addSourceActionCommand($, params.getContext(), generateToStringQuickAssist);
}
Expand All @@ -218,7 +221,7 @@ public List<Either<Command, CodeAction>> getSourceActionCommands(CodeActionParam
return convertToWorkspaceEdit(cu, edit);
};
// Generate QuickAssist
if (isInTypeDeclaration) {
if (isInTypeDeclaration && !toStringExists) {
Optional<Either<Command, CodeAction>> generateToStringQuickAssist = getCodeActionFromProposal(params.getContext(), context.getCompilationUnit(), ActionMessages.GenerateToStringAction_label,
JavaCodeActionKind.QUICK_ASSIST, generateToStringProposal, CodeActionComparator.GENERATE_TOSTRING_PRIORITY);
addSourceActionCommand($, params.getContext(), generateToStringQuickAssist);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,4 +177,23 @@ public void testGenerateToStringQuickAssist_emptyFields() throws JavaModelExcept
quickAssistActions = CodeActionHandlerTest.findActions(codeActions, JavaCodeActionKind.QUICK_ASSIST);
Assert.assertFalse(CodeActionHandlerTest.commandExists(quickAssistActions, CodeActionHandler.COMMAND_ID_APPLY_EDIT, ActionMessages.GenerateToStringAction_label));
}

@Test
public void testNoGenerateToStringQuickAssist() throws JavaModelException {
//@formatter:off
ICompilationUnit unit = fPackageP.createCompilationUnit("A.java", "package p;\r\n" +
"\r\n" +
"public class A {\r\n" +
" String name;\r\n" +
" public String toString() {\r\n" +
" return this.name;\r\n" +
" }\r\n" +
"}"
, true, null);
//@formatter:on
CodeActionParams params = CodeActionUtil.constructCodeActionParams(unit, "A");
List<Either<Command, CodeAction>> codeActions = server.codeAction(params).join();
Assert.assertNotNull(codeActions);
Assert.assertNull(CodeActionHandlerTest.findAction(codeActions, JavaCodeActionKind.QUICK_ASSIST, "Generate toString()..."));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -147,4 +147,25 @@ public void testHashCodeEqualsQuickAssist() throws JavaModelException {
Assert.assertFalse(quickAssistActions.isEmpty());
Assert.assertFalse(CodeActionHandlerTest.commandExists(quickAssistActions, SourceAssistProcessor.COMMAND_ID_ACTION_HASHCODEEQUALSPROMPT));
}

@Test
public void testNoHashCodeEqualsQuickAssist() throws JavaModelException {
//@formatter:off
ICompilationUnit unit = fPackageP.createCompilationUnit("A.java", "package p;\r\n" +
"\r\n" +
"public class A {\r\n" +
" String name;\r\n" +
" public int hashCode() {\r\n" +
" }\r\n" +
" public boolean equals(Object a) {\r\n" +
" return true;\r\n" +
" }\r\n" +
"}"
, true, null);
//@formatter:on
CodeActionParams params = CodeActionUtil.constructCodeActionParams(unit, "A");
List<Either<Command, CodeAction>> codeActions = server.codeAction(params).join();
Assert.assertNotNull(codeActions);
Assert.assertNull(CodeActionHandlerTest.findAction(codeActions, JavaCodeActionKind.QUICK_ASSIST, "Generate hashCode() and equals()..."));
}
}