package fr.adrienbrault.idea.symfony2plugin.dic.container.util;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.search.FilenameIndex;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.CachedValue;
import com.intellij.psi.util.CachedValueProvider;
import com.intellij.psi.util.CachedValuesManager;
import com.intellij.psi.util.PsiModificationTracker;
import fr.adrienbrault.idea.symfony2plugin.util.yaml.YamlHelper;
import org.apache.commons.lang.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.yaml.YAMLUtil;
import org.jetbrains.yaml.psi.*;
import java.io.IOException;
import java.util.*;
import java.util.function.Consumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author Daniel Espendiller <daniel@espendiller.net>
*/
public class DotEnvUtil {
private static final Key<CachedValue<Set<String>>> DOT_ENV_VARIABLE_CACHE = new Key<>("DOT_ENV_VARIABLE_CACHE");
/**
* case insensitive filenames
*/
private static final String[] DOCKER_FILES = {
"Dockerfile", "dockerfile"
};
/**
* Provide targets for "%env(FOOBAR)%"
*
* @param parameter %env(FOOBAR)%
*/
@NotNull
public static Collection<PsiElement> getEnvironmentVariableTargetsForParameter(@NotNull Project project, @NotNull String parameter) {
Collection<PsiElement> targets = new ArrayList<>();
if(parameter.length() > 7 && parameter.startsWith("%env(") && parameter.endsWith(")%")) {
targets.addAll(
DotEnvUtil.getEnvironmentVariableTargets(project, parameter.substring(5, parameter.length() - 2))
);
}
return targets;
}
@NotNull
public static Collection<String> getEnvironmentVariables(@NotNull Project project) {
CachedValue<Set<String>> cache = project.getUserData(DOT_ENV_VARIABLE_CACHE);
if (cache == null) {
cache = CachedValuesManager.getManager(project).createCachedValue(() -> {
Set<String> items = new HashSet<>();
DotEnvUtil.visitEnvironment(project, pair ->
items.add(pair.getFirst())
);
return CachedValueProvider.Result.create(items, PsiModificationTracker.MODIFICATION_COUNT);
}, false
);
project.putUserData(DOT_ENV_VARIABLE_CACHE, cache);
}
return cache.getValue();
}
@NotNull
public static Collection<PsiElement> getEnvironmentVariableTargets(@NotNull Project project, @NotNull String environmentVariable) {
Collection<PsiElement> items = new ArrayList<>();
DotEnvUtil.visitEnvironment(project, pair -> {
if(environmentVariable.equals(pair.getFirst())) {
items.add(pair.getSecond());
}
});
return items;
}
private static void visitEnvironment(@NotNull Project project, @NotNull Consumer<Pair<String, PsiElement>> consumer) {
for (VirtualFile virtualFile : FilenameIndex.getAllFilesByExt(project, "env", GlobalSearchScope.allScope(project))) {
Properties variables = new Properties();
try {
variables.load(virtualFile.getInputStream());
} catch (IOException e) {
continue;
}
PsiFile file = PsiManager.getInstance(project).findFile(virtualFile);
if(file == null) {
continue;
}
for (Map.Entry<Object, Object> variable : variables.entrySet()) {
Object key = variable.getKey();
if(key instanceof String) {
consumer.accept(Pair.create(key.toString(), file));
}
}
}
for (PsiFile psiFile : FilenameIndex.getFilesByName(project, "docker-compose.yml", GlobalSearchScope.allScope(project))) {
if(!(psiFile instanceof YAMLFile)) {
continue;
}
for (YAMLKeyValue yamlKeyValue : YAMLUtil.getTopLevelKeys((YAMLFile) psiFile)) {
if ("services".equals(yamlKeyValue.getKeyText())) {
PsiElement yamlKeyValueLastChild = yamlKeyValue.getLastChild();
if (yamlKeyValueLastChild instanceof YAMLMapping) {
for (YAMLKeyValue keyValue : ((YAMLMapping) yamlKeyValueLastChild).getKeyValues()) {
visitEnvironmentSquenceItems(consumer, keyValue);
}
}
}
visitEnvironmentSquenceItems(consumer, yamlKeyValue);
}
}
for (String file : DOCKER_FILES) {
for (PsiFile psiFile : FilenameIndex.getFilesByName(project, file, GlobalSearchScope.allScope(project))) {
Matcher matcher = Pattern.compile("ENV\\s+([^\\s]*)\\s+").matcher(psiFile.getText());
while(matcher.find()){
consumer.accept(Pair.create(matcher.group(1), psiFile));
}
}
}
}
/**
* environment:
* - FOOBAR=0
*/
private static void visitEnvironmentSquenceItems(@NotNull Consumer<Pair<String, PsiElement>> consumer, @NotNull YAMLKeyValue yamlKeyValue) {
YAMLKeyValue environment = YamlHelper.getYamlKeyValue(yamlKeyValue, "environment");
if (environment != null) {
for (YAMLSequenceItem yamlSequenceItem : YamlHelper.getSequenceItems(environment)) {
YAMLValue value = yamlSequenceItem.getValue();
if (value instanceof YAMLScalar) {
String textValue = ((YAMLScalar) value).getTextValue();
if (StringUtils.isNotBlank(textValue)) {
String[] split = textValue.split("=");
if (split.length > 1) {
consumer.accept(Pair.create(split[0], value));
}
}
}
}
}
}
}