/* * 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); } } }