note
	description: "Metric expression generator"
	legal: "See notice at end of class."
	status: "See notice at end of class."
	author: ""
	date: "$Date$"
	revision: "$Revision$"

class
	EB_METRIC_EXPRESSION_GENERATOR

inherit
	EB_METRIC_VISITOR

	SHARED_TEXT_ITEMS

	QL_SHARED_NAMES

	EB_METRIC_SHARED

create
	make

feature{NONE} -- Initialization

	make (a_output: like output)
			-- Initialize `output' with `a_output'.
		require
			a_output_attached: a_output /= Void
		do
			set_output (a_output)
		end

feature -- Access

	output: EB_METRIC_EXPRESSION_OUTPUT
			-- Generated metric expression output

feature -- Setting

	set_output (a_output: like output)
			-- Set `output' with `a_output'.
		require
			a_output_attached: a_output /= Void
		do
			output := a_output
		ensure
			output_set: output = a_output
		end

feature -- Output generation

	generate_output (a_item: EB_METRIC_VISITABLE)
			-- Generate output for `a_item'.
		require
			a_item_attached: a_item /= Void
		do
			a_item.process (Current)
			output.prepare_output
		end

feature{NONE} -- Process

	process_basic_metric (a_basic_metric: EB_METRIC_BASIC)
			-- Process `a_basic_metric'.
		do
			if a_basic_metric.criteria /= Void then
				a_basic_metric.criteria.process (Current)
			end
		end

	process_linear_metric (a_linear_metric: EB_METRIC_LINEAR)
			-- Process `a_linear_metric'.
		local
			l_metrics: LIST [STRING]
			l_coefficients: LIST [DOUBLE]
			l_is_first: BOOLEAN
			l_coefficient: DOUBLE
			l_output: like output
		do
			l_output := output
			l_metrics := a_linear_metric.variable_metric
			l_coefficients := a_linear_metric.coefficient
			from
				l_is_first := True
				l_metrics.start
				l_coefficients.start
			until
				l_metrics.after or l_coefficients.after
			loop
				l_coefficient := l_coefficients.item
				if l_coefficient >= 0 then
					if not l_is_first then
						l_output.put_operator (once " + ")
					end
				else
					if not l_is_first then
						l_output.put_operator (once " - ")
					else
						l_output.put_operator (once "- ")
					end
				end
				l_output.put_double (l_coefficient.abs)
				l_output.put_operator (once " * ")
				l_output.put_normal_text (ti_l_parenthesis.as_string_32)
				l_output.put_metric_name (l_metrics.item)

				l_output.put_normal_text (ti_r_parenthesis.as_string_32)
				l_is_first := False
				l_metrics.forth
				l_coefficients.forth
			end
		end

	process_ratio_metric (a_ratio_metric: EB_METRIC_RATIO)
			-- Process `a_ratio_metric'.
		do
			append_metric (a_ratio_metric.numerator_coefficient, a_ratio_metric.numerator_metric_name)
			output.put_operator (once " / ")
			append_metric (a_ratio_metric.denominator_coefficient, a_ratio_metric.denominator_metric_name)
		end

	process_criterion (a_criterion: EB_METRIC_CRITERION)
			-- Process `a_criterion'.
		do
		end

	process_domain_criterion (a_criterion: EB_METRIC_DOMAIN_CRITERION)
			-- Process `a_criterion'.
		do
			process_domain_criterion_internal (a_criterion, Void, Void)
		end

	process_domain_criterion_internal (a_criterion: EB_METRIC_DOMAIN_CRITERION; a_modifiers: LIST [STRING_GENERAL]; a_modifier_separator: STRING_GENERAL)
			-- Process `a_criterion'.
			-- If `a_modifiers' is not Void, display those modifiers (separated by `a_modifier_separator') as well.
		require
			a_criterion_attached: a_criterion /= Void
			a_modifiers_valid: a_modifiers /= Void implies (not a_modifiers.has (Void))
			a_modifier_separator_valid: (a_modifiers /= Void and then not a_modifiers.is_empty) implies a_modifier_separator /= Void
		local
			l_output: like output
			l_cursor: CURSOR
		do
			l_output := output
			append_negation_start (a_criterion)
			l_output.put_criterion_name (a_criterion)
			l_output.put_normal_text (ti_space.as_string_32)
			l_output.put_normal_text (ti_l_parenthesis.as_string_32)
			process_domain_internal (a_criterion.domain)
				-- Process modifiers if any.
			if a_modifiers /= Void and then not a_modifiers.is_empty then
				l_output.put_operator (", ")
				l_cursor := a_modifiers.cursor
				from
					a_modifiers.start
				until
					a_modifiers.after
				loop
					l_output.put_modifier (a_modifiers.item)
					if a_modifiers.index < a_modifiers.count then
						l_output.put_normal_text (" ")
						l_output.put_operator (a_modifier_separator)
						l_output.put_normal_text (" ")
					end
					a_modifiers.forth
				end
				a_modifiers.go_to (l_cursor)
			end
			l_output.put_normal_text (ti_r_parenthesis.as_string_32)
			append_negation_end (a_criterion)
		end

	process_caller_callee_criterion (a_criterion: EB_METRIC_CALLER_CALLEE_CRITERION)
			-- Process `a_criterion'.		
		local
			l_modifiers: ARRAYED_LIST [STRING_GENERAL]
		do
			create l_modifiers.make (2)
			if a_criterion.only_current_version then
				l_modifiers.extend (metric_names.l_current_version)
			else
				l_modifiers.extend (metric_names.l_current)
				l_modifiers.extend (metric_names.l_desendent_versions)
			end
			process_domain_criterion_internal (a_criterion, l_modifiers, "+")
		end

	process_supplier_client_criterion (a_criterion: EB_METRIC_SUPPLIER_CLIENT_CRITERION)
			-- Process `a_criterion'.
		local
			l_modifiers: ARRAYED_LIST [STRING_GENERAL]
		do
			create l_modifiers.make (3)
			if a_criterion.indirect_referenced_class_retrieved then
				l_modifiers.extend (metric_names.l_indirect)
			else
				l_modifiers.extend (metric_names.l_direct)
			end
			if a_criterion.normal_referenced_class_retrieved then
				l_modifiers.extend (metric_names.l_normal_referenced)
			end
			if a_criterion.only_syntactically_referencedd_class_retrieved then
				l_modifiers.extend (metric_names.l_syntactical_referenced)
			end
			process_domain_criterion_internal (a_criterion, l_modifiers, "+")
		end

	process_text_criterion_internal (a_criterion: EB_METRIC_TEXT_CRITERION; a_show_matching_strategy: BOOLEAN)
			-- Process `a_criterion'.
			-- If `a_show_matching_strategy' is True, display matching strategy modifier.
		require
			a_criterion_attached: a_criterion /= Void
		local
			l_output: like output
		do
			l_output := output
			append_negation_start (a_criterion)
			l_output.put_criterion_name (a_criterion)
			l_output.put_normal_text (once " ")
			l_output.put_normal_text (ti_l_parenthesis.as_string_32)
			if a_criterion.text.is_empty then
				l_output.put_warning ("%"%"")
			else
				l_output.put_string (a_criterion.text)
			end
			l_output.put_normal_text (", ")
			if a_show_matching_strategy then
				l_output.put_modifier (matching_strategy_names_table.item (a_criterion.matching_strategy).as_lower)
				l_output.put_operator (" + ")
			end
			if a_criterion.is_case_sensitive then
				l_output.put_modifier (metric_names.t_case_sensitive.as_lower)
			else
				l_output.put_modifier (metric_names.t_case_insensitive.as_lower)
			end
			l_output.put_normal_text (ti_r_parenthesis.as_string_32)
			append_negation_end (a_criterion)
		end

	process_text_criterion (a_criterion: EB_METRIC_TEXT_CRITERION)
			-- Process `a_criterion'.
		do
			process_text_criterion_internal (a_criterion, True)
		end

	process_path_criterion (a_criterion: EB_METRIC_PATH_CRITERION)
			-- Process `a_criterion'.
		do
			process_text_criterion_internal (a_criterion, False)
		end

	process_normal_criterion (a_criterion: EB_METRIC_NORMAL_CRITERION)
			-- Process `a_criterion'.
		do
			append_negation_start (a_criterion)
			output.put_criterion_name (a_criterion)
			append_negation_end (a_criterion)
		end

	process_value_criterion (a_criterion: EB_METRIC_VALUE_CRITERION)
			-- Process `a_criterion'.
		local
			l_output: like output
		do
			l_output := output
			l_output.put_normal_text ("value of metric (")
			l_output.put_metric_name (a_criterion.metric_name)
			if a_criterion.should_delayed_domain_from_parent_be_used then
				l_output.put_normal_text (", ")
				l_output.put_modifier (metric_names.l_use_external_delayed_domain)
			end
			l_output.put_normal_text (") over (")
			process_domain_internal (a_criterion.domain)
			l_output.put_normal_text (") is (")
			a_criterion.value_tester.process (Current)
			l_output.put_normal_text (ti_r_parenthesis.as_string_32)
		end

	process_external_command_criterion (a_criterion: EB_METRIC_EXTERNAL_COMMAND_CRITERION)
			-- Process `a_criterion'.
		local
			l_output: like output
		do
			l_output := output
			l_output.put_normal_text ("satisfies command (")
			a_criterion.tester.process (Current)
			l_output.put_normal_text (")")
		end

	process_nary_criterion (a_criterion: EB_METRIC_NARY_CRITERION)
			-- Process `a_criterion'.
		local
			l_para_needed: BOOLEAN
			l_count: INTEGER
			l_operands: LIST [EB_METRIC_CRITERION]
			l_output: like output
		do
			l_output := output
			l_para_needed := a_criterion.operands.count > 1
			append_negation_start (a_criterion)
			if l_para_needed then
				l_output.put_normal_text (ti_l_parenthesis.as_string_32)
			end
			from
				l_operands := a_criterion.operands
				l_count := l_operands.count
				l_operands.start
			until
				l_operands.after
			loop
				if l_para_needed and then l_operands.item.is_nary_criterion then
					l_output.put_normal_text (ti_l_parenthesis.as_string_32)
				end
				l_operands.item.process (Current)
				if l_para_needed and then l_operands.item.is_nary_criterion then
					l_output.put_normal_text (ti_r_parenthesis.as_string_32)
				end
				if l_operands.index < l_count then
					l_output.put_normal_text (ti_space.as_string_32)
					l_output.put_criterion_name (a_criterion)
					l_output.put_normal_text (ti_space.as_string_32)
				end
				a_criterion.operands.forth
			end
			if l_para_needed then
				l_output.put_normal_text (ti_r_parenthesis.as_string_32)
			end
			append_negation_end (a_criterion)
		end

	process_and_criterion (a_criterion: EB_METRIC_AND_CRITERION)
			-- Process `a_criterion'.
		do
			process_nary_criterion (a_criterion)
		end

	process_or_criterion (a_criterion: EB_METRIC_OR_CRITERION)
			-- Process `a_criterion'.
		do
			process_nary_criterion (a_criterion)
		end

	process_domain (a_domain: EB_METRIC_DOMAIN)
			-- Process `a_domain'.
		local
			l_count: INTEGER
			l_output: like output
		do
			from
				l_output := output
				l_count := a_domain.count
				a_domain.start
			until
				a_domain.after
			loop
				if a_domain.item /= Void then
					a_domain.item.process (Current)
				end
				if a_domain.index < l_count then
					l_output.put_normal_text (ti_comma.as_string_32)
					l_output.put_normal_text (ti_space.as_string_32)
				end
				a_domain.forth
			end
		end

	process_domain_item (a_item: EB_METRIC_DOMAIN_ITEM)
			-- Process `a_item'.
		do
		end

	process_application_target_domain_item (a_item: EB_METRIC_TARGET_DOMAIN_ITEM)
			-- Process `a_item'.
		do
			output.put_target_domain_item (a_item)
		end

	process_group_domain_item (a_item: EB_METRIC_GROUP_DOMAIN_ITEM)
			-- Process `a_item'.
		do
			output.put_group_domain_item (a_item)
		end

	process_folder_domain_item (a_item: EB_METRIC_FOLDER_DOMAIN_ITEM)
			-- Process `a_item'.
		do
			output.put_folder_domain_item (a_item)
		end

	process_class_domain_item (a_item: EB_METRIC_CLASS_DOMAIN_ITEM)
			-- Process `a_item'.
		do
			output.put_class_domain_item (a_item)
		end

	process_feature_domain_item (a_item: EB_METRIC_FEATURE_DOMAIN_ITEM)
			-- Process `a_item'.
		do
			output.put_feature_domain_item (a_item)
		end

	process_delayed_domain_item (a_item: EB_METRIC_DELAYED_DOMAIN_ITEM)
			-- Process `a_item'.
		do
			output.put_delayed_domain_item (a_item)
		end

	process_metric_archive_node (a_item: EB_METRIC_ARCHIVE_NODE)
			-- Process `a_item'.
		do
		end

	process_value_retriever (a_item: EB_METRIC_VALUE_RETRIEVER)
			-- Process `a_item'.
		do
		end

	process_value_tester (a_item: EB_METRIC_VALUE_TESTER)
			-- Process `a_item'.
		local
			l_cri_list: LIST [TUPLE [l_retriever: EB_METRIC_VALUE_RETRIEVER; l_criterion_type: INTEGER]]
			l_count: INTEGER
			l_connector: STRING
			l_output: like output
		do
			l_output := output
			if a_item.criteria.is_empty then
				l_output.put_warning (metric_names.l_no_value_tester)
			else
				if a_item.is_anded then
					l_connector := query_language_names.ql_cri_and
				else
					l_connector := query_language_names.ql_cri_or
				end
				from
					l_cri_list := a_item.criteria
					l_count := l_cri_list.count
					l_cri_list.start
				until
					l_cri_list.after
				loop
					l_output.put_operator (operator_table.item (l_cri_list.item.l_criterion_type))
					l_cri_list.item.l_retriever.process (Current)
					if l_cri_list.index < l_count then
						l_output.put_normal_text (ti_space.as_string_32)
						l_output.put_keyword (l_connector)
						l_output.put_normal_text (ti_space.as_string_32)
					end
					l_cri_list.forth
				end
			end
		end

	process_constant_value_retriever (a_item: EB_METRIC_CONSTANT_VALUE_RETRIEVER)
			-- Process `a_item'.
		do
			output.put_double (a_item.value_internal)
		end

	process_metric_value_retriever (a_item: EB_METRIC_METRIC_VALUE_RETRIEVER)
			-- Process `a_item'.
		local
			l_output: like output
		do
			l_output := output
			l_output.put_normal_text ("value of metric (")
			l_output.put_metric_name (a_item.metric_name)
			l_output.put_normal_text (") over (")
			process_domain_internal (a_item.input_domain)
			l_output.put_normal_text (")")
		end

	process_domain_internal (a_domain: EB_METRIC_DOMAIN)
			-- Process `a_domain'.
		require
			a_domain_attached: a_domain /= Void
		do
			if a_domain.is_empty then
				output.put_warning (metric_names.l_empty_domain)
			else
				a_domain.process (Current)
			end
		end

	process_external_command_tester (a_item: EB_METRIC_EXTERNAL_COMMAND_TESTER)
			-- Process `a_item'.
		local
			l_first: BOOLEAN
			l_output: like output
		do
			l_output := output
			l_output.put_string (a_item.command)
			l_first := True
			if not a_item.input.is_empty then
				l_output.put_normal_text (ti_comma.as_string_32)
				l_output.put_normal_text (ti_space.as_string_32)
				l_first := False
				if a_item.is_input_as_file then
					l_output.put_modifier ("input as file")
				else
					l_output.put_modifier ("input")
				end
			end
			if a_item.is_output_enabled then
				if not l_first then
					l_output.put_operator (" + ")
				else
					l_output.put_normal_text (ti_comma.as_string_32)
					l_output.put_normal_text (ti_space.as_string_32)
					l_first := False
				end
				if a_item.is_output_as_file then
					l_output.put_modifier ("output as file")
				else
					l_output.put_modifier ("output")
				end
			end
			if a_item.is_error_enabled then
				if not l_first then
					l_output.put_operator (" + ")
				else
					l_output.put_normal_text (ti_comma.as_string_32)
					l_output.put_normal_text (ti_space.as_string_32)
					l_first := False
				end
				if not a_item.is_error_redirected_to_output then
					if a_item.is_error_as_file then
						l_output.put_modifier ("error as file")
					else
						l_output.put_modifier ("error")
					end
				end
			end
			if a_item.is_exit_code_enabled then
				if not l_first then
					l_output.put_operator (" + ")
				else
					l_output.put_normal_text (ti_comma.as_string_32)
					l_output.put_normal_text (ti_space.as_string_32)
				end
				l_output.put_modifier ("exit code (")
				l_output.put_integer (a_item.exit_code)
				l_output.put_modifier (")")
			end
		end

feature{NONE} -- Implementation

	append_negation_start (a_criterion: EB_METRIC_CRITERION)
			-- Append negation for `a_criterion'.
		require
			a_criterion_attached: a_criterion /= Void
		local
			l_output: like output
		do
			l_output := output
			if a_criterion.is_negation_used then
				l_output.put_normal_text (ti_l_parenthesis.as_string_32)
				l_output.put_keyword (ti_not_keyword.as_string_32)
				l_output.put_normal_text (ti_space.as_string_32)
			end
		end

	append_negation_end (a_criterion: EB_METRIC_CRITERION)
			-- Append negation end.
		require
			a_criterion_attached: a_criterion /= Void
		do
			if a_criterion.is_negation_used then
				output.put_normal_text (ti_r_parenthesis.as_string_32)
			end
		end

	append_metric (a_coefficient: DOUBLE; a_metric_name: STRING)
			-- Append `a_metric_name' with `a_coefficient'.
		require
			a_metric_name_attached: a_metric_name /= Void
		local
			l_output: like output
		do
			l_output := output
			l_output.put_normal_text (ti_l_parenthesis.as_string_32)
			l_output.put_double (a_coefficient)
			l_output.put_normal_text (ti_space.as_string_32)
			l_output.put_operator (once "*")
			l_output.put_normal_text (ti_space.as_string_32)
			l_output.put_metric_name (a_metric_name)
			l_output.put_normal_text (ti_r_parenthesis.as_string_32)
		end

invariant
	output_attached: output /= Void

note
	copyright:	"Copyright (c) 1984-2012, Eiffel Software"
	license:	"GPL version 2 (see http://www.eiffel.com/licensing/gpl.txt)"
	licensing_options:	"http://www.eiffel.com/licensing"
	copying: "[
			This file is part of Eiffel Software's Eiffel Development Environment.
			
			Eiffel Software's Eiffel Development Environment is free
			software; you can redistribute it and/or modify it under
			the terms of the GNU General Public License as published
			by the Free Software Foundation, version 2 of the License
			(available at the URL listed under "license" above).
			
			Eiffel Software's Eiffel Development Environment is
			distributed in the hope that it will be useful, but
			WITHOUT ANY WARRANTY; without even the implied warranty
			of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
			See the GNU General Public License for more details.
			
			You should have received a copy of the GNU General Public
			License along with Eiffel Software's Eiffel Development
			Environment; if not, write to the Free Software Foundation,
			Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
		]"
	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