package fr.adrienbrault.idea.symfony2plugin.stubs.cache;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
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 com.intellij.util.indexing.FileBasedIndex;
import com.intellij.util.indexing.ID;
import fr.adrienbrault.idea.symfony2plugin.stubs.SymfonyProcessors;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* All FileBasedIndex are slow and cross project data, we need them every often
* Cache values as long nothing globally change in our project.
*
* @author Daniel Espendiller <daniel@espendiller.net>
*/
public class FileIndexCaches {
/**
* @param dataHolderKey Main data to cache
* @param dataHolderNames Cache extracted name Set
*/
static public synchronized <T> Map<String, List<T>> getSetDataCache(@NotNull final Project project, @NotNull Key<CachedValue<Map<String, List<T>>>> dataHolderKey, final @NotNull Key<CachedValue<Set<String>>> dataHolderNames, @NotNull final ID<String, T> ID, @NotNull final GlobalSearchScope scope) {
CachedValue<Map<String, List<T>>> cache = project.getUserData(dataHolderKey);
if(cache == null) {
cache = CachedValuesManager.getManager(project).createCachedValue(() -> {
Map<String, List<T>> items = new HashMap<>();
final FileBasedIndex fileBasedIndex = FileBasedIndex.getInstance();
getIndexKeysCache(project, dataHolderNames, ID).stream().forEach(service ->
items.put(service, fileBasedIndex.getValues(ID, service, scope))
);
return CachedValueProvider.Result.create(items, PsiModificationTracker.MODIFICATION_COUNT);
}, false);
project.putUserData(dataHolderKey, cache);
}
return cache.getValue();
}
/**
* @param dataHolderKey Main data to cache
* @param dataHolderNames Cache extracted name Set
*/
static public synchronized Map<String, List<String>> getStringDataCache(@NotNull final Project project, @NotNull Key<CachedValue<Map<String, List<String>>>> dataHolderKey, final @NotNull Key<CachedValue<Set<String>>> dataHolderNames, @NotNull final ID<String, String> ID, @NotNull final GlobalSearchScope scope) {
CachedValue<Map<String, List<String>>> cache = project.getUserData(dataHolderKey);
if(cache == null) {
cache = CachedValuesManager.getManager(project).createCachedValue(() -> {
Map<String, List<String>> strings = new HashMap<>();
final FileBasedIndex fileBasedIndex = FileBasedIndex.getInstance();
getIndexKeysCache(project, dataHolderNames, ID).stream().forEach(parameterName -> {
// just for secure
if(parameterName == null) {
return;
}
strings.put(parameterName, fileBasedIndex.getValues(ID, parameterName, scope));
});
return CachedValueProvider.Result.create(strings, PsiModificationTracker.MODIFICATION_COUNT);
}, false);
project.putUserData(dataHolderKey, cache);
}
return cache.getValue();
}
/**
* There several methods that just need to check for names, as they also needed for value extraction, so cache them also
*/
static public synchronized Set<String> getIndexKeysCache(@NotNull final Project project, @NotNull Key<CachedValue<Set<String>>> dataHolderKey, @NotNull final ID<String, ?> ID) {
CachedValue<Set<String>> cache = project.getUserData(dataHolderKey);
if(cache == null) {
cache = CachedValuesManager.getManager(project).createCachedValue(() -> {
SymfonyProcessors.CollectProjectUniqueKeys projectUniqueKeys = new SymfonyProcessors.CollectProjectUniqueKeys(project, ID);
FileBasedIndex.getInstance().processAllKeys(ID, projectUniqueKeys, project);
return CachedValueProvider.Result.create(projectUniqueKeys.getResult(), PsiModificationTracker.MODIFICATION_COUNT);
}, false);
project.putUserData(dataHolderKey, cache);
}
return cache.getValue();
}
}