/** * Copyright (C) 2010 STMicroelectronics * * This file is part of "Mind Compiler" is free software: you can redistribute * it and/or modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * Contact: mind@ow2.org * * Authors: Matthieu Leclercq * Contributors: */ package org.ow2.mind.inject; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.objectweb.fractal.adl.CompilerError; import org.objectweb.fractal.adl.error.GenericErrors; import org.ow2.mind.plugin.ConfigurationElement; import org.ow2.mind.plugin.PluginManager; import org.ow2.mind.plugin.util.Assert; import org.ow2.mind.plugin.util.BooleanEvaluatorHelper; import com.google.inject.Module; import com.google.inject.util.Modules; /** * Helper class for the Guice Module extension point. */ public final class GuiceModuleExtensionHelper { /** The extension point ID. */ public static final String GUICE_MODULE_EXTENSION_POINT_ID = "org.ow2.mind.plugin.guice-module"; private static final String CLASS = "class"; private static final String ENABLE_WHEN = "enableWhen"; private static final String OVERRIDE = "override"; private static final String COMBINE = "combine"; private static Iterable<Module> modules; private GuiceModuleExtensionHelper() { } /** * Returns the modules that has been registered using the Guice Module * extension point. * * @param pluginManager the pluginManager. * @param context the compilation context. * @return the modules that has been registered using the Guice Module * extension points. */ public static synchronized Iterable<Module> getModules( final PluginManager pluginManager, final Map<Object, Object> context) { if (modules == null) { initializeModules(pluginManager, context); } return modules; } private static void initializeModules(final PluginManager pluginManager, final Map<Object, Object> context) { final Map<String, SimpleModuleDesc> moduleDescs = new HashMap<String, SimpleModuleDesc>(); for (final ConfigurationElement module : pluginManager .getConfigurationElements(GUICE_MODULE_EXTENSION_POINT_ID)) { final String clazz = module.getAttribute(CLASS); if (moduleDescs.containsKey(clazz)) { Assert.fail("Module class '" + clazz + "' is already used"); } final ConfigurationElement condition = module.getChild(ENABLE_WHEN); if (condition == null || BooleanEvaluatorHelper.evaluate(condition.getChild(), pluginManager, context)) { moduleDescs.put(clazz, new SimpleModuleDesc(module)); } } // process combine directives final Map<String, ModuleDesc> combinedModules = new HashMap<String, ModuleDesc>( moduleDescs.size()); for (final SimpleModuleDesc moduleDesc : moduleDescs.values()) { final ConfigurationElement combine = moduleDesc.desc.getChild(COMBINE); if (combine != null) { final String otherClass = combine.getAttribute(CLASS); final SimpleModuleDesc otherModuleDesc = moduleDescs.get(otherClass); if (otherModuleDesc == null) { throw new CompilerError(GenericErrors.GENERIC_ERROR, "Unknown module class '" + otherModuleDesc + "'"); } final ModuleDesc combinedModule = combinedModules.get(moduleDesc.clazz); if (combinedModule != null) { assert combinedModule instanceof CombinedModuleDesc; final ModuleDesc otherCombinedModule = combinedModules .get(otherClass); if (otherCombinedModule != null) { if (otherCombinedModule instanceof CombinedModuleDesc) { // merge the two combined modules ((CombinedModuleDesc) combinedModule) .merge((CombinedModuleDesc) otherCombinedModule); for (final SimpleModuleDesc desc : ((CombinedModuleDesc) otherCombinedModule).combinedModules) { assert combinedModules.get(desc.clazz) == otherCombinedModule; combinedModules.put(desc.clazz, combinedModule); } } } else { ((CombinedModuleDesc) combinedModule).add(otherModuleDesc); combinedModules.put(otherClass, combinedModule); } } else { // combinedModule == null final ModuleDesc otherCombinedModule = combinedModules .get(otherClass); if (otherCombinedModule == null || otherCombinedModule instanceof SimpleModuleDesc) { final CombinedModuleDesc m = new CombinedModuleDesc(); m.add(moduleDesc); m.add(otherModuleDesc); combinedModules.put(moduleDesc.clazz, m); combinedModules.put(otherClass, m); } else { assert otherCombinedModule instanceof CombinedModuleDesc; ((CombinedModuleDesc) otherCombinedModule).add(moduleDesc); combinedModules.put(moduleDesc.clazz, otherCombinedModule); } } } else { final ModuleDesc combinedModule = combinedModules.get(moduleDesc.clazz); if (combinedModule != null) { assert combinedModule instanceof CombinedModuleDesc; ((CombinedModuleDesc) combinedModule).add(moduleDesc); combinedModules.put(moduleDesc.clazz, combinedModule); } else { combinedModules.put(moduleDesc.clazz, moduleDesc); } } } // process override directives for (final SimpleModuleDesc moduleDesc : moduleDescs.values()) { final ConfigurationElement override = moduleDesc.desc.getChild(OVERRIDE); if (override != null) { final String overriddenClass = override.getAttribute(CLASS); final ModuleDesc overriddenModule = combinedModules .get(overriddenClass); if (overriddenModule == null) { Assert.fail("Unknown module class '" + overriddenClass + "'"); } else { overriddenModule.overridingModules.add(moduleDesc); } } } final Set<Module> result = new HashSet<Module>(combinedModules.size()); for (final ModuleDesc moduleDesc : combinedModules.values()) { if (moduleDesc.isPrimary) { result.add(moduleDesc.getModule()); } } modules = result; } private static abstract class ModuleDesc { Module module; boolean loadingModule; Collection<ModuleDesc> overridingModules = new ArrayList<ModuleDesc>(); boolean isPrimary = true; Module getModule() { if (module == null) { if (loadingModule) { // cycle in module inheritance graph Assert.fail("Cycle in module inheritance graph."); } loadingModule = true; try { final Module localModule = createLocalModule(); if (!overridingModules.isEmpty()) { final List<Module> modules = new ArrayList<Module>( overridingModules.size()); for (final ModuleDesc desc : overridingModules) { modules.add(desc.getModule()); } module = Modules.override(localModule).with(modules); } else { module = localModule; } } finally { loadingModule = false; } } return module; } abstract Module createLocalModule(); } private static class SimpleModuleDesc extends ModuleDesc { final String clazz; final ConfigurationElement desc; SimpleModuleDesc(final ConfigurationElement desc) { this.clazz = desc.getAttribute(CLASS); this.desc = desc; this.isPrimary = desc.getChild(OVERRIDE) == null; } @Override Module createLocalModule() { return desc.createInstance(CLASS, Module.class); }; } private static class CombinedModuleDesc extends ModuleDesc { Collection<SimpleModuleDesc> combinedModules = new ArrayList<SimpleModuleDesc>(); @Override Module createLocalModule() { final List<Module> modules = new ArrayList<Module>(combinedModules.size()); for (final SimpleModuleDesc desc : combinedModules) { modules.add(desc.getModule()); } return Modules.combine(modules); } void add(final SimpleModuleDesc moduleDesc) { combinedModules.add(moduleDesc); isPrimary &= moduleDesc.isPrimary; } void merge(final CombinedModuleDesc moduleDesc) { combinedModules.addAll(moduleDesc.combinedModules); isPrimary &= moduleDesc.isPrimary; } } }