package org.erlide.engine.internal.model.cache;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.eclipse.xtext.xbase.lib.Pair;
import org.erlide.engine.ErlangEngine;
import org.erlide.engine.model.IErlElement;
import org.erlide.engine.model.root.IErlModelChangeListener;
import org.erlide.engine.model.root.IErlModule;
import org.erlide.engine.model.root.IErlProject;
import org.erlide.engine.services.search.OpenService.ExternalTreeEntry;
import org.erlide.util.IDisposable;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
public class ErlModelCache implements IDisposable {
private static final int CACHE_SIZE = 200;
private static final int CACHE_TIME_MINUTES = 15;
private static final boolean disabled = Boolean
.valueOf(System.getProperty("erlide.noModelCache"));
private static volatile ErlModelCache fgInstance;
private final Cache<IErlModule, List<IErlModule>> moduleIncludeCache;
private final Cache<String, IErlModule> pathToModuleCache;
private final Cache<String, Pair<IErlProject, List<ExternalTreeEntry>>> externalTreeCache;
private final Map<String, IErlModule> editedModulesMap;
// private final Cache<String, Set<IErlModule>> nameToModuleCache;
private final ModelChangeListener modelChangeListener;
private final Cache<IErlProject, List<IErlModule>> projectModuleCache;
private final Cache<IErlProject, List<IErlModule>> projectIncludeCache;
public static ErlModelCache getDefault() {
if (fgInstance == null) {
fgInstance = disabled ? new DisabledErlModelCache() : new ErlModelCache();
}
return fgInstance;
}
private class ModelChangeListener implements IErlModelChangeListener {
// TODO should we handle changes of projects and includes too?
// Which is hard, since the IOldErlangProjectProperties doesn't have
// listeners
@Override
public void elementChanged(final IErlElement element) {
if (element instanceof IErlModule) {
final IErlModule module = (IErlModule) element;
moduleIncludeCache.invalidate(module);
}
}
}
private static <K, V> Cache<K, V> newCache() {
final Cache<K, V> cache = CacheBuilder.newBuilder().maximumSize(CACHE_SIZE)
.expireAfterAccess(CACHE_TIME_MINUTES, TimeUnit.MINUTES)
.initialCapacity(16).build();
return cache;
}
private ErlModelCache() {
pathToModuleCache = newCache();
editedModulesMap = Maps.newHashMap();
// nameToModuleCache = newCache();
moduleIncludeCache = newCache();
externalTreeCache = newCache();
projectModuleCache = newCache();
projectIncludeCache = newCache();
modelChangeListener = new ModelChangeListener();
ErlangEngine.getInstance().getModel().addModelChangeListener(modelChangeListener);
}
public void putModule(final IErlModule module) {
// final String moduleName = module.getModuleName();
// Set<IErlModule> modules = getModulesByName(moduleName);
// if (modules == null) {
// modules = Sets.newHashSet();
// }
// modules.add(module);
// nameToModuleCache.put(moduleName, modules);
final String path = module.getFilePath();
pathToModuleCache.put(path, module);
}
public void removeModule(final IErlModule module) {
// final String moduleName = module.getModuleName();
// nameToModuleCache.remove(moduleName);
final String path = module.getFilePath();
if (path != null) {
pathToModuleCache.invalidate(path);
}
}
// public Set<IErlModule> getModulesByName(final String moduleName) {
// final Set<IErlModule> modules = nameToModuleCache.get(moduleName);
// if (modules == null) {
// return Sets.newHashSet();
// }
// return Sets.newHashSet(modules);
// }
// public void put(final String path, final IErlModule module) {
// if (noModelCache) {
// return;
// }
// pathToModuleCache.put(path, module);
// }
public void putEdited(final String path, final IErlModule module) {
if (module == null) {
editedModulesMap.remove(path);
} else {
editedModulesMap.put(path, module);
}
}
public IErlModule getModuleByPath(final String path) {
final IErlModule module = editedModulesMap.get(path);
if (module != null) {
return module;
}
return pathToModuleCache.getIfPresent(path);
}
public void putIncludedFilesForModule(final IErlModule module,
final Collection<IErlModule> result) {
if (result == null) {
moduleIncludeCache.invalidate(module);
} else {
moduleIncludeCache.put(module, Lists.newArrayList(result));
}
}
public List<IErlModule> getIncludedFilesForModule(final IErlModule module) {
final List<IErlModule> modules = moduleIncludeCache.getIfPresent(module);
if (modules == null) {
return Lists.newArrayList();
}
return Lists.newArrayList(modules);
}
@Override
public void dispose() {
ErlangEngine.getInstance().getModel()
.removeModelChangeListener(modelChangeListener);
}
public void putExternalTree(final String externalPath, final IErlProject project,
final List<ExternalTreeEntry> externalTree) {
if (externalTree == null) {
externalTreeCache.invalidate(externalPath);
} else {
externalTreeCache.put(externalPath,
new Pair<IErlProject, List<ExternalTreeEntry>>(project,
Lists.newArrayList(externalTree)));
}
}
public List<ExternalTreeEntry> getExternalTree(final String externalPath) {
final Pair<IErlProject, List<ExternalTreeEntry>> tuple = externalTreeCache
.getIfPresent(externalPath);
if (tuple == null) {
return null;
}
final List<ExternalTreeEntry> entries = tuple.getValue();
if (entries == null) {
return null;
}
return Lists.newArrayList(entries);
}
public List<IErlModule> getModulesForProject(final IErlProject project) {
final List<IErlModule> modules = projectModuleCache.getIfPresent(project);
if (modules == null) {
return null;
}
return Lists.newArrayList(modules);
}
public List<IErlModule> getIncludesForProject(final IErlProject project) {
final List<IErlModule> includes = projectIncludeCache.getIfPresent(project);
if (includes == null) {
return null;
}
return Lists.newArrayList(includes);
}
public void putModulesForProject(final IErlProject project,
final List<IErlModule> modules) {
final List<String> moduleNames = Lists.newArrayList();
for (final IErlModule module : modules) {
moduleNames.add(module.getName());
}
// ErlLogger.debug("setModulesForProject %s %s", project.getName(),
// moduleNames);
projectModuleCache.put(project, Lists.newArrayList(modules));
}
public void putIncludesForProject(final IErlProject project,
final List<IErlModule> includes) {
final List<String> moduleNames = Lists.newArrayList();
for (final IErlModule module : includes) {
moduleNames.add(module.getName());
}
// ErlLogger.debug("setIncludesForProject %s %s", project.getName(),
// moduleNames);
projectIncludeCache.put(project, Lists.newArrayList(includes));
}
public void removeProject(final IErlProject project) {
// ErlLogger.debug("removeForProject %s", project.getName());
final List<IErlModule> includes = projectIncludeCache.getIfPresent(project);
if (includes != null) {
for (final IErlModule module : includes) {
moduleIncludeCache.invalidate(module);
}
}
final List<IErlModule> modules = projectModuleCache.getIfPresent(project);
if (modules != null) {
for (final IErlModule module : modules) {
moduleIncludeCache.invalidate(module);
}
}
projectIncludeCache.invalidate(project);
projectModuleCache.invalidate(project);
final Set<String> keysToRemove = Sets.newHashSet();
final Set<Entry<String, Pair<IErlProject, List<ExternalTreeEntry>>>> entrySet = externalTreeCache
.asMap().entrySet();
for (final Entry<String, Pair<IErlProject, List<ExternalTreeEntry>>> entry : entrySet) {
if (entry.getValue().getKey() == project) {
keysToRemove.add(entry.getKey());
}
}
for (final String keyToRemove : keysToRemove) {
externalTreeCache.invalidate(keyToRemove);
}
}
public void pathVarsChanged() {
// FIXME we need to clear some stuff here...
}
public void newProjectCreated() {
pathToModuleCache.invalidateAll();
// nameToModuleCache.clear();
}
public void putModules(final Collection<IErlModule> modules) {
for (final IErlModule module : modules) {
putModule(module);
}
}
public void clearModelCache() {
moduleIncludeCache.invalidateAll();
pathToModuleCache.invalidateAll();
externalTreeCache.invalidateAll();
// editedModulesMap.clear();
// nameToModuleCache.clear();
projectModuleCache.invalidateAll();
projectIncludeCache.invalidateAll();
}
private static class DisabledErlModelCache extends ErlModelCache {
@Override
public void putEdited(final String path, final IErlModule module) {
}
@Override
public void putExternalTree(final String externalPath, final IErlProject project,
final List<ExternalTreeEntry> externalTree) {
}
@Override
public void putIncludedFilesForModule(final IErlModule module,
final Collection<IErlModule> result) {
}
@Override
public void putIncludesForProject(final IErlProject project,
final List<IErlModule> includes) {
}
@Override
public void putModule(final IErlModule module) {
}
@Override
public void putModules(final Collection<IErlModule> modules) {
}
@Override
public void putModulesForProject(final IErlProject project,
final List<IErlModule> modules) {
}
}
}