package de.plushnikov.intellij.plugin.lombokconfig; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiFile; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.util.PathUtil; import com.intellij.util.indexing.FileBasedIndex; import de.plushnikov.intellij.plugin.psi.LombokLightClassBuilder; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; public class ConfigDiscovery { private final FileBasedIndex fileBasedIndex; public static ConfigDiscovery getInstance() { return ServiceManager.getService(ConfigDiscovery.class); } public ConfigDiscovery(FileBasedIndex fileBasedIndex) { this.fileBasedIndex = fileBasedIndex; } @NotNull public String getStringLombokConfigProperty(@NotNull ConfigKey configKey, @NotNull PsiClass psiClass) { final String canonicalPath = calculateCanonicalPath(psiClass); if (null != canonicalPath) { return discoverProperty(configKey, canonicalPath, psiClass.getProject()); } else { return configKey.getConfigDefaultValue(); } } public boolean getBooleanLombokConfigProperty(@NotNull ConfigKey configKey, @NotNull PsiClass psiClass) { final String configProperty = getStringLombokConfigProperty(configKey, psiClass); return Boolean.parseBoolean(configProperty); } @NotNull public String[] getMultipleValueLombokConfigProperty(@NotNull ConfigKey configKey, @NotNull PsiClass psiClass) { final Collection<String> result = new HashSet<String>(); final String canonicalPath = calculateCanonicalPath(psiClass); if (null != canonicalPath) { final List<String> properties = discoverProperties(configKey, canonicalPath, 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()); } return result.toArray(new String[result.size()]); } @Nullable private String calculateCanonicalPath(@NotNull PsiClass psiClass) { String canonicalPath = null; final 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(); } if (null != psiFile) { canonicalPath = getDirectoryCanonicalPath(psiFile); if (null == canonicalPath) { canonicalPath = getDirectoryCanonicalPath(psiFile.getOriginalFile()); } } return PathUtil.toSystemIndependentName(canonicalPath); } @Nullable private String getDirectoryCanonicalPath(@NotNull PsiFile psiFile) { final VirtualFile virtualFile = psiFile.getVirtualFile(); if (null != virtualFile) { final VirtualFile fileDirectory = virtualFile.getParent(); if (null != fileDirectory) { return fileDirectory.getCanonicalPath(); } } return null; } @NotNull private String discoverProperty(@NotNull ConfigKey configKey, @NotNull String canonicalPath, @NotNull Project project) { final GlobalSearchScope searchScope = GlobalSearchScope.projectScope(project); String currentPath = canonicalPath; while (null != currentPath) { final String property = readProperty(fileBasedIndex, searchScope, currentPath, configKey); if (null == property) { if (shouldStopBubbling(fileBasedIndex, searchScope, currentPath)) { break; } } else { return property; } currentPath = bubbleUp(currentPath); } return configKey.getConfigDefaultValue(); } @Nullable private String bubbleUp(@NotNull String currentPath) { final int endIndex = currentPath.lastIndexOf('/'); if (endIndex > 0) { currentPath = currentPath.substring(0, endIndex); } else { currentPath = null; } return currentPath; } private boolean shouldStopBubbling(@NotNull FileBasedIndex fileBasedIndex, @NotNull GlobalSearchScope searchScope, @NotNull String currentPath) { final String stopBubblingProperty = readProperty(fileBasedIndex, searchScope, currentPath, ConfigKey.CONFIG_STOP_BUBBLING); return Boolean.parseBoolean(stopBubblingProperty); } @Nullable private String readProperty(FileBasedIndex fileBasedIndex, GlobalSearchScope searchScope, String directoryName, ConfigKey configKey) { final ConfigIndexKey configIndexKey = new ConfigIndexKey(directoryName, configKey.getConfigKey()); final List<String> values = fileBasedIndex.getValues(LombokConfigIndex.NAME, configIndexKey, searchScope); if (!values.isEmpty()) { return values.iterator().next(); } return null; } @NotNull private List<String> discoverProperties(@NotNull ConfigKey configKey, @NotNull String canonicalPath, @NotNull Project project) { List<String> result = new ArrayList<String>(); final GlobalSearchScope searchScope = GlobalSearchScope.projectScope(project); String currentPath = canonicalPath; while (null != currentPath) { final String property = readProperty(fileBasedIndex, searchScope, currentPath, configKey); if (null == property) { if (shouldStopBubbling(fileBasedIndex, searchScope, currentPath)) { break; } } else { result.add(property); } currentPath = bubbleUp(currentPath); } return result; } }