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