Fixed various issue with URI template, added corresponding tests
This commit is contained in:
@@ -153,6 +153,8 @@ feature -- Builder
|
|||||||
feature -- Match
|
feature -- Match
|
||||||
|
|
||||||
match (a_uri: STRING): detachable URI_TEMPLATE_MATCH_RESULT
|
match (a_uri: STRING): detachable URI_TEMPLATE_MATCH_RESULT
|
||||||
|
require
|
||||||
|
is_valid: is_valid
|
||||||
local
|
local
|
||||||
b: BOOLEAN
|
b: BOOLEAN
|
||||||
tpl: like template
|
tpl: like template
|
||||||
@@ -163,6 +165,8 @@ feature -- Match
|
|||||||
vv: STRING
|
vv: STRING
|
||||||
l_vars, l_path_vars, l_query_vars: HASH_TABLE [STRING, STRING]
|
l_vars, l_path_vars, l_query_vars: HASH_TABLE [STRING, STRING]
|
||||||
l_uri_count: INTEGER
|
l_uri_count: INTEGER
|
||||||
|
tpl_count: INTEGER
|
||||||
|
l_next_literal_separator: detachable STRING
|
||||||
do
|
do
|
||||||
--| Extract expansion parts "\\{([^\\}]*)\\}"
|
--| Extract expansion parts "\\{([^\\}]*)\\}"
|
||||||
analyze
|
analyze
|
||||||
@@ -173,8 +177,10 @@ feature -- Match
|
|||||||
b := True
|
b := True
|
||||||
l_uri_count := a_uri.count
|
l_uri_count := a_uri.count
|
||||||
tpl := template
|
tpl := template
|
||||||
|
tpl_count := tpl.count
|
||||||
if l_expressions.is_empty then
|
if l_expressions.is_empty then
|
||||||
b := a_uri.substring (1, tpl.count).same_string (tpl)
|
-- b := a_uri.substring (1, tpl_count).same_string (tpl)
|
||||||
|
b := a_uri.same_string (tpl)
|
||||||
else
|
else
|
||||||
from
|
from
|
||||||
l_expressions.start
|
l_expressions.start
|
||||||
@@ -200,6 +206,8 @@ feature -- Match
|
|||||||
b := s.same_string (t)
|
b := s.same_string (t)
|
||||||
p := exp.end_position
|
p := exp.end_position
|
||||||
end
|
end
|
||||||
|
l_expressions.forth --| we forth `l_expressions' so be careful
|
||||||
|
|
||||||
--| Check related variable
|
--| Check related variable
|
||||||
if b and then not vn.is_empty then
|
if b and then not vn.is_empty then
|
||||||
if exp.is_query then
|
if exp.is_query then
|
||||||
@@ -211,10 +219,21 @@ feature -- Match
|
|||||||
inspect vn[1]
|
inspect vn[1]
|
||||||
when '?' then
|
when '?' then
|
||||||
import_form_style_parameters_into (a_uri.substring (q + l_offset + 1, l_uri_count), l_vars)
|
import_form_style_parameters_into (a_uri.substring (q + l_offset + 1, l_uri_count), l_vars)
|
||||||
|
p := tpl_count + 1
|
||||||
|
l_offset := l_offset + (l_uri_count - (q + l_offset + 1))
|
||||||
when ';' then
|
when ';' then
|
||||||
import_path_style_parameters_into (a_uri.substring (q + l_offset, l_uri_count), l_vars)
|
import_path_style_parameters_into (a_uri.substring (q + l_offset, l_uri_count), l_vars)
|
||||||
|
p := tpl_count + 1
|
||||||
else
|
else
|
||||||
vv := next_path_variable_value (a_uri, q + l_offset)
|
if not l_expressions.after then
|
||||||
|
exp := l_expressions.item --| We change `exp' here
|
||||||
|
l_next_literal_separator := tpl.substring (p, exp.position -1)
|
||||||
|
elseif p < tpl_count then
|
||||||
|
l_next_literal_separator := tpl.substring (p, tpl_count)
|
||||||
|
else
|
||||||
|
l_next_literal_separator := Void
|
||||||
|
end
|
||||||
|
vv := next_path_variable_value (a_uri, q + l_offset, l_next_literal_separator)
|
||||||
l_vars.force (vv, vn)
|
l_vars.force (vv, vn)
|
||||||
l_offset := l_offset + vv.count - (vn.count + 2)
|
l_offset := l_offset + vv.count - (vn.count + 2)
|
||||||
end
|
end
|
||||||
@@ -222,7 +241,17 @@ feature -- Match
|
|||||||
b := exp.is_query --| query are optional
|
b := exp.is_query --| query are optional
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
l_expressions.forth
|
if b and l_expressions.after then
|
||||||
|
if
|
||||||
|
(p < tpl_count) or
|
||||||
|
(p + l_offset < l_uri_count)
|
||||||
|
then
|
||||||
|
--| Remaining literal part
|
||||||
|
t := tpl.substring (p, tpl_count)
|
||||||
|
s := a_uri.substring (p + l_offset, l_uri_count)
|
||||||
|
b := s.same_string (t)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if b then
|
if b then
|
||||||
@@ -231,8 +260,36 @@ feature -- Match
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
feature -- Basic operation
|
||||||
|
|
||||||
|
parse
|
||||||
|
-- Parse template
|
||||||
|
do
|
||||||
|
reset
|
||||||
|
analyze
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Status report
|
||||||
|
|
||||||
|
is_valid: BOOLEAN
|
||||||
|
-- Is Current URI template valid?
|
||||||
|
do
|
||||||
|
analyze
|
||||||
|
Result := not has_syntax_error
|
||||||
|
end
|
||||||
|
|
||||||
feature {NONE} -- Internal Access
|
feature {NONE} -- Internal Access
|
||||||
|
|
||||||
|
reset
|
||||||
|
do
|
||||||
|
expressions := Void
|
||||||
|
has_syntax_error := False
|
||||||
|
end
|
||||||
|
|
||||||
|
has_syntax_error: BOOLEAN
|
||||||
|
-- Has syntax error
|
||||||
|
--| Make sense only if `analyze' was processed before
|
||||||
|
|
||||||
expressions: detachable LIST [URI_TEMPLATE_EXPRESSION]
|
expressions: detachable LIST [URI_TEMPLATE_EXPRESSION]
|
||||||
-- Expansion parts
|
-- Expansion parts
|
||||||
|
|
||||||
@@ -248,6 +305,7 @@ feature {NONE} -- Implementation
|
|||||||
in_query: BOOLEAN
|
in_query: BOOLEAN
|
||||||
x: STRING
|
x: STRING
|
||||||
exp: URI_TEMPLATE_EXPRESSION
|
exp: URI_TEMPLATE_EXPRESSION
|
||||||
|
l_has_query_expression: BOOLEAN
|
||||||
do
|
do
|
||||||
l_expressions := expressions
|
l_expressions := expressions
|
||||||
if l_expressions = Void then
|
if l_expressions = Void then
|
||||||
@@ -258,6 +316,7 @@ feature {NONE} -- Implementation
|
|||||||
from
|
from
|
||||||
i := 1
|
i := 1
|
||||||
n := tpl.count
|
n := tpl.count
|
||||||
|
l_has_query_expression := False
|
||||||
create x.make_empty
|
create x.make_empty
|
||||||
until
|
until
|
||||||
i > n
|
i > n
|
||||||
@@ -269,6 +328,10 @@ feature {NONE} -- Implementation
|
|||||||
l_expressions.force (exp)
|
l_expressions.force (exp)
|
||||||
x.wipe_out
|
x.wipe_out
|
||||||
in_x := False
|
in_x := False
|
||||||
|
if l_has_query_expression and then i < n then
|
||||||
|
--| Remaining text after {?exp}
|
||||||
|
has_syntax_error := True
|
||||||
|
end
|
||||||
else
|
else
|
||||||
x.extend (c)
|
x.extend (c)
|
||||||
end
|
end
|
||||||
@@ -278,8 +341,11 @@ feature {NONE} -- Implementation
|
|||||||
check x_is_empty: x.is_empty end
|
check x_is_empty: x.is_empty end
|
||||||
p := i
|
p := i
|
||||||
in_x := True
|
in_x := True
|
||||||
|
if not l_has_query_expression then
|
||||||
|
l_has_query_expression := tpl.valid_index (i+1) and then tpl[i+1] = '?'
|
||||||
|
end
|
||||||
if not in_query then
|
if not in_query then
|
||||||
in_query := tpl.valid_index (i+1) and then tpl[i+1] = '?'
|
in_query := l_has_query_expression
|
||||||
end
|
end
|
||||||
when '?' then
|
when '?' then
|
||||||
in_query := True
|
in_query := True
|
||||||
@@ -344,23 +410,39 @@ feature {NONE} -- Implementation
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
next_path_variable_value (a_uri: STRING; a_index: INTEGER): STRING
|
next_path_variable_value (a_uri: STRING; a_index: INTEGER; a_end_token: detachable STRING): STRING
|
||||||
require
|
require
|
||||||
valid_index: a_index <= a_uri.count
|
valid_index: a_index <= a_uri.count
|
||||||
local
|
local
|
||||||
|
c: CHARACTER
|
||||||
i,n,p: INTEGER
|
i,n,p: INTEGER
|
||||||
|
l_end_token_first_char: CHARACTER
|
||||||
|
l_end_token_count: INTEGER
|
||||||
do
|
do
|
||||||
from
|
from
|
||||||
|
if a_end_token /= Void and then not a_end_token.is_empty then
|
||||||
|
l_end_token_first_char := a_end_token.item (1)
|
||||||
|
l_end_token_count := a_end_token.count
|
||||||
|
end
|
||||||
i := a_index
|
i := a_index
|
||||||
n := a_uri.count
|
n := a_uri.count
|
||||||
until
|
until
|
||||||
i > n
|
i > n
|
||||||
loop
|
loop
|
||||||
inspect a_uri[i]
|
c := a_uri[i]
|
||||||
|
inspect c
|
||||||
when '/', '?' then
|
when '/', '?' then
|
||||||
i := n
|
i := n
|
||||||
else
|
else
|
||||||
p := i
|
if
|
||||||
|
a_end_token /= Void and then
|
||||||
|
c = l_end_token_first_char and then
|
||||||
|
a_uri.substring (i, i + l_end_token_count - 1).same_string (a_end_token)
|
||||||
|
then
|
||||||
|
i := n
|
||||||
|
else
|
||||||
|
p := i
|
||||||
|
end
|
||||||
end
|
end
|
||||||
i := i + 1
|
i := i + 1
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ inherit
|
|||||||
ANY
|
ANY
|
||||||
|
|
||||||
DEBUG_OUTPUT
|
DEBUG_OUTPUT
|
||||||
|
export {NONE} all end
|
||||||
|
|
||||||
URI_TEMPLATE_CONSTANTS
|
URI_TEMPLATE_CONSTANTS
|
||||||
export {NONE} all end
|
export {NONE} all end
|
||||||
|
|||||||
@@ -24,22 +24,75 @@ feature {NONE} -- Initialization
|
|||||||
make (create {like path_variables}.make (0), create {like query_variables}.make (0))
|
make (create {like path_variables}.make (0), create {like query_variables}.make (0))
|
||||||
end
|
end
|
||||||
|
|
||||||
feature -- Access
|
|
||||||
|
|
||||||
variable (n: STRING): detachable STRING
|
feature -- Access
|
||||||
-- Value related to variable name `n'
|
|
||||||
do
|
|
||||||
Result := query_variables.item (n)
|
|
||||||
if Result = Void then
|
|
||||||
Result := path_variables.item (n)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
path_variables: HASH_TABLE [STRING, STRING]
|
path_variables: HASH_TABLE [STRING, STRING]
|
||||||
-- Variables being part of the path segments
|
-- Variables being part of the path segments
|
||||||
|
|
||||||
query_variables: HASH_TABLE [STRING, STRING]
|
query_variables: HASH_TABLE [STRING, STRING]
|
||||||
-- Variables being part of the query segments (i.e: after the ? )
|
-- Variables being part of the query segments (i.e: after the ?)
|
||||||
|
|
||||||
|
feature -- Query
|
||||||
|
|
||||||
|
path_variable (n: STRING): detachable STRING
|
||||||
|
-- Value related to query variable name `n'
|
||||||
|
do
|
||||||
|
Result := path_variables.item (n)
|
||||||
|
end
|
||||||
|
|
||||||
|
query_variable (n: STRING): detachable STRING
|
||||||
|
-- Value related to path variable name `n'
|
||||||
|
do
|
||||||
|
Result := query_variables.item (n)
|
||||||
|
end
|
||||||
|
|
||||||
|
variable (n: STRING): detachable STRING
|
||||||
|
-- Value related to variable name `n'
|
||||||
|
do
|
||||||
|
Result := query_variable (n)
|
||||||
|
if Result = Void then
|
||||||
|
Result := path_variable (n)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Query: url-decoded
|
||||||
|
|
||||||
|
url_decoded_query_variable (n: STRING): detachable STRING_32
|
||||||
|
-- Unencoded value related to variable name `n'
|
||||||
|
do
|
||||||
|
if attached query_variable (n) as v then
|
||||||
|
Result := url_decoded_string (v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
url_decoded_path_variable (n: STRING): detachable STRING_32
|
||||||
|
-- Unencoded value related to variable name `n'
|
||||||
|
do
|
||||||
|
if attached path_variable (n) as v then
|
||||||
|
Result := url_decoded_string (v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
url_decoded_variable (n: STRING): detachable STRING_32
|
||||||
|
-- Unencoded value related to variable name `n'
|
||||||
|
do
|
||||||
|
if attached variable (n) as v then
|
||||||
|
Result := url_decoded_string (v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
feature {NONE} -- Implementation
|
||||||
|
|
||||||
|
url_decoded_string (s: READABLE_STRING_GENERAL): STRING_32
|
||||||
|
do
|
||||||
|
Result := url_encoder.decoded_string (s.as_string_8)
|
||||||
|
end
|
||||||
|
|
||||||
|
url_encoder: URL_ENCODER
|
||||||
|
once
|
||||||
|
create Result
|
||||||
|
end
|
||||||
|
|
||||||
;note
|
;note
|
||||||
copyright: "2011-2011, Eiffel Software and others"
|
copyright: "2011-2011, Eiffel Software and others"
|
||||||
|
|||||||
@@ -21,6 +21,9 @@ feature -- Test routines
|
|||||||
do
|
do
|
||||||
uri_template_parse ("api/foo/{foo_id}/{?id,extra}", <<"foo_id">>, <<"id", "extra">>)
|
uri_template_parse ("api/foo/{foo_id}/{?id,extra}", <<"foo_id">>, <<"id", "extra">>)
|
||||||
uri_template_parse ("weather/{state}/{city}?forecast={day}", <<"state", "city">>, <<"day">>)
|
uri_template_parse ("weather/{state}/{city}?forecast={day}", <<"state", "city">>, <<"day">>)
|
||||||
|
uri_template_parse ("/hello/{name}.{format}", <<"name", "format">>, <<>>)
|
||||||
|
uri_template_parse ("/hello.{format}/{name}", <<"format", "name">>, <<>>)
|
||||||
|
uri_template_parse ("/hello/Joce.{format}/foo{?foobar};crazy=IDEA", <<"name">>, <<"foobar">>)
|
||||||
end
|
end
|
||||||
|
|
||||||
test_uri_template_matcher
|
test_uri_template_matcher
|
||||||
@@ -49,6 +52,32 @@ feature -- Test routines
|
|||||||
|
|
||||||
create tpl.make ("weather/{state}/{city}?forecast={day}")
|
create tpl.make ("weather/{state}/{city}?forecast={day}")
|
||||||
uri_template_match (tpl, "weather/California/Goleta?forecast=today", <<["state", "California"], ["city", "Goleta"]>>, <<["day", "today"]>>)
|
uri_template_match (tpl, "weather/California/Goleta?forecast=today", <<["state", "California"], ["city", "Goleta"]>>, <<["day", "today"]>>)
|
||||||
|
|
||||||
|
create tpl.make ("/hello")
|
||||||
|
uri_template_match (tpl, "/hello", <<>>, <<>>)
|
||||||
|
uri_template_mismatch (tpl, "/hello/Foo2") -- longer
|
||||||
|
uri_template_mismatch (tpl, "/hell") -- shorter
|
||||||
|
|
||||||
|
create tpl.make ("/hello.{format}")
|
||||||
|
uri_template_match (tpl, "/hello.xml", <<["format", "xml"]>>, <<>>)
|
||||||
|
uri_template_mismatch (tpl, "/hello.xml/Bar")
|
||||||
|
|
||||||
|
|
||||||
|
create tpl.make ("/hello.{format}/{name}")
|
||||||
|
uri_template_match (tpl, "/hello.xml/Joce", <<["format", "xml"], ["name", "Joce"]>>, <<>>)
|
||||||
|
|
||||||
|
create tpl.make ("/hello/{name}.{format}")
|
||||||
|
uri_template_match (tpl, "/hello/Joce.json", <<["name", "Joce"], ["format", "json"]>>, <<>>)
|
||||||
|
|
||||||
|
create tpl.make ("/hello/{name}.{format}/foo")
|
||||||
|
uri_template_match (tpl, "/hello/Joce.xml/foo", <<["name", "Joce"], ["format", "xml"]>>, <<>>)
|
||||||
|
uri_template_mismatch (tpl, "/hello/Joce.xml/fooBAR")
|
||||||
|
|
||||||
|
create tpl.make ("/hello/{name}.{format}/foo{?foo};crazy={idea}")
|
||||||
|
-- uri_template_match (tpl, "/hello/Joce.xml/foo", <<["name", "Joce"], ["format", "xml"]>>, <<>>)
|
||||||
|
uri_template_match (tpl, "/hello/Joce.xml/foo?foo=FOO", <<["name", "Joce"], ["format", "xml"]>>, <<["foo", "FOO"]>>)
|
||||||
|
uri_template_match (tpl, "/hello/Joce.xml/foo;crazy=IDEA", <<["name", "Joce"], ["format", "xml"]>>, <<["idea", "IDEA"], ["crazy", "IDEA"]>>)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
uri_template_string_errors: detachable LIST [STRING]
|
uri_template_string_errors: detachable LIST [STRING]
|
||||||
@@ -544,6 +573,8 @@ feature -- Test routines
|
|||||||
i: INTEGER
|
i: INTEGER
|
||||||
do
|
do
|
||||||
create u.make (s)
|
create u.make (s)
|
||||||
|
u.parse
|
||||||
|
assert ("Template %""+ s +"%" is valid", u.is_valid)
|
||||||
if attached u.path_variable_names as vars then
|
if attached u.path_variable_names as vars then
|
||||||
matched := vars.count = path_vars.count
|
matched := vars.count = path_vars.count
|
||||||
from
|
from
|
||||||
@@ -559,7 +590,7 @@ feature -- Test routines
|
|||||||
else
|
else
|
||||||
matched := path_vars.is_empty
|
matched := path_vars.is_empty
|
||||||
end
|
end
|
||||||
assert ("path variables matched", matched)
|
assert ("path variables matched for %""+ s +"%"", matched)
|
||||||
|
|
||||||
if attached u.query_variable_names as vars then
|
if attached u.query_variable_names as vars then
|
||||||
matched := vars.count = query_vars.count
|
matched := vars.count = query_vars.count
|
||||||
@@ -576,7 +607,7 @@ feature -- Test routines
|
|||||||
else
|
else
|
||||||
matched := query_vars.is_empty
|
matched := query_vars.is_empty
|
||||||
end
|
end
|
||||||
assert ("query variables matched", matched)
|
assert ("query variables matched %""+ s +"%"", matched)
|
||||||
end
|
end
|
||||||
|
|
||||||
uri_template_mismatch (a_uri_template: URI_TEMPLATE; a_uri: STRING)
|
uri_template_mismatch (a_uri_template: URI_TEMPLATE; a_uri: STRING)
|
||||||
|
|||||||
Reference in New Issue
Block a user