From 04f377d8aefc0cb8faeb62022d402ef17a6e6e96 Mon Sep 17 00:00:00 2001 From: sszuev Date: Sun, 20 Oct 2024 13:39:54 +0300 Subject: [PATCH 1/3] GH-2783: [ontapi] do not allow OntDisjoints with no items --- .../jena/ontapi/impl/OntGraphModelImpl.java | 13 ++++- .../impl/factories/OWL2ObjectFactories.java | 6 +- .../ontapi/impl/factories/OntDisjoints.java | 2 +- .../apache/jena/ontapi/OntModelMiscTest.java | 55 +++++++++++++++++++ .../jena/ontapi/OntModelOWL1SpecsTest.java | 16 ++++-- .../jena/ontapi/OntModelOWL2ELSpecTest.java | 8 ++- .../jena/ontapi/OntModelOWL2QLSpecTest.java | 4 +- .../jena/ontapi/OntModelOWL2RLSpecTest.java | 4 +- 8 files changed, 93 insertions(+), 15 deletions(-) diff --git a/jena-ontapi/src/main/java/org/apache/jena/ontapi/impl/OntGraphModelImpl.java b/jena-ontapi/src/main/java/org/apache/jena/ontapi/impl/OntGraphModelImpl.java index 588fa3e26bb..ad7bae3c944 100644 --- a/jena-ontapi/src/main/java/org/apache/jena/ontapi/impl/OntGraphModelImpl.java +++ b/jena-ontapi/src/main/java/org/apache/jena/ontapi/impl/OntGraphModelImpl.java @@ -965,8 +965,10 @@ public OntGraphModelImpl deleteOntList(OntObject subject, Property predicate, On @Override public OntDisjoint.Classes createDisjointClasses(Collection classes) { + if (classes.isEmpty()) { + throw new IllegalArgumentException("Empty list is specified"); + } checkType(OntDisjoint.Classes.class); - checkType(OntClass.IntersectionOf.class); return checkCreate(model -> OntDisjointImpl.createDisjointClasses(model, classes.stream()), OntDisjoint.Classes.class ); @@ -974,6 +976,9 @@ public OntDisjoint.Classes createDisjointClasses(Collection classes) { @Override public OntDisjoint.Individuals createDifferentIndividuals(Collection individuals) { + if (individuals.isEmpty()) { + throw new IllegalArgumentException("Empty list is specified"); + } checkType(OntDisjoint.Individuals.class); return checkCreate(model -> OntDisjointImpl.createDifferentIndividuals(model, individuals.stream()), OntDisjoint.Individuals.class @@ -982,6 +987,9 @@ public OntDisjoint.Individuals createDifferentIndividuals(Collection properties) { + if (properties.isEmpty()) { + throw new IllegalArgumentException("Empty list is specified"); + } checkType(OntDisjoint.ObjectProperties.class); return checkCreate(model -> OntDisjointImpl.createDisjointObjectProperties(model, properties.stream()), OntDisjoint.ObjectProperties.class @@ -990,6 +998,9 @@ public OntDisjoint.ObjectProperties createDisjointObjectProperties(Collection properties) { + if (properties.isEmpty()) { + throw new IllegalArgumentException("Empty list is specified"); + } checkType(OntDisjoint.DataProperties.class); return checkCreate(model -> OntDisjointImpl.createDisjointDataProperties(model, properties.stream()), OntDisjoint.DataProperties.class diff --git a/jena-ontapi/src/main/java/org/apache/jena/ontapi/impl/factories/OWL2ObjectFactories.java b/jena-ontapi/src/main/java/org/apache/jena/ontapi/impl/factories/OWL2ObjectFactories.java index d79b8ea209e..fe4daf97720 100644 --- a/jena-ontapi/src/main/java/org/apache/jena/ontapi/impl/factories/OWL2ObjectFactories.java +++ b/jena-ontapi/src/main/java/org/apache/jena/ontapi/impl/factories/OWL2ObjectFactories.java @@ -785,16 +785,16 @@ public final class OWL2ObjectFactories { OntFacetRestriction.LangRange.class ); - public static final EnhNodeFactory CLASSES_DISJOINT = OntDisjoints.createDisjointClassesFactory(0); + public static final EnhNodeFactory CLASSES_DISJOINT = OntDisjoints.createDisjointClassesFactory(1); public static final EnhNodeFactory EL_CLASSES_DISJOINT = OntDisjoints.createDisjointClassesFactory(2); public static final EnhNodeFactory QL_RL_CLASSES_DISJOINT = OntDisjoints.createQLRLDisjointClassesFactory(); public static final Function DIFFERENT_INDIVIDUALS_DISJOINT = OntDisjoints::createDLFullDifferentIndividualsFactory; public static final EnhNodeFactory EL_QL_RL_DIFFERENT_INDIVIDUALS_DISJOINT = OntDisjoints.createELQLRLDifferentIndividualsFactory(); - public static final EnhNodeFactory OBJECT_PROPERTIES_DISJOINT = OntDisjoints.createDisjointObjectPropertiesFactory(0); + public static final EnhNodeFactory OBJECT_PROPERTIES_DISJOINT = OntDisjoints.createDisjointObjectPropertiesFactory(1); public static final EnhNodeFactory QL_RL_OBJECT_PROPERTIES_DISJOINT = OntDisjoints.createDisjointObjectPropertiesFactory(2); - public static final EnhNodeFactory DATA_PROPERTIES_DISJOINT = OntDisjoints.createDisjointDataPropertiesFactory(0); + public static final EnhNodeFactory DATA_PROPERTIES_DISJOINT = OntDisjoints.createDisjointDataPropertiesFactory(1); public static final EnhNodeFactory QL_RL_DATA_PROPERTIES_DISJOINT = OntDisjoints.createDisjointDataPropertiesFactory(2); public static final EnhNodeFactory ANY_PROPERTIES_DISJOINT = OntEnhNodeFactories.createFrom( diff --git a/jena-ontapi/src/main/java/org/apache/jena/ontapi/impl/factories/OntDisjoints.java b/jena-ontapi/src/main/java/org/apache/jena/ontapi/impl/factories/OntDisjoints.java index 8b6561a3be6..421c74541bc 100644 --- a/jena-ontapi/src/main/java/org/apache/jena/ontapi/impl/factories/OntDisjoints.java +++ b/jena-ontapi/src/main/java/org/apache/jena/ontapi/impl/factories/OntDisjoints.java @@ -68,7 +68,7 @@ public static EnhNodeFactory createDLFullDifferentIndividualsFactory(OntConfig c OWL2.AllDifferent, OntIndividual.class, it -> true, - 0, + 1, predicates ); } diff --git a/jena-ontapi/src/test/java/org/apache/jena/ontapi/OntModelMiscTest.java b/jena-ontapi/src/test/java/org/apache/jena/ontapi/OntModelMiscTest.java index fe11343c046..72cb0acf889 100644 --- a/jena-ontapi/src/test/java/org/apache/jena/ontapi/OntModelMiscTest.java +++ b/jena-ontapi/src/test/java/org/apache/jena/ontapi/OntModelMiscTest.java @@ -22,10 +22,14 @@ import org.apache.jena.graph.Triple; import org.apache.jena.ontapi.impl.GraphListenerBase; import org.apache.jena.ontapi.model.OntClass; +import org.apache.jena.ontapi.model.OntDisjoint; import org.apache.jena.ontapi.model.OntModel; +import org.apache.jena.ontapi.utils.OntModels; import org.apache.jena.rdf.model.Model; import org.apache.jena.rdf.model.ModelFactory; import org.apache.jena.rdf.model.Resource; +import org.apache.jena.shared.PrefixMapping; +import org.apache.jena.vocabulary.OWL; import org.apache.jena.vocabulary.OWL2; import org.apache.jena.vocabulary.RDF; import org.junit.jupiter.api.Assertions; @@ -99,4 +103,55 @@ public void testWriteAll() { Assertions.assertTrue(d.contains(d.createResource("http://ex2/B"), RDF.type, OWL2.Class)); } + @Test + public void testOntDisjointProperties() { + var m = ModelFactory.createDefaultModel().setNsPrefixes(PrefixMapping.Standard); + var p1 = m.createResource("p1", OWL.DatatypeProperty); + var p2 = m.createResource("p2", OWL.DatatypeProperty); + var d = m.createResource().addProperty(RDF.type, OWL.AllDisjointProperties); + var list = m.createList(p1, p2); + d.addProperty(OWL.members, list); + + var ont = OntModelFactory.createModel(m.getGraph()); + var actual1 = ont.ontObjects(OntDisjoint.DataProperties.class).toList(); + Assertions.assertEquals(1, actual1.size()); + + var actual2 = ont.ontObjects(OntDisjoint.ObjectProperties.class).toList(); + Assertions.assertEquals(0, actual2.size()); + + var actual3 = ont.ontObjects(OntDisjoint.class).toList(); + Assertions.assertEquals(1, actual3.size()); + Assertions.assertEquals(OntDisjoint.DataProperties.class, OntModels.getOntType(actual3.get(0))); + + Assertions.assertThrows(IllegalArgumentException.class, ont::createDisjointObjectProperties); + Assertions.assertThrows(IllegalArgumentException.class, ont::createDisjointDataProperties); + } + + @Test + public void testOntDisjointClasses() { + var m = ModelFactory.createDefaultModel().setNsPrefixes(PrefixMapping.Standard); + var d = m.createResource().addProperty(RDF.type, OWL.AllDisjointClasses); + var list = m.createList(); + d.addProperty(OWL.members, list); + + var ont = OntModelFactory.createModel(m.getGraph()); + var actual1 = ont.ontObjects(OntDisjoint.Classes.class).toList(); + Assertions.assertEquals(0, actual1.size()); + + Assertions.assertThrows(IllegalArgumentException.class, ont::createDisjointClasses); + } + + @Test + public void testOntDisjointIndividuals() { + var m = ModelFactory.createDefaultModel().setNsPrefixes(PrefixMapping.Standard); + var d = m.createResource().addProperty(RDF.type, OWL.AllDifferent); + var list = m.createList(); + d.addProperty(OWL.members, list); + + var ont = OntModelFactory.createModel(m.getGraph()); + var actual1 = ont.ontObjects(OntDisjoint.Individuals.class).toList(); + Assertions.assertEquals(0, actual1.size()); + + Assertions.assertThrows(IllegalArgumentException.class, ont::createDifferentIndividuals); + } } diff --git a/jena-ontapi/src/test/java/org/apache/jena/ontapi/OntModelOWL1SpecsTest.java b/jena-ontapi/src/test/java/org/apache/jena/ontapi/OntModelOWL1SpecsTest.java index c2ad938e31c..eec304b72fd 100644 --- a/jena-ontapi/src/test/java/org/apache/jena/ontapi/OntModelOWL1SpecsTest.java +++ b/jena-ontapi/src/test/java/org/apache/jena/ontapi/OntModelOWL1SpecsTest.java @@ -189,21 +189,24 @@ public void testDisjointIndividualsForOWL1(TestSpec spec) { OntModel m = OntModelFactory.createModel(spec.inst); OntIndividual i1 = m.getOWLThing().createIndividual("A"); OntIndividual i2 = m.getOWLThing().createIndividual("B"); + OntClass c1 = m.createOntClass("C"); + OntDataProperty p1 = m.createDataProperty("p1"); + OntObjectProperty p2 = m.createObjectProperty("p2"); OntDisjoint.Individuals d = m.createDifferentIndividuals(i1, i2); Assertions.assertEquals( List.of("A", "B"), d.members().map(Resource::getURI).sorted().collect(Collectors.toList()) ); - Assertions.assertEquals(8, m.statements().count()); + Assertions.assertEquals(11, m.statements().count()); Assertions.assertEquals(1, m.ontObjects(OntDisjoint.Individuals.class).count()); Assertions.assertEquals(1, m.ontObjects(OntDisjoint.class).count()); - Assertions.assertThrows(OntJenaException.Unsupported.class, m::createDisjointClasses); - Assertions.assertThrows(OntJenaException.Unsupported.class, m::createDisjointDataProperties); - Assertions.assertThrows(OntJenaException.Unsupported.class, m::createDisjointObjectProperties); + Assertions.assertThrows(OntJenaException.Unsupported.class, () -> m.createDisjointClasses(c1)); + Assertions.assertThrows(OntJenaException.Unsupported.class, () -> m.createDisjointDataProperties(p1)); + Assertions.assertThrows(OntJenaException.Unsupported.class, () -> m.createDisjointObjectProperties(p2)); - Assertions.assertEquals(8, m.statements().count()); + Assertions.assertEquals(11, m.statements().count()); Assertions.assertEquals(1, m.ontObjects(OntDisjoint.Individuals.class).count()); Assertions.assertEquals(1, m.ontObjects(OntDisjoint.class).count()); } @@ -244,6 +247,7 @@ public void testOntClassCastOWL1(TestSpec spec) { @ParameterizedTest @EnumSource(names = { "OWL1_MEM", + "OWL1_DL_MEM", "OWL1_LITE_MEM", }) public void testDisabledFeaturesOWL1(TestSpec spec) { @@ -259,7 +263,7 @@ public void testDisabledFeaturesOWL1(TestSpec spec) { ); d.createDataProperty("dp1").addEquivalentProperty(d.createDataProperty("dp2")); - OntModel m = OntModelFactory.createModel(d.getGraph(), OntSpecification.OWL1_DL_MEM); + OntModel m = OntModelFactory.createModel(d.getGraph(), spec.inst); OntClass.Named x = m.getOntClass("X"); OntClass.Named q = m.createOntClass("Q"); OntDataProperty dp1 = m.createDataProperty("dp1"); diff --git a/jena-ontapi/src/test/java/org/apache/jena/ontapi/OntModelOWL2ELSpecTest.java b/jena-ontapi/src/test/java/org/apache/jena/ontapi/OntModelOWL2ELSpecTest.java index d7edeaa0a4a..4948dc388b3 100644 --- a/jena-ontapi/src/test/java/org/apache/jena/ontapi/OntModelOWL2ELSpecTest.java +++ b/jena-ontapi/src/test/java/org/apache/jena/ontapi/OntModelOWL2ELSpecTest.java @@ -316,7 +316,9 @@ public void testHasKey(TestSpec spec) { public void testDifferentIndividuals(TestSpec spec) { OntModel data = OntModelFactory.createModel(); data.createDifferentIndividuals(data.createIndividual("a")); - data.createDifferentIndividuals(); + data.createResource() + .addProperty(RDF.type, OWL2.AllDifferent) + .addProperty(OWL2.members, data.createList()); data.createDifferentIndividuals(data.createIndividual("b"), data.createIndividual("c")); OntModel m = OntModelFactory.createModel(data.getGraph(), spec.inst); @@ -348,7 +350,9 @@ public void testDifferentIndividuals(TestSpec spec) { public void testDisjointClasses(TestSpec spec) { OntModel data = OntModelFactory.createModel(); data.createDisjointClasses(data.createOntClass("a")); - data.createDisjointClasses(); + data.createResource() + .addProperty(RDF.type, OWL2.AllDisjointClasses) + .addProperty(OWL2.members, data.createList()); data.createDisjointClasses(data.createOntClass("b"), data.createOntClass("c")); OntModel m = OntModelFactory.createModel(data.getGraph(), spec.inst); diff --git a/jena-ontapi/src/test/java/org/apache/jena/ontapi/OntModelOWL2QLSpecTest.java b/jena-ontapi/src/test/java/org/apache/jena/ontapi/OntModelOWL2QLSpecTest.java index 5001096a61e..e15b612634d 100644 --- a/jena-ontapi/src/test/java/org/apache/jena/ontapi/OntModelOWL2QLSpecTest.java +++ b/jena-ontapi/src/test/java/org/apache/jena/ontapi/OntModelOWL2QLSpecTest.java @@ -441,7 +441,9 @@ public void testHasKey(TestSpec spec) { public void testDisjointDataProperties(TestSpec spec) { OntModel data = OntModelFactory.createModel(); data.createDisjointDataProperties(data.createDataProperty("a")); - data.createDisjointDataProperties(); + data.createResource() + .addProperty(RDF.type, OWL2.AllDisjointProperties) + .addProperty(OWL2.members, data.createList()); data.createDisjointDataProperties(data.createDataProperty("b"), data.createDataProperty("c")); OntModel m = OntModelFactory.createModel(data.getGraph(), spec.inst); diff --git a/jena-ontapi/src/test/java/org/apache/jena/ontapi/OntModelOWL2RLSpecTest.java b/jena-ontapi/src/test/java/org/apache/jena/ontapi/OntModelOWL2RLSpecTest.java index 04ff59dcdb0..1f9a1584df1 100644 --- a/jena-ontapi/src/test/java/org/apache/jena/ontapi/OntModelOWL2RLSpecTest.java +++ b/jena-ontapi/src/test/java/org/apache/jena/ontapi/OntModelOWL2RLSpecTest.java @@ -644,7 +644,9 @@ public void testHasKey(TestSpec spec) { public void testDisjointObjectProperties(TestSpec spec) { OntModel data = OntModelFactory.createModel(); data.createDisjointObjectProperties(data.createObjectProperty("a")); - data.createDisjointObjectProperties(); + data.createResource() + .addProperty(RDF.type, OWL2.AllDisjointProperties) + .addProperty(OWL2.members, data.createList()); data.createDisjointObjectProperties(data.createObjectProperty("b"), data.createObjectProperty("c")); OntModel m = OntModelFactory.createModel(data.getGraph(), spec.inst); From 0d8060cffbfd30927a2a7286dce43510c81babd2 Mon Sep 17 00:00:00 2001 From: Ostrzyciel Date: Sat, 26 Oct 2024 13:05:50 +0200 Subject: [PATCH 2/3] GH-2797: Lazy init for ValidationState hash maps --- .../impl/validation/ValidationState.java | 29 ++++++++++++++----- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/jena-core/src/main/java/org/apache/jena/ext/xerces/impl/validation/ValidationState.java b/jena-core/src/main/java/org/apache/jena/ext/xerces/impl/validation/ValidationState.java index 9978270f2d2..0e7b0a384a3 100644 --- a/jena-core/src/main/java/org/apache/jena/ext/xerces/impl/validation/ValidationState.java +++ b/jena-core/src/main/java/org/apache/jena/ext/xerces/impl/validation/ValidationState.java @@ -28,6 +28,8 @@ /** * Implementation of the ValidationContext interface. Used to establish an * environment for simple type validation. + *

+ * This class is not thread-safe. * * {@literal @xerces.internal} * @@ -50,9 +52,10 @@ public class ValidationState implements ValidationContext { private SymbolTable fSymbolTable = null; private Locale fLocale = null; - //REVISIT: Should replace with a lighter structure. - private final HashMap fIdTable = new HashMap(); - private final HashMap fIdRefTable = new HashMap(); + // REVISIT: Should replace with a lighter structure. + // These tables are initialized only on demand to avoid unneeded allocations. + private HashMap fIdTable = null; + private HashMap fIdRefTable = null; private final static Object fNullValue = new Object(); // @@ -91,11 +94,12 @@ public void setSymbolTable(SymbolTable sTable) { * otherwise return the first IDREF value without a matching ID value. */ public String checkIDRefID () { + if (fIdRefTable == null) return null; Iterator iter = fIdRefTable.keySet().iterator(); String key; while (iter.hasNext()) { key = (String) iter.next(); - if (!fIdTable.containsKey(key)) { + if (fIdTable == null || !fIdTable.containsKey(key)) { return key; } } @@ -106,8 +110,8 @@ public void reset () { fExtraChecking = true; fFacetChecking = true; fNamespaces = true; - fIdTable.clear(); - fIdRefTable.clear(); + fIdTable = null; + fIdRefTable = null; fEntityState = null; fNamespaceContext = null; fSymbolTable = null; @@ -120,8 +124,8 @@ public void reset () { * the two tables. */ public void resetIDTables() { - fIdTable.clear(); - fIdRefTable.clear(); + fIdTable = null; + fIdRefTable = null; } // @@ -169,16 +173,25 @@ public boolean isEntityUnparsed (String name) { // id @Override public boolean isIdDeclared(String name) { + if (fIdTable == null) { + return false; + } return fIdTable.containsKey(name); } @Override public void addId(String name) { + if (fIdTable == null) { + fIdTable = new HashMap(); + } fIdTable.put(name, fNullValue); } // idref @Override public void addIdRef(String name) { + if (fIdRefTable == null) { + fIdRefTable = new HashMap(); + } fIdRefTable.put(name, fNullValue); } // get symbols From 08209aea91d5aea22956866be6fb886d53653e63 Mon Sep 17 00:00:00 2001 From: Ostrzyciel Date: Sat, 26 Oct 2024 12:00:27 +0200 Subject: [PATCH 3/3] GH-2795: Fix race condition in datatype registration --- .../org/apache/jena/datatypes/TypeMapper.java | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/jena-core/src/main/java/org/apache/jena/datatypes/TypeMapper.java b/jena-core/src/main/java/org/apache/jena/datatypes/TypeMapper.java index 5dd07fba125..821df17ae59 100644 --- a/jena-core/src/main/java/org/apache/jena/datatypes/TypeMapper.java +++ b/jena-core/src/main/java/org/apache/jena/datatypes/TypeMapper.java @@ -126,18 +126,16 @@ public RDFDatatype getSafeTypeByName(final String uri) { // Plain literal return null; } - RDFDatatype dtype = uriToDT.get(uri); - if (dtype == null) { + return uriToDT.computeIfAbsent(uri, u -> { // Unknown datatype if (JenaParameters.enableSilentAcceptanceOfUnknownDatatypes) { - dtype = new BaseDatatype(uri); - registerDatatype(dtype); + // No need to update classToDT because BaseDatatype.getJavaClass is always null + return new BaseDatatype(u); } else { throw new DatatypeFormatException( "Attempted to created typed literal using an unknown datatype - " + uri); } - } - return dtype; + }); } /** @@ -183,6 +181,9 @@ public RDFDatatype getTypeByClass(final Class clazz) { /** * Register a new datatype + * This will overwrite any existing registration for this datatype IRI. + * Comparisons of literals with different datatype instances will fail, so be careful + * if you are using this outside of initialization code. */ public void registerDatatype(final RDFDatatype type) { uriToDT.put(type.getURI(), type); @@ -194,6 +195,10 @@ public void registerDatatype(final RDFDatatype type) { /** * Remove a datatype registration. + *

+ * WARNING: This method may cause unexpected behavior if the datatype is still in use. + * If you unregister a datatype that is still used somewhere on the heap, literal comparisons with + * that datatype will fail. */ public void unregisterDatatype(final RDFDatatype type) { uriToDT.remove(type.getURI());