diff --git a/OWLSharp.Test/Extensions/TIME/Validator/RuleSet/TIMEIntervalDisjointAnalysisRuleTest.cs b/OWLSharp.Test/Extensions/TIME/Validator/RuleSet/TIMEIntervalDisjointAnalysisRuleTest.cs new file mode 100644 index 00000000..1d17ef06 --- /dev/null +++ b/OWLSharp.Test/Extensions/TIME/Validator/RuleSet/TIMEIntervalDisjointAnalysisRuleTest.cs @@ -0,0 +1,701 @@ +/* + Copyright 2014-2025 Marco De Salvo + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using OWLSharp.Extensions.TIME; +using OWLSharp.Ontology; +using OWLSharp.Validator; +using RDFSharp.Model; + +namespace OWLSharp.Test.Extensions.TIME +{ + [TestClass] + public class TIMEIntervalDisjointAnalysisRuleTest : TIMETestOntology + { + #region Tests + [TestMethod] + public async Task ShouldAnalyzeIntervalDisjointAndViolateRule1() + { + OWLOntology ontology = new OWLOntology(TestOntology); + ontology.DeclarationAxioms.AddRange([ + new OWLDeclaration(new OWLNamedIndividual(new RDFResource("ex:IntervalA"))), + new OWLDeclaration(new OWLNamedIndividual(new RDFResource("ex:IntervalB"))), + new OWLDeclaration(new OWLNamedIndividual(new RDFResource("ex:IntervalC"))), + ]); + ontology.AssertionAxioms.AddRange([ + new OWLClassAssertion( + new OWLClass(RDFVocabulary.TIME.INTERVAL), + new OWLNamedIndividual(new RDFResource("ex:IntervalA"))), + new OWLClassAssertion( + new OWLClass(RDFVocabulary.TIME.INTERVAL), + new OWLNamedIndividual(new RDFResource("ex:IntervalB"))), + new OWLClassAssertion( + new OWLClass(RDFVocabulary.TIME.INTERVAL), + new OWLNamedIndividual(new RDFResource("ex:IntervalC"))), + new OWLObjectPropertyAssertion( + new OWLObjectProperty(RDFVocabulary.TIME.INTERVAL_DISJOINT), + new OWLNamedIndividual(new RDFResource("ex:IntervalA")), + new OWLNamedIndividual(new RDFResource("ex:IntervalB"))), + new OWLObjectPropertyAssertion( + new OWLObjectProperty(RDFVocabulary.TIME.INTERVAL_CONTAINS), + new OWLNamedIndividual(new RDFResource("ex:IntervalA")), + new OWLNamedIndividual(new RDFResource("ex:IntervalB"))), //clash + new OWLObjectPropertyAssertion( + new OWLObjectProperty(RDFVocabulary.TIME.INTERVAL_DISJOINT), + new OWLNamedIndividual(new RDFResource("ex:IntervalA")), + new OWLNamedIndividual(new RDFResource("ex:IntervalC"))), + ]); + Dictionary> cacheRegistry = new Dictionary>() + { + { "INSTANTS", ontology.GetIndividualsOf(new OWLClass(RDFVocabulary.TIME.INSTANT)) }, + { "INTERVALS", ontology.GetIndividualsOf(new OWLClass(RDFVocabulary.TIME.INTERVAL)) } + }; + List issues = await TIMEIntervalDisjointAnalysisRule.ExecuteRuleAsync(ontology, cacheRegistry); + string clashingRelation = "time:intervalContains"; + + Assert.IsNotNull(issues); + Assert.IsTrue(issues.Count == 1); + Assert.IsTrue(issues[0].Severity == OWLEnums.OWLIssueSeverity.Error); + Assert.IsTrue(string.Equals(issues[0].RuleName, TIMEIntervalDisjointAnalysisRule.rulename)); + Assert.IsTrue(string.Equals(issues[0].Description, string.Format(TIMEIntervalDisjointAnalysisRule.rulesugg, clashingRelation))); + Assert.IsTrue(string.Equals(issues[0].Suggestion, $"TIME intervals 'ex:IntervalA' and 'ex:IntervalB' should be adjusted to not clash on temporal relations (time:intervalDisjoint VS {clashingRelation})")); + } + + [TestMethod] + public async Task ShouldAnalyzeIntervalDisjointAndViolateRule2() + { + OWLOntology ontology = new OWLOntology(TestOntology); + ontology.DeclarationAxioms.AddRange([ + new OWLDeclaration(new OWLNamedIndividual(new RDFResource("ex:IntervalA"))), + new OWLDeclaration(new OWLNamedIndividual(new RDFResource("ex:IntervalB"))), + new OWLDeclaration(new OWLNamedIndividual(new RDFResource("ex:IntervalC"))), + ]); + ontology.AssertionAxioms.AddRange([ + new OWLClassAssertion( + new OWLClass(RDFVocabulary.TIME.INTERVAL), + new OWLNamedIndividual(new RDFResource("ex:IntervalA"))), + new OWLClassAssertion( + new OWLClass(RDFVocabulary.TIME.INTERVAL), + new OWLNamedIndividual(new RDFResource("ex:IntervalB"))), + new OWLClassAssertion( + new OWLClass(RDFVocabulary.TIME.INTERVAL), + new OWLNamedIndividual(new RDFResource("ex:IntervalC"))), + new OWLObjectPropertyAssertion( + new OWLObjectProperty(RDFVocabulary.TIME.INTERVAL_DISJOINT), + new OWLNamedIndividual(new RDFResource("ex:IntervalA")), + new OWLNamedIndividual(new RDFResource("ex:IntervalB"))), + new OWLObjectPropertyAssertion( + new OWLObjectProperty(RDFVocabulary.TIME.INTERVAL_DURING), + new OWLNamedIndividual(new RDFResource("ex:IntervalA")), + new OWLNamedIndividual(new RDFResource("ex:IntervalB"))), //clash + new OWLObjectPropertyAssertion( + new OWLObjectProperty(RDFVocabulary.TIME.INTERVAL_DISJOINT), + new OWLNamedIndividual(new RDFResource("ex:IntervalA")), + new OWLNamedIndividual(new RDFResource("ex:IntervalC"))), + ]); + Dictionary> cacheRegistry = new Dictionary>() + { + { "INSTANTS", ontology.GetIndividualsOf(new OWLClass(RDFVocabulary.TIME.INSTANT)) }, + { "INTERVALS", ontology.GetIndividualsOf(new OWLClass(RDFVocabulary.TIME.INTERVAL)) } + }; + List issues = await TIMEIntervalDisjointAnalysisRule.ExecuteRuleAsync(ontology, cacheRegistry); + string clashingRelation = "time:intervalDuring"; + + Assert.IsNotNull(issues); + Assert.IsTrue(issues.Count == 1); + Assert.IsTrue(issues[0].Severity == OWLEnums.OWLIssueSeverity.Error); + Assert.IsTrue(string.Equals(issues[0].RuleName, TIMEIntervalDisjointAnalysisRule.rulename)); + Assert.IsTrue(string.Equals(issues[0].Description, string.Format(TIMEIntervalDisjointAnalysisRule.rulesugg, clashingRelation))); + Assert.IsTrue(string.Equals(issues[0].Suggestion, $"TIME intervals 'ex:IntervalA' and 'ex:IntervalB' should be adjusted to not clash on temporal relations (time:intervalDisjoint VS {clashingRelation})")); + } + + [TestMethod] + public async Task ShouldAnalyzeIntervalDisjointAndViolateRule3() + { + OWLOntology ontology = new OWLOntology(TestOntology); + ontology.DeclarationAxioms.AddRange([ + new OWLDeclaration(new OWLNamedIndividual(new RDFResource("ex:IntervalA"))), + new OWLDeclaration(new OWLNamedIndividual(new RDFResource("ex:IntervalB"))), + new OWLDeclaration(new OWLNamedIndividual(new RDFResource("ex:IntervalC"))), + ]); + ontology.AssertionAxioms.AddRange([ + new OWLClassAssertion( + new OWLClass(RDFVocabulary.TIME.INTERVAL), + new OWLNamedIndividual(new RDFResource("ex:IntervalA"))), + new OWLClassAssertion( + new OWLClass(RDFVocabulary.TIME.INTERVAL), + new OWLNamedIndividual(new RDFResource("ex:IntervalB"))), + new OWLClassAssertion( + new OWLClass(RDFVocabulary.TIME.INTERVAL), + new OWLNamedIndividual(new RDFResource("ex:IntervalC"))), + new OWLObjectPropertyAssertion( + new OWLObjectProperty(RDFVocabulary.TIME.INTERVAL_DISJOINT), + new OWLNamedIndividual(new RDFResource("ex:IntervalA")), + new OWLNamedIndividual(new RDFResource("ex:IntervalB"))), + new OWLObjectPropertyAssertion( + new OWLObjectProperty(RDFVocabulary.TIME.INTERVAL_EQUALS), + new OWLNamedIndividual(new RDFResource("ex:IntervalA")), + new OWLNamedIndividual(new RDFResource("ex:IntervalB"))), //clash + new OWLObjectPropertyAssertion( + new OWLObjectProperty(RDFVocabulary.TIME.INTERVAL_DISJOINT), + new OWLNamedIndividual(new RDFResource("ex:IntervalA")), + new OWLNamedIndividual(new RDFResource("ex:IntervalC"))), + ]); + Dictionary> cacheRegistry = new Dictionary>() + { + { "INSTANTS", ontology.GetIndividualsOf(new OWLClass(RDFVocabulary.TIME.INSTANT)) }, + { "INTERVALS", ontology.GetIndividualsOf(new OWLClass(RDFVocabulary.TIME.INTERVAL)) } + }; + List issues = await TIMEIntervalDisjointAnalysisRule.ExecuteRuleAsync(ontology, cacheRegistry); + string clashingRelation = "time:intervalEquals"; + + Assert.IsNotNull(issues); + Assert.IsTrue(issues.Count == 1); + Assert.IsTrue(issues[0].Severity == OWLEnums.OWLIssueSeverity.Error); + Assert.IsTrue(string.Equals(issues[0].RuleName, TIMEIntervalDisjointAnalysisRule.rulename)); + Assert.IsTrue(string.Equals(issues[0].Description, string.Format(TIMEIntervalDisjointAnalysisRule.rulesugg, clashingRelation))); + Assert.IsTrue(string.Equals(issues[0].Suggestion, $"TIME intervals 'ex:IntervalA' and 'ex:IntervalB' should be adjusted to not clash on temporal relations (time:intervalDisjoint VS {clashingRelation})")); + } + + [TestMethod] + public async Task ShouldAnalyzeIntervalDisjointAndViolateRule4() + { + OWLOntology ontology = new OWLOntology(TestOntology); + ontology.DeclarationAxioms.AddRange([ + new OWLDeclaration(new OWLNamedIndividual(new RDFResource("ex:IntervalA"))), + new OWLDeclaration(new OWLNamedIndividual(new RDFResource("ex:IntervalB"))), + new OWLDeclaration(new OWLNamedIndividual(new RDFResource("ex:IntervalC"))), + ]); + ontology.AssertionAxioms.AddRange([ + new OWLClassAssertion( + new OWLClass(RDFVocabulary.TIME.INTERVAL), + new OWLNamedIndividual(new RDFResource("ex:IntervalA"))), + new OWLClassAssertion( + new OWLClass(RDFVocabulary.TIME.INTERVAL), + new OWLNamedIndividual(new RDFResource("ex:IntervalB"))), + new OWLClassAssertion( + new OWLClass(RDFVocabulary.TIME.INTERVAL), + new OWLNamedIndividual(new RDFResource("ex:IntervalC"))), + new OWLObjectPropertyAssertion( + new OWLObjectProperty(RDFVocabulary.TIME.INTERVAL_DISJOINT), + new OWLNamedIndividual(new RDFResource("ex:IntervalA")), + new OWLNamedIndividual(new RDFResource("ex:IntervalB"))), + new OWLObjectPropertyAssertion( + new OWLObjectProperty(RDFVocabulary.TIME.INTERVAL_FINISHED_BY), + new OWLNamedIndividual(new RDFResource("ex:IntervalA")), + new OWLNamedIndividual(new RDFResource("ex:IntervalB"))), //clash + new OWLObjectPropertyAssertion( + new OWLObjectProperty(RDFVocabulary.TIME.INTERVAL_DISJOINT), + new OWLNamedIndividual(new RDFResource("ex:IntervalA")), + new OWLNamedIndividual(new RDFResource("ex:IntervalC"))), + ]); + Dictionary> cacheRegistry = new Dictionary>() + { + { "INSTANTS", ontology.GetIndividualsOf(new OWLClass(RDFVocabulary.TIME.INSTANT)) }, + { "INTERVALS", ontology.GetIndividualsOf(new OWLClass(RDFVocabulary.TIME.INTERVAL)) } + }; + List issues = await TIMEIntervalDisjointAnalysisRule.ExecuteRuleAsync(ontology, cacheRegistry); + string clashingRelation = "time:intervalFinishedBy"; + + Assert.IsNotNull(issues); + Assert.IsTrue(issues.Count == 1); + Assert.IsTrue(issues[0].Severity == OWLEnums.OWLIssueSeverity.Error); + Assert.IsTrue(string.Equals(issues[0].RuleName, TIMEIntervalDisjointAnalysisRule.rulename)); + Assert.IsTrue(string.Equals(issues[0].Description, string.Format(TIMEIntervalDisjointAnalysisRule.rulesugg, clashingRelation))); + Assert.IsTrue(string.Equals(issues[0].Suggestion, $"TIME intervals 'ex:IntervalA' and 'ex:IntervalB' should be adjusted to not clash on temporal relations (time:intervalDisjoint VS {clashingRelation})")); + } + + [TestMethod] + public async Task ShouldAnalyzeIntervalDisjointAndViolateRule5() + { + OWLOntology ontology = new OWLOntology(TestOntology); + ontology.DeclarationAxioms.AddRange([ + new OWLDeclaration(new OWLNamedIndividual(new RDFResource("ex:IntervalA"))), + new OWLDeclaration(new OWLNamedIndividual(new RDFResource("ex:IntervalB"))), + new OWLDeclaration(new OWLNamedIndividual(new RDFResource("ex:IntervalC"))), + ]); + ontology.AssertionAxioms.AddRange([ + new OWLClassAssertion( + new OWLClass(RDFVocabulary.TIME.INTERVAL), + new OWLNamedIndividual(new RDFResource("ex:IntervalA"))), + new OWLClassAssertion( + new OWLClass(RDFVocabulary.TIME.INTERVAL), + new OWLNamedIndividual(new RDFResource("ex:IntervalB"))), + new OWLClassAssertion( + new OWLClass(RDFVocabulary.TIME.INTERVAL), + new OWLNamedIndividual(new RDFResource("ex:IntervalC"))), + new OWLObjectPropertyAssertion( + new OWLObjectProperty(RDFVocabulary.TIME.INTERVAL_DISJOINT), + new OWLNamedIndividual(new RDFResource("ex:IntervalA")), + new OWLNamedIndividual(new RDFResource("ex:IntervalB"))), + new OWLObjectPropertyAssertion( + new OWLObjectProperty(RDFVocabulary.TIME.INTERVAL_FINISHES), + new OWLNamedIndividual(new RDFResource("ex:IntervalA")), + new OWLNamedIndividual(new RDFResource("ex:IntervalB"))), //clash + new OWLObjectPropertyAssertion( + new OWLObjectProperty(RDFVocabulary.TIME.INTERVAL_DISJOINT), + new OWLNamedIndividual(new RDFResource("ex:IntervalA")), + new OWLNamedIndividual(new RDFResource("ex:IntervalC"))), + ]); + Dictionary> cacheRegistry = new Dictionary>() + { + { "INSTANTS", ontology.GetIndividualsOf(new OWLClass(RDFVocabulary.TIME.INSTANT)) }, + { "INTERVALS", ontology.GetIndividualsOf(new OWLClass(RDFVocabulary.TIME.INTERVAL)) } + }; + List issues = await TIMEIntervalDisjointAnalysisRule.ExecuteRuleAsync(ontology, cacheRegistry); + string clashingRelation = "time:intervalFinishes"; + + Assert.IsNotNull(issues); + Assert.IsTrue(issues.Count == 1); + Assert.IsTrue(issues[0].Severity == OWLEnums.OWLIssueSeverity.Error); + Assert.IsTrue(string.Equals(issues[0].RuleName, TIMEIntervalDisjointAnalysisRule.rulename)); + Assert.IsTrue(string.Equals(issues[0].Description, string.Format(TIMEIntervalDisjointAnalysisRule.rulesugg, clashingRelation))); + Assert.IsTrue(string.Equals(issues[0].Suggestion, $"TIME intervals 'ex:IntervalA' and 'ex:IntervalB' should be adjusted to not clash on temporal relations (time:intervalDisjoint VS {clashingRelation})")); + } + + [TestMethod] + public async Task ShouldAnalyzeIntervalDisjointAndViolateRule6() + { + OWLOntology ontology = new OWLOntology(TestOntology); + ontology.DeclarationAxioms.AddRange([ + new OWLDeclaration(new OWLNamedIndividual(new RDFResource("ex:IntervalA"))), + new OWLDeclaration(new OWLNamedIndividual(new RDFResource("ex:IntervalB"))), + new OWLDeclaration(new OWLNamedIndividual(new RDFResource("ex:IntervalC"))), + ]); + ontology.AssertionAxioms.AddRange([ + new OWLClassAssertion( + new OWLClass(RDFVocabulary.TIME.INTERVAL), + new OWLNamedIndividual(new RDFResource("ex:IntervalA"))), + new OWLClassAssertion( + new OWLClass(RDFVocabulary.TIME.INTERVAL), + new OWLNamedIndividual(new RDFResource("ex:IntervalB"))), + new OWLClassAssertion( + new OWLClass(RDFVocabulary.TIME.INTERVAL), + new OWLNamedIndividual(new RDFResource("ex:IntervalC"))), + new OWLObjectPropertyAssertion( + new OWLObjectProperty(RDFVocabulary.TIME.INTERVAL_DISJOINT), + new OWLNamedIndividual(new RDFResource("ex:IntervalA")), + new OWLNamedIndividual(new RDFResource("ex:IntervalB"))), + new OWLObjectPropertyAssertion( + new OWLObjectProperty(RDFVocabulary.TIME.HAS_INSIDE), + new OWLNamedIndividual(new RDFResource("ex:IntervalB")), + new OWLNamedIndividual(new RDFResource("ex:IntervalA"))), //clash + new OWLObjectPropertyAssertion( + new OWLObjectProperty(RDFVocabulary.TIME.INTERVAL_DISJOINT), + new OWLNamedIndividual(new RDFResource("ex:IntervalA")), + new OWLNamedIndividual(new RDFResource("ex:IntervalC"))), + ]); + Dictionary> cacheRegistry = new Dictionary>() + { + { "INSTANTS", ontology.GetIndividualsOf(new OWLClass(RDFVocabulary.TIME.INSTANT)) }, + { "INTERVALS", ontology.GetIndividualsOf(new OWLClass(RDFVocabulary.TIME.INTERVAL)) } + }; + List issues = await TIMEIntervalDisjointAnalysisRule.ExecuteRuleAsync(ontology, cacheRegistry); + string clashingRelation = "time:hasInside"; + + Assert.IsNotNull(issues); + Assert.IsTrue(issues.Count == 1); + Assert.IsTrue(issues[0].Severity == OWLEnums.OWLIssueSeverity.Error); + Assert.IsTrue(string.Equals(issues[0].RuleName, TIMEIntervalDisjointAnalysisRule.rulename)); + Assert.IsTrue(string.Equals(issues[0].Description, string.Format(TIMEIntervalDisjointAnalysisRule.rulesugg, clashingRelation))); + Assert.IsTrue(string.Equals(issues[0].Suggestion, $"TIME intervals 'ex:IntervalA' and 'ex:IntervalB' should be adjusted to not clash on temporal relations (time:intervalDisjoint VS {clashingRelation})")); + } + + [TestMethod] + public async Task ShouldAnalyzeIntervalDisjointAndViolateRule7() + { + OWLOntology ontology = new OWLOntology(TestOntology); + ontology.DeclarationAxioms.AddRange([ + new OWLDeclaration(new OWLNamedIndividual(new RDFResource("ex:IntervalA"))), + new OWLDeclaration(new OWLNamedIndividual(new RDFResource("ex:IntervalB"))), + new OWLDeclaration(new OWLNamedIndividual(new RDFResource("ex:IntervalC"))), + ]); + ontology.AssertionAxioms.AddRange([ + new OWLClassAssertion( + new OWLClass(RDFVocabulary.TIME.INTERVAL), + new OWLNamedIndividual(new RDFResource("ex:IntervalA"))), + new OWLClassAssertion( + new OWLClass(RDFVocabulary.TIME.INTERVAL), + new OWLNamedIndividual(new RDFResource("ex:IntervalB"))), + new OWLClassAssertion( + new OWLClass(RDFVocabulary.TIME.INTERVAL), + new OWLNamedIndividual(new RDFResource("ex:IntervalC"))), + new OWLObjectPropertyAssertion( + new OWLObjectProperty(RDFVocabulary.TIME.INTERVAL_DISJOINT), + new OWLNamedIndividual(new RDFResource("ex:IntervalA")), + new OWLNamedIndividual(new RDFResource("ex:IntervalB"))), + new OWLObjectPropertyAssertion( + new OWLObjectProperty(RDFVocabulary.TIME.INTERVAL_IN), + new OWLNamedIndividual(new RDFResource("ex:IntervalA")), + new OWLNamedIndividual(new RDFResource("ex:IntervalB"))), //clash + new OWLObjectPropertyAssertion( + new OWLObjectProperty(RDFVocabulary.TIME.INTERVAL_DISJOINT), + new OWLNamedIndividual(new RDFResource("ex:IntervalA")), + new OWLNamedIndividual(new RDFResource("ex:IntervalC"))), + ]); + Dictionary> cacheRegistry = new Dictionary>() + { + { "INSTANTS", ontology.GetIndividualsOf(new OWLClass(RDFVocabulary.TIME.INSTANT)) }, + { "INTERVALS", ontology.GetIndividualsOf(new OWLClass(RDFVocabulary.TIME.INTERVAL)) } + }; + List issues = await TIMEIntervalDisjointAnalysisRule.ExecuteRuleAsync(ontology, cacheRegistry); + string clashingRelation = "time:intervalIn"; + + Assert.IsNotNull(issues); + Assert.IsTrue(issues.Count == 1); + Assert.IsTrue(issues[0].Severity == OWLEnums.OWLIssueSeverity.Error); + Assert.IsTrue(string.Equals(issues[0].RuleName, TIMEIntervalDisjointAnalysisRule.rulename)); + Assert.IsTrue(string.Equals(issues[0].Description, string.Format(TIMEIntervalDisjointAnalysisRule.rulesugg, clashingRelation))); + Assert.IsTrue(string.Equals(issues[0].Suggestion, $"TIME intervals 'ex:IntervalA' and 'ex:IntervalB' should be adjusted to not clash on temporal relations (time:intervalDisjoint VS {clashingRelation})")); + } + + [TestMethod] + public async Task ShouldAnalyzeIntervalDisjointAndViolateRule8() + { + OWLOntology ontology = new OWLOntology(TestOntology); + ontology.DeclarationAxioms.AddRange([ + new OWLDeclaration(new OWLNamedIndividual(new RDFResource("ex:IntervalA"))), + new OWLDeclaration(new OWLNamedIndividual(new RDFResource("ex:IntervalB"))), + new OWLDeclaration(new OWLNamedIndividual(new RDFResource("ex:IntervalC"))), + ]); + ontology.AssertionAxioms.AddRange([ + new OWLClassAssertion( + new OWLClass(RDFVocabulary.TIME.INTERVAL), + new OWLNamedIndividual(new RDFResource("ex:IntervalA"))), + new OWLClassAssertion( + new OWLClass(RDFVocabulary.TIME.INTERVAL), + new OWLNamedIndividual(new RDFResource("ex:IntervalB"))), + new OWLClassAssertion( + new OWLClass(RDFVocabulary.TIME.INTERVAL), + new OWLNamedIndividual(new RDFResource("ex:IntervalC"))), + new OWLObjectPropertyAssertion( + new OWLObjectProperty(RDFVocabulary.TIME.INTERVAL_DISJOINT), + new OWLNamedIndividual(new RDFResource("ex:IntervalA")), + new OWLNamedIndividual(new RDFResource("ex:IntervalB"))), + new OWLObjectPropertyAssertion( + new OWLObjectProperty(RDFVocabulary.TIME.INTERVAL_MEETS), + new OWLNamedIndividual(new RDFResource("ex:IntervalA")), + new OWLNamedIndividual(new RDFResource("ex:IntervalB"))), //clash + new OWLObjectPropertyAssertion( + new OWLObjectProperty(RDFVocabulary.TIME.INTERVAL_DISJOINT), + new OWLNamedIndividual(new RDFResource("ex:IntervalA")), + new OWLNamedIndividual(new RDFResource("ex:IntervalC"))), + ]); + Dictionary> cacheRegistry = new Dictionary>() + { + { "INSTANTS", ontology.GetIndividualsOf(new OWLClass(RDFVocabulary.TIME.INSTANT)) }, + { "INTERVALS", ontology.GetIndividualsOf(new OWLClass(RDFVocabulary.TIME.INTERVAL)) } + }; + List issues = await TIMEIntervalDisjointAnalysisRule.ExecuteRuleAsync(ontology, cacheRegistry); + string clashingRelation = "time:intervalMeets"; + + Assert.IsNotNull(issues); + Assert.IsTrue(issues.Count == 1); + Assert.IsTrue(issues[0].Severity == OWLEnums.OWLIssueSeverity.Error); + Assert.IsTrue(string.Equals(issues[0].RuleName, TIMEIntervalDisjointAnalysisRule.rulename)); + Assert.IsTrue(string.Equals(issues[0].Description, string.Format(TIMEIntervalDisjointAnalysisRule.rulesugg, clashingRelation))); + Assert.IsTrue(string.Equals(issues[0].Suggestion, $"TIME intervals 'ex:IntervalA' and 'ex:IntervalB' should be adjusted to not clash on temporal relations (time:intervalDisjoint VS {clashingRelation})")); + } + + [TestMethod] + public async Task ShouldAnalyzeIntervalDisjointAndViolateRule9() + { + OWLOntology ontology = new OWLOntology(TestOntology); + ontology.DeclarationAxioms.AddRange([ + new OWLDeclaration(new OWLNamedIndividual(new RDFResource("ex:IntervalA"))), + new OWLDeclaration(new OWLNamedIndividual(new RDFResource("ex:IntervalB"))), + new OWLDeclaration(new OWLNamedIndividual(new RDFResource("ex:IntervalC"))), + ]); + ontology.AssertionAxioms.AddRange([ + new OWLClassAssertion( + new OWLClass(RDFVocabulary.TIME.INTERVAL), + new OWLNamedIndividual(new RDFResource("ex:IntervalA"))), + new OWLClassAssertion( + new OWLClass(RDFVocabulary.TIME.INTERVAL), + new OWLNamedIndividual(new RDFResource("ex:IntervalB"))), + new OWLClassAssertion( + new OWLClass(RDFVocabulary.TIME.INTERVAL), + new OWLNamedIndividual(new RDFResource("ex:IntervalC"))), + new OWLObjectPropertyAssertion( + new OWLObjectProperty(RDFVocabulary.TIME.INTERVAL_DISJOINT), + new OWLNamedIndividual(new RDFResource("ex:IntervalA")), + new OWLNamedIndividual(new RDFResource("ex:IntervalB"))), + new OWLObjectPropertyAssertion( + new OWLObjectProperty(RDFVocabulary.TIME.INTERVAL_MET_BY), + new OWLNamedIndividual(new RDFResource("ex:IntervalA")), + new OWLNamedIndividual(new RDFResource("ex:IntervalB"))), //clash + new OWLObjectPropertyAssertion( + new OWLObjectProperty(RDFVocabulary.TIME.INTERVAL_DISJOINT), + new OWLNamedIndividual(new RDFResource("ex:IntervalA")), + new OWLNamedIndividual(new RDFResource("ex:IntervalC"))), + ]); + Dictionary> cacheRegistry = new Dictionary>() + { + { "INSTANTS", ontology.GetIndividualsOf(new OWLClass(RDFVocabulary.TIME.INSTANT)) }, + { "INTERVALS", ontology.GetIndividualsOf(new OWLClass(RDFVocabulary.TIME.INTERVAL)) } + }; + List issues = await TIMEIntervalDisjointAnalysisRule.ExecuteRuleAsync(ontology, cacheRegistry); + string clashingRelation = "time:intervalMetBy"; + + Assert.IsNotNull(issues); + Assert.IsTrue(issues.Count == 1); + Assert.IsTrue(issues[0].Severity == OWLEnums.OWLIssueSeverity.Error); + Assert.IsTrue(string.Equals(issues[0].RuleName, TIMEIntervalDisjointAnalysisRule.rulename)); + Assert.IsTrue(string.Equals(issues[0].Description, string.Format(TIMEIntervalDisjointAnalysisRule.rulesugg, clashingRelation))); + Assert.IsTrue(string.Equals(issues[0].Suggestion, $"TIME intervals 'ex:IntervalA' and 'ex:IntervalB' should be adjusted to not clash on temporal relations (time:intervalDisjoint VS {clashingRelation})")); + } + + [TestMethod] + public async Task ShouldAnalyzeIntervalDisjointAndViolateRule10() + { + OWLOntology ontology = new OWLOntology(TestOntology); + ontology.DeclarationAxioms.AddRange([ + new OWLDeclaration(new OWLNamedIndividual(new RDFResource("ex:IntervalA"))), + new OWLDeclaration(new OWLNamedIndividual(new RDFResource("ex:IntervalB"))), + new OWLDeclaration(new OWLNamedIndividual(new RDFResource("ex:IntervalC"))), + ]); + ontology.AssertionAxioms.AddRange([ + new OWLClassAssertion( + new OWLClass(RDFVocabulary.TIME.INTERVAL), + new OWLNamedIndividual(new RDFResource("ex:IntervalA"))), + new OWLClassAssertion( + new OWLClass(RDFVocabulary.TIME.INTERVAL), + new OWLNamedIndividual(new RDFResource("ex:IntervalB"))), + new OWLClassAssertion( + new OWLClass(RDFVocabulary.TIME.INTERVAL), + new OWLNamedIndividual(new RDFResource("ex:IntervalC"))), + new OWLObjectPropertyAssertion( + new OWLObjectProperty(RDFVocabulary.TIME.INTERVAL_DISJOINT), + new OWLNamedIndividual(new RDFResource("ex:IntervalA")), + new OWLNamedIndividual(new RDFResource("ex:IntervalB"))), + new OWLObjectPropertyAssertion( + new OWLObjectProperty(RDFVocabulary.TIME.NOT_DISJOINT), + new OWLNamedIndividual(new RDFResource("ex:IntervalA")), + new OWLNamedIndividual(new RDFResource("ex:IntervalB"))), //clash + new OWLObjectPropertyAssertion( + new OWLObjectProperty(RDFVocabulary.TIME.INTERVAL_DISJOINT), + new OWLNamedIndividual(new RDFResource("ex:IntervalA")), + new OWLNamedIndividual(new RDFResource("ex:IntervalC"))), + ]); + Dictionary> cacheRegistry = new Dictionary>() + { + { "INSTANTS", ontology.GetIndividualsOf(new OWLClass(RDFVocabulary.TIME.INSTANT)) }, + { "INTERVALS", ontology.GetIndividualsOf(new OWLClass(RDFVocabulary.TIME.INTERVAL)) } + }; + List issues = await TIMEIntervalDisjointAnalysisRule.ExecuteRuleAsync(ontology, cacheRegistry); + string clashingRelation = "time:notDisjoint"; + + Assert.IsNotNull(issues); + Assert.IsTrue(issues.Count == 1); + Assert.IsTrue(issues[0].Severity == OWLEnums.OWLIssueSeverity.Error); + Assert.IsTrue(string.Equals(issues[0].RuleName, TIMEIntervalDisjointAnalysisRule.rulename)); + Assert.IsTrue(string.Equals(issues[0].Description, string.Format(TIMEIntervalDisjointAnalysisRule.rulesugg, clashingRelation))); + Assert.IsTrue(string.Equals(issues[0].Suggestion, $"TIME intervals 'ex:IntervalA' and 'ex:IntervalB' should be adjusted to not clash on temporal relations (time:intervalDisjoint VS {clashingRelation})")); + } + + [TestMethod] + public async Task ShouldAnalyzeIntervalDisjointAndViolateRule11() + { + OWLOntology ontology = new OWLOntology(TestOntology); + ontology.DeclarationAxioms.AddRange([ + new OWLDeclaration(new OWLNamedIndividual(new RDFResource("ex:IntervalA"))), + new OWLDeclaration(new OWLNamedIndividual(new RDFResource("ex:IntervalB"))), + new OWLDeclaration(new OWLNamedIndividual(new RDFResource("ex:IntervalC"))), + ]); + ontology.AssertionAxioms.AddRange([ + new OWLClassAssertion( + new OWLClass(RDFVocabulary.TIME.INTERVAL), + new OWLNamedIndividual(new RDFResource("ex:IntervalA"))), + new OWLClassAssertion( + new OWLClass(RDFVocabulary.TIME.INTERVAL), + new OWLNamedIndividual(new RDFResource("ex:IntervalB"))), + new OWLClassAssertion( + new OWLClass(RDFVocabulary.TIME.INTERVAL), + new OWLNamedIndividual(new RDFResource("ex:IntervalC"))), + new OWLObjectPropertyAssertion( + new OWLObjectProperty(RDFVocabulary.TIME.INTERVAL_DISJOINT), + new OWLNamedIndividual(new RDFResource("ex:IntervalA")), + new OWLNamedIndividual(new RDFResource("ex:IntervalB"))), + new OWLObjectPropertyAssertion( + new OWLObjectProperty(RDFVocabulary.TIME.INTERVAL_OVERLAPPED_BY), + new OWLNamedIndividual(new RDFResource("ex:IntervalA")), + new OWLNamedIndividual(new RDFResource("ex:IntervalB"))), //clash + new OWLObjectPropertyAssertion( + new OWLObjectProperty(RDFVocabulary.TIME.INTERVAL_DISJOINT), + new OWLNamedIndividual(new RDFResource("ex:IntervalA")), + new OWLNamedIndividual(new RDFResource("ex:IntervalC"))), + ]); + Dictionary> cacheRegistry = new Dictionary>() + { + { "INSTANTS", ontology.GetIndividualsOf(new OWLClass(RDFVocabulary.TIME.INSTANT)) }, + { "INTERVALS", ontology.GetIndividualsOf(new OWLClass(RDFVocabulary.TIME.INTERVAL)) } + }; + List issues = await TIMEIntervalDisjointAnalysisRule.ExecuteRuleAsync(ontology, cacheRegistry); + string clashingRelation = "time:intervalOverlappedBy"; + + Assert.IsNotNull(issues); + Assert.IsTrue(issues.Count == 1); + Assert.IsTrue(issues[0].Severity == OWLEnums.OWLIssueSeverity.Error); + Assert.IsTrue(string.Equals(issues[0].RuleName, TIMEIntervalDisjointAnalysisRule.rulename)); + Assert.IsTrue(string.Equals(issues[0].Description, string.Format(TIMEIntervalDisjointAnalysisRule.rulesugg, clashingRelation))); + Assert.IsTrue(string.Equals(issues[0].Suggestion, $"TIME intervals 'ex:IntervalA' and 'ex:IntervalB' should be adjusted to not clash on temporal relations (time:intervalDisjoint VS {clashingRelation})")); + } + + [TestMethod] + public async Task ShouldAnalyzeIntervalDisjointAndViolateRule12() + { + OWLOntology ontology = new OWLOntology(TestOntology); + ontology.DeclarationAxioms.AddRange([ + new OWLDeclaration(new OWLNamedIndividual(new RDFResource("ex:IntervalA"))), + new OWLDeclaration(new OWLNamedIndividual(new RDFResource("ex:IntervalB"))), + new OWLDeclaration(new OWLNamedIndividual(new RDFResource("ex:IntervalC"))), + ]); + ontology.AssertionAxioms.AddRange([ + new OWLClassAssertion( + new OWLClass(RDFVocabulary.TIME.INTERVAL), + new OWLNamedIndividual(new RDFResource("ex:IntervalA"))), + new OWLClassAssertion( + new OWLClass(RDFVocabulary.TIME.INTERVAL), + new OWLNamedIndividual(new RDFResource("ex:IntervalB"))), + new OWLClassAssertion( + new OWLClass(RDFVocabulary.TIME.INTERVAL), + new OWLNamedIndividual(new RDFResource("ex:IntervalC"))), + new OWLObjectPropertyAssertion( + new OWLObjectProperty(RDFVocabulary.TIME.INTERVAL_DISJOINT), + new OWLNamedIndividual(new RDFResource("ex:IntervalA")), + new OWLNamedIndividual(new RDFResource("ex:IntervalB"))), + new OWLObjectPropertyAssertion( + new OWLObjectProperty(RDFVocabulary.TIME.INTERVAL_OVERLAPS), + new OWLNamedIndividual(new RDFResource("ex:IntervalA")), + new OWLNamedIndividual(new RDFResource("ex:IntervalB"))), //clash + new OWLObjectPropertyAssertion( + new OWLObjectProperty(RDFVocabulary.TIME.INTERVAL_DISJOINT), + new OWLNamedIndividual(new RDFResource("ex:IntervalA")), + new OWLNamedIndividual(new RDFResource("ex:IntervalC"))), + ]); + Dictionary> cacheRegistry = new Dictionary>() + { + { "INSTANTS", ontology.GetIndividualsOf(new OWLClass(RDFVocabulary.TIME.INSTANT)) }, + { "INTERVALS", ontology.GetIndividualsOf(new OWLClass(RDFVocabulary.TIME.INTERVAL)) } + }; + List issues = await TIMEIntervalDisjointAnalysisRule.ExecuteRuleAsync(ontology, cacheRegistry); + string clashingRelation = "time:intervalOverlaps"; + + Assert.IsNotNull(issues); + Assert.IsTrue(issues.Count == 1); + Assert.IsTrue(issues[0].Severity == OWLEnums.OWLIssueSeverity.Error); + Assert.IsTrue(string.Equals(issues[0].RuleName, TIMEIntervalDisjointAnalysisRule.rulename)); + Assert.IsTrue(string.Equals(issues[0].Description, string.Format(TIMEIntervalDisjointAnalysisRule.rulesugg, clashingRelation))); + Assert.IsTrue(string.Equals(issues[0].Suggestion, $"TIME intervals 'ex:IntervalA' and 'ex:IntervalB' should be adjusted to not clash on temporal relations (time:intervalDisjoint VS {clashingRelation})")); + } + + [TestMethod] + public async Task ShouldAnalyzeIntervalDisjointAndViolateRule13() + { + OWLOntology ontology = new OWLOntology(TestOntology); + ontology.DeclarationAxioms.AddRange([ + new OWLDeclaration(new OWLNamedIndividual(new RDFResource("ex:IntervalA"))), + new OWLDeclaration(new OWLNamedIndividual(new RDFResource("ex:IntervalB"))), + new OWLDeclaration(new OWLNamedIndividual(new RDFResource("ex:IntervalC"))), + ]); + ontology.AssertionAxioms.AddRange([ + new OWLClassAssertion( + new OWLClass(RDFVocabulary.TIME.INTERVAL), + new OWLNamedIndividual(new RDFResource("ex:IntervalA"))), + new OWLClassAssertion( + new OWLClass(RDFVocabulary.TIME.INTERVAL), + new OWLNamedIndividual(new RDFResource("ex:IntervalB"))), + new OWLClassAssertion( + new OWLClass(RDFVocabulary.TIME.INTERVAL), + new OWLNamedIndividual(new RDFResource("ex:IntervalC"))), + new OWLObjectPropertyAssertion( + new OWLObjectProperty(RDFVocabulary.TIME.INTERVAL_DISJOINT), + new OWLNamedIndividual(new RDFResource("ex:IntervalA")), + new OWLNamedIndividual(new RDFResource("ex:IntervalB"))), + new OWLObjectPropertyAssertion( + new OWLObjectProperty(RDFVocabulary.TIME.INTERVAL_STARTED_BY), + new OWLNamedIndividual(new RDFResource("ex:IntervalA")), + new OWLNamedIndividual(new RDFResource("ex:IntervalB"))), //clash + new OWLObjectPropertyAssertion( + new OWLObjectProperty(RDFVocabulary.TIME.INTERVAL_DISJOINT), + new OWLNamedIndividual(new RDFResource("ex:IntervalA")), + new OWLNamedIndividual(new RDFResource("ex:IntervalC"))), + ]); + Dictionary> cacheRegistry = new Dictionary>() + { + { "INSTANTS", ontology.GetIndividualsOf(new OWLClass(RDFVocabulary.TIME.INSTANT)) }, + { "INTERVALS", ontology.GetIndividualsOf(new OWLClass(RDFVocabulary.TIME.INTERVAL)) } + }; + List issues = await TIMEIntervalDisjointAnalysisRule.ExecuteRuleAsync(ontology, cacheRegistry); + string clashingRelation = "time:intervalStartedBy"; + + Assert.IsNotNull(issues); + Assert.IsTrue(issues.Count == 1); + Assert.IsTrue(issues[0].Severity == OWLEnums.OWLIssueSeverity.Error); + Assert.IsTrue(string.Equals(issues[0].RuleName, TIMEIntervalDisjointAnalysisRule.rulename)); + Assert.IsTrue(string.Equals(issues[0].Description, string.Format(TIMEIntervalDisjointAnalysisRule.rulesugg, clashingRelation))); + Assert.IsTrue(string.Equals(issues[0].Suggestion, $"TIME intervals 'ex:IntervalA' and 'ex:IntervalB' should be adjusted to not clash on temporal relations (time:intervalDisjoint VS {clashingRelation})")); + } + + [TestMethod] + public async Task ShouldAnalyzeIntervalDisjointAndViolateRule14() + { + OWLOntology ontology = new OWLOntology(TestOntology); + ontology.DeclarationAxioms.AddRange([ + new OWLDeclaration(new OWLNamedIndividual(new RDFResource("ex:IntervalA"))), + new OWLDeclaration(new OWLNamedIndividual(new RDFResource("ex:IntervalB"))), + new OWLDeclaration(new OWLNamedIndividual(new RDFResource("ex:IntervalC"))), + ]); + ontology.AssertionAxioms.AddRange([ + new OWLClassAssertion( + new OWLClass(RDFVocabulary.TIME.INTERVAL), + new OWLNamedIndividual(new RDFResource("ex:IntervalA"))), + new OWLClassAssertion( + new OWLClass(RDFVocabulary.TIME.INTERVAL), + new OWLNamedIndividual(new RDFResource("ex:IntervalB"))), + new OWLClassAssertion( + new OWLClass(RDFVocabulary.TIME.INTERVAL), + new OWLNamedIndividual(new RDFResource("ex:IntervalC"))), + new OWLObjectPropertyAssertion( + new OWLObjectProperty(RDFVocabulary.TIME.INTERVAL_DISJOINT), + new OWLNamedIndividual(new RDFResource("ex:IntervalA")), + new OWLNamedIndividual(new RDFResource("ex:IntervalB"))), + new OWLObjectPropertyAssertion( + new OWLObjectProperty(RDFVocabulary.TIME.INTERVAL_STARTS), + new OWLNamedIndividual(new RDFResource("ex:IntervalA")), + new OWLNamedIndividual(new RDFResource("ex:IntervalB"))), //clash + new OWLObjectPropertyAssertion( + new OWLObjectProperty(RDFVocabulary.TIME.INTERVAL_DISJOINT), + new OWLNamedIndividual(new RDFResource("ex:IntervalA")), + new OWLNamedIndividual(new RDFResource("ex:IntervalC"))), + ]); + Dictionary> cacheRegistry = new Dictionary>() + { + { "INSTANTS", ontology.GetIndividualsOf(new OWLClass(RDFVocabulary.TIME.INSTANT)) }, + { "INTERVALS", ontology.GetIndividualsOf(new OWLClass(RDFVocabulary.TIME.INTERVAL)) } + }; + List issues = await TIMEIntervalDisjointAnalysisRule.ExecuteRuleAsync(ontology, cacheRegistry); + string clashingRelation = "time:intervalStarts"; + + Assert.IsNotNull(issues); + Assert.IsTrue(issues.Count == 1); + Assert.IsTrue(issues[0].Severity == OWLEnums.OWLIssueSeverity.Error); + Assert.IsTrue(string.Equals(issues[0].RuleName, TIMEIntervalDisjointAnalysisRule.rulename)); + Assert.IsTrue(string.Equals(issues[0].Description, string.Format(TIMEIntervalDisjointAnalysisRule.rulesugg, clashingRelation))); + Assert.IsTrue(string.Equals(issues[0].Suggestion, $"TIME intervals 'ex:IntervalA' and 'ex:IntervalB' should be adjusted to not clash on temporal relations (time:intervalDisjoint VS {clashingRelation})")); + } + #endregion + } +} \ No newline at end of file diff --git a/OWLSharp.Test/Extensions/TIME/Validator/TIMEValidatorTest.cs b/OWLSharp.Test/Extensions/TIME/Validator/TIMEValidatorTest.cs index 4cbf62fd..ab7c65af 100644 --- a/OWLSharp.Test/Extensions/TIME/Validator/TIMEValidatorTest.cs +++ b/OWLSharp.Test/Extensions/TIME/Validator/TIMEValidatorTest.cs @@ -295,5 +295,53 @@ public async Task ShouldValidateIntervalContains() Assert.IsTrue(string.Equals(issues[1].Description, string.Format(TIMEIntervalContainsAnalysisRule.rulesugg, clashingRelation))); Assert.IsTrue(string.Equals(issues[1].Suggestion, $"TIME intervals 'ex:IntervalB' and 'ex:IntervalA' should be adjusted to not clash on temporal relations ({testingRelation} VS {clashingRelation})")); } + + [TestMethod] + public async Task ShouldValidateIntervalDisjoint() + { + OWLOntology ontology = new OWLOntology(TestOntology); + ontology.DeclarationAxioms.AddRange([ + new OWLDeclaration(new OWLNamedIndividual(new RDFResource("ex:IntervalA"))), + new OWLDeclaration(new OWLNamedIndividual(new RDFResource("ex:IntervalB"))), + new OWLDeclaration(new OWLNamedIndividual(new RDFResource("ex:IntervalC"))), + ]); + ontology.AssertionAxioms.AddRange([ + new OWLClassAssertion( + new OWLClass(RDFVocabulary.TIME.INTERVAL), + new OWLNamedIndividual(new RDFResource("ex:IntervalA"))), + new OWLClassAssertion( + new OWLClass(RDFVocabulary.TIME.INTERVAL), + new OWLNamedIndividual(new RDFResource("ex:IntervalB"))), + new OWLClassAssertion( + new OWLClass(RDFVocabulary.TIME.INTERVAL), + new OWLNamedIndividual(new RDFResource("ex:IntervalC"))), + new OWLObjectPropertyAssertion( + new OWLObjectProperty(RDFVocabulary.TIME.INTERVAL_DISJOINT), + new OWLNamedIndividual(new RDFResource("ex:IntervalA")), + new OWLNamedIndividual(new RDFResource("ex:IntervalB"))), + new OWLObjectPropertyAssertion( + new OWLObjectProperty(RDFVocabulary.TIME.NOT_DISJOINT), + new OWLNamedIndividual(new RDFResource("ex:IntervalA")), + new OWLNamedIndividual(new RDFResource("ex:IntervalB"))), //clash + new OWLObjectPropertyAssertion( + new OWLObjectProperty(RDFVocabulary.TIME.INTERVAL_DISJOINT), + new OWLNamedIndividual(new RDFResource("ex:IntervalA")), + new OWLNamedIndividual(new RDFResource("ex:IntervalC"))), + ]); + + TIMEValidator validator = new TIMEValidator(); + validator.AddRule(TIMEEnums.TIMEValidatorRules.IntervalDisjointAnalysis); + + List issues = await validator.ApplyToOntologyAsync(ontology); + string testingRelation = "time:intervalDisjoint"; + string clashingRelation = "time:notDisjoint"; + + Assert.IsNotNull(issues); + Assert.IsTrue(issues.Count == 1); + Assert.IsTrue(issues[0].Severity == OWLEnums.OWLIssueSeverity.Error); + Assert.IsTrue(string.Equals(issues[0].RuleName, TIMEIntervalDisjointAnalysisRule.rulename)); + Assert.IsTrue(string.Equals(issues[0].Description, string.Format(TIMEIntervalDisjointAnalysisRule.rulesugg, clashingRelation))); + Assert.IsTrue(string.Equals(issues[0].Suggestion, $"TIME intervals 'ex:IntervalA' and 'ex:IntervalB' should be adjusted to not clash on temporal relations ({testingRelation} VS {clashingRelation})")); + } } } \ No newline at end of file diff --git a/OWLSharp/Extensions/TIME/Validator/RuleSet/TIMEIntervalDisjointAnalysisRule.cs b/OWLSharp/Extensions/TIME/Validator/RuleSet/TIMEIntervalDisjointAnalysisRule.cs new file mode 100644 index 00000000..7065d5c3 --- /dev/null +++ b/OWLSharp/Extensions/TIME/Validator/RuleSet/TIMEIntervalDisjointAnalysisRule.cs @@ -0,0 +1,214 @@ +/* + Copyright 2014-2025 Marco De Salvo + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +using OWLSharp.Ontology; +using OWLSharp.Reasoner; +using OWLSharp.Validator; +using RDFSharp.Model; +using RDFSharp.Query; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace OWLSharp.Extensions.TIME +{ + internal class TIMEIntervalDisjointAnalysisRule + { + internal static readonly string rulename = TIMEEnums.TIMEValidatorRules.IntervalDisjointAnalysis.ToString(); + internal static readonly string rulesugg = "There should not be OWL-TIME intervals having a clash in temporal relations (time:intervalDisjoint VS {0}"; + + internal static async Task> ExecuteRuleAsync(OWLOntology ontology, Dictionary> cacheRegistry) + { + List issues = new List(); + List violations = new List(); + + #region Utilities + async Task ExecuteRuleBodyAsync(string ruleDescription, string ruleSugg, SWRLObjectPropertyAtom clashingAtom, string shortClashingProperty) + { + SWRLRule clashRule = new SWRLRule( + new RDFPlainLiteral(nameof(TIMEIntervalContainsAnalysisRule)), + new RDFPlainLiteral(ruleDescription), + new SWRLAntecedent() + { + Atoms = new List() + { + new SWRLClassAtom( + new OWLClass(RDFVocabulary.TIME.INTERVAL), + new SWRLVariableArgument(new RDFVariable("?I1"))) { IndividualsCache = cacheRegistry["INTERVALS"] }, + new SWRLClassAtom( + new OWLClass(RDFVocabulary.TIME.INTERVAL), + new SWRLVariableArgument(new RDFVariable("?I2"))) { IndividualsCache = cacheRegistry["INTERVALS"] }, + new SWRLObjectPropertyAtom( + new OWLObjectProperty(RDFVocabulary.TIME.INTERVAL_DISJOINT), + new SWRLVariableArgument(new RDFVariable("?I1")), + new SWRLVariableArgument(new RDFVariable("?I2"))), + clashingAtom, + }, + BuiltIns = new List() + { + SWRLBuiltIn.NotEqual( + new SWRLVariableArgument(new RDFVariable("?I1")), + new SWRLVariableArgument(new RDFVariable("?I2"))) + } + }, + new SWRLConsequent() + { + Atoms = new List() + { + new SWRLObjectPropertyAtom( + new OWLObjectProperty(TIMEValidator.ViolationIRI), + new SWRLVariableArgument(new RDFVariable("?I1")), + new SWRLVariableArgument(new RDFVariable("?I2"))) + } + }); + violations.AddRange(await clashRule.ApplyToOntologyAsync(ontology)); + violations.ForEach(violation => issues.Add( + new OWLIssue( + OWLEnums.OWLIssueSeverity.Error, + rulename, + ruleSugg, + $"TIME intervals '{((OWLObjectPropertyAssertion)violation.Axiom).SourceIndividualExpression.GetIRI()}' and '{((OWLObjectPropertyAssertion)violation.Axiom).TargetIndividualExpression.GetIRI()}' should be adjusted to not clash on temporal relations (time:intervalDisjoint VS {shortClashingProperty})" + ))); + violations.Clear(); + } + #endregion + + await ExecuteRuleBodyAsync( + "INTERVAL_DISJOINT(?I1,?I2) ^ INTERVAL_CONTAINS(?I2,?I1) -> ERROR", + string.Format(rulesugg, "time:intervalContains"), + new SWRLObjectPropertyAtom( + new OWLObjectProperty(RDFVocabulary.TIME.INTERVAL_CONTAINS), + new SWRLVariableArgument(new RDFVariable("?I1")), + new SWRLVariableArgument(new RDFVariable("?I2"))), + "time:intervalContains"); + + await ExecuteRuleBodyAsync( + "INTERVAL_DISJOINT(?I1,?I2) ^ INTERVAL_DURING(?I1,?I2) -> ERROR", + string.Format(rulesugg, "time:intervalDuring"), + new SWRLObjectPropertyAtom( + new OWLObjectProperty(RDFVocabulary.TIME.INTERVAL_DURING), + new SWRLVariableArgument(new RDFVariable("?I1")), + new SWRLVariableArgument(new RDFVariable("?I2"))), + "time:intervalDuring"); + + await ExecuteRuleBodyAsync( + "INTERVAL_DISJOINT(?I1,?I2) ^ INTERVAL_EQUALS(?I1,?I2) -> ERROR", + string.Format(rulesugg, "time:intervalEquals"), + new SWRLObjectPropertyAtom( + new OWLObjectProperty(RDFVocabulary.TIME.INTERVAL_EQUALS), + new SWRLVariableArgument(new RDFVariable("?I1")), + new SWRLVariableArgument(new RDFVariable("?I2"))), + "time:intervalEquals"); + + await ExecuteRuleBodyAsync( + "INTERVAL_DISJOINT(?I1,?I2) ^ INTERVAL_FINISHED_BY(?I1,?I2) -> ERROR", + string.Format(rulesugg, "time:intervalFinishedBy"), + new SWRLObjectPropertyAtom( + new OWLObjectProperty(RDFVocabulary.TIME.INTERVAL_FINISHED_BY), + new SWRLVariableArgument(new RDFVariable("?I1")), + new SWRLVariableArgument(new RDFVariable("?I2"))), + "time:intervalFinishedBy"); + + await ExecuteRuleBodyAsync( + "INTERVAL_DISJOINT(?I1,?I2) ^ INTERVAL_FINISHES(?I1,?I2) -> ERROR", + string.Format(rulesugg, "time:intervalFinishes"), + new SWRLObjectPropertyAtom( + new OWLObjectProperty(RDFVocabulary.TIME.INTERVAL_FINISHES), + new SWRLVariableArgument(new RDFVariable("?I1")), + new SWRLVariableArgument(new RDFVariable("?I2"))), + "time:intervalFinishes"); + + await ExecuteRuleBodyAsync( + "INTERVAL_DISJOINT(?I1,?I2) ^ HAS_INSIDE(?I2,?I1) -> ERROR", + string.Format(rulesugg, "time:hasInside"), + new SWRLObjectPropertyAtom( + new OWLObjectProperty(RDFVocabulary.TIME.HAS_INSIDE), + new SWRLVariableArgument(new RDFVariable("?I2")), + new SWRLVariableArgument(new RDFVariable("?I1"))), + "time:hasInside"); + + await ExecuteRuleBodyAsync( + "INTERVAL_DISJOINT(?I1,?I2) ^ INTERVAL_IN(?I1,?I2) -> ERROR", + string.Format(rulesugg, "time:intervalIn"), + new SWRLObjectPropertyAtom( + new OWLObjectProperty(RDFVocabulary.TIME.INTERVAL_IN), + new SWRLVariableArgument(new RDFVariable("?I1")), + new SWRLVariableArgument(new RDFVariable("?I2"))), + "time:intervalIn"); + + await ExecuteRuleBodyAsync( + "INTERVAL_DISJOINT(?I1,?I2) ^ INTERVAL_MEETS(?I1,?I2) -> ERROR", + string.Format(rulesugg, "time:intervalMeets"), + new SWRLObjectPropertyAtom( + new OWLObjectProperty(RDFVocabulary.TIME.INTERVAL_MEETS), + new SWRLVariableArgument(new RDFVariable("?I1")), + new SWRLVariableArgument(new RDFVariable("?I2"))), + "time:intervalMeets"); + + await ExecuteRuleBodyAsync( + "INTERVAL_DISJOINT(?I1,?I2) ^ INTERVAL_MET_BY(?I1,?I2) -> ERROR", + string.Format(rulesugg, "time:intervalMetBy"), + new SWRLObjectPropertyAtom( + new OWLObjectProperty(RDFVocabulary.TIME.INTERVAL_MET_BY), + new SWRLVariableArgument(new RDFVariable("?I1")), + new SWRLVariableArgument(new RDFVariable("?I2"))), + "time:intervalMetBy"); + + await ExecuteRuleBodyAsync( + "INTERVAL_DISJOINT(?I1,?I2) ^ NOT_DISJOINT(?I1,?I2) -> ERROR", + string.Format(rulesugg, "time:notDisjoint"), + new SWRLObjectPropertyAtom( + new OWLObjectProperty(RDFVocabulary.TIME.NOT_DISJOINT), + new SWRLVariableArgument(new RDFVariable("?I1")), + new SWRLVariableArgument(new RDFVariable("?I2"))), + "time:notDisjoint"); + + await ExecuteRuleBodyAsync( + "INTERVAL_DISJOINT(?I1,?I2) ^ INTERVAL_OVERLAPPED_BY(?I1,?I2) -> ERROR", + string.Format(rulesugg, "time:intervalOverlappedBy"), + new SWRLObjectPropertyAtom( + new OWLObjectProperty(RDFVocabulary.TIME.INTERVAL_OVERLAPPED_BY), + new SWRLVariableArgument(new RDFVariable("?I1")), + new SWRLVariableArgument(new RDFVariable("?I2"))), + "time:intervalOverlappedBy"); + + await ExecuteRuleBodyAsync( + "INTERVAL_DISJOINT(?I1,?I2) ^ INTERVAL_OVERLAPS(?I1,?I2) -> ERROR", + string.Format(rulesugg, "time:intervalOverlaps"), + new SWRLObjectPropertyAtom( + new OWLObjectProperty(RDFVocabulary.TIME.INTERVAL_OVERLAPS), + new SWRLVariableArgument(new RDFVariable("?I1")), + new SWRLVariableArgument(new RDFVariable("?I2"))), + "time:intervalOverlaps"); + + await ExecuteRuleBodyAsync( + "INTERVAL_DISJOINT(?I1,?I2) ^ INTERVAL_STARTED_BY(?I1,?I2) -> ERROR", + string.Format(rulesugg, "time:intervalStartedBy"), + new SWRLObjectPropertyAtom( + new OWLObjectProperty(RDFVocabulary.TIME.INTERVAL_STARTED_BY), + new SWRLVariableArgument(new RDFVariable("?I1")), + new SWRLVariableArgument(new RDFVariable("?I2"))), + "time:intervalStartedBy"); + + await ExecuteRuleBodyAsync( + "INTERVAL_DISJOINT(?I1,?I2) ^ INTERVAL_STARTS(?I1,?I2) -> ERROR", + string.Format(rulesugg, "time:intervalStarts"), + new SWRLObjectPropertyAtom( + new OWLObjectProperty(RDFVocabulary.TIME.INTERVAL_STARTS), + new SWRLVariableArgument(new RDFVariable("?I1")), + new SWRLVariableArgument(new RDFVariable("?I2"))), + "time:intervalStarts"); + + return issues; + } + } +} \ No newline at end of file diff --git a/OWLSharp/Extensions/TIME/Validator/TIMEValidator.cs b/OWLSharp/Extensions/TIME/Validator/TIMEValidator.cs index 70ad31fc..e8fca74b 100644 --- a/OWLSharp/Extensions/TIME/Validator/TIMEValidator.cs +++ b/OWLSharp/Extensions/TIME/Validator/TIMEValidator.cs @@ -84,10 +84,10 @@ await Rules.ParallelForEachAsync(async (rule) => case TIMEEnums.TIMEValidatorRules.IntervalContainsAnalysis: issueRegistry[TIMEEnums.TIMEValidatorRules.IntervalContainsAnalysis.ToString()] = await TIMEIntervalContainsAnalysisRule.ExecuteRuleAsync(ontology, cacheRegistry); break; - /*case TIMEEnums.TIMEValidatorRules.IntervalDisjointAnalysis: + case TIMEEnums.TIMEValidatorRules.IntervalDisjointAnalysis: issueRegistry[TIMEEnums.TIMEValidatorRules.IntervalDisjointAnalysis.ToString()] = await TIMEIntervalDisjointAnalysisRule.ExecuteRuleAsync(ontology, cacheRegistry); break; - case TIMEEnums.TIMEValidatorRules.IntervalDuringAnalysis: + /*case TIMEEnums.TIMEValidatorRules.IntervalDuringAnalysis: issueRegistry[TIMEEnums.TIMEValidatorRules.IntervalDuringAnalysis.ToString()] = await TIMEIntervalDuringAnalysisRule.ExecuteRuleAsync(ontology, cacheRegistry); break; case TIMEEnums.TIMEValidatorRules.IntervalEqualsAnalysis: