diff --git a/library/protocol/uri_template/license.lic b/library/protocol/uri_template/license.lic
new file mode 100644
index 00000000..cf2d1ed9
--- /dev/null
+++ b/library/protocol/uri_template/license.lic
@@ -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
+ ]"
diff --git a/library/protocol/uri_template/src/uri_template.e b/library/protocol/uri_template/src/uri_template.e
new file mode 100644
index 00000000..dbc53430
--- /dev/null
+++ b/library/protocol/uri_template/src/uri_template.e
@@ -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
diff --git a/library/protocol/uri_template/src/uri_template_expression.e b/library/protocol/uri_template/src/uri_template_expression.e
new file mode 100644
index 00000000..298a53c5
--- /dev/null
+++ b/library/protocol/uri_template/src/uri_template_expression.e
@@ -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
diff --git a/library/protocol/uri_template/src/uri_template_expression_variable.e b/library/protocol/uri_template/src/uri_template_expression_variable.e
new file mode 100644
index 00000000..81cf11e6
--- /dev/null
+++ b/library/protocol/uri_template/src/uri_template_expression_variable.e
@@ -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
diff --git a/library/protocol/uri_template/src/uri_template_handler.e b/library/protocol/uri_template/src/uri_template_handler.e
new file mode 100644
index 00000000..7f19b55c
--- /dev/null
+++ b/library/protocol/uri_template/src/uri_template_handler.e
@@ -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
diff --git a/library/protocol/uri_template/tests/test_uri_template.e b/library/protocol/uri_template/tests/test_uri_template.e
new file mode 100644
index 00000000..6248f31b
--- /dev/null
+++ b/library/protocol/uri_template/tests/test_uri_template.e
@@ -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
+
+
diff --git a/library/protocol/uri_template/uri_template-safe.ecf b/library/protocol/uri_template/uri_template-safe.ecf
new file mode 100644
index 00000000..ed5b9178
--- /dev/null
+++ b/library/protocol/uri_template/uri_template-safe.ecf
@@ -0,0 +1,32 @@
+
+
+
+
+
+ /.git$
+ /EIFGENs$
+ /.svn$
+
+
+
+
+
+
+
+
+
+
+ /.git$
+ /EIFGENs$
+ /.svn$
+
+
+
+
+
+
+
diff --git a/library/protocol/uri_template/uri_template.ecf b/library/protocol/uri_template/uri_template.ecf
new file mode 100644
index 00000000..8d31c66c
--- /dev/null
+++ b/library/protocol/uri_template/uri_template.ecf
@@ -0,0 +1,18 @@
+
+
+
+
+
+ /.git$
+ /EIFGENs$
+ /.svn$
+
+
+
+
+
+
+
+
diff --git a/library/text/encoder/src/url_encoder.e b/library/text/encoder/src/url_encoder.e
index 63b1af79..b5370e49 100644
--- a/library/text/encoder/src/url_encoder.e
+++ b/library/text/encoder/src/url_encoder.e
@@ -1,5 +1,9 @@
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."
status: "See notice at end of class."
date: "$Date$"
@@ -59,6 +63,47 @@ feature -- Encoder
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
url_encoded_char (uc: CHARACTER_32): STRING_8
@@ -309,7 +354,7 @@ feature {NONE} -- Hexadecimal and strings
end
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)"
source: "[
Eiffel Software