/*
* Copyright 2003-2012 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jetbrains.mps.smodel.language;
import jetbrains.mps.classloading.ClassLoaderManager;
import jetbrains.mps.classloading.MPSClassesListener;
import jetbrains.mps.classloading.MPSClassesListenerAdapter;
import jetbrains.mps.components.CoreComponent;
import jetbrains.mps.module.ReloadableModuleBase;
import jetbrains.mps.project.Solution;
import jetbrains.mps.smodel.Language;
import jetbrains.mps.smodel.MPSModuleRepository;
import jetbrains.mps.smodel.structure.ExtensionDescriptor;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.mps.openapi.module.SModule;
import org.jetbrains.mps.openapi.module.SRepositoryListener;
import org.jetbrains.mps.openapi.module.SRepositoryListenerBase;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class ExtensionRegistry extends BaseExtensionRegistry implements CoreComponent {
private static Logger LOG = LogManager.getLogger(ExtensionRegistry.class);
private static ExtensionRegistry INSTANCE;
private Map<SModule, String> myModuleToNamespace = new HashMap<SModule, String>();
private HashMap<String, ExtensionDescriptor> myExtensionDescriptors = new HashMap<String, ExtensionDescriptor>();
private SRepositoryListener myListener = new MyModuleRepositoryAdapter();
@Nullable
private ClassLoaderManager myClm;
@Nullable
private MPSModuleRepository myRepo;
private final MPSClassesListener myClassesListener = new MPSClassesListenerAdapter() {
@Override
public void beforeClassesUnloaded(Set<? extends ReloadableModuleBase> unloadedModules) {
unloadExtensionDescriptors(unloadedModules);
}
@Override
public void afterClassesLoaded(Set<? extends ReloadableModuleBase> loadedModules) {
loadExtensionDescriptors(loadedModules);
}
};
public static ExtensionRegistry getInstance() {
return INSTANCE;
}
public ExtensionRegistry(@Nullable ClassLoaderManager clm, @Nullable MPSModuleRepository repo) {
myClm = clm;
myRepo = repo;
}
@Override
public void init() {
if (INSTANCE != null) {
throw new IllegalStateException("double initialization");
}
if (myRepo != null) {
myRepo.addRepositoryListener(myListener);
}
if (myClm != null) {
myClm.addClassesHandler(myClassesListener);
}
INSTANCE = this;
}
@Override
public void dispose() {
INSTANCE = null;
if (myClm != null) {
myClm.removeClassesHandler(myClassesListener);
}
if (myRepo != null) {
myRepo.removeRepositoryListener(myListener);
}
}
private void unloadExtensionDescriptors(Collection<? extends SModule> unloadedModules) {
for (SModule module : unloadedModules) {
final ExtensionDescriptor desc = myExtensionDescriptors.remove(module.getModuleName());
if (desc != null) {
unregisterExtensionDescriptor(desc);
}
}
}
private void loadExtensionDescriptors(Collection<? extends SModule> loadedModules) {
for (SModule module : loadedModules) {
String namespace = myModuleToNamespace.get(module);
if (namespace == null) {
namespace = module.getModuleName();
}
ExtensionDescriptor desc = findExtensionDescriptor(module);
if (desc != null) {
myModuleToNamespace.put(module, namespace);
myExtensionDescriptors.put(namespace, desc);
registerExtensionDescriptor(desc);
} else {
myModuleToNamespace.remove(module);
}
}
}
public void registerExtensionDescriptor(ExtensionDescriptor extensionDescriptor) {
registerExtensions(extensionDescriptor.getExtensions());
registerExtensionPoints(extensionDescriptor.getExtensionPoints());
}
public void unregisterExtensionDescriptor(ExtensionDescriptor extensionDescriptor) {
unregisterExtensionPoints(extensionDescriptor.getExtensionPoints());
unregisterExtensions(extensionDescriptor.getExtensions());
}
private ExtensionDescriptor findExtensionDescriptor(SModule mod) {
if (mod instanceof Language) {
return findLanguageExtensionDescriptor((Language) mod);
} else if (mod instanceof Solution) {
switch (((Solution) mod).getKind()) {
case PLUGIN_CORE:
case PLUGIN_EDITOR:
case PLUGIN_OTHER:
return findPluginSolutionExtensionDescriptor((Solution) mod);
default:
break;
}
}
return null;
}
private ExtensionDescriptor findPluginSolutionExtensionDescriptor(Solution solution) {
// TODO: more flexible way of loading extensions from plugin solution
String namespace = solution.getModuleName();
String className = namespace + ".plugin.ExtensionDescriptor";
Object compiled = getObjectByClassName(className, solution, true);
if (compiled instanceof ExtensionDescriptor) {
return (ExtensionDescriptor) compiled;
}
return null;
}
private ExtensionDescriptor findLanguageExtensionDescriptor(Language lang) {
String namespace = lang.getModuleName();
String className = namespace + ".plugin.ExtensionDescriptor";
Object compiled = getObjectByClassName(className, lang, true);
if (compiled instanceof ExtensionDescriptor) {
return (ExtensionDescriptor) compiled;
}
return null;
}
@Nullable
public static Object getObjectByClassName(String className, @Nullable SModule module, boolean avoidLogErrors) {
try {
if (module == null) {
return null;
}
Class clazz = ClassLoaderManager.getInstance().getOwnClass(module, className);
if (clazz == null) {
return null;
}
return clazz.newInstance();
} catch (Throwable e) {
LOG.debug("error loading class\"" + className + "\"", e);
}
return null;
}
private class MyModuleRepositoryAdapter extends SRepositoryListenerBase {
@Override
public void moduleAdded(@NotNull SModule module) {
// awaiting next classes reload?
}
@Override
public void beforeModuleRemoved(@NotNull SModule module) {
String namespace = myModuleToNamespace.get(module);
if (namespace == null) return;
ExtensionDescriptor desc = myExtensionDescriptors.remove(namespace);
if (desc == null) return;
unregisterExtensionDescriptor(desc);
myModuleToNamespace.remove(module);
}
}
}