diff --git a/vcell-core/src/main/java/org/jlibsedml/Model.java b/vcell-core/src/main/java/org/jlibsedml/Model.java index 5172caa9bb..b5d8392e2f 100644 --- a/vcell-core/src/main/java/org/jlibsedml/Model.java +++ b/vcell-core/src/main/java/org/jlibsedml/Model.java @@ -8,6 +8,8 @@ import org.jlibsedml.modelsupport.SUPPORTED_LANGUAGE; +import static org.jlibsedml.execution.ArchiveModelResolver.SPACE_URI_ESCAPE_SEQUENCE; + /** * Encapsulates a SED-ML Model element. Note that this object is not the * computational model itself, but holds information about the model within @@ -19,7 +21,7 @@ public final class Model extends AbstractIdentifiableElement { private String language = null; - private String source = null; + private final String source_path_or_URI_string; /** * Sets the model language that this element refers to. This should ideally be a URN, @@ -45,21 +47,21 @@ public void setLanguage(String language) { * This should ideally be a URN, which may already be defined in * {@link SUPPORTED_LANGUAGE}. If no language describes this * model, the argument may be null. - * @param source - * A mandatory URI to the model source. + * @param source_path_or_URI_string + * A mandatory URI or local path to the model source. * @throws IllegalArgumentException * if any argument except name is null or an empty * string. */ - public Model(String id, String name, String language, String source) { + public Model(String id, String name, String language, String source_path_or_URI_string) { super(id, name); if (SEDMLElementFactory.getInstance().isStrictCreation()) { - Assert.checkNoNullArgs(source); - Assert.stringsNotEmpty(source); + Assert.checkNoNullArgs(source_path_or_URI_string); + Assert.stringsNotEmpty(source_path_or_URI_string); } this.language = language; - this.source = source; + this.source_path_or_URI_string = source_path_or_URI_string; } /** @@ -76,7 +78,7 @@ public Model(String id, String name, String language, String source) { * if any argument is null */ public Model(Model toCopy, String id) { - this(id, toCopy.getName(), toCopy.getLanguage(), toCopy.getSource()); + this(id, toCopy.getName(), toCopy.getLanguage(), toCopy.getSourcePathOrURIString()); } /** @@ -139,9 +141,8 @@ public String getLanguage() { * * @return A String */ - public String getSource() { - - return source; + public String getSourcePathOrURIString() { + return this.source_path_or_URI_string; } /** @@ -231,9 +232,7 @@ public String getSource() { * cannot be converted to a URI. */ public URI getSourceURI() throws URISyntaxException { - - return new URI(source); - + return new URI(this.source_path_or_URI_string.replace(" ", SPACE_URI_ESCAPE_SEQUENCE)); } /** @@ -269,23 +268,13 @@ public boolean isSourceURIRelative() { */ public boolean isSourceValidURI() { try { - new URI(getSource()); + getSourceURI(); } catch (URISyntaxException e) { return false; } return true; } - /** - * - * @param srcString - * A URI of where this model can be retrieved. - * Attention: if the string is a model reference, it should be prefixed with "#" - */ - public void setSource(String srcString) { - source = srcString; - } - /** * @see Object#toString() */ @@ -293,7 +282,7 @@ public String toString() { return new StringBuffer().append("Model [").append("id=").append( getId()).append(", ").append("name=").append(getName()) .append(", ").append("language=").append(language).append( - ", ").append("src=").append(source).append("]").toString(); + ", ").append("src=").append(this.source_path_or_URI_string).append("]").toString(); } @Override @@ -311,6 +300,5 @@ public boolean accept(SEDMLVisitor visitor){ } } return true; - } } diff --git a/vcell-core/src/main/java/org/jlibsedml/SEDMLWriter.java b/vcell-core/src/main/java/org/jlibsedml/SEDMLWriter.java index 09fcb126c9..46cee96f0e 100644 --- a/vcell-core/src/main/java/org/jlibsedml/SEDMLWriter.java +++ b/vcell-core/src/main/java/org/jlibsedml/SEDMLWriter.java @@ -119,7 +119,7 @@ Element getXML(Model sedmlModel) { node.setAttribute(SEDMLTags.MODEL_ATTR_LANGUAGE, s); // insert // 'type' // attribute - s = sedmlModel.getSource(); + s = sedmlModel.getSourcePathOrURIString(); if (s != null) node.setAttribute(SEDMLTags.MODEL_ATTR_SOURCE, s); // insert // 'source' diff --git a/vcell-core/src/main/java/org/jlibsedml/SedML.java b/vcell-core/src/main/java/org/jlibsedml/SedML.java index d0f592fe92..debdb8b795 100644 --- a/vcell-core/src/main/java/org/jlibsedml/SedML.java +++ b/vcell-core/src/main/java/org/jlibsedml/SedML.java @@ -733,7 +733,7 @@ public final boolean addIdentifiersAsDataGenerators(AbstractTask task, public List getBaseModels() { Set set = new HashSet(); for (Model m : getModels()) { - if (getModelWithId(m.getSource()) == null) { + if (getModelWithId(m.getSourcePathOrURIString()) == null) { set.add(m); } } diff --git a/vcell-core/src/main/java/org/jlibsedml/execution/ArchiveModelResolver.java b/vcell-core/src/main/java/org/jlibsedml/execution/ArchiveModelResolver.java index 4d3320a65b..2dc3bb76a1 100644 --- a/vcell-core/src/main/java/org/jlibsedml/execution/ArchiveModelResolver.java +++ b/vcell-core/src/main/java/org/jlibsedml/execution/ArchiveModelResolver.java @@ -10,6 +10,8 @@ import org.jlibsedml.IModelContent; public class ArchiveModelResolver implements IModelResolver { + public final static String SPACE_URI_ESCAPE_SEQUENCE = "%20"; + private ArchiveComponents ac; private String sedmlPath = ""; public ArchiveModelResolver(ArchiveComponents ac) { @@ -17,7 +19,7 @@ public ArchiveModelResolver(ArchiveComponents ac) { } public String getModelXMLFor(URI modelURI) { List children = ac.getModelFiles(); - String modelStr = modelURI.toString(); + String modelStr = modelURI.toString().replace(SPACE_URI_ESCAPE_SEQUENCE, " "); // try direct match first for (IModelContent imc: children) { String modelElementStr = imc.getName(); diff --git a/vcell-core/src/main/java/org/jlibsedml/execution/ModelResolver.java b/vcell-core/src/main/java/org/jlibsedml/execution/ModelResolver.java index a98470e07a..8b61dd2621 100644 --- a/vcell-core/src/main/java/org/jlibsedml/execution/ModelResolver.java +++ b/vcell-core/src/main/java/org/jlibsedml/execution/ModelResolver.java @@ -121,7 +121,7 @@ public String getModelString(Model m) { srcURI = sedml.getModelWithId(modelRef).getSourceURI(); // If we need to recurse to the next level: modelRef = sedml.getModelWithId(modelRef.startsWith("#") ? - modelRef.substring(1): modelRef).getSource(); + modelRef.substring(1): modelRef).getSourcePathOrURIString(); } while (srcURI != null && srcURI.toString().contains("#")); } catch (URISyntaxException e) { logger.error(MODEL_SRC_NOT_VALID_URI+": "+e.getMessage(), e); @@ -187,7 +187,7 @@ final String getBaseModel(URI modelSrc) { } void getModelModificationTree(Model m, List modelRefs2) { - String modelSrcRef = m.getSource(); + String modelSrcRef = m.getSourcePathOrURIString(); modelRefs2.add(m.getId()); if (sedml.getModelWithId(modelSrcRef) != null) { diff --git a/vcell-core/src/main/java/org/jlibsedml/validation/ModelCyclesDetector.java b/vcell-core/src/main/java/org/jlibsedml/validation/ModelCyclesDetector.java index 74669cd71e..782da6e126 100644 --- a/vcell-core/src/main/java/org/jlibsedml/validation/ModelCyclesDetector.java +++ b/vcell-core/src/main/java/org/jlibsedml/validation/ModelCyclesDetector.java @@ -39,7 +39,7 @@ public List validate() throws XMLException { List errs = new ArrayList(); List models = sedml.getModels(); for (Model model : models) { - String src = model.getSource(); + String src = model.getSourcePathOrURIString(); String id = model.getId(); Set ids = new HashSet(); ids.add(id); @@ -50,12 +50,12 @@ public List validate() throws XMLException { errs.add(new SedMLError(line, "Cycles detected in source references for model " + newID + " and " - + sedml.getModelWithId(newID).getSource(), + + sedml.getModelWithId(newID).getSourcePathOrURIString(), ERROR_SEVERITY.ERROR)); return errs; } else { ids.add(newID); - src = sedml.getModelWithId(src).getSource(); + src = sedml.getModelWithId(src).getSourcePathOrURIString(); } } } diff --git a/vcell-core/src/main/java/org/jlibsedml/validation/URIValidator.java b/vcell-core/src/main/java/org/jlibsedml/validation/URIValidator.java index 2e1cdbf128..2c5d186d68 100644 --- a/vcell-core/src/main/java/org/jlibsedml/validation/URIValidator.java +++ b/vcell-core/src/main/java/org/jlibsedml/validation/URIValidator.java @@ -30,9 +30,9 @@ public List validate() { for (Model model: models){ try { - URI uri = new URI(model.getSource()); + URI uri = model.getSourceURI(); } catch (URISyntaxException e) { - errs.add(new SedMLError(0,"ErrMessageRoot[" + model.getSource() +"]", ERROR_SEVERITY.WARNING )); + errs.add(new SedMLError(0,"ErrMessageRoot[" + model.getSourcePathOrURIString() +"]", ERROR_SEVERITY.WARNING )); } diff --git a/vcell-core/src/main/java/org/vcell/sedml/SEDMLImporter.java b/vcell-core/src/main/java/org/vcell/sedml/SEDMLImporter.java index e25b734f66..b007a8d7ff 100644 --- a/vcell-core/src/main/java/org/vcell/sedml/SEDMLImporter.java +++ b/vcell-core/src/main/java/org/vcell/sedml/SEDMLImporter.java @@ -807,7 +807,7 @@ private Map createBioModels(List models) throws SEDMLIm // Group models by type for processing order for (Model model : models){ - if (model.getSource().startsWith("#")){ + if (model.getSourcePathOrURIString().startsWith("#")){ advancedModelsList.get(ADVANCED_MODEL_TYPES.REFERENCING_MODELS.ordinal()).add(model); } else if (!model.getListOfChanges().isEmpty()) { advancedModelsList.get(ADVANCED_MODEL_TYPES.CHANGED_MODELS.ordinal()).add(model); @@ -866,13 +866,13 @@ private Map createBioModels(List models) throws SEDMLIm } private String getReferenceId(Model model){ - String referenceId = model.getSource(); + String referenceId = model.getSourcePathOrURIString(); return referenceId.startsWith("#") ? referenceId.substring(1) : referenceId; } private BioModel getModelReference(String referenceId, Model model, Map idToBiomodelMap) throws SEDMLImportException { // Were we given a reference ID? We need to check if the parent was processed yet. - if (referenceId != null && !model.getSource().equals(referenceId)){ + if (referenceId != null && !model.getSourcePathOrURIString().equals(referenceId)){ boolean canTranslate; BioModel parentBiomodel = idToBiomodelMap.get(referenceId); diff --git a/vcell-math/src/main/java/cbit/vcell/parser/ExpressionMathMLParser.java b/vcell-math/src/main/java/cbit/vcell/parser/ExpressionMathMLParser.java index fbebad4ca7..8fc9d7c1b9 100644 --- a/vcell-math/src/main/java/cbit/vcell/parser/ExpressionMathMLParser.java +++ b/vcell-math/src/main/java/cbit/vcell/parser/ExpressionMathMLParser.java @@ -141,6 +141,27 @@ private SimpleNode getRootNode(Element nodeMathML, String timeSymbol) throws Exp vcellOperationNode = new ASTAndNode(); } else if (operation.getName().equals(MathMLTags.OR)){ vcellOperationNode = new ASTOrNode(); + } else if (operation.getName().equals(MathMLTags.XOR)){ + // XOR(A,B) --> OR(AND(A,NOT(B)),AND(NOT(A),B)) + // NOT(B) + ASTNotNode notNode = new ASTNotNode(); + notNode.jjtAddChild(getRootNode((Element)children.get(2), timeSymbol)); + // AND(A,NOT(B)) + ASTAndNode andNode1 = new ASTAndNode(); + andNode1.jjtAddChild(getRootNode((Element)children.get(1), timeSymbol)); + andNode1.jjtAddChild(notNode); + // NOT(A) + ASTNotNode notNode2 = new ASTNotNode(); + notNode2.jjtAddChild(getRootNode((Element)children.get(1), timeSymbol)); + // AND(NOT(A),B) + ASTAndNode andNode2 = new ASTAndNode(); + andNode2.jjtAddChild(notNode2); + andNode2.jjtAddChild(getRootNode((Element)children.get(2), timeSymbol)); + // OR(AND(A,NOT(B)),AND(NOT(A),B)) + ASTOrNode orNode = new ASTOrNode(); + orNode.jjtAddChild(andNode1); + orNode.jjtAddChild(andNode2); + return orNode; } else if (operation.getName().equals(MathMLTags.NOT)){ vcellOperationNode = new ASTNotNode(); } else if (operation.getName().equals(MathMLTags.PLUS)){ diff --git a/vcell-math/src/test/java/cbit/vcell/parser/MathMLTester.java b/vcell-math/src/test/java/cbit/vcell/parser/MathMLTest.java similarity index 80% rename from vcell-math/src/test/java/cbit/vcell/parser/MathMLTester.java rename to vcell-math/src/test/java/cbit/vcell/parser/MathMLTest.java index 4c89b6f371..1c28811272 100644 --- a/vcell-math/src/test/java/cbit/vcell/parser/MathMLTester.java +++ b/vcell-math/src/test/java/cbit/vcell/parser/MathMLTest.java @@ -14,6 +14,7 @@ import org.jmathml.ASTNode; import org.jmathml.MathMLReader; import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; @@ -25,13 +26,7 @@ import static org.junit.jupiter.api.Assertions.fail; @Tag("Fast") -public class MathMLTester { - - private Expression expression; - - public MathMLTester(Expression exp) { - this.expression = exp; - } +public class MathMLTest { public static Collection testCases() throws ExpressionException { int depthOfExpressionTree = 2; @@ -72,9 +67,23 @@ public static Collection testCases() throws ExpressionException { return expressions; } + @Test + public void testMathMLParsing_XOR() throws IOException, ExpressionException { + // XOR is supported as an SBML operator for importing only + Expression exp = new Expression("(x && !y) || (!x && y)"); + String temp_mathMLStr_with_and = ExpressionMathMLPrinter.getMathML(new Expression("x && y"), true, + ExpressionMathMLPrinter.MathType.REAL, ExpressionMathMLPrinter.Dialect.SBML_SUBSET); + String mathMLStr_with_xor = temp_mathMLStr_with_and.replace("and", "xor"); + Expression xor_from_MathML = new ExpressionMathMLParser(null).fromMathML(mathMLStr_with_xor, "t"); + boolean equiv = ExpressionUtils.functionallyEquivalent(exp, xor_from_MathML, true); + String msg = "not equivalent: origExp='"+exp.infix()+"', expMathML='"+xor_from_MathML.infix()+"'"; + assertTrue(equiv, msg); + } + + @ParameterizedTest @MethodSource("testCases") - public void testMathML_SBMLSubset() throws IOException, ExpressionException { + public void testMathML_SBMLSubset(Expression expression) throws IOException, ExpressionException { if (expression.infix().contains("atan")){ return; } @@ -88,7 +97,7 @@ public void testMathML_SBMLSubset() throws IOException, ExpressionException { @ParameterizedTest @MethodSource("testCases") - public void test_vcell_mathml_jMathML_mathml_vcell() throws IOException, ExpressionException { + public void test_vcell_mathml_jMathML_mathml_vcell(Expression expression) throws IOException, ExpressionException { List tokensToAvoid = Arrays.asList( "atan", "sech", "csch", "coth", @@ -119,7 +128,7 @@ public void test_vcell_mathml_jMathML_mathml_vcell() throws IOException, Express @ParameterizedTest @MethodSource("testCases") - public void testMathML_GeneralSubset() throws IOException, ExpressionException { + public void testMathML_GeneralSubset(Expression expression) throws IOException, ExpressionException { if (expression.infix().contains("atan")){ return; } @@ -133,14 +142,14 @@ public void testMathML_GeneralSubset() throws IOException, ExpressionException { @ParameterizedTest @MethodSource("testCases") - public void testFlatten() throws ExpressionException { + public void testFlatten(Expression expression) throws ExpressionException { Expression expFlattened = expression.flatten(); assertTrue(functionallyEquivalent(expression, expFlattened, false), "not equivalent: origExp='" + expression.infix() + "', expFlattened='" + expFlattened.infix() + "'"); } @ParameterizedTest @MethodSource("testCases") - public void testJSCLParseInfix() throws ExpressionException, ParseException { + public void testJSCLParseInfix(Expression expression) throws ExpressionException, ParseException { List tokensToAvoid = Arrays.asList( "<",">","=","&","|","!" ); @@ -168,7 +177,7 @@ public void testJSCLParseInfix() throws ExpressionException, ParseException { @ParameterizedTest @MethodSource("testCases") - public void testJSCLSimplifyInfix() { + public void testJSCLSimplifyInfix(Expression expression) { Expression expSimplified = null; try { expSimplified = expression.simplifyJSCL(20000, true);