From 82eee8811eb3d0b6ae137d0d7c7846bbb3eda669 Mon Sep 17 00:00:00 2001
From: Piotr Gawron <piotr.gawron@uni.lu>
Date: Wed, 23 May 2018 11:42:54 +0200
Subject: [PATCH] when exporting reaction that has the same product and
 reactant system doesn't crash

Instead of crashing reaction is omitted and proper info is added to the model notes.
---
 .../celldesigner/CellDesignerXmlParser.java   | 78 +++++++++++--------
 .../reaction/ReactionCollectionXmlParser.java |  4 +
 .../celldesigner/reaction/ReactionToXml.java  |  4 +
 .../reaction/SelfReactionException.java       | 31 ++++++++
 .../CellDesignerXmlParserTest.java            | 37 +++++++++
 .../reaction/ReactionToXmlTest.java           | 47 ++++++-----
 6 files changed, 151 insertions(+), 50 deletions(-)
 create mode 100644 converter-CellDesigner/src/main/java/lcsb/mapviewer/converter/model/celldesigner/reaction/SelfReactionException.java

diff --git a/converter-CellDesigner/src/main/java/lcsb/mapviewer/converter/model/celldesigner/CellDesignerXmlParser.java b/converter-CellDesigner/src/main/java/lcsb/mapviewer/converter/model/celldesigner/CellDesignerXmlParser.java
index 6a7e6d10c7..8ab5a64b64 100644
--- a/converter-CellDesigner/src/main/java/lcsb/mapviewer/converter/model/celldesigner/CellDesignerXmlParser.java
+++ b/converter-CellDesigner/src/main/java/lcsb/mapviewer/converter/model/celldesigner/CellDesignerXmlParser.java
@@ -13,11 +13,13 @@ import java.util.List;
 import org.apache.commons.io.FilenameUtils;
 import org.apache.commons.lang3.StringEscapeUtils;
 import org.apache.log4j.Logger;
+import org.apache.log4j.spi.LoggingEvent;
 import org.apache.xerces.parsers.DOMParser;
 import org.w3c.dom.Document;
 import org.w3c.dom.Node;
 import org.w3c.dom.NodeList;
 
+import lcsb.mapviewer.common.EventStorageLoggerAppender;
 import lcsb.mapviewer.common.MimeType;
 import lcsb.mapviewer.common.Pair;
 import lcsb.mapviewer.common.XmlParser;
@@ -563,47 +565,59 @@ public class CellDesignerXmlParser extends XmlParser implements IConverter {
    *           thrown when then model is invalid
    * @throws InconsistentModelException
    */
-  public String toXml(Model model) throws InconsistentModelException, InconsistentModelException {
-    CellDesignerElementCollection elements = new CellDesignerElementCollection();
+  public String toXml(Model model) throws InconsistentModelException {
+    EventStorageLoggerAppender appender = new EventStorageLoggerAppender();
+    try {
+      Logger.getRootLogger().addAppender(appender);
+      CellDesignerElementCollection elements = new CellDesignerElementCollection();
 
-    SpeciesCollectionXmlParser speciesCollectionXmlParser = new SpeciesCollectionXmlParser(elements);
-    ReactionCollectionXmlParser reactionCollectionXmlParser = new ReactionCollectionXmlParser(model, elements, false);
-    UnitCollectionXmlParser unitCollectionXmlParser = new UnitCollectionXmlParser();
-    FunctionCollectionXmlParser functionCollectionXmlParser = new FunctionCollectionXmlParser();
-    ParameterCollectionXmlParser parameterCollectionXmlParser = new ParameterCollectionXmlParser(model);
+      SpeciesCollectionXmlParser speciesCollectionXmlParser = new SpeciesCollectionXmlParser(elements);
+      ReactionCollectionXmlParser reactionCollectionXmlParser = new ReactionCollectionXmlParser(model, elements, false);
+      UnitCollectionXmlParser unitCollectionXmlParser = new UnitCollectionXmlParser();
+      FunctionCollectionXmlParser functionCollectionXmlParser = new FunctionCollectionXmlParser();
+      ParameterCollectionXmlParser parameterCollectionXmlParser = new ParameterCollectionXmlParser(model);
 
-    aliasCollectionParser = new AliasCollectionXmlParser(elements, model);
+      aliasCollectionParser = new AliasCollectionXmlParser(elements, model);
 
-    StringBuilder result = new StringBuilder();
-    result.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
-    result.append("<sbml xmlns=\"http://www.sbml.org/sbml/level2/version4\" "
-        + "xmlns:celldesigner=\"http://www.sbml.org/2001/ns/celldesigner\" level=\"2\" version=\"4\">\n");
-    // metaid is a string cell designer id, usually it's model id and as far as
-    // we can tell it's not used at all
-    result.append("<model metaid=\"" + model.getIdModel() + "\" id=\"" + model.getIdModel() + "\">\n");
-
-    if (model.getNotes() != null) {
-      result.append("<notes>");
-      result.append(escapeXml(model.getNotes()));
-      result.append("</notes>");
-    }
+      StringBuilder result = new StringBuilder();
+      result.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
+      result.append("<sbml xmlns=\"http://www.sbml.org/sbml/level2/version4\" "
+          + "xmlns:celldesigner=\"http://www.sbml.org/2001/ns/celldesigner\" level=\"2\" version=\"4\">\n");
+      // metaid is a string cell designer id, usually it's model id and as far as
+      // we can tell it's not used at all
+      result.append("<model metaid=\"" + model.getIdModel() + "\" id=\"" + model.getIdModel() + "\">\n");
 
-    result.append(unitCollectionXmlParser.toXml(model.getUnits()));
-    result.append(functionCollectionXmlParser.toXml(model.getFunctions()));
-    result.append(parameterCollectionXmlParser.toXml(model.getParameters()));
-    result.append(annotationToXml(model, elements));
+      result.append(unitCollectionXmlParser.toXml(model.getUnits()));
+      result.append(functionCollectionXmlParser.toXml(model.getFunctions()));
+      result.append(parameterCollectionXmlParser.toXml(model.getParameters()));
+      result.append(annotationToXml(model, elements));
 
-    compartmentCollectionXmlParser = new CompartmentCollectionXmlParser(elements);
+      compartmentCollectionXmlParser = new CompartmentCollectionXmlParser(elements);
 
-    result.append(compartmentCollectionXmlParser.toXml(model.getCompartments()));
+      result.append(compartmentCollectionXmlParser.toXml(model.getCompartments()));
 
-    result.append(speciesCollectionXmlParser.speciesCollectionToSbmlString(model.getSpeciesList()));
+      result.append(speciesCollectionXmlParser.speciesCollectionToSbmlString(model.getSpeciesList()));
 
-    result.append(reactionCollectionXmlParser.reactionCollectionToXmlString(model.getReactions()));
+      result.append(reactionCollectionXmlParser.reactionCollectionToXmlString(model.getReactions()));
+      if (model.getNotes() != null || !appender.getWarnings().isEmpty()) {
+        result.append("<notes>");
+        if (model.getNotes() != null) {
+          result.append(escapeXml(model.getNotes()));
+        }
+        for (LoggingEvent event : appender.getWarnings()) {
+          if (event.getMessage() instanceof String) {
+            result.append("\n" + ((String) event.getMessage()));
+          }
+        }
+        result.append("</notes>");
+      }
 
-    result.append("</model>");
-    result.append("</sbml>");
-    return result.toString();
+      result.append("</model>");
+      result.append("</sbml>");
+      return result.toString();
+    } finally {
+      Logger.getRootLogger().removeAppender(appender);
+    }
   }
 
   /**
diff --git a/converter-CellDesigner/src/main/java/lcsb/mapviewer/converter/model/celldesigner/reaction/ReactionCollectionXmlParser.java b/converter-CellDesigner/src/main/java/lcsb/mapviewer/converter/model/celldesigner/reaction/ReactionCollectionXmlParser.java
index e42618f464..cb8166ffe7 100644
--- a/converter-CellDesigner/src/main/java/lcsb/mapviewer/converter/model/celldesigner/reaction/ReactionCollectionXmlParser.java
+++ b/converter-CellDesigner/src/main/java/lcsb/mapviewer/converter/model/celldesigner/reaction/ReactionCollectionXmlParser.java
@@ -101,7 +101,11 @@ public class ReactionCollectionXmlParser extends XmlParser {
 		String result = "";
 		result += "<listOfReactions>\n";
 		for (Reaction reaction : collection) {
+		  try {
 			result += xmlStructureFactory.toXml(reaction);
+		  } catch (SelfReactionException e) {
+		    logger.warn("Reaction omitted: " + e.getMessage());
+		  }
 		}
 		result += "</listOfReactions>\n";
 		return result;
diff --git a/converter-CellDesigner/src/main/java/lcsb/mapviewer/converter/model/celldesigner/reaction/ReactionToXml.java b/converter-CellDesigner/src/main/java/lcsb/mapviewer/converter/model/celldesigner/reaction/ReactionToXml.java
index e1656a8ce4..7058ed10ec 100644
--- a/converter-CellDesigner/src/main/java/lcsb/mapviewer/converter/model/celldesigner/reaction/ReactionToXml.java
+++ b/converter-CellDesigner/src/main/java/lcsb/mapviewer/converter/model/celldesigner/reaction/ReactionToXml.java
@@ -108,6 +108,10 @@ public class ReactionToXml extends XmlParser {
    * @throws ConverterException
    */
   public String toXml(Reaction reaction) throws InconsistentModelException {
+    if (reaction.getReactants().get(0).getElement().equals(reaction.getProducts().get(0).getElement())) {
+      throw new SelfReactionException("Reaction " + reaction.getElementId() + " is a self reference for element "
+          + reaction.getProducts().get(0).getElement().getElementId());
+    }
     StringBuilder sb = new StringBuilder();
     sb.append("<reaction ");
     sb.append("metaid=\"" + reaction.getIdReaction() + "\" ");
diff --git a/converter-CellDesigner/src/main/java/lcsb/mapviewer/converter/model/celldesigner/reaction/SelfReactionException.java b/converter-CellDesigner/src/main/java/lcsb/mapviewer/converter/model/celldesigner/reaction/SelfReactionException.java
new file mode 100644
index 0000000000..b85d6f5537
--- /dev/null
+++ b/converter-CellDesigner/src/main/java/lcsb/mapviewer/converter/model/celldesigner/reaction/SelfReactionException.java
@@ -0,0 +1,31 @@
+package lcsb.mapviewer.converter.model.celldesigner.reaction;
+
+import lcsb.mapviewer.model.map.InconsistentModelException;
+
+/**
+ * Exception thrown when CellDesigner exporter tries to export reaction that
+ * uses the same element as product and reactant.
+ * 
+ * @author Piotr Gawron
+ * 
+ */
+public class SelfReactionException extends InconsistentModelException {
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 1L;
+
+  /**
+   * Default constructor with a message passed in the argument.
+   * 
+   * @param message
+   *          text message of this exception
+   */
+  public SelfReactionException(String message) {
+    super(message);
+  }
+
+  public SelfReactionException(Exception e) {
+    super(e);
+  }
+}
diff --git a/converter-CellDesigner/src/test/java/lcsb/mapviewer/converter/model/celldesigner/CellDesignerXmlParserTest.java b/converter-CellDesigner/src/test/java/lcsb/mapviewer/converter/model/celldesigner/CellDesignerXmlParserTest.java
index efa2ee8835..6f97c0fbcd 100644
--- a/converter-CellDesigner/src/test/java/lcsb/mapviewer/converter/model/celldesigner/CellDesignerXmlParserTest.java
+++ b/converter-CellDesigner/src/test/java/lcsb/mapviewer/converter/model/celldesigner/CellDesignerXmlParserTest.java
@@ -39,7 +39,10 @@ import lcsb.mapviewer.model.map.layout.graphics.LayerText;
 import lcsb.mapviewer.model.map.model.Model;
 import lcsb.mapviewer.model.map.model.ModelComparator;
 import lcsb.mapviewer.model.map.model.ModelFullIndexed;
+import lcsb.mapviewer.model.map.reaction.Product;
+import lcsb.mapviewer.model.map.reaction.Reactant;
 import lcsb.mapviewer.model.map.reaction.Reaction;
+import lcsb.mapviewer.model.map.reaction.type.StateTransitionReaction;
 import lcsb.mapviewer.model.map.species.Element;
 import lcsb.mapviewer.model.map.species.Gene;
 import lcsb.mapviewer.model.map.species.GenericProtein;
@@ -962,4 +965,38 @@ public class CellDesignerXmlParserTest extends CellDesignerTestFunctions {
     }
   }
 
+  @Test
+  public void testModelWithSelfReactionToXml() throws Exception {
+    try {
+      Model model = new ModelFullIndexed(null);
+      model.setIdModel("as");
+      model.setWidth(10);
+      model.setHeight(10);
+      Species protein = new GenericProtein("id1");
+      protein.setName("ROS");
+      model.addElement(protein);
+
+      Reaction reaction = new StateTransitionReaction();
+      reaction.setIdReaction("re1");
+      Product product = new Product(protein);
+      product.setLine(new PolylineData(new Point2D.Double(0, 0), new Point2D.Double(20, 20)));
+      reaction.addProduct(product);
+      Reactant reactant = new Reactant(protein);
+      reactant.setLine(new PolylineData(new Point2D.Double(20, 20), new Point2D.Double(0, 0)));
+      reaction.addReactant(reactant);
+      model.addReaction(reaction);
+
+      CellDesignerXmlParser parser = new CellDesignerXmlParser();
+      String xmlString = parser.toXml(model);
+
+      assertNotNull(xmlString);
+      assertTrue(xmlString.contains("omitted"));
+      assertTrue(xmlString.contains("re1"));
+
+    } catch (Exception e) {
+      e.printStackTrace();
+      throw e;
+    }
+  }
+
 }
diff --git a/converter-CellDesigner/src/test/java/lcsb/mapviewer/converter/model/celldesigner/reaction/ReactionToXmlTest.java b/converter-CellDesigner/src/test/java/lcsb/mapviewer/converter/model/celldesigner/reaction/ReactionToXmlTest.java
index fdc5a88fc6..892243e5b3 100644
--- a/converter-CellDesigner/src/test/java/lcsb/mapviewer/converter/model/celldesigner/reaction/ReactionToXmlTest.java
+++ b/converter-CellDesigner/src/test/java/lcsb/mapviewer/converter/model/celldesigner/reaction/ReactionToXmlTest.java
@@ -57,24 +57,26 @@ public class ReactionToXmlTest {
   @Test
   public void testInvalidModification() throws InconsistentModelException {
     Model model = new ModelFullIndexed(null);
-    Species alias = new GenericProtein("2");
+    Species protein1 = new GenericProtein("2");
+    Species protein2 = new GenericProtein("3");
 
-    model.addElement(alias);
+    model.addElement(protein1);
+    model.addElement(protein2);
 
     Reaction reaction = new TransportReaction();
 
-    Reactant reactant = new Reactant(alias);
+    Reactant reactant = new Reactant(protein1);
     reactant.setLine(new PolylineData(new Point2D.Double(), new Point2D.Double(10, 0)));
-    Product product = new Product(alias);
+    Product product = new Product(protein2);
     product.setLine(new PolylineData(new Point2D.Double(), new Point2D.Double(20, 0)));
 
     reaction.addReactant(reactant);
     reaction.addProduct(product);
 
-    Modifier modifier = new Catalysis(alias);
+    Modifier modifier = new Catalysis(protein1);
     modifier.setLine(new PolylineData(new Point2D.Double(), new Point2D.Double(30, 0)));
 
-    Modifier modifier2 = new Catalysis(alias);
+    Modifier modifier2 = new Catalysis(protein1);
     List<Point2D> points = new ArrayList<>();
     points.add(new Point2D.Double(0, 0));
     points.add(new Point2D.Double(30, 30));
@@ -114,28 +116,30 @@ public class ReactionToXmlTest {
   public void testModificationFromInsideComplex() throws InconsistentModelException {
 
     Model model = new ModelFullIndexed(null);
-    Species alias = new GenericProtein("2");
+    Species protein1 = new GenericProtein("2");
+    Species protein2 = new GenericProtein("3");
 
-    model.addElement(alias);
+    model.addElement(protein1);
+    model.addElement(protein2);
 
     Complex complex = new Complex("4");
-    complex.addSpecies(alias);
+    complex.addSpecies(protein1);
 
-    alias.setComplex(complex);
+    protein1.setComplex(complex);
 
     model.addElement(complex);
 
     Reaction reaction = new TransportReaction();
 
-    Reactant reactant = new Reactant(alias);
+    Reactant reactant = new Reactant(protein1);
     reactant.setLine(new PolylineData(new Point2D.Double(), new Point2D.Double(10, 0)));
-    Product product = new Product(alias);
+    Product product = new Product(protein2);
     product.setLine(new PolylineData(new Point2D.Double(), new Point2D.Double(20, 0)));
 
     reaction.addReactant(reactant);
     reaction.addProduct(product);
 
-    Modifier modifier = new Catalysis(alias);
+    Modifier modifier = new Catalysis(protein1);
     modifier.setLine(new PolylineData(new Point2D.Double(), new Point2D.Double(30, 0)));
 
     reaction.addModifier(modifier);
@@ -154,12 +158,17 @@ public class ReactionToXmlTest {
     Complex complex = new Complex("4");
     complex.setComplex(complex);
     model.addElement(complex);
+    
+    Species protein1 = new GenericProtein("2");
+
+    model.addElement(protein1);
+    
 
     Reaction reaction = new TransportReaction();
 
     Reactant reactant = new Reactant(complex);
     reactant.setLine(new PolylineData(new Point2D.Double(), new Point2D.Double(10, 0)));
-    Product product = new Product(complex);
+    Product product = new Product(protein1);
     product.setLine(new PolylineData(new Point2D.Double(), new Point2D.Double(20, 0)));
 
     reaction.addReactant(reactant);
@@ -178,15 +187,17 @@ public class ReactionToXmlTest {
   public void testInvalidReaction() throws InconsistentModelException {
 
     Model model = new ModelFullIndexed(null);
-    Species alias = new GenericProtein("2");
+    Species protein1 = new GenericProtein("2");
+    Species protein2 = new GenericProtein("3");
 
-    model.addElement(alias);
+    model.addElement(protein1);
+    model.addElement(protein2);
 
     Reaction reaction = new Reaction();
 
-    Reactant reactant = new Reactant(alias);
+    Reactant reactant = new Reactant(protein1);
     reactant.setLine(new PolylineData(new Point2D.Double(), new Point2D.Double(10, 0)));
-    Product product = new Product(alias);
+    Product product = new Product(protein2);
     product.setLine(new PolylineData(new Point2D.Double(), new Point2D.Double(20, 0)));
 
     reaction.addReactant(reactant);
-- 
GitLab