diff --git a/plugins/lombok/src/main/java/de/plushnikov/intellij/plugin/inspection/SpringQualifierCopyableLombokAnnotationInspection.java b/plugins/lombok/src/main/java/de/plushnikov/intellij/plugin/inspection/SpringQualifierCopyableLombokAnnotationInspection.java index df76a7d97760..4cf7ce984982 100644 --- a/plugins/lombok/src/main/java/de/plushnikov/intellij/plugin/inspection/SpringQualifierCopyableLombokAnnotationInspection.java +++ b/plugins/lombok/src/main/java/de/plushnikov/intellij/plugin/inspection/SpringQualifierCopyableLombokAnnotationInspection.java @@ -11,7 +11,7 @@ import de.plushnikov.intellij.plugin.lombokconfig.ConfigKey; import de.plushnikov.intellij.plugin.util.PsiAnnotationSearchUtil; import org.jetbrains.annotations.NotNull; -import java.util.Arrays; +import java.util.Collection; import java.util.Objects; /** @@ -48,10 +48,10 @@ public class SpringQualifierCopyableLombokAnnotationInspection extends LombokJav if (psiClass != null && PsiAnnotationSearchUtil.isAnnotatedWith(psiClass, LombokClassNames.REQUIRED_ARGS_CONSTRUCTOR, LombokClassNames.ALL_ARGS_CONSTRUCTOR)) { - String[] configuredCopyableAnnotations = + Collection configuredCopyableAnnotations = ConfigDiscovery.getInstance().getMultipleValueLombokConfigProperty(ConfigKey.COPYABLE_ANNOTATIONS, psiClass); - if (!Arrays.asList(configuredCopyableAnnotations).contains(SPRING_QUALIFIER_FQN)) { + if (!configuredCopyableAnnotations.contains(SPRING_QUALIFIER_FQN)) { holder.registerProblem(annotation, LombokBundle.message("inspection.message.annotation.not.lombok.copyable", SPRING_QUALIFIER_FQN), diff --git a/plugins/lombok/src/main/java/de/plushnikov/intellij/plugin/lombokconfig/ConfigDiscovery.java b/plugins/lombok/src/main/java/de/plushnikov/intellij/plugin/lombokconfig/ConfigDiscovery.java index 74b428326067..b6493f9ea430 100644 --- a/plugins/lombok/src/main/java/de/plushnikov/intellij/plugin/lombokconfig/ConfigDiscovery.java +++ b/plugins/lombok/src/main/java/de/plushnikov/intellij/plugin/lombokconfig/ConfigDiscovery.java @@ -9,9 +9,10 @@ import com.intellij.psi.PsiClass; import com.intellij.psi.PsiFile; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.search.GlobalSearchScopes; -import com.intellij.util.ArrayUtil; +import com.intellij.psi.util.CachedValueProvider; +import com.intellij.psi.util.CachedValuesManager; +import com.intellij.util.containers.ConcurrentFactoryMap; import com.intellij.util.indexing.FileBasedIndex; -import de.plushnikov.intellij.plugin.psi.LombokLightClassBuilder; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -25,12 +26,11 @@ public class ConfigDiscovery { @NotNull public String getStringLombokConfigProperty(@NotNull ConfigKey configKey, @NotNull PsiClass psiClass) { - @Nullable VirtualFile file = calculateDirectory(psiClass); - if (null != file) { - return discoverProperty(configKey, file, psiClass.getProject()); - } else { - return configKey.getConfigDefaultValue(); + @Nullable PsiFile psiFile = calculatePsiFile(psiClass); + if (null != psiFile) { + return discoverPropertyWithCache(configKey, psiFile); } + return configKey.getConfigDefaultValue(); } public boolean getBooleanLombokConfigProperty(@NotNull ConfigKey configKey, @NotNull PsiClass psiClass) { @@ -38,67 +38,44 @@ public class ConfigDiscovery { return Boolean.parseBoolean(configProperty); } - public String @NotNull [] getMultipleValueLombokConfigProperty(@NotNull ConfigKey configKey, @NotNull PsiClass psiClass) { - final Collection result = new HashSet<>(); - - @Nullable VirtualFile file = calculateDirectory(psiClass); - if (file != null) { - final List properties = discoverProperties(configKey, file, psiClass.getProject()); - Collections.reverse(properties); - - for (String configProperty : properties) { - if (StringUtil.isNotEmpty(configProperty)) { - final String[] values = configProperty.split(";"); - for (String value : values) { - if (value.startsWith("+")) { - result.add(value.substring(1)); - } else if (value.startsWith("-")) { - result.remove(value.substring(1)); - } - } - } - } - } else { - result.add(configKey.getConfigDefaultValue()); + public @NotNull Collection getMultipleValueLombokConfigProperty(@NotNull ConfigKey configKey, @NotNull PsiClass psiClass) { + @Nullable PsiFile psiFile = calculatePsiFile(psiClass); + if (psiFile != null) { + return discoverPropertiesWithCache(configKey, psiFile); } - return ArrayUtil.toStringArray(result); + return Collections.singletonList(configKey.getConfigDefaultValue()); } @Nullable - private static VirtualFile calculateDirectory(@NotNull PsiClass psiClass) { - PsiFile psiFile; - if (psiClass instanceof LombokLightClassBuilder) { - // Use containing class for all LombokLightClasses - final PsiClass containingClass = psiClass.getContainingClass(); - if (null != containingClass) { - psiFile = containingClass.getContainingFile(); - } else { - psiFile = null; - } - } else { - psiFile = psiClass.getContainingFile(); - } + private static PsiFile calculatePsiFile(@NotNull PsiClass psiClass) { + PsiFile psiFile = psiClass.getContainingFile(); if (psiFile != null) { - PsiFile originalFile = psiFile.getOriginalFile(); - if (originalFile != null) { - psiFile = originalFile; - } + psiFile = psiFile.getOriginalFile(); } - - return psiFile != null ? psiFile.getVirtualFile() : null; + return psiFile; } @NotNull - private String discoverProperty(@NotNull ConfigKey configKey, @Nullable VirtualFile file, @NotNull Project project) { - @Nullable VirtualFile currentFile = file; + protected String discoverPropertyWithCache(@NotNull ConfigKey configKey, @NotNull PsiFile psiFile) { + return CachedValuesManager.getCachedValue(psiFile, () -> { + Map result = + ConcurrentFactoryMap.createMap(configKeyInner -> discoverProperty(configKeyInner, psiFile)); + return CachedValueProvider.Result.create(result, LombokConfigIndex.CONFIG_CHANGE_TRACKER); + }).get(configKey); + } + + @NotNull + protected String discoverProperty(@NotNull ConfigKey configKey, @NotNull PsiFile psiFile) { + @Nullable VirtualFile currentFile = psiFile.getVirtualFile(); while (currentFile != null) { - ConfigValue configValue = readProperty(configKey, project, currentFile); + ConfigValue configValue = readProperty(configKey, psiFile.getProject(), currentFile); if (null != configValue) { if (null == configValue.getValue()) { if (configValue.isStopBubbling()) { break; } - } else { + } + else { return configValue.getValue(); } } @@ -125,25 +102,52 @@ public class ConfigDiscovery { } @NotNull - private List discoverProperties(@NotNull ConfigKey configKey, @Nullable VirtualFile file, @NotNull Project project) { - List result = new ArrayList<>(); + protected Collection discoverPropertiesWithCache(@NotNull ConfigKey configKey, @NotNull PsiFile psiFile) { + return CachedValuesManager.getCachedValue(psiFile, () -> { + Map> result = ConcurrentFactoryMap.createMap(configKeyInner -> discoverProperties(configKeyInner, psiFile)); + return CachedValueProvider.Result.create(result, LombokConfigIndex.CONFIG_CHANGE_TRACKER); + }).get(configKey); + } - @Nullable VirtualFile currentFile = file; + @NotNull + protected Collection discoverProperties(@NotNull ConfigKey configKey, @NotNull PsiFile file) { + List properties = new ArrayList<>(); + + @Nullable VirtualFile currentFile = file.getVirtualFile(); while (currentFile != null) { - final ConfigValue configValue = readProperty(configKey, project, currentFile); + final ConfigValue configValue = readProperty(configKey, file.getProject(), currentFile); if (null != configValue) { if (null == configValue.getValue()) { if (configValue.isStopBubbling()) { break; } - } else { - result.add(configValue.getValue()); + } + else { + properties.add(configValue.getValue()); } } currentFile = currentFile.getParent(); } + Collections.reverse(properties); + + Set result = new HashSet<>(); + + for (String configProperty : properties) { + if (StringUtil.isNotEmpty(configProperty)) { + final String[] values = configProperty.split(";"); + for (String value : values) { + if (value.startsWith("+")) { + result.add(value.substring(1)); + } + else if (value.startsWith("-")) { + result.remove(value.substring(1)); + } + } + } + } + return result; } } diff --git a/plugins/lombok/src/main/java/de/plushnikov/intellij/plugin/lombokconfig/ConfigKey.java b/plugins/lombok/src/main/java/de/plushnikov/intellij/plugin/lombokconfig/ConfigKey.java index 3d7b0d9e1a44..cb507ded865f 100644 --- a/plugins/lombok/src/main/java/de/plushnikov/intellij/plugin/lombokconfig/ConfigKey.java +++ b/plugins/lombok/src/main/java/de/plushnikov/intellij/plugin/lombokconfig/ConfigKey.java @@ -1,5 +1,7 @@ package de.plushnikov.intellij.plugin.lombokconfig; +import java.util.Locale; + public enum ConfigKey { CONFIG_STOP_BUBBLING("config.stopBubbling", "false"), @@ -89,7 +91,7 @@ public enum ConfigKey { private final String configDefaultValue; ConfigKey(String configKey, String configDefaultValue) { - this.configKey = configKey.toLowerCase(); + this.configKey = configKey.toLowerCase(Locale.ENGLISH); this.configDefaultValue = configDefaultValue; } diff --git a/plugins/lombok/src/main/java/de/plushnikov/intellij/plugin/lombokconfig/LombokConfigIndex.java b/plugins/lombok/src/main/java/de/plushnikov/intellij/plugin/lombokconfig/LombokConfigIndex.java index 2c29db3b68e6..d5ebeb241ed7 100644 --- a/plugins/lombok/src/main/java/de/plushnikov/intellij/plugin/lombokconfig/LombokConfigIndex.java +++ b/plugins/lombok/src/main/java/de/plushnikov/intellij/plugin/lombokconfig/LombokConfigIndex.java @@ -1,5 +1,6 @@ package de.plushnikov.intellij.plugin.lombokconfig; +import com.intellij.openapi.util.ModificationTracker; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; @@ -24,8 +25,12 @@ import java.io.IOException; import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.atomic.AtomicLong; public class LombokConfigIndex extends FileBasedIndexExtension { + private static final AtomicLong configChangeCount = new AtomicLong(1); + public static final ModificationTracker CONFIG_CHANGE_TRACKER = configChangeCount::get; + @NonNls public static final ID NAME = ID.create("LombokConfigIndex"); @@ -56,7 +61,7 @@ public class LombokConfigIndex extends FileBasedIndexExtension configuredCopyableAnnotations = ConfigDiscovery.getInstance().getMultipleValueLombokConfigProperty(ConfigKey.COPYABLE_ANNOTATIONS, containingClass); - combinedListOfCopyableAnnotations.addAll(Arrays.asList(configuredCopyableAnnotations)); + combinedListOfCopyableAnnotations.addAll(configuredCopyableAnnotations); } final List existingAnnotations = ContainerUtil.map(psiField.getAnnotations(), PsiAnnotation::getQualifiedName); diff --git a/plugins/lombok/src/main/java/de/plushnikov/intellij/plugin/processor/field/AccessorsInfo.java b/plugins/lombok/src/main/java/de/plushnikov/intellij/plugin/processor/field/AccessorsInfo.java index ad86cee39b2b..37180c4c7775 100644 --- a/plugins/lombok/src/main/java/de/plushnikov/intellij/plugin/processor/field/AccessorsInfo.java +++ b/plugins/lombok/src/main/java/de/plushnikov/intellij/plugin/processor/field/AccessorsInfo.java @@ -4,6 +4,7 @@ import com.intellij.psi.PsiAnnotation; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiField; import com.intellij.psi.PsiVariable; +import com.intellij.util.ArrayUtil; import de.plushnikov.intellij.plugin.LombokClassNames; import de.plushnikov.intellij.plugin.lombokconfig.ConfigDiscovery; import de.plushnikov.intellij.plugin.lombokconfig.ConfigKey; @@ -30,7 +31,7 @@ public class AccessorsInfo { this.fluent = fluentValue; this.chain = chainValue; this.doNotUseIsPrefix = doNotUseIsPrefix; - this.prefixes = null == prefixes ? new String[0] : prefixes; + this.prefixes = null == prefixes ? ArrayUtil.EMPTY_STRING_ARRAY : prefixes; } @NotNull @@ -109,9 +110,9 @@ public class AccessorsInfo { } if (prefixDeclared.isEmpty()) { - prefixes = configDiscovery.getMultipleValueLombokConfigProperty(ConfigKey.ACCESSORS_PREFIX, psiClass); + prefixes = ArrayUtil.toStringArray(configDiscovery.getMultipleValueLombokConfigProperty(ConfigKey.ACCESSORS_PREFIX, psiClass)); } else { - prefixes = prefixDeclared.toArray(new String[0]); + prefixes = ArrayUtil.toStringArray(prefixDeclared); } doNotUseIsPrefix = configDiscovery.getBooleanLombokConfigProperty(ConfigKey.GETTER_NO_IS_PREFIX, psiClass); @@ -119,7 +120,7 @@ public class AccessorsInfo { } else { isFluent = null != fluentDeclaredValue && fluentDeclaredValue; isChained = null != chainDeclaredValue && chainDeclaredValue; - prefixes = prefixDeclared.toArray(new String[0]); + prefixes = ArrayUtil.toStringArray(prefixDeclared); doNotUseIsPrefix = false; } diff --git a/plugins/lombok/src/test/java/de/plushnikov/intellij/plugin/lombokconfig/ConfigDiscoveryTest.java b/plugins/lombok/src/test/java/de/plushnikov/intellij/plugin/lombokconfig/ConfigDiscoveryTest.java index 2f44904a9d90..70324fb74da3 100644 --- a/plugins/lombok/src/test/java/de/plushnikov/intellij/plugin/lombokconfig/ConfigDiscoveryTest.java +++ b/plugins/lombok/src/test/java/de/plushnikov/intellij/plugin/lombokconfig/ConfigDiscoveryTest.java @@ -13,14 +13,12 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; -import java.util.ArrayList; -import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.List; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; +import static org.hamcrest.CoreMatchers.hasItems; +import static org.junit.Assert.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.when; @@ -58,10 +56,23 @@ public class ConfigDiscoveryTest { protected FileBasedIndex getFileBasedIndex() { return fileBasedIndex; } + + @Override + protected @NotNull String discoverPropertyWithCache(@NotNull ConfigKey configKey, + @NotNull PsiFile psiFile) { + return super.discoverProperty(configKey, psiFile); + } + + @Override + protected @NotNull Collection discoverPropertiesWithCache(@NotNull ConfigKey configKey, + @NotNull PsiFile psiFile) { + return super.discoverProperties(configKey, psiFile); + } }; - when(psiClass.getProject()).thenReturn(project); + when(psiFile.getProject()).thenReturn(project); when(psiClass.getContainingFile()).thenReturn(psiFile); + when(psiFile.getOriginalFile()).thenReturn(psiFile); when(psiFile.getVirtualFile()).thenReturn(virtualFile); when(virtualFile.getParent()).thenReturn(parentVirtualFile); when(parentVirtualFile.getParent()).thenReturn(parentParVirtualFile); @@ -113,13 +124,8 @@ public class ConfigDiscoveryTest { when(fileBasedIndex.getValues(eq(LombokConfigIndex.NAME), eq(configKey), any(GlobalSearchScope.class))) .thenReturn(makeValue("+_d;"), Collections.emptyList(), makeValue("-a;+cc"), makeValue("+a;+b")); - final String[] properties = discovery.getMultipleValueLombokConfigProperty(configKey, psiClass); - assertNotNull(properties); - assertEquals(3, properties.length); - final ArrayList list = new ArrayList<>(Arrays.asList(properties)); - assertTrue(list.contains("b")); - assertTrue(list.contains("cc")); - assertTrue(list.contains("_d")); + final Collection properties = discovery.getMultipleValueLombokConfigProperty(configKey, psiClass); + assertThat(properties, hasItems("b", "cc", "_d")); } @NotNull @@ -133,10 +139,7 @@ public class ConfigDiscoveryTest { when(fileBasedIndex.getValues(eq(LombokConfigIndex.NAME), eq(configKey), isA(GlobalSearchScope.class))) .thenReturn(makeValue("+_d;")); - final String[] properties = discovery.getMultipleValueLombokConfigProperty(configKey, psiClass); - assertNotNull(properties); - assertEquals(1, properties.length); - final ArrayList list = new ArrayList<>(Arrays.asList(properties)); - assertTrue(list.contains("_d")); + final Collection properties = discovery.getMultipleValueLombokConfigProperty(configKey, psiClass); + assertThat(properties, hasItems("_d")); } }