From a00c1ad102dcc86e14c6f5db6dd3bd3c2b5f2c71 Mon Sep 17 00:00:00 2001
From: Piotr Gawron <piotr.gawron@uni.lu>
Date: Thu, 23 Nov 2017 17:54:18 +0100
Subject: [PATCH] parsing of relation between compartment and species added

---
 .../model/sbml/SbmlCompartmentParser.java     | 10 ++--
 .../model/sbml/SbmlElementParser.java         | 16 +++---
 .../converter/model/sbml/SbmlParser.java      | 42 ++++++++++++++--
 .../model/sbml/SbmlSpeciesParser.java         | 50 +++++++++++++++++--
 .../model/sbml/GenericSbmlParserTest.java     |  3 +-
 .../converter/model/sbml/SbmlParserTest.java  | 48 +++++++++++++++---
 6 files changed, 143 insertions(+), 26 deletions(-)

diff --git a/converter-sbml/src/main/java/lcsb/mapviewer/converter/model/sbml/SbmlCompartmentParser.java b/converter-sbml/src/main/java/lcsb/mapviewer/converter/model/sbml/SbmlCompartmentParser.java
index 9e119bfbaf..2e19442c08 100644
--- a/converter-sbml/src/main/java/lcsb/mapviewer/converter/model/sbml/SbmlCompartmentParser.java
+++ b/converter-sbml/src/main/java/lcsb/mapviewer/converter/model/sbml/SbmlCompartmentParser.java
@@ -20,15 +20,19 @@ import lcsb.mapviewer.model.map.compartment.SquareCompartment;
 public class SbmlCompartmentParser extends SbmlElementParser<org.sbml.jsbml.Compartment> {
   Logger logger = Logger.getLogger(SbmlCompartmentParser.class);
 
-  public SbmlCompartmentParser(Layout layout) {
-    super(layout);
+  public SbmlCompartmentParser(Layout layout, lcsb.mapviewer.model.map.model.Model minervaModel) {
+    super(layout, minervaModel);
   }
 
   @Override
-  protected Compartment parse(org.sbml.jsbml.Compartment compartment) throws InvalidInputDataExecption {
+  protected Compartment parse(org.sbml.jsbml.Compartment compartment, Model sbmlModel)
+      throws InvalidInputDataExecption {
     Compartment result = new SquareCompartment(compartment.getId());
     result.setMiriamData(parseAnnotation(compartment.getAnnotation()));
     result.setName(compartment.getName());
+    if (result.getName() == null || result.getName().isEmpty()) {
+      result.setName(result.getElementId());
+    }
     try {
       result.setNotes(compartment.getNotesString());
     } catch (XMLStreamException e) {
diff --git a/converter-sbml/src/main/java/lcsb/mapviewer/converter/model/sbml/SbmlElementParser.java b/converter-sbml/src/main/java/lcsb/mapviewer/converter/model/sbml/SbmlElementParser.java
index 5b7c7edcb9..8530ae1a6b 100644
--- a/converter-sbml/src/main/java/lcsb/mapviewer/converter/model/sbml/SbmlElementParser.java
+++ b/converter-sbml/src/main/java/lcsb/mapviewer/converter/model/sbml/SbmlElementParser.java
@@ -25,17 +25,20 @@ public abstract class SbmlElementParser<T extends org.sbml.jsbml.Symbol> {
 
   Layout layout;
 
-  public SbmlElementParser(Layout layout) {
-    this.layout = layout;
+  lcsb.mapviewer.model.map.model.Model minervaModel;
+
+  public SbmlElementParser(Layout sbmlLayout, lcsb.mapviewer.model.map.model.Model minervaModel) {
+    this.layout = sbmlLayout;
+    this.minervaModel = minervaModel;
   }
 
   public List<Element> parseList(Model sbmlModel) throws InvalidInputDataExecption {
     List<Element> result = new ArrayList<>();
     for (T sbmlElement : getSbmlElementList(sbmlModel)) {
-      result.add(parse(sbmlElement));
+      result.add(parse(sbmlElement, sbmlModel));
     }
     if (layout != null) {
-      return mergeLayout(result, layout);
+      return mergeLayout(result, layout, sbmlModel);
     } else {
       return result;
     }
@@ -43,7 +46,8 @@ public abstract class SbmlElementParser<T extends org.sbml.jsbml.Symbol> {
 
   protected abstract ListOf<T> getSbmlElementList(Model sbmlModel);
 
-  private List<Element> mergeLayout(List<Element> elements, Layout sbmlLayout) throws InvalidInputDataExecption {
+  protected List<Element> mergeLayout(List<Element> elements, Layout sbmlLayout, Model sbmlModel)
+      throws InvalidInputDataExecption {
     Set<Element> used = new HashSet<>();
     Map<String, Element> elementById = new HashMap<>();
     for (Element species : elements) {
@@ -81,7 +85,7 @@ public abstract class SbmlElementParser<T extends org.sbml.jsbml.Symbol> {
 
   protected abstract List<Pair<String, AbstractReferenceGlyph>> getGlyphs(Layout sbmlLayout);
 
-  protected abstract Element parse(T species) throws InvalidInputDataExecption;
+  protected abstract Element parse(T species, Model sbmlModel) throws InvalidInputDataExecption;
 
   protected Set<MiriamData> parseAnnotation(Annotation annotation) {
     if (annotation.getCVTermCount() > 0) {
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 7e32722234..a14a15c80c 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
@@ -1,5 +1,6 @@
 package lcsb.mapviewer.converter.model.sbml;
 
+import java.awt.Point;
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
@@ -48,8 +49,8 @@ public class SbmlParser implements IConverter {
 
       Layout layout = getSbmlLayout(sbmlModel);
 
-      SbmlCompartmentParser compartmentParser = new SbmlCompartmentParser(layout);
-      SbmlSpeciesParser speciesParser = new SbmlSpeciesParser(layout);
+      SbmlCompartmentParser compartmentParser = new SbmlCompartmentParser(layout, model);
+      SbmlSpeciesParser speciesParser = new SbmlSpeciesParser(layout, model);
 
       Set<MiriamData> annotations = compartmentParser.parseAnnotation(sbmlModel.getAnnotation());
       if (annotations.size() > 0) {
@@ -108,6 +109,17 @@ public class SbmlParser implements IConverter {
   }
 
   private void createLayout(Model model, Layout layout) {
+
+    for (Element element : model.getElements()) {
+      if (element.getWidth() == 0 || element.getHeight() == 0) {
+        element.setWidth(100);
+        element.setHeight(20);
+        Point coord = nextCoordinates();
+        element.setX(coord.x * 120);
+        element.setX(coord.y * 30);
+      }
+    }
+
     double width;
     double height;
     if (layout != null) {
@@ -119,9 +131,6 @@ public class SbmlParser implements IConverter {
       double minX = Double.MAX_VALUE;
       double minY = Double.MAX_VALUE;
       for (Element element : model.getElements()) {
-        if (element.getWidth() == 0.0) {
-          throw new NotImplementedException("Element without layout not implemented");
-        }
         width = Math.max(width, element.getX() + element.getWidth());
         height = Math.max(height, element.getY() + element.getHeight());
         minX = Math.min(minX, element.getX());
@@ -139,6 +148,29 @@ public class SbmlParser implements IConverter {
     model.setHeight(height);
   }
 
+  Point coordinates = null;
+  int radius = 0;
+
+  protected Point nextCoordinates() {
+    if (coordinates == null) {
+      coordinates = new Point(0, 0);
+    } else {
+      if (coordinates.x == coordinates.y) {
+        radius++;
+        coordinates = new Point(radius, 0);
+      } else if (coordinates.x == radius) {
+        if (coordinates.y == radius - 1) {
+          coordinates = new Point(0, radius);
+        } else {
+          coordinates = new Point(coordinates.x, coordinates.y + 1);
+        }
+      } else {
+        coordinates = new Point(coordinates.x + 1, coordinates.y);
+      }
+    }
+    return coordinates;
+  }
+
   private Layout getSbmlLayout(org.sbml.jsbml.Model sbmlModel) {
     Layout layout = null;
     if (sbmlModel.getExtensionCount() > 0) {
diff --git a/converter-sbml/src/main/java/lcsb/mapviewer/converter/model/sbml/SbmlSpeciesParser.java b/converter-sbml/src/main/java/lcsb/mapviewer/converter/model/sbml/SbmlSpeciesParser.java
index e5d5547c4d..b2cf5c6150 100644
--- a/converter-sbml/src/main/java/lcsb/mapviewer/converter/model/sbml/SbmlSpeciesParser.java
+++ b/converter-sbml/src/main/java/lcsb/mapviewer/converter/model/sbml/SbmlSpeciesParser.java
@@ -10,23 +10,27 @@ import org.apache.log4j.Logger;
 import org.sbml.jsbml.ListOf;
 import org.sbml.jsbml.Model;
 import org.sbml.jsbml.ext.layout.AbstractReferenceGlyph;
+import org.sbml.jsbml.ext.layout.CompartmentGlyph;
 import org.sbml.jsbml.ext.layout.Layout;
 import org.sbml.jsbml.ext.layout.SpeciesGlyph;
 
 import lcsb.mapviewer.common.Pair;
 import lcsb.mapviewer.common.exception.InvalidStateException;
 import lcsb.mapviewer.converter.InvalidInputDataExecption;
+import lcsb.mapviewer.model.map.compartment.Compartment;
+import lcsb.mapviewer.model.map.species.Element;
 import lcsb.mapviewer.model.map.species.Species;
 import lcsb.mapviewer.model.map.species.Unknown;
 
 public class SbmlSpeciesParser extends SbmlElementParser<org.sbml.jsbml.Species> {
   Logger logger = Logger.getLogger(SbmlSpeciesParser.class);
 
-  public SbmlSpeciesParser(Layout layout) {
-    super(layout);
+  public SbmlSpeciesParser(Layout layout, lcsb.mapviewer.model.map.model.Model minervaModel) {
+    super(layout, minervaModel);
   }
 
-  protected Species parse(org.sbml.jsbml.Species species) throws InvalidInputDataExecption {
+  @Override
+  protected Species parse(org.sbml.jsbml.Species species, Model sbmlModel) throws InvalidInputDataExecption {
     String type = species.getSpeciesType();
     Class<? extends Species> clazz = null;
     if (type == null || type.isEmpty()) {
@@ -40,6 +44,9 @@ public class SbmlSpeciesParser extends SbmlElementParser<org.sbml.jsbml.Species>
       result = clazz.getConstructor(String.class).newInstance(species.getId());
       result.setMiriamData(parseAnnotation(species.getAnnotation()));
       result.setName(species.getName());
+      if (result.getName() == null || result.getName().isEmpty()) {
+        result.setName(result.getElementId());
+      }
       try {
         result.setNotes(species.getNotesString());
       } catch (XMLStreamException e) {
@@ -52,6 +59,43 @@ public class SbmlSpeciesParser extends SbmlElementParser<org.sbml.jsbml.Species>
     }
   }
 
+  @Override
+  protected List<Element> mergeLayout(List<Element> elements, Layout sbmlLayout, Model sbmlModel) throws InvalidInputDataExecption {
+    List<Element> result = super.mergeLayout(elements, sbmlLayout, sbmlModel);
+
+    for (Element element : result) {
+      String compartmentId = null;
+      if (sbmlLayout.getSpeciesGlyph(element.getElementId())!=null) {
+        compartmentId = ((org.sbml.jsbml.Species)sbmlLayout.getSpeciesGlyph(element.getElementId()).getSpeciesInstance()).getCompartment();
+      } else {
+        compartmentId = sbmlModel.getSpecies(element.getElementId()).getCompartment();
+      }
+      assignCompartment(element, compartmentId);
+    }
+    return result;
+  }
+
+  private void assignCompartment(Element element, String compartmentId) {
+    Compartment compartment = minervaModel.getElementByElementId(compartmentId);
+    if (compartment == null) {
+      List<Compartment> compartments = new ArrayList<>();
+      for (CompartmentGlyph glyph : layout.getListOfCompartmentGlyphs()) {
+        if (glyph.getCompartment().equals(compartmentId)) {
+          compartments.add(minervaModel.getElementByElementId(glyph.getId()));
+        }
+      }
+      for (Compartment compartment2 : compartments) {
+        if (compartment2.contains(element)) {
+          compartment = compartment2;
+        }
+      }
+    }
+    if (compartment != null) {
+      compartment.addElement(element);
+    }
+
+  }
+
   @Override
   protected ListOf<org.sbml.jsbml.Species> getSbmlElementList(Model sbmlModel) {
     return sbmlModel.getListOfSpecies();
diff --git a/converter-sbml/src/test/java/lcsb/mapviewer/converter/model/sbml/GenericSbmlParserTest.java b/converter-sbml/src/test/java/lcsb/mapviewer/converter/model/sbml/GenericSbmlParserTest.java
index d28517873a..252a952df8 100644
--- a/converter-sbml/src/test/java/lcsb/mapviewer/converter/model/sbml/GenericSbmlParserTest.java
+++ b/converter-sbml/src/test/java/lcsb/mapviewer/converter/model/sbml/GenericSbmlParserTest.java
@@ -27,8 +27,6 @@ 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.compartment.SquareCompartment;
 import lcsb.mapviewer.model.map.model.Model;
 import lcsb.mapviewer.model.map.model.ModelComparator;
 
@@ -63,6 +61,7 @@ public class GenericSbmlParserTest {
 
       Model model = converter.createModel(new ConverterParams().filename(filePath.toString()));
       model.setName(null);
+      logger.debug(model.getElements().size());
 
       // Create and display image of parsed map
       AbstractImageGenerator.Params params = new AbstractImageGenerator.Params().height(model.getHeight())
diff --git a/converter-sbml/src/test/java/lcsb/mapviewer/converter/model/sbml/SbmlParserTest.java b/converter-sbml/src/test/java/lcsb/mapviewer/converter/model/sbml/SbmlParserTest.java
index 72a079086f..adef0ecf5b 100644
--- a/converter-sbml/src/test/java/lcsb/mapviewer/converter/model/sbml/SbmlParserTest.java
+++ b/converter-sbml/src/test/java/lcsb/mapviewer/converter/model/sbml/SbmlParserTest.java
@@ -5,6 +5,7 @@ import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
+import java.awt.Point;
 import java.io.FileNotFoundException;
 
 import org.apache.log4j.Logger;
@@ -29,8 +30,8 @@ public class SbmlParserTest {
     Compartment compartment = model.getElementByElementId("CompartmentGlyph_1");
     assertNotNull(compartment.getX());
     assertNotNull(compartment.getY());
-    assertTrue(compartment.getWidth()>0);
-    assertTrue(compartment.getHeight()>0);
+    assertTrue(compartment.getWidth() > 0);
+    assertTrue(compartment.getHeight() > 0);
     assertNotNull(model.getHeight());
     assertNotNull(model.getWidth());
     assertTrue(model.getWidth() >= compartment.getX() + compartment.getWidth());
@@ -40,15 +41,15 @@ public class SbmlParserTest {
 
   @Test
   public void testParseSpecies() throws FileNotFoundException, InvalidInputDataExecption {
-    Model model = parser.createModel(
-        new ConverterParams().filename("testFiles/layoutExample/SpeciesGlyph_Example_level2_level3.xml"));
+    Model model = parser
+        .createModel(new ConverterParams().filename("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);
+    assertTrue(glucoseSpecies.getWidth() > 0);
+    assertTrue(glucoseSpecies.getHeight() > 0);
     assertNotNull(model.getHeight());
     assertNotNull(model.getWidth());
     assertTrue(model.getWidth() >= glucoseSpecies.getX() + glucoseSpecies.getWidth());
@@ -56,5 +57,38 @@ public class SbmlParserTest {
     assertFalse(glucoseSpecies.getClass().equals(Species.class));
   }
 
-  
+  @Test
+  public void testParseSpeciesInCompartments() throws FileNotFoundException, InvalidInputDataExecption {
+    Model model = parser
+        .createModel(new ConverterParams().filename("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 testNextCoordinates() throws FileNotFoundException, InvalidInputDataExecption {
+    parser = new SbmlParser();
+    assertEquals(new Point(0, 0), parser.nextCoordinates());
+    assertEquals(new Point(1, 0), parser.nextCoordinates());
+    assertEquals(new Point(0, 1), parser.nextCoordinates());
+    assertEquals(new Point(1, 1), parser.nextCoordinates());
+    assertEquals(new Point(2, 0), parser.nextCoordinates());
+    assertEquals(new Point(2, 1), parser.nextCoordinates());
+    assertEquals(new Point(0, 2), parser.nextCoordinates());
+    assertEquals(new Point(1, 2), parser.nextCoordinates());
+    assertEquals(new Point(2, 2), parser.nextCoordinates());
+    assertEquals(new Point(3, 0), parser.nextCoordinates());
+    
+  }
 }
-- 
GitLab