Add RDF.LangString.match_language?/2

This commit is contained in:
Marcel Otto 2018-07-10 00:18:16 +02:00
parent 4cea91e52f
commit 34898cd696
3 changed files with 106 additions and 2 deletions

View file

@ -18,7 +18,8 @@ This project adheres to [Semantic Versioning](http://semver.org/) and
all numeric literals, eg. arithmetic functions
- the logical operators and the Effective Boolean Value (EBV) coercion algorithm
from the XPath and SPARQL specs on `RDF.Boolean`
- `RDF.Term.equal?/2` and `RDF.Term.equal_value?/2`
- `RDF.Term.equal?/2` and `RDF.Term.equal_value?/2`
- `RDF.LangString.match_language?/2`
### Changed

View file

@ -22,4 +22,35 @@ defmodule RDF.LangString do
def valid?(%Literal{language: nil}), do: false
def valid?(literal), do: super(literal)
@doc """
Checks if a language tagged string literal or language tag matches a language range.
The check is performed per the basic filtering scheme defined in
[RFC4647](http://www.ietf.org/rfc/rfc4647.txt) section 3.3.1.
A language range is a basic language range per _Matching of Language Tags_ in
RFC4647 section 2.1.
A language range of `"*"` matches any non-empty language-tag string.
see <https://www.w3.org/TR/sparql11-query/#func-langMatches>
"""
def match_language?(language_tag, language_range)
def match_language?(%Literal{language: nil}, _), do: false
def match_language?(%Literal{language: language_tag}, language_range),
do: match_language?(language_tag, language_range)
def match_language?("", "*"), do: false
def match_language?(_, "*"), do: true
def match_language?(language_tag, language_range) do
language_tag = String.downcase(language_tag)
language_range = String.downcase(language_range)
case String.split(language_tag, language_range, parts: 2) do
[_, rest] -> rest == "" or String.starts_with?(rest, "-")
_ -> false
end
end
end

View file

@ -146,5 +146,77 @@ defmodule RDF.LangStringTest do
end
end
end
describe "match_language?/2" do
@positive_examples [
{"de", "de"},
{"de", "DE"},
{"de-DE", "de"},
{"de-CH", "de"},
{"de-CH", "de-ch"},
{"de-DE-1996", "de-de"},
]
@negative_examples [
{"en", "de"},
{"de", "de-CH"},
{"de-Deva", "de-de"},
{"de-Latn-DE", "de-de"},
]
test "with a language tag and a matching non-'*' language range" do
Enum.each @positive_examples, fn {language_tag, language_range} ->
assert LangString.match_language?(language_tag, language_range),
"expected language range #{inspect language_range} to match language tag #{inspect language_tag}, but it didn't"
end
end
test "with a language tag and a non-matching non-'*' language range" do
Enum.each @negative_examples, fn {language_tag, language_range} ->
refute LangString.match_language?(language_tag, language_range),
"expected language range #{inspect language_range} to not match language tag #{inspect language_tag}, but it did"
end
end
test "with a language tag and '*' language range" do
Enum.each @positive_examples ++ @negative_examples, fn {language_tag, _} ->
assert LangString.match_language?(language_tag, "*"),
~s[expected language range "*" to match language tag #{inspect language_tag}, but it didn't]
end
end
test "with the empty string as language tag" do
refute LangString.match_language?("", "de")
refute LangString.match_language?("", "*")
end
test "with the empty string as language range" do
refute LangString.match_language?("de", "")
end
test "with a language-tagged literal and a language range" do
Enum.each @positive_examples, fn {language_tag, language_range} ->
literal = RDF.lang_string("foo", language: language_tag)
assert LangString.match_language?(literal, language_range),
"expected language range #{inspect language_range} to match #{inspect literal}, but it didn't"
end
Enum.each @negative_examples, fn {language_tag, language_range} ->
literal = RDF.lang_string("foo", language: language_tag)
refute LangString.match_language?(literal, language_range),
"expected language range #{inspect language_range} to not match #{inspect literal}, but it did"
end
refute LangString.match_language?(RDF.lang_string("foo", language: ""), "de")
refute LangString.match_language?(RDF.lang_string("foo", language: ""), "*")
refute LangString.match_language?(RDF.lang_string("foo", language: nil), "de")
refute LangString.match_language?(RDF.lang_string("foo", language: nil), "*")
end
test "with a non-language-tagged literal" do
refute RDF.string("42") |> LangString.match_language?("de")
refute RDF.string("42") |> LangString.match_language?("")
refute RDF.integer("42") |> LangString.match_language?("de")
end
end
end