note
	description: "Summary description for {URI_TEMPLATE_EXPRESSION}."
	author: ""
	date: "$Date$"
	revision: "$Revision$"

class
	URI_TEMPLATE_EXPRESSION

inherit
	ANY

	DEBUG_OUTPUT
		export {NONE} all end

	URI_TEMPLATE_CONSTANTS
		export {NONE} all end

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 Reserved_operator then
						--| '+'
						reserved := True
						operator := '+'
					when Label_operator then
						--| '.'
						operator := '.'
						op_prefix := '.'
						op_delimiter := '.'
					when Path_segment_operator then
						--| '/'
						operator := '/'
						op_prefix := '/'
						op_delimiter := '/'
					when Path_style_parameters_operator then
						--| ';'
						operator := ';'
						op_prefix := ';'
						op_delimiter := ';'
					when Form_style_query_operator then
						--| '?'
						operator := '?'
						op_prefix := '?'
						op_delimiter := '&'
					when form_style_query_continuation then
						--| '&'
						operator := '&'
						op_prefix := '&'
						op_delimiter := '&'
					when fragment_expansion then
						--| '#'
						reserved := True
						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 [URI_TEMPLATE_EXPRESSION_VARIABLE]} vars.make (lst.count)
						lst.start
					until
						lst.after
					loop
						s := lst.item
						vmodifier := Void
						p := s.index_of (Default_delimiter, 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 Explode_plus, Explode_star, Modifier_substring, Modifier_remainder 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 (Current, vn, s, vmodifier))
						lst.forth
					end
					variables := vars
				end
				is_analyzed := True
			end
		end

feature -- Access

	position: INTEGER
			-- Character position on Current in the template

	end_position: INTEGER
		do
			Result := position + expression.count + 2 --|  '{' + `expression' + '}'
		end

	expression: STRING
			-- Operator? + VariableName + modifier

	is_query: BOOLEAN
			-- Is in the query part (i.e: after '?' ?)

feature -- Status

	operator: CHARACTER
			-- First character of `expression' if among

	reserved: BOOLEAN
			-- Is reserved
			-- i.e: do not url-encode the reserved character

	op_prefix: CHARACTER
			-- When expanding list of table, first character to use
			--| ex: '?'  for  {?var}

	op_delimiter: CHARACTER
			-- When expanding list of table, delimiter character to use
			--| ex: ','  for  {?var}

	variables: detachable LIST [URI_TEMPLATE_EXPRESSION_VARIABLE]
			-- List of variables declared in `expression'
			--| ex: "foo", "bar"  for {?foo,bar}

	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_expanded_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)
			end
		end

feature {NONE} -- Implementation

	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: URI_TEMPLATE_EXPRESSION_VARIABLE
			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.expanded_string (vdata)
						if vstr = Void and vi.has_explode 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 = Form_style_query_operator or operator = form_style_query_continuation or operator = path_style_parameters_operator) and not vi.has_explode_star then
							a_buffer.append (vi.name)
							if vi.has_explode_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-2014, 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