diff --git a/src/main/java/de/plushnikov/intellij/plugin/action/delombok/BaseDelombokHandler.java b/src/main/java/de/plushnikov/intellij/plugin/action/delombok/BaseDelombokHandler.java index 0e121c848..c736d14bc 100644 --- a/src/main/java/de/plushnikov/intellij/plugin/action/delombok/BaseDelombokHandler.java +++ b/src/main/java/de/plushnikov/intellij/plugin/action/delombok/BaseDelombokHandler.java @@ -3,25 +3,7 @@ import com.intellij.openapi.command.undo.UndoUtil; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.psi.JavaPsiFacade; -import com.intellij.psi.PsiAnnotation; -import com.intellij.psi.PsiClass; -import com.intellij.psi.PsiClassType; -import com.intellij.psi.PsiCodeBlock; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiElementFactory; -import com.intellij.psi.PsiField; -import com.intellij.psi.PsiFile; -import com.intellij.psi.PsiJavaCodeReferenceElement; -import com.intellij.psi.PsiJavaFile; -import com.intellij.psi.PsiMethod; -import com.intellij.psi.PsiModifier; -import com.intellij.psi.PsiModifierList; -import com.intellij.psi.PsiNameValuePair; -import com.intellij.psi.PsiParameter; -import com.intellij.psi.PsiType; -import com.intellij.psi.PsiTypeParameterList; -import com.intellij.psi.PsiTypeParameterListOwner; +import com.intellij.psi.*; import com.intellij.psi.codeStyle.CodeStyleManager; import com.intellij.psi.codeStyle.JavaCodeStyleManager; import de.plushnikov.intellij.plugin.processor.AbstractProcessor; @@ -171,14 +153,16 @@ private PsiMethod rebuildMethod(@NotNull Project project, @NotNull PsiMethod fro for (PsiParameter parameter : fromMethod.getParameterList().getParameters()) { PsiParameter param = elementFactory.createParameter(parameter.getName(), parameter.getType()); - if (parameter.getModifierList() != null) { + final PsiModifierList parameterModifierList = parameter.getModifierList(); + if (parameterModifierList != null) { PsiModifierList modifierList = param.getModifierList(); - for (PsiAnnotation originalAnnotation : parameter.getModifierList().getAnnotations()) { + for (PsiAnnotation originalAnnotation : parameterModifierList.getAnnotations()) { final PsiAnnotation annotation = modifierList.addAnnotation(originalAnnotation.getQualifiedName()); for (PsiNameValuePair nameValuePair : originalAnnotation.getParameterList().getAttributes()) { annotation.setDeclaredAttributeValue(nameValuePair.getName(), nameValuePair.getValue()); } } + modifierList.setModifierProperty(PsiModifier.FINAL, parameterModifierList.hasModifierProperty(PsiModifier.FINAL)); } resultMethod.getParameterList().add(param); } diff --git a/src/main/java/de/plushnikov/intellij/plugin/processor/AbstractProcessor.java b/src/main/java/de/plushnikov/intellij/plugin/processor/AbstractProcessor.java index 6605930cd..071515167 100644 --- a/src/main/java/de/plushnikov/intellij/plugin/processor/AbstractProcessor.java +++ b/src/main/java/de/plushnikov/intellij/plugin/processor/AbstractProcessor.java @@ -7,12 +7,9 @@ import com.intellij.psi.PsiField; import com.intellij.psi.PsiModifierList; import com.intellij.psi.PsiModifierListOwner; -import com.intellij.psi.PsiType; import com.intellij.util.ArrayUtil; import de.plushnikov.intellij.plugin.lombokconfig.ConfigDiscovery; import de.plushnikov.intellij.plugin.lombokconfig.ConfigKey; -import de.plushnikov.intellij.plugin.processor.field.AccessorsInfo; -import de.plushnikov.intellij.plugin.thirdparty.LombokUtils; import de.plushnikov.intellij.plugin.util.LombokProcessorUtil; import de.plushnikov.intellij.plugin.util.PsiAnnotationSearchUtil; import de.plushnikov.intellij.plugin.util.PsiAnnotationUtil; @@ -90,15 +87,6 @@ public List process(@NotNull PsiClass psiClass) { @NotNull public abstract Collection collectProcessedAnnotations(@NotNull PsiClass psiClass); - protected String getGetterName(final @NotNull PsiField psiField) { - final AccessorsInfo accessorsInfo = AccessorsInfo.build(psiField); - - final String psiFieldName = psiField.getName(); - final boolean isBoolean = PsiType.BOOLEAN.equals(psiField.getType()); - - return LombokUtils.toGetterName(accessorsInfo, psiFieldName, isBoolean); - } - protected void filterToleratedElements(@NotNull Collection definedMethods) { definedMethods.removeIf(definedMethod -> PsiAnnotationSearchUtil.isAnnotatedWith(definedMethod, Tolerate.class)); } diff --git a/src/main/java/de/plushnikov/intellij/plugin/processor/clazz/AbstractClassProcessor.java b/src/main/java/de/plushnikov/intellij/plugin/processor/clazz/AbstractClassProcessor.java index fa0f5e71e..9fd909bd2 100644 --- a/src/main/java/de/plushnikov/intellij/plugin/processor/clazz/AbstractClassProcessor.java +++ b/src/main/java/de/plushnikov/intellij/plugin/processor/clazz/AbstractClassProcessor.java @@ -18,24 +18,17 @@ import de.plushnikov.intellij.plugin.processor.clazz.constructor.AbstractConstructorClassProcessor; import de.plushnikov.intellij.plugin.quickfix.PsiQuickFixFactory; import de.plushnikov.intellij.plugin.thirdparty.LombokUtils; -import de.plushnikov.intellij.plugin.util.LombokProcessorUtil; import de.plushnikov.intellij.plugin.util.PsiAnnotationSearchUtil; -import de.plushnikov.intellij.plugin.util.PsiAnnotationUtil; import de.plushnikov.intellij.plugin.util.PsiClassUtil; -import de.plushnikov.intellij.plugin.util.PsiMethodUtil; import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.Getter; import lombok.NoArgsConstructor; import lombok.RequiredArgsConstructor; -import lombok.Value; import org.jetbrains.annotations.NotNull; import java.lang.annotation.Annotation; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.HashSet; import java.util.List; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -66,7 +59,7 @@ public List process(@NotNull PsiClass psiClass) { PsiAnnotation psiAnnotation = PsiAnnotationSearchUtil.findAnnotation(psiClass, getSupportedAnnotationClasses()); if (null != psiAnnotation) { if (validate(psiAnnotation, psiClass, ProblemEmptyBuilder.getInstance())) { - result = new ArrayList(); + result = new ArrayList<>(); generatePsiElements(psiClass, psiAnnotation, result); } } @@ -111,7 +104,7 @@ public Collection verifyAnnotation(@NotNull PsiAnnotation psiAnno protected abstract void generatePsiElements(@NotNull PsiClass psiClass, @NotNull PsiAnnotation psiAnnotation, @NotNull List target); - protected void validateOfParam(PsiClass psiClass, ProblemBuilder builder, PsiAnnotation psiAnnotation, Collection ofProperty) { + void validateOfParam(PsiClass psiClass, ProblemBuilder builder, PsiAnnotation psiAnnotation, Collection ofProperty) { for (String fieldName : ofProperty) { if (!StringUtil.isEmptyOrSpaces(fieldName)) { PsiField fieldByName = psiClass.findFieldByName(fieldName, false); @@ -124,7 +117,7 @@ protected void validateOfParam(PsiClass psiClass, ProblemBuilder builder, PsiAnn } } - protected void validateExcludeParam(PsiClass psiClass, ProblemBuilder builder, PsiAnnotation psiAnnotation, Collection excludeProperty) { + void validateExcludeParam(PsiClass psiClass, ProblemBuilder builder, PsiAnnotation psiAnnotation, Collection excludeProperty) { for (String fieldName : excludeProperty) { if (!StringUtil.isEmptyOrSpaces(fieldName)) { PsiField fieldByName = psiClass.findFieldByName(fieldName, false); @@ -146,88 +139,13 @@ protected void validateExcludeParam(PsiClass psiClass, ProblemBuilder builder, P private String calcNewPropertyValue(Collection allProperties, String fieldName) { String result = null; if (!allProperties.isEmpty() && (allProperties.size() > 1 || !allProperties.contains(fieldName))) { - result = allProperties.stream().filter(((Predicate) fieldName::equals).negate()).collect(Collectors.joining("\",\"", "{\"", "\"}")); + result = allProperties.stream().filter(((Predicate) fieldName::equals).negate()) + .collect(Collectors.joining("\",\"", "{\"", "\"}")); } return result; } - protected Collection filterFields(@NotNull PsiClass psiClass, @NotNull PsiAnnotation psiAnnotation, boolean filterTransient) { - final boolean explicitOf = PsiAnnotationUtil.hasDeclaredProperty(psiAnnotation, "of"); - final boolean explicitExclude = PsiAnnotationUtil.hasDeclaredProperty(psiAnnotation, "exclude"); - final boolean onlyExplicitlyIncluded = PsiAnnotationUtil.getBooleanAnnotationValue(psiAnnotation, "onlyExplicitlyIncluded", false); - - final String annotationFQN = psiAnnotation.getQualifiedName(); - final String annotationIncludeFQN = annotationFQN + ".Include"; - final String annotationExcludeFQN = annotationFQN + ".Exclude"; - - //Having both exclude and of generates a warning; the exclude parameter will be ignored in that case. - final Collection ofProperty; - final Collection excludeProperty; - if (!explicitOf) { - excludeProperty = makeSet(PsiAnnotationUtil.getAnnotationValues(psiAnnotation, "exclude", String.class)); - ofProperty = Collections.emptyList(); - } else { - ofProperty = makeSet(PsiAnnotationUtil.getAnnotationValues(psiAnnotation, "of", String.class)); - excludeProperty = Collections.emptyList(); - } - - final Collection psiFields = PsiClassUtil.collectClassFieldsIntern(psiClass); - - final Collection result = new ArrayList(psiFields.size()); - - for (PsiField classField : psiFields) { - if (!PsiAnnotationSearchUtil.isAnnotatedWith(classField, annotationIncludeFQN)) { - if (!onlyExplicitlyIncluded) { - if (classField.hasModifierProperty(PsiModifier.STATIC) || (filterTransient && classField.hasModifierProperty(PsiModifier.TRANSIENT))) { - continue; - } - final String fieldName = classField.getName(); - if (null == fieldName) { - continue; - } - if (explicitExclude && excludeProperty.contains(fieldName)) { - continue; - } - if (explicitOf && !ofProperty.contains(fieldName)) { - continue; - } - - if (fieldName.startsWith(LombokUtils.LOMBOK_INTERN_FIELD_MARKER) && !ofProperty.contains(fieldName)) { - continue; - } - - if (PsiAnnotationSearchUtil.isAnnotatedWith(classField, annotationExcludeFQN)) { - continue; - } - result.add(classField); - } - } else { - result.add(classField); - } - } - return result; - } - - protected String buildAttributeNameString(boolean doNotUseGetters, @NotNull PsiField classField, @NotNull PsiClass psiClass) { - final String fieldName = classField.getName(); - if (doNotUseGetters) { - return fieldName; - } else { - final String getterName = getGetterName(classField); - - final boolean hasGetter; - if (PsiAnnotationSearchUtil.isAnnotatedWith(psiClass, Data.class, Value.class, lombok.experimental.Value.class, Getter.class)) { - final PsiAnnotation getterLombokAnnotation = PsiAnnotationSearchUtil.findAnnotation(psiClass, Getter.class); - hasGetter = null == getterLombokAnnotation || null != LombokProcessorUtil.getMethodModifier(getterLombokAnnotation); - } else { - hasGetter = PsiMethodUtil.hasMethodByName(PsiClassUtil.collectClassMethodsIntern(psiClass), getterName); - } - - return hasGetter ? getterName + "()" : fieldName; - } - } - - protected boolean shouldGenerateNoArgsConstructor(@NotNull PsiClass psiClass, @NotNull AbstractConstructorClassProcessor argsConstructorProcessor) { + boolean shouldGenerateNoArgsConstructor(@NotNull PsiClass psiClass, @NotNull AbstractConstructorClassProcessor argsConstructorProcessor) { boolean result = ConfigDiscovery.getInstance().getBooleanLombokConfigProperty(ConfigKey.NO_ARGS_CONSTRUCTOR_EXTRA_PRIVATE, psiClass); if (result) { result = !PsiClassUtil.hasSuperClass(psiClass); @@ -244,10 +162,4 @@ protected boolean shouldGenerateNoArgsConstructor(@NotNull PsiClass psiClass, @N return result; } - private Collection makeSet(@NotNull Collection exclude) { - if (exclude.isEmpty()) { - return Collections.emptySet(); - } - return new HashSet(exclude); - } } diff --git a/src/main/java/de/plushnikov/intellij/plugin/processor/clazz/EqualsAndHashCodeProcessor.java b/src/main/java/de/plushnikov/intellij/plugin/processor/clazz/EqualsAndHashCodeProcessor.java index 6697ecdd1..d8bebf60f 100644 --- a/src/main/java/de/plushnikov/intellij/plugin/processor/clazz/EqualsAndHashCodeProcessor.java +++ b/src/main/java/de/plushnikov/intellij/plugin/processor/clazz/EqualsAndHashCodeProcessor.java @@ -1,6 +1,7 @@ package de.plushnikov.intellij.plugin.processor.clazz; import com.intellij.codeInspection.LocalQuickFix; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.util.PsiTypesUtil; @@ -8,6 +9,8 @@ import de.plushnikov.intellij.plugin.lombokconfig.ConfigKey; import de.plushnikov.intellij.plugin.problem.ProblemBuilder; import de.plushnikov.intellij.plugin.processor.LombokPsiElementUsage; +import de.plushnikov.intellij.plugin.processor.handler.EqualsAndHashCodeToStringHandler; +import de.plushnikov.intellij.plugin.processor.handler.EqualsAndHashCodeToStringHandler.MemberInfo; import de.plushnikov.intellij.plugin.psi.LombokLightMethodBuilder; import de.plushnikov.intellij.plugin.quickfix.PsiQuickFixFactory; import de.plushnikov.intellij.plugin.util.PsiAnnotationSearchUtil; @@ -36,8 +39,13 @@ public class EqualsAndHashCodeProcessor extends AbstractClassProcessor { private static final String HASH_CODE_METHOD_NAME = "hashCode"; private static final String CAN_EQUAL_METHOD_NAME = "canEqual"; + private static final String INCLUDE_ANNOTATION_METHOD = "replaces"; + + private final EqualsAndHashCodeToStringHandler handler; + public EqualsAndHashCodeProcessor() { super(PsiMethod.class, EqualsAndHashCode.class); + handler = new EqualsAndHashCodeToStringHandler(); } @Override @@ -126,16 +134,19 @@ protected Collection createEqualAndHashCode(@NotNull PsiClass psiClas return Collections.emptyList(); } + final Collection memberInfos = handler.filterFields(psiClass, psiAnnotation, true, INCLUDE_ANNOTATION_METHOD); + final boolean shouldGenerateCanEqual = shouldGenerateCanEqual(psiClass); Collection result = new ArrayList(3); - result.add(createEqualsMethod(psiClass, psiAnnotation, shouldGenerateCanEqual)); - result.add(createHashCodeMethod(psiClass, psiAnnotation)); + result.add(createEqualsMethod(psiClass, psiAnnotation, shouldGenerateCanEqual, memberInfos)); final Collection classMethods = PsiClassUtil.collectClassMethodsIntern(psiClass); if (shouldGenerateCanEqual && !PsiMethodUtil.hasMethodByName(classMethods, CAN_EQUAL_METHOD_NAME)) { result.add(createCanEqualMethod(psiClass, psiAnnotation)); } + + result.add(createHashCodeMethod(psiClass, psiAnnotation, memberInfos)); return result; } @@ -152,7 +163,7 @@ private boolean shouldGenerateCanEqual(@NotNull PsiClass psiClass) { } @NotNull - private PsiMethod createEqualsMethod(@NotNull PsiClass psiClass, @NotNull PsiAnnotation psiAnnotation, boolean hasCanEqualMethod) { + private PsiMethod createEqualsMethod(@NotNull PsiClass psiClass, @NotNull PsiAnnotation psiAnnotation, boolean hasCanEqualMethod, Collection memberInfos) { final PsiManager psiManager = psiClass.getManager(); return new LombokLightMethodBuilder(psiManager, EQUALS_METHOD_NAME) @@ -160,18 +171,18 @@ private PsiMethod createEqualsMethod(@NotNull PsiClass psiClass, @NotNull PsiAnn .withMethodReturnType(PsiType.BOOLEAN) .withContainingClass(psiClass) .withNavigationElement(psiAnnotation) - .withParameter("o", PsiType.getJavaLangObject(psiManager, GlobalSearchScope.allScope(psiClass.getProject()))) - .withBody(createEqualsCodeBlock(psiClass, psiAnnotation, hasCanEqualMethod)); + .withFinalParameter("o", PsiType.getJavaLangObject(psiManager, GlobalSearchScope.allScope(psiClass.getProject()))) + .withBody(createEqualsCodeBlock(psiClass, psiAnnotation, hasCanEqualMethod, memberInfos)); } @NotNull - private PsiCodeBlock createEqualsCodeBlock(@NotNull PsiClass psiClass, @NotNull PsiAnnotation psiAnnotation, boolean hasCanEqualMethod) { - final String blockText = createEqualsBlockString(psiClass, psiAnnotation, hasCanEqualMethod); + private PsiCodeBlock createEqualsCodeBlock(@NotNull PsiClass psiClass, @NotNull PsiAnnotation psiAnnotation, boolean hasCanEqualMethod, Collection memberInfos) { + final String blockText = createEqualsBlockString(psiClass, psiAnnotation, hasCanEqualMethod, memberInfos); return PsiMethodUtil.createCodeBlockFromText(blockText, psiClass); } @NotNull - private PsiMethod createHashCodeMethod(@NotNull PsiClass psiClass, @NotNull PsiAnnotation psiAnnotation) { + private PsiMethod createHashCodeMethod(@NotNull PsiClass psiClass, @NotNull PsiAnnotation psiAnnotation, Collection memberInfos) { final PsiManager psiManager = psiClass.getManager(); return new LombokLightMethodBuilder(psiManager, HASH_CODE_METHOD_NAME) @@ -179,12 +190,12 @@ private PsiMethod createHashCodeMethod(@NotNull PsiClass psiClass, @NotNull PsiA .withMethodReturnType(PsiType.INT) .withContainingClass(psiClass) .withNavigationElement(psiAnnotation) - .withBody(createHashCodeBlock(psiClass, psiAnnotation)); + .withBody(createHashCodeBlock(psiClass, psiAnnotation, memberInfos)); } @NotNull - private PsiCodeBlock createHashCodeBlock(@NotNull PsiClass psiClass, @NotNull PsiAnnotation psiAnnotation) { - final String blockText = createHashcodeBlockString(psiClass, psiAnnotation); + private PsiCodeBlock createHashCodeBlock(@NotNull PsiClass psiClass, @NotNull PsiAnnotation psiAnnotation, Collection memberInfos) { + final String blockText = createHashcodeBlockString(psiClass, psiAnnotation, memberInfos); return PsiMethodUtil.createCodeBlockFromText(blockText, psiClass); } @@ -197,7 +208,7 @@ private PsiMethod createCanEqualMethod(@NotNull PsiClass psiClass, @NotNull PsiA .withMethodReturnType(PsiType.BOOLEAN) .withContainingClass(psiClass) .withNavigationElement(psiAnnotation) - .withParameter("other", PsiType.getJavaLangObject(psiManager, GlobalSearchScope.allScope(psiClass.getProject()))) + .withFinalParameter("other", PsiType.getJavaLangObject(psiManager, GlobalSearchScope.allScope(psiClass.getProject()))) .withBody(createCanEqualCodeBlock(psiClass)); } @@ -207,7 +218,7 @@ private PsiCodeBlock createCanEqualCodeBlock(@NotNull PsiClass psiClass) { return PsiMethodUtil.createCodeBlockFromText(blockText, psiClass); } - private String createEqualsBlockString(@NotNull PsiClass psiClass, @NotNull PsiAnnotation psiAnnotation, boolean hasCanEqualMethod) { + private String createEqualsBlockString(@NotNull PsiClass psiClass, @NotNull PsiAnnotation psiAnnotation, boolean hasCanEqualMethod, Collection memberInfos) { final boolean callSuper = readCallSuperAnnotationOrConfigProperty(psiAnnotation, psiClass); final boolean doNotUseGetters = readAnnotationOrConfigProperty(psiAnnotation, psiClass, "doNotUseGetters", ConfigKey.EQUALSANDHASHCODE_DO_NOT_USE_GETTERS); @@ -227,33 +238,31 @@ private String createEqualsBlockString(@NotNull PsiClass psiClass, @NotNull PsiA builder.append("if (!super.equals(o)) return false;\n"); } - final Collection psiFields = filterFields(psiClass, psiAnnotation, true); - for (PsiField classField : psiFields) { - final String fieldName = classField.getName(); - - final String fieldAccessor = buildAttributeNameString(doNotUseGetters, classField, psiClass); + for (MemberInfo memberInfo : memberInfos) { + final String memberAccessor = handler.getMemberAccessorName(memberInfo, doNotUseGetters, psiClass); - final PsiType classFieldType = classField.getType(); - if (classFieldType instanceof PsiPrimitiveType) { - if (PsiType.FLOAT.equals(classFieldType)) { - builder.append("if (java.lang.Float.compare(this.").append(fieldAccessor).append(", other.").append(fieldAccessor).append(") != 0) return false;\n"); - } else if (PsiType.DOUBLE.equals(classFieldType)) { - builder.append("if (java.lang.Double.compare(this.").append(fieldAccessor).append(", other.").append(fieldAccessor).append(") != 0) return false;\n"); + final PsiType memberType = memberInfo.getType(); + if (memberType instanceof PsiPrimitiveType) { + if (PsiType.FLOAT.equals(memberType)) { + builder.append("if (java.lang.Float.compare(this.").append(memberAccessor).append(", other.").append(memberAccessor).append(") != 0) return false;\n"); + } else if (PsiType.DOUBLE.equals(memberType)) { + builder.append("if (java.lang.Double.compare(this.").append(memberAccessor).append(", other.").append(memberAccessor).append(") != 0) return false;\n"); } else { - builder.append("if (this.").append(fieldAccessor).append(" != other.").append(fieldAccessor).append(") return false;\n"); + builder.append("if (this.").append(memberAccessor).append(" != other.").append(memberAccessor).append(") return false;\n"); } - } else if (classFieldType instanceof PsiArrayType) { - final PsiType componentType = ((PsiArrayType) classFieldType).getComponentType(); + } else if (memberType instanceof PsiArrayType) { + final PsiType componentType = ((PsiArrayType) memberType).getComponentType(); if (componentType instanceof PsiPrimitiveType) { - builder.append("if (!java.util.Arrays.equals(this.").append(fieldAccessor).append(", other.").append(fieldAccessor).append(")) return false;\n"); + builder.append("if (!java.util.Arrays.equals(this.").append(memberAccessor).append(", other.").append(memberAccessor).append(")) return false;\n"); } else { - builder.append("if (!java.util.Arrays.deepEquals(this.").append(fieldAccessor).append(", other.").append(fieldAccessor).append(")) return false;\n"); + builder.append("if (!java.util.Arrays.deepEquals(this.").append(memberAccessor).append(", other.").append(memberAccessor).append(")) return false;\n"); } } else { - builder.append("final java.lang.Object this$").append(fieldName).append(" = this.").append(fieldAccessor).append(";\n"); - builder.append("final java.lang.Object other$").append(fieldName).append(" = other.").append(fieldAccessor).append(";\n"); - builder.append("if (this$").append(fieldName).append(" == null ? other$").append(fieldName).append(" != null : !this$") - .append(fieldName).append(".equals(other$").append(fieldName).append(")) return false;\n"); + final String memberName = memberInfo.getName(); + builder.append("final java.lang.Object this$").append(memberName).append(" = this.").append(memberAccessor).append(";\n"); + builder.append("final java.lang.Object other$").append(memberName).append(" = other.").append(memberAccessor).append(";\n"); + builder.append("if (this$").append(memberName).append(" == null ? other$").append(memberName).append(" != null : !this$") + .append(memberName).append(".equals(other$").append(memberName).append(")) return false;\n"); } } builder.append("return true;\n"); @@ -265,15 +274,13 @@ private String createEqualsBlockString(@NotNull PsiClass psiClass, @NotNull PsiA private static final int PRIME_FOR_FALSE = 97; private static final int PRIME_FOR_NULL = 43; - private String createHashcodeBlockString(@NotNull PsiClass psiClass, @NotNull PsiAnnotation psiAnnotation) { + private String createHashcodeBlockString(@NotNull PsiClass psiClass, @NotNull PsiAnnotation psiAnnotation, Collection memberInfos) { final boolean callSuper = readCallSuperAnnotationOrConfigProperty(psiAnnotation, psiClass); final boolean doNotUseGetters = readAnnotationOrConfigProperty(psiAnnotation, psiClass, "doNotUseGetters", ConfigKey.EQUALSANDHASHCODE_DO_NOT_USE_GETTERS); final StringBuilder builder = new StringBuilder(); - final Collection psiFields = filterFields(psiClass, psiAnnotation, true); - - if (!psiFields.isEmpty()) { + if (!memberInfos.isEmpty()) { builder.append("final int PRIME = ").append(PRIME_FOR_HASHCODE).append(";\n"); } builder.append("int result = "); @@ -284,36 +291,35 @@ private String createHashcodeBlockString(@NotNull PsiClass psiClass, @NotNull Ps builder.append("1;\n"); } - for (PsiField classField : psiFields) { - final String fieldName = classField.getName(); - - final String fieldAccessor = buildAttributeNameString(doNotUseGetters, classField, psiClass); + for (MemberInfo memberInfo : memberInfos) { + final String memberAccessor = handler.getMemberAccessorName(memberInfo, doNotUseGetters, psiClass); + final String memberName = memberInfo.getMethod() == null ? memberInfo.getName() : "$" + memberInfo.getName(); - final PsiType classFieldType = classField.getType(); + final PsiType classFieldType = memberInfo.getType(); if (classFieldType instanceof PsiPrimitiveType) { if (PsiType.BOOLEAN.equals(classFieldType)) { - builder.append("result = result * PRIME + (this.").append(fieldAccessor).append(" ? ").append(PRIME_FOR_TRUE).append(" : ").append(PRIME_FOR_FALSE).append(");\n"); + builder.append("result = result * PRIME + (this.").append(memberAccessor).append(" ? ").append(PRIME_FOR_TRUE).append(" : ").append(PRIME_FOR_FALSE).append(");\n"); } else if (PsiType.LONG.equals(classFieldType)) { - builder.append("final long $").append(fieldName).append(" = this.").append(fieldAccessor).append(";\n"); - builder.append("result = result * PRIME + (int)($").append(fieldName).append(" >>> 32 ^ $").append(fieldName).append(");\n"); + builder.append("final long $").append(memberName).append(" = this.").append(memberAccessor).append(";\n"); + builder.append("result = result * PRIME + (int)($").append(memberName).append(" >>> 32 ^ $").append(memberName).append(");\n"); } else if (PsiType.FLOAT.equals(classFieldType)) { - builder.append("result = result * PRIME + java.lang.Float.floatToIntBits(this.").append(fieldAccessor).append(");\n"); + builder.append("result = result * PRIME + java.lang.Float.floatToIntBits(this.").append(memberAccessor).append(");\n"); } else if (PsiType.DOUBLE.equals(classFieldType)) { - builder.append("final long $").append(fieldName).append(" = java.lang.Double.doubleToLongBits(this.").append(fieldAccessor).append(");\n"); - builder.append("result = result * PRIME + (int)($").append(fieldName).append(" >>> 32 ^ $").append(fieldName).append(");\n"); + builder.append("final long $").append(memberName).append(" = java.lang.Double.doubleToLongBits(this.").append(memberAccessor).append(");\n"); + builder.append("result = result * PRIME + (int)($").append(memberName).append(" >>> 32 ^ $").append(memberName).append(");\n"); } else { - builder.append("result = result * PRIME + this.").append(fieldAccessor).append(";\n"); + builder.append("result = result * PRIME + this.").append(memberAccessor).append(";\n"); } } else if (classFieldType instanceof PsiArrayType) { final PsiType componentType = ((PsiArrayType) classFieldType).getComponentType(); if (componentType instanceof PsiPrimitiveType) { - builder.append("result = result * PRIME + java.util.Arrays.hashCode(this.").append(fieldAccessor).append(");\n"); + builder.append("result = result * PRIME + java.util.Arrays.hashCode(this.").append(memberAccessor).append(");\n"); } else { - builder.append("result = result * PRIME + java.util.Arrays.deepHashCode(this.").append(fieldAccessor).append(");\n"); + builder.append("result = result * PRIME + java.util.Arrays.deepHashCode(this.").append(memberAccessor).append(");\n"); } } else { - builder.append("final java.lang.Object $").append(fieldName).append(" = this.").append(fieldAccessor).append(";\n"); - builder.append("result = result * PRIME + ($").append(fieldName).append(" == null ? " + PRIME_FOR_NULL + " : $").append(fieldName).append(".hashCode());\n"); + builder.append("final java.lang.Object $").append(memberName).append(" = this.").append(memberAccessor).append(";\n"); + builder.append("result = result * PRIME + ($").append(memberName).append(" == null ? " + PRIME_FOR_NULL + " : $").append(memberName).append(".hashCode());\n"); } } builder.append("return result;\n"); @@ -325,7 +331,7 @@ private boolean readCallSuperAnnotationOrConfigProperty(@NotNull PsiAnnotation p final Boolean declaredAnnotationValue = PsiAnnotationUtil.getDeclaredBooleanAnnotationValue(psiAnnotation, "callSuper"); if (null == declaredAnnotationValue) { final String configProperty = ConfigDiscovery.getInstance().getStringLombokConfigProperty(ConfigKey.EQUALSANDHASHCODE_CALL_SUPER, psiClass); - result = "CALL".equalsIgnoreCase(configProperty); + result = PsiClassUtil.hasSuperClass(psiClass) && "CALL".equalsIgnoreCase(configProperty); } else { result = declaredAnnotationValue; } @@ -344,7 +350,9 @@ public Collection collectProcessedAnnotations(@NotNull PsiClass p public LombokPsiElementUsage checkFieldUsage(@NotNull PsiField psiField, @NotNull PsiAnnotation psiAnnotation) { final PsiClass containingClass = psiField.getContainingClass(); if (null != containingClass) { - if (PsiClassUtil.getNames(filterFields(containingClass, psiAnnotation, true)).contains(psiField.getName())) { + final String psiFieldName = StringUtil.notNullize(psiField.getName()); + if (handler.filterFields(containingClass, psiAnnotation, true, INCLUDE_ANNOTATION_METHOD).stream() + .map(MemberInfo::getName).anyMatch(psiFieldName::equals)) { return LombokPsiElementUsage.READ; } } diff --git a/src/main/java/de/plushnikov/intellij/plugin/processor/clazz/ToStringProcessor.java b/src/main/java/de/plushnikov/intellij/plugin/processor/clazz/ToStringProcessor.java index ce818d26a..b1dcdf2f8 100644 --- a/src/main/java/de/plushnikov/intellij/plugin/processor/clazz/ToStringProcessor.java +++ b/src/main/java/de/plushnikov/intellij/plugin/processor/clazz/ToStringProcessor.java @@ -1,10 +1,13 @@ package de.plushnikov.intellij.plugin.processor.clazz; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; import com.intellij.psi.search.GlobalSearchScope; import de.plushnikov.intellij.plugin.lombokconfig.ConfigKey; import de.plushnikov.intellij.plugin.problem.ProblemBuilder; import de.plushnikov.intellij.plugin.processor.LombokPsiElementUsage; +import de.plushnikov.intellij.plugin.processor.handler.EqualsAndHashCodeToStringHandler; +import de.plushnikov.intellij.plugin.processor.handler.EqualsAndHashCodeToStringHandler.MemberInfo; import de.plushnikov.intellij.plugin.psi.LombokLightMethodBuilder; import de.plushnikov.intellij.plugin.quickfix.PsiQuickFixFactory; import de.plushnikov.intellij.plugin.util.PsiAnnotationUtil; @@ -27,8 +30,15 @@ public class ToStringProcessor extends AbstractClassProcessor { public static final String METHOD_NAME = "toString"; + private static final String INCLUDE_ANNOTATION_METHOD = "name"; + private static final String INCLUDE_ANNOTATION_RANK = "rank"; + private static final String INCLUDE_ANNOTATION_SKIP_NULL = "skipNull"; + + private final EqualsAndHashCodeToStringHandler handler; + public ToStringProcessor() { super(PsiMethod.class, ToString.class); + handler = new EqualsAndHashCodeToStringHandler(); } @Override @@ -84,13 +94,13 @@ Collection createToStringMethod(@NotNull PsiClass psiClass, @NotNull return Collections.emptyList(); } - final Collection psiFields = filterFields(psiClass, psiAnnotation, false); - final PsiMethod stringMethod = createToStringMethod(psiClass, psiFields, psiAnnotation); + final Collection memberInfos = handler.filterFields(psiClass, psiAnnotation, false, INCLUDE_ANNOTATION_METHOD); + final PsiMethod stringMethod = createToStringMethod(psiClass, memberInfos, psiAnnotation); return Collections.singletonList(stringMethod); } @NotNull - public PsiMethod createToStringMethod(@NotNull PsiClass psiClass, @NotNull Collection psiFields, @NotNull PsiAnnotation psiAnnotation) { + public PsiMethod createToStringMethod(@NotNull PsiClass psiClass, @NotNull Collection memberInfos, @NotNull PsiAnnotation psiAnnotation) { final PsiManager psiManager = psiClass.getManager(); return new LombokLightMethodBuilder(psiManager, METHOD_NAME) @@ -98,13 +108,13 @@ public PsiMethod createToStringMethod(@NotNull PsiClass psiClass, @NotNull Colle .withContainingClass(psiClass) .withNavigationElement(psiAnnotation) .withModifier(PsiModifier.PUBLIC) - .withBody(createCodeBlock(psiClass, psiFields, psiAnnotation)); + .withBody(createCodeBlock(psiClass, memberInfos, psiAnnotation)); } @NotNull - private PsiCodeBlock createCodeBlock(@NotNull PsiClass psiClass, @NotNull Collection psiFields, @NotNull PsiAnnotation psiAnnotation) { + private PsiCodeBlock createCodeBlock(@NotNull PsiClass psiClass, @NotNull Collection memberInfos, @NotNull PsiAnnotation psiAnnotation) { final String blockText; - final String paramString = createParamString(psiClass, psiFields, psiAnnotation); + final String paramString = createParamString(psiClass, memberInfos, psiAnnotation); blockText = String.format("return \"%s(%s)\";", getSimpleClassName(psiClass), paramString); return PsiMethodUtil.createCodeBlockFromText(blockText, psiClass); } @@ -124,7 +134,7 @@ private String getSimpleClassName(@NotNull PsiClass psiClass) { return psiClassName.toString(); } - private String createParamString(@NotNull PsiClass psiClass, @NotNull Collection psiFields, @NotNull PsiAnnotation psiAnnotation) { + private String createParamString(@NotNull PsiClass psiClass, @NotNull Collection memberInfos, @NotNull PsiAnnotation psiAnnotation) { final boolean callSuper = PsiAnnotationUtil.getBooleanAnnotationValue(psiAnnotation, "callSuper", false); final boolean doNotUseGetters = readAnnotationOrConfigProperty(psiAnnotation, psiClass, "doNotUseGetters", ConfigKey.TOSTRING_DO_NOT_USE_GETTERS); final boolean includeFieldNames = readAnnotationOrConfigProperty(psiAnnotation, psiClass, "includeFieldNames", ConfigKey.TOSTRING_INCLUDE_FIELD_NAMES); @@ -134,15 +144,14 @@ private String createParamString(@NotNull PsiClass psiClass, @NotNull Collection paramString.append("super=\" + super.toString() + \", "); } - for (PsiField classField : psiFields) { - final String fieldName = classField.getName(); + for (MemberInfo memberInfo : memberInfos) { if (includeFieldNames) { - paramString.append(fieldName).append('='); + paramString.append(memberInfo.getName()).append('='); } paramString.append("\"+"); - final PsiType classFieldType = classField.getType(); + final PsiType classFieldType = memberInfo.getType(); if (classFieldType instanceof PsiArrayType) { final PsiType componentType = ((PsiArrayType) classFieldType).getComponentType(); if (componentType instanceof PsiPrimitiveType) { @@ -152,8 +161,8 @@ private String createParamString(@NotNull PsiClass psiClass, @NotNull Collection } } - final String fieldAccessor = buildAttributeNameString(doNotUseGetters, classField, psiClass); - paramString.append("this.").append(fieldAccessor); + final String memberAccessor = handler.getMemberAccessorName(memberInfo, doNotUseGetters, psiClass); + paramString.append("this.").append(memberAccessor); if (classFieldType instanceof PsiArrayType) { paramString.append(")"); @@ -179,7 +188,9 @@ public Collection collectProcessedAnnotations(@NotNull PsiClass p public LombokPsiElementUsage checkFieldUsage(@NotNull PsiField psiField, @NotNull PsiAnnotation psiAnnotation) { final PsiClass containingClass = psiField.getContainingClass(); if (null != containingClass) { - if (PsiClassUtil.getNames(filterFields(containingClass, psiAnnotation, false)).contains(psiField.getName())) { + final String psiFieldName = StringUtil.notNullize(psiField.getName()); + if (handler.filterFields(containingClass, psiAnnotation, false, INCLUDE_ANNOTATION_METHOD).stream() + .map(MemberInfo::getName).anyMatch(psiFieldName::equals)) { return LombokPsiElementUsage.READ; } } diff --git a/src/main/java/de/plushnikov/intellij/plugin/processor/field/GetterFieldProcessor.java b/src/main/java/de/plushnikov/intellij/plugin/processor/field/GetterFieldProcessor.java index cd596581e..e218593aa 100644 --- a/src/main/java/de/plushnikov/intellij/plugin/processor/field/GetterFieldProcessor.java +++ b/src/main/java/de/plushnikov/intellij/plugin/processor/field/GetterFieldProcessor.java @@ -97,7 +97,7 @@ private boolean validateExistingMethods(@NotNull PsiField psiField, @NotNull Pro for (String methodName : methodNames) { if (PsiMethodUtil.hasSimilarMethod(classMethods, methodName, 0)) { - final String setterMethodName = getGetterName(psiField); + final String setterMethodName = LombokUtils.getGetterName(psiField); builder.addWarning("Not generated '%s'(): A method with similar name '%s' already exists", setterMethodName, methodName); result = false; @@ -118,7 +118,7 @@ private boolean validateAccessorPrefix(@NotNull PsiField psiField, @NotNull Prob @NotNull public PsiMethod createGetterMethod(@NotNull PsiField psiField, @NotNull PsiClass psiClass, @NotNull String methodModifier) { - final String methodName = getGetterName(psiField); + final String methodName = LombokUtils.getGetterName(psiField); LombokLightMethodBuilder method = new LombokLightMethodBuilder(psiField.getManager(), methodName) .withMethodReturnType(psiField.getType()) diff --git a/src/main/java/de/plushnikov/intellij/plugin/processor/handler/BuilderHandler.java b/src/main/java/de/plushnikov/intellij/plugin/processor/handler/BuilderHandler.java index ee8b2def1..77d56c514 100644 --- a/src/main/java/de/plushnikov/intellij/plugin/processor/handler/BuilderHandler.java +++ b/src/main/java/de/plushnikov/intellij/plugin/processor/handler/BuilderHandler.java @@ -10,14 +10,24 @@ import de.plushnikov.intellij.plugin.processor.handler.singular.SingularHandlerFactory; import de.plushnikov.intellij.plugin.psi.LombokLightClassBuilder; import de.plushnikov.intellij.plugin.psi.LombokLightMethodBuilder; -import de.plushnikov.intellij.plugin.util.*; +import de.plushnikov.intellij.plugin.util.PsiAnnotationSearchUtil; +import de.plushnikov.intellij.plugin.util.PsiAnnotationUtil; +import de.plushnikov.intellij.plugin.util.PsiClassUtil; +import de.plushnikov.intellij.plugin.util.PsiMethodUtil; +import de.plushnikov.intellij.plugin.util.PsiTypeUtil; import lombok.*; import lombok.experimental.FieldDefaults; import lombok.experimental.Wither; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.*; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Optional; import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -386,7 +396,9 @@ private LombokLightClassBuilder createEmptyBuilderClass(@NotNull PsiClass psiCla @NotNull public PsiMethod createToStringMethod(@NotNull PsiAnnotation psiAnnotation, @NotNull PsiClass builderClass) { - return toStringProcessor.createToStringMethod(builderClass, Arrays.asList(builderClass.getFields()), psiAnnotation); + final List memberInfos = Arrays.stream(builderClass.getFields()) + .map(EqualsAndHashCodeToStringHandler.MemberInfo::new).collect(Collectors.toList()); + return toStringProcessor.createToStringMethod(builderClass, memberInfos, psiAnnotation); } @NotNull diff --git a/src/main/java/de/plushnikov/intellij/plugin/processor/handler/EqualsAndHashCodeToStringHandler.java b/src/main/java/de/plushnikov/intellij/plugin/processor/handler/EqualsAndHashCodeToStringHandler.java new file mode 100644 index 000000000..2a672a33d --- /dev/null +++ b/src/main/java/de/plushnikov/intellij/plugin/processor/handler/EqualsAndHashCodeToStringHandler.java @@ -0,0 +1,210 @@ +package de.plushnikov.intellij.plugin.processor.handler; + +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.PsiAnnotation; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiField; +import com.intellij.psi.PsiMember; +import com.intellij.psi.PsiMethod; +import com.intellij.psi.PsiModifier; +import com.intellij.psi.PsiType; +import de.plushnikov.intellij.plugin.thirdparty.LombokUtils; +import de.plushnikov.intellij.plugin.util.LombokProcessorUtil; +import de.plushnikov.intellij.plugin.util.PsiAnnotationSearchUtil; +import de.plushnikov.intellij.plugin.util.PsiAnnotationUtil; +import de.plushnikov.intellij.plugin.util.PsiClassUtil; +import de.plushnikov.intellij.plugin.util.PsiMethodUtil; +import lombok.Data; +import lombok.Getter; +import lombok.Value; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; + +public class EqualsAndHashCodeToStringHandler { + public static class MemberInfo { + private final PsiField psiField; + private final PsiMethod psiMethod; + private final String memberName; + private final boolean defaultInclude; + + MemberInfo(PsiField psiField) { + this(psiField, psiField.getName(), false); + } + + MemberInfo(PsiField psiField, String memberName) { + this(psiField, memberName, false); + } + + MemberInfo(PsiField psiField, String memberName, boolean defaultInclude) { + this.psiField = psiField; + this.psiMethod = null; + this.memberName = memberName; + this.defaultInclude = defaultInclude; + } + + MemberInfo(PsiMethod psiMethod, String memberName) { + this.psiField = null; + this.psiMethod = psiMethod; + this.memberName = memberName; + this.defaultInclude = false; + } + + public PsiField getField() { + return psiField; + } + + public PsiMethod getMethod() { + return psiMethod; + } + + public String getName() { + return memberName; + } + + public PsiType getType() { + if (null != psiField) { + return psiField.getType(); + } + return psiMethod.getReturnType(); + } + + private boolean matchDefaultIncludedFieldName(String fieldName) { + if (null != psiField && defaultInclude) { + return fieldName.equals(psiField.getName()); + } + return false; + } + } + + public Collection filterFields(@NotNull PsiClass psiClass, @NotNull PsiAnnotation psiAnnotation, boolean filterTransient, String includeAnnotationProperty) { + final boolean explicitOf = PsiAnnotationUtil.hasDeclaredProperty(psiAnnotation, "of"); + final boolean onlyExplicitlyIncluded = PsiAnnotationUtil.getBooleanAnnotationValue(psiAnnotation, "onlyExplicitlyIncluded", false); + + final String annotationFQN = psiAnnotation.getQualifiedName(); + final String annotationIncludeFQN = annotationFQN + ".Include"; + final String annotationExcludeFQN = annotationFQN + ".Exclude"; + + //Having both exclude and of generates a warning; the exclude parameter will be ignored in that case. + final Collection ofProperty; + final Collection excludeProperty; + if (!explicitOf) { + ofProperty = Collections.emptyList(); + excludeProperty = makeSet(PsiAnnotationUtil.getAnnotationValues(psiAnnotation, "exclude", String.class)); + } else { + ofProperty = makeSet(PsiAnnotationUtil.getAnnotationValues(psiAnnotation, "of", String.class)); + excludeProperty = Collections.emptyList(); + } + + final Collection psiMembers = PsiClassUtil.collectClassMemberIntern(psiClass); + + final Collection fieldNames2BeReplaced = new ArrayList<>(); + final Collection result = new ArrayList<>(psiMembers.size()); + + for (PsiMember psiMember : psiMembers) { + final PsiAnnotation includeAnnotation = PsiAnnotationSearchUtil.findAnnotation(psiMember, annotationIncludeFQN); + if (null == includeAnnotation) { + if (onlyExplicitlyIncluded) { + continue; + } + if (!(psiMember instanceof PsiField)) { + continue; + } + final PsiField psiField = (PsiField) psiMember; + final String fieldName = psiField.getName(); + if (null == fieldName) { + continue; + } + + if (ofProperty.contains(fieldName)) { + result.add(new MemberInfo(psiField)); + continue; + } else if (explicitOf) { + continue; + } + + if (excludeProperty.contains(fieldName)) { + continue; + } + + if (psiField.hasModifierProperty(PsiModifier.STATIC)) { + continue; + } + if ((filterTransient && psiField.hasModifierProperty(PsiModifier.TRANSIENT))) { + continue; + } + if (fieldName.startsWith(LombokUtils.LOMBOK_INTERN_FIELD_MARKER)) { + continue; + } + if (PsiAnnotationSearchUtil.isAnnotatedWith(psiField, annotationExcludeFQN)) { + continue; + } + result.add(new MemberInfo(psiField, fieldName, true)); + } else { + final String annotationValue = PsiAnnotationUtil.getStringAnnotationValue(includeAnnotation, includeAnnotationProperty); + final String newMemberName; + if (StringUtil.isEmptyOrSpaces(annotationValue)) { + newMemberName = psiMember.getName(); + } else { + newMemberName = annotationValue; + } + + if ((psiMember instanceof PsiMethod)) { + final PsiMethod psiMethod = (PsiMethod) psiMember; + if (0 == psiMethod.getParameterList().getParametersCount()) { + fieldNames2BeReplaced.add(newMemberName); + result.add(new MemberInfo(psiMethod, psiMethod.getName())); + } + } else { + result.add(new MemberInfo((PsiField) psiMember, newMemberName)); + } + } + } + + for (String fieldName : fieldNames2BeReplaced) { + // delete default-included fields with the same name as an explicit inclusion + result.removeIf(memberInfo -> memberInfo.matchDefaultIncludedFieldName(fieldName)); + } + + return result; + } + + public String getMemberAccessorName(@NotNull MemberInfo memberInfo, boolean doNotUseGetters, @NotNull PsiClass psiClass) { + final String memberAccessor; + if (null == memberInfo.getMethod()) { + memberAccessor = buildAttributeNameString(doNotUseGetters, memberInfo.getField(), psiClass); + } else { + memberAccessor = memberInfo.getName() + "()"; + } + return memberAccessor; + } + + private String buildAttributeNameString(boolean doNotUseGetters, @NotNull PsiField classField, @NotNull PsiClass psiClass) { + final String fieldName = classField.getName(); + if (doNotUseGetters) { + return fieldName; + } else { + final String getterName = LombokUtils.getGetterName(classField); + + final boolean hasGetter; + if (PsiAnnotationSearchUtil.isAnnotatedWith(psiClass, Data.class, Value.class, lombok.experimental.Value.class, Getter.class)) { + final PsiAnnotation getterLombokAnnotation = PsiAnnotationSearchUtil.findAnnotation(psiClass, Getter.class); + hasGetter = null == getterLombokAnnotation || null != LombokProcessorUtil.getMethodModifier(getterLombokAnnotation); + } else { + hasGetter = PsiMethodUtil.hasMethodByName(PsiClassUtil.collectClassMethodsIntern(psiClass), getterName); + } + + return hasGetter ? getterName + "()" : fieldName; + } + } + + private Collection makeSet(@NotNull Collection exclude) { + if (exclude.isEmpty()) { + return Collections.emptySet(); + } + return new HashSet<>(exclude); + } +} diff --git a/src/main/java/de/plushnikov/intellij/plugin/psi/LombokLightMethodBuilder.java b/src/main/java/de/plushnikov/intellij/plugin/psi/LombokLightMethodBuilder.java index 8fa655610..7a9fde981 100644 --- a/src/main/java/de/plushnikov/intellij/plugin/psi/LombokLightMethodBuilder.java +++ b/src/main/java/de/plushnikov/intellij/plugin/psi/LombokLightMethodBuilder.java @@ -3,22 +3,7 @@ import com.intellij.lang.ASTNode; import com.intellij.lang.java.JavaLanguage; import com.intellij.openapi.util.TextRange; -import com.intellij.psi.JavaPsiFacade; -import com.intellij.psi.PsiClass; -import com.intellij.psi.PsiClassType; -import com.intellij.psi.PsiCodeBlock; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiElementFactory; -import com.intellij.psi.PsiFile; -import com.intellij.psi.PsiIdentifier; -import com.intellij.psi.PsiManager; -import com.intellij.psi.PsiMethod; -import com.intellij.psi.PsiModifier; -import com.intellij.psi.PsiModifierList; -import com.intellij.psi.PsiParameter; -import com.intellij.psi.PsiReferenceList; -import com.intellij.psi.PsiType; -import com.intellij.psi.PsiTypeParameter; +import com.intellij.psi.*; import com.intellij.psi.impl.CheckUtil; import com.intellij.psi.impl.light.LightMethodBuilder; import com.intellij.psi.impl.light.LightModifierList; @@ -80,8 +65,19 @@ public LightMethodBuilder setMethodReturnType(PsiType returnType) { return super.setMethodReturnType(returnType); } + public LombokLightMethodBuilder withFinalParameter(@NotNull String name, @NotNull PsiType type) { + final LombokLightParameter lombokLightParameter = createParameter(name, type); + lombokLightParameter.setModifiers(PsiModifier.FINAL); + return withParameter(lombokLightParameter); + } + public LombokLightMethodBuilder withParameter(@NotNull String name, @NotNull PsiType type) { - return withParameter(new LombokLightParameter(name, type, this, JavaLanguage.INSTANCE)); + return withParameter(createParameter(name, type)); + } + + @NotNull + private LombokLightParameter createParameter(@NotNull String name, @NotNull PsiType type) { + return new LombokLightParameter(name, type, this, JavaLanguage.INSTANCE); } public LombokLightMethodBuilder withParameter(@NotNull PsiParameter psiParameter) { diff --git a/src/main/java/de/plushnikov/intellij/plugin/thirdparty/LombokUtils.java b/src/main/java/de/plushnikov/intellij/plugin/thirdparty/LombokUtils.java index 92bcf1a73..d7381e159 100644 --- a/src/main/java/de/plushnikov/intellij/plugin/thirdparty/LombokUtils.java +++ b/src/main/java/de/plushnikov/intellij/plugin/thirdparty/LombokUtils.java @@ -1,7 +1,10 @@ package de.plushnikov.intellij.plugin.thirdparty; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.PsiField; +import com.intellij.psi.PsiType; import de.plushnikov.intellij.plugin.processor.field.AccessorsInfo; +import org.jetbrains.annotations.NotNull; import java.util.Collection; import java.util.HashSet; @@ -24,6 +27,15 @@ public class LombokUtils { public static final Pattern DEPRECATED_PATTERN = Pattern.compile("^(?:deprecated)$", Pattern.CASE_INSENSITIVE); + public static String getGetterName(final @NotNull PsiField psiField) { + final AccessorsInfo accessorsInfo = AccessorsInfo.build(psiField); + + final String psiFieldName = psiField.getName(); + final boolean isBoolean = PsiType.BOOLEAN.equals(psiField.getType()); + + return LombokUtils.toGetterName(accessorsInfo, psiFieldName, isBoolean); + } + /** * Generates a getter name from a given field name. *

diff --git a/src/main/java/de/plushnikov/intellij/plugin/util/PsiClassUtil.java b/src/main/java/de/plushnikov/intellij/plugin/util/PsiClassUtil.java index cd47fd5bd..22652e23c 100644 --- a/src/main/java/de/plushnikov/intellij/plugin/util/PsiClassUtil.java +++ b/src/main/java/de/plushnikov/intellij/plugin/util/PsiClassUtil.java @@ -62,6 +62,11 @@ public static Collection collectInnerClassesIntern(@NotNull PsiClass p } } + @NotNull + public static Collection collectClassMemberIntern(@NotNull PsiClass psiClass) { + return Arrays.stream(psiClass.getChildren()).filter(e -> e instanceof PsiField || e instanceof PsiMethod).map(PsiMember.class::cast).collect(Collectors.toList()); + } + private static Collection filterPsiElements(@NotNull PsiClass psiClass, @NotNull Class desiredClass) { return Arrays.stream(psiClass.getChildren()).filter(desiredClass::isInstance).map(desiredClass::cast).collect(Collectors.toList()); } diff --git a/testData/action/delombok/data/afterDataSimple.java b/testData/action/delombok/data/afterDataSimple.java index 062a82ddc..719e35579 100644 --- a/testData/action/delombok/data/afterDataSimple.java +++ b/testData/action/delombok/data/afterDataSimple.java @@ -73,7 +73,7 @@ public void setA(String a) { this.a = a; } - public boolean equals(Object o) { + public boolean equals(final Object o) { if (o == this) return true; if (!(o instanceof DataSimple)) return false; final DataSimple other = (DataSimple) o; @@ -91,6 +91,10 @@ public boolean equals(Object o) { return true; } + protected boolean canEqual(final Object other) { + return other instanceof DataSimple; + } + public int hashCode() { final int PRIME = 59; int result = 1; @@ -107,11 +111,7 @@ public int hashCode() { return result; } - protected boolean canEqual(Object other) { - return other instanceof DataSimple; - } - public String toString() { return "DataSimple(finalX=" + this.getFinalX() + ", x=" + this.getX() + ", f=" + this.getF() + ", d=" + this.getD() + ", bool=" + this.isBool() + ", y=" + java.util.Arrays.toString(this.getY()) + ", z=" + java.util.Arrays.deepToString(this.getZ()) + ", a=" + this.getA() + ")"; } -} \ No newline at end of file +} diff --git a/testData/action/delombok/equalsandhashcode/afterEqualsAndHashCodeCallSuper.java b/testData/action/delombok/equalsandhashcode/afterEqualsAndHashCodeCallSuper.java index d1f8de344..52eade4d1 100644 --- a/testData/action/delombok/equalsandhashcode/afterEqualsAndHashCodeCallSuper.java +++ b/testData/action/delombok/equalsandhashcode/afterEqualsAndHashCodeCallSuper.java @@ -35,7 +35,7 @@ String getA() { return a; } - public boolean equals(Object o) { + public boolean equals(final Object o) { if (o == this) return true; if (!(o instanceof EqualsAndHashCodeCallSuper)) return false; final EqualsAndHashCodeCallSuper other = (EqualsAndHashCodeCallSuper) o; @@ -53,10 +53,13 @@ public boolean equals(Object o) { return true; } + protected boolean canEqual(final Object other) { + return other instanceof EqualsAndHashCodeCallSuper; + } + public int hashCode() { final int PRIME = 59; - int result = 1; - result = result * PRIME + super.hashCode(); + int result = super.hashCode(); result = result * PRIME + this.getX(); result = result * PRIME + Float.floatToIntBits(this.getF()); final long $d = Double.doubleToLongBits(this.getD()); @@ -68,8 +71,4 @@ public int hashCode() { result = result * PRIME + ($a == null ? 43 : $a.hashCode()); return result; } - - protected boolean canEqual(Object other) { - return other instanceof EqualsAndHashCodeCallSuper; - } } diff --git a/testData/after/builder/BuilderGenerics.java b/testData/after/builder/BuilderGenerics.java index 4823665a0..ddb9d0820 100644 --- a/testData/after/builder/BuilderGenerics.java +++ b/testData/after/builder/BuilderGenerics.java @@ -58,7 +58,7 @@ public boolean equals(Object o) { if (!(o instanceof ObjectApiResponse)) return false; - final ObjectApiResponse other = (ObjectApiResponse) o; + final ObjectApiResponse other = (ObjectApiResponse) o; if (!other.canEqual((Object) this)) return false; diff --git a/testData/configsystem/equalsandhashcode/callSuper/after/WithSuperTest.java b/testData/configsystem/equalsandhashcode/callSuper/after/WithSuperTest.java index 7969327ee..5265c132d 100644 --- a/testData/configsystem/equalsandhashcode/callSuper/after/WithSuperTest.java +++ b/testData/configsystem/equalsandhashcode/callSuper/after/WithSuperTest.java @@ -12,29 +12,27 @@ public boolean isBooleanProperty() { return booleanProperty; } - public boolean equals(Object o) { + public boolean equals(final Object o) { if (o == this) return true; - if (!(o instanceof BasisClass)) return false; - final BasisClass other = (BasisClass) o; + if (!(o instanceof WithSuperTest.BasisClass)) return false; + final WithSuperTest.BasisClass other = (WithSuperTest.BasisClass) o; if (!other.canEqual((java.lang.Object) this)) return false; - if (!super.equals(o)) return false; if (this.getIntProperty() != other.getIntProperty()) return false; if (this.isBooleanProperty() != other.isBooleanProperty()) return false; return true; } + protected boolean canEqual(final Object other) { + return other instanceof WithSuperTest.BasisClass; + } + public int hashCode() { final int PRIME = 59; int result = 1; - result = result * PRIME + super.hashCode(); result = result * PRIME + this.getIntProperty(); result = result * PRIME + (this.isBooleanProperty() ? 79 : 97); return result; } - - protected boolean canEqual(Object other) { - return other instanceof BasisClass; - } } private static class ExtendedClass extends BasisClass { @@ -49,38 +47,36 @@ public String getStringProperty() { return stringProperty; } - public boolean equals(Object o) { + public boolean equals(final Object o) { if (o == this) return true; - if (!(o instanceof ExtendedClass)) return false; - final ExtendedClass other = (ExtendedClass) o; + if (!(o instanceof WithSuperTest.ExtendedClass)) return false; + final WithSuperTest.ExtendedClass other = (WithSuperTest.ExtendedClass) o; if (!other.canEqual((java.lang.Object) this)) return false; if (!super.equals(o)) return false; if (java.lang.Double.compare(this.getDoubleProperty(), other.getDoubleProperty()) != 0) return false; final java.lang.Object this$stringProperty = this.getStringProperty(); final java.lang.Object other$stringProperty = other.getStringProperty(); - if (this$stringProperty == null ? other$stringProperty != null : !this$stringProperty.equals(other$stringProperty)) - return false; + if (this$stringProperty == null ? other$stringProperty != null : !this$stringProperty.equals(other$stringProperty)) return false; return true; } + protected boolean canEqual(final Object other) { + return other instanceof WithSuperTest.ExtendedClass; + } + public int hashCode() { final int PRIME = 59; - int result = 1; - result = result * PRIME + super.hashCode(); + int result = super.hashCode(); final long $doubleProperty = java.lang.Double.doubleToLongBits(this.getDoubleProperty()); result = result * PRIME + (int) ($doubleProperty >>> 32 ^ $doubleProperty); final java.lang.Object $stringProperty = this.getStringProperty(); result = result * PRIME + ($stringProperty == null ? 43 : $stringProperty.hashCode()); return result; } - - protected boolean canEqual(Object other) { - return other instanceof ExtendedClass; - } } public static void main(String[] args) { final ExtendedClass test = new ExtendedClass(); System.out.println(test.hashCode()); } -} \ No newline at end of file +} diff --git a/testData/configsystem/equalsandhashcode/callSuper/after/WithoutSuperTest.java b/testData/configsystem/equalsandhashcode/callSuper/after/WithoutSuperTest.java index 3089c094d..7bc42f021 100644 --- a/testData/configsystem/equalsandhashcode/callSuper/after/WithoutSuperTest.java +++ b/testData/configsystem/equalsandhashcode/callSuper/after/WithoutSuperTest.java @@ -33,7 +33,6 @@ public boolean equals(Object o) { if (!(o instanceof WithoutSuperTest)) return false; final WithoutSuperTest other = (WithoutSuperTest) o; if (!other.canEqual((java.lang.Object) this)) return false; - if (!super.equals(o)) return false; if (this.getIntProperty() != other.getIntProperty()) return false; if (this.isBooleanProperty() != other.isBooleanProperty()) return false; if (java.lang.Double.compare(this.getDoubleProperty(), other.getDoubleProperty()) != 0) return false; @@ -47,7 +46,6 @@ public boolean equals(Object o) { public int hashCode() { final int PRIME = 59; int result = 1; - result = result * PRIME + super.hashCode(); result = result * PRIME + this.getIntProperty(); result = result * PRIME + (this.isBooleanProperty() ? 79 : 97); final long $doubleProperty = java.lang.Double.doubleToLongBits(this.getDoubleProperty());