First version of URI Template library

as specified by http://tools.ietf.org/html/draft-gregorio-uritemplate-05
(it seems to contains some error in the spec .. or minor incoherences, to double check)
The matcher is basic, it does not handle all the details of the string builder, but that seems ok for now.
This commit is contained in:
Jocelyn Fiat
2011-07-20 12:11:05 +02:00
parent 917f80c0c8
commit 51b70a2490
9 changed files with 1764 additions and 2 deletions

View File

@@ -0,0 +1,10 @@
${NOTE_KEYWORD}
copyright: "2011-${YEAR}, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"

View File

@@ -0,0 +1,332 @@
note
description: "[
Summary description for {URI_TEMPLATE}.
See http://tools.ietf.org/html/draft-gregorio-uritemplate-05
]"
legal: "See notice at end of class."
status: "See notice at end of class."
date: "$Date$"
revision: "$Revision$"
class
URI_TEMPLATE
create
make
feature {NONE} -- Initialization
make (s: STRING)
do
template := s
end
make_with_handler (s: STRING; a_handler: detachable URI_TEMPLATE_HANDLER)
do
make (s)
analyze (a_handler)
end
feature -- Access
template: STRING
-- URI string representation
path_variable_names: LIST [STRING]
do
analyze (Void)
if attached expansion_parts as l_x_parts then
create {ARRAYED_LIST [STRING]} Result.make (l_x_parts.count)
from
l_x_parts.start
until
l_x_parts.after
loop
if not l_x_parts.item.is_query then
Result.append (l_x_parts.item.variable_names)
end
l_x_parts.forth
end
else
create {ARRAYED_LIST [STRING]} Result.make (0)
end
end
query_variable_names: LIST [STRING]
do
analyze (Void)
if attached expansion_parts as l_x_parts then
create {ARRAYED_LIST [STRING]} Result.make (l_x_parts.count)
from
l_x_parts.start
until
l_x_parts.after
loop
if l_x_parts.item.is_query then
Result.append (l_x_parts.item.variable_names)
end
l_x_parts.forth
end
else
create {ARRAYED_LIST [STRING]} Result.make (0)
end
end
feature -- Builder
string (a_ht: HASH_TABLE [detachable ANY, STRING]): STRING
local
tpl: like template
exp: URI_TEMPLATE_EXPRESSION
p,q: INTEGER
do
analyze (Void)
tpl := template
if attached expansion_parts as l_x_parts then
create Result.make (tpl.count)
from
l_x_parts.start
p := 1
until
l_x_parts.after
loop
q := l_x_parts.item.position
--| Added inter variable text
Result.append (tpl.substring (p, q - 1))
--| Expand variables ...
exp := l_x_parts.item
exp.append_to_string (a_ht, Result)
p := q + l_x_parts.item.expression.count + 2
l_x_parts.forth
end
Result.append (tpl.substring (p, tpl.count))
else
create Result.make_from_string (tpl)
end
end
url_encoder: URL_ENCODER
once
create Result
end
feature -- Analyze
match (a_uri: STRING): detachable TUPLE [path_variables: HASH_TABLE [STRING, STRING]; query_variables: HASH_TABLE [STRING, STRING]]
local
b: BOOLEAN
tpl: like template
l_offset: INTEGER
p,q: INTEGER
exp: URI_TEMPLATE_EXPRESSION
vn, s,t: STRING
vv: STRING
l_vars, l_path_vars, l_query_vars: HASH_TABLE [STRING, STRING]
do
--| Extract expansion parts "\\{([^\\}]*)\\}"
analyze (Void)
if attached expansion_parts as l_x_parts then
create l_path_vars.make (l_x_parts.count)
create l_query_vars.make (l_x_parts.count)
l_vars := l_path_vars
tpl := template
b := True
from
l_x_parts.start
p := 1
l_offset := 0
until
l_x_parts.after or not b
loop
exp := l_x_parts.item
vn := exp.expression
q := exp.position
--| Check text between vars
if q > p then
t := tpl.substring (p, q - 1)
s := a_uri.substring (p + l_offset, q + l_offset - 1)
b := s.same_string (t)
p := q + vn.count + 2
end
--| Check related variable
if not vn.is_empty then
if exp.is_query then
l_vars := l_query_vars
else
l_vars := l_path_vars
end
inspect vn[1]
when '?' then
import_form_style_parameters_into (a_uri.substring (q + l_offset + 1, a_uri.count), l_vars)
when ';' then
import_path_style_parameters_into (a_uri.substring (q + l_offset, a_uri.count), l_vars)
else
vv := next_path_variable_value (a_uri, q + l_offset)
l_vars.force (vv, vn)
l_offset := l_offset + vv.count - (vn.count + 2)
end
end
l_x_parts.forth
end
if b then
Result := [l_path_vars, l_query_vars]
end
end
end
analyze (a_handler: detachable URI_TEMPLATE_HANDLER)
local
l_x_parts: like expansion_parts
c: CHARACTER
i,p,n: INTEGER
tpl: like template
in_x: BOOLEAN
in_query: BOOLEAN
x: STRING
exp: URI_TEMPLATE_EXPRESSION
do
l_x_parts := expansion_parts
if l_x_parts = Void then
tpl := template
--| Extract expansion parts "\\{([^\\}]*)\\}"
create {ARRAYED_LIST [like expansion_parts.item]} l_x_parts.make (tpl.occurrences ('{'))
from
i := 1
n := tpl.count
create x.make_empty
until
i > n
loop
c := tpl[i]
if in_x then
if c = '}' then
create exp.make (p, x.twin, in_query)
l_x_parts.force (exp)
x.wipe_out
in_x := False
else
x.extend (c)
end
else
inspect c
when '{' then
check x_is_empty: x.is_empty end
p := i
in_x := True
if not in_query then
in_query := tpl.valid_index (i+1) and then tpl[i+1] = '?'
end
when '?' then
in_query := True
else
end
end
i := i + 1
end
expansion_parts := l_x_parts
end
end
feature {NONE} -- Implementation
expansion_parts: detachable LIST [URI_TEMPLATE_EXPRESSION]
-- Expansion parts
import_path_style_parameters_into (a_content: STRING; res: HASH_TABLE [STRING, STRING])
require
a_content_attached: a_content /= Void
res_attached: res /= Void
do
import_custom_style_parameters_into (a_content, ';', res)
end
import_form_style_parameters_into (a_content: STRING; res: HASH_TABLE [STRING, STRING])
require
a_content_attached: a_content /= Void
res_attached: res /= Void
do
import_custom_style_parameters_into (a_content, '&', res)
end
import_custom_style_parameters_into (a_content: STRING; a_separator: CHARACTER; res: HASH_TABLE [STRING, STRING])
require
a_content_attached: a_content /= Void
res_attached: res /= Void
local
n, p, i, j: INTEGER
s: STRING
l_name,l_value: STRING
do
n := a_content.count
if n > 0 then
from
p := 1
until
p = 0
loop
i := a_content.index_of (a_separator, p)
if i = 0 then
s := a_content.substring (p, n)
p := 0
else
s := a_content.substring (p, i - 1)
p := i + 1
end
if not s.is_empty then
j := s.index_of ('=', 1)
if j > 0 then
l_name := s.substring (1, j - 1)
l_value := s.substring (j + 1, s.count)
res.force (l_value, l_name)
end
end
end
end
end
next_path_variable_value (a_uri: STRING; a_index: INTEGER): STRING
require
valid_index: a_index <= a_uri.count
local
i,n,p: INTEGER
do
from
i := a_index
n := a_uri.count
until
i > n
loop
inspect a_uri[i]
when '/', '?' then
i := n
else
p := i
end
i := i + 1
end
Result := a_uri.substring (a_index, p)
end
comma_separated_variable_names (s: STRING): LIST [STRING]
do
Result := s.split (',')
end
note
copyright: "2011-2011, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -0,0 +1,286 @@
note
description: "Summary description for {URI_TEMPLATE_EXPRESSION}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
URI_TEMPLATE_EXPRESSION
inherit
DEBUG_OUTPUT
create
make
feature {NONE} -- Initialization
make (a_position: INTEGER; a_expression: STRING; a_is_query: BOOLEAN)
do
position := a_position
expression := a_expression
is_query := a_is_query
operator := '%U'
end
feature -- Processing
analyze
local
exp: like expression
s: detachable STRING
lst: LIST [STRING]
p: INTEGER
vars: like variables
vn: STRING
vmodifier: detachable STRING
i,n: INTEGER
do
if not is_analyzed then
exp := expression
if not exp.is_empty then
op_prefix := '%U'
op_delimiter := ','
inspect exp[1]
when '+' then
reserved := True
operator := '+'
when '.' then
operator := '.'
op_prefix := '.'
op_delimiter := '.'
when '/' then
operator := '/'
op_prefix := '/'
op_delimiter := '/'
when ';' then
operator := ';'
op_prefix := ';'
op_delimiter := ';'
when '?' then
operator := '?'
op_prefix := '?'
op_delimiter := '&'
when '|', '!', '@' then
operator := exp[1]
else
operator := '%U'
end
if operator /= '%U' then
s := exp.substring (2, exp.count)
else
s := exp
end
lst := s.split (',')
from
create {ARRAYED_LIST [like variables.item]} vars.make (lst.count)
lst.start
until
lst.after
loop
s := lst.item
vmodifier := Void
p := s.index_of ('|', 1)
if p > 0 then
vn := s.substring (1, p - 1)
s := s.substring (p + 1, s.count)
else
vn := s
s := Void
end
from
vmodifier := Void
i := 1
n := vn.count
until
i > n
loop
inspect vn[i]
when '*', '+', ':', '^' then
vmodifier := vn.substring (i, n)
vn := vn.substring (1, i - 1)
i := n + 1 --| exit
else
i := i + 1
end
end
vars.force (create {URI_TEMPLATE_EXPRESSION_VARIABLE}.make (operator, vn, s, vmodifier))
lst.forth
end
variables := vars
end
is_analyzed := True
end
end
feature -- Access
position: INTEGER
expression: STRING
is_query: BOOLEAN
feature -- Status
operator: CHARACTER
has_operator: BOOLEAN
do
Result := operator /= '%U'
end
reserved: BOOLEAN
has_op_prefix: BOOLEAN
do
Result := op_prefix /= '%U'
end
op_prefix: CHARACTER
op_delimiter: CHARACTER
variables: detachable LIST [URI_TEMPLATE_EXPRESSION_VARIABLE]
variable_names: LIST [STRING]
do
analyze
if attached variables as vars then
create {ARRAYED_LIST [STRING]} Result.make (vars.count)
from
vars.start
until
vars.after
loop
Result.force (vars.item.name)
vars.forth
end
else
create {ARRAYED_LIST [STRING]} Result.make (0)
end
end
feature -- Status report
is_analyzed: BOOLEAN
feature -- Report
append_to_string (a_ht: HASH_TABLE [detachable ANY, STRING]; a_buffer: STRING)
do
analyze
if attached variables as vars then
append_custom_variables_to_string (a_ht, vars, op_prefix, op_delimiter, True, a_buffer)
-- inspect operator
-- when '?' then
-- append_custom_variables_to_string (a_ht, vars, '?', '&', True, a_buffer)
-- when ';' then
-- append_custom_variables_to_string (a_ht, vars, ';', ';', False, a_buffer)
-- when '.' then
-- append_custom_variables_to_string (a_ht, vars, '.', ',', True, a_buffer)
-- when '/' then
-- append_custom_variables_to_string (a_ht, vars, '/', '/', True, a_buffer)
-- else
-- append_custom_variables_to_string (a_ht, vars, '%U', ',', False, a_buffer)
-- end
end
end
feature {NONE} -- Implementation
url_encoded_string (s: READABLE_STRING_GENERAL; a_encoded: BOOLEAN): STRING
do
if a_encoded then
Result := url_encoder.encoded_string (s.as_string_32)
else
Result := url_encoder.partial_encoded_string (s.as_string_32, <<
':', ',',
'+', '.', '/', ';', '?',
'|', '!', '@'
>>)
end
end
url_encoder: URL_ENCODER
once
create Result
end
append_custom_variables_to_string (a_ht: HASH_TABLE [detachable ANY, STRING]; vars: like variables; prefix_char, delimiter_char: CHARACTER; a_include_name: BOOLEAN; a_buffer: STRING)
-- If `first_char' is '%U' do not print any first character
local
vi: like variables.item
l_is_first: BOOLEAN
vdata: detachable ANY
vstr: detachable STRING
l_use_default: BOOLEAN
do
if vars /= Void then
from
vars.start
l_is_first := True
until
vars.after
loop
vi := vars.item
vdata := a_ht.item (vi.name)
vstr := Void
if vdata /= Void then
vstr := vi.string (vdata)
if vstr = Void and vi.has_explode_modifier then
--| Missing or list empty
vstr := vi.default_value
l_use_default := True
else
l_use_default := False
end
else
--| Missing
vstr := vi.default_value
l_use_default := True
end
if vstr /= Void then
if l_is_first then
if prefix_char /= '%U' then
a_buffer.append_character (prefix_char)
end
l_is_first := False
else
a_buffer.append_character (delimiter_char)
end
if l_use_default and (operator = '?') and not vi.has_explode_modifier_star then
a_buffer.append (vi.name)
if vi.has_explode_modifier_plus then
a_buffer.append_character ('.')
else
a_buffer.append_character ('=')
end
end
a_buffer.append (vstr)
end
vars.forth
end
end
end
feature -- Status report
debug_output: STRING
-- String that should be displayed in debugger to represent `Current'.
do
Result := expression
end
;note
copyright: "2011-2011, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -0,0 +1,398 @@
note
description: "Summary description for {URI_TEMPLATE_EXPRESSION_VARIABLE}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
URI_TEMPLATE_EXPRESSION_VARIABLE
create
make
feature {NONE} -- Initialization
make (op: like operator; n: like name; d: like default_value; m: like modifier)
do
operator := op
name := n
default_value := d
modifier := m
op_prefix := '%U'
op_separator := ','
inspect op
when '+' then
reserved := True
when '?' then
op_prefix := '?'
op_separator := '&'
when ';' then
op_prefix := ';'
op_separator := ';'
when '/' then
op_prefix := '/'
op_separator := '/'
when '.' then
op_prefix := '.'
op_separator := '.'
else
end
end
feature -- Access
operator: CHARACTER
name: STRING
default_value: detachable STRING
reserved: BOOLEAN
op_prefix: CHARACTER
op_separator: CHARACTER
modifier: detachable STRING
has_modifier: BOOLEAN
do
Result := modifier /= Void
end
modified (s: READABLE_STRING_GENERAL): READABLE_STRING_GENERAL
local
t: STRING
i,n: INTEGER
do
Result := s
if attached modifier as m and then m.count > 1 and then m[1] = ':' then
n := s.count
t := m.substring (2, m.count)
if t.is_integer then
i := t.to_integer
if i > 0 then
if i < n then
Result := s.substring (1, i)
end
elseif i < 0 then
Result := s.substring (n - i, n)
end
end
end
end
has_explode_modifier: BOOLEAN
do
Result := attached modifier as m and then m.count = 1 and then (
m[1] = '+' or m[1] = '*'
)
end
has_explode_modifier_plus: BOOLEAN
do
Result := attached modifier as m and then m.count = 1 and then
m[1] = '+'
end
has_explode_modifier_star: BOOLEAN
do
Result := attached modifier as m and then m.count = 1 and then
m[1] = '*'
end
feature -- Report
string (d: detachable ANY): detachable STRING
local
l_delimiter: CHARACTER
v_enc: detachable STRING
k_enc: STRING
l_obj: detachable ANY
i,n: INTEGER
modifier_is_plus: BOOLEAN
modifier_is_star: BOOLEAN
modifier_has_explode: BOOLEAN
dft: detachable ANY
has_list_op: BOOLEAN
do
modifier_has_explode := has_explode_modifier
if modifier_has_explode then
modifier_is_plus := has_explode_modifier_plus
modifier_is_star := has_explode_modifier_star
end
has_list_op := operator /= '%U' and operator /= '+'
dft := default_value
create Result.make (20)
if attached {READABLE_STRING_GENERAL} d as l_string then
v_enc := url_encoded_string (modified (l_string), not reserved)
if operator = '?' then
Result.append (name)
Result.append_character ('=')
elseif operator = ';' then
Result.append (name)
if not v_enc.is_empty then
Result.append_character ('=')
end
end
Result.append (v_enc)
elseif attached {ARRAY [detachable ANY]} d as l_array then
if l_array.is_empty then
if dft /= Void then
inspect operator
when '?',';' then
if not modifier_has_explode then
Result.append (name)
Result.append_character ('=')
Result.append (dft.out)
else
if modifier_is_plus then
Result.append (name)
Result.append_character ('.')
end
Result.append (dft.out)
end
when '/' then
if modifier_is_plus then
Result.append (name)
Result.append_character ('.')
end
Result.append (dft.out)
when '.' then
else
if modifier_has_explode then
if modifier_is_plus then
Result.append (name)
Result.append_character ('.')
end
Result.append (dft.out)
end
end
else
-- nothing ...
end
else
if modifier_has_explode then
l_delimiter := op_separator
else
l_delimiter := ','
inspect operator
when '?' then
Result.append (name)
Result.append_character ('=')
when ';' then
when '/' then
-- Result.append_character ('/')
else
end
end
from
i := l_array.lower
n := l_array.upper
until
i > n
loop
l_obj := l_array[i]
if l_obj /= Void then
v_enc := url_encoded_string (l_obj.out, not reserved)
else
v_enc := ""
end
if modifier_is_plus then
if
(operator = '?' and modifier_is_plus) or
(operator = ';' and modifier_has_explode)
then
Result.append (name)
Result.append_character ('=')
else
Result.append (name)
Result.append_character ('.')
end
elseif modifier_is_star and operator = '?' then
Result.append (name)
Result.append_character ('=')
end
Result.append (v_enc)
if i < n then
Result.append_character (l_delimiter)
end
i := i + 1
end
end
if Result.is_empty then
Result := Void
end
elseif attached {HASH_TABLE [detachable ANY, STRING]} d as l_table then
-- if operator = '?' and not modifier_has_explode and l_table.is_empty and dft = Void then
-- elseif operator = '?' and not modifier_has_explode then
-- Result.append (name)
-- Result.append_character ('=')
-- if l_table.is_empty and dft /= Void then
-- Result.append (dft.out)
-- end
-- elseif l_table.is_empty and dft /= Void then
-- if modifier_has_explode then
-- if modifier_is_plus then
-- Result.append (name)
-- Result.append_character ('.')
-- end
-- Result.append (dft.out)
-- end
-- end
if l_table.is_empty then
if dft /= Void then
inspect operator
when '?',';' then
if not modifier_has_explode then
Result.append (name)
Result.append_character ('=')
Result.append (dft.out)
else
if modifier_is_plus then
Result.append (name)
Result.append_character ('.')
end
Result.append (dft.out)
end
when '/' then
if modifier_is_plus then
Result.append (name)
Result.append_character ('.')
end
Result.append (dft.out)
when '.' then
else
if modifier_has_explode then
if modifier_is_plus then
Result.append (name)
Result.append_character ('.')
end
Result.append (dft.out)
end
end
else
-- nothing ...
end
else
if modifier_has_explode then
l_delimiter := op_separator
else
l_delimiter := ','
inspect operator
when '?' then
Result.append (name)
Result.append_character ('=')
when ';' then
when '/' then
else
end
end
from
l_table.start
until
l_table.after
loop
k_enc := url_encoded_string (l_table.key_for_iteration, not reserved)
l_obj := l_table.item_for_iteration
if l_obj /= Void then
v_enc := url_encoded_string (l_obj.out, not reserved)
else
v_enc := ""
end
if modifier_is_plus then
Result.append (name)
Result.append_character ('.')
end
if
modifier_has_explode and
(
operator = '%U' or
operator = '+' or
operator = '?' or
operator = '.' or
operator = ';' or
operator = '/'
)
then
Result.append (k_enc)
Result.append_character ('=')
else
Result.append (k_enc)
Result.append_character (l_delimiter)
end
Result.append (v_enc)
l_table.forth
if not l_table.after then
Result.append_character (l_delimiter)
end
end
end
if Result.is_empty then
Result := Void
end
else
if d /= Void then
v_enc := url_encoded_string (d.out, not reserved)
elseif dft /= Void then
v_enc := url_encoded_string (dft.out, not reserved)
else
v_enc := default_value
end
if operator = '?' then
Result.append (name)
if v_enc /= Void then
Result.append_character ('=')
end
elseif operator = ';' then
Result.append (name)
if v_enc /= Void and then not v_enc.is_empty then
Result.append_character ('=')
end
end
if v_enc /= Void then
Result.append (v_enc)
end
end
end
feature {NONE} -- Implementation
url_encoded_string (s: READABLE_STRING_GENERAL; a_encoded: BOOLEAN): STRING
do
if a_encoded then
Result := url_encoder.encoded_string (s.as_string_32)
else
Result := url_encoder.partial_encoded_string (s.as_string_32, <<
':', ',',
'+', '.', '/', ';', '?',
'|', '!', '@'
>>)
end
end
url_encoder: URL_ENCODER
once
create Result
end
;note
copyright: "2011-2011, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -0,0 +1,25 @@
note
description: "Summary description for {URI_TEMPLATE_HANDLER}."
author: ""
date: "$Date$"
revision: "$Revision$"
deferred class
URI_TEMPLATE_HANDLER
feature -- Events
note
copyright: "2011-2011, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -0,0 +1,616 @@
note
description: "[
Eiffel tests that can be executed by testing tool.
]"
author: "EiffelStudio test wizard"
date: "$Date$"
revision: "$Revision$"
testing: "type/manual"
class
TEST_URI_TEMPLATE
inherit
EQA_TEST_SET
feature -- Test routines
test_uri_template_parser
note
testing: "uri-template"
do
uri_template_parse ("api/foo/{foo_id}/{?id,extra}", <<"foo_id">>, <<"id", "extra">>)
uri_template_parse ("weather/{state}/{city}?forecast={day}", <<"state", "city">>, <<"day">>)
end
test_uri_template_matcher
note
testing: "uri-template"
local
tpl: URI_TEMPLATE
do
create tpl.make ("api/foo/{foo_id}/{?id,extra}")
uri_template_match (tpl, "api/foo/bar/?id=123", <<["foo_id", "bar"]>>, <<["id", "123"]>>)
uri_template_match (tpl, "api/foo/bar/?id=123&extra=test", <<["foo_id", "bar"]>>, <<["id", "123"], ["extra", "test"]>>)
create tpl.make ("weather/{state}/{city}?forecast={day}")
uri_template_match (tpl, "weather/California/Goleta?forecast=today", <<["state", "California"], ["city", "Goleta"]>>, <<["day", "today"]>>)
end
uri_template_string_errors: detachable LIST [STRING]
test_uri_template_string_builder
note
testing: "uri-template"
local
ht: HASH_TABLE [detachable ANY, STRING]
empty_keys: HASH_TABLE [STRING, STRING]
empty_list: ARRAY [STRING]
favs: HASH_TABLE [detachable ANY, STRING]
keys: HASH_TABLE [STRING, STRING]
colors: ARRAY [STRING]
names: ARRAY [STRING]
semi_dot: HASH_TABLE [STRING, STRING]
vals: ARRAY [STRING]
do
create ht.make (3)
ht.force ("FooBar", "foo_id")
ht.force ("That's right!", "extra")
ht.force ("123", "id")
ht.force ("California", "state")
ht.force ("Goleta", "city")
ht.force ("today", "day")
ht.force ("value", "var")
ht.force ("Hello World!", "hello")
ht.force ("", "empty")
ht.force ("/foo/bar", "path")
ht.force ("1024", "x")
ht.force ("768", "y")
ht.force ("fred", "foo")
ht.force ("That's right!", "foo2")
ht.force ("http://example.com/home/", "base")
names := <<"Fred", "Wilma", "Pebbles">>
ht.force (names, "name")
create favs.make (2)
favs.force ("red", "color")
favs.force ("high", "volume")
ht.force (favs, "favs")
create empty_list.make_empty
ht.force (empty_list,"empty_list")
create empty_keys.make (0)
ht.force (empty_keys,"empty_keys")
vals := <<"val1", "val2", "val3">>
ht.force (vals, "list")
create keys.make (2)
keys.force ("val1", "key1")
keys.force ("val2", "key2")
ht.force (keys, "keys")
colors := <<"red", "green", "blue">>
create semi_dot.make (3)
semi_dot.force (";", "semi")
semi_dot.force (".", "dot")
semi_dot.force (",", "comma")
create {ARRAYED_LIST [STRING]} uri_template_string_errors.make (10)
--| Simple string expansion
uri_template_string (ht, "{var}", "value")
uri_template_string (ht, "{hello}", "Hello+World%%21")
uri_template_string (ht, "O{empty}X", "OX")
uri_template_string (ht, "O{undef}X", "OX")
--| String expansion with defaults
uri_template_string (ht, "{var|default}", "value")
uri_template_string (ht, "O{empty|default}X", "OX")
uri_template_string (ht, "O{undef|default}X", "OdefaultX")
--| Reserved expansion with defaults
uri_template_string (ht, "{+var}", "value")
uri_template_string (ht, "{+hello}", "Hello+World!")
uri_template_string (ht, "{+path}/here", "/foo/bar/here")
uri_template_string (ht, "here?ref={+path}", "here?ref=/foo/bar")
uri_template_string (ht, "up{+path}{x}/here", "up/foo/bar1024/here")
uri_template_string (ht, "up{+empty|/1}/here", "up/here")
uri_template_string (ht, "up{+undef|/1}/here", "up/1/here")
--| String expansion with multiple variables
uri_template_string (ht, "{x,y}", "1024,768")
uri_template_string (ht, "{x,hello,y}", "1024,Hello+World%%21,768")
uri_template_string (ht, "?{x,empty}", "?1024,")
uri_template_string (ht, "?{x,undef}", "?1024")
uri_template_string (ht, "?{undef,y}", "?768")
uri_template_string (ht, "?{x,undef|0}", "?1024,0")
--| Reserved expansion with multiple variables
uri_template_string (ht, "{+x,hello,y}", "1024,Hello+World!,768")
uri_template_string (ht, "{+path,x}/here", "/foo/bar,1024/here")
--| Label expansion, dot-prefixed
uri_template_string (ht, "X{.var}", "X.value")
uri_template_string (ht, "X{.empty}", "X.")
uri_template_string (ht, "X{.undef}", "X")
--| Path segments, slash-prefixed
uri_template_string (ht, "{/var}", "/value")
uri_template_string (ht, "{/var,empty}", "/value/")
uri_template_string (ht, "{/var,undef}", "/value")
--| Path-style parameters, semicolon-prefixed
uri_template_string (ht, "{;x,y}", ";x=1024;y=768")
uri_template_string (ht, "{;x,y,empty}", ";x=1024;y=768;empty")
uri_template_string (ht, "{;x,y,undef}", ";x=1024;y=768")
--| Form-style query, ampersand-separated
uri_template_string (ht, "{?x,y}", "?x=1024&y=768")
uri_template_string (ht, "{?x,y,empty}", "?x=1024&y=768&empty=")
uri_template_string (ht, "{?x,y,undef}", "?x=1024&y=768")
ht.force (colors, "list")
ht.force (semi_dot, "keys")
--| String expansion with value modifiers
uri_template_string (ht, "{var:3}", "val")
uri_template_string (ht, "{var:30}", "value")
uri_template_string (ht, "{list}", "red,green,blue")
uri_template_string (ht, "{list*}", "red,green,blue")
uri_template_string (ht, "{keys}", "semi,%%3B,dot,.,comma,%%2C")
uri_template_string (ht, "{keys*}", "semi=%%3B,dot=.,comma=%%2C")
--| Reserved expansion with value modifiers
uri_template_string (ht, "{+path:6}/here", "/foo/b/here")
uri_template_string (ht, "{+list}", "red,green,blue")
uri_template_string (ht, "{+list*}", "red,green,blue")
uri_template_string (ht, "{+keys}", "semi,;,dot,.,comma,,")
uri_template_string (ht, "{+keys*}", "semi=;,dot=.,comma=,")
--| Label expansion, dot-prefixed
uri_template_string (ht, "X{.var:3}", "X.val")
uri_template_string (ht, "X{.list}", "X.red,green,blue")
uri_template_string (ht, "X{.list*}", "X.red.green.blue")
uri_template_string (ht, "X{.keys}", "X.semi,%%3B,dot,.,comma,%%2C")
uri_template_string (ht, "X{.keys*}", "X.semi=%%3B.dot=..comma=%%2C")
--| Path segments, slash-prefixed
uri_template_string (ht, "{/var:1,var}", "/v/value")
uri_template_string (ht, "{/list}", "/red,green,blue")
uri_template_string (ht, "{/list*}", "/red/green/blue")
uri_template_string (ht, "{/list*,path:4}", "/red/green/blue/%%2Ffoo")
uri_template_string (ht, "{/keys}", "/semi,%%3B,dot,.,comma,%%2C")
uri_template_string (ht, "{/keys*}", "/semi=%%3B/dot=./comma=%%2C")
--| Path-style parameters, semicolon-prefixed
uri_template_string (ht, "{;hello:5}", ";hello=Hello")
uri_template_string (ht, "{;list}", ";red,green,blue")
uri_template_string (ht, "{;list*}", ";red;green;blue")
uri_template_string (ht, "{;keys}", ";semi,%%3B,dot,.,comma,%%2C")
uri_template_string (ht, "{;keys*}", ";semi=%%3B;dot=.;comma=%%2C")
--| Form-style query, ampersand-separated
uri_template_string (ht, "{?var:3}", "?var=val")
uri_template_string (ht, "{?list}", "?list=red,green,blue")
uri_template_string (ht, "{?list*}", "?list=red&list=green&list=blue")
uri_template_string (ht, "{?keys}", "?keys=semi,%%3B,dot,.,comma,%%2C")
uri_template_string (ht, "{?keys*}", "?semi=%%3B&dot=.&comma=%%2C")
assert ("all strings built", uri_template_string_errors = Void or (attached uri_template_string_errors as err and then err.is_empty))
end
test_uri_template_string_builder_extra
note
testing: "uri-template"
local
ht: HASH_TABLE [detachable ANY, STRING]
empty_keys: HASH_TABLE [STRING, STRING]
empty_list: ARRAY [STRING]
favs: HASH_TABLE [detachable ANY, STRING]
keys: HASH_TABLE [STRING, STRING]
colors: ARRAY [STRING]
names: ARRAY [STRING]
semi_dot: HASH_TABLE [STRING, STRING]
vals: ARRAY [STRING]
do
create ht.make (3)
ht.force ("FooBar", "foo_id")
ht.force ("That's right!", "extra")
ht.force ("123", "id")
ht.force ("California", "state")
ht.force ("Goleta", "city")
ht.force ("today", "day")
ht.force ("value", "var")
ht.force ("Hello World!", "hello")
ht.force ("", "empty")
ht.force ("/foo/bar", "path")
ht.force ("1024", "x")
ht.force ("768", "y")
ht.force ("fred", "foo")
ht.force ("That's right!", "foo2")
ht.force ("http://example.com/home/", "base")
names := <<"Fred", "Wilma", "Pebbles">>
ht.force (names, "name")
create favs.make (2)
favs.force ("red", "color")
favs.force ("high", "volume")
ht.force (favs, "favs")
create empty_list.make_empty
ht.force (empty_list,"empty_list")
create empty_keys.make (0)
ht.force (empty_keys,"empty_keys")
vals := <<"val1", "val2", "val3">>
ht.force (vals, "list")
create keys.make (2)
keys.force ("val1", "key1")
keys.force ("val2", "key2")
ht.force (keys, "keys")
colors := <<"red", "green", "blue">>
create semi_dot.make (3)
semi_dot.force (";", "semi")
semi_dot.force (".", "dot")
semi_dot.force (",", "comma")
create {ARRAYED_LIST [STRING]} uri_template_string_errors.make (10)
--| Addition to the spec
uri_template_string (ht, "api/foo/{foo_id}/{?id,extra}",
"api/foo/FooBar/?id=123&extra=That%%27s+right%%21")
uri_template_string (ht, "api/foo/{foo_id}/{?id,empty,undef,extra}",
"api/foo/FooBar/?id=123&empty=&extra=That%%27s+right%%21")
uri_template_string (ht, "weather/{state}/{city}?forecast={day}",
"weather/California/Goleta?forecast=today")
uri_template_string (ht, "{var|default}", "value")
uri_template_string (ht, "{undef|default}", "default")
uri_template_string (ht, "{undef:3|default}", "default")
uri_template_string (ht, "x{empty}y", "xy")
uri_template_string (ht, "x{empty|_}y", "xy")
uri_template_string (ht, "x{undef}y", "xy")
uri_template_string (ht, "x{undef|_}y", "x_y")
uri_template_string (ht, "x{.name|none}", "x.Fred,Wilma,Pebbles")
uri_template_string (ht, "x{.name*|none}", "x.Fred.Wilma.Pebbles")
uri_template_string (ht, "x{.empty}", "x.")
uri_template_string (ht, "x{.empty|none}", "x.")
uri_template_string (ht, "x{.undef}", "x")
uri_template_string (ht, "x{.undef|none}", "x.none")
uri_template_string (ht, "x{/name|none}", "x/Fred,Wilma,Pebbles")
uri_template_string (ht, "x{/name*|none}", "x/Fred/Wilma/Pebbles")
uri_template_string (ht, "x{/undef}", "x")
uri_template_string (ht, "x{/undef|none}", "x/none")
uri_template_string (ht, "x{/empty}", "x/")
uri_template_string (ht, "x{/empty|none}", "x/")
uri_template_string (ht, "x{/empty_keys}", "x")
uri_template_string (ht, "x{/empty_keys|none}", "x/none")
uri_template_string (ht, "x{/empty_keys*}", "x")
uri_template_string (ht, "x{/empty_keys*|none}", "x/none")
-- uri_template_string (ht, "x{;name|none}", "x;name=Fred,Wilma,Pebbles")
-- uri_template_string (ht, "x{;favs|none}", "x;favs=color,red,volume,high")
uri_template_string (ht, "x{;favs*|none}", "x;color=red;volume=high")
uri_template_string (ht, "x{;empty}", "x;empty")
uri_template_string (ht, "x{;empty|none}", "x;empty")
uri_template_string (ht, "x{;undef}", "x")
uri_template_string (ht, "x{;undef|none}", "x;none")
uri_template_string (ht, "x{;undef|foo=y}", "x;foo=y")
uri_template_string (ht, "x{?var|none}", "x?var=value")
uri_template_string (ht, "x{?favs|none}", "x?favs=color,red,volume,high")
uri_template_string (ht, "x{?favs*|none}", "x?color=red&volume=high")
uri_template_string (ht, "x{?empty}", "x?empty=")
uri_template_string (ht, "x{?empty|foo=none}", "x?empty=")
uri_template_string (ht, "x{?undef}", "x")
-- uri_template_string (ht, "x{?undef|foo=none}", "x?foo=none")
uri_template_string (ht, "x{?empty_keys}", "x")
-- uri_template_string (ht, "x{?empty_keys|none}", "x?none")
-- uri_template_string (ht, "x{?empty_keys|y=z}", "x?y=z")
uri_template_string (ht, "x{?empty_keys*|y=z}", "x?y=z")
------
uri_template_string (ht, "x{empty_list}y", "xy")
uri_template_string (ht, "x{empty_list|_}y", "xy")
uri_template_string (ht, "x{empty_list*}y", "xy")
uri_template_string (ht, "x{empty_list*|_}y", "x_y")
uri_template_string (ht, "x{empty_list+}y", "xy")
uri_template_string (ht, "x{empty_list+|_}y", "xempty_list._y")
uri_template_string (ht, "x{empty_keys}y", "xy")
uri_template_string (ht, "x{empty_keys|_}y", "xy")
uri_template_string (ht, "x{empty_keys*}y", "xy")
uri_template_string (ht, "x{empty_keys*|_}y", "x_y")
uri_template_string (ht, "x{empty_keys+}y", "xy")
uri_template_string (ht, "x{empty_keys+|_}y", "xempty_keys._y")
uri_template_string (ht, "x{?name|none}", "x?name=Fred,Wilma,Pebbles")
uri_template_string (ht, "x{?favs|none}", "x?favs=color,red,volume,high")
uri_template_string (ht, "x{?favs*|none}", "x?color=red&volume=high")
uri_template_string (ht, "x{?favs+|none}", "x?favs.color=red&favs.volume=high")
uri_template_string (ht, "x{?undef}", "x")
uri_template_string (ht, "x{?undef|none}", "x?undef=none")
uri_template_string (ht, "x{?empty}", "x?empty=")
uri_template_string (ht, "x{?empty|none}", "x?empty=")
uri_template_string (ht, "x{?empty_list}", "x")
uri_template_string (ht, "x{?empty_list|none}", "x?empty_list=none")
uri_template_string (ht, "x{?empty_list*}", "x")
uri_template_string (ht, "x{?empty_list*|none}", "x?none")
uri_template_string (ht, "x{?empty_list+}", "x")
uri_template_string (ht, "x{?empty_list+|none}", "x?empty_list.none")
uri_template_string (ht, "x{?empty_keys}", "x")
uri_template_string (ht, "x{?empty_keys|none}", "x?empty_keys=none")
uri_template_string (ht, "x{?empty_keys*}", "x")
uri_template_string (ht, "x{?empty_keys*|none}", "x?none")
uri_template_string (ht, "x{?empty_keys+}", "x")
uri_template_string (ht, "x{?empty_keys+|none}", "x?empty_keys.none")
-- uri_template_string (ht, "x{;name|none}", "x;name=Fred,Wilma,Pebbles")
-- uri_template_string (ht, "x{;favs|none}", "x;favs=color,red,volume,high")
uri_template_string (ht, "x{;favs|none}", "x;color,red,volume,high") -- DIFF
uri_template_string (ht, "x{;favs*|none}", "x;color=red;volume=high")
uri_template_string (ht, "x{;favs+|none}", "x;favs.color=red;favs.volume=high")
uri_template_string (ht, "x{;undef}", "x")
-- uri_template_string (ht, "x{;undef|none}", "x;undef=none")
uri_template_string (ht, "x{;undef|none}", "x;none")
uri_template_string (ht, "x{;empty}", "x;empty")
uri_template_string (ht, "x{;empty|none}", "x;empty")
uri_template_string (ht, "x{;empty_list}", "x")
uri_template_string (ht, "x{;empty_list|none}", "x;empty_list=none")
uri_template_string (ht, "x{;empty_list*}", "x")
uri_template_string (ht, "x{;empty_list*|none}", "x;none")
uri_template_string (ht, "x{;empty_list+}", "x")
uri_template_string (ht, "x{;empty_list+|none}", "x;empty_list.none")
uri_template_string (ht, "x{;empty_keys}", "x")
uri_template_string (ht, "x{;empty_keys|none}", "x;empty_keys=none")
uri_template_string (ht, "x{;empty_keys*}", "x")
uri_template_string (ht, "x{;empty_keys*|none}", "x;none")
uri_template_string (ht, "x{;empty_keys+}", "x")
uri_template_string (ht, "x{;empty_keys+|none}", "x;empty_keys.none")
uri_template_string (ht, "x{/name|none}", "x/Fred,Wilma,Pebbles")
uri_template_string (ht, "x{/name*|none}", "x/Fred/Wilma/Pebbles")
uri_template_string (ht, "x{/name+|none}", "x/name.Fred/name.Wilma/name.Pebbles")
uri_template_string (ht, "x{/favs|none}", "x/color,red,volume,high")
uri_template_string (ht, "x{/favs*|none}", "x/color/red/volume/high")
uri_template_string (ht, "x{/favs+|none}", "x/favs.color/red/favs.volume/high")
uri_template_string (ht, "x{/undef}", "x")
uri_template_string (ht, "x{/undef|none}", "x/none")
uri_template_string (ht, "x{/empty}", "x/")
uri_template_string (ht, "x{/empty|none}", "x/")
uri_template_string (ht, "x{/empty_list}", "x")
uri_template_string (ht, "x{/empty_list|none}", "x/none")
uri_template_string (ht, "x{/empty_list*}", "x")
uri_template_string (ht, "x{/empty_list*|none}", "x/none")
uri_template_string (ht, "x{/empty_list+}", "x")
uri_template_string (ht, "x{/empty_list+|none}", "x/empty_list.none")
uri_template_string (ht, "x{/empty_keys}", "x")
uri_template_string (ht, "x{/empty_keys|none}", "x/none")
uri_template_string (ht, "x{/empty_keys*}", "x")
uri_template_string (ht, "x{/empty_keys*|none}", "x/none")
uri_template_string (ht, "x{/empty_keys+}", "x")
uri_template_string (ht, "x{/empty_keys+|none}", "x/empty_keys.none")
--| Simple expansion with comma-separated values
uri_template_string (ht, "{var}", "value")
uri_template_string (ht, "{hello}", "Hello+World%%21")
uri_template_string (ht, "{path}/here", "%%2Ffoo%%2Fbar/here")
uri_template_string (ht, "{x,y}", "1024,768")
uri_template_string (ht, "{var|default}", "value")
uri_template_string (ht, "{undef|default}", "default")
uri_template_string (ht, "{list}", "val1,val2,val3")
uri_template_string (ht, "{list*}", "val1,val2,val3")
uri_template_string (ht, "{list+}", "list.val1,list.val2,list.val3")
uri_template_string (ht, "{keys}", "key1,val1,key2,val2")
uri_template_string (ht, "{keys*}", "key1,val1,key2,val2")
uri_template_string (ht, "{keys+}", "keys.key1,val1,keys.key2,val2")
--| Reserved expansion with comma-separated values
uri_template_string (ht, "{+var}", "value")
uri_template_string (ht, "{+hello}", "Hello+World!")
uri_template_string (ht, "{+path}/here", "/foo/bar/here")
uri_template_string (ht, "{+path,x}/here", "/foo/bar,1024/here")
uri_template_string (ht, "{+path}{x}/here", "/foo/bar1024/here")
uri_template_string (ht, "{+empty}/here", "/here")
uri_template_string (ht, "{+undef}/here", "/here")
uri_template_string (ht, "{+list}", "val1,val2,val3")
uri_template_string (ht, "{+list*}", "val1,val2,val3")
uri_template_string (ht, "{+list+}", "list.val1,list.val2,list.val3")
uri_template_string (ht, "{+keys}", "key1,val1,key2,val2")
uri_template_string (ht, "{+keys*}", "key1,val1,key2,val2")
uri_template_string (ht, "{+keys+}", "keys.key1,val1,keys.key2,val2")
--| Path-style parameters, semicolon-prefixed
uri_template_string (ht, "{;x,y}", ";x=1024;y=768")
uri_template_string (ht, "{;x,y,empty}", ";x=1024;y=768;empty")
uri_template_string (ht, "{;x,y,undef}", ";x=1024;y=768")
-- uri_template_string (ht, "{;list}", ";val1,val2,val3") -- DIFF
uri_template_string (ht, "{;list}", ";list=val1,val2,val3") -- DIFF
uri_template_string (ht, "{;list*}", ";val1;val2;val3")
uri_template_string (ht, "{;list+}", ";list=val1;list=val2;list=val3")
uri_template_string (ht, "{;keys}", ";key1,val1,key2,val2")
uri_template_string (ht, "{;keys*}", ";key1=val1;key2=val2")
uri_template_string (ht, "{;keys+}", ";keys.key1=val1;keys.key2=val2")
--| Form-style parameters, ampersand-separated
uri_template_string (ht, "{?x,y}", "?x=1024&y=768")
uri_template_string (ht, "{?x,y,empty}", "?x=1024&y=768&empty=")
uri_template_string (ht, "{?x,y,undef}", "?x=1024&y=768")
uri_template_string (ht, "{?list}", "?list=val1,val2,val3")
uri_template_string (ht, "{?list*}", "?val1&val2&val3")
uri_template_string (ht, "{?list+}", "?list=val1&list=val2&list=val3")
uri_template_string (ht, "{?keys}", "?keys=key1,val1,key2,val2")
uri_template_string (ht, "{?keys*}", "?key1=val1&key2=val2")
uri_template_string (ht, "{?keys+}", "?keys.key1=val1&keys.key2=val2")
--| Hierarchical path segments, slash-separated
uri_template_string (ht, "{/var}", "/value")
uri_template_string (ht, "{/var,empty}", "/value/")
uri_template_string (ht, "{/var,undef}", "/value")
uri_template_string (ht, "{/list}", "/val1,val2,val3")
uri_template_string (ht, "{/list*}", "/val1/val2/val3")
uri_template_string (ht, "{/list*,x}", "/val1/val2/val3/1024")
uri_template_string (ht, "{/list+}", "/list.val1/list.val2/list.val3")
uri_template_string (ht, "{/keys}", "/key1,val1,key2,val2")
uri_template_string (ht, "{/keys*}", "/key1/val1/key2/val2")
uri_template_string (ht, "{/keys+}", "/keys.key1/val1/keys.key2/val2")
--| Label expansion, dot-prefixed
uri_template_string (ht, "X{.var}", "X.value")
uri_template_string (ht, "X{.empty}", "X.")
uri_template_string (ht, "X{.undef}", "X")
uri_template_string (ht, "X{.list}", "X.val1,val2,val3")
uri_template_string (ht, "X{.list*}", "X.val1.val2.val3")
uri_template_string (ht, "X{.list*,x}", "X.val1.val2.val3.1024")
uri_template_string (ht, "X{.list+}", "X.list.val1.list.val2.list.val3")
uri_template_string (ht, "X{.keys}", "X.key1,val1,key2,val2")
uri_template_string (ht, "X{.keys*}", "X.key1.val1.key2.val2")
uri_template_string (ht, "X{.keys+}", "X.keys.key1.val1.keys.key2.val2")
--| Simple Expansion
uri_template_string (ht, "{foo}", "fred")
uri_template_string (ht, "{foo,foo}", "fred,fred")
uri_template_string (ht, "{bar,foo}", "fred")
uri_template_string (ht, "{bar|wilma}", "wilma")
--| Reserved Expansion
uri_template_string (ht, "{foo2}", "That%%27s+right%%21")
uri_template_string (ht, "{+foo2}", "That%%27s+right!")
uri_template_string (ht, "{base}index", "http%%3A%%2F%%2Fexample.com%%2Fhome%%2Findex")
uri_template_string (ht, "{+base}index", "http://example.com/home/index")
assert ("all strings built", uri_template_string_errors = Void or (attached uri_template_string_errors as err and then err.is_empty))
end
uri_template_string (a_ht: HASH_TABLE [detachable ANY, STRING]; a_expression: STRING; a_expected: STRING)
local
tpl: URI_TEMPLATE
s: STRING
m: STRING
do
create tpl.make (a_expression)
s := tpl.string (a_ht)
if not s.same_string (a_expected) then
m := "Expected string for %"" + a_expression + "%" expected=%""+ a_expected +"%" but got %"" + s + "%"%N"
if attached uri_template_string_errors as err then
print (m)
err.force (m)
else
assert (m, False)
end
end
end
uri_template_parse (s: STRING_8; path_vars: ARRAY [STRING]; query_vars: ARRAY [STRING])
local
u: URI_TEMPLATE
matched: BOOLEAN
i: INTEGER
do
create u.make (s)
if attached u.path_variable_names as vars then
matched := vars.count = path_vars.count
from
i := path_vars.lower
vars.start
until
not matched or i > path_vars.upper
loop
matched := vars.item.same_string (path_vars[i])
vars.forth
i := i + 1
end
else
matched := path_vars.is_empty
end
assert ("path variables matched", matched)
if attached u.query_variable_names as vars then
matched := vars.count = query_vars.count
from
i := query_vars.lower
vars.start
until
not matched or i > query_vars.upper
loop
matched := vars.item.same_string (query_vars[i])
vars.forth
i := i + 1
end
else
matched := query_vars.is_empty
end
assert ("query variables matched", matched)
end
uri_template_match (a_uri_template: URI_TEMPLATE; a_uri: STRING; path_res: ARRAY [TUPLE [name: STRING; value: STRING]]; query_res: ARRAY [TUPLE [name: STRING; value: STRING]])
local
b: BOOLEAN
i: INTEGER
do
if attached a_uri_template.match (a_uri) as l_match then
if attached l_match.path_variables as path_ht then
b := path_ht.count = path_res.count
from
i := path_res.lower
until
not b or i > path_res.upper
loop
b := attached path_ht.item (path_res[i].name) as s and then s.same_string (path_res[i].value)
i := i + 1
end
assert ("uri matched path variables", b)
end
if attached l_match.query_variables as query_ht then
b := query_ht.count = query_res.count
from
i := query_res.lower
until
not b or i > query_res.upper
loop
b := attached query_ht.item (query_res[i].name) as s and then s.same_string (query_res[i].value)
i := i + 1
end
assert ("uri matched query variables", b)
end
else
assert ("uri matched", False)
end
end
note
copyright: "2011-2011, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-8-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-8-0 http://www.eiffel.com/developers/xml/configuration-1-8-0.xsd" name="uri_template" uuid="64B64199-7F12-4A33-A962-4AD314E9593D" library_target="uri_template">
<target name="uri_template">
<root all_classes="true"/>
<file_rule>
<exclude>/.git$</exclude>
<exclude>/EIFGENs$</exclude>
<exclude>/.svn$</exclude>
</file_rule>
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all">
<assertions precondition="true"/>
</option>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="encoder" location="..\..\text\encoder\encoder-safe.ecf"/>
<library name="time" location="$ISE_LIBRARY\library\time\time-safe.ecf"/>
<cluster name="src" location="src\" recursive="true"/>
</target>
<target name="tests" extends="uri_template">
<root class="ANY" feature="default_create"/>
<file_rule>
<exclude>/.git$</exclude>
<exclude>/EIFGENs$</exclude>
<exclude>/.svn$</exclude>
</file_rule>
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all">
<assertions precondition="true"/>
</option>
<setting name="console_application" value="false"/>
<library name="testing" location="$ISE_LIBRARY\library\testing\testing-safe.ecf"/>
<tests name="tests" location=".\tests\"/>
</target>
</system>

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-8-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-8-0 http://www.eiffel.com/developers/xml/configuration-1-8-0.xsd" name="uri_template" uuid="64B64199-7F12-4A33-A962-4AD314E9593D" library_target="uri_template">
<target name="uri_template">
<root all_classes="true"/>
<file_rule>
<exclude>/.git$</exclude>
<exclude>/EIFGENs$</exclude>
<exclude>/.svn$</exclude>
</file_rule>
<option warning="true" full_class_checking="true">
<assertions precondition="true"/>
</option>
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
<library name="time" location="$ISE_LIBRARY\library\time\time.ecf"/>
<cluster name="src" location="src\" recursive="true"/>
</target>
</system>

View File

@@ -1,5 +1,9 @@
note note
description: "Summary description for {URL_ENCODER}." description: "[
Summary description for {URL_ENCODER}.
See: http://www.faqs.org/rfcs/rfc3986.html
]"
legal: "See notice at end of class." legal: "See notice at end of class."
status: "See notice at end of class." status: "See notice at end of class."
date: "$Date$" date: "$Date$"
@@ -59,6 +63,47 @@ feature -- Encoder
end end
end end
partial_encoded_string (s: STRING_32; a_ignore: ARRAY [CHARACTER]): STRING_8
-- URL-encoded value of `s'.
local
i, n: INTEGER
uc: CHARACTER_32
c: CHARACTER_8
do
has_error := False
create Result.make (s.count + s.count // 10)
n := s.count
from i := 1 until i > n loop
uc := s.item (i)
if uc.is_character_8 then
c := uc.to_character_8
inspect c
when
'A' .. 'Z',
'a' .. 'z', '0' .. '9',
'.', '-', '~', '_'
then
Result.extend (c)
when ' ' then
Result.extend ('+')
else
if a_ignore.has (c) then
Result.extend (c)
else
Result.append (url_encoded_char (uc))
end
end
else
if a_ignore.has (c) then
Result.extend (c)
else
Result.append (url_encoded_char (uc))
end
end
i := i + 1
end
end
feature {NONE} -- encoder character feature {NONE} -- encoder character
url_encoded_char (uc: CHARACTER_32): STRING_8 url_encoded_char (uc: CHARACTER_32): STRING_8
@@ -309,7 +354,7 @@ feature {NONE} -- Hexadecimal and strings
end end
note note
copyright: "Copyright (c) 1984-2011, Eiffel Software and others" copyright: "2011-2011, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[ source: "[
Eiffel Software Eiffel Software