From f8e4f427bcf4d45cf7d932af4a3f3132a96208f8 Mon Sep 17 00:00:00 2001 From: Dave Parker Date: Mon, 21 Sep 2015 23:42:35 +0000 Subject: [PATCH] Refactoring of to-PRISM language translations: addition of PrismLanguageTranslator abstract class (preliminary version of) and connection with SBML/reactions/PEPA translators. Also, add support for SBML import to Prism class (but not used elsewhere yet). git-svn-id: https://www.prismmodelchecker.org/svn/prism/prism/trunk@10687 bbc10eb1-c90d-0410-af57-cb519fbb1720 --- prism/src/prism/PEPA2Prism.java | 91 ++++++++++++++ prism/src/prism/Prism.java | 94 +++++++++----- prism/src/prism/PrismLanguageTranslator.java | 94 ++++++++++++++ prism/src/prism/Reactions2Prism.java | 5 +- prism/src/prism/ReactionsText2Prism.java | 118 +++++++++-------- prism/src/prism/SBML2Prism.java | 126 +++++++++++-------- 6 files changed, 387 insertions(+), 141 deletions(-) create mode 100644 prism/src/prism/PEPA2Prism.java create mode 100644 prism/src/prism/PrismLanguageTranslator.java diff --git a/prism/src/prism/PEPA2Prism.java b/prism/src/prism/PEPA2Prism.java new file mode 100644 index 00000000..714362f5 --- /dev/null +++ b/prism/src/prism/PEPA2Prism.java @@ -0,0 +1,91 @@ +package prism; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintStream; + +public class PEPA2Prism extends PrismLanguageTranslator +{ + private File modelFile; + + @Override + public String getName() + { + return "pepa"; + } + + @Override + public void load(File file) throws PrismException + { + modelFile = file; + } + + @Override + public void load(String s) throws PrismException + { + try { + // Create temporary file containing pepa model + modelFile = File.createTempFile("tempPepa" + System.currentTimeMillis(), ".pepa"); + FileWriter write = new FileWriter(modelFile); + write.write(s); + write.close(); + } catch (IOException e) { + if (modelFile != null) { + modelFile.delete(); + modelFile = null; + } + throw new PrismException("Couldn't create temporary file for PEPA conversion"); + } + } + + @Override + public void load(InputStream in) throws PrismException + { + try { + // Create temporary file containing pepa model + modelFile = File.createTempFile("tempPepa" + System.currentTimeMillis(), ".pepa"); + FileWriter write = new FileWriter(modelFile); + int byteIn = in.read(); + while (byteIn != -1) { + write.write(byteIn); + byteIn = in.read(); + } + write.close(); + } catch (IOException e) { + if (modelFile != null) { + modelFile.delete(); + modelFile = null; + } + throw new PrismException("Couldn't create temporary file for PEPA conversion"); + } + } + + @Override + public String translateToString() throws PrismException + { + // Translate PEPA model to PRISM model string + String prismModelString = null; + try { + prismModelString = pepa.compiler.Main.compile("" + modelFile); + } catch (pepa.compiler.InternalError e) { + if (modelFile != null) { + modelFile.delete(); + modelFile = null; + } + throw new PrismException("Could not import PEPA model:\n" + e.getMessage()); + } + if (modelFile != null) { + modelFile.delete(); + modelFile = null; + } + return prismModelString; + } + + @Override + public void translate(PrintStream out) throws PrismException + { + out.print(translateToString()); + } +} diff --git a/prism/src/prism/Prism.java b/prism/src/prism/Prism.java index 4ce7050f..1b730b63 100644 --- a/prism/src/prism/Prism.java +++ b/prism/src/prism/Prism.java @@ -34,6 +34,8 @@ import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileWriter; import java.io.IOException; +import java.io.InputStream; +import java.io.PrintStream; import java.util.ArrayList; import java.util.List; @@ -1405,17 +1407,7 @@ public class Prism extends PrismComponent implements PrismSettingsListener */ public ModulesFile importPepaFile(File file) throws PrismException, PrismLangException { - String modelString; - - // compile pepa file to string - try { - modelString = pepa.compiler.Main.compile("" + file); - } catch (pepa.compiler.InternalError e) { - throw new PrismException("Could not import PEPA file:\n" + e.getMessage()); - } - - // parse string as prism model and return - return parseModelString(modelString); + return importModelFile("pepa", file); } /** @@ -1424,34 +1416,68 @@ public class Prism extends PrismComponent implements PrismSettingsListener */ public ModulesFile importPepaString(String s) throws PrismException, PrismLangException { - File pepaFile = null; - String modelString; + return importModelString("pepa", s); + } - // create temporary file containing pepa model - try { - pepaFile = File.createTempFile("tempPepa" + System.currentTimeMillis(), ".pepa"); - FileWriter write = new FileWriter(pepaFile); - write.write(s); - write.close(); - } catch (IOException e) { - if (pepaFile != null) - pepaFile.delete(); - throw new PrismException("Couldn't create temporary file for PEPA conversion"); - } + /** + * Import a PRISM model from an SBML model in a file + * @param file File to read in + */ + public ModulesFile importSBMLFile(File file) throws PrismException, PrismLangException + { + return importModelFile("sbml", file); + } - // compile pepa file to string - try { - modelString = pepa.compiler.Main.compile("" + pepaFile); - } catch (pepa.compiler.InternalError e) { - if (pepaFile != null) - pepaFile.delete(); - throw new PrismException("Could not import PEPA file:\n" + e.getMessage()); - } + /** + * Import a PRISM model from an SBML model in a string + * @param file File to read in + */ + public ModulesFile importSBMLString(String s) throws PrismException, PrismLangException + { + return importModelString("sbml", s); + } - // parse string as prism model and return - return parseModelString(modelString); + /** + * Import a PRISM model by translating from another language + */ + public ModulesFile importModelFile(String lang, File file) throws PrismException, PrismLangException + { + PrismLanguageTranslator importer = createPrismLanguageTranslator(lang); + importer.load(file); + String prismModelString = importer.translateToString(); + return parseModelString(prismModelString); + } + + /** + * Import a PRISM model by translating from another language + */ + public ModulesFile importModelString(String lang, String s) throws PrismException, PrismLangException + { + PrismLanguageTranslator importer = createPrismLanguageTranslator(lang); + importer.load(s); + String prismModelString = importer.translateToString(); + return parseModelString(prismModelString); } + /** + * Create a translator to the PRISM language. + */ + private PrismLanguageTranslator createPrismLanguageTranslator(String lang) throws PrismException + { + PrismLanguageTranslator importer = null; + switch (lang) { + case "pepa": + importer = new PEPA2Prism(); + break; + case "sbml": + importer = new SBML2Prism(); + break; + default: + throw new PrismException("Unknown import language \"" + lang + "\""); + } + return importer; + } + /** * Import a PRISM model from a PRISM preprocessor file * @param file File to read in diff --git a/prism/src/prism/PrismLanguageTranslator.java b/prism/src/prism/PrismLanguageTranslator.java new file mode 100644 index 00000000..c4575628 --- /dev/null +++ b/prism/src/prism/PrismLanguageTranslator.java @@ -0,0 +1,94 @@ +//============================================================================== +// +// Copyright (c) 2015- +// Authors: +// * Dave Parker (University of Birmingham) +// +//------------------------------------------------------------------------------ +// +// This file is part of PRISM. +// +// PRISM is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// PRISM is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with PRISM; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +//============================================================================== + +package prism; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.io.PrintStream; +import java.io.UnsupportedEncodingException; + +/** + * Base class for classes that convert to PRISM language models from other languages. + */ +public abstract class PrismLanguageTranslator +{ + /** + * Get a name associated with the language to be translated from. + */ + public abstract String getName(); + + /** + * Load a model to be translated from an input stream. + */ + public abstract void load(InputStream in) throws PrismException; + + /** + * Load a model to be translated from a string. + */ + public void load(String modelString) throws PrismException + { + load(new ByteArrayInputStream(modelString.getBytes())); + } + + /** + * Load a model to be translated from a file. + */ + public void load(File file) throws PrismException + { + FileInputStream in; + try { + in = new FileInputStream(file); + } catch (FileNotFoundException e) { + throw new PrismException("File \"" + file.getPath() + "\" not found"); + } + load(in); + } + + /** + * Translate the loaded model to a PRISM language model and write it to an output stream. + */ + public abstract void translate(PrintStream out) throws PrismException; + + /** + * Translate the loaded model to a PRISM language model and return it as a string. + */ + public String translateToString() throws PrismException + { + ByteArrayOutputStream os = new ByteArrayOutputStream(); + PrintStream out = new PrintStream(os); + translate(out); + try { + return os.toString("UTF-8"); + } catch (UnsupportedEncodingException e) { + throw new PrismException("Error translating output stream to string: " + e.getMessage()); + } + } +} diff --git a/prism/src/prism/Reactions2Prism.java b/prism/src/prism/Reactions2Prism.java index bb27a7b5..a2fc0db0 100644 --- a/prism/src/prism/Reactions2Prism.java +++ b/prism/src/prism/Reactions2Prism.java @@ -31,7 +31,6 @@ import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; -import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintStream; import java.util.ArrayList; @@ -44,7 +43,7 @@ import parser.PrismParser; /** * Base class for classes that build/store a set of biological reactions and then convert to PRISM. */ -public class Reactions2Prism +public abstract class Reactions2Prism extends PrismLanguageTranslator { // Log for output of warnings, messages protected PrismLog mainLog = null; @@ -104,7 +103,7 @@ public class Reactions2Prism /** * Process the currently loaded reaction set model, convert to PRISM code, export to an OutputStream. */ - protected void convertToPRISMCode(OutputStream out) throws PrismException + protected void convertToPRISMCode(PrintStream out) throws PrismException { StringBuilder sb = convertToPRISMCode(); try { diff --git a/prism/src/prism/ReactionsText2Prism.java b/prism/src/prism/ReactionsText2Prism.java index 02b72b63..97a1a895 100644 --- a/prism/src/prism/ReactionsText2Prism.java +++ b/prism/src/prism/ReactionsText2Prism.java @@ -28,8 +28,10 @@ package prism; import java.io.BufferedReader; import java.io.File; -import java.io.FileReader; import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.PrintStream; import java.util.ArrayList; /** @@ -37,32 +39,9 @@ import java.util.ArrayList; */ public class ReactionsText2Prism extends Reactions2Prism { - /** - * Calling point for command-line script: - * e.g. java -cp classes prism.ReactionsText2Prism myfile.txt 100 - * (100 denotes (integer) maximum for species population sizes, default is 100) - */ - public static void main(String args[]) - { - PrismLog errLog = new PrismPrintStreamLog(System.err); - try { - if (args.length < 1) { - System.err.println("Usage: java -cp classes prism.ReactionsText2Prism [max_amount]"); - System.exit(1); - } - ReactionsText2Prism rt2prism = new ReactionsText2Prism(errLog); - try { - if (args.length > 1) - rt2prism.setMaxAmount(Integer.parseInt(args[1])); - } catch (NumberFormatException e) { - throw new PrismException("Invalid max amount \"" + args[1] + "\""); - } - rt2prism.translate(new File(args[0])); - } catch (PrismException e) { - errLog.println("Error: " + e.getMessage() + "."); - } - } - + /** Name of the original file */ + protected String parsedFileName; + // Enums private enum SectionType { @@ -81,25 +60,24 @@ public class ReactionsText2Prism extends Reactions2Prism super(mainLog); } - /** - * Main method: load reactions file, process and send resulting PRISM file to stdout - */ - public void translate(File file) throws PrismException + @Override + public String getName() { - // Read in file - extractModelFromFile(file); - // Generate PRISM code - prismCodeHeader = "// File generated by reactions-to-PRISM conversion\n"; - prismCodeHeader += "// Original file: " + file.getPath() + "\n\n"; - convertToPRISMCode(System.out); + return "reactions"; } - /** - * Build the reaction set model from a parsed SBML file. - */ - private void extractModelFromFile(File file) throws PrismException + @Override + public void load(File file) throws PrismException + { + // Store filename for later use before parsing + parsedFileName = file.getPath(); + super.load(file); + } + + @Override + public void load(InputStream in) throws PrismException { - BufferedReader in; + BufferedReader buf; SectionType secType = null; String s, s2, ss[], ss2[]; int i, lineNum = 0; @@ -115,9 +93,9 @@ public class ReactionsText2Prism extends Reactions2Prism try { // Open file for reading - in = new BufferedReader(new FileReader(file)); + buf = new BufferedReader(new InputStreamReader(in)); // Read remaining lines - s = in.readLine(); + s = buf.readLine(); lineNum++; while (s != null) { // Strip comments @@ -213,7 +191,7 @@ public class ReactionsText2Prism extends Reactions2Prism reaction.addProduct(product, 1); } // Next line - s = in.readLine(); + s = buf.readLine(); lineNum++; if (s == null) throw new PrismException("missing line in reaction definition"); @@ -244,18 +222,58 @@ public class ReactionsText2Prism extends Reactions2Prism } } // read next line - s = in.readLine(); + s = buf.readLine(); lineNum++; } // close file - in.close(); + buf.close(); } catch (IOException e) { - throw new PrismException("File I/O error reading from \"" + file + "\""); + throw new PrismException("I/O error reading reactions: " + e.getMessage()); } catch (NumberFormatException e) { - throw new PrismException("Error detected at line " + lineNum + " of file \"" + file + "\""); + throw new PrismException("Error detected at line " + lineNum + " of reactions file"); + } catch (PrismException e) { + throw new PrismException("Error detected (" + e.getMessage() + ") at line " + lineNum + " of reactions file"); + } + } + + @Override + public void translate(PrintStream out) throws PrismException + { + // Generate PRISM code + prismCodeHeader = "// File generated by reactions-to-PRISM conversion\n"; + if (parsedFileName != null) { + prismCodeHeader += "// Original file: " + parsedFileName + "\n\n"; + } + convertToPRISMCode(out); + // Reset filename storage + parsedFileName = null; + } + + /** + * Calling point for command-line script: + * e.g. java -cp classes prism.ReactionsText2Prism myfile.txt 100 + * (100 denotes (integer) maximum for species population sizes, default is 100) + */ + public static void main(String args[]) + { + PrismLog errLog = new PrismPrintStreamLog(System.err); + try { + if (args.length < 1) { + System.err.println("Usage: java -cp classes prism.ReactionsText2Prism [max_amount]"); + System.exit(1); + } + ReactionsText2Prism rt2prism = new ReactionsText2Prism(errLog); + try { + if (args.length > 1) + rt2prism.setMaxAmount(Integer.parseInt(args[1])); + } catch (NumberFormatException e) { + throw new PrismException("Invalid max amount \"" + args[1] + "\""); + } + rt2prism.load(new File(args[0])); + rt2prism.translate(System.out); } catch (PrismException e) { - throw new PrismException("Error detected (" + e.getMessage() + ") at line " + lineNum + " of file \"" + file + "\""); + errLog.println("Error: " + e.getMessage() + "."); } } } diff --git a/prism/src/prism/SBML2Prism.java b/prism/src/prism/SBML2Prism.java index 83b81de2..da555cf5 100644 --- a/prism/src/prism/SBML2Prism.java +++ b/prism/src/prism/SBML2Prism.java @@ -29,6 +29,7 @@ package prism; import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.io.PrintStream; import java.util.ArrayList; import javax.xml.parsers.DocumentBuilder; @@ -47,33 +48,10 @@ import org.xml.sax.SAXParseException; public class SBML2Prism extends Reactions2Prism implements EntityResolver { - /** - * Calling point for command-line script: - * This is probably the sbml2prism (found in etc/scripts) - * But it can also be called as, e.g.: java -cp classes prism.SBML2Prism myfile.sbml 100 - * (100 denotes (integer) maximum for species population sizes, default is 100) - * (also used to compute amounts from (real-valued) concentrations) - */ - public static void main(String args[]) - { - PrismLog errLog = new PrismPrintStreamLog(System.err); - try { - if (args.length < 1) { - System.err.println("Usage: sbml2prism [max_amount]"); - System.exit(1); - } - SBML2Prism sbml2prism = new SBML2Prism(errLog); - try { - if (args.length > 1) - sbml2prism.setMaxAmount(Integer.parseInt(args[1])); - } catch (NumberFormatException e) { - throw new PrismException("Invalid max amount \"" + args[1] + "\""); - } - sbml2prism.translate(new File(args[0])); - } catch (PrismException e) { - errLog.println("Error: " + e.getMessage() + "."); - } - } + /** Name of the original SBML file */ + protected String parsedFileName; + /** Temporary storage of the parsed XML */ + protected Document parsedSBML; // Constructors @@ -87,29 +65,26 @@ public class SBML2Prism extends Reactions2Prism implements EntityResolver super(mainLog); } - /** - * Main method: load SBML file, process and send resulting PRISM file to stdout - */ - public void translate(File file) throws PrismException + @Override + public String getName() { - // Read in SBML - Document doc = parseSBML(file); - checkSBMLVersion(doc); - extractModelFromSBML(doc); - // Generate PRISM code - prismCodeHeader = "// File generated by automatic SBML-to-PRISM conversion\n"; - prismCodeHeader += "// Original SBML file: " + file.getPath() + "\n\n"; - convertToPRISMCode(System.out); + return "sbml"; } - /** - * Parse SBML file {@code file} and return as an XML Document object. - */ - private Document parseSBML(File file) throws PrismException + @Override + public void load(File file) throws PrismException + { + // Store filename for later use before parsing + parsedFileName = file.getPath(); + super.load(file); + } + + @Override + public void load(InputStream in) throws PrismException { DocumentBuilderFactory factory; DocumentBuilder builder; - Document doc = null; + parsedSBML = null; // Create XML parser factory = DocumentBuilderFactory.newInstance(); @@ -138,23 +113,22 @@ public class SBML2Prism extends Reactions2Prism implements EntityResolver throw new PrismException("Couldn't create XML parser"); } - // Parse + // Parse XML try { - doc = builder.parse(file); + parsedSBML = builder.parse(in); } catch (IOException e) { - throw new PrismException("Couldn't load file \"" + file.getPath() + "\": " + e.getMessage()); + throw new PrismException("Error reading SBML: " + e.getMessage()); } catch (SAXException e) { throw new PrismException("Invalid XML file:\n" + e.getMessage()); } - - return doc; } - // Function used by parseSBML() above to find the SBML DTD - // (this currently unused since we do not validate the SBML file when reading) - // (and since the DTD specified in the SBML files is not local) - // (if validation is enabled, put the DTD file "sbml.dtd" in PRISM's "dtds" directory) - + /** + * Function used by parseSBML() above to find the SBML DTD + * (this currently unused since we do not validate the SBML file when reading) + * (and since the DTD specified in the SBML files is not local) + * (if validation is enabled, put the DTD file "sbml.dtd" in PRISM's "dtds" directory) + */ public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException { InputSource inputSource = null; @@ -170,6 +144,21 @@ public class SBML2Prism extends Reactions2Prism implements EntityResolver return inputSource; } + @Override + public void translate(PrintStream out) throws PrismException + { + checkSBMLVersion(parsedSBML); + extractModelFromSBML(parsedSBML); + // Generate PRISM code + prismCodeHeader = "// File generated by automatic SBML-to-PRISM conversion\n"; + if (parsedFileName != null) { + prismCodeHeader += "// Original SBML file: " + parsedFileName + "\n\n"; + } + convertToPRISMCode(out); + // Reset filename storage + parsedFileName = null; + } + /** * Check that we can handle whatever level/version this file is. */ @@ -348,4 +337,33 @@ public class SBML2Prism extends Reactions2Prism implements EntityResolver reactionList.add(reaction); } } + + /** + * Calling point for command-line script: + * This is probably the sbml2prism (found in etc/scripts) + * But it can also be called as, e.g.: java -cp classes prism.SBML2Prism myfile.sbml 100 + * (100 denotes (integer) maximum for species population sizes, default is 100) + * (also used to compute amounts from (real-valued) concentrations) + */ + public static void main(String args[]) + { + PrismLog errLog = new PrismPrintStreamLog(System.err); + try { + if (args.length < 1) { + System.err.println("Usage: sbml2prism [max_amount]"); + System.exit(1); + } + SBML2Prism sbml2prism = new SBML2Prism(errLog); + try { + if (args.length > 1) + sbml2prism.setMaxAmount(Integer.parseInt(args[1])); + } catch (NumberFormatException e) { + throw new PrismException("Invalid max amount \"" + args[1] + "\""); + } + sbml2prism.load(new File(args[0])); + sbml2prism.translate(System.out); + } catch (PrismException e) { + errLog.println("Error: " + e.getMessage() + "."); + } + } }