/* * (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.sg.generator; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Hashtable; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.Vector; import java.util.logging.Level; import java.util.logging.Logger; import org.apache.velocity.VelocityContext; import org.openflexo.foundation.FlexoModelObject; import org.openflexo.foundation.cg.CGSymbolicDirectory; import org.openflexo.foundation.cg.generator.GeneratorUtils; import org.openflexo.foundation.cg.templates.CGTemplate; import org.openflexo.foundation.rm.ResourceType; import org.openflexo.foundation.rm.cg.CGRepositoryFileResource; import org.openflexo.foundation.sg.SourceRepository; import org.openflexo.foundation.sg.implmodel.TechnologyModuleDefinition; import org.openflexo.foundation.sg.implmodel.TechnologyModuleImplementation; import org.openflexo.foundation.utils.FlexoProjectFile; import org.openflexo.generator.GeneratedResourceFileFactory; import org.openflexo.generator.Generator; import org.openflexo.generator.MetaGenerator; import org.openflexo.generator.exception.GenerationException; import org.openflexo.generator.exception.TemplateNotFoundException; import org.openflexo.sg.file.SGFile; import org.openflexo.sg.file.SGJavaFile; import org.openflexo.sg.file.SGJavaFileResource; import org.openflexo.sg.file.SGTextFile; import org.openflexo.sg.file.SGTextFileResource; import org.openflexo.sg.generationdef.FileEntry; import org.openflexo.sg.generationdef.GenerationDefinition; import org.openflexo.sg.generationdef.SymbolicPathEntry; import org.openflexo.toolbox.FileFormat; import org.openflexo.toolbox.ToolBox; /** * @author sylvain * */ public class ModuleGenerator extends MetaGenerator<FlexoModelObject, SourceRepository> { private static final Logger logger = Logger.getLogger(ModuleGenerator.class.getPackage().getName()); private final TechnologyModuleImplementation module; /** * This map allows data transfer between modules. <br> * Key is a custom String allowing to classify the content. <br> * Value is a sorted map with the 'from generator' and an object representing the data list. <br> * This object can be either a List<Object>, a Map<Object, Object> or a Set<Object> depending on method used to perform the data * insertion. <br> * The 'from generator' is stored to allow clean of the transfered value from this generator only (as not all files are regenerated at * each time, we must keep previous not regenerated content). */ private final Map<String, LinkedHashMap<Generator<?, ?>, Object>> crossModuleDataMap = new HashMap<String, LinkedHashMap<Generator<?, ?>, Object>>(); private final Hashtable<CGRepositoryFileResource, SGGenerator> generators; public ModuleGenerator(ProjectGenerator projectGenerator, TechnologyModuleImplementation module) { super(projectGenerator, projectGenerator.getProject()); this.module = module; generators = new Hashtable<CGRepositoryFileResource, SGGenerator>(); } public TechnologyModuleImplementation getTechnologyModule() { return module; } @Override public ProjectGenerator getProjectGenerator() { return (ProjectGenerator) super.getProjectGenerator(); } @Override public Logger getGeneratorLogger() { return logger; } @Override protected VelocityContext defaultContext() { VelocityContext vc = super.defaultContext(); vc.put("implementationModel", getRepository().getImplementationModel()); // Add the technology module and all its required modules in context for (TechnologyModuleDefinition moduleDefinition : getTechnologyModule().getTechnologyModuleDefinition().getAllRequiredModules()) { TechnologyModuleImplementation implementation = getRepository().getImplementationModel().getTechnologyModule(moduleDefinition); if (implementation != null) { vc.put(ModuleGenerator.getTechnologyImplementationVelocityName(implementation), implementation); } } // Add the compatible technology modules in context for (TechnologyModuleDefinition moduleDefinition : getTechnologyModule().getTechnologyModuleDefinition().getCompatibleModules()) { TechnologyModuleImplementation implementation = getRepository().getImplementationModel().getTechnologyModule(moduleDefinition); if (implementation != null) { vc.put(ModuleGenerator.getTechnologyImplementationVelocityName(implementation), implementation); } } return vc; } public static String getTechnologyImplementationVelocityName(TechnologyModuleImplementation implementation) { return ToolBox.getJavaName(implementation.getTechnologyModuleDefinition().getName() + "Implementation"); } public String getTemplatesFolder() { String resourcePath = getTechnologyModule().getTechnologyModuleDefinition().getResourcePath(); if (resourcePath.startsWith("/")) { resourcePath = resourcePath.substring(1); } return getTechnologyModule().getTechnologyModuleDefinition().getTechnologyLayer().getFolderName() + "/" + resourcePath + "/"; } public String getTemplateName() { return getTemplatesFolder() + "main.xml.vm"; } public SourceRepository getSourceRepository() { return getProjectGenerator().getRepository(); } /** * Retrieve all data inserted by other modules for the specified classifier. <br> * The returned Object can be either a List<Object>, a Map<Object, Object> or a Set<Object> depending on method used to perform the data * insertion. * * @param classifier * : the data classifier. * @return all data inserted by other modules for the specified classifier. */ @SuppressWarnings({ "unchecked" }) public Object getModuleData(String classifier) { Object result = null; if (crossModuleDataMap.get(classifier) == null) { return result; } List<Object> values = new ArrayList<Object>(crossModuleDataMap.get(classifier).values()); Collections.reverse(values); // Reverse order to get the highest layer generator first for (Object object : values) { if (object instanceof List) { if (result == null) { result = new ArrayList<Object>(); } ((List<Object>) result).addAll((List<Object>) object); } else if (object instanceof Set) { if (result == null) { result = new LinkedHashSet<Object>(); } ((HashSet<Object>) result).addAll((HashSet<Object>) object); } else if (object instanceof Map) { if (result == null) { result = new LinkedHashMap<Object, Object>(); } ((Map<Object, Object>) result).putAll((Map<Object, Object>) object); } } return result; } /** * Add a data object in this module cross data for the specified classifier. <br> * The data container used for the specified classifier will be a List<Object>. <br> * If this module already contains data for this classifier, it MUST have been inserted using the same method to ensure List<Object> is * used everywhere. <br> * If this module already contains data for this classifier which is not a List<Object>, a RuntimeException is thrown. * * @param fromGenerator * the generator which adds this data * @param classifier * the data classifier, used to allow different type of data for the same module. * @param data */ @SuppressWarnings("unchecked") public void addInModuleDataList(Generator<?, ?> fromGenerator, String classifier, Object data) { LinkedHashMap<Generator<?, ?>, Object> classifiedMap = crossModuleDataMap.get(classifier); if (classifiedMap == null) { classifiedMap = new LinkedHashMap<Generator<?, ?>, Object>(); crossModuleDataMap.put(classifier, classifiedMap); } // Checks all data are in List<Object> for each generator for (Entry<Generator<?, ?>, Object> entry : classifiedMap.entrySet()) { if (entry.getValue() != null && !(entry.getValue() instanceof List)) { throw new RuntimeException("Cannot add cross module data in module '" + this.getTechnologyModule().getName() + "' for classifier '" + classifier + "' because it already contains a non List value (" + entry.getValue().getClass() + ") added by generator '" + entry.getKey() + "'"); } } List<Object> dataList = (List<Object>) classifiedMap.get(fromGenerator); if (dataList == null) { dataList = new ArrayList<Object>(); classifiedMap.put(fromGenerator, dataList); } dataList.add(data); } /** * Add a data object in this module cross data for the specified classifier. <br> * The data container used for the specified classifier will be a Set<Object>. <br> * If this module already contains data for this classifier, it MUST have been inserted using the same method to ensure Set<Object> is * used everywhere. <br> * If this module already contains data for this classifier which is not a Set<Object>, a RuntimeException is thrown. * * @param fromGenerator * the generator which adds this data * @param classifier * the data classifier, used to allow different type of data for the same module. * @param data */ @SuppressWarnings("unchecked") public void addInModuleDataSet(Generator<?, ?> fromGenerator, String classifier, Object data) { LinkedHashMap<Generator<?, ?>, Object> classifiedMap = crossModuleDataMap.get(classifier); if (classifiedMap == null) { classifiedMap = new LinkedHashMap<Generator<?, ?>, Object>(); crossModuleDataMap.put(classifier, classifiedMap); } // Checks all data are in List<Object> for each generator for (Entry<Generator<?, ?>, Object> entry : classifiedMap.entrySet()) { if (entry.getValue() != null && !(entry.getValue() instanceof Set)) { throw new RuntimeException("Cannot add cross module data in module '" + this.getTechnologyModule().getName() + "' for classifier '" + classifier + "' because it already contains a non Set value (" + entry.getValue().getClass() + ") added by generator '" + entry.getKey() + "'"); } } Set<Object> dataSet = (Set<Object>) classifiedMap.get(fromGenerator); if (dataSet == null) { dataSet = new LinkedHashSet<Object>(); classifiedMap.put(fromGenerator, dataSet); } dataSet.add(data); } /** * Add a data object in this module cross data for the specified classifier. <br> * The data container used for the specified classifier will be a Map<Object, Object>. <br> * If this module already contains data for this classifier, it MUST have been inserted using the same method to ensure Map<Object, * Object> is used everywhere. <br> * If this module already contains data for this classifier which is not a Map<Object, Object>, a RuntimeException is thrown. * * @param fromGenerator * the generator which adds this data * @param classifier * the data classifier, used to allow different type of data for the same module. * @param key * the key to use to insert the data into the map * @param data */ @SuppressWarnings("unchecked") public void addInModuleDataMap(Generator<?, ?> fromGenerator, String classifier, Object key, Object data) { LinkedHashMap<Generator<?, ?>, Object> classifiedMap = crossModuleDataMap.get(classifier); if (classifiedMap == null) { classifiedMap = new LinkedHashMap<Generator<?, ?>, Object>(); crossModuleDataMap.put(classifier, classifiedMap); } // Checks all data are in List<Object> for each generator for (Entry<Generator<?, ?>, Object> entry : classifiedMap.entrySet()) { if (entry.getValue() != null && !(entry.getValue() instanceof Map)) { throw new RuntimeException("Cannot add cross module data in module '" + this.getTechnologyModule().getName() + "' for classifier '" + classifier + "' because it already contains a non Map value (" + entry.getValue().getClass() + ") added by generator '" + entry.getKey() + "'"); } } Map<Object, Object> dataMap = (Map<Object, Object>) classifiedMap.get(fromGenerator); if (dataMap == null) { dataMap = new LinkedHashMap<Object, Object>(); classifiedMap.put(fromGenerator, dataMap); } dataMap.put(key, data); } protected void cleanCrossModuleDataForGenerator(Generator<?, ?> generator) { for (LinkedHashMap<Generator<?, ?>, Object> map : crossModuleDataMap.values()) { if (map.containsKey(generator)) { map.put(generator, null); // Don't remove it from map to avoid losing the order } } } @Override public void buildResourcesAndSetGenerators(SourceRepository repository, Vector<CGRepositoryFileResource> resources) { try { templateWithName(getTemplateName()); } catch (TemplateNotFoundException e1) { logger.warning("Could not generate TechnologyModule " + getTechnologyModule().getTechnologyModuleDefinition().getName() + " no main.xml.vm found"); return; } String generationResult; try { // Clean cross module data for (ModuleGenerator moduleGenerator : getProjectGenerator().getAllModuleGenerators()) { moduleGenerator.cleanCrossModuleDataForGenerator(this); } generationResult = merge(getTemplateName(), defaultContext()); if (logger.isLoggable(Level.INFO)) { logger.info("generationResult=" + generationResult); } GenerationDefinition generation = GenerationDefinition.retrieveGenerationDefinition(generationResult); for (SymbolicPathEntry symbolicPath : generation.symbolicPaths) { // Ensure declaration or creation of used symbolic dir if (getSourceRepository().getSymbolicDirectoryNamed(symbolicPath.name, false) == null) { FlexoProjectFile resourcesSymbDir = new FlexoProjectFile(getProject(), getSourceRepository().getSourceCodeRepository(), symbolicPath.path); getSourceRepository().setSymbolicDirectoryForKey( new CGSymbolicDirectory(getSourceRepository(), symbolicPath.name, resourcesSymbDir), symbolicPath.name); resourcesSymbDir.getFile().mkdirs(); } } for (FileEntry file : generation.files) { logger.info("> File: " + file.name); String identifier = SGFile.makeIdentifier(file.name, file.symbolicPath, file.relativePath); CGSymbolicDirectory symbDir = getSourceRepository().getSymbolicDirectoryNamed(file.symbolicPath, true); if (file.getFormat().equals(FileFormat.JAVA)) { SGJavaFileResource javaResource = (SGJavaFileResource) resourceForKeyWithCGFile(ResourceType.JAVA_FILE, GeneratorUtils.nameForRepositoryAndIdentifier(repository, identifier)); if (javaResource == null) { SGJavaClassGenerator generator = new SGJavaClassGenerator(this, file); javaResource = new SGJavaFileResource(getProject()); javaResource.setGenerator(generator); javaResource.setName(GeneratorUtils.nameForRepositoryAndIdentifier(repository, identifier)); SGJavaFile sgJavaFile = new SGJavaFile(repository, javaResource); initSGFile(sgJavaFile, symbDir, javaResource); registerResource(javaResource, file.name, file.relativePath); logger.info("New java resource " + javaResource.getFile().getAbsolutePath()); } else { logger.info("Retrieved java resource " + javaResource.getFile().getAbsolutePath()); if (javaResource.getGenerator() == null) { SGJavaClassGenerator generator = new SGJavaClassGenerator(this, file); javaResource.setGenerator(generator); } } resources.add(javaResource); generators.put(javaResource, javaResource.getGenerator()); } else { SGTextFileResource textResource = (SGTextFileResource) resourceForKeyWithCGFile(ResourceType.TEXT_FILE, GeneratorUtils.nameForRepositoryAndIdentifier(repository, identifier)); if (textResource == null) { SGTextFileGenerator generator = new SGTextFileGenerator(this, file); textResource = new SGTextFileResource(getProject()); textResource.setGenerator(generator); textResource.setName(GeneratorUtils.nameForRepositoryAndIdentifier(repository, identifier)); SGTextFile sgTextFile = new SGTextFile(repository, textResource); initSGFile(sgTextFile, symbDir, textResource); registerResource(textResource, file.name, file.relativePath); logger.info("New text resource " + textResource.getFile().getAbsolutePath()); } else { logger.info("Retrieved text resource " + textResource.getFile().getAbsolutePath()); if (textResource.getGenerator() == null) { SGTextFileGenerator generator = new SGTextFileGenerator(this, file); textResource.setGenerator(generator); } } resources.add(textResource); generators.put(textResource, textResource.getGenerator()); } } } catch (GenerationException e) { // TODO Auto-generated catch block e.printStackTrace(); } /* * Hashtable<ComponentDefinition, ComponentGenerator> hash = new Hashtable<ComponentDefinition, ComponentGenerator>(); Hashtable<PopupComponentDefinition, PopupLinkComponentGenerator> links = * new Hashtable<PopupComponentDefinition, PopupLinkComponentGenerator>(); for(TabComponentDefinition tcd:getProject().getFlexoComponentLibrary().getTabComponentList()) { ComponentGenerator * generator = getGenerator(tcd); if (generator != null) { hash.put(tcd,generator); generator.buildResourcesAndSetGenerators(repository,resources); } else { if * (logger.isLoggable(Level.WARNING)) logger.warning("Could not instanciate ComponentGenerator for "+tcd); } } for(PopupComponentDefinition * pcd:getProject().getFlexoComponentLibrary().getPopupsComponentList()) { if (pcd.isHelper()) continue; PopupLinkComponentGenerator linkGenerator = getPopupLinkGenerator(pcd); if * (linkGenerator != null) { links.put(pcd,linkGenerator); linkGenerator.buildResourcesAndSetGenerators(repository,resources); } else { if (logger.isLoggable(Level.WARNING)) * logger.warning("Could not instanciate ComponentGenerator for "+pcd); } ComponentGenerator generator = getGenerator(pcd); if (generator != null) { hash.put(pcd,generator); * generator.buildResourcesAndSetGenerators(repository,resources); } else { if (logger.isLoggable(Level.WARNING)) logger.warning("Could not instanciate ComponentGenerator for "+pcd); } } * for(OperationComponentDefinition ocd:getProject().getFlexoComponentLibrary().getOperationsComponentList()) { ComponentGenerator generator = getGenerator(ocd); if (generator != null) { * hash.put(ocd,generator); generator.buildResourcesAndSetGenerators(repository,resources); } else { if (logger.isLoggable(Level.WARNING)) * logger.warning("Could not instanciate ComponentGenerator for "+ocd); } } * * if (getTarget() == CodeType.PROTOTYPE) { //Add page to manage samples StaticComponentGenerator generator = getStaticComponentGenerator("PrototypeSamplesAdminPage", * "PrototypeSamplesAdminPage"); if (generator != null) generator.buildResourcesAndSetGenerators(repository,resources); else { if (logger.isLoggable(Level.WARNING)) * logger.warning("Could not instanciate StaticComponentGenerator for PrototypeSamplesAdminPage"); } } * * generators.clear(); popupLinkGenerators.clear(); generators = hash; popupLinkGenerators = links; */ } private static void initSGFile(SGFile cgFile, CGSymbolicDirectory symbDir, CGRepositoryFileResource returned) { GeneratedResourceFileFactory.initCGFile(cgFile, symbDir, returned); } private static <FR extends CGRepositoryFileResource> FR registerResource(FR returned, String fileName, String folderPath) { return GeneratedResourceFileFactory.registerResource(returned, fileName, folderPath); } /** * @see SGGenerator#getVelocityMacroTemplates(Generator, ModuleGenerator) */ @Override public List<CGTemplate> getVelocityMacroTemplates() { return SGGenerator.getVelocityMacroTemplates(this, this); } }