Skip to content
Permalink
 
 
Cannot retrieve contributors at this time
431 lines (384 sloc) 15.3 KB
/*******************************************************************************************************
*
* 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.1)
*
* (c) 2007-2020 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.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 msi.gama.common.interfaces.IGamlIssue;
import msi.gama.common.interfaces.IKeyword;
import msi.gama.util.GamaMapFactory;
import msi.gama.util.IMap;
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 IMap<String, ISyntacticElement> speciesNodes = GamaMapFactory.create();
final IMap<String, IMap<String, ISyntacticElement>>[] experimentNodes = new IMap[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 = GamaMapFactory.createUnordered();
for (final ISyntacticElement cm : models.reverse()) {
final SyntacticModelElement currentModel = (SyntacticModelElement) cm;
if (currentModel != null) {
if (currentModel.hasFacets()) {
if (globalFacets == null) {
globalFacets = 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] = GamaMapFactory.create();
}
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].forEachPair((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].forEachPair((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) {
mm.forEach((k, v) -> model.getTypesManager().alias(v.getName(), k));
// 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].forEachPair((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, IMap<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, GamaMapFactory.create());
}
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) {
return source.replace(' ', '_') + ModelDescription.MODEL_SUFFIX;
}
}
You can’t perform that action at this time.