This is an automated email from the ASF dual-hosted git repository. sszuev pushed a commit to branch ontapi-model-change-listener in repository https://gitbox.apache.org/repos/asf/jena.git
commit 07d3b2957751ff33178217b36d8c85d71b4faba7 Author: sszuev <[email protected]> AuthorDate: Thu Dec 12 23:24:51 2024 +0300 GH-2868: [ontapi] override register\unregister ModelChangedListener functionality: use only UnionGraph (not InfGraph) to hold graph-listeners --- .../apache/jena/ontapi/impl/OntGraphModelImpl.java | 20 ++++ .../apache/jena/ontapi/impl/UnionGraphImpl.java | 9 +- .../ontapi/impl/objects/OntSimpleClassImpl.java | 11 ++- .../org/apache/jena/ontapi/model/MutableModel.java | 33 +++++++ .../org/apache/jena/ontapi/OntModelMiscTest.java | 102 +++++++++++++++++++++ 5 files changed, 166 insertions(+), 9 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 ad7bae3c94..c6747bfe92 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 @@ -68,6 +68,7 @@ import org.apache.jena.ontapi.utils.StdModels; import org.apache.jena.rdf.model.InfModel; import org.apache.jena.rdf.model.Literal; import org.apache.jena.rdf.model.Model; +import org.apache.jena.rdf.model.ModelChangedListener; import org.apache.jena.rdf.model.Property; import org.apache.jena.rdf.model.RDFList; import org.apache.jena.rdf.model.RDFNode; @@ -355,6 +356,25 @@ public class OntGraphModelImpl extends ModelCom implements OntModel, OntEnhGraph return new ModelCom(getBaseGraph()); } + @Override + public OntGraphModelImpl register(ModelChangedListener listener) { + getUnionGraph().getEventManager().register(adapt(listener)); + return this; + } + + @Override + public OntGraphModelImpl unregister(ModelChangedListener listener) { + getUnionGraph().getEventManager().unregister(adapt(listener)); + return this; + } + + @Override + public OntGraphModelImpl notifyEvent(Object event) { + var ug = getUnionGraph(); + ug.getEventManager().notifyEvent(ug, event); + return this; + } + @Override public OntID getID() { checkType(OntID.class); diff --git a/jena-ontapi/src/main/java/org/apache/jena/ontapi/impl/UnionGraphImpl.java b/jena-ontapi/src/main/java/org/apache/jena/ontapi/impl/UnionGraphImpl.java index 6565221138..f73d964347 100644 --- a/jena-ontapi/src/main/java/org/apache/jena/ontapi/impl/UnionGraphImpl.java +++ b/jena-ontapi/src/main/java/org/apache/jena/ontapi/impl/UnionGraphImpl.java @@ -18,17 +18,18 @@ package org.apache.jena.ontapi.impl; -import org.apache.jena.ontapi.UnionGraph; -import org.apache.jena.ontapi.utils.Graphs; -import org.apache.jena.ontapi.utils.Iterators; import org.apache.jena.graph.Graph; import org.apache.jena.graph.GraphEventManager; import org.apache.jena.graph.GraphEvents; import org.apache.jena.graph.GraphListener; +import org.apache.jena.graph.GraphUtil; import org.apache.jena.graph.Node; import org.apache.jena.graph.Triple; import org.apache.jena.graph.compose.CompositionBase; import org.apache.jena.graph.impl.SimpleEventManager; +import org.apache.jena.ontapi.UnionGraph; +import org.apache.jena.ontapi.utils.Graphs; +import org.apache.jena.ontapi.utils.Iterators; import org.apache.jena.shared.PrefixMapping; import org.apache.jena.util.iterator.ExtendedIterator; @@ -223,7 +224,7 @@ public class UnionGraphImpl extends CompositionBase implements UnionGraph { Triple t = Triple.createMatch(s, p, o); UnionGraph.EventManager em = getEventManager(); em.onDeleteTriple(this, t); - super.remove(s, p, o); + GraphUtil.remove(this, s, p, o); em.notifyEvent(this, GraphEvents.remove(s, p, o)); } diff --git a/jena-ontapi/src/main/java/org/apache/jena/ontapi/impl/objects/OntSimpleClassImpl.java b/jena-ontapi/src/main/java/org/apache/jena/ontapi/impl/objects/OntSimpleClassImpl.java index cef800397b..e39cc0869d 100644 --- a/jena-ontapi/src/main/java/org/apache/jena/ontapi/impl/objects/OntSimpleClassImpl.java +++ b/jena-ontapi/src/main/java/org/apache/jena/ontapi/impl/objects/OntSimpleClassImpl.java @@ -22,6 +22,7 @@ import org.apache.jena.enhanced.EnhGraph; import org.apache.jena.graph.Node; import org.apache.jena.ontapi.OntJenaException; import org.apache.jena.ontapi.OntModelControls; +import org.apache.jena.ontapi.common.OntPersonalities; import org.apache.jena.ontapi.impl.OntGraphModelImpl; import org.apache.jena.ontapi.model.OntClass; import org.apache.jena.ontapi.model.OntDataProperty; @@ -34,6 +35,7 @@ import org.apache.jena.ontapi.model.OntRelationalProperty; import org.apache.jena.ontapi.model.OntStatement; import org.apache.jena.rdf.model.Resource; import org.apache.jena.vocabulary.OWL2; +import org.apache.jena.vocabulary.RDFS; import java.util.Arrays; import java.util.Collection; @@ -42,10 +44,9 @@ import java.util.Optional; import java.util.stream.Stream; /** - * {@code owl:Class} Implementation. - * Instance of this class as a class with unknown nature is only available in a spec with corresponding permissions - * ({@link OntModelControls}). - * Specialized classes have their own implementations ({@link NamedImpl} or {@link OntClassImpl}). + * Simple Ontology Class implementation. + * Represents RDFS OntClass or OWL OntClass with unknown nature. + * Specialized OWL classes have their own implementations ({@link NamedImpl} or {@link OntClassImpl}). */ @SuppressWarnings("WeakerAccess") public class OntSimpleClassImpl extends OntObjectImpl implements OntClass { @@ -56,7 +57,7 @@ public class OntSimpleClassImpl extends OntObjectImpl implements OntClass { @Override public Optional<OntStatement> findRootStatement() { - return getOptionalRootStatement(this, OWL2.Class); + return getOptionalRootStatement(this, OntPersonalities.isRDFS(getModel().getOntPersonality()) ? RDFS.Class : OWL2.Class); } @Override diff --git a/jena-ontapi/src/main/java/org/apache/jena/ontapi/model/MutableModel.java b/jena-ontapi/src/main/java/org/apache/jena/ontapi/model/MutableModel.java index 996d9fb8f2..92d04d229f 100644 --- a/jena-ontapi/src/main/java/org/apache/jena/ontapi/model/MutableModel.java +++ b/jena-ontapi/src/main/java/org/apache/jena/ontapi/model/MutableModel.java @@ -21,6 +21,7 @@ package org.apache.jena.ontapi.model; import org.apache.jena.datatypes.RDFDatatype; import org.apache.jena.rdf.model.Literal; import org.apache.jena.rdf.model.Model; +import org.apache.jena.rdf.model.ModelChangedListener; import org.apache.jena.rdf.model.ModelCon; import org.apache.jena.rdf.model.Property; import org.apache.jena.rdf.model.RDFNode; @@ -109,4 +110,36 @@ interface MutableModel<R extends Model> extends Model { @Override R add(Resource s, Property p, String lex, String lang); + + /** + * Registers a listener for model-changed events on this model. + * The methods on the listener will be called when API add/remove calls on the model succeed + * [in whole or in part]. + * The same listener may be registered many times; + * if so, its methods will be called as many times as it's registered for each event. + * + * @param listener {@link ModelChangedListener}, not null + * @return this model, for cascading + */ + R register(ModelChangedListener listener); + + /** + * Unregisters a listener from model-changed events on this model. + * The listener is detached from the model. + * The model is returned to permit cascading. + * If the listener is not attached to the model, then nothing happens. + * + * @param listener {@link ModelChangedListener}, not null + */ + R unregister(ModelChangedListener listener); + + /** + * Notifies any listeners that the {@code event} has occurred. + * + * @param event the event, which has occurred, e.g. {@code GraphEvents#startRead} + * @return this model, for cascading + * @see ModelChangedListener + * @see org.apache.jena.graph.GraphEvents + */ + R notifyEvent(Object event); } 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 72cb0acf88..263a537acc 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 @@ -25,15 +25,19 @@ 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.listeners.StatementListener; 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.rdf.model.Statement; 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; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -154,4 +158,102 @@ public class OntModelMiscTest { Assertions.assertThrows(IllegalArgumentException.class, ont::createDifferentIndividuals); } + + @ParameterizedTest + @EnumSource(names = { + "OWL2_DL_MEM_RDFS_BUILTIN_INF", + "OWL2_DL_MEM", + "OWL2_DL_MEM_RDFS_INF", + "OWL2_DL_MEM_TRANS_INF", + "OWL2_DL_MEM_RULES_INF", + "OWL2_MEM", + "OWL2_MEM_RDFS_INF", + "OWL2_MEM_TRANS_INF", + "OWL2_MEM_RULES_INF", + "OWL2_MEM_MINI_RULES_INF", + "OWL2_MEM_MICRO_RULES_INF", + "OWL2_EL_MEM", + "OWL2_EL_MEM_RDFS_INF", + "OWL2_EL_MEM_TRANS_INF", + "OWL2_EL_MEM_RULES_INF", + "OWL2_QL_MEM", + "OWL2_QL_MEM_RDFS_INF", + "OWL2_QL_MEM_TRANS_INF", + "OWL2_QL_MEM_RULES_INF", + "OWL2_RL_MEM", + "OWL2_RL_MEM_RDFS_INF", + "OWL2_RL_MEM_TRANS_INF", + "OWL2_RL_MEM_RULES_INF", + "OWL1_DL_MEM", + "OWL1_DL_MEM_RDFS_INF", + "OWL1_DL_MEM_TRANS_INF", + "OWL1_DL_MEM_RULES_INF", + "OWL1_MEM", + "OWL1_MEM_RDFS_INF", + "OWL1_MEM_TRANS_INF", + "OWL1_MEM_RULES_INF", + "OWL1_MEM_MINI_RULES_INF", + "OWL1_MEM_MICRO_RULES_INF", + "OWL1_LITE_MEM", + "OWL1_LITE_MEM_RDFS_INF", + "OWL1_LITE_MEM_TRANS_INF", + "OWL1_LITE_MEM_RULES_INF", + "RDFS_MEM", + "RDFS_MEM_RDFS_INF", + "RDFS_MEM_TRANS_INF", + }) + public void testModelChangeListenerGH2868(TestSpec spec) { + var listener = new TestModelChangedListener(); + var m = OntModelFactory.createModel(spec.inst).register(listener); + + var type1 = m.createOntClass("http://x1"); + type1.removeProperties(); + Assertions.assertEquals(1, listener.addedStatements.size()); + Assertions.assertEquals(1, listener.removedStatements.size()); + Assertions.assertEquals(1, listener.events.size()); + + listener.clear(); + + var type2 = m.createOntClass("http://x2"); + m.remove(type2.getMainStatement()); + Assertions.assertEquals(1, listener.addedStatements.size()); + Assertions.assertEquals(1, listener.removedStatements.size()); + Assertions.assertEquals(0, listener.events.size()); + + listener.clear(); + + m.createOntClass("http://x3"); + m.createOntClass("http://x4"); + m.removeAll(null, RDF.type, null); + Assertions.assertEquals(2, listener.addedStatements.size()); + Assertions.assertEquals(2, listener.removedStatements.size()); + Assertions.assertEquals(1, listener.events.size()); + } + + private static class TestModelChangedListener extends StatementListener { + private final List<Statement> addedStatements = new ArrayList<>(); + private final List<Statement> removedStatements = new ArrayList<>(); + private final List<Object> events = new ArrayList<>(); + + @Override + public void addedStatement(Statement x) { + addedStatements.add(x); + } + + @Override + public void removedStatement(Statement x) { + removedStatements.add(x); + } + + @Override + public void notifyEvent(Model m, Object event) { + events.add(event); + } + + private void clear() { + addedStatements.clear(); + removedStatements.clear(); + events.clear(); + } + } }
