Add RDF.Literal.matches?/3
This commit is contained in:
parent
d21e859c53
commit
489e964c6d
3 changed files with 126 additions and 1 deletions
11
CHANGELOG.md
11
CHANGELOG.md
|
@ -5,6 +5,17 @@ This project adheres to [Semantic Versioning](http://semver.org/) and
|
|||
[Keep a CHANGELOG](http://keepachangelog.com).
|
||||
|
||||
|
||||
## Unreleased
|
||||
|
||||
### Added
|
||||
|
||||
- `RDF.Literal.matches?/3` for XQuery regex pattern matching
|
||||
|
||||
|
||||
[Compare v0.6.0...HEAD](https://github.com/marcelotto/rdf-ex/compare/v0.6.0...HEAD)
|
||||
|
||||
|
||||
|
||||
## 0.6.0 - 2019-04-06
|
||||
|
||||
### Added
|
||||
|
|
|
@ -319,6 +319,63 @@ defmodule RDF.Literal do
|
|||
|
||||
def compare(_, _), do: nil
|
||||
|
||||
|
||||
@doc """
|
||||
Matches the string representation of the given value against a XPath and XQuery regular expression pattern.
|
||||
|
||||
The regular expression language is defined in _XQuery 1.0 and XPath 2.0 Functions and Operators_.
|
||||
|
||||
The `pattern` and the optional `flags` can be given as an Elixir string or as
|
||||
`xsd:string` `RDF.Literal`s.
|
||||
|
||||
see <https://www.w3.org/TR/xpath-functions/#func-matches>
|
||||
"""
|
||||
def matches?(value, pattern, flags \\ "")
|
||||
|
||||
def matches?(value, %RDF.Literal{datatype: @xsd_string} = pattern, flags),
|
||||
do: matches?(value, pattern.value, flags)
|
||||
|
||||
def matches?(value, pattern, %RDF.Literal{datatype: @xsd_string} = flags),
|
||||
do: matches?(value, pattern, flags.value)
|
||||
|
||||
def matches?(value, pattern, flags) when is_binary(pattern) and is_binary(flags) do
|
||||
string = to_string(value)
|
||||
case xpath_pattern(pattern, flags) do
|
||||
{:regex, regex} ->
|
||||
Regex.match?(regex, string)
|
||||
|
||||
{:q, pattern} ->
|
||||
String.contains?(string, pattern)
|
||||
|
||||
{:qi, pattern} ->
|
||||
string
|
||||
|> String.downcase()
|
||||
|> String.contains?(String.downcase(pattern))
|
||||
|
||||
_ ->
|
||||
raise "Invalid XQuery regex pattern or flags"
|
||||
end
|
||||
end
|
||||
|
||||
defp xpath_pattern(pattern, flags) do
|
||||
q_pattern(pattern, flags) || xpath_regex_pattern(pattern, flags)
|
||||
end
|
||||
|
||||
defp q_pattern(pattern, flags) do
|
||||
if String.contains?(flags, "q") and String.replace(flags, ~r/[qi]/, "") == "" do
|
||||
{(if String.contains?(flags, "i"), do: :qi, else: :q), pattern}
|
||||
end
|
||||
end
|
||||
|
||||
defp xpath_regex_pattern(pattern, flags) do
|
||||
with {:ok, regex} <- Regex.compile(pattern, xpath_regex_flags(flags)) do
|
||||
{:regex, regex}
|
||||
end
|
||||
end
|
||||
|
||||
defp xpath_regex_flags(flags) do
|
||||
String.replace(flags, "q", "") <> "u"
|
||||
end
|
||||
end
|
||||
|
||||
defimpl String.Chars, for: RDF.Literal do
|
||||
|
|
|
@ -232,6 +232,64 @@ defmodule RDF.LiteralTest do
|
|||
end
|
||||
|
||||
|
||||
@poem RDF.string """
|
||||
<poem author="Wilhelm Busch">
|
||||
Kaum hat dies der Hahn gesehen,
|
||||
Fängt er auch schon an zu krähen:
|
||||
Kikeriki! Kikikerikih!!
|
||||
Tak, tak, tak! - da kommen sie.
|
||||
</poem>
|
||||
"""
|
||||
|
||||
describe "matches?" do
|
||||
test "without flags" do
|
||||
[
|
||||
{~L"abracadabra", ~L"bra", true},
|
||||
{~L"abracadabra", ~L"^a.*a$", true},
|
||||
{~L"abracadabra", ~L"^bra", false},
|
||||
{@poem, ~L"Kaum.*krähen", false},
|
||||
{@poem, ~L"^Kaum.*gesehen,$", false},
|
||||
|
||||
{~L"abracadabra"en, ~L"bra", true},
|
||||
{"abracadabra", "bra", true},
|
||||
{RDF.integer("42"), ~L"4", true},
|
||||
{RDF.integer("42"), ~L"en", false},
|
||||
]
|
||||
|> Enum.each(fn {literal, pattern, expected_result} ->
|
||||
result = Literal.matches?(literal, pattern)
|
||||
assert result == expected_result,
|
||||
"expected RDF.Literal.matches?(#{inspect literal}, #{inspect pattern}) to return #{inspect expected_result}, but got #{result}"
|
||||
end)
|
||||
end
|
||||
|
||||
test "with flags" do
|
||||
[
|
||||
{@poem, ~L"Kaum.*krähen", ~L"s", true},
|
||||
{@poem, ~L"^Kaum.*gesehen,$", ~L"m", true},
|
||||
{@poem, ~L"kiki", ~L"i", true},
|
||||
]
|
||||
|> Enum.each(fn {literal, pattern, flags, result} ->
|
||||
assert Literal.matches?(literal, pattern, flags) == result
|
||||
end)
|
||||
end
|
||||
|
||||
test "with q flag" do
|
||||
[
|
||||
{~L"abcd", ~L".*", ~L"q", false},
|
||||
{~L"Mr. B. Obama", ~L"B. OBAMA", ~L"iq", true},
|
||||
|
||||
# If the q flag is used together with the m, s, or x flag, that flag has no effect.
|
||||
{~L"abcd", ~L".*", ~L"mq", true},
|
||||
{~L"abcd", ~L".*", ~L"qim", true},
|
||||
{~L"abcd", ~L".*", ~L"xqm", true},
|
||||
]
|
||||
|> Enum.each(fn {literal, pattern, flags, result} ->
|
||||
assert Literal.matches?(literal, pattern, flags) == result
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
describe "String.Chars protocol implementation" do
|
||||
Enum.each values(:all_plain), fn value ->
|
||||
@tag value: value
|
||||
|
@ -258,7 +316,6 @@ defmodule RDF.LiteralTest do
|
|||
assert to_string(literal) == rep
|
||||
end
|
||||
end)
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue