From 01539b462d9d8ce9e418fb3605ac4fae834a1306 Mon Sep 17 00:00:00 2001 From: Marco De Salvo Date: Wed, 29 Jan 2025 15:14:10 +0100 Subject: [PATCH] #361 Add support for HasLangDir expression from SPARQL-1.2 --- .../RDFHasLangDirExpressionTest.cs | 332 ++++++++++++++++++ .../Expressions/RDFHasLangDirExpression.cs | 100 ++++++ .../Expressions/RDFLangDirExpression.cs | 8 +- 3 files changed, 436 insertions(+), 4 deletions(-) create mode 100644 RDFSharp.Test/Query/Mirella/Algebra/Expressions/RDFHasLangDirExpressionTest.cs create mode 100644 RDFSharp/Query/Mirella/Algebra/Expressions/RDFHasLangDirExpression.cs diff --git a/RDFSharp.Test/Query/Mirella/Algebra/Expressions/RDFHasLangDirExpressionTest.cs b/RDFSharp.Test/Query/Mirella/Algebra/Expressions/RDFHasLangDirExpressionTest.cs new file mode 100644 index 00000000..8e2a810a --- /dev/null +++ b/RDFSharp.Test/Query/Mirella/Algebra/Expressions/RDFHasLangDirExpressionTest.cs @@ -0,0 +1,332 @@ +/* + Copyright 2012-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 Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Data; +using RDFSharp.Model; +using RDFSharp.Query; + +namespace RDFSharp.Test.Query +{ + [TestClass] + public class RDFHasLangDirExpressionTest + { + #region Tests + [TestMethod] + public void ShouldCreateHasLangDirExpressionWithExpression() + { + RDFHasLangDirExpression expression = new RDFHasLangDirExpression( + new RDFAddExpression(new RDFVariable("?V1"), new RDFVariable("?V2"))); + + Assert.IsNotNull(expression); + Assert.IsNotNull(expression.LeftArgument); + Assert.IsNull(expression.RightArgument); + Assert.IsTrue(expression.ToString().Equals("(HASLANGDIR((?V1 + ?V2)))")); + Assert.IsTrue(expression.ToString([]).Equals("(HASLANGDIR((?V1 + ?V2)))")); + } + + [TestMethod] + public void ShouldCreateHasLangDirExpressionWithVariable() + { + RDFHasLangDirExpression expression = new RDFHasLangDirExpression( + new RDFVariable("?V1")); + + Assert.IsNotNull(expression); + Assert.IsNotNull(expression.LeftArgument); + Assert.IsNull(expression.RightArgument); + Assert.IsTrue(expression.ToString().Equals("(HASLANGDIR(?V1))")); + Assert.IsTrue(expression.ToString([]).Equals("(HASLANGDIR(?V1))")); + } + + [TestMethod] + public void ShouldThrowExceptionOnCreatingHasLangDirExpressionWithExpressionBecauseNullLeftArgument() + => Assert.ThrowsException(() => new RDFHasLangDirExpression(null as RDFExpression)); + + [TestMethod] + public void ShouldThrowExceptionOnCreatingHasLangDirExpressionWithVariableBecauseNullLeftArgument() + => Assert.ThrowsException(() => new RDFHasLangDirExpression(null as RDFVariable)); + + [TestMethod] + public void ShouldApplyExpressionWithExpressionAndCalculateResultOnNull() + { + DataTable table = new DataTable(); + table.Columns.Add("?A", typeof(string)); + DataRow row = table.NewRow(); + row["?A"] = null; + table.Rows.Add(row); + table.AcceptChanges(); + + RDFHasLangDirExpression expression = new RDFHasLangDirExpression( + new RDFVariableExpression(new RDFVariable("?A"))); + RDFPatternMember expressionResult = expression.ApplyExpression(table.Rows[0]); + + Assert.IsNotNull(expressionResult); + Assert.IsTrue(expressionResult.Equals(RDFTypedLiteral.False)); + } + + [TestMethod] + public void ShouldApplyExpressionWithExpressionAndCalculateResultOnResource() + { + DataTable table = new DataTable(); + table.Columns.Add("?A", typeof(string)); + DataRow row = table.NewRow(); + row["?A"] = new RDFResource("ex:subj").ToString(); + table.Rows.Add(row); + table.AcceptChanges(); + + RDFHasLangDirExpression expression = new RDFHasLangDirExpression( + new RDFVariableExpression(new RDFVariable("?A"))); + RDFPatternMember expressionResult = expression.ApplyExpression(table.Rows[0]); + + Assert.IsNotNull(expressionResult); + Assert.IsTrue(expressionResult.Equals(RDFTypedLiteral.False)); + } + + [TestMethod] + public void ShouldApplyExpressionWithExpressionAndCalculateResultOnPlainLiteral() + { + DataTable table = new DataTable(); + table.Columns.Add("?A", typeof(string)); + DataRow row = table.NewRow(); + row["?A"] = new RDFPlainLiteral("hello").ToString(); + table.Rows.Add(row); + table.AcceptChanges(); + + RDFHasLangDirExpression expression = new RDFHasLangDirExpression( + new RDFVariableExpression(new RDFVariable("?A"))); + RDFPatternMember expressionResult = expression.ApplyExpression(table.Rows[0]); + + Assert.IsNotNull(expressionResult); + Assert.IsTrue(expressionResult.Equals(RDFTypedLiteral.False)); + } + + [TestMethod] + public void ShouldApplyExpressionWithExpressionAndCalculateResultOnPlainLiteralWithLanguage() + { + DataTable table = new DataTable(); + table.Columns.Add("?A", typeof(string)); + DataRow row = table.NewRow(); + row["?A"] = new RDFPlainLiteral("hello","en-US").ToString(); + table.Rows.Add(row); + table.AcceptChanges(); + + RDFHasLangDirExpression expression = new RDFHasLangDirExpression( + new RDFVariableExpression(new RDFVariable("?A"))); + RDFPatternMember expressionResult = expression.ApplyExpression(table.Rows[0]); + + Assert.IsNotNull(expressionResult); + Assert.IsTrue(expressionResult.Equals(RDFTypedLiteral.False)); + } + + [TestMethod] + public void ShouldApplyExpressionWithExpressionAndCalculateResultOnPlainLiteralWithLanguageLTR() + { + DataTable table = new DataTable(); + table.Columns.Add("?A", typeof(string)); + DataRow row = table.NewRow(); + row["?A"] = new RDFPlainLiteral("hello", "en-US--ltr").ToString(); + table.Rows.Add(row); + table.AcceptChanges(); + + RDFHasLangDirExpression expression = new RDFHasLangDirExpression( + new RDFVariableExpression(new RDFVariable("?A"))); + RDFPatternMember expressionResult = expression.ApplyExpression(table.Rows[0]); + + Assert.IsNotNull(expressionResult); + Assert.IsTrue(expressionResult.Equals(RDFTypedLiteral.True)); + } + + [TestMethod] + public void ShouldApplyExpressionWithExpressionAndCalculateResultOnPlainLiteralWithLanguageRTL() + { + DataTable table = new DataTable(); + table.Columns.Add("?A", typeof(string)); + DataRow row = table.NewRow(); + row["?A"] = new RDFPlainLiteral("hello", "en-US--rtl").ToString(); + table.Rows.Add(row); + table.AcceptChanges(); + + RDFHasLangDirExpression expression = new RDFHasLangDirExpression( + new RDFVariableExpression(new RDFVariable("?A"))); + RDFPatternMember expressionResult = expression.ApplyExpression(table.Rows[0]); + + Assert.IsNotNull(expressionResult); + Assert.IsTrue(expressionResult.Equals(RDFTypedLiteral.True)); + } + + [TestMethod] + public void ShouldApplyExpressionWithExpressionAndCalculateResultOnTypedLiteral() + { + DataTable table = new DataTable(); + table.Columns.Add("?A", typeof(string)); + DataRow row = table.NewRow(); + row["?A"] = new RDFTypedLiteral("hello", RDFModelEnums.RDFDatatypes.RDFS_LITERAL).ToString(); + table.Rows.Add(row); + table.AcceptChanges(); + + RDFHasLangDirExpression expression = new RDFHasLangDirExpression( + new RDFVariableExpression(new RDFVariable("?A"))); + RDFPatternMember expressionResult = expression.ApplyExpression(table.Rows[0]); + + Assert.IsNotNull(expressionResult); + Assert.IsTrue(expressionResult.Equals(RDFTypedLiteral.False)); + } + + [TestMethod] + public void ShouldApplyExpressionWithExpressionAndNotCalculateResultBecauseNotBoundVariable() + { + DataTable table = new DataTable(); + table.Columns.Add("?A", typeof(string)); + DataRow row = table.NewRow(); + row["?A"] = new RDFTypedLiteral("45", RDFModelEnums.RDFDatatypes.XSD_NORMALIZEDSTRING).ToString(); + table.Rows.Add(row); + table.AcceptChanges(); + + RDFHasLangDirExpression expression = new RDFHasLangDirExpression( + new RDFVariableExpression(new RDFVariable("?Q"))); + RDFPatternMember expressionResult = expression.ApplyExpression(table.Rows[0]); + + Assert.IsNotNull(expressionResult); + Assert.IsTrue(expressionResult.Equals(RDFTypedLiteral.False)); + } + + [TestMethod] + public void ShouldApplyExpressionWithVariableAndCalculateResultOnResource() + { + DataTable table = new DataTable(); + table.Columns.Add("?A", typeof(string)); + DataRow row = table.NewRow(); + row["?A"] = new RDFResource("ex:subj").ToString(); + table.Rows.Add(row); + table.AcceptChanges(); + + RDFHasLangDirExpression expression = new RDFHasLangDirExpression( + new RDFVariable("?A")); + RDFPatternMember expressionResult = expression.ApplyExpression(table.Rows[0]); + + Assert.IsNotNull(expressionResult); + Assert.IsTrue(expressionResult.Equals(RDFTypedLiteral.False)); + } + + [TestMethod] + public void ShouldApplyExpressionWithVariableAndCalculateResultOnPlainLiteral() + { + DataTable table = new DataTable(); + table.Columns.Add("?A", typeof(string)); + DataRow row = table.NewRow(); + row["?A"] = new RDFPlainLiteral("hello").ToString(); + table.Rows.Add(row); + table.AcceptChanges(); + + RDFHasLangDirExpression expression = new RDFHasLangDirExpression( + new RDFVariable("?A")); + RDFPatternMember expressionResult = expression.ApplyExpression(table.Rows[0]); + + Assert.IsNotNull(expressionResult); + Assert.IsTrue(expressionResult.Equals(RDFTypedLiteral.False)); + } + + [TestMethod] + public void ShouldApplyExpressionWithVariableAndCalculateResultOnPlainLiteralWithLanguage() + { + DataTable table = new DataTable(); + table.Columns.Add("?A", typeof(string)); + DataRow row = table.NewRow(); + row["?A"] = new RDFPlainLiteral("hello", "en-US").ToString(); + table.Rows.Add(row); + table.AcceptChanges(); + + RDFHasLangDirExpression expression = new RDFHasLangDirExpression( + new RDFVariable("?A")); + RDFPatternMember expressionResult = expression.ApplyExpression(table.Rows[0]); + + Assert.IsNotNull(expressionResult); + Assert.IsTrue(expressionResult.Equals(RDFTypedLiteral.False)); + } + + [TestMethod] + public void ShouldApplyExpressionWithVariableAndCalculateResultOnPlainLiteralWithLanguageLTR() + { + DataTable table = new DataTable(); + table.Columns.Add("?A", typeof(string)); + DataRow row = table.NewRow(); + row["?A"] = new RDFPlainLiteral("hello", "en-US--ltr").ToString(); + table.Rows.Add(row); + table.AcceptChanges(); + + RDFHasLangDirExpression expression = new RDFHasLangDirExpression( + new RDFVariable("?A")); + RDFPatternMember expressionResult = expression.ApplyExpression(table.Rows[0]); + + Assert.IsNotNull(expressionResult); + Assert.IsTrue(expressionResult.Equals(RDFTypedLiteral.True)); + } + + [TestMethod] + public void ShouldApplyExpressionWithVariableAndCalculateResultOnPlainLiteralWithLanguageRTL() + { + DataTable table = new DataTable(); + table.Columns.Add("?A", typeof(string)); + DataRow row = table.NewRow(); + row["?A"] = new RDFPlainLiteral("hello", "en-US--rtl").ToString(); + table.Rows.Add(row); + table.AcceptChanges(); + + RDFHasLangDirExpression expression = new RDFHasLangDirExpression( + new RDFVariable("?A")); + RDFPatternMember expressionResult = expression.ApplyExpression(table.Rows[0]); + + Assert.IsNotNull(expressionResult); + Assert.IsTrue(expressionResult.Equals(RDFTypedLiteral.True)); + } + + [TestMethod] + public void ShouldApplyExpressionWithVariableAndCalculateResultOnTypedLiteral() + { + DataTable table = new DataTable(); + table.Columns.Add("?A", typeof(string)); + DataRow row = table.NewRow(); + row["?A"] = new RDFTypedLiteral("hello", RDFModelEnums.RDFDatatypes.RDFS_LITERAL).ToString(); + table.Rows.Add(row); + table.AcceptChanges(); + + RDFHasLangDirExpression expression = new RDFHasLangDirExpression( + new RDFVariable("?A")); + RDFPatternMember expressionResult = expression.ApplyExpression(table.Rows[0]); + + Assert.IsNotNull(expressionResult); + Assert.IsTrue(expressionResult.Equals(RDFTypedLiteral.False)); + } + + [TestMethod] + public void ShouldApplyExpressionWithVariableAndNotCalculateResultBecauseNotBoundVariable() + { + DataTable table = new DataTable(); + table.Columns.Add("?A", typeof(string)); + DataRow row = table.NewRow(); + row["?A"] = new RDFTypedLiteral("45", RDFModelEnums.RDFDatatypes.XSD_NORMALIZEDSTRING).ToString(); + table.Rows.Add(row); + table.AcceptChanges(); + + RDFHasLangDirExpression expression = new RDFHasLangDirExpression( + new RDFVariable("?Q")); + RDFPatternMember expressionResult = expression.ApplyExpression(table.Rows[0]); + + Assert.IsNull(expressionResult); + } + #endregion + } +} \ No newline at end of file diff --git a/RDFSharp/Query/Mirella/Algebra/Expressions/RDFHasLangDirExpression.cs b/RDFSharp/Query/Mirella/Algebra/Expressions/RDFHasLangDirExpression.cs new file mode 100644 index 00000000..ba039685 --- /dev/null +++ b/RDFSharp/Query/Mirella/Algebra/Expressions/RDFHasLangDirExpression.cs @@ -0,0 +1,100 @@ +/* + Copyright 2012-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 RDFSharp.Model; +using System.Collections.Generic; +using System.Data; +using System.Text; + +namespace RDFSharp.Query +{ + /// + /// RDFHasLangDirExpression represents a direction-detector function to be applied on a query results table. + /// + public class RDFHasLangDirExpression : RDFExpression + { + #region Ctors + /// + /// Default-ctor to build a hasLangDir function with given arguments + /// + public RDFHasLangDirExpression(RDFExpression leftArgument) : base(leftArgument, null as RDFExpression) { } + + /// + /// Default-ctor to build a hasLangDir function with given arguments + /// + public RDFHasLangDirExpression(RDFVariable leftArgument) : base(leftArgument, null as RDFExpression) { } + #endregion + + #region Interfaces + /// + /// Gives the string representation of the hasLangDir function + /// + public override string ToString() + => ToString(new List()); + internal override string ToString(List prefixes) + { + StringBuilder sb = new StringBuilder(); + + //(HASLANGDIR(L)) + sb.Append("(HASLANGDIR("); + if (LeftArgument is RDFExpression expLeftArgument) + sb.Append(expLeftArgument.ToString(prefixes)); + else + sb.Append(RDFQueryPrinter.PrintPatternMember((RDFPatternMember)LeftArgument, prefixes)); + sb.Append("))"); + + return sb.ToString(); + } + #endregion + + #region Methods + /// + /// Applies the hasLangDir function on the given datarow + /// + internal override RDFPatternMember ApplyExpression(DataRow row) + { + RDFTypedLiteral expressionResult = null; + + #region Guards + if (LeftArgument is RDFVariable && !row.Table.Columns.Contains(LeftArgument.ToString())) + return expressionResult; + #endregion + + try + { + #region Evaluate Arguments + //Evaluate left argument (Expression VS Variable) + RDFPatternMember leftArgumentPMember = null; + if (LeftArgument is RDFExpression leftArgumentExpression) + leftArgumentPMember = leftArgumentExpression.ApplyExpression(row); + else + leftArgumentPMember = RDFQueryUtilities.ParseRDFPatternMember(row[LeftArgument.ToString()].ToString()); + #endregion + + #region Calculate Result + if (leftArgumentPMember is RDFPlainLiteral leftArgumentPMemberPLiteral && leftArgumentPMemberPLiteral.HasDirection()) + expressionResult = RDFTypedLiteral.True; + else + expressionResult = RDFTypedLiteral.False; + #endregion + } + catch { /* Just a no-op, since type errors are normal when trying to face variable's bindings */ } + + return expressionResult; + } + #endregion + } +} \ No newline at end of file diff --git a/RDFSharp/Query/Mirella/Algebra/Expressions/RDFLangDirExpression.cs b/RDFSharp/Query/Mirella/Algebra/Expressions/RDFLangDirExpression.cs index 0ec367f2..2cb2f8f4 100644 --- a/RDFSharp/Query/Mirella/Algebra/Expressions/RDFLangDirExpression.cs +++ b/RDFSharp/Query/Mirella/Algebra/Expressions/RDFLangDirExpression.cs @@ -28,19 +28,19 @@ public class RDFLangDirExpression : RDFExpression { #region Ctors /// - /// Default-ctor to build a langdir function with given arguments + /// Default-ctor to build a langDir function with given arguments /// public RDFLangDirExpression(RDFExpression leftArgument) : base(leftArgument, null as RDFExpression) { } /// - /// Default-ctor to build a langdir function with given arguments + /// Default-ctor to build a langDir function with given arguments /// public RDFLangDirExpression(RDFVariable leftArgument) : base(leftArgument, null as RDFExpression) { } #endregion #region Interfaces /// - /// Gives the string representation of the langdir function + /// Gives the string representation of the langDir function /// public override string ToString() => ToString(new List()); @@ -62,7 +62,7 @@ internal override string ToString(List prefixes) #region Methods /// - /// Applies the langdir function on the given datarow + /// Applies the langDir function on the given datarow /// internal override RDFPatternMember ApplyExpression(DataRow row) {