/** * Copyright (c) 2013-2016 Angelo ZERR. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation */ package tern.eclipse.ide.internal.core; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceChangeEvent; import org.eclipse.core.resources.IResourceChangeListener; import org.eclipse.core.resources.IResourceDelta; import org.eclipse.core.resources.IResourceDeltaVisitor; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.preferences.InstanceScope; import com.eclipsesource.json.JsonArray; import com.eclipsesource.json.JsonObject; import com.eclipsesource.json.JsonValue; import tern.TernException; import tern.eclipse.ide.core.IIDETernProject; import tern.eclipse.ide.core.IIDETernRepository; import tern.eclipse.ide.core.ITernRepositoryManager; import tern.eclipse.ide.core.TernCorePlugin; import tern.eclipse.ide.core.preferences.TernCorePreferenceConstants; import tern.eclipse.ide.internal.core.preferences.TernCorePreferencesSupport; import tern.eclipse.ide.internal.core.resources.IDETernProject; import tern.server.ITernModule; import tern.server.ITernPlugin; import tern.utils.StringUtils; import tern.utils.TernModuleHelper; /** * Manager of tern repository. * */ public class TernRepositoryManager implements ITernRepositoryManager, IResourceChangeListener, IResourceDeltaVisitor { private static final TernRepositoryManager INSTANCE = new TernRepositoryManager(); private static IIDETernRepository DEFAULT_REPOSITORY; public static TernRepositoryManager getManager() { return INSTANCE; } private final Map<String, IIDETernRepository> repositories; public TernRepositoryManager() { this.repositories = new HashMap<String, IIDETernRepository>(); } public void initialize() { ResourcesPlugin.getWorkspace().addResourceChangeListener(this); } public void dispose() { ResourcesPlugin.getWorkspace().removeResourceChangeListener(this); } @Override public Collection<IIDETernRepository> getRepositories() { loadRepositoriesIfNeeded(); return repositories.values(); } /** * Load tern repositories if needed. */ private void loadRepositoriesIfNeeded() { if (repositories.size() == 0) { loadRepositories(); } } /** * Load tern repositories. */ private synchronized void loadRepositories() { if (repositories.size() > 0) { // already loaded. return; } repositories.clear(); // default repositories loadDefaultRepositories(); // custom repositories loadCustomRepositories(); // install external modules installExternalModules(); } /** * Deploy module contributions declared with extension point * "tern.eclipse.ide.core.ternModuleContributions" in each tern * repositories. */ private void installExternalModules() { TernModuleInstall[] modules = TernModuleInstallManager.getManager().getTernModuleInstalls(); for (TernModuleInstall module : modules) { Collection<IIDETernRepository> reps = repositories.values(); for (IIDETernRepository repository : reps) { try { repository.install(module.getSrc()); } catch (Throwable e) { Trace.trace(Trace.SEVERE, "Cannot install module" + module.getName(), e); } } } } /** * Load default tern repositories. */ private void loadDefaultRepositories() { addRepository(getDefaultRepository(), repositories); } /** * Load custom tern repositories. */ private void loadCustomRepositories() { String values = new InstanceScope().getNode(TernCorePlugin.getDefault().getBundle().getSymbolicName()) .get(TernCorePreferenceConstants.REPOSITORIES, null); if (!StringUtils.isEmpty(values)) { String[] s = values.split(REPOSITORY_SEPARATOR); String name = null; File baseDir = null; for (int i = 0; i < s.length / 2; i++) { name = s[i]; baseDir = new File(s[i + 1]); addRepository(createRepository(name, baseDir), repositories); } } } @Override public IIDETernRepository createRepository(String name, File baseDir) { return createRepository(name, baseDir, false); } private IIDETernRepository createRepository(String name, File baseDir, boolean isDefault) { return new IDETernRepository(name, baseDir, isDefault); } @Override public IIDETernRepository getDefaultRepository() { if (DEFAULT_REPOSITORY != null) { return DEFAULT_REPOSITORY; } try { DEFAULT_REPOSITORY = createDefaultRepository(); } catch (Throwable e) { Trace.trace(Trace.SEVERE, "Cannot load the default tern repository.", e); } return DEFAULT_REPOSITORY; } private synchronized IIDETernRepository createDefaultRepository() throws TernException, IOException { if (DEFAULT_REPOSITORY != null) { return DEFAULT_REPOSITORY; } return createRepository(DEFAULT_REPOSITORY_NAME, TernCorePlugin.getTernRepositoryBaseDir(), true); } private void addRepository(IIDETernRepository repository, Map<String, IIDETernRepository> repositories) { if (repository != null) { repositories.put(repository.getName(), repository); } } @Override public IIDETernRepository getRepository(String name) { loadRepositoriesIfNeeded(); return repositories.get(name); } private IIDETernRepository getRepository(IIDETernProject ternProject) { return getRepository(ternProject != null ? ternProject.getProject() : null); } @Override public IIDETernRepository getRepository(IProject project) { loadRepositoriesIfNeeded(); String name = TernCorePreferencesSupport.getInstance().getUsedTernRepositoryName(project); if (!StringUtils.isEmpty(name)) { IIDETernRepository repository = getRepository(name); if (repository != null) { return repository; } } return getDefaultRepository(); } @Override public void setRepositories(Collection<IIDETernRepository> repositories) { StringBuilder value = new StringBuilder(); for (IIDETernRepository repository : repositories) { if (!repository.isDefault()) { if (value.length() > 0) { value.append(REPOSITORY_SEPARATOR); } value.append(repository.getName()); value.append(REPOSITORY_SEPARATOR); value.append(repository.getTernBaseDirAsString()); } } new InstanceScope().getNode(TernCorePlugin.getDefault().getBundle().getSymbolicName()) .put(TernCorePreferenceConstants.REPOSITORIES, value.toString()); loadRepositories(); } @Override public List<ITernModule> getCheckedModules(IIDETernProject ternProject, List<ITernModule> allModules) { List<ITernModule> checkedModules = new ArrayList<ITernModule>(); // Tern Plugins JsonValue options = null; JsonObject plugins = ternProject.getPlugins(); for (String name : plugins.names()) { options = plugins.get(name); ITernModule plugin = findTernModule(name.toString(), ternProject); updateCheckedModule(plugin, options, allModules, checkedModules); } // JSON Type Definitions JsonArray defs = ternProject.getLibs(); for (JsonValue name : defs) { ITernModule def = findTernModule(name.asString(), ternProject); updateCheckedModule(def, null, allModules, checkedModules); } return checkedModules; } @Override public List<ITernModule> getCheckedModules(String[] moduleNames, List<ITernModule> allModules, List<ITernModule> groupedModules) { List<ITernModule> checkedModules = new ArrayList<ITernModule>(); for (String name : moduleNames) { ITernModule module = findTernModule(name, allModules); updateCheckedModule(module, null, groupedModules, checkedModules); } return checkedModules; } /** * Update the checked modules with the given module. * * @param module * @param options * @param allModules * @param checkedModules */ private void updateCheckedModule(ITernModule module, JsonValue options, List<ITernModule> allModules, List<ITernModule> checkedModules) { if (module != null) { if (!TernModuleHelper.isConfigurableModule(module)) { addModule(module, checkedModules); } else { try { addModule(TernModuleHelper.findConfigurable(module, options, allModules), checkedModules); } catch (TernException e) { Trace.trace(Trace.SEVERE, "Error while finding configurable module.", e); } } } } private void addModule(ITernModule module, List<ITernModule> checkedModules) { if (!checkedModules.contains(module)) { checkedModules.add(module); } } @Override public ITernModule findTernModule(String name, IIDETernProject ternProject) { IIDETernRepository repository = getRepository(ternProject); ITernModule m = repository.getModule(name); if (m != null) { return m; } if (ternProject != null) { List<ITernModule> projectModules = ternProject.getProjectModules(); return findTernModule(name, projectModules); } return null; } private ITernModule findTernModule(String name, List<ITernModule> projectModules) { if (projectModules != null) { for (ITernModule module : projectModules) { if (module.getName().equals(name)) { return module; } } } return null; } @Override public ITernModule[] getTernModules(String moduleNames, IDETernProject ternProject) { ITernModule module = null; List<ITernModule> modules = new ArrayList<ITernModule>(); String[] names = moduleNames.split(","); for (int i = 0; i < names.length; i++) { module = findTernModule(names[i], ternProject); if (module != null) { addModule(module, modules); } } return modules.toArray(ITernPlugin.EMPTY_MODULE); } @Override public void resourceChanged(IResourceChangeEvent event) { try { IResource resource = event.getResource(); switch (event.getType()) { case IResourceChangeEvent.PRE_DELETE: // called when project is deleted. case IResourceChangeEvent.PRE_CLOSE: // called when project is closed. if (resource != null && resource.getType() == IResource.PROJECT) { IProject project = (IProject) resource; disconnectProject(project); } break; case IResourceChangeEvent.POST_CHANGE: IResourceDelta delta = event.getDelta(); if (delta != null) { delta.accept(this); } break; } } catch (Throwable e) { Trace.trace(Trace.SEVERE, "Error while tern repository synchronization", e); } } private void connectProject(IProject project) { IPath location = project.getLocation(); if (location != null) { for (IIDETernRepository repository : repositories.values()) { if (location.equals(repository.getLocation())) { ((IDETernRepository) repository).setProject(project); } } } } private void disconnectProject(IProject project) { for (IIDETernRepository repository : repositories.values()) { if (project.equals(repository.getProject())) { ((IDETernRepository) repository).setProject(null); } } } @Override public boolean visit(IResourceDelta delta) throws CoreException { IResource resource = delta.getResource(); if (resource == null) { return false; } switch (resource.getType()) { case IResource.ROOT: return true; case IResource.PROJECT: IProject project = (IProject) resource; if (project.isOpen() && (delta.getKind() == IResourceDelta.CHANGED || delta.getKind() == IResourceDelta.ADDED) && (delta.getFlags() & IResourceDelta.OPEN) != 0) { connectProject(project); } return false; } return false; } }