Commit b59a2c90 authored by Piotr Gawron's avatar Piotr Gawron

Merge branch '572-add-support-for-modifications-and-states-in-sbml-export-import' into 'master'

Resolve "add support for modifications and states in sbml export/import"

Closes #572

See merge request !560
parents 8611235f dc5b9db3
Pipeline #8097 passed with stage
in 10 minutes and 28 seconds
......@@ -22,6 +22,11 @@
<artifactId>commons-io</artifactId>
<version>${commons-io.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId>
<version>${apache.commons-text.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
......
......@@ -49,6 +49,7 @@ public abstract class Comparator<T extends Object> implements java.util.Comparat
}
return internalCompare(arg0, arg1);
} else {
logger.debug("Class different: " + arg0.getClass() + "; " + arg1.getClass());
return -1;
}
}
......
......@@ -21,7 +21,7 @@ import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.text.StringEscapeUtils;
import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
......@@ -416,7 +416,7 @@ public class XmlParser {
return null;
}
// quite expensive
return StringEscapeUtils.escapeXml(string).replaceAll("\n", "&#10;").replace("\r", "&#13;");
return StringEscapeUtils.escapeXml10(string).replaceAll("\n", "&#10;").replace("\r", "&#13;");
}
public List<Node> getAllNotNecessirellyDirectChild(String tagName, Node root) {
......
......@@ -5,10 +5,16 @@ import java.util.List;
import org.apache.log4j.Logger;
import lcsb.mapviewer.common.exception.InvalidArgumentException;
import lcsb.mapviewer.common.exception.NotImplementedException;
import lcsb.mapviewer.converter.model.celldesigner.structure.fields.CellDesignerModificationResidue;
import lcsb.mapviewer.model.map.species.AntisenseRna;
import lcsb.mapviewer.model.map.species.Gene;
import lcsb.mapviewer.model.map.species.Species;
import lcsb.mapviewer.model.map.species.field.CodingRegion;
import lcsb.mapviewer.model.map.species.field.ModificationResidue;
import lcsb.mapviewer.model.map.species.field.ModificationSite;
import lcsb.mapviewer.model.map.species.field.ProteinBindingDomain;
/**
* Class representing CellDesigner {@link AntisenseRna}.
......@@ -140,9 +146,19 @@ public class CellDesignerAntisenseRna extends CellDesignerSpecies<AntisenseRna>
}
@Override
public void updateModelElementAfterLayoutAdded(Species element) {
public void updateModelElementAfterLayoutAdded(Species species) {
AntisenseRna antisenseRna = (AntisenseRna) species;
for (CellDesignerModificationResidue region : regions) {
((AntisenseRna) element).addRegion(region.createModificationResidue(element));
ModificationResidue mr = region.createModificationResidue(antisenseRna);
if (mr instanceof CodingRegion) {
antisenseRna.addCodingRegion((CodingRegion) mr);
} else if (mr instanceof ModificationSite) {
antisenseRna.addModificationSite((ModificationSite) mr);
} else if (mr instanceof ProteinBindingDomain) {
antisenseRna.addProteinBindingDomain((ProteinBindingDomain) mr);
} else {
throw new InvalidArgumentException("Cannot add modification residue to element: " + mr.getClass());
}
}
}
......
......@@ -149,8 +149,4 @@ public class CellDesignerCompartment extends CellDesignerElement<Compartment>
return result;
}
@Override
public void updateModelElementAfterLayoutAdded(Compartment element) {
}
}
......@@ -429,8 +429,6 @@ public abstract class CellDesignerElement<T extends Element> implements Serializ
*/
public abstract T createModelElement(String modelElementId);
public abstract void updateModelElementAfterLayoutAdded(T element);
/**
* Sets values from this cell designer structure into model object.
*
......
......@@ -3,10 +3,18 @@ package lcsb.mapviewer.converter.model.celldesigner.structure;
import java.util.ArrayList;
import java.util.List;
import org.apache.log4j.Logger;
import lcsb.mapviewer.common.exception.InvalidArgumentException;
import lcsb.mapviewer.common.exception.NotImplementedException;
import lcsb.mapviewer.converter.model.celldesigner.structure.fields.CellDesignerModificationResidue;
import lcsb.mapviewer.model.map.species.Gene;
import lcsb.mapviewer.model.map.species.Species;
import lcsb.mapviewer.model.map.species.field.CodingRegion;
import lcsb.mapviewer.model.map.species.field.ModificationResidue;
import lcsb.mapviewer.model.map.species.field.ModificationSite;
import lcsb.mapviewer.model.map.species.field.RegulatoryRegion;
import lcsb.mapviewer.model.map.species.field.TranscriptionSite;
/**
* Class representing CellDesigner {@link Gene}.
......@@ -15,6 +23,11 @@ import lcsb.mapviewer.model.map.species.Species;
*
*/
public class CellDesignerGene extends CellDesignerSpecies<Gene> {
/**
* Default class logger.
*/
@SuppressWarnings("unused")
private static Logger logger = Logger.getLogger(CellDesignerGene.class);
/**
*
......@@ -113,10 +126,21 @@ public class CellDesignerGene extends CellDesignerSpecies<Gene> {
}
@Override
public void updateModelElementAfterLayoutAdded(Species element) {
public void updateModelElementAfterLayoutAdded(Species species) {
Gene gene = (Gene) species;
for (CellDesignerModificationResidue region : modificationResidues) {
((Gene) element).addModificationResidue(region.createModificationResidue(element));
ModificationResidue mr = region.createModificationResidue(gene);
if (mr instanceof CodingRegion) {
gene.addCodingRegion((CodingRegion) mr);
} else if (mr instanceof ModificationSite) {
gene.addModificationSite((ModificationSite) mr);
} else if (mr instanceof RegulatoryRegion) {
gene.addRegulatoryRegion((RegulatoryRegion) mr);
} else if (mr instanceof TranscriptionSite) {
gene.addTranscriptionSite((TranscriptionSite) mr);
} else {
throw new InvalidArgumentException("Cannot add modification residue to element: " + mr.getClass());
}
}
}
}
......@@ -5,10 +5,14 @@ import java.util.List;
import org.apache.log4j.Logger;
import lcsb.mapviewer.common.exception.InvalidArgumentException;
import lcsb.mapviewer.common.exception.NotImplementedException;
import lcsb.mapviewer.converter.model.celldesigner.structure.fields.CellDesignerModificationResidue;
import lcsb.mapviewer.model.map.species.Protein;
import lcsb.mapviewer.model.map.species.Species;
import lcsb.mapviewer.model.map.species.field.BindingRegion;
import lcsb.mapviewer.model.map.species.field.ModificationResidue;
import lcsb.mapviewer.model.map.species.field.Residue;
/**
* Class representing CellDesigner {@link Protein} object.
......@@ -150,9 +154,17 @@ public class CellDesignerProtein<T extends Protein> extends CellDesignerSpecies<
}
@Override
public void updateModelElementAfterLayoutAdded(Species element) {
for (CellDesignerModificationResidue mr : modificationResidues) {
((Protein) element).addModificationResidue(mr.createModificationResidue(element));
public void updateModelElementAfterLayoutAdded(Species species) {
Protein protein= (Protein) species;
for (CellDesignerModificationResidue region : modificationResidues) {
ModificationResidue mr = region.createModificationResidue(protein);
if (mr instanceof Residue) {
protein.addResidue((Residue) mr);
} else if (mr instanceof BindingRegion) {
protein.addBindingRegion((BindingRegion) mr);
} else {
throw new InvalidArgumentException("Cannot add modification residue to element: " + mr.getClass());
}
}
}
......
......@@ -5,10 +5,15 @@ import java.util.List;
import org.apache.log4j.Logger;
import lcsb.mapviewer.common.exception.InvalidArgumentException;
import lcsb.mapviewer.common.exception.NotImplementedException;
import lcsb.mapviewer.converter.model.celldesigner.structure.fields.CellDesignerModificationResidue;
import lcsb.mapviewer.model.map.species.Rna;
import lcsb.mapviewer.model.map.species.Species;
import lcsb.mapviewer.model.map.species.field.CodingRegion;
import lcsb.mapviewer.model.map.species.field.ModificationResidue;
import lcsb.mapviewer.model.map.species.field.ModificationSite;
import lcsb.mapviewer.model.map.species.field.ProteinBindingDomain;
/**
* Class representing CellDesigner {@link Rna}.
......@@ -139,10 +144,19 @@ public class CellDesignerRna extends CellDesignerSpecies<Rna> {
}
@Override
public void updateModelElementAfterLayoutAdded(Species element) {
public void updateModelElementAfterLayoutAdded(Species species) {
Rna rna = (Rna) species;
for (CellDesignerModificationResidue region : regions) {
Rna rna = (Rna) element;
rna.addRegion(region.createModificationResidue(rna));
ModificationResidue mr = region.createModificationResidue(rna);
if (mr instanceof CodingRegion) {
rna.addCodingRegion((CodingRegion) mr);
} else if (mr instanceof ModificationSite) {
rna.addModificationSite((ModificationSite) mr);
} else if (mr instanceof ProteinBindingDomain) {
rna.addProteinBindingDomain((ProteinBindingDomain) mr);
} else {
throw new InvalidArgumentException("Cannot add modification residue to element: " + mr.getClass());
}
}
}
......
......@@ -540,7 +540,6 @@ public class CellDesignerSpecies<T extends Species> extends CellDesignerElement<
this.constant = constant;
}
@Override
public void updateModelElementAfterLayoutAdded(Species element) {
}
......
......@@ -449,8 +449,7 @@ public class CellDesignerXmlParserTest extends CellDesignerTestFunctions {
assertNotNull(model2);
ModelComparator comparator = new ModelComparator();
assertEquals(0, comparator.compare(model, model2));
assertTrue(model2.getNotes() == null || model2.getNotes().isEmpty());
} catch (Exception e) {
e.printStackTrace();
throw e;
......
......@@ -90,7 +90,7 @@ public class GeneXmlParserTest extends CellDesignerTestFunctions {
public void testParsePhosphorylatedGene() throws Exception {
try {
Model model = getModelForFile("testFiles/problematic/phosphorylated_gene.xml");
Gene gene = (Gene) model.getElementByElementId("sa1");
Gene gene = model.getElementByElementId("sa1");
assertEquals(1, gene.getModificationResidues().size());
ModificationSite residue = (ModificationSite) gene.getModificationResidues().get(0);
assertEquals(ModificationState.PHOSPHORYLATED, residue.getState());
......
......@@ -56,7 +56,7 @@ public class SpeciesStateTest {
rna.setHeight(10);
CodingRegion mr = new CodingRegion();
mr.setPosition(new Point2D.Double(10, 40));
rna.addRegion(mr);
rna.addCodingRegion(mr);
SpeciesState state = new SpeciesState(rna);
assertEquals(1, state.getModifications().size());
}
......@@ -68,7 +68,7 @@ public class SpeciesStateTest {
gene.setHeight(10);
ModificationSite mr = new ModificationSite();
mr.setPosition(new Point2D.Double(10, 40));
gene.addModificationResidue(mr);
gene.addModificationSite(mr);
SpeciesState state = new SpeciesState(gene);
assertEquals(1, state.getModifications().size());
}
......
......@@ -15,7 +15,7 @@ import java.nio.charset.StandardCharsets;
import javax.xml.bind.JAXBException;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.text.StringEscapeUtils;
import org.apache.log4j.Logger;
import org.apache.log4j.spi.LoggingEvent;
import org.sbgn.SbgnUtil;
......@@ -74,7 +74,7 @@ public class SbgnmlXmlConverter implements IConverter {
throw new ConverterException("problematic output sbgn. Cannot find map tag");
}
String notesNode = "<notes><html:body xmlns:html=\"http://www.w3.org/1999/xhtml\">" +
StringEscapeUtils.escapeXml(notes.toString()) + "\n</html:body></notes> ";
StringEscapeUtils.escapeXml10(notes.toString()) + "\n</html:body></notes> ";
xml = xml.substring(0, position) + notesNode + xml.substring(position, xml.length());
}
result = new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8));
......
......@@ -831,7 +831,7 @@ public class SbgnmlXmlParser {
mr.setPosition(new Point2D.Double(x, y));
protein.addModificationResidue(mr);
protein.addResidue(mr);
}
/**
......
......@@ -33,7 +33,7 @@ public class SbgnmlXmlParserTest2 {
Residue mr = new Residue();
Point2D position = new Point2D.Double(100, 20);
mr.setPosition(position);
protein.addModificationResidue(mr);
protein.addResidue(mr);
parser.adjustModificationCoordinates(protein);
assertTrue(mr.getPosition().distance(position) > Configuration.EPSILON);
......
......@@ -197,7 +197,7 @@ public class SpeciesConverterTest {
bindingRegion.setPosition(new Point2D.Double(10, 10));
bindingRegion.setWidth(100.0);
bindingRegion.setHeight(10.0);
protein.addModificationResidue(bindingRegion);
protein.addBindingRegion(bindingRegion);
ProteinConverter converter = Mockito.spy(new ProteinConverter(colorExtractor));
converter.drawModification(bindingRegion, graphics, false);
......
......@@ -104,7 +104,7 @@
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
<!-- https://github.com/sbmlteam/jsbml/issues/156 -->
<!-- https://github.com/sbmlteam/jsbml/issues/156 -->
<exclusion>
<groupId>org.sbml.jsbml</groupId>
<artifactId>jsbml-core</artifactId>
......@@ -153,6 +153,12 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId>
<version>${apache.commons-text.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
......
package lcsb.mapviewer.converter.model.sbml;
import javax.xml.stream.XMLStreamException;
import org.apache.commons.text.StringEscapeUtils;
import org.sbml.jsbml.AbstractNamedSBase;
import lcsb.mapviewer.converter.InvalidInputDataExecption;
/**
* This utility class parses notes from SBML node and prepares escaped string
* ready to use in SBML.
*
* @author Piotr Gawron
*
*/
public class NotesUtility {
/**
* Extract notes from SBML node
*
* @param sbmlElement
* SBML node
* @return notes
* @throws InvalidInputDataExecption
* thrown when there is problem with extracting notes
*/
public static String extractNotes(AbstractNamedSBase sbmlElement) throws InvalidInputDataExecption {
String notes = "";
try {
notes = sbmlElement.getNotesString();
} catch (XMLStreamException e) {
throw new InvalidInputDataExecption(sbmlElement.getId() + " Invalid notes", e);
}
if (sbmlElement.getNotes() != null) {
if (sbmlElement.getNotes().getChildCount() > 1) {
if (sbmlElement.getNotes().getChild(1).getChildCount() > 1) {
if (sbmlElement.getNotes().getChild(1).getChild(1).getChildCount() > 0) {
notes = sbmlElement.getNotes().getChild(1).getChild(1).getChild(0).getCharacters();
} else {
notes = sbmlElement.getNotes().getChild(1).getChild(1).getCharacters();
}
}
}
}
return notes;
}
/**
* Prepares escaped xml string with notes.
*
* @param notes
* notes to be processed
* @return escaped xml string with notes
*/
public static String prepareEscapedXmlNotes(String notes) {
if (notes == null) {
return "";
}
return StringEscapeUtils.escapeXml10(notes);
}
}
......@@ -3,15 +3,19 @@ package lcsb.mapviewer.converter.model.sbml;
import java.awt.Color;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.xml.stream.XMLStreamException;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.log4j.Logger;
import org.sbml.jsbml.Model;
import org.sbml.jsbml.ext.SBasePlugin;
import org.sbml.jsbml.ext.layout.AbstractReferenceGlyph;
import org.sbml.jsbml.ext.layout.Layout;
import org.sbml.jsbml.ext.layout.LayoutModelPlugin;
import org.sbml.jsbml.ext.multi.MultiModelPlugin;
import org.sbml.jsbml.ext.render.ColorDefinition;
import org.sbml.jsbml.ext.render.LocalRenderInformation;
import org.sbml.jsbml.ext.render.LocalStyle;
......@@ -28,11 +32,24 @@ import lcsb.mapviewer.model.map.InconsistentModelException;
public abstract class SbmlBioEntityExporter<T extends BioEntity, S extends org.sbml.jsbml.AbstractNamedSBase>
extends XmlParser {
Logger logger = Logger.getLogger(SbmlBioEntityExporter.class);
/**
* Default class logger.
*/
private Logger logger = Logger.getLogger(SbmlBioEntityExporter.class);
/**
* SBML Layout used when exporting map.
*/
private Layout layout;
/**
* Map that we are exporting.
*/
private lcsb.mapviewer.model.map.model.Model minervaModel;
/**
* SBML model to which we are exporting.
*/
private Model sbmlModel;
private Map<String, S> sbmlElementByElementId = new HashMap<>();
......@@ -42,13 +59,17 @@ public abstract class SbmlBioEntityExporter<T extends BioEntity, S extends org.s
private int idCounter = 0;
public SbmlBioEntityExporter(Layout sbmlLayout, lcsb.mapviewer.model.map.model.Model minervaModel) {
this.layout = sbmlLayout;
private Set<SbmlExtension> sbmlExtensions = new HashSet<>();
public SbmlBioEntityExporter(Model sbmlModel, lcsb.mapviewer.model.map.model.Model minervaModel,
Collection<SbmlExtension> sbmlExtensions) {
this.sbmlModel = sbmlModel;
this.layout = getLayout(sbmlModel);
this.minervaModel = minervaModel;
this.sbmlExtensions.addAll(sbmlExtensions);
}
public void exportElements(Model model) throws InconsistentModelException {
sbmlModel = model;
public void exportElements() throws InconsistentModelException {
Collection<T> speciesList = getElementList();
for (T species : speciesList) {
S sbmlElement = getSbmlElement(species);
......@@ -58,12 +79,25 @@ public abstract class SbmlBioEntityExporter<T extends BioEntity, S extends org.s
}
sbmlElementByElementId.put(species.getElementId(), sbmlElement);
}
for (T species : speciesList) {
AbstractReferenceGlyph elementGlyph = createGlyph(species);
sbmlGlyphByElementId.put(species.getElementId(), elementGlyph);
if (isExtensionEnabled(SbmlExtension.LAYOUT)) {
for (T species : speciesList) {
AbstractReferenceGlyph elementGlyph = createGlyph(species);
sbmlGlyphByElementId.put(species.getElementId(), elementGlyph);
}
}
}
/**
* Checks if exporter should used extension.
*
* @param sbmlExtension
* {@link SbmlExtension} to be checked
* @return <code>true</code> if extension should be supported by exported file
*/
protected boolean isExtensionEnabled(SbmlExtension sbmlExtension) {
return sbmlExtensions.contains(sbmlExtension);
}
protected abstract Collection<T> getElementList();
public abstract S createSbmlElement(T element) throws InconsistentModelException;
......@@ -81,7 +115,7 @@ public abstract class SbmlBioEntityExporter<T extends BioEntity, S extends org.s
}
sbmlElement.setName(element.getName());
try {
sbmlElement.setNotes(StringEscapeUtils.escapeXml(element.getNotes()));
sbmlElement.setNotes(NotesUtility.prepareEscapedXmlNotes(element.getNotes()));
} catch (XMLStreamException e) {
throw new InvalidStateException(e);
}
......@@ -108,35 +142,48 @@ public abstract class SbmlBioEntityExporter<T extends BioEntity, S extends org.s
return (idCounter++) + "";
}
protected Model getSbmlModel() {
return sbmlModel;
}
protected void setSbmlModel(Model sbmlModel) {
this.sbmlModel = sbmlModel;
private Layout getLayout(org.sbml.jsbml.Model sbmlModel) {
Layout layout = null;
if (sbmlModel.getExtensionCount() > 0) {
for (SBasePlugin plugin : sbmlModel.getExtensionPackages().values()) {
if (plugin.getClass().equals(org.sbml.jsbml.ext.layout.LayoutModelPlugin.class)) {
LayoutModelPlugin layoutPlugin = (LayoutModelPlugin) plugin;
if (layoutPlugin.getLayoutCount() == 0) {
logger.warn("Layout plugin available but no layouts defined");
} else if (layoutPlugin.getLayoutCount() > 1) {
logger.warn(layoutPlugin.getLayoutCount() + " layouts defined. Using first one.");
layout = layoutPlugin.getLayout(0);
} else {
layout = layoutPlugin.getLayout(0);
}
}
}
}
return layout;
}
protected Layout getLayout() {
return layout;
}
protected void setLayout(Layout layout) {
this.layout = layout;
}
protected RenderLayoutPlugin getRenderPlugin() {
if (layout.getExtensionCount() > 0) {
return (RenderLayoutPlugin) layout.getExtension("render");
if (getLayout().getExtensionCount() > 0) {
return (RenderLayoutPlugin) getLayout().getExtension("render");
}
return null;
}
protected MultiModelPlugin getMultiPlugin() {
return (MultiModelPlugin) sbmlModel.getExtension("multi");
}
protected lcsb.mapviewer.model.map.model.Model getMinervaModel() {
return minervaModel;
}
protected void setMinervaModel(lcsb.mapviewer.model.map.model.Model minervaModel) {
this.minervaModel = minervaModel;
protected Model getSbmlModel() {
return sbmlModel;
}
public S getSbmlElementByElementId(String id) {
......
......@@ -6,12 +6,13 @@ import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.xml.stream.XMLStreamException;
import org.apache.log4j.Logger;
import org.sbml.jsbml.AbstractNamedSBase;
import org.sbml.jsbml.Annotation;
import org.sbml.jsbml.ext.SBasePlugin;
import org.sbml.jsbml.ext.layout.Layout;
import org.sbml.jsbml.ext.layout.LayoutModelPlugin;
import org.sbml.jsbml.ext.multi.MultiModelPlugin;
import org.sbml.jsbml.ext.render.LocalRenderInformation;
import org.sbml.jsbml.ext.render.LocalStyle;
import org.sbml.jsbml.ext.render.RenderLayoutPlugin;
......@@ -24,18 +25,49 @@ import lcsb.mapviewer.converter.annotation.XmlAnnotationParser;
import lcsb.mapviewer.converter.model.sbml.species.ElementColorEnum;
import lcsb.mapviewer.model.map.BioEntity;
import lcsb.mapviewer.model.map.MiriamData;
import lcsb.mapviewer.model.map.model.Model;
import lcsb.mapviewer.model.map.species.Element;
public class SbmlBioEntityParser extends XmlParser {
Logger logger = Logger.getLogger(SbmlBioEntityParser.class);
protected Layout layout;
protected lcsb.mapviewer.model.map.model.Model minervaModel;
/**
* Default class logger.
*/
private static Logger logger = Logger.getLogger(SbmlBioEntityParser.class);
/**
* SBML layout attached to parsed {@link #sbmlModel}.