package com.anjlab.eclipse.tapestry5;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.jdt.core.IType;
import com.anjlab.eclipse.tapestry5.watchdog.IEclipseClasspathListener;
public class TapestryModuleFactory implements IEclipseClasspathListener
{
// Cache for tapestry modules built from JARs: they're almost never change,
// but instantiating and initializing new module is a heavy operation.
//
// Theoretically in workspace we may have two module classes with the same name,
// but sourced from JARs with different versions, depending on project's classpath.
private Map<IType, Map<IProject, TapestryModule>> jarModules =
new HashMap<IType, Map<IProject, TapestryModule>>();
private Map<IType, Map<IProject, TapestryModule>> localModules = new HashMap<IType, Map<IProject, TapestryModule>>();
// TODO Monitor changes in config.json
@Override
public void classpathChanged(IFile classpath)
{
// Reset cache when classpath updated
// TODO Be more intelligent and check if some dependencies with tapestry
// modules remain the same
clearCache(classpath.getProject());
}
public void clearCache(IProject project)
{
clearProjectCache(project, localModules);
clearProjectCache(project, jarModules);
}
private void clearProjectCache(IProject project, Map<IType, Map<IProject, TapestryModule>> modulesCache)
{
Set<IType> emptyKeys = new HashSet<IType>();
for (Entry<IType, Map<IProject, TapestryModule>> entry : modulesCache.entrySet())
{
Map<IProject, TapestryModule> value = entry.getValue();
value.remove(project);
if (value.isEmpty())
{
emptyKeys.add(entry.getKey());
}
}
for (IType key : emptyKeys)
{
modulesCache.remove(key);
}
}
private interface ModuleCreator
{
TapestryModule createModule(TapestryProject project, IType moduleClass);
}
public TapestryModule createTapestryModule(
TapestryProject project,
IType moduleClass,
ObjectCallback<TapestryModule, RuntimeException> moduleCreated)
{
if (moduleClass.getResource() != null)
{
return getOrCreateModule(localModules, project, moduleClass, moduleCreated, new ModuleCreator()
{
@Override
public TapestryModule createModule(TapestryProject project, IType moduleClass)
{
return new LocalTapestryModule(project, moduleClass);
}
});
}
return getOrCreateModule(jarModules, project, moduleClass, moduleCreated, new ModuleCreator()
{
@Override
public TapestryModule createModule(TapestryProject project, IType moduleClass)
{
return new JarTapestryModule(project, moduleClass);
}
});
}
private TapestryModule getOrCreateModule(
Map<IType, Map<IProject, TapestryModule>> modulesCache,
TapestryProject project,
IType moduleClass,
ObjectCallback<TapestryModule, RuntimeException> moduleCreated,
ModuleCreator moduleCreator)
{
Map<IProject, TapestryModule> projectModules = modulesCache.get(moduleClass);
if (projectModules == null)
{
projectModules = new HashMap<IProject, TapestryModule>();
modulesCache.put(moduleClass, projectModules);
}
TapestryModule cachedModule = projectModules.get(project.getProject());
if (cachedModule != null)
{
return cachedModule;
}
final TapestryModule module = moduleCreator.createModule(project, moduleClass);
projectModules.put(project.getProject(), module);
notifyModuleCreated(moduleCreated, module);
return module;
}
private void notifyModuleCreated(
ObjectCallback<TapestryModule, RuntimeException> moduleCreated,
final TapestryModule module)
{
if (moduleCreated != null)
{
moduleCreated.callback(module);
}
}
public void localModuleChanged(IFile moduleClass)
{
Set<IType> emptyKeys = new HashSet<IType>();
for (Entry<IType, Map<IProject, TapestryModule>> entry : localModules.entrySet())
{
Map<IProject, TapestryModule> value = entry.getValue();
// Check if IType is from the same resource as moduleClass
boolean found = false;
for (TapestryModule module : value.values())
{
if (TapestryUtils.isModuleFile(moduleClass, module))
{
found = true;
break;
}
}
if (found)
{
emptyKeys.add(entry.getKey());
}
}
for (IType key : emptyKeys)
{
localModules.remove(key);
}
}
public void clearLocalCache(IProject project)
{
clearProjectCache(project, localModules);
}
}