note
	description: "[
		Used with EDITOR classes.
		Object which can be edited in a dialog box, an {EDITOR} using
		a format stored in a `schema'.
		]"
	date: "7 Mar 03"
	author:		"Jimmy J. Johnson"
	copyright:	"Copyright 2012, Jimmy J. Johnson"
	license:	"Eiffel Forum License v2 (see forum.txt)"
	URL: 		"$URL: file:///F:/eiffel_repositories/jj_vision/trunk/interface/system/editable.e $"
	date:		"$Date: 2012-03-16 14:05:07 -0400 (Fri, 16 Mar 2012) $"
	revision:	"$Revision: 7 $"

class
	EDITABLE

inherit

	TIME_STAMPABLE
		redefine
			default_create
--			infix "<"
		end

create
	default_create

feature {NONE} -- Initialization

	default_create
		do
			Precursor {TIME_STAMPABLE}
			create data_table.make (5)
			schema_imp := Default_schema
		end

feature -- Access

	value (a_key: STRING): detachable ANY
			-- The value associated with `key' or Void if none.
		require
			key_exists: a_key /= Void
		do
			Result := data_table.item (a_key)
		end

	schema: SCHEMA
			-- Used to construct a dialog in which to edit the values
			-- which are then stored in `data_table'.
		require
			schema_available: is_schema_available
		do
			check attached schema_imp as s then
				Result := s
			end
		ensure
			result_exists: Result /= Void
		end

	display_name: STRING_32
			-- Used as an "out" value for display in TOOLs and other widgets.
		do
			if attached {STRING_32} value (schema.identifying_field.label) as s then
				Result := s
			else
				Result := Current.id
			end
		end

feature -- Element change

	set_target_label_field (a_field: STRING_FIELD)
			-- Change the field used to display an identifier in the tools, etc.
			-- This is really in the `schema' of the editable.
		require
			field_exists: a_field /= Void
		local
			w: JJ_MAIN_WINDOW
		do
			schema.set_identifying_field (a_field)
				-- Because `target_label_field' is posibly used by all views, when it changes
				-- all views must be updated because there is no way for the view to know
				-- if it contains `target_label_field'.  Originally `target_label_field'
				-- was added to the views using `add_object' (see VIEW), but using a FIELD
				-- implies some EDITABLEs in the system.  I wanted the "Standard Interface"
				-- cluster to stand alone and not need the "Dynamic Editor" cluster to which
				-- this class belongs.
			check attached {JJ_APPLICATION} (create {EV_ENVIRONMENT}).application as a then
					-- because only JJ_APPLICATIONS should use this.  really ???
				-- Get hold of any VIEW; `first_window' will work.
				w := a.first_window
				w.draw_all_views
			end
		end

	set_schema (a_schema: like schema)
			-- Change `schema' indirectly by changing `schema_imp'.
		require
			schema_exists: a_schema /= Void
		do
			schema_imp := a_schema
		ensure
			schema_was_set: schema = a_schema
		end

	remove_schema
			-- Make `schema_imp' Void
		do
			schema_imp := Void
		ensure
			schema_imp_void: schema = Void
		end

	extend_value (a_value: ANY; a_key: STRING)
			-- Add `a_value' to the `data_table' and associate it with `a_key'
			-- and accessable through feature `value'.
			-- NOTE:  `a_value' can be Void.
		require
			key_exists: a_key /= Void
			value_exists: a_value /= Void	-- temp
		do
			data_table.force (a_value, a_key)
		ensure
			has_value: a_value /= Void implies data_table.has_item (a_value)
			has_key: data_table.has (a_key)
		end

	remove_value (a_key: STRING)
			-- Remove the value associated with `a_key' and `a_key'.
		require
			key_exists: a_key /= Void
		do
			data_table.remove (a_key)
		ensure
			not_has_value: not data_table.has (a_key)
			not_has_key: not data_table.has (a_key)
		end

--	extend_with_field (a_value: ANY; a_field: FIELD) is
--			-- Change the value of the data as index using the `name' from `a_field'.
--			-- NOTE:  `a_value' can be Void.
--		require
--			field_exists: a_field /= Void
--			schema_has_field: schema.has (a_field)
--		do
--			extend (a_value, a_field.label)
--		ensure
--			has_value: a_value /= Void implies data_table.has_item (a_value)
--			has_key: data_table.has (a_field.label)
--		end

--	change (a_control: VALUE_KEY_PAIR) is
--			-- Change the `value' in Current to the value stored in `a_control'
--			-- NOTE: `a_control' contains a `key' / `value' pair.
--		do
--			data_table.force (a_control.value, a_control.key)
--		end

	remove_unreachable_data
			-- Remove any data from this EDITABLE that cannot be
			-- accessed	using the keys in `a_key_set' or the
			-- `keys' in `template'.
		do
		end

feature -- Status report

	is_schema_available: BOOLEAN
			-- Does Current have a schema?
		do
			Result := schema_imp /= Void
		end

feature -- Query

	has_schema (a_schema: SCHEMA): BOOLEAN
			-- Does `schema' equal `a_schema'?
		require
			schema_exists: a_schema /= Void
		do
			Result := schema = a_schema
		end

	has_value (a_key: STRING): BOOLEAN
			-- Is there a value associated with `a_key'?
		require
			key_exists: a_key /= Void
		do
			data_table.search (a_key)
			Result := data_table.found
		end

feature -- Comparison

	is_less alias "<" (a_other: like Current): BOOLEAN
			-- Is Current less than `a_other'?
			-- This uses the `keys' from `schema' first and then the `id'.
		do
			if not (Current = a_other) then
				if schema = Void and a_other.schema = Void then
					Result := Current < a_other
				elseif schema /= Void and a_other.schema = Void then
					Result := True
				elseif schema = Void and a_other.schema /= Void then
					Result := False
				elseif schema /= Void and a_other.schema /= Void then
					Result := compare_key_values (a_other)
				end
			end
		ensure then
			zero_schemas: (schema = Void and a_other.schema = Void) implies Result = (Current < a_other)
			one_schema_current: (schema /= Void and a_other.schema = Void) implies Result
			one_schema_other: (schema = Void and a_other.schema /= Void) implies not Result
			same_schema_both: (schema /= Void and a_other.schema /= Void) implies Result = compare_key_values (a_other)
		end

feature {NONE} -- Implementation

	compare_key_values (a_other: like Current): BOOLEAN
			-- Is Current less than `a_other' when comparing keys?
			-- Feature to decompose `infix "<"'.
		require
			other_exists: a_other /= Void
			schema_exists: schema /= Void
			other_schema_exists: a_other.schema /= Void
		local
			keys: LINKED_SET [STRING]
			k: STRING
			done: BOOLEAN
		do
			keys := schema.keys
			if schema /= a_other.schema then
				keys.intersect (a_other.schema.keys)
			end
			from keys.start
			until done or else keys.exhausted
			loop
				k := keys.item
					-- Now compare the values.  If both are Void (can happen) or reference
					-- the same object (unlikely or imposible) then just continue to
					-- the next key.  If they are not equal then do comparison.
				if attached {COMPARABLE} value (k) as c and
						attached {COMPARABLE} a_other.value (k) as other_c then
					Result := c < other_c
					done := True
				end
				keys.forth
			end
				-- At this point we have either made a determination using the
				-- values at the keys, in which case `done' will be true, or we
				-- have exhausted the keys without making a determination.  In
				-- the second case compare the `id' features; this should be
				-- true if Current was created before `a_other'.  (Feature `id'
				-- incorporates a `time_stamp'.)
			if not done then
				Result := Current.id < a_other.id
			end
		end

feature {NONE}-- Implementation

	schema_imp: detachable like schema
			-- Implementation of `schema'. Set by `set_schema' and
			-- removed by `remove_schema.
			-- NOTE:  The FIELDs in a SCHEMA will determine the keys to be
			-- used in `data_table' and will be used to build the controls
			-- for obtaining the values asscociated with the keys.

	data_table: HASH_TABLE [ANY, STRING]
			-- The actual data is stored here.		

	Default_schema: SCHEMA
			-- Schema available to all EDITABLEs
		once
			create Result
		end

invariant

--	data_table_exists: data_table /= Void

end
