mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[lombok] IDEA-310810 IDEA-380110 Show Lombok-generated methods in 'Caller-Hierarchy' tool window
GitOrigin-RevId: 063456efa192d4d60b402a14c4b860cf231d585c
This commit is contained in:
committed by
intellij-monorepo-bot
parent
cd926ffe35
commit
b836adb49b
@@ -106,6 +106,8 @@
|
||||
</extensionPoint>
|
||||
<extensionPoint qualifiedName="com.intellij.hierarchy.referenceProcessor"
|
||||
interface="com.intellij.ide.hierarchy.call.CallReferenceProcessor" dynamic="true"/>
|
||||
<extensionPoint qualifiedName="com.intellij.hierarchy.elementProvider"
|
||||
interface="com.intellij.ide.hierarchy.call.CallHierarchyElementProvider" dynamic="true"/>
|
||||
<extensionPoint qualifiedName="com.intellij.refactoring.pullUpHelperFactory" beanClass="com.intellij.lang.LanguageExtensionPoint"
|
||||
dynamic="true">
|
||||
<with attribute="implementationClass" implements="com.intellij.refactoring.memberPullUp.PullUpHelperFactory"/>
|
||||
|
||||
@@ -17,10 +17,8 @@ import com.intellij.util.ArrayUtilRt;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public final class CallerMethodsTreeStructure extends HierarchyTreeStructure {
|
||||
private final String myScopeType;
|
||||
@@ -119,6 +117,11 @@ public final class CallerMethodsTreeStructure extends HierarchyTreeStructure {
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
// add other method elements without references
|
||||
for (CallHierarchyElementProvider provider : CallHierarchyElementProvider.EP_NAME.getExtensionList()) {
|
||||
provider.appendReferencedMethods(methodToFind, data);
|
||||
}
|
||||
}
|
||||
|
||||
return ArrayUtil.toObjectArray(methodToDescriptorMap.values());
|
||||
@@ -127,9 +130,19 @@ public final class CallerMethodsTreeStructure extends HierarchyTreeStructure {
|
||||
assert enclosingElement instanceof PsiField || enclosingElement instanceof PsiRecordComponent
|
||||
: "Enclosing element should be a field, but was " + enclosingElement.getClass() + ", text: " + enclosingElement.getText();
|
||||
|
||||
return ReferencesSearch
|
||||
final Stream<@NotNull PsiElement> referencedElements = ReferencesSearch
|
||||
.search(enclosingElement, searchScope).findAll().stream()
|
||||
.map(PsiReference::getElement)
|
||||
.map(PsiReference::getElement);
|
||||
|
||||
// collect other field elements without references
|
||||
Collection<@NotNull PsiElement> providedElements = new ArrayList<>();
|
||||
for (CallHierarchyElementProvider provider : CallHierarchyElementProvider.EP_NAME.getExtensionList()) {
|
||||
if(provider.canProvide(enclosingElement)) {
|
||||
providedElements.addAll(provider.provideReferencedMembers(enclosingElement));
|
||||
}
|
||||
}
|
||||
|
||||
return Stream.concat(referencedElements, providedElements.stream())
|
||||
.distinct()
|
||||
.map(e -> new CallHierarchyNodeDescriptor(myProject, nodeDescriptor, e, false, false))
|
||||
.filter(n -> n.getEnclosingElement() != null)
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.ide.hierarchy.call;
|
||||
|
||||
import com.intellij.openapi.extensions.ExtensionPointName;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiMember;
|
||||
import com.intellij.psi.PsiMethod;
|
||||
import com.intellij.psi.search.SearchScope;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public interface CallHierarchyElementProvider {
|
||||
ExtensionPointName<CallHierarchyElementProvider> EP_NAME =
|
||||
ExtensionPointName.create("com.intellij.hierarchy.elementProvider");
|
||||
|
||||
boolean canProvide(@NotNull PsiMember element);
|
||||
|
||||
Collection<PsiElement> provideReferencedMembers(@NotNull PsiMember reference);
|
||||
|
||||
void appendReferencedMethods(@NotNull PsiMethod methodToFind, @NotNull JavaCallHierarchyData hierarchyData);
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
package de.plushnikov.intellij.plugin.hierarchy;
|
||||
|
||||
import com.intellij.ide.hierarchy.call.CallHierarchyElementProvider;
|
||||
import com.intellij.ide.hierarchy.call.CallHierarchyNodeDescriptor;
|
||||
import com.intellij.ide.hierarchy.call.JavaCallHierarchyData;
|
||||
import com.intellij.ide.util.treeView.NodeDescriptor;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.search.SearchScope;
|
||||
import de.plushnikov.intellij.plugin.psi.LombokLightMethodBuilder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class LombokFieldHierarchyElementProvider implements CallHierarchyElementProvider {
|
||||
|
||||
@Override
|
||||
public boolean canProvide(@NotNull PsiMember element) {
|
||||
return element instanceof PsiField;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<PsiElement> provideReferencedMembers(@NotNull PsiMember elementToSearch) {
|
||||
final PsiClass containingClass = elementToSearch.getContainingClass();
|
||||
if (containingClass != null) {
|
||||
final Collection<PsiElement> result = new ArrayList<>();
|
||||
|
||||
Arrays.stream(containingClass.getMethods())
|
||||
.filter(LombokLightMethodBuilder.class::isInstance)
|
||||
.map(LombokLightMethodBuilder.class::cast)
|
||||
.filter(psiMethod -> psiMethod.getNavigationElement() == elementToSearch || psiMethod.hasRelatedMember(elementToSearch))
|
||||
.forEach(result::add);
|
||||
|
||||
Arrays.stream(containingClass.getInnerClasses())
|
||||
.map(PsiClass::getMethods)
|
||||
.flatMap(Arrays::stream)
|
||||
.filter(LombokLightMethodBuilder.class::isInstance)
|
||||
.map(LombokLightMethodBuilder.class::cast)
|
||||
.filter(psiMethod -> psiMethod.hasRelatedMember(elementToSearch))
|
||||
.forEach(result::add);
|
||||
|
||||
return result;
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendReferencedMethods(@NotNull PsiMethod methodToFind, @NotNull JavaCallHierarchyData hierarchyData) {
|
||||
if (methodToFind.isConstructor()) {
|
||||
final PsiClass containingClass = methodToFind.getContainingClass();
|
||||
if (null != containingClass) {
|
||||
Arrays.stream(containingClass.getInnerClasses())
|
||||
.map(PsiClass::getMethods)
|
||||
.flatMap(Arrays::stream)
|
||||
.filter(LombokLightMethodBuilder.class::isInstance)
|
||||
.map(LombokLightMethodBuilder.class::cast)
|
||||
.filter(methodBuilder -> methodBuilder.hasRelatedMember(methodToFind))
|
||||
.forEach(methodBuilder -> {
|
||||
CallHierarchyNodeDescriptor parentDescriptor = (CallHierarchyNodeDescriptor)hierarchyData.getNodeDescriptor();
|
||||
final Map<PsiMember, NodeDescriptor<?>> nodeDescriptorMap = hierarchyData.getResultMap();
|
||||
CallHierarchyNodeDescriptor d = (CallHierarchyNodeDescriptor)nodeDescriptorMap.get(methodBuilder);
|
||||
if (d == null) {
|
||||
d = new CallHierarchyNodeDescriptor(hierarchyData.getProject(), parentDescriptor, methodBuilder, false, true);
|
||||
nodeDescriptorMap.put(methodBuilder, d);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+24
-13
@@ -3,6 +3,7 @@ package de.plushnikov.intellij.plugin.processor.clazz;
|
||||
import com.intellij.codeInspection.LocalQuickFix;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiTypesUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import de.plushnikov.intellij.plugin.LombokClassNames;
|
||||
import de.plushnikov.intellij.plugin.lombokconfig.ConfigKey;
|
||||
import de.plushnikov.intellij.plugin.problem.ProblemSink;
|
||||
@@ -194,19 +195,28 @@ public final class EqualsAndHashCodeProcessor extends AbstractClassProcessor {
|
||||
LombokAddNullAnnotations.createRelevantNullableAnnotation(psiClass, parameter);
|
||||
copyOnXAnnotationsForFirstParam(psiAnnotation, parameter);
|
||||
}
|
||||
|
||||
methodBuilder.withRelatedMembers(m -> {
|
||||
final Collection<MemberInfo> memberInfos = collectEqualsAndHashcodeMemberInfo(m);
|
||||
return ContainerUtil.map(memberInfos, MemberInfo::getField);
|
||||
});
|
||||
methodBuilder.withBodyText(m -> {
|
||||
PsiClass containingClass = m.getContainingClass();
|
||||
PsiAnnotation anno = (PsiAnnotation)m.getNavigationElement();
|
||||
return createEqualsBlockString(containingClass, anno, hasCanEqualMethod,
|
||||
EqualsAndHashCodeToStringHandler.filterMembers(containingClass, anno, true,
|
||||
INCLUDE_ANNOTATION_METHOD, null,
|
||||
EQUALS_AND_HASHCODE_INCLUDE,
|
||||
EQUALS_AND_HASHCODE_EXCLUDE));
|
||||
final PsiClass containingClass = m.getContainingClass();
|
||||
final PsiAnnotation anno = (PsiAnnotation)m.getNavigationElement();
|
||||
final Collection<MemberInfo> memberInfos = collectEqualsAndHashcodeMemberInfo(m);
|
||||
return createEqualsBlockString(containingClass, anno, hasCanEqualMethod, memberInfos);
|
||||
});
|
||||
return methodBuilder;
|
||||
}
|
||||
|
||||
private static @NotNull Collection<MemberInfo> collectEqualsAndHashcodeMemberInfo(LombokLightMethodBuilder m) {
|
||||
PsiClass containingClass = m.getContainingClass();
|
||||
PsiAnnotation anno = (PsiAnnotation)m.getNavigationElement();
|
||||
return EqualsAndHashCodeToStringHandler.filterMembers(containingClass, anno, true,
|
||||
INCLUDE_ANNOTATION_METHOD, null,
|
||||
EQUALS_AND_HASHCODE_INCLUDE,
|
||||
EQUALS_AND_HASHCODE_EXCLUDE);
|
||||
}
|
||||
|
||||
private @NotNull PsiMethod createHashCodeMethod(@NotNull PsiClass psiClass,
|
||||
@NotNull PsiAnnotation psiAnnotation) {
|
||||
final PsiManager psiManager = psiClass.getManager();
|
||||
@@ -216,14 +226,15 @@ public final class EqualsAndHashCodeProcessor extends AbstractClassProcessor {
|
||||
.withMethodReturnType(PsiTypes.intType())
|
||||
.withContainingClass(psiClass)
|
||||
.withNavigationElement(psiAnnotation)
|
||||
.withRelatedMembers(m -> {
|
||||
final Collection<MemberInfo> memberInfos = collectEqualsAndHashcodeMemberInfo(m);
|
||||
return ContainerUtil.map(memberInfos, MemberInfo::getField);
|
||||
})
|
||||
.withBodyText(m -> {
|
||||
PsiClass containingClass = m.getContainingClass();
|
||||
PsiAnnotation anno = (PsiAnnotation)m.getNavigationElement();
|
||||
return createHashcodeBlockString(containingClass, anno,
|
||||
EqualsAndHashCodeToStringHandler.filterMembers(containingClass, anno, true,
|
||||
INCLUDE_ANNOTATION_METHOD, null,
|
||||
EQUALS_AND_HASHCODE_INCLUDE,
|
||||
EQUALS_AND_HASHCODE_EXCLUDE));
|
||||
final Collection<MemberInfo> memberInfos = collectEqualsAndHashcodeMemberInfo(m);
|
||||
return createHashcodeBlockString(containingClass, anno, memberInfos);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+4
@@ -126,6 +126,10 @@ public final class ToStringProcessor extends AbstractClassProcessor {
|
||||
|
||||
LombokAddNullAnnotations.createRelevantNonNullAnnotation(psiClass, methodBuilder);
|
||||
|
||||
for (MemberInfo memberInfo : memberInfos) {
|
||||
methodBuilder.withRelatedMember(memberInfo.getField());
|
||||
}
|
||||
|
||||
return methodBuilder;
|
||||
}
|
||||
|
||||
|
||||
+8
@@ -328,6 +328,10 @@ public abstract class AbstractConstructorClassProcessor extends AbstractClassPro
|
||||
.withModifier(modifier)
|
||||
.withWriteAccess();
|
||||
|
||||
for (PsiField param : params) {
|
||||
constructorBuilder.withRelatedMember(param);
|
||||
}
|
||||
|
||||
LombokCopyableAnnotations.copyOnXAnnotations(psiAnnotation, constructorBuilder.getModifierList(), "onConstructor");
|
||||
|
||||
if (useJavaDefaults) {
|
||||
@@ -395,6 +399,10 @@ public abstract class AbstractConstructorClassProcessor extends AbstractClassPro
|
||||
.withModifier(PsiModifier.STATIC)
|
||||
.withWriteAccess();
|
||||
|
||||
for (PsiField param : params) {
|
||||
methodBuilder.withRelatedMember(param);
|
||||
}
|
||||
|
||||
PsiSubstitutor substitutor = PsiSubstitutor.EMPTY;
|
||||
if (psiClass.hasTypeParameters()) {
|
||||
final PsiTypeParameter[] classTypeParameters = psiClass.getTypeParameters();
|
||||
|
||||
+11
-4
@@ -23,10 +23,7 @@ import de.plushnikov.intellij.plugin.provider.LombokUserDataKeys;
|
||||
import de.plushnikov.intellij.plugin.psi.LombokLightClassBuilder;
|
||||
import de.plushnikov.intellij.plugin.psi.LombokLightMethodBuilder;
|
||||
import de.plushnikov.intellij.plugin.thirdparty.LombokCopyableAnnotations;
|
||||
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.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -766,6 +763,16 @@ public class BuilderHandler {
|
||||
.withContainingClass(builderClass)
|
||||
.withNavigationElement(parentClass)
|
||||
.withModifier(getBuilderInnerAccessVisibility(psiAnnotation));
|
||||
|
||||
if (null == psiMethod) {
|
||||
Stream.of(parentClass.getConstructors())
|
||||
.filter(m -> sameParameters(m.getParameterList().getParameters(), builderInfos))
|
||||
.findFirst().ifPresent(methodBuilder::withRelatedMember);
|
||||
}
|
||||
else if (psiMethod.isConstructor()) {
|
||||
methodBuilder.withRelatedMember(psiMethod);
|
||||
}
|
||||
|
||||
final String codeBlockText =
|
||||
createBuildMethodCodeBlockText(psiMethod, builderClass, returnType, buildMethodPrepare, buildMethodParameters);
|
||||
methodBuilder.withBodyText(codeBlockText);
|
||||
|
||||
+31
@@ -21,6 +21,9 @@ import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
|
||||
@@ -38,6 +41,11 @@ public class LombokLightMethodBuilder extends LightMethodBuilder implements Synt
|
||||
|
||||
private boolean myReadWriteAccess = false;
|
||||
|
||||
private Function<LombokLightMethodBuilder, Collection<PsiElement>> myRelatedMembersFunction = builder -> {
|
||||
return List.of();
|
||||
};
|
||||
private Collection<PsiElement> myRelatedMembers;
|
||||
|
||||
public LombokLightMethodBuilder(@NotNull PsiManager manager, @NotNull String name) {
|
||||
super(manager, JavaLanguage.INSTANCE, name,
|
||||
new LombokLightParameterListBuilder(manager, JavaLanguage.INSTANCE),
|
||||
@@ -48,6 +56,29 @@ public class LombokLightMethodBuilder extends LightMethodBuilder implements Synt
|
||||
setBaseIcon(LombokIcons.Nodes.LombokMethod);
|
||||
}
|
||||
|
||||
public LombokLightMethodBuilder withRelatedMembers(@NotNull Function<LombokLightMethodBuilder, Collection<PsiElement>> buildFunction) {
|
||||
myRelatedMembersFunction = buildFunction;
|
||||
myRelatedMembers = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
public LombokLightMethodBuilder withRelatedMember(@Nullable PsiElement member) {
|
||||
if(null != member) {
|
||||
if (myRelatedMembers == null) {
|
||||
myRelatedMembers = new ArrayList<>();
|
||||
}
|
||||
myRelatedMembers.add(member);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean hasRelatedMember(@NotNull PsiMember member) {
|
||||
if(myRelatedMembers == null) {
|
||||
myRelatedMembers = myRelatedMembersFunction.apply(this);
|
||||
}
|
||||
return myRelatedMembers.contains(member);
|
||||
}
|
||||
|
||||
public LombokLightMethodBuilder withNavigationElement(PsiElement navigationElement) {
|
||||
setNavigationElement(navigationElement);
|
||||
return this;
|
||||
|
||||
@@ -113,6 +113,8 @@
|
||||
|
||||
<canBeFinal implementation="de.plushnikov.intellij.plugin.extension.LombokCanBeFinalHandler"/>
|
||||
|
||||
<hierarchy.elementProvider implementation="de.plushnikov.intellij.plugin.hierarchy.LombokFieldHierarchyElementProvider"/>
|
||||
|
||||
<localInspection groupPath="Java" language="JAVA" shortName="LombokGetterMayBeUsed" enabledByDefault="true" level="WARNING"
|
||||
bundle="messages.LombokBundle" key="inspection.lombok.getter.may.be.used.display.name"
|
||||
groupBundle="messages.InspectionsBundle" groupKey="group.names.verbose.or.redundant.code.constructs"
|
||||
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
package de.plushnikov.intellij.plugin.hierarchy;
|
||||
|
||||
import com.intellij.ide.hierarchy.HierarchyBrowserBaseEx;
|
||||
import com.intellij.ide.hierarchy.JavaHierarchyUtil;
|
||||
import com.intellij.ide.hierarchy.call.CallerMethodsTreeStructure;
|
||||
import com.intellij.ide.util.treeView.NodeDescriptor;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.JavaPsiFacade;
|
||||
import com.intellij.psi.PsiClass;
|
||||
import com.intellij.psi.PsiField;
|
||||
import com.intellij.psi.search.ProjectScope;
|
||||
import com.intellij.testFramework.codeInsight.hierarchy.HierarchyViewTestFixture;
|
||||
import de.plushnikov.intellij.plugin.AbstractLombokLightCodeInsightTestCase;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Tests for CallHierarchy with Lombok annotations.
|
||||
* Compares the call hierarchy of Lombok-generated methods with equivalent plain Java implementations.
|
||||
* The test normalizes class names in the hierarchy dumps so that only the caller methods matter.
|
||||
* Only caller methods are compared, excluding internal class hierarchy elements.
|
||||
*/
|
||||
public class LombokCallHierarchyTest extends AbstractLombokLightCodeInsightTestCase {
|
||||
|
||||
@Override
|
||||
protected String getBasePath() {
|
||||
return super.getBasePath() + "/hierarchy";
|
||||
}
|
||||
|
||||
// ==================== @Getter Comparison Test ====================
|
||||
|
||||
public void testGetterHierarchyComparison() {
|
||||
doCompareFieldHierarchies(
|
||||
"GetterLombok", "GetterLombokCaller", "name",
|
||||
"GetterPlainJava", "GetterPlainJavaCaller"
|
||||
);
|
||||
}
|
||||
|
||||
// ==================== @Setter Comparison Test ====================
|
||||
|
||||
public void testSetterHierarchyComparison() {
|
||||
doCompareFieldHierarchies(
|
||||
"SetterLombok", "SetterLombokCaller", "value",
|
||||
"SetterPlainJava", "SetterPlainJavaCaller"
|
||||
);
|
||||
}
|
||||
|
||||
// ==================== @Data Comparison Test ====================
|
||||
|
||||
public void testDataHierarchyComparison() {
|
||||
doCompareFieldHierarchies(
|
||||
"DataLombok", "DataLombokCaller", "id",
|
||||
"DataPlainJava", "DataPlainJavaCaller"
|
||||
);
|
||||
}
|
||||
|
||||
// ==================== @Value Comparison Test ====================
|
||||
|
||||
public void testValueHierarchyComparison() {
|
||||
doCompareFieldHierarchies(
|
||||
"ValueLombok", "ValueLombokCaller", "code",
|
||||
"ValuePlainJava", "ValuePlainJavaCaller"
|
||||
);
|
||||
}
|
||||
|
||||
// ==================== @ToString Comparison Test ====================
|
||||
|
||||
public void testToStringHierarchyComparison() {
|
||||
doCompareFieldHierarchies(
|
||||
"ToStringLombok", "ToStringLombokCaller", "description",
|
||||
"ToStringPlainJava", "ToStringPlainJavaCaller"
|
||||
);
|
||||
}
|
||||
|
||||
// ==================== @Builder Comparison Test ====================
|
||||
|
||||
public void testBuilderHierarchyComparison() {
|
||||
doCompareFieldHierarchies(
|
||||
"BuilderLombok", "BuilderLombokCaller", "title",
|
||||
"BuilderPlainJava", "BuilderPlainJavaCaller"
|
||||
);
|
||||
}
|
||||
|
||||
// ==================== Helper Methods ====================
|
||||
|
||||
/**
|
||||
* Compares the call hierarchy of a field in a Lombok class with a one in a plain Java class.
|
||||
* The hierarchies are normalized to remove class name differences, so only the method structure matters.
|
||||
*/
|
||||
private void doCompareFieldHierarchies(
|
||||
String lombokClassName, String lombokCallerClassName, String fieldName,
|
||||
String plainJavaClassName, String plainJavaCallerClassName) {
|
||||
|
||||
// Load all files together
|
||||
myFixture.configureByFiles(
|
||||
"/" + lombokClassName.replace('$', '/') + ".java",
|
||||
"/" + lombokCallerClassName + ".java",
|
||||
"/" + plainJavaClassName.replace('$', '/').split("\\$")[0] + ".java",
|
||||
"/" + plainJavaCallerClassName + ".java"
|
||||
);
|
||||
|
||||
// Get Lombok hierarchy (field-based)
|
||||
@NotNull CallerMethodsTreeStructure lombokHierarchy = getFieldCallerHierarchy(lombokClassName, fieldName);
|
||||
|
||||
// Get Plain Java hierarchy (field-based)
|
||||
@NotNull CallerMethodsTreeStructure plainJavaHierarchy = getFieldCallerHierarchy(plainJavaClassName, fieldName);
|
||||
|
||||
final Comparator<NodeDescriptor<?>> nodeDescriptorComparator = JavaHierarchyUtil.getComparator(getProject());
|
||||
String plainDumped = HierarchyViewTestFixture.dump(plainJavaHierarchy, null, nodeDescriptorComparator, 0);
|
||||
|
||||
String plainDumpedNormalized = plainDumped.replaceAll(plainJavaClassName, lombokClassName);
|
||||
plainDumpedNormalized = plainDumpedNormalized.replaceAll(plainJavaCallerClassName, lombokCallerClassName);
|
||||
|
||||
final String equalsMethodCall = lombokClassName + ".equals(Object)";
|
||||
if(StringUtil.getOccurrenceCount(plainDumpedNormalized, equalsMethodCall) > 1) {
|
||||
plainDumpedNormalized = removeFirstLineWithSubstring(plainDumpedNormalized, equalsMethodCall);
|
||||
}
|
||||
|
||||
HierarchyViewTestFixture.doHierarchyTest(lombokHierarchy, plainDumpedNormalized);
|
||||
}
|
||||
|
||||
public static String removeFirstLineWithSubstring(String text, String substring) {
|
||||
AtomicBoolean removed = new AtomicBoolean(false);
|
||||
|
||||
return Arrays.stream(text.split("\\R"))
|
||||
.filter(line -> {
|
||||
if (!removed.get() && line.contains(substring)) {
|
||||
removed.set(true);
|
||||
return false; // erstes Vorkommen raus
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.collect(Collectors.joining(System.lineSeparator()));
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
private CallerMethodsTreeStructure getFieldCallerHierarchy(String className, String fieldName) {
|
||||
PsiClass psiClass = JavaPsiFacade.getInstance(getProject())
|
||||
.findClass(className, ProjectScope.getProjectScope(getProject()));
|
||||
assertNotNull("Class '" + className + "' not found", psiClass);
|
||||
|
||||
PsiField field = psiClass.findFieldByName(fieldName, false);
|
||||
assertNotNull("Field '" + fieldName + "' not found in " + className, field);
|
||||
|
||||
return new CallerMethodsTreeStructure(getProject(), field, HierarchyBrowserBaseEx.SCOPE_PROJECT);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import lombok.Builder;
|
||||
|
||||
@Builder
|
||||
public class BuilderLombok {
|
||||
private String title;
|
||||
private String author;
|
||||
private int pages;
|
||||
private boolean published;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
public class BuilderLombokCaller {
|
||||
|
||||
public void useBuilder() {
|
||||
BuilderLombok book = BuilderLombok.builder()
|
||||
.title("Clean Code")
|
||||
.author("Robert C. Martin")
|
||||
.pages(464)
|
||||
.published(true)
|
||||
.build();
|
||||
}
|
||||
|
||||
public BuilderLombok createBook(String title, String author) {
|
||||
return BuilderLombok.builder()
|
||||
.title(title)
|
||||
.author(author)
|
||||
.pages(100)
|
||||
.published(false)
|
||||
.build();
|
||||
}
|
||||
|
||||
public BuilderLombok.BuilderLombokBuilder startBuilding() {
|
||||
return BuilderLombok.builder()
|
||||
.title("New Book");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
public class BuilderPlainJava {
|
||||
private String title;
|
||||
private String author;
|
||||
private int pages;
|
||||
private boolean published;
|
||||
|
||||
BuilderPlainJava(String title, String author, int pages, boolean published) {
|
||||
this.title = title;
|
||||
this.author = author;
|
||||
this.pages = pages;
|
||||
this.published = published;
|
||||
}
|
||||
|
||||
public static BuilderPlainJavaBuilder builder() {
|
||||
return new BuilderPlainJavaBuilder();
|
||||
}
|
||||
|
||||
public static class BuilderPlainJavaBuilder {
|
||||
private String title;
|
||||
private String author;
|
||||
private int pages;
|
||||
private boolean published;
|
||||
|
||||
BuilderPlainJavaBuilder() {
|
||||
}
|
||||
|
||||
public BuilderPlainJavaBuilder title(String title) {
|
||||
this.title = title;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BuilderPlainJavaBuilder author(String author) {
|
||||
this.author = author;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BuilderPlainJavaBuilder pages(int pages) {
|
||||
this.pages = pages;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BuilderPlainJavaBuilder published(boolean published) {
|
||||
this.published = published;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BuilderPlainJava build() {
|
||||
return new BuilderPlainJava(title, author, pages, published);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
public class BuilderPlainJavaCaller {
|
||||
|
||||
public void useBuilder() {
|
||||
BuilderPlainJava book = BuilderPlainJava.builder()
|
||||
.title("Clean Code")
|
||||
.author("Robert C. Martin")
|
||||
.pages(464)
|
||||
.published(true)
|
||||
.build();
|
||||
}
|
||||
|
||||
public BuilderPlainJava createBook(String title, String author) {
|
||||
return BuilderPlainJava.builder()
|
||||
.title(title)
|
||||
.author(author)
|
||||
.pages(100)
|
||||
.published(false)
|
||||
.build();
|
||||
}
|
||||
|
||||
public BuilderPlainJava.BuilderPlainJavaBuilder startBuilding() {
|
||||
return BuilderPlainJava.builder()
|
||||
.title("New Book");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class DataLombok {
|
||||
private Long id;
|
||||
private String name;
|
||||
private boolean active;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
public class DataLombokCaller {
|
||||
|
||||
public void useData() {
|
||||
DataLombok obj = new DataLombok();
|
||||
obj.setId(1L);
|
||||
obj.setName("Test");
|
||||
obj.setActive(true);
|
||||
|
||||
Long id = obj.getId();
|
||||
String name = obj.getName();
|
||||
boolean active = obj.isActive();
|
||||
|
||||
System.out.println("ID: " + id + ", Name: " + name + ", Active: " + active);
|
||||
}
|
||||
|
||||
public Long fetchId(DataLombok data) {
|
||||
return data.getId();
|
||||
}
|
||||
|
||||
public void updateId(DataLombok data, Long newId) {
|
||||
data.setId(newId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
import java.util.Objects;
|
||||
|
||||
public class DataPlainJava {
|
||||
private Long id;
|
||||
private String name;
|
||||
private boolean active;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public boolean isActive() {
|
||||
return active;
|
||||
}
|
||||
|
||||
public void setActive(boolean active) {
|
||||
this.active = active;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
DataPlainJava that = (DataPlainJava) o;
|
||||
return active == that.active && Objects.equals(id, that.id) && Objects.equals(name, that.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id, name, active);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DataPlainJava{" +
|
||||
"id=" + id +
|
||||
", name='" + name + '\'' +
|
||||
", active=" + active +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
public class DataPlainJavaCaller {
|
||||
|
||||
public void useData() {
|
||||
DataPlainJava obj = new DataPlainJava();
|
||||
obj.setId(1L);
|
||||
obj.setName("Test");
|
||||
obj.setActive(true);
|
||||
|
||||
Long id = obj.getId();
|
||||
String name = obj.getName();
|
||||
boolean active = obj.isActive();
|
||||
|
||||
System.out.println("ID: " + id + ", Name: " + name + ", Active: " + active);
|
||||
}
|
||||
|
||||
public Long fetchId(DataPlainJava data) {
|
||||
return data.getId();
|
||||
}
|
||||
|
||||
public void updateId(DataPlainJava data, Long newId) {
|
||||
data.setId(newId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public class GetterLombok {
|
||||
private String name;
|
||||
private int age;
|
||||
|
||||
public GetterLombok(String name, int age) {
|
||||
this.name = name;
|
||||
this.age = age;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
public class GetterLombokCaller {
|
||||
|
||||
public void useGetter() {
|
||||
GetterLombok obj = new GetterLombok("Test", 25);
|
||||
String name = obj.getName();
|
||||
int age = obj.getAge();
|
||||
System.out.println("Name: " + name + ", Age: " + age);
|
||||
}
|
||||
|
||||
public String fetchName(GetterLombok lombok) {
|
||||
return lombok.getName();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
public class GetterPlainJava {
|
||||
private String name;
|
||||
private int age;
|
||||
|
||||
public GetterPlainJava(String name, int age) {
|
||||
this.name = name;
|
||||
this.age = age;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public int getAge() {
|
||||
return age;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
public class GetterPlainJavaCaller {
|
||||
|
||||
public void useGetter() {
|
||||
GetterPlainJava obj = new GetterPlainJava("Test", 25);
|
||||
String name = obj.getName();
|
||||
int age = obj.getAge();
|
||||
System.out.println("Name: " + name + ", Age: " + age);
|
||||
}
|
||||
|
||||
public String fetchName(GetterPlainJava plainJava) {
|
||||
return plainJava.getName();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import lombok.Setter;
|
||||
|
||||
@Setter
|
||||
public class SetterLombok {
|
||||
private String value;
|
||||
private int count;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
public class SetterLombokCaller {
|
||||
|
||||
public void useSetter() {
|
||||
SetterLombok obj = new SetterLombok();
|
||||
obj.setValue("Hello");
|
||||
obj.setCount(42);
|
||||
}
|
||||
|
||||
public void updateValue(SetterLombok lombok, String newValue) {
|
||||
lombok.setValue(newValue);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
public class SetterPlainJava {
|
||||
private String value;
|
||||
private int count;
|
||||
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public void setCount(int count) {
|
||||
this.count = count;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
public class SetterPlainJavaCaller {
|
||||
|
||||
public void useSetter() {
|
||||
SetterPlainJava obj = new SetterPlainJava();
|
||||
obj.setValue("Hello");
|
||||
obj.setCount(42);
|
||||
}
|
||||
|
||||
public void updateValue(SetterPlainJava plainJava, String newValue) {
|
||||
plainJava.setValue(newValue);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import lombok.ToString;
|
||||
|
||||
@ToString
|
||||
public class ToStringLombok {
|
||||
private String description;
|
||||
private int priority;
|
||||
private boolean completed;
|
||||
|
||||
public ToStringLombok(String description, int priority, boolean completed) {
|
||||
this.description = description;
|
||||
this.priority = priority;
|
||||
this.completed = completed;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
public class ToStringLombokCaller {
|
||||
|
||||
public void useToString() {
|
||||
ToStringLombok obj = new ToStringLombok("Task description", 1, false);
|
||||
String result = obj.toString();
|
||||
System.out.println(result);
|
||||
}
|
||||
|
||||
public String getStringRepresentation(ToStringLombok lombok) {
|
||||
return lombok.toString();
|
||||
}
|
||||
|
||||
public void logObject(ToStringLombok lombok) {
|
||||
System.out.println("Object: " + lombok.toString());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
public class ToStringPlainJava {
|
||||
private String description;
|
||||
private int priority;
|
||||
private boolean completed;
|
||||
|
||||
public ToStringPlainJava(String description, int priority, boolean completed) {
|
||||
this.description = description;
|
||||
this.priority = priority;
|
||||
this.completed = completed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ToStringPlainJava{" +
|
||||
"description='" + description + '\'' +
|
||||
", priority=" + priority +
|
||||
", completed=" + completed +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
public class ToStringPlainJavaCaller {
|
||||
|
||||
public void useToString() {
|
||||
ToStringPlainJava obj = new ToStringPlainJava("Task description", 1, false);
|
||||
String result = obj.toString();
|
||||
System.out.println(result);
|
||||
}
|
||||
|
||||
public String getStringRepresentation(ToStringPlainJava plainJava) {
|
||||
return plainJava.toString();
|
||||
}
|
||||
|
||||
public void logObject(ToStringPlainJava plainJava) {
|
||||
System.out.println("Object: " + plainJava.toString());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import lombok.Value;
|
||||
|
||||
@Value
|
||||
public class ValueLombok {
|
||||
String code;
|
||||
int version;
|
||||
boolean enabled;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
public class ValueLombokCaller {
|
||||
|
||||
public void useValue() {
|
||||
ValueLombok obj = new ValueLombok("ABC", 1, true);
|
||||
|
||||
String code = obj.getCode();
|
||||
int version = obj.getVersion();
|
||||
boolean enabled = obj.isEnabled();
|
||||
|
||||
System.out.println("Code: " + code + ", Version: " + version + ", Enabled: " + enabled);
|
||||
}
|
||||
|
||||
public String fetchCode(ValueLombok value) {
|
||||
return value.getCode();
|
||||
}
|
||||
|
||||
public boolean checkEnabled(ValueLombok value) {
|
||||
return value.isEnabled();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import java.util.Objects;
|
||||
|
||||
public final class ValuePlainJava {
|
||||
private final String code;
|
||||
private final int version;
|
||||
private final boolean enabled;
|
||||
|
||||
public ValuePlainJava(String code, int version, boolean enabled) {
|
||||
this.code = code;
|
||||
this.version = version;
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public int getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
ValuePlainJava that = (ValuePlainJava) o;
|
||||
return version == that.version && enabled == that.enabled && Objects.equals(code, that.code);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(code, version, enabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ValuePlainJava{" +
|
||||
"code='" + code + '\'' +
|
||||
", version=" + version +
|
||||
", enabled=" + enabled +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
public class ValuePlainJavaCaller {
|
||||
|
||||
public void useValue() {
|
||||
ValuePlainJava obj = new ValuePlainJava("ABC", 1, true);
|
||||
|
||||
String code = obj.getCode();
|
||||
int version = obj.getVersion();
|
||||
boolean enabled = obj.isEnabled();
|
||||
|
||||
System.out.println("Code: " + code + ", Version: " + version + ", Enabled: " + enabled);
|
||||
}
|
||||
|
||||
public String fetchCode(ValuePlainJava value) {
|
||||
return value.getCode();
|
||||
}
|
||||
|
||||
public boolean checkEnabled(ValuePlainJava value) {
|
||||
return value.isEnabled();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user