/**
* 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.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.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtensionDelta;
import org.eclipse.core.runtime.IExtensionRegistry;
import org.eclipse.core.runtime.IRegistryChangeEvent;
import org.eclipse.core.runtime.IRegistryChangeListener;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.preferences.DefaultScope;
import org.eclipse.core.runtime.preferences.IScopeContext;
import org.eclipse.core.runtime.preferences.InstanceScope;
import tern.eclipse.ide.core.DefaultTernModule;
import tern.eclipse.ide.core.IDefaultTernModulesProvider;
import tern.eclipse.ide.core.ITernNatureCapability;
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.resources.IDETernProject;
import tern.metadata.TernModuleMetadata;
import tern.resources.TernProject;
import tern.server.ITernModule;
import tern.utils.StringUtils;
import tern.utils.TernModuleHelper;
import com.eclipsesource.json.JsonObject;
/**
* Manager of tern nature adapters loaded by the extension point
* "ternNatureAdapters"
*
*/
public class TernNatureAdaptersManager implements IRegistryChangeListener {
private static final String EXTENSION_TERN_NATURE_ADAPTERS = "ternNatureAdapters";
private static final TernNatureAdaptersManager INSTANCE = new TernNatureAdaptersManager();
// cached copy of all tern nature adapaters
private Map<ITernNatureCapability, List<DefaultTernModule>> ternNatureAdapters;
private boolean registryListenerIntialized;
public static TernNatureAdaptersManager getManager() {
return INSTANCE;
}
public TernNatureAdaptersManager() {
this.registryListenerIntialized = false;
}
@Override
public void registryChanged(final IRegistryChangeEvent event) {
IExtensionDelta[] deltas = event.getExtensionDeltas(
TernCorePlugin.PLUGIN_ID, EXTENSION_TERN_NATURE_ADAPTERS);
if (deltas != null) {
for (IExtensionDelta delta : deltas)
handleTernNatureAdapterDelta(delta);
}
}
/**
* Load the tern server types.
*/
private synchronized void loadTernNatureAdapters() {
if (ternNatureAdapters != null)
return;
Trace.trace(Trace.EXTENSION_POINT,
"->- Loading .ternNatureAdapters extension point ->-");
IExtensionRegistry registry = Platform.getExtensionRegistry();
IConfigurationElement[] cf = registry.getConfigurationElementsFor(
TernCorePlugin.PLUGIN_ID, EXTENSION_TERN_NATURE_ADAPTERS);
Map<ITernNatureCapability, List<DefaultTernModule>> map = new HashMap<ITernNatureCapability, List<DefaultTernModule>>(
cf.length);
addTernNatureAdapters(cf, map);
addRegistryListenerIfNeeded();
ternNatureAdapters = map;
Trace.trace(Trace.EXTENSION_POINT,
"-<- Done loading .ternNatureAdapters extension point -<-");
}
/**
* Load the tern project describers.
*/
private synchronized void addTernNatureAdapters(IConfigurationElement[] cf,
Map<ITernNatureCapability, List<DefaultTernModule>> map) {
for (IConfigurationElement ce : cf) {
String id = ce.getAttribute("id");
String className = ce.getAttribute("class");
try {
if (!StringUtils.isEmpty(className)) {
map.put((ITernNatureCapability) ce
.createExecutableExtension("class"),
getDefaultModules(ce));
} else if (!StringUtils.isEmpty(id)) {
map.put(new DefaultTernNatureAdapter(id),
getDefaultModules(ce));
}
Trace.trace(Trace.EXTENSION_POINT,
" Loaded project describer: " + id != null ? id
: className != null ? className : "");
} catch (Throwable t) {
Trace.trace(
Trace.SEVERE,
" Could not load project describers: " + id != null ? id
: className != null ? className : "", t);
}
}
}
private List<DefaultTernModule> getDefaultModules(IConfigurationElement ce) {
List<DefaultTernModule> defaultModules = new ArrayList<DefaultTernModule>();
for (IConfigurationElement dmce : ce.getChildren("defaultModules")) {
for (IConfigurationElement mce : dmce.getChildren("module")) {
String module = mce.getAttribute("name");
if (module != null && !module.trim().isEmpty()) {
String name = module.trim();
boolean withDependencies = StringUtils.asBoolean(
mce.getAttribute("withDependencies"), false);
JsonObject options = getOptions(mce.getAttribute("options"));
defaultModules.add(new DefaultTernModule(name,
withDependencies, options));
}
}
}
return defaultModules;
}
private JsonObject getOptions(String options) {
if (!StringUtils.isEmpty(options)) {
try {
return JsonObject.readFrom(options);
} catch (Throwable e) {
}
}
return null;
}
protected void handleTernNatureAdapterDelta(IExtensionDelta delta) {
if (ternNatureAdapters == null) // not loaded yet
return;
IConfigurationElement[] cf = delta.getExtension()
.getConfigurationElements();
Map<ITernNatureCapability, List<DefaultTernModule>> map = new HashMap<ITernNatureCapability, List<DefaultTernModule>>(
ternNatureAdapters);
if (delta.getKind() == IExtensionDelta.ADDED) {
addTernNatureAdapters(cf, map);
} else {
}
ternNatureAdapters = map;
}
private void addRegistryListenerIfNeeded() {
if (registryListenerIntialized)
return;
IExtensionRegistry registry = Platform.getExtensionRegistry();
registry.addRegistryChangeListener(this, TernCorePlugin.PLUGIN_ID);
registryListenerIntialized = true;
}
public void initialize() {
}
public void destroy() {
if (ternNatureAdapters == null) // not loaded yet
return;
Platform.getExtensionRegistry().removeRegistryChangeListener(this);
}
/**
* Returns true if the given project has tern nature and false otherwise.
*
* @param project
* @return
*/
public boolean hasTernNature(IProject project) {
if (project.isAccessible()) {
try {
// test if project has a .tern-project file
if (project.getFile(TernProject.TERN_PROJECT_FILE).exists())
return true;
// use tern nature adapaters
Map<ITernNatureCapability, List<DefaultTernModule>> ternNatureAdapters = getTernNatureAdapters();
for (ITernNatureCapability natureAdapter : ternNatureAdapters
.keySet()) {
if (natureAdapter.hasTernNature(project)) {
return true;
}
}
} catch (CoreException e) {
Trace.trace(Trace.SEVERE, "Error tern nature", e);
}
}
return false;
}
private Map<ITernNatureCapability, List<DefaultTernModule>> getTernNatureAdapters() {
if (ternNatureAdapters == null) {
loadTernNatureAdapters();
}
return ternNatureAdapters;
}
/**
* Add default modules for the given tern project.
*
* @param ternProject
* tern project
* @throws CoreException
*/
public void addDefaultModules(IDETernProject ternProject)
throws CoreException {
if (ternProject.hasModules()) {
// tern project is not empty, don't add default modules
return;
}
List<ITernModule> contributedModules = new ArrayList<ITernModule>();
Map<ITernModule, JsonObject> moduleOptions = new HashMap<ITernModule, JsonObject>();
// add default module from preferences
ITernModule moduleFromPreferences = null;
ITernModule[] modulesFromPreferences = getModulesFromPreferences(ternProject);
for (int i = 0; i < modulesFromPreferences.length; i++) {
moduleFromPreferences = modulesFromPreferences[i];
if (!contributedModules.contains(moduleFromPreferences)) {
contributedModules.add(moduleFromPreferences);
}
}
// Add default module from extension point.
ITernRepositoryManager repositoryManager = TernCorePlugin
.getTernRepositoryManager();
Map<ITernNatureCapability, List<DefaultTernModule>> ternNatureAdapters = getTernNatureAdapters();
for (ITernNatureCapability natureAdapter : ternNatureAdapters.keySet()) {
if (natureAdapter.hasTernNature(ternProject.getProject())) {
Collection<DefaultTernModule> defaultModules = ternNatureAdapters
.get(natureAdapter);
// collect static default modules
for (DefaultTernModule defaultModule : defaultModules) {
collectionDefaultModule(defaultModule, ternProject,
contributedModules, moduleOptions,
repositoryManager);
}
// collect dynamic default modules
if (natureAdapter instanceof IDefaultTernModulesProvider) {
defaultModules = ((IDefaultTernModulesProvider) natureAdapter)
.getTernModules(ternProject.getProject());
if (defaultModules != null) {
for (DefaultTernModule defaultModule : defaultModules) {
collectionDefaultModule(defaultModule, ternProject,
contributedModules, moduleOptions,
repositoryManager);
}
}
}
}
}
// sort modules
TernModuleHelper.sort(contributedModules);
// loop for collected sorted modules
JsonObject options = null;
for (ITernModule module : contributedModules) {
options = moduleOptions.get(module);
TernModuleHelper.update(module, options, ternProject);
}
}
private void collectionDefaultModule(DefaultTernModule defaultModule,
IDETernProject ternProject, List<ITernModule> contributedModules,
Map<ITernModule, JsonObject> moduleOptions,
ITernRepositoryManager repositoryManager) {
ITernModule module = repositoryManager.findTernModule(
defaultModule.getName(), ternProject);
if (module != null) {
if (!contributedModules.contains(module)) {
contributedModules.add(module);
if (defaultModule.getOptions() != null) {
moduleOptions.put(module, defaultModule.getOptions());
}
}
if (defaultModule.isWithDependencies()) {
// add modules dependencies
TernModuleMetadata metadata = module.getMetadata();
if (metadata != null) {
Collection<String> dependencies = metadata
.getDependencies(module.getVersion());
for (String dependency : dependencies) {
ITernModule dependencyModule = repositoryManager
.findTernModule(dependency, ternProject);
if (dependencyModule != null) {
if (!contributedModules.contains(dependencyModule)) {
contributedModules.add(dependencyModule);
}
}
}
}
}
}
}
private ITernModule[] getModulesFromPreferences(IDETernProject ternProject) {
IScopeContext[] lookupOrder = new IScopeContext[] {
InstanceScope.INSTANCE, DefaultScope.INSTANCE };
String moduleNames = Platform.getPreferencesService().getString(
TernCorePlugin.getDefault().getBundle().getSymbolicName(),
TernCorePreferenceConstants.DEFAULT_TERN_MODULES,
TernCorePreferenceConstants.DEFAULT_TERN_MODULES_VALUE,
lookupOrder);
return TernCorePlugin.getTernRepositoryManager().getTernModules(
moduleNames, ternProject);
}
}