package uk.ac.ed.inf.biopepa.core.sba.export;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map.Entry;
import uk.ac.ed.inf.biopepa.core.BioPEPAException;
import uk.ac.ed.inf.biopepa.core.compiler.ModelCompiler;
import uk.ac.ed.inf.biopepa.core.interfaces.Exporter;
import uk.ac.ed.inf.biopepa.core.interfaces.Result;
import uk.ac.ed.inf.biopepa.core.sba.FileStringConsumer;
import uk.ac.ed.inf.biopepa.core.sba.LineStringBuilder;
import uk.ac.ed.inf.biopepa.core.sba.PhaseLine;
import uk.ac.ed.inf.biopepa.core.sba.SBAComponentBehaviour;
import uk.ac.ed.inf.biopepa.core.sba.SBAModel;
import uk.ac.ed.inf.biopepa.core.sba.SBAReaction;
import uk.ac.ed.inf.biopepa.core.sba.StringConsumer;
import uk.ac.ed.inf.biopepa.core.sba.export.SimulationTracer.NullTraceLog;
import uk.ac.ed.inf.biopepa.core.sba.export.SimulationTracer.SimulationTraceLog;
public class TraviandoExport implements Exporter {
private static final String description;
private SBAModel model;
private int numberFiringsLimit = Integer.MAX_VALUE;
private double timeLimit = Double.MAX_VALUE;
private double dataPointStep = 1.0;
public void setDataPointStep (double newDataPointStep){
this.dataPointStep = newDataPointStep;
}
/* Now for phases */
/* Now we set up the phases */
private PhaseLine [] phaseLines;
public void setPhaseLines (PhaseLine[] phaseLines){
this.phaseLines = phaseLines;
}
private boolean displayComments = true;
private String modelComment = "Trace generated automagically from " + "BioPEPA Eclipse Plugin";
/*
* public TraviandoExport(int numberFiringsLimit, double timeLimit){
* this.numberFiringsLimit = numberFiringsLimit; this.timeLimit = timeLimit
* ; }
*/
static {
StringBuilder sb = new StringBuilder();
// This description should be much more verbose
sb.append("Traviando export");
description = sb.toString();
}
public void setFiringsLimit(int newLimit) {
this.numberFiringsLimit = newLimit;
}
public void setTimeLimit(double newLimit) {
this.timeLimit = newLimit;
}
public void setDisplayComments(boolean b) {
this.displayComments = b;
}
public void setModelComment(String comment) {
this.modelComment = comment;
}
public String canExport() {
if (model == null)
throw new IllegalStateException("Model has not been set using setModel/1");
return null;
}
public String getDescription() {
return description;
}
public String getExportPrefix() {
return "xml";
}
public String getLongName() {
return "Traviandor trace analyser";
}
public String getShortName() {
return "Trav";
}
public Object requiredDataStructure() {
return SBAModel.class;
}
public void setModel(SBAModel model) throws UnsupportedOperationException {
if (model == null)
throw new NullPointerException("SBA model must be non-null");
if (this.model != null)
throw new IllegalStateException("Model has already been set.");
this.model = model;
}
public void setModel(ModelCompiler compiledModel) {
throw new UnsupportedOperationException();
}
private String modelName = null;
public void setName(String modelName) {
this.modelName = modelName;
}
public Object toDataStructure() throws UnsupportedOperationException {
throw new UnsupportedOperationException ();
}
/*
* Here we are turning a model into a traviando trace file.
*/
public String toString() {
if (model == null)
throw new IllegalStateException("Model has not been set using setModel/1");
LineStringBuilder travSb = new LineStringBuilder();
// ISBJava isbJava = new ISBJava(model, model.getComponentNames());
// SimulationTrace simTrace = isbJava.new SimulationTrace();
/* I should also be able to make a string consumer which is
* outputs to a file and hence doesn't cost as much in memory
* hence allowing longer traces.
*/
try {
TraviandoTraceLog traceLog = new TraviandoTraceLog(travSb, model);
traceLog.setModelName(this.modelName);
traceLog.setModelComment(this.modelComment);
traceLog.setDisplayComments(this.displayComments);
SimulationTracer simTracer = new SimulationTracer(model);
simTracer.generateSimulationTrace(traceLog);
} catch (BioPEPAException e) {
// Todo We should report this better using perhaps
// a dialog box
travSb.appendLine(e.getMessage());
} catch (IOException e){
travSb.appendLine(e.getMessage());
}
return travSb.toString();
}
private Result simulationResults;
public Result getSimulationResults(){
return this.simulationResults;
}
/*
* Although the 'toString' method above is required for
* the 'Exporter' interface, here we allow a more efficient
* version which writes directly out to file.
*/
public void exportToFile (String filename) throws IOException{
FileStringConsumer fsc = new FileStringConsumer(filename);
try {
fsc.openStringConsumer();
TraviandoTraceLog traceLog = new TraviandoTraceLog(fsc, model);
traceLog.setModelName(this.modelName);
traceLog.setModelComment(this.modelComment);
traceLog.setDisplayComments(this.displayComments);
SimulationTracer simTracer = new SimulationTracer(model);
// Set up the simulation parameters
simTracer.setTimeLimit(this.timeLimit);
simTracer.setFiringsLimit(this.numberFiringsLimit);
simTracer.setDataPointStep(this.dataPointStep);
simTracer.setPhaseLines(this.phaseLines);
// Then just let the simulation tracer do its work
simTracer.generateSimulationTrace(traceLog);
this.simulationResults = simTracer.getSimulationResults();
} catch (BioPEPAException e) {
fsc.appendLine(e.getMessage());
}
fsc.closeStringConsumer();
}
/*
* Well clearly this is not really appropriate for this class
* the intention is that this class is generalised into a
* simulation tracer which may not actually produce a file
*/
public void justDrawTheGraph () throws IOException {
try {
SimulationTracer simTracer = new SimulationTracer(model);
NullTraceLog traceLog = new NullTraceLog();
// Set up the simulation parameters
simTracer.setTimeLimit(this.timeLimit);
simTracer.setFiringsLimit(this.numberFiringsLimit);
simTracer.setDataPointStep(this.dataPointStep);
simTracer.setPhaseLines(this.phaseLines);
// Then just let the simulation tracer do its work
simTracer.generateSimulationTrace(traceLog);
this.simulationResults = simTracer.getSimulationResults();
} catch (BioPEPAException e) {
return;
}
}
public static class TraviandoTraceLog implements SimulationTraceLog {
private StringConsumer scon;
/*
* If this trace is a file (or other output) of its
* own then it should open and close the string consumer.
* However this trace may be part of a larger output and
* hence should not manage the open and closing of the file
* consumer.
*/
private boolean completeConsumer = true;
private HashMap<String, String> compVarNames;
private HashMap<String, String> reactionIdMap;
private HashMap<String, String> compIdMap;
private String [] componentNames;
private SBAReaction[] sbaReactions;
private String modelName = "";
private String modelComment = "";
private boolean displayComments = false;
/*
* If this trace is a file (or other output) of its
* own then it should open and close the string consumer.
* However this trace may be part of a larger output and
* hence should not manage the open and closing of the file
* consumer. Hence in the latter case the caller should call
* setCompleteConsumer(false) and manage the string consumer
* themselves.
*/
public void setCompleteConsumer (boolean complete){
this.completeConsumer = complete;
}
public TraviandoTraceLog (StringConsumer scon, SBAModel model){
this.scon = scon;
this.compVarNames = new HashMap<String, String>();
this.reactionIdMap = new HashMap<String, String>();
this.compIdMap = new HashMap<String, String>();
this.componentNames = model.getComponentNames();
this.sbaReactions = model.getReactions();
}
public void setModelName (String name){
this.modelName = name;
}
public void setModelComment (String comment){
this.modelComment = comment;
}
public void setDisplayComments (boolean display){
this.displayComments = display;
}
public void traceLogFooter (Result result) throws IOException {
scon.appendLine("</Sequence>");
scon.appendLine("</Trace>");
if (this.completeConsumer){
scon.closeStringConsumer();
}
}
public void traceLogHeader(HashMap<String, Number> componentCounts)
throws IOException {
if (this.completeConsumer){
scon.openStringConsumer();
}
scon.appendLine("<?xml version=\"1.0\" encoding=\"utf-8\" ?>");
scon.appendLine("<!-- Trace file of model " + modelName + " -->");
scon.appendLine("<Trace model=\"" + modelName + "\" generator=\"BioPEPA-Eclipse-Plugin\" >");
scon.appendLine("<Comment> " + modelComment + " </Comment>");
scon.appendLine("<!-- declaration of processes -->");
/*
* Here we say that each population is a process which has one
* variable which is its own population count. This may be a bit
* redundant but I don't think you can assign to a process. This
* means the var name is the same as the Process name, but I think
* that's actually correct.
*/
for (int i = 0; i < componentNames.length; i++) {
String procName = componentNames[i];
scon.appendLine("<Process id=\"" + i + "\" name=\"" + procName + "\" >");
/*
* Now we wish to know if we should output any (private) actions
* for this process, so we iterate through all the reactions and
* for each reaction if it is the sole product or reactant then
* we output the reaction as a private action of this process
*/
for (int index = 0; index < sbaReactions.length; index++) {
SBAReaction reaction = sbaReactions[index];
// appendLine(sb, "Doing reaction - " + reaction.getName());
boolean containsThis = false;
boolean containsOther = false;
// appendLine(sb, "Doing the reaction: " +
// reaction.getName());
for (SBAComponentBehaviour product : reaction.getProducts()) {
String pName = product.getName();
if (procName.equals(pName)){
containsThis = true;
} else {
containsOther = true;
}
}
for (SBAComponentBehaviour reactant : reaction.getReactants()) {
if (procName.equals(reactant.getName())){
containsThis = true;
} else {
containsOther = true;
}
}
// appendLine(sb, "End reaction: " + reaction.getName());
if (containsThis && !containsOther) {
String idName = "a_" + index;
String rName = reaction.getName();
scon.appendLine("<Action id=\"" + idName + "\" name =\"" + rName + "\" />");
reactionIdMap.put(rName, idName);
}
}
/*
* Now we output the 'Var' description which basically just
* declares a variable associated with the process the
* associated variable contains the population count.
*/
compIdMap.put(procName, Integer.toString(i));
String popName = "pop_" + i;
scon.appendLine("<Var id = \"" + popName + "\" name=\"" + procName + "\" />");
scon.appendLine("</Process>");
compVarNames.put(procName, popName);
}
/*
* Now the interactions basically for each reaction we state that a
* component is 'touched' by it, if it is either a product or a
* reactant
*/
scon.appendLine("<Interactions>");
// ExperimentLine experimentLine = new ExperimentSet().emptyExperimentLine("default");
for (int index = 0; index < sbaReactions.length; index++) {
SBAReaction reaction = sbaReactions[index];
List<SBAComponentBehaviour> products = reaction.getProducts();
List<SBAComponentBehaviour> reactants = reaction.getReactants();
String reactionId = "sa_" + index;
/*
* This test kind of assumes we don't have a single component
* which is both a product and a reactant
*/
if (products.size() + reactants.size() > 1) {
scon.appendLine("<Undiraction id=\"" + reactionId + "\" name=\"" + reaction.getName() + "\" >");
reactionIdMap.put(reaction.getName(), reactionId);
for (SBAComponentBehaviour p : products) {
scon.appendLine("<Touch>" + compIdMap.get(p.getName()) + "</Touch>");
}
for (SBAComponentBehaviour r : reactants) {
scon.appendLine("<Touch>" + compIdMap.get(r.getName()) + "</Touch>");
}
scon.appendLine("</Undiraction>");
}
}
scon.appendLine("</Interactions>");
scon.appendLine("<Sequence type=\"not-sure\">");
scon.appendLine("<S>");
for (String compName : componentNames) {
String compId = compVarNames.get(compName);
Number compCount = componentCounts.get(compName);
outputAssignment(compId, compCount.intValue());
}
scon.appendLine("</S>");
}
private void outputComponentCounts(HashMap<String, Number> componentCounts) throws IOException {
scon.appendLine(" <!-- ");
for (Entry<String, Number> entry : componentCounts.entrySet()) {
scon.appendLine("Component: " + entry.getKey() + " = " + entry.getValue());
}
scon.appendLine(" --> ");
}
/* Output the initial population counts */
private void outputAssignment(String procName, int count) throws IOException {
scon.appendLine("<V id=\"" + procName + "\" val=\"" + count + "\" />");
}
public void displayComponentCounts (HashMap<String, Number> componentCounts)
throws IOException{
if (displayComments) {
outputComponentCounts(componentCounts);
}
}
public void displayEnabledReaction(String reactionName, double rateValue) throws IOException {
if (displayComments) {
scon.appendLine(" <!-- " + reactionName + " has rate " + rateValue + " (which is : "
+ /*rValue.toString() + */ ") --> ");
}
}
/*
* (non-Javadoc)
* @see uk.ac.ed.inf.biopepa.core.sba.export.SimulationTracer.SimulationTraceLog#startEvent(java.lang.String, double)
* The arguments to startEvent and stopEvent are set up for traviando traces,
* we should either pass all information into each of them or find a better way to
* log events.
*/
public void startEvent(String rname, double totalTime) throws BioPEPAException, IOException{
String idName = reactionIdMap.get(rname);
if (idName == null){
throw new BioPEPAException ("Reaction: " + rname +
" not found in reaction id map");
}
scon.appendLine("<A id=\"" + idName + "\" t=\"" + totalTime + "\"" + " i=\""
+ rname + ".1\" >");
}
public void outputComponentUpdate (String rName, int newValue) throws IOException {
outputAssignment(compVarNames.get(rName), newValue);
}
public void endEvent(double thisDelay, double totalRate,
HashMap<String, Number> componentCounts)
throws IOException {
if (displayComments) {
scon.appendLine(" <!-- This firing's delay is: " + thisDelay + " --> ");
scon.appendLine(" <!-- The rate is: " + totalRate + " --> ");
outputComponentCounts(componentCounts);
}
scon.appendLine("</A>");
}
public void reportDeadlocked () throws IOException {
scon.appendLine("<!-- Total rate = zero, deadlocked state -->");
}
}
}