/*
* (c) Copyright 2010-2011 AgileBirds
*
* This file is part of OpenFlexo.
*
* OpenFlexo is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* OpenFlexo 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenFlexo. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.openflexo.foundation.sg.implmodel;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Collection;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.io.IOUtils;
import org.openflexo.foundation.sg.implmodel.enums.TechnologyLayer;
import org.openflexo.foundation.sg.implmodel.exception.TechnologyModuleCompatibilityCheckException;
import org.openflexo.foundation.sg.implmodel.exception.TechnologyModuleInitializationException;
import org.openflexo.toolbox.FileResource;
import org.openflexo.toolbox.JavaResourceUtil;
import org.openflexo.xmlcode.XMLDecoder;
import org.openflexo.xmlcode.XMLMapping;
/**
* Contains the content of the module.xml at the {@link TechnologyModuleImplementation} creation
*
* @author ndaniels
*/
public abstract class TechnologyModuleDefinition {
private static final Logger logger = Logger.getLogger(TechnologyModuleDefinition.class.getPackage().getName());
public static File MODULES_DIR = new FileResource("Generator/TechnologyModules");
private static XMLMapping MODULE_MODEL;
private static Map<String, TechnologyModuleDefinition> technologyModuleDefinitionMap = null;
private TechnologyLayer technologyLayer;
private String name;
private String version;
private String description;
private LinkedHashSet<String> requiredModuleNames;
private LinkedHashSet<String> compatibleModuleNames;
private LinkedHashSet<String> incompatibleModuleNames;
public TechnologyModuleDefinition() throws TechnologyModuleInitializationException {
loadModule();
}
/**
* Return the path to the resources needed by this module. <br>
* Can be a path to a java resource folder (ie. in a jar) or a path relative to MODULES_DIR in Flexo files. <br>
* The returned path must NOT ends with a '/'. <br>
* The denoted folder must contains a module.xml file. <br>
* By default, this method will return the first folder containing a module.xml file. <br>
* Pay attention that this method will be called BEFORE the module initialization (thus before having its name/version/... set)
*
* @return the path to the resources needed by this module.
* @throws TechnologyModuleInitializationException
* if resource path cannot be found
*/
public String getResourcePath() {
for (String resourceName : JavaResourceUtil.getMatchingResources(this.getClass(), "module.xml")) {
if (resourceName.endsWith("/module.xml")) {
return resourceName.substring(0, resourceName.length() - "/module.xml".length());
}
}
throw new TechnologyModuleInitializationException("module.xml not found for module '" + this.getClass() + "' !");
}
/**
* Create a new instance of the appropriate {@link TechnologyModuleImplementation}.
*
* @param implementationModel
* @return the created {@link TechnologyModuleImplementation}
* @throws TechnologyModuleCompatibilityCheckException
* if there is incompatibility with existing module.
*/
public abstract TechnologyModuleImplementation createNewImplementation(ImplementationModel implementationModel)
throws TechnologyModuleCompatibilityCheckException;
/**
* Load and parse the module.xml file associated to this {@link TechnologyModuleDefinition}. <br>
* The variable of this TechnologyModuleDefinition must be fully initialized after this method call. <br>
* This method is called at SG module loading, it can be used to perform any initialization needed. <br>
* For example it will record all inspectors available in this jar. <br>
* Override the method to load the needed GUI elements in the module using SGModule.recordTechnologyModuleGUIFactory.
*
* @throws TechnologyModuleInitializationException
* if the module.xml file is not found, if it has parsing error or if any other exception occurred during initialization
*/
protected void loadModule() throws TechnologyModuleInitializationException {
InputStream inputStream = null;
try {
// Try to load from java resources
inputStream = this.getClass().getResourceAsStream(getResourcePath() + "/module.xml");
if (inputStream == null) {
File xmlFile = new File(new File(MODULES_DIR, getResourcePath()), "module.xml");
if (xmlFile.exists()) {
inputStream = new FileInputStream(xmlFile);
}
}
if (inputStream == null) {
logger.severe("Cannot find module.xml using path '" + getResourcePath() + "' !");
throw new TechnologyModuleInitializationException("Cannot find module.xml using path '" + getResourcePath() + "' !");
}
TechnologyModuleDefinitionDTO returned = (TechnologyModuleDefinitionDTO) XMLDecoder.decodeObjectWithMapping(inputStream,
getModuleModel());
fillFromDTO(returned);
// Load inspectors
SGJarInspectorGroup.INSTANCE.recordAllInspectors(getClass());
logger.info("TechnologyModule " + name + " loaded");
} catch (Exception e) {
logger.log(Level.SEVERE, "Cannot load module '" + getClass() + "' !", e);
if (e instanceof TechnologyModuleInitializationException) {
throw (TechnologyModuleInitializationException) e;
}
throw new TechnologyModuleInitializationException(e);
} finally {
IOUtils.closeQuietly(inputStream);
}
}
private void fillFromDTO(TechnologyModuleDefinitionDTO dto) {
this.technologyLayer = dto.technologyLayer;
this.name = dto.name;
this.description = dto.description;
this.version = dto.version;
this.requiredModuleNames = new LinkedHashSet<String>();
this.compatibleModuleNames = new LinkedHashSet<String>();
this.incompatibleModuleNames = new LinkedHashSet<String>();
for (TechnologyModuleDefinitionDTO.CompatibilityModule module : dto.requiredModuleList) {
this.requiredModuleNames.add(module.name);
}
for (TechnologyModuleDefinitionDTO.CompatibilityModule module : dto.compatibleModuleList) {
if (!this.requiredModuleNames.contains(module.name)) {
this.compatibleModuleNames.add(module.name);
}
}
for (TechnologyModuleDefinitionDTO.CompatibilityModule module : dto.incompatibleModuleList) {
if (!this.requiredModuleNames.contains(module.name) && !this.compatibleModuleNames.contains(module.name)) {
this.incompatibleModuleNames.add(module.name);
}
}
}
/**
* Recursively retrieve all modules required by this module, including itself. <br>
* Return a map containing for each recursion level the list of required module. <br>
* Order is preserved in each level.
*
* @return the retrieved required modules.
*/
public Map<Integer, LinkedHashSet<TechnologyModuleDefinition>> getAllRequiredModulesByLevel() {
Map<Integer, LinkedHashSet<TechnologyModuleDefinition>> requiredModules = new HashMap<Integer, LinkedHashSet<TechnologyModuleDefinition>>();
fillRequiredModules(requiredModules, 0);
return requiredModules;
}
/**
* Recursively retrieve all modules required by this module, including itself.
*
* @return the retrieved required modules.
*/
public Set<TechnologyModuleDefinition> getAllRequiredModules() {
Map<Integer, LinkedHashSet<TechnologyModuleDefinition>> requiredModules = getAllRequiredModulesByLevel();
LinkedHashSet<TechnologyModuleDefinition> result = new LinkedHashSet<TechnologyModuleDefinition>();
for (LinkedHashSet<TechnologyModuleDefinition> set : requiredModules.values()) {
result.addAll(set);
}
return result;
}
private void fillRequiredModules(Map<Integer, LinkedHashSet<TechnologyModuleDefinition>> requiredModules, int level) {
// Avoid infinite loop (module1 requires module2 and module2 requires module1)
for (LinkedHashSet<TechnologyModuleDefinition> set : requiredModules.values()) {
if (set.contains(this)) {
return;
}
}
LinkedHashSet<TechnologyModuleDefinition> set = requiredModules.get(level);
if (set == null) {
set = new LinkedHashSet<TechnologyModuleDefinition>();
requiredModules.put(level, set);
}
set.add(this);
for (TechnologyModuleDefinition moduleDefinition : getRequiredModules()) {
moduleDefinition.fillRequiredModules(requiredModules, level + 1);
}
}
protected static XMLMapping getModuleModel() {
if (MODULE_MODEL == null) {
File moduleModelFile = new FileResource("Models/TechnologyModules/ModuleModel.xml");
try {
MODULE_MODEL = new XMLMapping(moduleModelFile);
} catch (Exception e) {
// Warns about the exception
if (logger.isLoggable(Level.WARNING)) {
logger.warning("Exception raised: " + e.getClass().getName() + ". See console for details.");
}
e.printStackTrace();
}
}
return MODULE_MODEL;
}
/**
* Retrieve all {@link TechnologyModuleDefinition} available from classpath. <br>
* Map contains the TechnologyModuleDefinition name as key and the TechnologyModuleDefinition itself as value.
*
* @return the retrieved TechnologyModuleDefinition map.
*/
private static Map<String, TechnologyModuleDefinition> getTechnologyModuleDefinitionMap() {
if (technologyModuleDefinitionMap == null) {
technologyModuleDefinitionMap = new Hashtable<String, TechnologyModuleDefinition>();
ServiceLoader<TechnologyModuleDefinition> loader = ServiceLoader.load(TechnologyModuleDefinition.class);
Iterator<TechnologyModuleDefinition> iterator = loader.iterator();
while (iterator.hasNext()) {
TechnologyModuleDefinition technologyModuleDefinition = iterator.next();
if (technologyModuleDefinitionMap.containsKey(technologyModuleDefinition.getName())) {
logger.severe("Cannot include TechnologyModuleDefinition with name '" + technologyModuleDefinition.getName()
+ "' because it already exists !!!! A Technology module name MUST be unique !");
} else {
technologyModuleDefinitionMap.put(technologyModuleDefinition.getName(), technologyModuleDefinition);
}
}
}
return technologyModuleDefinitionMap;
}
/**
* Return all available TechnologyModuleDefinition.
*
* @return all available TechnologyModuleDefinition.
*/
public static Collection<TechnologyModuleDefinition> getAllTechnologyModuleDefinitions() {
return getTechnologyModuleDefinitionMap().values();
}
/**
* Retrieve and return the TechnologyModuleDefinition with the specified name, null if it doesn't exist.
*
* @param technologyModuleName
* @return the TechnologyModuleDefinition with the specified name.
*/
public static TechnologyModuleDefinition getTechnologyModuleDefinition(String technologyModuleName) {
return getTechnologyModuleDefinitionMap().get(technologyModuleName);
}
/* ===================== */
/* == Getter / Setter == */
/* ===================== */
public TechnologyLayer getTechnologyLayer() {
return technologyLayer;
}
public String getName() {
return name;
}
public String getVersion() {
return version;
}
public String getDescription() {
return description;
}
public Set<String> getRequiredModuleNames() {
return requiredModuleNames;
}
public Set<String> getCompatibleModuleNames() {
return compatibleModuleNames;
}
public Set<String> getIncompatibleModuleNames() {
return incompatibleModuleNames;
}
public LinkedHashSet<TechnologyModuleDefinition> getRequiredModules() {
LinkedHashSet<TechnologyModuleDefinition> result = new LinkedHashSet<TechnologyModuleDefinition>();
for (String name : requiredModuleNames) {
TechnologyModuleDefinition technologyModuleDefinition = getTechnologyModuleDefinition(name);
if (technologyModuleDefinition != null) {
result.add(technologyModuleDefinition);
}
}
return result;
}
public LinkedHashSet<TechnologyModuleDefinition> getCompatibleModules() {
LinkedHashSet<TechnologyModuleDefinition> result = new LinkedHashSet<TechnologyModuleDefinition>();
for (String name : compatibleModuleNames) {
TechnologyModuleDefinition technologyModuleDefinition = getTechnologyModuleDefinition(name);
if (technologyModuleDefinition != null) {
result.add(technologyModuleDefinition);
}
}
return result;
}
public LinkedHashSet<TechnologyModuleDefinition> getIncompatibleModules() {
LinkedHashSet<TechnologyModuleDefinition> result = new LinkedHashSet<TechnologyModuleDefinition>();
for (String name : incompatibleModuleNames) {
TechnologyModuleDefinition technologyModuleDefinition = getTechnologyModuleDefinition(name);
if (technologyModuleDefinition != null) {
result.add(technologyModuleDefinition);
}
}
return result;
}
}