package com.plectix.simulator.parser.abstractmodel.reader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import com.plectix.simulator.parser.DocumentFormatException;
import com.plectix.simulator.parser.KappaFileLine;
import com.plectix.simulator.parser.KappaFileParagraph;
import com.plectix.simulator.parser.ParseErrorException;
import com.plectix.simulator.parser.ParseErrorMessage;
import com.plectix.simulator.parser.abstractmodel.ModelAgent;
import com.plectix.simulator.parser.abstractmodel.ModelPerturbation;
import com.plectix.simulator.parser.abstractmodel.perturbations.ModelLinearExpression;
import com.plectix.simulator.parser.abstractmodel.perturbations.RateExpressionParser;
import com.plectix.simulator.parser.abstractmodel.perturbations.SpeciesExpressionParser;
import com.plectix.simulator.parser.abstractmodel.perturbations.conditions.ModelConjuctionCondition;
import com.plectix.simulator.parser.abstractmodel.perturbations.conditions.ModelSpeciesCondition;
import com.plectix.simulator.parser.abstractmodel.perturbations.conditions.ModelTimeCondition;
import com.plectix.simulator.parser.abstractmodel.perturbations.conditions.PerturbationCondition;
import com.plectix.simulator.parser.abstractmodel.perturbations.modifications.AbstractOnceModification;
import com.plectix.simulator.parser.abstractmodel.perturbations.modifications.ModelAddOnceModification;
import com.plectix.simulator.parser.abstractmodel.perturbations.modifications.ModelDeleteOnceModification;
import com.plectix.simulator.parser.abstractmodel.perturbations.modifications.ModelRateModification;
import com.plectix.simulator.parser.abstractmodel.perturbations.modifications.PerturbationModification;
import com.plectix.simulator.parser.util.AgentFactory;
import com.plectix.simulator.parser.util.ParserUtil;
import com.plectix.simulator.simulator.SimulationArguments;
import com.plectix.simulator.util.InequalitySign;
/*package*/ final class PerturbationsParagraphReader extends
KappaParagraphReader<List<ModelPerturbation>> {
private final AgentFactory agentFactory;
public PerturbationsParagraphReader(SimulationArguments arguments, AgentFactory factory) {
super(arguments, factory);
agentFactory = factory;
}
private final PerturbationCondition parseCondition(String st, KappaFileLine perturbationStr) throws ParseErrorException {
String[] conditions = st.split(" & ");
if (conditions.length == 1) {
return parseSimpleCondition(st, perturbationStr);
} else {
return parseComplexCondition(conditions, perturbationStr);
}
}
private final PerturbationCondition parseSimpleCondition(String st, KappaFileLine perturbationStr) throws ParseErrorException {
if (st.indexOf("$T") == 0) {
return parseTimeCondition(st, perturbationStr);
} else {
return parseSpeciesCondition(st, perturbationStr);
}
}
private final PerturbationCondition parseComplexCondition(String[] conditionStrings, KappaFileLine perturbationStr) throws ParseErrorException {
Collection<PerturbationCondition> simpleConditions = new LinkedHashSet<PerturbationCondition>();
for (String simpleString : conditionStrings) {
simpleConditions.add(parseSimpleCondition(simpleString.trim(), perturbationStr));
}
return new ModelConjuctionCondition(simpleConditions);
}
public final List<ModelPerturbation> readComponent(KappaFileParagraph perturbationsParagraph)
throws ParseErrorException, DocumentFormatException {
List<ModelPerturbation> perturbations = new ArrayList<ModelPerturbation>();
int pertubationID = 0;
for (KappaFileLine perturbationStr : perturbationsParagraph.getLines()) {
try {
String st = perturbationStr.getLine().trim();
ParserUtil.checkString("do", st, perturbationStr);
String stCondition = st.substring(0, st.indexOf("do") - 1);
String stModification = st.substring(st.indexOf("do") + 2);
PerturbationCondition condition;
PerturbationModification modification;
// condition
condition = parseCondition(stCondition, perturbationStr);
// modification
modification = checkOnce(stModification, perturbationStr);
if (modification == null) {
modification = parseRateExpression(stModification, perturbationStr);
}
perturbations.add(new ModelPerturbation(pertubationID++, condition, modification));
} catch (ParseErrorException e) {
e.setLineDescription(perturbationStr);
throw e;
}
}
return perturbations;
}
private final ModelTimeCondition parseTimeCondition(String st, KappaFileLine perturbationStr) throws ParseErrorException {
InequalitySign inequalitySign = parseInequalitySign(st, 2);
if (inequalitySign != InequalitySign.GREATER) {
throw new ParseErrorException(perturbationStr,
ParseErrorMessage.WRONG_TIME_PERTURBATION_SYNTAX);
}
st = st.substring(2).trim();
st = st.substring(1).trim();
double time = 0;
try {
time = Double.valueOf(st);
} catch (NumberFormatException e) {
throw new ParseErrorException(perturbationStr,
ParseErrorMessage.WRONG_TIME_BOUNDARY, st);
}
return new ModelTimeCondition(time);
}
private final ModelRateModification parseRateExpression(String line,
KappaFileLine perturbationLine) throws ParseErrorException {
boolean fail = false;
if (line.length() > 0) {
fail = Character.isLetter(line.charAt(0));
} else {
throw new ParseErrorException(perturbationLine,
ParseErrorMessage.MODIFICATION_EXPECTED);
}
ParserUtil.checkString("'", line, perturbationLine);
line = line.substring(line.indexOf("'") + 1).trim();
if (fail) {
throw new ParseErrorException(perturbationLine,
ParseErrorMessage.DO_EXPECTED);
}
ParserUtil.checkString("'", line, perturbationLine);
String ruleName = line.substring(0, line.indexOf("'")).trim();
int index = line.indexOf(":=");
ParserUtil.checkString(":=", line, perturbationLine);
line = line.substring(index + 2);
ModelLinearExpression expressionRHS = new RateExpressionParser().parse(line,
perturbationLine);
return new ModelRateModification(ruleName, expressionRHS);
}
private final ModelSpeciesCondition parseSpeciesCondition(String line,
KappaFileLine perturbationLine) throws ParseErrorException {
ParserUtil.checkString("[", line, perturbationLine);
line = line.substring(line.indexOf("[") + 1).trim();
ParserUtil.checkString("'", line, perturbationLine);
line = line.substring(line.indexOf("'") + 1).trim();
String argumentObservableName = ParserUtil.parseRuleName(line);
ParserUtil.checkString("]", line, perturbationLine);
line = line.substring(line.indexOf("]") + 1).trim();
InequalitySign inequalitySign = parseInequalitySign(line, 0);
line = line.substring(1).trim();
ModelLinearExpression expressionRHS = new SpeciesExpressionParser().parse(
line, perturbationLine);
return new ModelSpeciesCondition(argumentObservableName,
expressionRHS, inequalitySign);
}
private final InequalitySign parseInequalitySign(String line, int beginIndex)
throws ParseErrorException {
line = line.substring(beginIndex).trim();
if (line.startsWith(">")) {
return InequalitySign.GREATER;
} else if (line.startsWith("<")) {
return InequalitySign.LESS;
} else
throw new ParseErrorException(ParseErrorMessage.SENSE_OF_INEQUALITY_EXPECTED, line);
}
private final AbstractOnceModification checkOnce(String line,
KappaFileLine perturbationLine) throws ParseErrorException, DocumentFormatException {
int indexAdd = line.indexOf("$ADDONCE");
int indexDel = line.indexOf("$DELETEONCE");
if (indexAdd == -1 && indexDel == -1)
return null;
if (indexAdd != -1 && indexDel != -1)
throw new ParseErrorException(perturbationLine,
ParseErrorMessage.$ADDONCE_OR_$DELETEONCE);
boolean addOnce = false;
String lineCopy = new String(line);
if (indexAdd != -1) {
lineCopy = lineCopy.substring(indexAdd + 8);
addOnce = true;
} else {
lineCopy = lineCopy.substring(indexDel + 11);
}
int quantity = 1;
int indexCount = lineCopy.indexOf("*");
if (indexCount != -1) {
String strCount = lineCopy.substring(0, indexCount).trim();
if ("$INF".equals(strCount.trim())) {
quantity = -1;
if (addOnce) {
throw new ParseErrorException(perturbationLine,
ParseErrorMessage.$INF_USED_WITH_$ADDONCE);
}
} else {
try {
quantity = Integer.valueOf(strCount);
} catch (NumberFormatException e) {
throw new ParseErrorException(perturbationLine,
ParseErrorMessage.ONCE_QUANTITY_FORMAT, strCount);
}
}
lineCopy = lineCopy.substring(indexCount + 1);
}
List<ModelAgent> agentList = agentFactory.parseAgent(lineCopy);
AbstractOnceModification modification;
if (addOnce) {
modification = new ModelAddOnceModification(agentList, quantity);
} else {
modification = new ModelDeleteOnceModification(agentList, quantity);
}
return modification;
}
}