Files
EWF/library/text/parser/uri_template/src/uri_template.e
2013-02-14 13:09:32 +01:00

525 lines
12 KiB
Plaintext

note
description: "[
Implementation of URI Template RFC6570.
See http://tools.ietf.org/html/rfc6570
]"
legal: "See notice at end of class."
status: "See notice at end of class."
date: "$Date$"
revision: "$Revision$"
EIS: "name=URI Template RFC6570", "protocol=URI", "src=http://tools.ietf.org/html/rfc6570"
class
URI_TEMPLATE
inherit
HASHABLE
DEBUG_OUTPUT
create
make
create {URI_TEMPLATE}
make_from_uri_template
convert
make ({READABLE_STRING_8, STRING_8})
feature {NONE} -- Initialization
make (s: READABLE_STRING_8)
do
template := s
end
make_from_uri_template (a_tpl: like Current)
do
template := a_tpl.template.string
end
feature -- Access
template: READABLE_STRING_8
-- URI string representation
duplicate: like Current
-- Duplicate object from Current
do
create Result.make_from_uri_template (Current)
end
feature -- Element change
set_template (t: like template)
-- Set `template' to `t'
do
template := t
reset
end
feature -- Status report
debug_output: STRING
-- String that should be displayed in debugger to represent `Current'.
do
create Result.make_from_string (template)
end
feature -- Access
hash_code: INTEGER
-- Hash code value
do
Result := template.hash_code
end
feature -- Structures
variable_names: LIST [STRING]
-- All variable names
do
analyze
if attached expressions as l_expressions then
create {ARRAYED_LIST [STRING]} Result.make (l_expressions.count)
from
l_expressions.start
until
l_expressions.after
loop
Result.append (l_expressions.item.variable_names)
l_expressions.forth
end
else
create {ARRAYED_LIST [STRING]} Result.make (0)
end
end
path_variable_names: LIST [STRING]
-- All variable names part of the path
do
analyze
if attached expressions as l_expressions then
create {ARRAYED_LIST [STRING]} Result.make (l_expressions.count)
from
l_expressions.start
until
l_expressions.after
loop
if not l_expressions.item.is_query then
Result.append (l_expressions.item.variable_names)
end
l_expressions.forth
end
else
create {ARRAYED_LIST [STRING]} Result.make (0)
end
end
query_variable_names: LIST [STRING]
-- All variable names part of the query (i.e after '?')
do
analyze
if attached expressions as l_expressions then
create {ARRAYED_LIST [STRING]} Result.make (l_expressions.count)
from
l_expressions.start
until
l_expressions.after
loop
if l_expressions.item.is_query then
Result.append (l_expressions.item.variable_names)
end
l_expressions.forth
end
else
create {ARRAYED_LIST [STRING]} Result.make (0)
end
end
feature -- Builder
expanded_string (a_ht: HASH_TABLE [detachable ANY, STRING]): STRING
-- Expanded template using variable from `a_ht'
local
tpl: like template
exp: URI_TEMPLATE_EXPRESSION
p,q: INTEGER
do
analyze
tpl := template
if attached expressions as l_expressions then
create Result.make (tpl.count)
from
l_expressions.start
p := 1
until
l_expressions.after
loop
q := l_expressions.item.position
--| Added inter variable text
Result.append (tpl.substring (p, q - 1))
--| Expand variables ...
exp := l_expressions.item
exp.append_expanded_to_string (a_ht, Result)
p := q + l_expressions.item.expression.count + 2
l_expressions.forth
end
Result.append (tpl.substring (p, tpl.count))
else
create Result.make_from_string (tpl)
end
end
expanded_string_with_base_url (a_base_url: READABLE_STRING_8; a_ht: HASH_TABLE [detachable ANY, STRING]): STRING
-- Expanded template using variable from `a_ht'
-- with based url
do
Result := a_base_url + expanded_string (a_ht)
end
feature -- Match
match (a_uri: READABLE_STRING_8): detachable URI_TEMPLATE_MATCH_RESULT
require
is_valid: is_valid
local
b: BOOLEAN
tpl: like template
l_offset: INTEGER
p,q,nb: INTEGER
exp: URI_TEMPLATE_EXPRESSION
vn, s,t: STRING
vv, path_vv: STRING
l_vars, l_path_vars, l_query_vars: HASH_TABLE [READABLE_STRING_8, READABLE_STRING_8]
l_uri_count: INTEGER
tpl_count: INTEGER
l_next_literal_separator: detachable STRING
do
--| Extract expansion parts "\\{([^\\}]*)\\}"
analyze
if attached expressions as l_expressions then
create l_path_vars.make (l_expressions.count)
create l_query_vars.make (l_expressions.count)
l_vars := l_path_vars
b := True
l_uri_count := a_uri.count
tpl := template
tpl_count := tpl.count
if l_expressions.is_empty then
-- b := a_uri.substring (1, tpl_count).same_string (tpl)
b := a_uri.same_string (tpl)
else
from
l_expressions.start
p := 0
l_offset := 0
until
l_expressions.after or not b
loop
exp := l_expressions.item
vn := exp.expression
q := exp.position
--| Check text between vars
if p = q then
--| There should be at least one literal between two expression
--| {var}{foobar} is ambigous for matching ...
--| unless with {/var} or {?var} ... since we can search for '/' or '?'
exp.analyze
b := exp.operator = '/' or exp.operator = '?'
p := exp.end_position
elseif q > p then
if p = 0 then
p := 1
end
t := tpl.substring (p, q - 1)
s := a_uri.substring (p + l_offset, q + l_offset - 1)
b := s.same_string (t)
p := exp.end_position
end
l_expressions.forth --| we forth `l_expressions' so be careful
--| Check related variable
if b and then not vn.is_empty then
if exp.is_query then
l_vars := l_query_vars
else
l_vars := l_path_vars
end
if q + l_offset <= l_uri_count then
inspect vn[1]
when '?' then
import_form_style_parameters_into (a_uri.substring (q + l_offset + 1, l_uri_count), l_vars)
p := tpl_count + 1
l_offset := l_offset + (l_uri_count - (q + l_offset + 1))
when ';' then
import_path_style_parameters_into (a_uri.substring (q + l_offset, l_uri_count), l_vars)
p := tpl_count + 1
else
if not l_expressions.after then
exp := l_expressions.item --| We change `exp' here
exp.analyze
if exp.operator = '/' or exp.operator = '?' then
l_next_literal_separator := Void
else
l_next_literal_separator := tpl.substring (p, exp.position -1)
end
elseif p < tpl_count then
l_next_literal_separator := tpl.substring (p, tpl_count)
else
l_next_literal_separator := Void
end
if vn[1] = '/' then
vn := vn.substring (2, vn.count)
from
create path_vv.make_empty
vv := "/"
nb := 0
until
vv.is_empty or q + l_offset + 1 > a_uri.count
loop
vv := next_path_variable_value (a_uri, q + l_offset + 1, l_next_literal_separator)
l_offset := l_offset + vv.count + 1
nb := nb + 1
if not vv.is_empty then
path_vv.extend ('/')
path_vv.append (vv)
l_vars.force (vv, vn + "[" + nb.out + "]")
end
end
l_vars.force (path_vv, vn)
l_offset := l_offset - (1 + vn.count + 2)
else
vv := next_path_variable_value (a_uri, q + l_offset, l_next_literal_separator)
l_vars.force (vv, vn)
l_offset := l_offset + vv.count - (vn.count + 2)
end
end
else
b := exp.is_query --| query are optional
end
end
if b and l_expressions.after then
if
(p < tpl_count) or
(p + l_offset < l_uri_count)
then
--| Remaining literal part
t := tpl.substring (p, tpl_count)
s := a_uri.substring (p + l_offset, l_uri_count)
b := s.same_string (t)
end
end
end
end
if b then
create Result.make (l_path_vars, l_query_vars)
end
end
end
feature -- Basic operation
parse
-- Parse template
do
reset
analyze
end
feature -- Status report
is_valid: BOOLEAN
-- Is Current URI template valid?
do
analyze
Result := not has_syntax_error
end
feature {NONE} -- Internal Access
reset
do
expressions := Void
has_syntax_error := False
end
has_syntax_error: BOOLEAN
-- Has syntax error
--| Make sense only if `analyze' was processed before
expressions: detachable LIST [URI_TEMPLATE_EXPRESSION]
-- Expansion parts
feature {NONE} -- Implementation
analyze
local
l_expressions: like expressions
c: CHARACTER
i,p,n: INTEGER
tpl: like template
in_x: BOOLEAN
in_query: BOOLEAN
x: STRING
exp: URI_TEMPLATE_EXPRESSION
l_has_query_expression: BOOLEAN
do
l_expressions := expressions
if l_expressions = Void then
tpl := template
--| Extract expansion parts "\\{([^\\}]*)\\}"
create {ARRAYED_LIST [like expressions.item]} l_expressions.make (tpl.occurrences ('{'))
from
i := 1
n := tpl.count
l_has_query_expression := False
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_expressions.force (exp)
x.wipe_out
in_x := False
if l_has_query_expression and then i < n then
--| Remaining text after {?exp}
has_syntax_error := True
end
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 l_has_query_expression then
l_has_query_expression := tpl.valid_index (i+1) and then tpl[i+1] = '?'
end
if not in_query then
in_query := l_has_query_expression
end
when '?' then
in_query := True
else
end
end
i := i + 1
end
expressions := l_expressions
end
end
import_path_style_parameters_into (a_content: STRING; res: HASH_TABLE [READABLE_STRING_8, READABLE_STRING_8])
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 [READABLE_STRING_8, READABLE_STRING_8])
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 [READABLE_STRING_8, READABLE_STRING_8])
require
a_content_attached: a_content /= Void
res_attached: res /= Void
local
n, p, i, j: INTEGER
s: READABLE_STRING_8
l_name, l_value: READABLE_STRING_8
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; a_end_token: detachable STRING): STRING
require
valid_index: a_index <= a_uri.count
local
c: CHARACTER
i,n,p: INTEGER
l_end_token_first_char: CHARACTER
l_end_token_count: INTEGER
do
from
if a_end_token /= Void and then not a_end_token.is_empty then
l_end_token_first_char := a_end_token.item (1)
l_end_token_count := a_end_token.count
end
i := a_index
n := a_uri.count
until
i > n
loop
c := a_uri[i]
inspect c
when '/', '?' then
i := n
else
if
a_end_token /= Void and then
c = l_end_token_first_char and then
a_uri.substring (i, i + l_end_token_count - 1).same_string (a_end_token)
then
i := n
else
p := i
end
end
i := i + 1
end
Result := a_uri.substring (a_index, p)
end
note
copyright: "2011-2012, Jocelyn Fiat, 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