From 154f1e686a6114ab89fc4dcf5abff717c3a9a699 Mon Sep 17 00:00:00 2001 From: Piotr Gawron <piotr.gawron@uni.lu> Date: Tue, 19 Dec 2017 17:01:50 +0100 Subject: [PATCH] export of compartments implemented --- .../java/lcsb/mapviewer/common/MimeType.java | 130 +++++++++--------- .../model/sbml/SbmlCompartmentExporter.java | 74 ++++++++++ .../model/sbml/SbmlElementExporter.java | 24 ++++ .../converter/model/sbml/SbmlExporter.java | 62 +++++++++ .../converter/model/sbml/SbmlParser.java | 32 +++-- .../model/sbml/AllSbmlConverterTests.java | 7 +- .../sbml/GenericSbmlToXmlParserTest.java | 88 ++++++++++++ .../model/sbml/SbmlExporterTest.java | 111 +++++++++++++++ 8 files changed, 453 insertions(+), 75 deletions(-) create mode 100644 converter-sbml/src/main/java/lcsb/mapviewer/converter/model/sbml/SbmlCompartmentExporter.java create mode 100644 converter-sbml/src/main/java/lcsb/mapviewer/converter/model/sbml/SbmlElementExporter.java create mode 100644 converter-sbml/src/main/java/lcsb/mapviewer/converter/model/sbml/SbmlExporter.java create mode 100644 converter-sbml/src/test/java/lcsb/mapviewer/converter/model/sbml/GenericSbmlToXmlParserTest.java create mode 100644 converter-sbml/src/test/java/lcsb/mapviewer/converter/model/sbml/SbmlExporterTest.java diff --git a/commons/src/main/java/lcsb/mapviewer/common/MimeType.java b/commons/src/main/java/lcsb/mapviewer/common/MimeType.java index 54c7c29e1b..ca6960532a 100644 --- a/commons/src/main/java/lcsb/mapviewer/common/MimeType.java +++ b/commons/src/main/java/lcsb/mapviewer/common/MimeType.java @@ -1,86 +1,86 @@ package lcsb.mapviewer.common; /** - * Mime content types of data used in the system. If there is publically - * available class with all posible types then we should use it, but I couldn't - * quickly find it. For the full list see <a - * href="http://www.iana.org/assignments/media-types/media-types.xhtml">IANA + * Mime content types of data used in the system. If there is publicly available + * class with all possible types then we should use it, but I couldn't quickly + * find it. For the full list see + * <a href="http://www.iana.org/assignments/media-types/media-types.xhtml">IANA * MIME media types list</a> * * @author Piotr Gawron * */ public enum MimeType { - /** - * SBML type of file. - */ - SBML("application/sbml+xml"), // + /** + * SBML type of file. + */ + SBML("application/sbml+xml"), // - /** - * Standard text file. - */ - TEXT("text/plain"), // + /** + * Standard text file. + */ + TEXT("text/plain"), // - /** - * Standard XML file. - */ - XML("application/xml"), + /** + * Standard XML file. + */ + XML("application/xml"), - /** - * <a href="http://www.w3.org/TR/SVG11/mimereg.html">SVG</a> file type. - */ - SVG("image/svg+xml"), + /** + * <a href="http://www.w3.org/TR/SVG11/mimereg.html">SVG</a> file type. + */ + SVG("image/svg+xml"), - /** - * JPG image file type. - */ - JPG("image/jpeg"), + /** + * JPG image file type. + */ + JPG("image/jpeg"), - /** - * PNG image file type. - */ - PNG("image/png"), + /** + * PNG image file type. + */ + PNG("image/png"), - /** - * PDF files (see <a href="http://www.rfc-editor.org/rfc/rfc3778.txt"> RFC - * 3778, The application/pdf Media Type</a>). - */ - PDF("application/pdf"), // + /** + * PDF files (see <a href="http://www.rfc-editor.org/rfc/rfc3778.txt"> RFC 3778, + * The application/pdf Media Type</a>). + */ + PDF("application/pdf"), // - /** - * CSS files. - */ - CSS("text/css"), // + /** + * CSS files. + */ + CSS("text/css"), // - /** - * Javascript files. - */ - JS("text/javascript"), - - /** - * Zip files. - */ - ZIP("application/zip"); // + /** + * JavaScript files. + */ + JS("text/javascript"), - /** - * String representation of the MIME content. - */ - private String textRepresentation; + /** + * Zip files. + */ + ZIP("application/zip"); // - /** - * Default constructor with string definition. - * - * @param textRepresentation - * text representation for MIME type - */ - MimeType(String textRepresentation) { - this.textRepresentation = textRepresentation; - } + /** + * String representation of the MIME content. + */ + private String textRepresentation; - /** - * @return the textRepresentation - */ - public String getTextRepresentation() { - return textRepresentation; - } + /** + * Default constructor with string definition. + * + * @param textRepresentation + * text representation for MIME type + */ + MimeType(String textRepresentation) { + this.textRepresentation = textRepresentation; + } + + /** + * @return the textRepresentation + */ + public String getTextRepresentation() { + return textRepresentation; + } } diff --git a/converter-sbml/src/main/java/lcsb/mapviewer/converter/model/sbml/SbmlCompartmentExporter.java b/converter-sbml/src/main/java/lcsb/mapviewer/converter/model/sbml/SbmlCompartmentExporter.java new file mode 100644 index 0000000000..07844dd698 --- /dev/null +++ b/converter-sbml/src/main/java/lcsb/mapviewer/converter/model/sbml/SbmlCompartmentExporter.java @@ -0,0 +1,74 @@ +package lcsb.mapviewer.converter.model.sbml; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.xml.stream.XMLStreamException; + +import org.apache.log4j.Logger; +import org.sbml.jsbml.Model; +import org.sbml.jsbml.ext.layout.BoundingBox; +import org.sbml.jsbml.ext.layout.CompartmentGlyph; +import org.sbml.jsbml.ext.layout.Dimensions; +import org.sbml.jsbml.ext.layout.Layout; +import org.sbml.jsbml.ext.layout.Point; + +import lcsb.mapviewer.common.exception.InvalidStateException; +import lcsb.mapviewer.model.map.InconsistentModelException; +import lcsb.mapviewer.model.map.compartment.Compartment; + +public class SbmlCompartmentExporter extends SbmlElementExporter<Compartment> { + Logger logger = Logger.getLogger(SbmlCompartmentExporter.class); + + Map<String, org.sbml.jsbml.Compartment> sbmlCompartmentByElementId = new HashMap<>(); + Map<String, org.sbml.jsbml.Compartment> sbmlCompartmentByName = new HashMap<>(); + Map<String, CompartmentGlyph> sbmlCompartmentGlyphByElementId = new HashMap<>(); + + public SbmlCompartmentExporter(Layout layout, lcsb.mapviewer.model.map.model.Model minervaModel) { + super(layout, minervaModel); + } + + @Override + public void exportElements(Model model) throws InconsistentModelException { + List<Compartment> compartments = minervaModel.getCompartments(); + for (Compartment compartment : compartments) { + if (sbmlCompartmentByName.get(compartment.getName()) == null) { + org.sbml.jsbml.Compartment sbmlCompartment = model.createCompartment("comp_" + compartment.getName()); + try { + sbmlCompartment.setNotes(compartment.getNotes()); + } catch (XMLStreamException e) { + throw new InvalidStateException(e); + } + sbmlCompartmentByName.put(compartment.getName(), sbmlCompartment); + } + org.sbml.jsbml.Compartment sbmlCompartment = sbmlCompartmentByName.get(compartment.getName()); + if (sbmlCompartmentByElementId.get(compartment.getElementId()) != null) { + throw new InconsistentModelException("More than one compartment with id: " + compartment.getElementId()); + } + sbmlCompartmentByElementId.put(compartment.getElementId(), sbmlCompartment); + } + for (Compartment compartment : compartments) { + CompartmentGlyph compartmentGlyph = createCompartmentGlyph(compartment); + sbmlCompartmentGlyphByElementId.put(compartment.getElementId(), compartmentGlyph); + } + + } + + private CompartmentGlyph createCompartmentGlyph(Compartment compartment) { + String sbmlCompartmentId = sbmlCompartmentByElementId.get(compartment.getElementId()).getId(); + String glyphId = compartment.getElementId(); + CompartmentGlyph compartmentGlyph = layout.createCompartmentGlyph(glyphId, sbmlCompartmentId); + BoundingBox boundingBox = new BoundingBox(); + + boundingBox.setPosition(new Point(compartment.getX(), compartment.getY())); + Dimensions dimensions = new Dimensions(); + dimensions.setWidth(compartment.getWidth()); + dimensions.setHeight(compartment.getHeight()); + boundingBox.setDimensions(dimensions); + + compartmentGlyph.setBoundingBox(boundingBox); + return compartmentGlyph; + } + +} diff --git a/converter-sbml/src/main/java/lcsb/mapviewer/converter/model/sbml/SbmlElementExporter.java b/converter-sbml/src/main/java/lcsb/mapviewer/converter/model/sbml/SbmlElementExporter.java new file mode 100644 index 0000000000..bac1ac7b6f --- /dev/null +++ b/converter-sbml/src/main/java/lcsb/mapviewer/converter/model/sbml/SbmlElementExporter.java @@ -0,0 +1,24 @@ +package lcsb.mapviewer.converter.model.sbml; + +import org.apache.log4j.Logger; +import org.sbml.jsbml.Model; +import org.sbml.jsbml.ext.layout.Layout; + +import lcsb.mapviewer.model.map.InconsistentModelException; +import lcsb.mapviewer.model.map.species.Element; + +public abstract class SbmlElementExporter<T extends Element> { + Logger logger = Logger.getLogger(SbmlElementExporter.class); + + Layout layout; + + lcsb.mapviewer.model.map.model.Model minervaModel; + + public SbmlElementExporter(Layout sbmlLayout, lcsb.mapviewer.model.map.model.Model minervaModel) { + this.layout = sbmlLayout; + this.minervaModel = minervaModel; + } + + public abstract void exportElements(Model model) throws InconsistentModelException; + +} diff --git a/converter-sbml/src/main/java/lcsb/mapviewer/converter/model/sbml/SbmlExporter.java b/converter-sbml/src/main/java/lcsb/mapviewer/converter/model/sbml/SbmlExporter.java new file mode 100644 index 0000000000..f92b8f5278 --- /dev/null +++ b/converter-sbml/src/main/java/lcsb/mapviewer/converter/model/sbml/SbmlExporter.java @@ -0,0 +1,62 @@ +package lcsb.mapviewer.converter.model.sbml; + +import javax.xml.stream.XMLStreamException; + +import org.apache.commons.io.output.ByteArrayOutputStream; +import org.sbml.jsbml.Compartment; +import org.sbml.jsbml.Creator; +import org.sbml.jsbml.History; +import org.sbml.jsbml.Model; +import org.sbml.jsbml.Reaction; +import org.sbml.jsbml.SBMLDocument; +import org.sbml.jsbml.SBMLException; +import org.sbml.jsbml.SBMLWriter; +import org.sbml.jsbml.Species; +import org.sbml.jsbml.SpeciesReference; +import org.sbml.jsbml.ext.layout.Dimensions; +import org.sbml.jsbml.ext.layout.Layout; +import org.sbml.jsbml.ext.layout.LayoutModelPlugin; + +import lcsb.mapviewer.model.map.InconsistentModelException; + +public class SbmlExporter { + public String toXml(lcsb.mapviewer.model.map.model.Model model) throws SBMLException, XMLStreamException, InconsistentModelException { + SBMLDocument doc = new SBMLDocument(3, 1); + Model result = doc.createModel(model.getName()); + Layout layout = createSbmlLayout(model, result); + + SbmlCompartmentExporter compartmentExporter = new SbmlCompartmentExporter(layout, model); + compartmentExporter.exportElements(result); + +// // Create some sample content in the SBML model. +// Species specOne = result.createSpecies("test_spec1", compartment); +// Species specTwo = result.createSpecies("test_spec2", compartment); +// Reaction sbReaction = result.createReaction("reaction_id"); +// +// // Add a substrate (SBO:0000015) and product (SBO:0000011) to the reaction. +// SpeciesReference subs = sbReaction.createReactant(specOne); +// subs.setSBOTerm(15); +// SpeciesReference prod = sbReaction.createProduct(specTwo); +// prod.setSBOTerm(11); + + // For brevity, we omit error checking, BUT YOU SHOULD CALL + // doc.checkConsistency() and check the error log. + + // Write the SBML document to a file. + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + SBMLWriter.write(doc, stream, "minerva", "1.0"); + return stream.toString(); + } + + private Layout createSbmlLayout(lcsb.mapviewer.model.map.model.Model model, Model result) { + LayoutModelPlugin layoutPlugin = new LayoutModelPlugin(result); + Layout layout = new Layout(); + Dimensions dimensions = new Dimensions(); + dimensions.setHeight(model.getHeight()); + dimensions.setWidth(model.getWidth()); + layout.setDimensions(dimensions); + layoutPlugin.add(layout); + result.addExtension("layout", layoutPlugin); + return layout; + } +} diff --git a/converter-sbml/src/main/java/lcsb/mapviewer/converter/model/sbml/SbmlParser.java b/converter-sbml/src/main/java/lcsb/mapviewer/converter/model/sbml/SbmlParser.java index c139cb3546..df96d4d304 100644 --- a/converter-sbml/src/main/java/lcsb/mapviewer/converter/model/sbml/SbmlParser.java +++ b/converter-sbml/src/main/java/lcsb/mapviewer/converter/model/sbml/SbmlParser.java @@ -2,7 +2,9 @@ package lcsb.mapviewer.converter.model.sbml; import java.awt.Point; import java.awt.geom.Point2D; +import java.io.ByteArrayInputStream; import java.io.File; +import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.util.HashSet; @@ -13,6 +15,7 @@ import javax.xml.stream.XMLStreamException; import org.apache.commons.io.FilenameUtils; import org.apache.log4j.Logger; import org.sbml.jsbml.SBMLDocument; +import org.sbml.jsbml.SBMLException; import org.sbml.jsbml.SBMLReader; import org.sbml.jsbml.ext.SBasePlugin; import org.sbml.jsbml.ext.layout.Layout; @@ -326,33 +329,44 @@ public class SbmlParser implements IConverter { @Override public InputStream exportModelToInputStream(Model model) throws ConverterException, InconsistentModelException { - // TODO Auto-generated method stub - return null; + String exportedString = toXml(model); + InputStream inputStream = new ByteArrayInputStream(exportedString.getBytes()); + return inputStream; + } + + private String toXml(Model model) throws ConverterException { + try { + return new SbmlExporter().toXml(model); + } catch (SBMLException | XMLStreamException | InconsistentModelException e) { + throw new ConverterException(e); + } } @Override public File exportModelToFile(Model model, String filePath) throws ConverterException, InconsistentModelException, IOException { - // TODO Auto-generated method stub + File file = new File(filePath); + String exportedString = toXml(model); + FileWriter fileWriter = new FileWriter(file); + fileWriter.write(exportedString); + fileWriter.flush(); + fileWriter.close(); return null; } @Override public String getCommonName() { - // TODO Auto-generated method stub - return null; + return "SBML"; } @Override public MimeType getMimeType() { - // TODO Auto-generated method stub - return null; + return MimeType.SBML; } @Override public String getFileExtension() { - // TODO Auto-generated method stub - return null; + return "xml"; } } diff --git a/converter-sbml/src/test/java/lcsb/mapviewer/converter/model/sbml/AllSbmlConverterTests.java b/converter-sbml/src/test/java/lcsb/mapviewer/converter/model/sbml/AllSbmlConverterTests.java index 5841758afb..337bc2c992 100644 --- a/converter-sbml/src/test/java/lcsb/mapviewer/converter/model/sbml/AllSbmlConverterTests.java +++ b/converter-sbml/src/test/java/lcsb/mapviewer/converter/model/sbml/AllSbmlConverterTests.java @@ -5,7 +5,12 @@ import org.junit.runners.Suite; import org.junit.runners.Suite.SuiteClasses; @RunWith(Suite.class) -@SuiteClasses({ GenericSbmlParserTest.class, SbmlPareserForInvalidReactionTest.class, SbmlParserTest.class }) +@SuiteClasses({ GenericSbmlParserTest.class, // + GenericSbmlToXmlParserTest.class, // + SbmlExporterTest.class, // + SbmlPareserForInvalidReactionTest.class, // + SbmlParserTest.class,// +}) public class AllSbmlConverterTests { } diff --git a/converter-sbml/src/test/java/lcsb/mapviewer/converter/model/sbml/GenericSbmlToXmlParserTest.java b/converter-sbml/src/test/java/lcsb/mapviewer/converter/model/sbml/GenericSbmlToXmlParserTest.java new file mode 100644 index 0000000000..65e7242d23 --- /dev/null +++ b/converter-sbml/src/test/java/lcsb/mapviewer/converter/model/sbml/GenericSbmlToXmlParserTest.java @@ -0,0 +1,88 @@ +package lcsb.mapviewer.converter.model.sbml; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import java.awt.Desktop; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintWriter; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collection; + +import org.apache.commons.io.FileUtils; +import org.apache.log4j.Logger; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +import lcsb.mapviewer.converter.ConverterParams; +import lcsb.mapviewer.converter.IConverter; +import lcsb.mapviewer.converter.graphics.AbstractImageGenerator; +import lcsb.mapviewer.converter.graphics.NormalImageGenerator; +import lcsb.mapviewer.converter.graphics.PngImageGenerator; +import lcsb.mapviewer.converter.model.celldesigner.CellDesignerXmlParser; +import lcsb.mapviewer.model.map.compartment.Compartment; +import lcsb.mapviewer.model.map.model.Model; +import lcsb.mapviewer.model.map.model.ModelComparator; +import lcsb.mapviewer.model.map.species.Element; + +@RunWith(Parameterized.class) +public class GenericSbmlToXmlParserTest { + + static Logger logger = Logger.getLogger(GenericSbmlToXmlParserTest.class.getName()); + + private Path filePath; + + public GenericSbmlToXmlParserTest(Path filePath) { + this.filePath = filePath; + } + + @Parameters(name = "{index} : {0}") + public static Collection<Object[]> data() throws IOException { + Collection<Object[]> data = new ArrayList<Object[]>(); + Files.walk(Paths.get("testFiles/layoutExample")).forEach(fPath -> { + if (Files.isRegularFile(fPath) && fPath.toString().endsWith(".xml")) { + data.add(new Object[] { fPath }); + } + }); + return data; + } + + @Test + public void toXmlModelTest() throws Exception { + try { + String dir = Files.createTempDirectory("sbgn-temp-images-dir").toFile().getAbsolutePath(); + + IConverter converter = new SbmlParser(); + + Model model = converter.createModel(new ConverterParams().filename(filePath.toString())); + model.setName(null); + + String pathWithouExtension = dir + "/" + + filePath.getFileName().toString().substring(0, filePath.getFileName().toString().indexOf(".xml")); + String xmlFilePath = pathWithouExtension.concat(".xml"); + converter.exportModelToFile(model, xmlFilePath); + + Model model2 = converter.createModel(new ConverterParams().filename(xmlFilePath)); + model.setName(null); + + assertNotNull(model2); + ModelComparator comparator = new ModelComparator(1.0); + assertEquals(0, comparator.compare(model, model2)); + FileUtils.deleteDirectory(new File(dir)); + + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + throw e; + } + } + +} diff --git a/converter-sbml/src/test/java/lcsb/mapviewer/converter/model/sbml/SbmlExporterTest.java b/converter-sbml/src/test/java/lcsb/mapviewer/converter/model/sbml/SbmlExporterTest.java new file mode 100644 index 0000000000..8128f83717 --- /dev/null +++ b/converter-sbml/src/test/java/lcsb/mapviewer/converter/model/sbml/SbmlExporterTest.java @@ -0,0 +1,111 @@ +package lcsb.mapviewer.converter.model.sbml; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.io.ByteArrayInputStream; + +import org.apache.log4j.Logger; +import org.junit.Test; + +import lcsb.mapviewer.converter.ConverterParams; +import lcsb.mapviewer.model.map.compartment.Compartment; +import lcsb.mapviewer.model.map.model.Model; +import lcsb.mapviewer.model.map.reaction.Reaction; +import lcsb.mapviewer.model.map.reaction.ReactionNode; +import lcsb.mapviewer.model.map.species.Species; + +public class SbmlExporterTest { + Logger logger = Logger.getLogger(SbmlExporterTest.class); + SbmlParser parser = new SbmlParser(); + SbmlExporter exporter = new SbmlExporter(); + + @Test + public void testParseCompartment() throws Exception { + Model model = getModelAfterSerializing("testFiles/layoutExample/CompartmentGlyph_Example_level2_level3.xml"); + assertNotNull(model); + assertEquals(1, model.getCompartments().size()); + Compartment compartment = model.getElementByElementId("CompartmentGlyph_1"); + assertNotNull(compartment); + assertNotNull(compartment.getX()); + assertNotNull(compartment.getY()); + assertTrue(compartment.getWidth() > 0); + assertTrue(compartment.getHeight() > 0); + assertNotNull(model.getHeight()); + assertNotNull(model.getWidth()); + assertTrue(model.getWidth() >= compartment.getX() + compartment.getWidth()); + assertTrue(model.getHeight() >= compartment.getY() + compartment.getHeight()); + assertFalse(compartment.getClass().equals(Compartment.class)); + } + + private Model getModelAfterSerializing(String filename) throws Exception { + Model originalModel = parser.createModel(new ConverterParams().filename(filename)); + String xml = exporter.toXml(originalModel); + ByteArrayInputStream stream = new ByteArrayInputStream(xml.getBytes("UTF-8")); + return parser.createModel(new ConverterParams().inputStream(stream)); + } + + @Test + public void testParseSpecies() throws Exception { + Model model = getModelAfterSerializing("testFiles/layoutExample/SpeciesGlyph_Example_level2_level3.xml"); + assertNotNull(model); + assertEquals(1, model.getElements().size()); + Species glucoseSpecies = model.getElementByElementId("SpeciesGlyph_Glucose"); + assertNotNull(glucoseSpecies.getX()); + assertNotNull(glucoseSpecies.getY()); + assertTrue(glucoseSpecies.getWidth() > 0); + assertTrue(glucoseSpecies.getHeight() > 0); + assertNotNull(model.getHeight()); + assertNotNull(model.getWidth()); + assertTrue(model.getWidth() >= glucoseSpecies.getX() + glucoseSpecies.getWidth()); + assertTrue(model.getHeight() >= glucoseSpecies.getY() + glucoseSpecies.getHeight()); + assertFalse(glucoseSpecies.getClass().equals(Species.class)); + } + + @Test + public void testParseSpeciesInCompartments() throws Exception { + Model model = getModelAfterSerializing("testFiles/layoutExample/SpeciesGlyph_Example.xml"); + assertNotNull(model); + assertEquals(2, model.getElements().size()); + Compartment compartment = model.getElementByElementId("Yeast"); + assertNotNull(compartment.getX()); + assertNotNull(compartment.getY()); + assertTrue(compartment.getWidth() > 0); + assertTrue(compartment.getHeight() > 0); + assertNotNull(model.getHeight()); + assertNotNull(model.getWidth()); + assertTrue(model.getWidth() >= compartment.getX() + compartment.getWidth()); + assertTrue(model.getHeight() >= compartment.getY() + compartment.getHeight()); + assertFalse(compartment.getClass().equals(Compartment.class)); + assertTrue(compartment.getElements().size() > 0); + } + + @Test + public void testParseReaction() throws Exception { + Model model = getModelAfterSerializing("testFiles/layoutExample/Complete_Example.xml"); + assertNotNull(model); + assertEquals(1, model.getReactions().size()); + Reaction reaction = model.getReactions().iterator().next(); + for (ReactionNode node : reaction.getReactionNodes()) { + assertNotNull(node.getLine()); + assertTrue(node.getLine().length() > 0); + } + assertEquals(2, reaction.getOperators().size()); + } + + @Test + public void testReactionWithoutLayout() throws Exception { + Model model = getModelAfterSerializing("testFiles/layoutExample/Complete_Example_level2.xml"); + assertNotNull(model); + assertEquals(1, model.getReactions().size()); + Reaction reaction = model.getReactions().iterator().next(); + for (ReactionNode node : reaction.getReactionNodes()) { + assertNotNull(node.getLine()); + assertTrue(node.getLine().length() > 0); + } + assertEquals(2, reaction.getOperators().size()); + } + +} -- GitLab