/* * (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.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Vector; import java.util.logging.Level; import java.util.logging.Logger; import org.openflexo.foundation.TargetType; import org.openflexo.foundation.cg.CGFile; import org.openflexo.foundation.cg.generator.IFlexoResourceGenerator; import org.openflexo.foundation.cg.templates.CGTemplate; import org.openflexo.foundation.rm.FlexoProject; import org.openflexo.foundation.rm.GeneratedResourceData; import org.openflexo.foundation.rm.cg.CGRepositoryFileResource; import org.openflexo.foundation.sg.SGTemplates; import org.openflexo.foundation.sg.SourceRepository; import org.openflexo.foundation.sg.implmodel.ImplementationModel; import org.openflexo.foundation.sg.implmodel.TechnologyModuleDefinition; import org.openflexo.foundation.sg.implmodel.TechnologyModuleImplementation; import org.openflexo.foundation.sg.implmodel.enums.TechnologyLayer; import org.openflexo.generator.AbstractProjectGenerator; import org.openflexo.generator.Generator; import org.openflexo.generator.exception.GenerationException; import org.openflexo.generator.exception.PermissionDeniedException; import org.openflexo.generator.exception.TemplateNotFoundException; /** * * @author sguerin */ public class ProjectGenerator extends AbstractProjectGenerator<SourceRepository> { protected static final Logger logger = Logger.getLogger(ProjectGenerator.class.getPackage().getName()); private static final String SG_MACRO_LIBRARY_NAME = "VM_global_library.vm"; private boolean hasBeenInitialized = false; private Map<TechnologyModuleDefinition, ModuleGenerator> generators = new HashMap<TechnologyModuleDefinition, ModuleGenerator>(); // ============================================================= // ======================== Constructor ======================== // ============================================================= /** * Default constructor * * @param workflowFile * @throws Exception */ public ProjectGenerator(FlexoProject project, SourceRepository repository) throws GenerationException { super(project, repository); if (repository == null) { if (logger.isLoggable(Level.FINE)) { logger.fine("No target repository, this may happen during dynamic invocation of code generator within the context of the Data model edition"); } } if (getRootOutputDirectory() != null) { if (!getResourceOutputDirectory().exists()) { getRootOutputDirectory().mkdirs(); } if (!getRootOutputDirectory().canWrite()) { throw new PermissionDeniedException(getRootOutputDirectory(), this); } } } public SourceRepository getSourceRepository() { return getRepository(); } public ImplementationModel getImplementationModel() { return getRepository().getImplementationModel(); } // private GenerationModule retrieveModule() @Override public SGTemplates getDefaultTemplates() { return getProject().getGeneratedSources().getTemplates(); } @Override public Logger getGeneratorLogger() { return logger; } /** * This method is very important, because it is the way we must identify or build all resources involved in code generation. After this * list has been built, we just let ResourceManager do the work. * * @param repository * : repository where resources should be retrieved or built * @param resources * : the list of resources we must retrieve or build */ @Override public void buildResourcesAndSetGenerators(SourceRepository repository, Vector<CGRepositoryFileResource> resources) { hasBeenInitialized = true; logger.info("We go for first pass on Project Generator"); Map<TechnologyModuleDefinition, ModuleGenerator> newGenerators = new LinkedHashMap<TechnologyModuleDefinition, ModuleGenerator>(); for (TechnologyModuleImplementation technologyModuleImplementation : repository.getImplementationModel().getTechnologyModules()) { ModuleGenerator moduleGenerator = generators.get(technologyModuleImplementation.getTechnologyModuleDefinition()); if (moduleGenerator == null) { moduleGenerator = new ModuleGenerator(this, technologyModuleImplementation); } newGenerators.put(technologyModuleImplementation.getTechnologyModuleDefinition(), moduleGenerator); } generators = newGenerators; for (ModuleGenerator generator : newGenerators.values()) { refreshProgressWindow("Generate " + generator.getTechnologyModule().getTechnologyModuleDefinition().getName(), false); generator.buildResourcesAndSetGenerators(repository, resources); } } /** * Calculate the appropriate order for module generation (as module can communicate). <br> * The order is as follow (Inverse of declaration in TechnologyLayer): * <ul> * <li>Database layer modules</li> * <li>DAO layer modules</li> * <li>Business logic layer modules</li> * <li>GUI layer modules</li> * <li>Transversal layer modules</li> * <li>Main layer modules</li> * </ul> * In the same layer, modules which are compatible with or require another module will be before this other module (except loop exists, * in such case order is unpredictable) * * @param filesToGenerate * the list of file which will be regenerated */ @Override public void sortResourcesForGeneration( List<CGRepositoryFileResource<? extends GeneratedResourceData, IFlexoResourceGenerator, CGFile>> resources) { List<CGRepositoryFileResource<? extends GeneratedResourceData, IFlexoResourceGenerator, CGFile>> notSGGeneratorResources = new ArrayList<CGRepositoryFileResource<? extends GeneratedResourceData, IFlexoResourceGenerator, CGFile>>(); Map<TechnologyLayer, Map<TechnologyModuleDefinition, List<CGRepositoryFileResource<? extends GeneratedResourceData, IFlexoResourceGenerator, CGFile>>>> map = new HashMap<TechnologyLayer, Map<TechnologyModuleDefinition, List<CGRepositoryFileResource<? extends GeneratedResourceData, IFlexoResourceGenerator, CGFile>>>>(); for (CGRepositoryFileResource<? extends GeneratedResourceData, IFlexoResourceGenerator, CGFile> resource : resources) { if (resource.getGenerator() instanceof SGGenerator<?, ?>) { TechnologyModuleDefinition moduleDefinition = ((SGGenerator<?, ?>) resource.getGenerator()).getModuleGenerator() .getTechnologyModule().getTechnologyModuleDefinition(); Map<TechnologyModuleDefinition, List<CGRepositoryFileResource<? extends GeneratedResourceData, IFlexoResourceGenerator, CGFile>>> mapForLayer = map .get(moduleDefinition.getTechnologyLayer()); if (mapForLayer == null) { mapForLayer = new HashMap<TechnologyModuleDefinition, List<CGRepositoryFileResource<? extends GeneratedResourceData, IFlexoResourceGenerator, CGFile>>>(); map.put(moduleDefinition.getTechnologyLayer(), mapForLayer); } List<CGRepositoryFileResource<? extends GeneratedResourceData, IFlexoResourceGenerator, CGFile>> resourcesForModule = mapForLayer .get(moduleDefinition); if (resourcesForModule == null) { resourcesForModule = new ArrayList<CGRepositoryFileResource<? extends GeneratedResourceData, IFlexoResourceGenerator, CGFile>>(); mapForLayer.put(moduleDefinition, resourcesForModule); } resourcesForModule.add(resource); } else { notSGGeneratorResources.add(resource); } } resources.clear(); List<TechnologyLayer> allLayers = Arrays.asList(TechnologyLayer.values()); Collections.reverse(allLayers); for (TechnologyLayer layer : allLayers) { Map<TechnologyModuleDefinition, List<CGRepositoryFileResource<? extends GeneratedResourceData, IFlexoResourceGenerator, CGFile>>> mapForLayer = map .get(layer); if (mapForLayer == null) { continue; } /* * The idea is to build a map containing, for each module, all the modules which are compatible or which request it. Once this map is built, we iterates over each key to takes empty * modules and we remove those modules from the list of other modules. We do this again until no module is taken or until the map is empty. */ // 1. Build the map with empty lists Map<TechnologyModuleDefinition, Set<TechnologyModuleDefinition>> requiringModuleMap = new HashMap<TechnologyModuleDefinition, Set<TechnologyModuleDefinition>>(); for (TechnologyModuleDefinition moduleDefinition : mapForLayer.keySet()) { requiringModuleMap.put(moduleDefinition, new HashSet<TechnologyModuleDefinition>()); } // 2. Fill the map for (TechnologyModuleDefinition definition : requiringModuleMap.keySet()) { Set<TechnologyModuleDefinition> requiredModules = definition.getRequiredModules(); requiredModules.addAll(definition.getCompatibleModules()); for (TechnologyModuleDefinition requiredModule : requiredModules) { if (requiringModuleMap.containsKey(requiredModule)) { requiringModuleMap.get(requiredModule).add(definition); } } } // 3. Perform iteration boolean hasRemovedKey; do { hasRemovedKey = false; for (Map.Entry<TechnologyModuleDefinition, Set<TechnologyModuleDefinition>> entry : new HashMap<TechnologyModuleDefinition, Set<TechnologyModuleDefinition>>( requiringModuleMap).entrySet()) { if (entry.getValue().isEmpty()) { requiringModuleMap.remove(entry.getKey()); for (Set<TechnologyModuleDefinition> set : requiringModuleMap.values()) { set.remove(entry.getKey()); } resources.addAll(mapForLayer.get(entry.getKey())); hasRemovedKey = true; } } } while (!requiringModuleMap.isEmpty() && hasRemovedKey); for (TechnologyModuleDefinition notRemovedModule : requiringModuleMap.keySet()) { resources.addAll(mapForLayer.get(notRemovedModule)); } } resources.addAll(notSGGeneratorResources); } /** * {@inheritDoc} */ @Override public boolean hasBeenInitialized() { return hasBeenInitialized; } /** * {@inheritDoc} */ @Override public File getRootOutputDirectory() { if (getRepository() == null) { return null; } return getRepository().getDirectory(); } public File getResourceOutputDirectory() { return getRootOutputDirectory(); } /** * {@inheritDoc} */ @Override public List<CGTemplate> getVelocityMacroTemplates() { List<CGTemplate> result = new ArrayList<CGTemplate>(); try { result.add(templateWithName(SG_MACRO_LIBRARY_NAME)); } catch (TemplateNotFoundException e) { logger.warning("Should include velocity macro template for project generator but template is not found '" + SG_MACRO_LIBRARY_NAME + "'"); e.printStackTrace(); } return result; } /** * {@inheritDoc} */ @Override public TargetType getTarget() { // No more used with new generator return null; } public String generateTimestamp() { return new Date().toString(); } /** * Retrieve the module generator associated to the specified TechnologyModuleDefinition. * * @param technologyModuleDefinition * @return the retrieved module generator if any, null otherwise. */ public ModuleGenerator getModuleGenerator(TechnologyModuleDefinition technologyModuleDefinition) { return generators.get(technologyModuleDefinition); } /** * Return all ModuleGenerators recorded in this project generator. * * @return all ModuleGenerators recorded in this project generator. */ public Collection<ModuleGenerator> getAllModuleGenerators() { return generators.values(); } /** * Add a cross module data in the specified targeted module. <br> * Intended to be used in macro defined in the targeted module itself. <br> * For those macro to be available in the from module, the targeted module must be in the "requireModule" or in the * "compatibleWithModule". * * @see ModuleGenerator#addInModuleDataList(Generator, String, Object) */ public void addCrossModuleDataInList(TechnologyModuleDefinition targetedModule, Generator<?, ?> fromGenerator, String classifier, Object data) { ModuleGenerator moduleGenerator = generators.get(targetedModule); if (moduleGenerator == null) { logger.info("Attempting to add cross module data in a non existent module '" + targetedModule.getName() + "'. Ignoring."); return; } moduleGenerator.addInModuleDataList(fromGenerator, classifier, data); } /** * Add a cross module data in the specified targeted module. <br> * Intended to be used in macro defined in the targeted module itself. <br> * For those macro to be available in the from module, the targeted module must be in the "requireModule" or in the * "compatibleWithModule". * * @see ModuleGenerator#addInModuleDataSet(Generator, String, Object) */ public void addCrossModuleDataInSet(TechnologyModuleDefinition targetedModule, Generator<?, ?> fromGenerator, String classifier, Object data) { ModuleGenerator moduleGenerator = generators.get(targetedModule); if (moduleGenerator == null) { logger.info("Attempting to add cross module data in a non existent module '" + targetedModule.getName() + "'. Ignoring."); return; } moduleGenerator.addInModuleDataSet(fromGenerator, classifier, data); } /** * Add a cross module data in the specified targeted module. <br> * Intended to be used in macro defined in the targeted module itself. <br> * For those macro to be available in the from module, the targeted module must be in the "requireModule" or in the * "compatibleWithModule". * * @see ModuleGenerator#addInModuleDataMap(Generator, String, Object, Object) */ public void addCrossModuleDataInMap(TechnologyModuleDefinition targetedModule, Generator<?, ?> fromGenerator, String classifier, Object key, Object data) { ModuleGenerator moduleGenerator = generators.get(targetedModule); if (moduleGenerator == null) { logger.info("Attempting to add cross module data in a non existent module '" + targetedModule.getName() + "'. Ignoring."); return; } moduleGenerator.addInModuleDataMap(fromGenerator, classifier, key, data); } }