diff --git a/.gitignore b/.gitignore index bbcea90463b8d87b5fec80ccacfd75fdaa3587b7..5f2ce9bf39d3f4369734360f98a8e770780ca767 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ web/src/main/webapp/svnversion.txt /target/ service/minerva-big/ npm-debug.log +converter-sbml/jsbml.log .idea/workspace.xml .idea/libraries/ 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 27cc67f3126ab3b670d497aeb02af8bcc2f2a573..9e119bfbaffaa72f7ad2b08d3636aa66e7ae074a 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 @@ -1,84 +1,32 @@ package lcsb.mapviewer.converter.model.sbml; import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; import java.util.List; -import java.util.Map; -import java.util.Set; import javax.xml.stream.XMLStreamException; import org.apache.log4j.Logger; -import org.sbml.jsbml.Annotation; +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.util.NotImplementedException; +import lcsb.mapviewer.common.Pair; import lcsb.mapviewer.converter.InvalidInputDataExecption; -import lcsb.mapviewer.model.map.MiriamData; import lcsb.mapviewer.model.map.compartment.Compartment; import lcsb.mapviewer.model.map.compartment.SquareCompartment; -public class SbmlCompartmentParser { +public class SbmlCompartmentParser extends SbmlElementParser<org.sbml.jsbml.Compartment> { Logger logger = Logger.getLogger(SbmlCompartmentParser.class); - Layout layout; - public SbmlCompartmentParser(Layout layout) { - this.layout = layout; - } - - public List<Compartment> parseList(Model sbmlModel) throws InvalidInputDataExecption { - List<Compartment> result = new ArrayList<>(); - for (org.sbml.jsbml.Compartment compartment : sbmlModel.getListOfCompartments()) { - result.add(parse(compartment)); - } - if (layout != null) { - return mergeLayout(result, layout); - } else { - return result; - } - } - - private List<Compartment> mergeLayout(List<Compartment> compartments, Layout sbmlLayout) - throws InvalidInputDataExecption { - Set<Compartment> used = new HashSet<>(); - Map<String, Compartment> compartmentById = new HashMap<>(); - for (Compartment compartment : compartments) { - if (compartmentById.get(compartment.getElementId()) != null) { - throw new InvalidInputDataExecption("Duplicated element id: " + compartment.getElementId()); - } - compartmentById.put(compartment.getElementId(), compartment); - } - List<Compartment> result = new ArrayList<>(); - - for (CompartmentGlyph glyph : sbmlLayout.getListOfCompartmentGlyphs()) { - Compartment source = compartmentById.get(glyph.getCompartment()); - if (source==null) { - throw new InvalidInputDataExecption("Layout contains invalid compartment id: "+glyph.getCompartment()); - } - used.add(source); - Compartment compartmentWithLayout = new SquareCompartment(source); - compartmentWithLayout.setElementId(glyph.getId()); - compartmentWithLayout.setX(glyph.getBoundingBox().getPosition().getX()); - compartmentWithLayout.setY(glyph.getBoundingBox().getPosition().getY()); - compartmentWithLayout.setWidth(glyph.getBoundingBox().getDimensions().getWidth()); - compartmentWithLayout.setHeight(glyph.getBoundingBox().getDimensions().getHeight()); - result.add(compartmentWithLayout); - } - for (Compartment compartment : compartments) { - if (!used.contains(compartment)) { - logger.warn("Layout doesn't contain information about compartment: "+compartment.getElementId()); - result.add(compartment); - } - } - return result; + super(layout); } - private Compartment parse(org.sbml.jsbml.Compartment compartment) throws InvalidInputDataExecption { - Compartment result = new Compartment(compartment.getId()); + @Override + protected Compartment parse(org.sbml.jsbml.Compartment compartment) throws InvalidInputDataExecption { + Compartment result = new SquareCompartment(compartment.getId()); result.setMiriamData(parseAnnotation(compartment.getAnnotation())); result.setName(compartment.getName()); try { @@ -89,11 +37,17 @@ public class SbmlCompartmentParser { return result; } - protected Set<MiriamData> parseAnnotation(Annotation annotation) { - if (annotation.getCVTermCount() > 0) { - throw new NotImplementedException(); + @Override + protected ListOf<org.sbml.jsbml.Compartment> getSbmlElementList(Model sbmlModel) { + return sbmlModel.getListOfCompartments(); + } + + @Override + protected List<Pair<String, AbstractReferenceGlyph>> getGlyphs(Layout sbmlLayout) { + List<Pair<String, AbstractReferenceGlyph>> result = new ArrayList<>(); + for (CompartmentGlyph glyph : sbmlLayout.getListOfCompartmentGlyphs()) { + result.add(new Pair<String, AbstractReferenceGlyph>(glyph.getCompartment(), glyph)); } - Set<MiriamData> result = new HashSet<>(); return result; } 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 new file mode 100644 index 0000000000000000000000000000000000000000..5b7c7edcb98e8b050fb3f0aca830347bd81dd201 --- /dev/null +++ b/converter-sbml/src/main/java/lcsb/mapviewer/converter/model/sbml/SbmlElementParser.java @@ -0,0 +1,94 @@ +package lcsb.mapviewer.converter.model.sbml; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.log4j.Logger; +import org.sbml.jsbml.Annotation; +import org.sbml.jsbml.ListOf; +import org.sbml.jsbml.Model; +import org.sbml.jsbml.ext.layout.AbstractReferenceGlyph; +import org.sbml.jsbml.ext.layout.Layout; +import org.sbml.jsbml.util.NotImplementedException; + +import lcsb.mapviewer.common.Pair; +import lcsb.mapviewer.converter.InvalidInputDataExecption; +import lcsb.mapviewer.model.map.MiriamData; +import lcsb.mapviewer.model.map.species.Element; + +public abstract class SbmlElementParser<T extends org.sbml.jsbml.Symbol> { + Logger logger = Logger.getLogger(SbmlElementParser.class); + + Layout layout; + + public SbmlElementParser(Layout layout) { + this.layout = layout; + } + + public List<Element> parseList(Model sbmlModel) throws InvalidInputDataExecption { + List<Element> result = new ArrayList<>(); + for (T sbmlElement : getSbmlElementList(sbmlModel)) { + result.add(parse(sbmlElement)); + } + if (layout != null) { + return mergeLayout(result, layout); + } else { + return result; + } + } + + protected abstract ListOf<T> getSbmlElementList(Model sbmlModel); + + private List<Element> mergeLayout(List<Element> elements, Layout sbmlLayout) throws InvalidInputDataExecption { + Set<Element> used = new HashSet<>(); + Map<String, Element> elementById = new HashMap<>(); + for (Element species : elements) { + if (elementById.get(species.getElementId()) != null) { + throw new InvalidInputDataExecption("Duplicated element id: " + species.getElementId()); + } + elementById.put(species.getElementId(), species); + } + List<Element> result = new ArrayList<>(); + + for (Pair<String, AbstractReferenceGlyph> idGlyphPair : getGlyphs(sbmlLayout)) { + String id = idGlyphPair.getLeft(); + Element source = elementById.get(id); + if (source == null) { + throw new InvalidInputDataExecption("Layout contains invalid Species id: " + idGlyphPair.getLeft()); + } + used.add(source); + AbstractReferenceGlyph glyph = idGlyphPair.getRight(); + Element SpeciesWithLayout = source.copy(); + SpeciesWithLayout.setElementId(glyph.getId()); + SpeciesWithLayout.setX(glyph.getBoundingBox().getPosition().getX()); + SpeciesWithLayout.setY(glyph.getBoundingBox().getPosition().getY()); + SpeciesWithLayout.setWidth(glyph.getBoundingBox().getDimensions().getWidth()); + SpeciesWithLayout.setHeight(glyph.getBoundingBox().getDimensions().getHeight()); + result.add(SpeciesWithLayout); + } + for (Element Species : elements) { + if (!used.contains(Species)) { + logger.warn("Layout doesn't contain information about Element: " + Species.getElementId()); + result.add(Species); + } + } + return result; + } + + protected abstract List<Pair<String, AbstractReferenceGlyph>> getGlyphs(Layout sbmlLayout); + + protected abstract Element parse(T species) throws InvalidInputDataExecption; + + protected Set<MiriamData> parseAnnotation(Annotation annotation) { + if (annotation.getCVTermCount() > 0) { + throw new NotImplementedException(); + } + Set<MiriamData> result = new HashSet<>(); + return result; + } + +} 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 357fcfefaf317e53937a7705f6139107c46efda0..7e32722234fe9ed7aaf6598e407dfac2d71d72fa 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 @@ -49,12 +49,14 @@ public class SbmlParser implements IConverter { Layout layout = getSbmlLayout(sbmlModel); SbmlCompartmentParser compartmentParser = new SbmlCompartmentParser(layout); + SbmlSpeciesParser speciesParser = new SbmlSpeciesParser(layout); Set<MiriamData> annotations = compartmentParser.parseAnnotation(sbmlModel.getAnnotation()); if (annotations.size() > 0) { throw new NotImplementedException("Annotations not implemented for model"); } model.addElements(compartmentParser.parseList(sbmlModel)); + model.addElements(speciesParser.parseList(sbmlModel)); if (sbmlModel.getConstraintCount() > 0) { throw new NotImplementedException("Constraints not implemented for model"); } @@ -91,9 +93,6 @@ public class SbmlParser implements IConverter { if (sbmlModel.getRuleCount() > 0) { throw new NotImplementedException("Rule not implemented for model"); } - if (sbmlModel.getSpeciesCount() > 0) { - throw new NotImplementedException("Species not implemented for model"); - } if (sbmlModel.getSpeciesReferenceCount() > 0) { throw new NotImplementedException("SpeciesReference not implemented for model"); } @@ -120,7 +119,7 @@ public class SbmlParser implements IConverter { double minX = Double.MAX_VALUE; double minY = Double.MAX_VALUE; for (Element element : model.getElements()) { - if (element.getX() == null) { + if (element.getWidth() == 0.0) { throw new NotImplementedException("Element without layout not implemented"); } width = Math.max(width, element.getX() + element.getWidth()); @@ -147,7 +146,7 @@ public class SbmlParser implements IConverter { 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 not layouts defined"); + 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); 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 new file mode 100644 index 0000000000000000000000000000000000000000..e5d5547c4de7caab6e0d8323379a5dfd7fb5ecee --- /dev/null +++ b/converter-sbml/src/main/java/lcsb/mapviewer/converter/model/sbml/SbmlSpeciesParser.java @@ -0,0 +1,69 @@ +package lcsb.mapviewer.converter.model.sbml; + +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.List; + +import javax.xml.stream.XMLStreamException; + +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.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.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); + } + + protected Species parse(org.sbml.jsbml.Species species) throws InvalidInputDataExecption { + String type = species.getSpeciesType(); + Class<? extends Species> clazz = null; + if (type == null || type.isEmpty()) { + clazz = Unknown.class; + } + if (clazz == null) { + throw new InvalidInputDataExecption("Unknown species type: " + type); + } + Species result; + try { + result = clazz.getConstructor(String.class).newInstance(species.getId()); + result.setMiriamData(parseAnnotation(species.getAnnotation())); + result.setName(species.getName()); + try { + result.setNotes(species.getNotesString()); + } catch (XMLStreamException e) { + throw new InvalidInputDataExecption(species.getId() + " Invalid Species notes", e); + } + return result; + } catch (SecurityException | NoSuchMethodException | InstantiationException | IllegalAccessException + | IllegalArgumentException | InvocationTargetException e) { + throw new InvalidStateException(e); + } + } + + @Override + protected ListOf<org.sbml.jsbml.Species> getSbmlElementList(Model sbmlModel) { + return sbmlModel.getListOfSpecies(); + } + + @Override + protected List<Pair<String, AbstractReferenceGlyph>> getGlyphs(Layout sbmlLayout) { + List<Pair<String, AbstractReferenceGlyph>> result = new ArrayList<>(); + for (SpeciesGlyph glyph : sbmlLayout.getListOfSpeciesGlyphs()) { + result.add(new Pair<String, AbstractReferenceGlyph>(glyph.getSpecies(), glyph)); + } + return result; + } + +} 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 835fe52df8cb3c10948025d3883a51de1c596c3b..d28517873a9e0e59e7528df3e46041049e2161fa 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 @@ -64,8 +64,6 @@ public class GenericSbmlParserTest { Model model = converter.createModel(new ConverterParams().filename(filePath.toString())); model.setName(null); - Compartment c = new SquareCompartment((Compartment) model.getElements().iterator().next()); - // Create and display image of parsed map AbstractImageGenerator.Params params = new AbstractImageGenerator.Params().height(model.getHeight()) .width(model.getWidth()).nested(true).scale(1).level(20).x(0).y(0).model(model); 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 b0da6b65b91d7c8aada9ef31d8342aa868c2bf50..72a079086fa04dceed1ee2d1033a3e1b12aed2ec 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 @@ -14,6 +14,7 @@ import lcsb.mapviewer.converter.ConverterParams; import lcsb.mapviewer.converter.InvalidInputDataExecption; import lcsb.mapviewer.model.map.compartment.Compartment; import lcsb.mapviewer.model.map.model.Model; +import lcsb.mapviewer.model.map.species.Species; public class SbmlParserTest { Logger logger = Logger.getLogger(SbmlParserTest.class); @@ -37,4 +38,23 @@ public class SbmlParserTest { assertFalse(compartment.getClass().equals(Compartment.class)); } + @Test + public void testParseSpecies() throws FileNotFoundException, InvalidInputDataExecption { + 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); + 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)); + } + + }