diff --git a/README.md b/README.md
index c41e622..26b6261 100644
--- a/README.md
+++ b/README.md
@@ -1,24 +1,49 @@
# SBML2RDF
+```
+ _____ _____ _____ __ ___ _____ ____ _____
+| __| __ | | | |_ | | __ | \| __|
+|__ | __ -| | | | |__ | _| | -| | | __|
+|_____|_____|_|_|_|_____| |___| |__|__|____/|__|
+```
-A simple tool that converts a metabolic network in SBML format into RDF (turtle synthax).
+A tool that converts a metabolic network in SBML format into RDF (turtle synthax).
+SBML2RDF also include an optional model enhancement for knowledge graph, adding links between same compounds in different compartments, direct links between reactants and products of the same reaction (bypassing [specie <- specieRef <- reaction -> specieRef -> specie] paths), and side compounds (also known as, or closely related to : ubiquitous/auxiliary/currency/ancillary compounds) typing from a provided list.
SBML2RDF use the [JSBML](http://sbml.org/Software/JSBML) library for SBML file parsing and the [JENA](https://jena.apache.org/documentation/rdf/index.html) RDF API for building the triples.
SBML2RDF use biomodels' [SBML vocabulary](https://registry.identifiers.org/registry/biomodels.vocabulary) to describe the SBML content.
## Usage
-the SBML2RDF convertor requires a metabolic network in SBML file format and a URI (Uniform Resource Identifiers) that uniquely identify this model
+The SBML2RDF convertor requires a metabolic network in SBML file format and a URI (Uniform Resource Identifiers) that uniquely identify this model
Examples: https://metexplore.toulouse.inra.fr/metexplore2/?idBioSource=1363, https://www.ebi.ac.uk/biomodels/MODEL1311110001
-```
-java -jar SBML2RDF.jar -i path/tp/sbml.xml -u 'http://my.model.uri#id' -o path/to/output.ttl
-
-
- -h (--help) : prints the help (default: false)
- -i (--sbml) VAL : input SBML file
- -o (--ttl) VAL : path to RDF turtle output file
- -s (--silent) : disable console print (default: false)
- -u (--uri) VAL : URI that uniquely identify the model
-```
+ ```
+ java -jar SBML2RDF.jar -i path/to/sbml.xml -u 'http://my.model.uri#id' -o path/to/output.ttl
+ ```
+The final model can be enhanced with extra triples using the following options:
+
+ ```
+ java -jar SBML2RDF.jar -i path/to/sbml.xml -u 'http://my.model.uri#id' -o path/to/output.ttl --linkCompartments --addMetaboLinks --importSideCompounds path/to/side_compounds_file.txt
+ ```
+The side compounds file must contains one entry per line, using the same identifier system as the input sbml. Such list can be defined manually or obtained using the Met4J toolbox.
+The linkCompartments option requires that the SBML's entries of the same compound in different compartments share the same names.
+
+ ```
+ -h (--help) : prints the help (default: true)
+ -i (--sbml) VAL : input SBML file
+ -lc (--linkCompartments) : [enhance] add links between same compounds
+ in different compartments (must share same
+ sbml.name) (default: false)
+ -ml (--addMetaboLinks) : [enhance] add direct "derives into" links
+ between reactants and products of the same
+ reaction (default: false)
+ -o (--ttl) VAL : path to RDF turtle output file
+ -s (--silent) : disable console print (default: false)
+ -sc (--importSideCompounds) VAL : [enhance] add side compounds typing, which
+ are ignored when using --addMetaboLink
+ (recommended). Requires a file with one side
+ compound sbml identifier per line
+ -u (--uri) VAL : URI that uniquely identify the model
+ ```
## Acknowledgment
diff --git a/pom.xml b/pom.xml
index cd2804c..ab98555 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,53 +5,24 @@
4.0.0
fr.metexplore
- SBMLtoRDF
- 1.0
+ SBML2RDF
+ SBML2RDF
+ 1.0-SNAPSHOT
UTF-8
- 11
- 11
+ 17
+ 17
+ A tool that converts a metabolic network in SBML format into RDF
+
-
- org.apache.maven.plugins
- maven-compiler-plugin
-
-
- 11
-
-
-
- org.apache.maven.plugins
- maven-assembly-plugin
-
-
- package
-
- single
-
-
-
-
- App
-
-
-
- jar-with-dependencies
-
- SBML2RDF
- false
-
-
-
-
org.apache.maven.plugins
maven-shade-plugin
- 2.1
+ 3.2.4
@@ -60,10 +31,24 @@
shade
+
+
+ *:*
+
+ META-INF/*.SF
+ META-INF/*.DSA
+ META-INF/*.RSA
+
+
+
App
+
+ true
+ ${project.version}
+
@@ -84,13 +69,12 @@
org.apache.jena
apache-jena-libs
pom
- 4.3.2
+ 4.10.0
-
org.apache.jena
- jena-core
- 4.3.2
+ jena-querybuilder
+ 4.10.0
diff --git a/src/main/java/App.java b/src/main/java/App.java
index 4e85afb..c1ce278 100644
--- a/src/main/java/App.java
+++ b/src/main/java/App.java
@@ -1,4 +1,3 @@
-import org.apache.jena.base.Sys;
import org.apache.jena.riot.RDFDataMgr;
import org.apache.jena.riot.RDFFormat;
import org.kohsuke.args4j.CmdLineException;
@@ -13,6 +12,13 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.Collection;
+import java.util.Set;
+import java.util.stream.Collectors;
/**
* The CLI for the conversion from sbml to turtle RDF
@@ -32,10 +38,22 @@ public class App {
@Option(name = "-s", aliases = {"--silent"},usage = "disable console print", required = false)
private Boolean silent = false;
+ @Option(name = "-lc", aliases = {"--linkCompartments"},usage = "[enhance] add links between same compounds in different compartments (must share same sbml.name)", required = false)
+ private Boolean linkCompartments = false;
+
+ @Option(name = "-ml", aliases = {"--addMetaboLinks"},usage = "[enhance] add direct \"derives into\" links between reactants and products of the same reaction", required = false)
+ private Boolean addMetaboLink = false;
+
+ @Option(name = "-sc", aliases = {"--importSideCompounds"},usage = "[enhance] add side compounds typing, which are ignored when using --addMetaboLink (recommended). Requires a file with one side compound sbml identifier per line", required = false)
+ private String importSideCompounds = null;
+
@Option(name = "-h", aliases = {"--help"},usage = "prints the help", required = false)
private Boolean h = false;
-
+ private static Set parseSideCompoundsFile(String inputpath) throws IOException {
+ Set sideCompounds = Files.lines(Paths.get(inputpath)).collect(Collectors.toSet());
+ return sideCompounds;
+ }
public static void main(String[] args) throws IOException {
@@ -46,6 +64,7 @@ public static void main(String[] args) throws IOException {
try {
//parse SBML using JSBML library
//------------------------------
+ Instant start = Instant.now();
if(!app.silent) System.out.println("parsing model...");
SBMLDocument doc = new SBMLReader().readSBMLFromFile(app.inputPath);
Model sbmlModel = doc.getModel(); //JSBML model stores all data from SBML file
@@ -63,6 +82,41 @@ public static void main(String[] args) throws IOException {
convert.run();
org.apache.jena.rdf.model.Model rdf = convert.getRdfModel();
+ if(!app.silent) System.out.println(rdf.listStatements().toList().size()+" triples");
+
+ // [optional] add extra links:
+ //----------------------------
+ int n = rdf.listStatements().toList().size();
+ if(!app.silent && (app.linkCompartments || app.importSideCompounds!=null || app.addMetaboLink)) System.out.println("[enhance] adding extra triples:");
+
+ // [optional] add links between compartments' compounds
+ //----------------------------------------------------------
+ if(app.linkCompartments){
+ if(!app.silent) System.out.println("[enhance] Harmonizing compartmentalized compound versions...");
+ PropertyFiller.harmonizeCompartments(rdf,false);
+ if(!app.silent) System.out.println((rdf.listStatements().toList().size()-n)+" triples added");
+ n = rdf.listStatements().toList().size();
+ }
+ // [optional] tag side compounds from file
+ //---------------------------------------------
+ if(app.importSideCompounds!=null){
+ if(!app.silent) System.out.println("[enhance] Importing side compounds...");
+ Collection sideCompounds = parseSideCompoundsFile(app.importSideCompounds);
+ if(!app.silent) System.out.println("[enhance] "+sideCompounds.size()+" side compounds imported.");
+ if(!app.silent) System.out.println("[enhance] Tagging reactions' side reactants and side products...");
+ PropertyFiller.importSideCompounds(rdf,sideCompounds);
+ if(!app.silent) System.out.println((rdf.listStatements().toList().size()-n)+" triples added");
+ n = rdf.listStatements().toList().size();
+ }
+ // [optional] add compound-to-compound metabolic relationship
+ //----------------------------------------------------------------
+ if(app.addMetaboLink){
+ if(!app.silent && app.importSideCompounds!=null) System.out.println("[enhance] Adding compound-to-compound metabolic links, ignoring side compounds...");
+ if(!app.silent && app.importSideCompounds==null) System.out.println("[enhance] Adding compound-to-compound metabolic links...");
+ PropertyFiller.addMetaboLinks(rdf,false);
+ if(!app.silent) System.out.println((rdf.listStatements().toList().size()-n)+" triples added");
+ }
+
rdf.setNsPrefix("cid","http://identifiers.org/pubchem.compound/");
rdf.setNsPrefix("chebi","http://identifiers.org/chebi/CHEBI:");
rdf.setNsPrefix("mnxCHEM", "http://identifiers.org/metanetx.chemical/");
@@ -70,11 +124,14 @@ public static void main(String[] args) throws IOException {
if(!app.silent) System.out.println(rdf.listStatements().toList().size()+" triples");
//write RDF model in turtle
- //-----------------------------------
+ //-------------------------
OutputStream out = new FileOutputStream(new File(app.outputPath));
RDFDataMgr.write(out, rdf, RDFFormat.TURTLE);
- if(!app.silent)System.out.println("\nRDF model exported");
- if(!app.silent)System.out.println(app.outputPath);
+ if(!app.silent)System.out.println("\nRDF model exported : "+app.outputPath);
+ Instant end = Instant.now();
+ Duration elapsedTime = Duration.between(start, end);
+
+ System.out.println("Execution time: " + elapsedTime.toSeconds()+"s");
} catch (XMLStreamException e) {
e.printStackTrace();
@@ -92,19 +149,29 @@ public static String getLabel() {return "\n" +
"|_____|_____|_|_|_|_____| |___| |__|__|____/|__| \n";}
public static String getDescription() {return "" +
- "A simple tool that converts a metabolic network in SBML format into RDF (turtle synthax).\n" +
+ "A tool that converts a metabolic network in SBML format into RDF (turtle synthax).\n" +
+ "SBML2RDF also include an optional model enhancement for knowledge graph, adding links between same compounds" +
+ " in different compartments, direct links between reactants and products of the same reaction " +
+ "(bypassing [specie <- specieRef <- reaction -> specieRef -> specie] paths), " +
+ "and side compounds (also known as, or closely related to : ubiquitous/auxiliary/currency/ancillary compounds) typing from a provided list.\n"+
"SBML2RDF use the [JSBML](http://sbml.org/Software/JSBML) library for SBML file parsing and the [JENA](https://jena.apache.org/documentation/rdf/index.html) RDF API for building the triples. \n" +
"SBML2RDF use biomodels' [SBML vocabulary](https://registry.identifiers.org/registry/biomodels.vocabulary) to describe the SBML content. \n";
}
public static String getUsage() {return "" +
- "the SBML2RDF convertor requires a metabolic network in SBML file format and a URI (Uniform Resource Identifiers) that uniquely identify this model\n" +
+ "The SBML2RDF convertor requires a metabolic network in SBML file format and a URI (Uniform Resource Identifiers) that uniquely identify this model\n" +
"Examples: " +
"https://metexplore.toulouse.inra.fr/metexplore2/?idBioSource=1363, " +
"https://www.ebi.ac.uk/biomodels/MODEL1311110001\n" +
"\n\t```" +
- "\n\tjava -jar SBML2RDF.jar -i path/tp/sbml.xml -u 'http://my.model.uri#id' -o path/to/output.ttl" +
- "\n\t```\n";
+ "\n\tjava -jar SBML2RDF.jar -i path/to/sbml.xml -u 'http://my.model.uri#id' -o path/to/output.ttl" +
+ "\n\t```\n"+
+ "The final model can be enhanced with extra triples using the following options:\n" +
+ "\n\t```" +
+ "\n\tjava -jar SBML2RDF.jar -i path/to/sbml.xml -u 'http://my.model.uri#id' -o path/to/output.ttl --linkCompartments --addMetaboLinks --importSideCompounds path/to/side_compounds_file.txt" +
+ "\n\t```\n" +
+ "The side compounds file must contains one entry per line, using the same identifier system as the input sbml. Such list can be defined manually or obtained using the Met4J toolbox.\n" +
+ "The linkCompartments option requires that the SBML's entries of the same compound in different compartments share the same names.\n\n";
}
@@ -133,5 +200,12 @@ protected void parseArguments(String[] args) {
System.exit(0);
}
}
+
+ if (this.h == true) {
+ this.printHeader();
+ parser.printUsage(System.out);
+ System.exit(0);
+ }
}
+
}
diff --git a/src/main/java/Convertor.java b/src/main/java/Convertor.java
index 527f427..b07bf68 100644
--- a/src/main/java/Convertor.java
+++ b/src/main/java/Convertor.java
@@ -3,6 +3,7 @@
import org.apache.jena.rdf.model.Resource;
import org.apache.jena.rdf.model.ResourceFactory;
import org.apache.jena.vocabulary.RDF;
+import org.apache.jena.vocabulary.RDFS;
import org.sbml.jsbml.*;
import org.sbml.jsbml.ext.fbc.*;
import vocabulary.SBMLRDF;
@@ -42,11 +43,12 @@ public class Convertor {
*/
public Convertor(org.sbml.jsbml.Model sbmlModel, org.apache.jena.rdf.model.Model rdfModel, String modelURI ) {
this.sbmlModel = sbmlModel;
- this.modelNamespace = modelURI+"#";
+ modelNamespace = modelURI+"#";
this.rdfModel = rdfModel;
- this.rdfModel.setNsPrefix(modelPrefix,modelNamespace);
+ this.rdfModel.setNsPrefix(SBMLRDF.getPREFIX(),SBMLRDF.getURI());
this.rdfModel.setNsPrefix(biomodelPrefix,SBMLRDF.BQURI);
+ this.rdfModel.setNsPrefix(modelPrefix,modelNamespace);
this.sbmlResource=createModelResource(sbmlModel);
}
@@ -62,24 +64,18 @@ public Convertor(org.sbml.jsbml.Model sbmlModel, org.apache.jena.rdf.model.Model
* @param modelURI the URI of the model
*/
public Convertor(org.sbml.jsbml.Model sbmlModel, String modelURI) {
- this.sbmlModel = sbmlModel;
- modelNamespace = modelURI+"#";
-
- this.rdfModel = ModelFactory.createDefaultModel();
- this.rdfModel.setNsPrefix(SBMLRDF.getPREFIX(),SBMLRDF.getURI());
- this.rdfModel.setNsPrefix(biomodelPrefix,SBMLRDF.BQURI);
- this.rdfModel.setNsPrefix(modelPrefix,modelNamespace);
-
- this.sbmlResource=createModelResource(sbmlModel);
+ this(sbmlModel, ModelFactory.createDefaultModel(),modelURI);
}
// Generate a Resource from a sbml id, using predefined base URI
- private Resource initResource(String id){
- return rdfModel.createResource(modelNamespace + id);
+ private Resource initResource(AbstractSBase sbmlEntry){
+ Resource node = rdfModel.createResource(modelNamespace + sbmlEntry.getMetaId());
+ node.addProperty(RDFS.label,sbmlEntry.getId());
+ return node;
}
private Resource createModelResource(Model sbmlModel){
- Resource sbmlResource = initResource(sbmlModel.getMetaId());
+ Resource sbmlResource = initResource(sbmlModel);
sbmlResource.addProperty(RDF.type,SBMLRDF.SBMLMODEL);
sbmlResource.addProperty(SBMLRDF.NAME,sbmlModel.getName());
createAnnotation(sbmlResource,sbmlModel);
@@ -87,7 +83,7 @@ private Resource createModelResource(Model sbmlModel){
}
private Resource createCompartmentResource(Compartment sbmlCompartment){
- Resource compartment = initResource(sbmlCompartment.getMetaId());
+ Resource compartment = initResource(sbmlCompartment);
compartment.addProperty(RDF.type, SBMLRDF.COMPARTMENT);
compartment.addProperty(SBMLRDF.NAME, sbmlCompartment.getName());
createAnnotation(compartment, sbmlCompartment);
@@ -95,7 +91,7 @@ private Resource createCompartmentResource(Compartment sbmlCompartment){
}
private Resource createSpeciesResource(Species sbmlSpecie){
- Resource specie = initResource(sbmlSpecie.getMetaId());
+ Resource specie = initResource(sbmlSpecie);
specie.addProperty(RDF.type, SBMLRDF.SPECIE);
specie.addProperty(SBMLRDF.NAME,sbmlSpecie.getName());
specie.addProperty(SBMLRDF.HAS_COMPARTMENT,rdfModel.createResource(modelNamespace + sbmlSpecie.getCompartmentInstance().getMetaId()));
@@ -109,7 +105,7 @@ private Resource createSpeciesReferenceResource(SpeciesReference sbmlSpecieRef){
//create blank node
specieRef=rdfModel.createResource();
}else{
- specieRef = initResource(sbmlSpecieRef.getMetaId());
+ specieRef = initResource(sbmlSpecieRef);
}
specieRef.addProperty(RDF.type, SBMLRDF.SPECIESREF);
@@ -119,7 +115,7 @@ private Resource createSpeciesReferenceResource(SpeciesReference sbmlSpecieRef){
}
private Resource createReactionResource(Reaction sbmlReaction){
- Resource reaction = initResource(sbmlReaction.getMetaId());
+ Resource reaction = initResource(sbmlReaction);
reaction.addProperty(RDF.type, SBMLRDF.REACTION);
reaction.addProperty(SBMLRDF.NAME,sbmlReaction.getName());
reaction.addLiteral(SBMLRDF.REVERSIBLE,sbmlReaction.getReversible());
@@ -208,7 +204,7 @@ public void convertGenes(){
for (GeneProduct sbmlGene : fbcParser.getListOfGeneProducts()){
- Resource gene = initResource(sbmlGene.getMetaId());
+ Resource gene = initResource(sbmlGene);
gene.addProperty(RDF.type, geneProduct);
gene.addProperty(SBMLRDF.NAME,sbmlGene.getLabel());
createAnnotation(gene,sbmlGene);
diff --git a/src/main/java/PropertyFiller.java b/src/main/java/PropertyFiller.java
new file mode 100644
index 0000000..4f37a0a
--- /dev/null
+++ b/src/main/java/PropertyFiller.java
@@ -0,0 +1,166 @@
+import org.apache.jena.arq.querybuilder.ConstructBuilder;
+import org.apache.jena.arq.querybuilder.SelectBuilder;
+import org.apache.jena.arq.querybuilder.WhereBuilder;
+import org.apache.jena.query.QueryExecution;
+import org.apache.jena.query.QueryExecutionFactory;
+import org.apache.jena.rdf.model.*;
+import org.apache.jena.sparql.core.Var;
+import org.apache.jena.sparql.expr.E_NotExists;
+import org.apache.jena.sparql.expr.E_OneOf;
+import org.apache.jena.sparql.expr.ExprList;
+import org.apache.jena.sparql.expr.ExprVar;
+import org.apache.jena.sparql.expr.nodevalue.NodeValueString;
+import org.apache.jena.sparql.syntax.ElementSubQuery;
+import org.apache.jena.vocabulary.OWL;
+import org.apache.jena.vocabulary.RDF;
+import org.apache.jena.vocabulary.RDFS;
+import vocabulary.SBMLRDF;
+
+import java.util.Collection;
+
+/**
+ * This class constructs statements beyond what is typically explicitly reported in SBML files, which still may be proven
+ * relevant in a knowledge graph context
+ */
+public class PropertyFiller {
+
+ /**
+ * Add statements about the relatedness of two SBML entries that describe the same compound in different compartments.
+ * The link construction assume that each located version of a given compound share the same name.
+ * @param rdfModel the model
+ * @param useSameAs if the relatedness should use the owl:sameAs property
+ */
+ public static void harmonizeCompartments(org.apache.jena.rdf.model.Model rdfModel, boolean useSameAs){
+ //define variables
+ Var compound1 = Var.alloc("c1");
+ Var compound2 = Var.alloc("c2");
+ Var name = Var.alloc("n");
+
+ //select property to use in construct
+ if (!useSameAs) rdfModel.setNsPrefix(SBMLRDF.SIOPREFIX,SBMLRDF.SIOURI);
+ Property link = useSameAs ? OWL.sameAs : SBMLRDF.IS_VARIANT_OF;
+
+ //build query
+ ConstructBuilder qb = new ConstructBuilder()
+ .addConstruct(compound1,link,compound2) //define statements to build
+ .addWhere(compound1, RDF.type, SBMLRDF.SPECIE) //looks for one resource that is a sbml specie
+ .addWhere(compound2, RDF.type, SBMLRDF.SPECIE) //looks for a second resource that is a sbml species
+ .addWhere(compound1, SBMLRDF.NAME, name) //looks for first specie's name
+ .addWhere(compound2, SBMLRDF.NAME, name) //looks for second species' name that match first's one
+ .addFilter("?c1 != ?c2"); // filter cases where the first and second species are the same
+
+ //execute query and add results to current model
+ QueryExecution qexec = QueryExecutionFactory.create(qb.build(),rdfModel);
+ Model res = qexec.execConstruct();
+ rdfModel.add(res);
+ qexec.close();
+
+ }
+
+ /**
+ * Add statements about compound-to-compound metabolic relatedness. Two compounds are related if one is consumed by
+ * a reaction that produce the other. This is equivalent to building a compound graph, and conveniently bypass the
+ * [specie <- specieRef <- reaction -> specieRef -> specie] property paths.
+ * @param rdfModel the model
+ * @param useTransitive if a transitive property should be use (prior flagging of side compounds is recommended)
+ */
+ public static void addMetaboLinks(org.apache.jena.rdf.model.Model rdfModel, Boolean useTransitive){
+ //select property to use in construct
+ rdfModel.setNsPrefix(SBMLRDF.SIOPREFIX,SBMLRDF.SIOURI);
+ Property metabolink = useTransitive ? SBMLRDF.DERIVES_INTO : SBMLRDF.IMMEDIATELY_DERIVES_INTO;
+
+ //define variables
+ Var reaction = Var.alloc("r");
+ Var reactant = Var.alloc("s");
+ Var product = Var.alloc("p");
+ Var reactantParticipant = Var.alloc("sp");
+ Var productParticipant = Var.alloc("pp");
+
+ //build query
+ // define side compounds filtering (ignoring metabolic relatedness between side compounds and other reactants/products)
+ SelectBuilder findSidesProd = new SelectBuilder()
+ .addWhere(productParticipant,RDF.type,SBMLRDF.SIDEPRODUCT);
+ SelectBuilder findSidesReact = new SelectBuilder()
+ .addWhere(reactantParticipant,RDF.type,SBMLRDF.SIDEREACTANT);
+
+ // define new statements pattern
+ ConstructBuilder qb = new ConstructBuilder()
+ .addConstruct(reactant,metabolink,product);
+
+ //define
+ WhereBuilder wb = new WhereBuilder()
+ .addWhere(reaction,SBMLRDF.REACTANT,reactantParticipant) //looks for the reactant specieRef of a reaction
+ .addWhere(reactantParticipant,SBMLRDF.HAS_SPECIE,reactant) //looks for its referenced specie
+ .addWhere(reaction,SBMLRDF.PRODUCT,productParticipant) //looks for the product specieRef of a reaction
+ .addWhere(productParticipant,SBMLRDF.HAS_SPECIE,product) //looks for its referenced specie
+ .addFilter(new E_NotExists(new ElementSubQuery(findSidesProd.build()))) //ensure product specieRef isn't a "side"
+ .addFilter(new E_NotExists(new ElementSubQuery(findSidesReact.build()))); //ensure reactant specieRef isn't a "side"
+
+ qb.addWhere(wb);
+
+ //execute query and add results to current model
+ QueryExecution qexec = QueryExecutionFactory.create(qb.build(),rdfModel);
+ Model res = qexec.execConstruct(rdfModel);
+ qexec.close();
+
+ //build new query to add reversed statement when a reaction is reversible
+ qb = new ConstructBuilder()
+ .addConstruct(product,metabolink,reactant);
+ wb.addWhere(reaction,SBMLRDF.REVERSIBLE,true);
+
+ qb.addWhere(wb);
+
+ //execute query and add results to current model
+ qexec = QueryExecutionFactory.create(qb.build(),rdfModel);
+ res = qexec.execConstruct();
+ rdfModel.add(res);
+ qexec.close();
+ }
+
+ /**
+ * From a list of side compounds (also known as, or closely related to : ubiquitous/auxiliary/ancillary compounds or currency metabolites), types speciesRefs as sideReactant or sideProduct.
+ * @param rdfModel the model
+ * @param sideCompoundIds a collection of side compounds identifiers, using the same identifier system as the input sbml model
+ */
+ public static void importSideCompounds(org.apache.jena.rdf.model.Model rdfModel, Collection sideCompoundIds){
+
+ //define variables
+ Var participant = Var.alloc("p");
+ Var metabolite = Var.alloc("m");
+ Var compoundId = Var.alloc("l");
+
+ //build query
+ // create sub-query for checking if a compound's id is in the side compound id list
+ ExprList sideCompoundsIdsSet = new ExprList();
+ for(String id : sideCompoundIds){
+ sideCompoundsIdsSet.add(new NodeValueString(id));
+ }
+ E_OneOf labelInList = new E_OneOf(new ExprVar(compoundId),sideCompoundsIdsSet);
+
+ // create construct clause adding side product/reactant type to specieref
+ ConstructBuilder sideProductBuilderTemplate = new ConstructBuilder()
+ .addConstruct(participant,RDF.type,SBMLRDF.SIDEPRODUCT);
+ ConstructBuilder sideReactantBuilderTemplate = new ConstructBuilder()
+ .addConstruct(participant,RDF.type,SBMLRDF.SIDEREACTANT);
+
+ // create where clauses to retrieve side compounds
+ sideReactantBuilderTemplate.addWhere(metabolite, RDFS.label, compoundId) //looks for metabolites with sbml identifiers
+ .addWhere(participant,SBMLRDF.HAS_SPECIE,metabolite) //looks for specieRef of metabolite
+ .addWhere(null,SBMLRDF.REACTANT,participant) //looks if reactant of a reaction
+ .addFilter(labelInList); //check if identifier is in side compounds id list
+ sideProductBuilderTemplate.addWhere(metabolite, RDFS.label, compoundId) //looks for metabolites with sbml identifiers
+ .addWhere(participant,SBMLRDF.HAS_SPECIE,metabolite) //looks for specieRef of metabolite
+ .addWhere(null,SBMLRDF.PRODUCT,participant) //looks if product of a reaction
+ .addFilter(labelInList); //check if identifier is in side compounds id list
+
+ //execute queries and add results to current model
+ QueryExecution qexec = QueryExecutionFactory.create(sideProductBuilderTemplate.build(),rdfModel);
+ Model res = qexec.execConstruct(rdfModel);
+ qexec.close();
+ qexec = QueryExecutionFactory.create(sideReactantBuilderTemplate.build(),rdfModel);
+ res = qexec.execConstruct();
+ rdfModel.add(res);
+ qexec.close();
+ }
+
+}
diff --git a/src/main/java/vocabulary/SBMLRDF.java b/src/main/java/vocabulary/SBMLRDF.java
index 547b0ea..6335648 100644
--- a/src/main/java/vocabulary/SBMLRDF.java
+++ b/src/main/java/vocabulary/SBMLRDF.java
@@ -13,6 +13,9 @@ public class SBMLRDF {
* The namespace of the vocabulary as a string
*/
public static final String BQURI ="http://biomodels.net/biology-qualifiers#";
+ public static final String SBOURI ="http://biomodels.net/SBO#";
+ public static final String SIOURI = "http://semanticscience.org/resource#";
+ public static final String SIOPREFIX = "sio";
public static final String NS = "http://identifiers.org/biomodels.vocabulary#";
public static final String PREFIX = "SBMLrdf";
@@ -56,4 +59,12 @@ protected static final Property property(String local) {
public final static Property REACTANT = property( "reactant" );
public final static Property REVERSIBLE = property( "isReversible" );
public final static Property STOICHIOMETRY = property( "stoichiometry" );
+
+ //non-sbml types and properties for model enhancement
+ public final static Property DERIVES_INTO = ResourceFactory.createProperty(SIOURI, "SIO_000245");
+ public final static Property IMMEDIATELY_DERIVES_INTO = ResourceFactory.createProperty(SIOURI, "SIO_000246");
+ public final static Property IS_VARIANT_OF = ResourceFactory.createProperty(SIOURI, "SIO_000272");
+
+ public static Resource SIDEREACTANT = ResourceFactory.createResource(SBOURI+"SBO_0000604");
+ public static Resource SIDEPRODUCT = ResourceFactory.createResource(SBOURI+"SBO_0000603");
}
\ No newline at end of file
diff --git a/src/test/java/ConvertorTest.java b/src/test/java/ConvertorTest.java
new file mode 100644
index 0000000..5d721fb
--- /dev/null
+++ b/src/test/java/ConvertorTest.java
@@ -0,0 +1,338 @@
+import org.apache.jena.rdf.model.*;
+import org.apache.jena.vocabulary.RDF;
+import org.apache.jena.vocabulary.RDFS;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sbml.jsbml.Model;
+import org.sbml.jsbml.*;
+import org.sbml.jsbml.ext.fbc.*;
+import vocabulary.SBMLRDF;
+
+import javax.xml.stream.XMLStreamException;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class ConvertorTest {
+
+ public SBMLDocument doc;
+ Model model;
+ Compartment cmp1, cmp2, cmp3;
+ Species a1, a2, b1, b2, c1, c2, d , e;
+ Reaction r1, r1_2, r2, r3, rta, rtc;
+ GeneProduct g1;
+
+ @Rule
+ public ExpectedException exception = ExpectedException.none();
+
+ @Before
+ public void init() throws XMLStreamException {
+ doc = new SBMLDocument(3, 2);
+ initModel();
+ }
+
+ private void initModel() {
+
+ model = doc.createModel();
+
+ model.setId("modelId");
+ model.setMetaId("modelId");
+ model.setName("modelName");
+
+ cmp1 = model.createCompartment("cmp1");
+ cmp1.setName("compartment1");
+ cmp1.setMetaId(cmp1.getId());
+ cmp2 = model.createCompartment("cmp2");
+ cmp2.setName("compartment2");
+ cmp2.setMetaId(cmp2.getId());
+ cmp3 = model.createCompartment("cmp3");
+ cmp3.setMetaId(cmp3.getId());
+
+ CompartmentType compartmentType = new CompartmentType("cType");
+ model.addCompartmentType(compartmentType);
+ cmp1.setCompartmentType(compartmentType);
+
+ cmp1.setOutside(cmp2);
+ cmp1.setOutside(cmp1);
+
+ cmp1.setSize(2.0);
+ cmp1.setSpatialDimensions(4.0);
+
+ a1 = model.createSpecies("a1", "A", cmp1);a1.setMetaId(a1.getId());
+ a2 = model.createSpecies("a2", "A", cmp2);a2.setMetaId(a2.getId());
+ b1 = model.createSpecies("b1", "B", cmp1);b1.setMetaId(b1.getId());
+ b2 = model.createSpecies("b2", "B", cmp2);b2.setMetaId(b2.getId());
+ c1 = model.createSpecies("c1", "C", cmp1);c1.setMetaId(c1.getId());
+ c2 = model.createSpecies("c2", "C", cmp2);c2.setMetaId(c2.getId());
+ e = model.createSpecies("e", "E", cmp2);e.setMetaId(e.getId());
+ d = model.createSpecies("d", "D", cmp1);d.setMetaId(d.getId());
+
+ a1.setConstant(true);
+ c2.setConstant(false);
+ e.setInitialAmount(2.0);
+ d.setInitialAmount(3.0);
+
+ Annotation annotation = new Annotation();
+ CVTerm cvterm = new CVTerm();
+ cvterm.addResource("https://identifiers.org/SBO_0000299");
+ cvterm.setQualifierType(org.sbml.jsbml.CVTerm.Type.BIOLOGICAL_QUALIFIER);
+ cvterm.setBiologicalQualifierType(CVTerm.Qualifier.BQB_IS);
+ annotation.addCVTerm(cvterm);
+
+ a1.setAnnotation(annotation);
+ a1.setSBOTerm("SBO:0000299");
+ b1.setSBOTerm("SBO:0000299");
+ c1.setSBOTerm("SBO:0000299");
+ d.setSBOTerm("SBO:0000299");
+ e.setSBOTerm("SBO:0000299");
+
+
+ SpeciesReference a1r1= new SpeciesReference(a1);a1r1.setStoichiometry(2.0);
+ SpeciesReference a2r1_2= new SpeciesReference(a2);a2r1_2.setStoichiometry(2.0);
+ SpeciesReference b1r1= new SpeciesReference(b1);
+ SpeciesReference b2r1_2= new SpeciesReference(b2);b2r1_2.setStoichiometry(1.0);
+ SpeciesReference c1r1= new SpeciesReference(c1);
+ SpeciesReference c1r2= new SpeciesReference(c1);c1r2.setStoichiometry(4.0);
+ SpeciesReference c2r1_2= new SpeciesReference(c2);
+ SpeciesReference c2r3= new SpeciesReference(c2);
+ SpeciesReference dr2= new SpeciesReference(d);dr2.setStoichiometry(4.0);
+ SpeciesReference er3= new SpeciesReference(e);
+ dr2.setConstant(true);
+ er3.setConstant(false);
+
+ r1 = model.createReaction("r1");
+ r1.setName("name1");
+ r1.setMetaId(r1.getId());
+ r1.setReversible(false);
+ r1.setSBOTerm("SBO:0000176");
+ r1.addReactant(a1r1);
+ r1.addProduct(b1r1);
+ r1.addProduct(c1r1);
+
+ r1_2 = model.createReaction("r1_2");
+ r1_2.setName("name1");
+ r1_2.setMetaId(r1_2.getId());
+ r1_2.setReversible(false);
+ r1_2.setSBOTerm("SBO:0000176");
+ r1_2.addReactant(a2r1_2);
+ r1_2.addProduct(b2r1_2);
+ r1_2.addProduct(c2r1_2);
+
+ r2 = model.createReaction("r2");
+ r2.setMetaId(r2.getId());
+ r2.addReactant(c1r2);
+ r2.addProduct(dr2);
+ r2.setReversible(true);
+ r2.setSBOTerm(0000167);
+
+ r3 = model.createReaction("r3");
+ r3.setMetaId(r3.getId());
+ r3.addReactant(c2r3);
+ r3.addProduct(er3);
+
+ rta = model.createReaction("rta");
+ rta.setMetaId(rta.getId());
+ rta.setName("transport-a");
+ rta.setReversible(true);
+ rta.setSBOTerm("SBO:0000167");
+ rta.addReactant(new SpeciesReference(a1r1));
+ rta.addProduct(new SpeciesReference(a2r1_2));
+
+ rtc = model.createReaction("rtc");
+ rtc.setMetaId(rtc.getId());
+ rtc.setName("transport-c");
+ rtc.setReversible(true);
+ rtc.setSBOTerm(0000167);
+ rtc.addProduct(new SpeciesReference(c1r1));
+ rtc.addReactant(new SpeciesReference(c2r1_2));
+
+
+ FBCModelPlugin fbcModel = (FBCModelPlugin) model.getPlugin("http://www.sbml.org/sbml/level3/version1/fbc/version2");
+ g1 = fbcModel.createGeneProduct();
+ g1.setId("g1");
+ g1.setMetaId("g1");
+ g1.setName("g1");
+ g1.setLabel("g1");
+ fbcModel.addGeneProduct(g1); // WARN (AbstractSBase.java:2189) - Trying to register geneProduct which is already registered under listOfGeneProducts ?
+
+ GeneProductRef geneRef1 = new GeneProductRef("g1r");
+ geneRef1.setGeneProduct("g1");
+
+ FBCReactionPlugin rxnPlugin = (FBCReactionPlugin) r1.getPlugin("fbc");
+ GeneProductAssociation GPA = rxnPlugin.createGeneProductAssociation();
+ GPA.setAssociation(geneRef1);
+
+ }
+
+ @Test
+ public void testRun(){
+ String baseUri = "org.mytest";
+ Convertor conv = new Convertor(model,baseUri);
+ conv.run();
+ org.apache.jena.rdf.model.Model rdf = conv.getRdfModel();
+
+ assertFalse(rdf.containsResource(ResourceFactory.createResource((baseUri+"#foo"))));
+ //build metab node
+ Resource a1node = ResourceFactory.createResource((baseUri + "#" + a1.getId()));
+ Resource a2node = ResourceFactory.createResource((baseUri+"#"+a2.getId()));
+ Resource b1node = ResourceFactory.createResource((baseUri+"#"+b1.getId()));
+ Resource b2node = ResourceFactory.createResource((baseUri+"#"+b2.getId()));
+ Resource c1node = ResourceFactory.createResource((baseUri+"#"+c1.getId()));
+ Resource c2node = ResourceFactory.createResource((baseUri+"#"+c2.getId()));
+ Resource dnode = ResourceFactory.createResource((baseUri+"#"+d.getId()));
+ Resource enode = ResourceFactory.createResource((baseUri+"#"+e.getId()));
+ //build compartment node
+ Resource cmp1node = ResourceFactory.createResource((baseUri+"#"+cmp1.getId()));
+ Resource cmp2node = ResourceFactory.createResource((baseUri+"#"+cmp2.getId()));
+ Resource cmp3node = ResourceFactory.createResource((baseUri+"#"+cmp3.getId()));
+ //build reaction node
+ Resource r1node = ResourceFactory.createResource((baseUri+"#"+r1.getId()));
+ Resource r1_2node = ResourceFactory.createResource((baseUri+"#"+r1_2.getId()));
+ Resource r2node = ResourceFactory.createResource((baseUri+"#"+r2.getId()));
+ Resource r3node = ResourceFactory.createResource((baseUri+"#"+r3.getId()));
+ Resource rtcnode = ResourceFactory.createResource((baseUri+"#"+rtc.getId()));
+ Resource rtanode = ResourceFactory.createResource((baseUri+"#"+rta.getId()));
+ Resource g1node = ResourceFactory.createResource((baseUri+"#"+g1.getId()));
+
+ //CHECK EXISTENCE OF METAB + COMP + location
+ assertTrue(rdf.contains(cmp1node, RDF.type,SBMLRDF.COMPARTMENT));
+ assertTrue(rdf.contains(cmp2node, RDF.type,SBMLRDF.COMPARTMENT));
+ assertTrue(rdf.contains(cmp3node, RDF.type,SBMLRDF.COMPARTMENT));
+ assertTrue(rdf.contains(a1node, RDF.type,SBMLRDF.SPECIE));
+ assertTrue(rdf.contains(a2node, RDF.type,SBMLRDF.SPECIE));
+ assertTrue(rdf.contains(b1node, RDF.type,SBMLRDF.SPECIE));
+ assertTrue(rdf.contains(b2node, RDF.type,SBMLRDF.SPECIE));
+ assertTrue(rdf.contains(c1node, RDF.type,SBMLRDF.SPECIE));
+ assertTrue(rdf.contains(c2node, RDF.type,SBMLRDF.SPECIE));
+ assertTrue(rdf.contains(dnode, RDF.type,SBMLRDF.SPECIE));
+ assertTrue(rdf.contains(enode, RDF.type,SBMLRDF.SPECIE));
+ assertTrue(rdf.contains(r1node, RDF.type,SBMLRDF.REACTION));
+ assertTrue(rdf.contains(r1_2node, RDF.type,SBMLRDF.REACTION));
+ assertTrue(rdf.contains(r2node, RDF.type,SBMLRDF.REACTION));
+ assertTrue(rdf.contains(r3node, RDF.type,SBMLRDF.REACTION));
+ assertTrue(rdf.contains(rtanode, RDF.type,SBMLRDF.REACTION));
+ assertTrue(rdf.contains(rtcnode, RDF.type,SBMLRDF.REACTION));
+
+ assertTrue(rdf.contains(a1node,SBMLRDF.NAME,"A"));
+ assertTrue(rdf.contains(a2node,SBMLRDF.NAME,"A"));
+ assertTrue(rdf.contains(b1node,SBMLRDF.NAME,"B"));
+ assertTrue(rdf.contains(b2node,SBMLRDF.NAME,"B"));
+ assertTrue(rdf.contains(c1node,SBMLRDF.NAME,"C"));
+ assertTrue(rdf.contains(c2node,SBMLRDF.NAME,"C"));
+ assertTrue(rdf.contains(dnode,SBMLRDF.NAME,"D"));
+ assertTrue(rdf.contains(enode,SBMLRDF.NAME,"E"));
+
+ assertTrue(rdf.contains(a1node,RDFS.label,"a1"));
+ assertTrue(rdf.contains(a2node,RDFS.label,"a2"));
+ assertTrue(rdf.contains(b1node,RDFS.label,"b1"));
+ assertTrue(rdf.contains(b2node,RDFS.label,"b2"));
+ assertTrue(rdf.contains(c1node,RDFS.label,"c1"));
+ assertTrue(rdf.contains(c2node,RDFS.label,"c2"));
+ assertTrue(rdf.contains(dnode,RDFS.label,"d"));
+ assertTrue(rdf.contains(enode,RDFS.label,"e"));
+
+ assertTrue(rdf.contains(a1node,SBMLRDF.HAS_COMPARTMENT,cmp1node));
+ assertTrue(rdf.contains(a2node,SBMLRDF.HAS_COMPARTMENT,cmp2node));
+ assertTrue(rdf.contains(b1node,SBMLRDF.HAS_COMPARTMENT,cmp1node));
+ assertTrue(rdf.contains(b2node,SBMLRDF.HAS_COMPARTMENT,cmp2node));
+ assertTrue(rdf.contains(c1node,SBMLRDF.HAS_COMPARTMENT,cmp1node));
+ assertTrue(rdf.contains(c2node,SBMLRDF.HAS_COMPARTMENT,cmp2node));
+ assertTrue(rdf.contains(dnode,SBMLRDF.HAS_COMPARTMENT,cmp1node));
+ assertTrue(rdf.contains(enode,SBMLRDF.HAS_COMPARTMENT,cmp2node));
+ assertFalse(rdf.contains(a1node,SBMLRDF.HAS_COMPARTMENT,cmp2node));
+ assertFalse(rdf.contains(a2node,SBMLRDF.HAS_COMPARTMENT,cmp1node));
+ assertFalse(rdf.contains(b1node,SBMLRDF.HAS_COMPARTMENT,cmp2node));
+ assertFalse(rdf.contains(b2node,SBMLRDF.HAS_COMPARTMENT,cmp1node));
+ assertFalse(rdf.contains(c1node,SBMLRDF.HAS_COMPARTMENT,cmp2node));
+ assertFalse(rdf.contains(c2node,SBMLRDF.HAS_COMPARTMENT,cmp1node));
+ assertFalse(rdf.contains(dnode,SBMLRDF.HAS_COMPARTMENT,cmp2node));
+ assertFalse(rdf.contains(enode,SBMLRDF.HAS_COMPARTMENT,cmp1node));
+
+ assertTrue(rdf.containsResource(cmp3node));
+
+ //test R1 comp1
+ List stmnts = rdf.listObjectsOfProperty(r1node,SBMLRDF.REACTANT).toList();
+ assertTrue(stmnts.size()==1);
+ assertTrue(rdf.contains(stmnts.get(0).asResource(),SBMLRDF.HAS_SPECIE,a1node));
+
+ stmnts = rdf.listObjectsOfProperty(r1node,SBMLRDF.PRODUCT).toList();
+ assertTrue(stmnts.size()==2);
+ List objects = stmnts.stream().map(n -> rdf.listObjectsOfProperty(n.asResource(), SBMLRDF.HAS_SPECIE).toList()).flatMap(List::stream).collect(Collectors.toList());
+ assertTrue(objects.contains(b1node));
+ assertTrue(objects.contains(c1node));
+
+ assertFalse(rdf.containsLiteral(r1node,SBMLRDF.REVERSIBLE,true));
+ assertTrue(rdf.containsLiteral(r1node,SBMLRDF.REVERSIBLE,false));
+
+ Resource geneProduct = rdf.createProperty(model.getPlugin("fbc").getURI()+"#geneProduct");
+ Property geneProductAssociation = rdf.createProperty(model.getPlugin("fbc").getURI()+"#geneProductAssociation");
+ assertTrue(rdf.contains(g1node,RDF.type,geneProduct));
+ assertTrue(rdf.contains(r1node,geneProductAssociation,g1node));
+
+ //test R1 comp2
+ stmnts = rdf.listObjectsOfProperty(r1_2node,SBMLRDF.REACTANT).toList();
+ assertTrue(stmnts.size()==1);
+ assertTrue(rdf.contains(stmnts.get(0).asResource(),SBMLRDF.HAS_SPECIE,a2node));
+
+ stmnts = rdf.listObjectsOfProperty(r1_2node,SBMLRDF.PRODUCT).toList();
+ assertTrue(stmnts.size()==2);
+ objects = stmnts.stream().map(n -> rdf.listObjectsOfProperty(n.asResource(), SBMLRDF.HAS_SPECIE).toList()).flatMap(List::stream).collect(Collectors.toList());
+ assertTrue(objects.contains(b2node));
+ assertTrue(objects.contains(c2node));
+
+ assertFalse(rdf.containsLiteral(r1_2node,SBMLRDF.REVERSIBLE,true));
+ assertTrue(rdf.containsLiteral(r1_2node,SBMLRDF.REVERSIBLE,false));
+
+ //test R2
+ stmnts = rdf.listObjectsOfProperty(r2node,SBMLRDF.REACTANT).toList();
+ assertTrue(stmnts.size()==1);
+ assertTrue(rdf.contains(stmnts.get(0).asResource(),SBMLRDF.HAS_SPECIE,c1node));
+
+ stmnts = rdf.listObjectsOfProperty(r2node,SBMLRDF.PRODUCT).toList();
+ assertTrue(stmnts.size()==1);
+ assertTrue(rdf.contains(stmnts.get(0).asResource(),SBMLRDF.HAS_SPECIE,dnode));
+
+ assertTrue(rdf.containsLiteral(r2node,SBMLRDF.REVERSIBLE,true));
+ assertFalse(rdf.containsLiteral(r2node,SBMLRDF.REVERSIBLE,false));
+
+ //test R3
+ stmnts = rdf.listObjectsOfProperty(r3node,SBMLRDF.REACTANT).toList();
+ assertTrue(stmnts.size()==1);
+ assertTrue(rdf.contains(stmnts.get(0).asResource(),SBMLRDF.HAS_SPECIE,c2node));
+
+ stmnts = rdf.listObjectsOfProperty(r3node,SBMLRDF.PRODUCT).toList();
+ assertTrue(stmnts.size()==1);
+ assertTrue(rdf.contains(stmnts.get(0).asResource(),SBMLRDF.HAS_SPECIE,enode));
+
+ assertTrue(rdf.containsLiteral(r3node,SBMLRDF.REVERSIBLE,true));
+ assertFalse(rdf.containsLiteral(r3node,SBMLRDF.REVERSIBLE,false));
+
+ //test R transport C
+ stmnts = rdf.listObjectsOfProperty(rtcnode,SBMLRDF.REACTANT).toList();
+ assertTrue(stmnts.size()==1);
+ assertTrue(rdf.contains(stmnts.get(0).asResource(),SBMLRDF.HAS_SPECIE,c2node));
+
+ stmnts = rdf.listObjectsOfProperty(rtcnode,SBMLRDF.PRODUCT).toList();
+ assertTrue(stmnts.size()==1);
+ assertTrue(rdf.contains(stmnts.get(0).asResource(),SBMLRDF.HAS_SPECIE,c1node));
+
+ assertTrue(rdf.containsLiteral(rtcnode,SBMLRDF.REVERSIBLE,true));
+ assertFalse(rdf.containsLiteral(rtcnode,SBMLRDF.REVERSIBLE,false));
+
+ //test R transport A
+ stmnts = rdf.listObjectsOfProperty(rtanode,SBMLRDF.REACTANT).toList();
+ assertTrue(stmnts.size()==1);
+ assertTrue(rdf.contains(stmnts.get(0).asResource(),SBMLRDF.HAS_SPECIE,a1node));
+
+ stmnts = rdf.listObjectsOfProperty(rtanode,SBMLRDF.PRODUCT).toList();
+ assertTrue(stmnts.size()==1);
+ assertTrue(rdf.contains(stmnts.get(0).asResource(),SBMLRDF.HAS_SPECIE,a2node));
+
+ assertTrue(rdf.containsLiteral(rtanode,SBMLRDF.REVERSIBLE,true));
+ assertFalse(rdf.containsLiteral(rtanode,SBMLRDF.REVERSIBLE,false));
+ }
+}
diff --git a/src/test/java/PropertyFillerTest.java b/src/test/java/PropertyFillerTest.java
new file mode 100644
index 0000000..1c78f29
--- /dev/null
+++ b/src/test/java/PropertyFillerTest.java
@@ -0,0 +1,257 @@
+import org.apache.jena.rdf.model.*;
+import org.apache.jena.vocabulary.RDF;
+import org.apache.jena.vocabulary.RDFS;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import vocabulary.SBMLRDF;
+
+import javax.xml.stream.XMLStreamException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static org.junit.Assert.*;
+
+public class PropertyFillerTest {
+
+ org.apache.jena.rdf.model.Model rdf;
+
+ Resource cmp1, cmp2, cmp;
+ Resource a1, a2, b1, b2, c1, c2, d, e;
+ Resource r1, r1_2, r2, r3, rta, rtc;
+
+ @Rule
+ public ExpectedException exception = ExpectedException.none();
+
+ @Before
+ public void init() throws XMLStreamException {
+ String baseUri = "org.mytest";
+ rdf = ModelFactory.createDefaultModel();
+ //build metab node
+ a1 = ResourceFactory.createResource((baseUri + "#a1"));rdf.add(a1, RDF.type,SBMLRDF.SPECIE);
+ a2 = ResourceFactory.createResource((baseUri+"#a2"));rdf.add(a2, RDF.type,SBMLRDF.SPECIE);
+ b1 = ResourceFactory.createResource((baseUri+"#b1"));rdf.add(b1, RDF.type,SBMLRDF.SPECIE);
+ b2 = ResourceFactory.createResource((baseUri+"#b2"));rdf.add(b2, RDF.type,SBMLRDF.SPECIE);
+ c1 = ResourceFactory.createResource((baseUri+"#c1"));rdf.add(c1, RDF.type,SBMLRDF.SPECIE);
+ c2 = ResourceFactory.createResource((baseUri+"#c2"));rdf.add(c2, RDF.type,SBMLRDF.SPECIE);
+ d = ResourceFactory.createResource((baseUri+"#d"));rdf.add(d, RDF.type,SBMLRDF.SPECIE);
+ e = ResourceFactory.createResource((baseUri+"#e"));rdf.add(e, RDF.type,SBMLRDF.SPECIE);
+ //build compartment node
+ cmp1 = ResourceFactory.createResource((baseUri+"#cmp1"));rdf.add(cmp1, RDF.type,SBMLRDF.COMPARTMENT);
+ cmp2 = ResourceFactory.createResource((baseUri+"#cmp2"));rdf.add(cmp2, RDF.type,SBMLRDF.COMPARTMENT);
+ cmp = ResourceFactory.createResource((baseUri+"#cmp3"));rdf.add(cmp, RDF.type,SBMLRDF.COMPARTMENT);
+ //build reaction node
+ r1 = ResourceFactory.createResource((baseUri+"#r1"));rdf.add(r1, RDF.type,SBMLRDF.REACTION);
+ r1_2 = ResourceFactory.createResource((baseUri+"#r1_2"));rdf.add(r1_2, RDF.type,SBMLRDF.REACTION);
+ r2 = ResourceFactory.createResource((baseUri+"#r2"));rdf.add(r2, RDF.type,SBMLRDF.REACTION);
+ r3 = ResourceFactory.createResource((baseUri+"#r3"));rdf.add(r3, RDF.type,SBMLRDF.REACTION);
+ rtc = ResourceFactory.createResource((baseUri+"#rtc"));rdf.add(rtc, RDF.type,SBMLRDF.REACTION);
+ rta = ResourceFactory.createResource((baseUri+"#rta"));rdf.add(rta, RDF.type,SBMLRDF.REACTION);
+ rdf.addLiteral(r1, SBMLRDF.REVERSIBLE,false);
+ rdf.addLiteral(r1, SBMLRDF.REVERSIBLE, false);
+ rdf.addLiteral(r1_2, SBMLRDF.REVERSIBLE,false);
+ rdf.addLiteral(r2, SBMLRDF.REVERSIBLE,true);
+ rdf.addLiteral(r3, SBMLRDF.REVERSIBLE,false);
+ rdf.addLiteral(rta, SBMLRDF.REVERSIBLE,true);
+ rdf.addLiteral(rtc, SBMLRDF.REVERSIBLE,true);
+
+ rdf.add(a1, SBMLRDF.NAME, "A");
+ rdf.add(a2, SBMLRDF.NAME, "A");
+ rdf.add(b1, SBMLRDF.NAME, "B");
+ rdf.add(b2, SBMLRDF.NAME, "B");
+ rdf.add(c1, SBMLRDF.NAME, "C");
+ rdf.add(c2, SBMLRDF.NAME, "C");
+ rdf.add(d, SBMLRDF.NAME, "D");
+ rdf.add(e, SBMLRDF.NAME, "E");
+
+ rdf.add(a1, RDFS.label, "a1");
+ rdf.add(a2, RDFS.label, "a2");
+ rdf.add(b1, RDFS.label, "b1");
+ rdf.add(b2, RDFS.label, "b2");
+ rdf.add(c1, RDFS.label, "c1");
+ rdf.add(c2, RDFS.label, "c2");
+ rdf.add(d, RDFS.label, "d");
+ rdf.add(e, RDFS.label, "e");
+
+
+ rdf.add(a1,SBMLRDF.HAS_COMPARTMENT, cmp1);
+ rdf.add(a2,SBMLRDF.HAS_COMPARTMENT, cmp2);
+ rdf.add(b1,SBMLRDF.HAS_COMPARTMENT, cmp1);
+ rdf.add(b2,SBMLRDF.HAS_COMPARTMENT, cmp2);
+ rdf.add(c1,SBMLRDF.HAS_COMPARTMENT, cmp1);
+ rdf.add(c2,SBMLRDF.HAS_COMPARTMENT, cmp2);
+ rdf.add(d,SBMLRDF.HAS_COMPARTMENT, cmp1);
+ rdf.add(e,SBMLRDF.HAS_COMPARTMENT, cmp2);
+
+ Resource a1r1= ResourceFactory.createResource();rdf.add(a1r1, RDF.type,SBMLRDF.SPECIESREF);
+ rdf.add(a1r1, SBMLRDF.HAS_SPECIE, a1);
+ rdf.add(r1, SBMLRDF.REACTANT,a1r1);
+ rdf.addLiteral(a1r1, SBMLRDF.STOICHIOMETRY,2.0);
+ Resource a2r1_2= ResourceFactory.createResource();rdf.add(a2r1_2, RDF.type,SBMLRDF.SPECIESREF);
+ rdf.add(a2r1_2, SBMLRDF.HAS_SPECIE, a2);
+ rdf.add(r1_2, SBMLRDF.REACTANT,a2r1_2);
+ rdf.addLiteral(a2r1_2, SBMLRDF.STOICHIOMETRY,2.0);
+ Resource b1r1= ResourceFactory.createResource();rdf.add(b1r1, RDF.type,SBMLRDF.SPECIESREF);
+ rdf.add(b1r1, SBMLRDF.HAS_SPECIE, b1);
+ rdf.add(r1, SBMLRDF.PRODUCT,b1r1);
+ rdf.addLiteral(b1r1, SBMLRDF.STOICHIOMETRY,1.0);
+ Resource b2r1_2= ResourceFactory.createResource();rdf.add(b2r1_2, RDF.type,SBMLRDF.SPECIESREF);
+ rdf.add(b2r1_2, SBMLRDF.HAS_SPECIE, b2);
+ rdf.add(r1_2, SBMLRDF.PRODUCT,b2r1_2);
+ rdf.addLiteral(b2r1_2, SBMLRDF.STOICHIOMETRY,1.0);
+ Resource c1r1= ResourceFactory.createResource();rdf.add(c1r1, RDF.type,SBMLRDF.SPECIESREF);
+ rdf.add(c1r1, SBMLRDF.HAS_SPECIE, c1);
+ rdf.add(r1, SBMLRDF.PRODUCT,c1r1);
+ rdf.addLiteral(c1r1, SBMLRDF.STOICHIOMETRY,1.0);
+ Resource c1r2= ResourceFactory.createResource();rdf.add(c1r2, RDF.type,SBMLRDF.SPECIESREF);
+ rdf.add(c1r2, SBMLRDF.HAS_SPECIE, c1);
+ rdf.add(r2, SBMLRDF.REACTANT,c1r2);
+ rdf.addLiteral(c1r2, SBMLRDF.STOICHIOMETRY,4.0);
+ Resource c2r1_2= ResourceFactory.createResource();rdf.add(c2r1_2, RDF.type,SBMLRDF.SPECIESREF);
+ rdf.add(c2r1_2, SBMLRDF.HAS_SPECIE, c2);
+ rdf.add(r1_2, SBMLRDF.PRODUCT,c2r1_2);
+ rdf.addLiteral(c2r1_2, SBMLRDF.STOICHIOMETRY,1.0);
+ Resource c2r3= ResourceFactory.createResource();rdf.add(c2r3, RDF.type,SBMLRDF.SPECIESREF);
+ rdf.add(c2r3, SBMLRDF.HAS_SPECIE, c2);
+ rdf.add(r3, SBMLRDF.REACTANT,c2r3);
+ rdf.addLiteral(c2r3, SBMLRDF.STOICHIOMETRY,4.0);
+ Resource dr2= ResourceFactory.createResource();rdf.add(dr2, RDF.type,SBMLRDF.SPECIESREF);
+ rdf.add(dr2, SBMLRDF.HAS_SPECIE, d);
+ rdf.add(r2, SBMLRDF.PRODUCT,dr2);
+ rdf.addLiteral(dr2, SBMLRDF.STOICHIOMETRY,4.0);
+ Resource er3= ResourceFactory.createResource();rdf.add(er3, RDF.type,SBMLRDF.SPECIESREF);
+ rdf.add(er3, SBMLRDF.HAS_SPECIE, e);
+ rdf.add(r3, SBMLRDF.PRODUCT,er3);
+ rdf.addLiteral(er3, SBMLRDF.STOICHIOMETRY,4.0);
+
+ Resource c1rtc= ResourceFactory.createResource();rdf.add(c1rtc, RDF.type,SBMLRDF.SPECIESREF);
+ rdf.add(c1rtc, SBMLRDF.HAS_SPECIE, c1);
+ rdf.add(rtc, SBMLRDF.REACTANT,c1rtc);
+ rdf.addLiteral(c1r1, SBMLRDF.STOICHIOMETRY,1.0);
+ Resource c2rtc= ResourceFactory.createResource();rdf.add(c1rtc, RDF.type,SBMLRDF.SPECIESREF);
+ rdf.add(c2rtc, SBMLRDF.HAS_SPECIE, c2);
+ rdf.add(rtc, SBMLRDF.PRODUCT,c2rtc);
+ rdf.addLiteral(c2rtc, SBMLRDF.STOICHIOMETRY,1.0);
+
+ Resource a1rta= ResourceFactory.createResource();rdf.add(c1rtc, RDF.type,SBMLRDF.SPECIESREF);
+ rdf.add(a1rta, SBMLRDF.HAS_SPECIE, a1);
+ rdf.add(rta, SBMLRDF.PRODUCT,a1rta);
+ rdf.addLiteral(a1rta, SBMLRDF.STOICHIOMETRY,1.0);
+ Resource a2rta= ResourceFactory.createResource();rdf.add(c1rtc, RDF.type,SBMLRDF.SPECIESREF);
+ rdf.add(a2rta, SBMLRDF.HAS_SPECIE, a2);
+ rdf.add(rta, SBMLRDF.REACTANT,a2rta);
+ rdf.addLiteral(a2rta, SBMLRDF.STOICHIOMETRY,1.0);
+ }
+
+ @Test
+ public void testMetaboLinks(){
+
+ PropertyFiller fill = new PropertyFiller();
+ fill.addMetaboLinks(rdf,false);
+
+ //property
+ Property derivesInto = ResourceFactory.createProperty(SBMLRDF.SIOURI, "SIO_000246");
+ assertEquals(11,rdf.listStatements(null, derivesInto, (RDFNode) null).toList().size());
+
+ //test R1 comp1
+ assertTrue(rdf.contains(a1,derivesInto, b1));
+ assertTrue(rdf.contains(a1,derivesInto, c1));
+
+ //test R1 comp2
+ assertTrue(rdf.contains(a2,derivesInto, b2));
+ assertTrue(rdf.contains(a2,derivesInto, c2));
+
+ //test R2
+ assertTrue(rdf.contains(c1,derivesInto, d));
+ assertTrue(rdf.contains(d,derivesInto, c1));
+
+ //test R3
+ assertTrue(rdf.contains(c2,derivesInto, e));
+
+ //test R transport C
+ assertTrue(rdf.contains(c2,derivesInto, c1));
+ assertTrue(rdf.contains(c1,derivesInto, c2));
+
+ //test R transport A
+ assertTrue(rdf.contains(a1,derivesInto, a2));
+ assertTrue(rdf.contains(a2,derivesInto, a1));
+ }
+
+ @Test
+ public void testMetaboLinksWithSide(){
+
+ Model rdf2 = ModelFactory.createDefaultModel();
+ rdf2.add(rdf);
+
+ ArrayList sideCompoundsIds = new ArrayList<>();
+ sideCompoundsIds.add("b1");
+ sideCompoundsIds.add("b2");
+ PropertyFiller fill = new PropertyFiller();
+ fill.importSideCompounds(rdf2,sideCompoundsIds);
+ fill.addMetaboLinks(rdf2,false);
+
+ //property
+ Property derivesInto = ResourceFactory.createProperty(SBMLRDF.SIOURI, "SIO_000246");
+ assertEquals(9,rdf2.listStatements(null, derivesInto, (RDFNode) null).toList().size());
+
+ //test R1 comp1
+ assertFalse(rdf2.contains(a1,derivesInto, b1));
+ assertTrue(rdf2.contains(a1,derivesInto, c1));
+
+ //test R1 comp2
+ assertFalse(rdf2.contains(a2,derivesInto, b2));
+ assertTrue(rdf2.contains(a2,derivesInto, c2));
+
+ //test R2
+ assertTrue(rdf2.contains(c1,derivesInto, d));
+ assertTrue(rdf2.contains(d,derivesInto, c1));
+
+ //test R3
+ assertTrue(rdf2.contains(c2,derivesInto, e));
+
+ //test R transport C
+ assertTrue(rdf2.contains(c2,derivesInto, c1));
+ assertTrue(rdf2.contains(c1,derivesInto, c2));
+
+ //test R transport A
+ assertTrue(rdf2.contains(a1,derivesInto, a2));
+ assertTrue(rdf2.contains(a2,derivesInto, a1));
+ }
+
+ @Test
+ public void testHarmonizeCompartment(){
+ PropertyFiller fill = new PropertyFiller();
+ fill.harmonizeCompartments(rdf,false);
+
+ //property
+ Property isComparableTo = ResourceFactory.createProperty(SBMLRDF.SIOURI, "SIO_000272");
+
+ assertEquals(6,rdf.listStatements(null, isComparableTo, (RDFNode) null).toList().size());
+
+ assertTrue(rdf.contains(a1,isComparableTo, a2));
+ assertTrue(rdf.contains(a2,isComparableTo, a1));
+ assertTrue(rdf.contains(b1,isComparableTo, b2));
+ assertTrue(rdf.contains(b2,isComparableTo, b1));
+ assertTrue(rdf.contains(c1,isComparableTo, c2));
+ assertTrue(rdf.contains(c2,isComparableTo, c1));
+
+ }
+
+ @Test
+ public void testImportSideCompounds(){
+ ArrayList sideCompoundsIds = new ArrayList<>();
+ sideCompoundsIds.add("b1");
+ sideCompoundsIds.add("b2");
+ PropertyFiller fill = new PropertyFiller();
+ fill.importSideCompounds(rdf,sideCompoundsIds);
+
+ List speciesRefs = rdf.listSubjectsWithProperty(RDF.type,ResourceFactory.createProperty(SBMLRDF.SBOURI, "SBO_0000603")).toList();
+ assertEquals(2,speciesRefs.size());
+
+ List labels = speciesRefs.stream().map(r -> rdf.getProperty(r,SBMLRDF.HAS_SPECIE).getObject().asResource().getLocalName()).collect(Collectors.toList());
+ assertTrue(labels.contains("b1"));
+ assertTrue(labels.contains("b2"));
+ }
+}