it.unibo.alchemist.model.biochemistry.reactions.BiochemicalReactionBuilder Maven / Gradle / Ivy
Show all versions of alchemist-incarnation-biochemistry Show documentation
/*
* Copyright (C) 2010-2023, Danilo Pianini and contributors
* listed, for each module, in the respective subproject's build.gradle.kts file.
*
* This file is part of Alchemist, and is distributed under the terms of the
* GNU General Public License, with a linking exception,
* as described in the file LICENSE in the Alchemist distribution's top directory.
*/
package it.unibo.alchemist.model.biochemistry.reactions;
import it.unibo.alchemist.model.biochemistry.BiochemistryParseException;
import it.unibo.alchemist.model.biochemistry.BiochemistryIncarnation;
import it.unibo.alchemist.model.biochemistry.actions.AddJunctionInCell;
import it.unibo.alchemist.model.biochemistry.actions.AddJunctionInNeighbor;
import it.unibo.alchemist.model.biochemistry.actions.ChangeBiomolConcentrationInCell;
import it.unibo.alchemist.model.biochemistry.actions.ChangeBiomolConcentrationInEnv;
import it.unibo.alchemist.model.biochemistry.actions.ChangeBiomolConcentrationInNeighbor;
import it.unibo.alchemist.model.biochemistry.actions.RemoveJunctionInCell;
import it.unibo.alchemist.model.biochemistry.actions.RemoveJunctionInNeighbor;
import it.unibo.alchemist.model.biochemistry.conditions.BiomolPresentInCell;
import it.unibo.alchemist.model.biochemistry.conditions.BiomolPresentInEnv;
import it.unibo.alchemist.model.biochemistry.conditions.BiomolPresentInNeighbor;
import it.unibo.alchemist.model.biochemistry.conditions.EnvPresent;
import it.unibo.alchemist.model.biochemistry.conditions.JunctionPresentInCell;
import it.unibo.alchemist.model.biochemistry.conditions.NeighborhoodPresent;
import it.unibo.alchemist.model.biochemistry.molecules.Biomolecule;
import it.unibo.alchemist.model.biochemistry.molecules.Junction;
import it.unibo.alchemist.model.positions.Euclidean2DPosition;
import it.unibo.alchemist.model.Action;
import it.unibo.alchemist.model.Condition;
import it.unibo.alchemist.model.Environment;
import it.unibo.alchemist.model.Incarnation;
import it.unibo.alchemist.model.Molecule;
import it.unibo.alchemist.model.Node;
import it.unibo.alchemist.model.Position;
import it.unibo.alchemist.model.Reaction;
import it.unibo.alchemist.model.TimeDistribution;
import it.unibo.alchemist.model.geometry.Vector;
import it.unibo.alchemist.model.biochemistry.CellProperty;
import it.unibo.alchemist.model.biochemistry.dsl.BiochemistrydslBaseVisitor;
import it.unibo.alchemist.model.biochemistry.dsl.BiochemistrydslLexer;
import it.unibo.alchemist.model.biochemistry.dsl.BiochemistrydslParser;
import it.unibo.alchemist.model.biochemistry.dsl.BiochemistrydslParser.ArgListContext;
import it.unibo.alchemist.model.biochemistry.dsl.BiochemistrydslParser.BiochemicalReactionRightElemContext;
import it.unibo.alchemist.model.biochemistry.dsl.BiochemistrydslParser.BiomoleculeContext;
import org.antlr.v4.runtime.ANTLRErrorListener;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.Parser;
import org.antlr.v4.runtime.RecognitionException;
import org.antlr.v4.runtime.Recognizer;
import org.antlr.v4.runtime.atn.ATNConfigSet;
import org.antlr.v4.runtime.dfa.DFA;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.apache.commons.math3.random.RandomGenerator;
import org.danilopianini.jirf.Factory;
import org.danilopianini.jirf.FactoryBuilder;
import org.kaikikm.threadresloader.ResourceLoader;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* This class implements a builder for chemical reactions.
*
* @param {@link Position} type
*/
public class BiochemicalReactionBuilder
& Vector
> {
private final BiochemistryIncarnation incarnation;
private final Node node;
private final Environment environment;
private RandomGenerator rand;
private TimeDistribution time;
private String reactionString;
/**
* Construct a builder for biochemical reactions.
* @param incarnation the current incarnation
* @param currentNode the node where the reaction is placed.
* @param environment the environment.
*/
public BiochemicalReactionBuilder(
final BiochemistryIncarnation incarnation,
final Node currentNode,
final Environment environment
) {
this.incarnation = incarnation;
node = currentNode;
this.environment = environment;
}
/**
* Builds the chemical reaction.
* @return a chemical reaction based on the given program
*/
public Reaction build() {
checkReaction();
final BiochemistrydslLexer lexer = new BiochemistrydslLexer(CharStreams.fromString(reactionString));
final BiochemistrydslParser parser = new BiochemistrydslParser(new CommonTokenStream(lexer));
parser.removeErrorListeners();
parser.addErrorListener(new BiochemistryParseErrorListener(reactionString));
final ParseTree tree = parser.reaction();
final BiochemistryDSLVisitor eval = new BiochemistryDSLVisitor<>(rand, incarnation, time, node, environment);
return Objects.requireNonNull(eval.visit(tree), "Unable to visit/parse " + reactionString);
}
private void checkReaction() {
if (rand == null) {
throw new IllegalArgumentException("Random generator cannot be null");
}
if (time == null) {
throw new IllegalArgumentException("Time distribution cannot be null");
}
if (reactionString == null) {
throw new IllegalArgumentException("Reaction string cannot be null");
}
}
/**
* Set the reaction to the passed program string.
* @param program the string version of this reaction
* @return .
*/
public BiochemicalReactionBuilder
program(final String program) {
reactionString = program;
return this;
}
/**
* set the random generator to the passed object.
* @param rg the random generator.
* @return .
*/
public BiochemicalReactionBuilder
randomGenerator(final RandomGenerator rg) {
rand = rg;
return this;
}
/**
* Set the time distribution to the passed object.
* @param td the time distribution
* @return .
*/
public BiochemicalReactionBuilder
timeDistribution(final TimeDistribution td) {
time = td;
return this;
}
private static final class BiochemistryDSLVisitor>
extends BiochemistrydslBaseVisitor> {
private static final String CONDITIONS_PACKAGE = "it.unibo.alchemist.model.biochemistry.conditions.";
private static final String ACTIONS_PACKAGE = "it.unibo.alchemist.model.biochemistry.actions.";
private final Factory factory;
private final @Nonnull RandomGenerator rand;
private final @Nonnull Node node;
private final @Nullable CellProperty cell;
private final @Nonnull Environment environment;
private final @Nonnull Reaction reaction;
private final List> conditionList = new ArrayList<>(0);
private final List> actionList = new ArrayList<>(0);
private final Map biomolConditionsInCell = new LinkedHashMap<>();
private final Map biomolConditionsInNeighbor = new LinkedHashMap<>();
private final List junctionList = new ArrayList<>();
private boolean neighborActionPresent;
private boolean envConditionPresent;
private boolean envActionPresent;
private BiochemistryDSLVisitor(
@Nonnull final RandomGenerator rand,
@Nonnull final BiochemistryIncarnation incarnation,
@Nonnull final TimeDistribution timeDistribution,
@Nonnull final Node currentNode,
@Nonnull final Environment environment
) {
this.rand = rand;
this.node = currentNode;
this.cell = node.asPropertyOrNull(CellProperty.class);
this.environment = environment;
reaction = new BiochemicalReaction(node, timeDistribution, this.environment, rand);
factory = new FactoryBuilder()
.withAutoBoxing()
.withBooleanIntConversions()
.withNarrowingConversions()
.withArrayBooleanIntConversions()
.withArrayNarrowingConversions()
.build();
factory.registerSingleton(Incarnation.class, incarnation);
factory.registerSingleton(Environment.class, environment);
factory.registerSingleton(TimeDistribution.class, timeDistribution);
factory.registerSingleton(Node.class, node);
factory.registerSingleton(Reaction.class, reaction);
factory.registerSingleton(RandomGenerator.class, rand);
factory.registerImplicit(String.class, Double.class, incarnation::createConcentration);
factory.registerImplicit(String.class, Molecule.class, incarnation::createMolecule);
factory.registerImplicit(String.class, Boolean.class, Boolean::parseBoolean);
}
@SuppressWarnings("unchecked")
private O createObject(
final BiochemistrydslParser.JavaConstructorContext context,
final String packageName
) {
String className = context.javaClass().getText();
if (!className.contains(".")) {
className = packageName + className; // NOPMD UseStringBufferForStringAppends
}
try {
final Class clazz = (Class) ResourceLoader.classForName(className);
final ArgListContext lctx = context.argList();
final List