Java tutorial
/******************************************************************************************************* * * msi.gaml.factories.ModelAssembler.java, in plugin msi.gama.core, * is part of the source code of the GAMA modeling and simulation platform (v. 1.8) * * (c) 2007-2018 UMI 209 UMMISCO IRD/SU & Partners * * Visit https://github.com/gama-platform/gama for license information and contacts. * ********************************************************************************************************/ package msi.gaml.factories; import static msi.gama.common.interfaces.IKeyword.FREQUENCY; import static msi.gama.common.interfaces.IKeyword.GLOBAL; import static msi.gama.common.interfaces.IKeyword.NAME; import static msi.gama.common.interfaces.IKeyword.SCHEDULES; import static msi.gama.common.interfaces.IKeyword.SPECIES; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.eclipse.emf.ecore.EObject; import org.jgrapht.DirectedGraph; import org.jgrapht.graph.SimpleDirectedGraph; import org.jgrapht.traverse.TopologicalOrderIterator; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Sets; import gnu.trove.map.hash.THashMap; import msi.gama.common.interfaces.IGamlIssue; import msi.gama.common.interfaces.IKeyword; import msi.gama.util.TOrderedHashMap; import msi.gaml.compilation.GamlCompilationError; import msi.gaml.compilation.ast.ISyntacticElement; import msi.gaml.compilation.ast.ISyntacticElement.SyntacticVisitor; import msi.gaml.compilation.ast.SyntacticFactory; import msi.gaml.compilation.ast.SyntacticModelElement; import msi.gaml.descriptions.ExperimentDescription; import msi.gaml.descriptions.IDescription; import msi.gaml.descriptions.IDescription.DescriptionVisitor; import msi.gaml.descriptions.ModelDescription; import msi.gaml.descriptions.SpeciesDescription; import msi.gaml.descriptions.SymbolDescription; import msi.gaml.descriptions.TypeDescription; import msi.gaml.descriptions.ValidationContext; import msi.gaml.statements.Facets; import msi.gaml.types.Types; /** * Class ModelAssembler. * * @author drogoul * @since 15 avr. 2014 * */ @SuppressWarnings({ "unchecked", "rawtypes" }) public class ModelAssembler { public ModelDescription assemble(final String projectPath, final String modelPath, final Iterable<ISyntacticElement> allModels, final ValidationContext collector, final boolean document, final Map<String, ModelDescription> mm) { final ImmutableList<ISyntacticElement> models = ImmutableList.copyOf(allModels); final TOrderedHashMap<String, ISyntacticElement> speciesNodes = new TOrderedHashMap(); final TOrderedHashMap<String, TOrderedHashMap<String, ISyntacticElement>>[] experimentNodes = new TOrderedHashMap[1]; final ISyntacticElement globalNodes = SyntacticFactory.create(GLOBAL, (EObject) null, true); final ISyntacticElement source = models.get(0); Facets globalFacets = null; if (source.hasFacet(IKeyword.PRAGMA)) { final Facets facets = source.copyFacets(null); final List<String> pragmas = (List<String>) facets.get(IKeyword.PRAGMA).getExpression().getConstValue(); collector.resetInfoAndWarning(); if (pragmas != null) { if (pragmas.contains(IKeyword.NO_INFO)) { collector.setNoInfo(); } if (pragmas.contains(IKeyword.NO_WARNING)) { collector.setNoWarning(); } if (pragmas.contains(IKeyword.NO_EXPERIMENT)) { collector.setNoExperiment(); } } } final Map<String, SpeciesDescription> tempSpeciesCache = new THashMap<>(); for (final ISyntacticElement cm : models.reverse()) { final SyntacticModelElement currentModel = (SyntacticModelElement) cm; if (currentModel != null) { if (currentModel.hasFacets()) { if (globalFacets == null) { globalFacets = new Facets(currentModel.copyFacets(null)); } else { globalFacets.putAll(currentModel.copyFacets(null)); } } currentModel.visitChildren(element -> globalNodes.addChild(element)); SyntacticVisitor visitor = element -> addSpeciesNode(element, speciesNodes, collector); currentModel.visitSpecies(visitor); // We input the species so that grids are always the last ones // (see DiffusionStatement) currentModel.visitGrids(visitor); visitor = element -> { if (experimentNodes[0] == null) { experimentNodes[0] = new TOrderedHashMap(); } addExperimentNode(element, currentModel.getName(), experimentNodes[0], collector); }; currentModel.visitExperiments(visitor); } } final String modelName = buildModelName(source.getName()); // We build a list of working paths from which the composite model will // be able to look for resources. These working paths come from the // imported models Set<String> absoluteAlternatePathAsStrings = models.isEmpty() ? null : ImmutableSet.copyOf( Iterables.transform(models.reverse(), each -> ((SyntacticModelElement) each).getPath())); if (mm != null) { for (final ModelDescription m1 : mm.values()) { for (final String im : m1.getAlternatePaths()) { absoluteAlternatePathAsStrings = Sets.union(absoluteAlternatePathAsStrings, Collections.singleton(im)); } } } final ModelDescription model = new ModelDescription(modelName, null, projectPath, modelPath, source.getElement(), null, ModelDescription.ROOT, null, globalFacets, collector, absoluteAlternatePathAsStrings); final Collection<String> allModelNames = models.size() == 1 ? null : ImmutableSet.copyOf( Iterables.transform(Iterables.skip(models, 1), each -> buildModelName(each.getName()))); model.setImportedModelNames(allModelNames); model.isDocumenting(document); // hqnghi add micro-models if (mm != null) { // model.setMicroModels(mm); model.addChildren(mm.values()); } // end-hqnghi // recursively add user-defined species to world and down on to the // hierarchy speciesNodes.forEachValue(speciesNode -> { addMicroSpecies(model, speciesNode, tempSpeciesCache); return true; }); if (experimentNodes[0] != null) { experimentNodes[0].forEachEntry((s, b) -> { b.forEachValue(experimentNode -> { addExperiment(s, model, experimentNode, tempSpeciesCache); return true; }); return true; }); } // Parent the species and the experiments of the model (all are now // known). speciesNodes.forEachValue(speciesNode -> { parentSpecies(model, speciesNode, model, tempSpeciesCache); return true; }); if (experimentNodes[0] != null) { experimentNodes[0].forEachEntry((s, b) -> { b.forEachValue(experimentNode -> { parentExperiment(model, experimentNode); return true; }); return true; }); } // Initialize the hierarchy of types model.buildTypes(); // hqnghi build micro-models as types if (mm != null) { for (final Entry<String, ModelDescription> entry : mm.entrySet()) { model.getTypesManager().alias(entry.getValue().getName(), entry.getKey()); } // end-hqnghi } // Make species and experiments recursively create their attributes, // actions.... complementSpecies(model, globalNodes); speciesNodes.forEachValue(speciesNode -> { complementSpecies(model.getMicroSpecies(speciesNode.getName()), speciesNode); return true; }); if (experimentNodes[0] != null) { experimentNodes[0].forEachEntry((s, b) -> { b.forEachValue(experimentNode -> { complementSpecies(model.getExperiment(experimentNode.getName()), experimentNode); return true; }); return true; }); } // Complement recursively the different species (incl. the world). The // recursion is hierarchical model.inheritFromParent(); for (final SpeciesDescription sd : getSpeciesInHierarchicalOrder(model)) { sd.inheritFromParent(); if (sd.isExperiment()) { if (!sd.finalizeDescription()) { return null; } } } // Issue #1708 (put before the finalization) if (model.hasFacet(SCHEDULES) || model.hasFacet(FREQUENCY)) { createSchedulerSpecies(model); } if (!model.finalizeDescription()) { return null; } if (document) { collector.document(model); } return model; } private Iterable<SpeciesDescription> getSpeciesInHierarchicalOrder(final ModelDescription model) { final DirectedGraph<SpeciesDescription, Object> hierarchy = new SimpleDirectedGraph<>(Object.class); final DescriptionVisitor visitor = desc -> { if (desc instanceof ModelDescription) { return true; } final SpeciesDescription sd = ((SpeciesDescription) desc).getParent(); if (sd == null || sd == desc) { return false; } hierarchy.addVertex((SpeciesDescription) desc); if (!sd.isBuiltIn()) { hierarchy.addVertex(sd); hierarchy.addEdge(sd, (SpeciesDescription) desc); } return true; }; model.visitAllSpecies(visitor); return () -> new TopologicalOrderIterator<>(hierarchy); } private void createSchedulerSpecies(final ModelDescription model) { final SpeciesDescription sd = (SpeciesDescription) DescriptionFactory.create(SPECIES, model, NAME, "_internal_global_scheduler"); sd.finalizeDescription(); if (model.hasFacet(SCHEDULES)) { // remove the warning as GAMA integrates a working workaround to use this facet at the global level // model.warning( // "'schedules' is deprecated in global. Define a dedicated species instead and add the facet to it", // IGamlIssue.DEPRECATED, NAME); sd.setFacet(SCHEDULES, model.getFacet(SCHEDULES)); model.removeFacets(SCHEDULES); } if (model.hasFacet(FREQUENCY)) { model.warning( "'frequency' is deprecated in global. Define a dedicated species instead and add the facet to it", IGamlIssue.DEPRECATED, NAME); sd.setFacet(FREQUENCY, model.getFacet(FREQUENCY)); model.removeFacets(FREQUENCY); } model.addChild(sd); } void addExperiment(final String origin, final ModelDescription model, final ISyntacticElement experiment, final Map<String, SpeciesDescription> cache) { // Create the experiment description final IDescription desc = DescriptionFactory.create(experiment, model, Collections.EMPTY_LIST); final ExperimentDescription eDesc = (ExperimentDescription) desc; cache.put(eDesc.getName(), eDesc); ((SymbolDescription) desc).resetOriginName(); desc.setOriginName(buildModelName(origin)); model.addChild(desc); } void addExperimentNode(final ISyntacticElement element, final String modelName, final Map<String, TOrderedHashMap<String, ISyntacticElement>> experimentNodes, final ValidationContext collector) { // First we verify that this experiment has not been declared previously final String experimentName = element.getName(); for (final String otherModel : experimentNodes.keySet()) { if (!otherModel.equals(modelName)) { final Map<String, ISyntacticElement> otherExperiments = experimentNodes.get(otherModel); if (otherExperiments.containsKey(experimentName)) { collector.add(new GamlCompilationError( "Experiment " + experimentName + " supersedes the one declared in " + otherModel, IGamlIssue.DUPLICATE_DEFINITION, element.getElement(), false, true)); // We remove the old one otherExperiments.remove(experimentName); } } } if (!experimentNodes.containsKey(modelName)) { experimentNodes.put(modelName, new TOrderedHashMap()); } final Map<String, ISyntacticElement> nodes = experimentNodes.get(modelName); if (nodes.containsKey(experimentName)) { collector.add(new GamlCompilationError("Experiment " + element.getName() + " is declared twice", IGamlIssue.DUPLICATE_DEFINITION, element.getElement(), false, false)); } nodes.put(experimentName, element); } void addMicroSpecies(final SpeciesDescription macro, final ISyntacticElement micro, final Map<String, SpeciesDescription> cache) { // Create the species description without any children. Passing // explicitly an empty list and not null; final SpeciesDescription mDesc = (SpeciesDescription) DescriptionFactory.create(micro, macro, Collections.EMPTY_LIST); cache.put(mDesc.getName(), mDesc); // Add it to its macro-species macro.addChild(mDesc); // Recursively create each micro-species of the newly added // micro-species final SyntacticVisitor visitor = element -> addMicroSpecies(mDesc, element, cache); micro.visitSpecies(visitor); micro.visitExperiments(visitor); } void addSpeciesNode(final ISyntacticElement element, final Map<String, ISyntacticElement> speciesNodes, final ValidationContext collector) { final String name = element.getName(); if (speciesNodes.containsKey(name)) { collector.add(new GamlCompilationError("Species " + name + " is declared twice", IGamlIssue.DUPLICATE_DEFINITION, element.getElement(), false, false)); collector.add(new GamlCompilationError("Species " + name + " is declared twice", IGamlIssue.DUPLICATE_DEFINITION, speciesNodes.get(name).getElement(), false, false)); } speciesNodes.put(name, element); } /** * Recursively complements a species and its micro-species. Add variables, behaviors (actions, reflex, task, states, * ...), aspects to species. * * @param macro * the macro-species * @param micro * the structure of micro-species */ void complementSpecies(final SpeciesDescription species, final ISyntacticElement node) { if (species == null) { return; } species.copyJavaAdditions(); node.visitChildren(element -> { final IDescription childDesc = DescriptionFactory.create(element, species, null); if (childDesc != null) { species.addChild(childDesc); } }); // recursively complement micro-species node.visitSpecies(element -> { final SpeciesDescription sd = species.getMicroSpecies(element.getName()); if (sd != null) { complementSpecies(sd, element); } }); } void parentExperiment(final ModelDescription model, final ISyntacticElement micro) { // Gather the previously created species final SpeciesDescription mDesc = model.getExperiment(micro.getName()); if (mDesc == null) { return; } final String p = mDesc.getLitteral(IKeyword.PARENT); // If no parent is defined, we assume it is "experiment" // No cache needed for experiments ?? SpeciesDescription parent = model.getExperiment(p); if (parent == null) { parent = Types.get(IKeyword.EXPERIMENT).getSpecies(); } mDesc.setParent(parent); } void parentSpecies(final SpeciesDescription macro, final ISyntacticElement micro, final ModelDescription model, final Map<String, SpeciesDescription> cache) { // Gather the previously created species final SpeciesDescription mDesc = cache.get(micro.getName()); if (mDesc == null || mDesc.isExperiment()) { return; } String p = mDesc.getLitteral(IKeyword.PARENT); // If no parent is defined, we assume it is "agent" if (p == null) { p = IKeyword.AGENT; } SpeciesDescription parent = lookupSpecies(p, cache); if (parent == null) { parent = model.getSpeciesDescription(p); } mDesc.setParent(parent); micro.visitSpecies(element -> parentSpecies(mDesc, element, model, cache)); } /** * Lookup first in the cache passed in argument, then in the built-in species * * @param cache * @return */ SpeciesDescription lookupSpecies(final String name, final Map<String, SpeciesDescription> cache) { SpeciesDescription result = cache.get(name); if (result == null) { for (final TypeDescription td : Types.getBuiltInSpecies()) { if (td.getName().equals(name)) { result = (SpeciesDescription) td; break; } } } return result; } protected String buildModelName(final String source) { final String modelName = source.replace(' ', '_') + ModelDescription.MODEL_SUFFIX; return modelName; } }