note
	description: "Implementation of DB_REPOSITORY"
	legal: "See notice at end of class."
	status: "See notice at end of class.";
	date: "$Date$"
	revision: "$Revision$"

class
	DATABASE_REPOSITORY [reference G -> DATABASE create default_create end]

inherit
	HANDLE_SPEC [G]
	
	EXCEPTIONS
		rename
			class_name as exceptions_class_name
		end

	DB_STATUS_USE

	ACTION
		redefine
			execute
		end

	EXT_INTERNAL

	BASIC_ROUTINES

	GLOBAL_SETTINGS

create -- Creation procedure

	make

feature -- Initialization

	make
			-- Create attributes
		do
			create table.make (1)
			create repository_name.make(1)
			create rep_qualifier.make(1)
			create rep_owner.make(1)
		end

	load
			-- Load in description of repository `repository_name'.
		require else
			repository_name_exists: repository_name /= Void
		local
			l_use_ext: BOOLEAN
		do
				-- Read repository names as STRING_8, since Eiffel do not support Unicode names anyway.
			l_use_ext := use_extended_types
			set_use_extended_types (False)
			table.wipe_out
			request_select.reset
			request_select.set_action (Current)
			request_select.set_map_name (repository_name, "rep")
			request_select.query (Selection_string)
			request_select.unset_map_name ("rep")
			if (request_select.is_ok) then
				request_select.load_result
			end
			request_select.terminate
			set_use_extended_types (l_use_ext)
		end


feature -- Basic operations

	generate_class(f: FILE)
			-- Generate Eiffel class template according to data
			-- representation given by `repository_name'.
		require
			file_exists: f /= Void and then f.exists
		local
			el: COLUMNS [G]
			el_type: INTEGER
			col_name: STRING
			tmp_class_name: STRING
			s,s1,s2,s3,s4: STRING
			l_column_name: detachable STRING
		do
			tmp_class_name := repository_name.as_upper
			Create s.make(20)

			s.append("note%N")
			s.append("%Tdescription: %"Class which allows EiffelStore to retrieve/store%%%N")
			s.append("%T      %%the content relative to a column of the table "+tmp_class_name+"%"%N%N")

			s.append("class%N%T"+tmp_class_name+" %N%N")
			s.append("inherit%N%TANY%N%N")
			s.append("create%N%Tmake%N%N")

			s.append ("%Nfeature -- Access%N%N")

			s1 := "feature -- Settings%N%N"
			s3 := "feature -- Initialization%N%N%Tmake%N%T%Tdo%N"
			s4 := "feature -- Output%N%N%Tout_32: STRING_32%N%T%Tdo%N%T%T%TResult := {STRING_32}%"%"%N"

			from
				table.start
			until
				table.off
			loop
				el := table.item
				check attached el.column_name as l_name then
						-- FIXME: implied by ...bug
					l_column_name := l_name.as_string_8
				end
				col_name := l_column_name.as_lower

				s1.append("%Tset_"+col_name+" (a_"+col_name+": ")
				s.append ("%T"+col_name+": ")
				el_type := el.eiffel_type
				inspect
					el_type
				when {DB_TYPES}.integer_32_type then
					s2 := "INTEGER"
					s3.append("%T%T%T"+col_name+" := 0%N")
				when {DB_TYPES}.boolean_type then
					s3.append("%T%T%T"+col_name+" := FALSE%N")
					s2 := "BOOLEAN"
				when {DB_TYPES}.real_32_type then
					s3.append("%T%T%T"+col_name+" := 0.0%N")
					s2 := "REAL_32"
				when {DB_TYPES}.real_64_type then
					s3.append("%T%T%T"+col_name+" := 0.0%N")
					s2 := "REAL_64"
				when {DB_TYPES}.string_type, {DB_TYPES}.character_type then
					if el.data_length = 1 then
						s3.append("%T%T%T"+col_name+" := 'a'%N")
						s2 := "CHARACTER"
					else
						s3.append("%T%T%T"+col_name+" := %"%"%N")
						s2 := "STRING"
					end
				when {DB_TYPES}.string_32_type then
					s3.append("%T%T%T"+col_name+" := {STRING_32}%"%"%N")
					s2 := "STRING_32"
				when {DB_TYPES}.date_type then
					s2 := "DATE_TIME"
					s3.append("%T%T%Tcreate "+col_name+".make_now%N")
				when {DB_TYPES}.decimal_type then
					if is_decimal_used then
						if attached decimal_creation_function.item (["10", 0, 2, 1]) as l_dec then
							s2 := l_dec.generating_type.out
						else
							s2 := "ANY"
						end
						s3.append("%T%T%T%T-- Rewrite the following code to create your instance of your own decimal type.%N")
						s3.append("%T%T%Tcreate "+col_name+"%N")
					else
							-- We use a REAL_64 to store decimal
						s3.append("%T%T%T"+col_name+" := 0.0%N")
						s2 := "REAL_64"
					end
				else
					s2 :=  "ANY"
				end

				if el_type /= {DB_TYPES}.string_32_type then
					s4.append("%T%T%TResult.append (" + col_name + ".out + %"%%N%")%N")
				else
					s4.append("%T%T%TResult.append (" + col_name + " + %"%%N%")%N")
				end

				s1.append(s2+")%N%T%T%T-- Set the value of "+col_name)
				s1.append("%N%T%Trequire")
				s1.append("%N%T%T%Tvalue_exists: a_"+col_name+" /= Void")
				s1.append("%N%T%Tdo")
				s1.append("%N%T%T%T"+col_name+" := a_"+col_name)
				s1.append("%N%T%Tensure")
				s1.append("%N%T%T%T" + col_name + "_set: a_"+col_name+" = "+col_name)
				s1.append("%N%T%Tend%N%N")
				s.append (s2+"%N%T%T%T-- Auto-generated.%N%N")
				table.forth
			end
			s3.append("%T%Tend%N%N")
			s4.append("%T%Tend%N%N")

			s.append (s3)
			s.append (s1)
			s.append (s4)
			s.append ("end -- class "+tmp_class_name+"%N")
			f.put_string (s)
		end

	execute
			-- Implement an ACTION operation
		local
			l_tmp_col: like tmp_acc_col
		do
			l_tmp_col := tmp_acc_col
			if l_tmp_col = Void then
				create l_tmp_col
				tmp_acc_col := l_tmp_col
			end
			request_select.object_convert (l_tmp_col)
			request_select.cursor_to_object
			table.extend (l_tmp_col.duplicate)
			l_tmp_col.clear_all
		end

feature -- Status setting

	allocate (object: ANY; table_name: STRING)
			-- Create a schema table `repository_name' conforming
			-- to `object' basic attributes.
		require else
			table_not_void: table_name /= Void
			object_not_void: object /= Void
		local
			i: INTEGER
			ft: INTEGER
			r_string: STRING_32
			t_int: INTEGER
			quoter: STRING_32
			sep: STRING_32
			l_obj: detachable ANY
			l_string_count, l_string_capacity: INTEGER
			l_is_string_8: BOOLEAN
			l_exception: DEVELOPER_EXCEPTION
		do
			create r_string.make (512)
			create quoter.make(1)
			create sep.make(1)
			quoter := db_spec.identifier_quoter
			sep := db_spec.qualifier_separator
			r_string.append ("create table ")
			if (rep_qualifier /= Void and then rep_qualifier.count > 0) then
				r_string.append(quoter)
				r_string.append(rep_qualifier)
				r_string.append(quoter)
			end
			if (rep_owner /= Void and then rep_owner.count > 0) then
				r_string.append(sep)
				r_string.append(quoter)
				r_string.append(rep_owner)
				r_string.append(quoter)
			end
			if ((rep_owner /= Void and then rep_owner.count > 0) or (rep_qualifier /= Void and then rep_qualifier.count > 0)) then
				r_string.append(".")
			end
			r_string.append(quoter)
			r_string.append(table_name)
			r_string.append(quoter)
			r_string.append (" (")
			from
				i := 1
			until
				i > field_count (object)
			loop
				ft := field_type (i, object)
				if ft = 0 then
					r_string.wipe_out
					r_string.append (field_name (i, object))
					r_string.append (" can't be Void")
					create l_exception
					l_exception.set_description (r_string)
					l_exception.raise
				end
				inspect ft
				when Integer_type then
					r_string.append (field_name (i, object))
					r_string.append (" ")
					r_string.append (db_spec.sql_name_integer)
				when integer_16_type then
					r_string.append (field_name (i, object))
					r_string.append (" ")
					r_string.append (db_spec.sql_name_integer_16)
				when integer_64_type then
					r_string.append (field_name (i, object))
					r_string.append (" ")
					r_string.append (db_spec.sql_name_integer_64)
				when Real_type then
					r_string.append (field_name (i, object))
					r_string.append (" ")
					r_string.append (db_spec.sql_name_real)
				when Double_type then
					r_string.append (field_name (i, object))
					r_string.append (" ")
					r_string.append (db_spec.sql_name_double)
				when Character_type then
					r_string.append (field_name (i, object))
					r_string.append (" ")
					r_string.append (db_spec.sql_name_character)
				when Boolean_type then
					r_string.append (field_name (i, object))
					r_string.append (" ")
					r_string.append (db_spec.sql_name_boolean)
				else
					l_obj := field (i, object)
					if is_readable_string_general (l_obj) then
						r_string.append (field_name (i, object))
						check attached {READABLE_STRING_GENERAL} l_obj as l_string then
							l_string_count := l_string.count
							l_string_capacity := l_string.capacity
							l_is_string_8 := l_string.is_string_8
						end
						if l_string_count < Max_char_size then
							r_string.append (" ")
							if l_is_string_8 then
								r_string.append (db_spec.sql_string)
							else
								r_string.append (db_spec.sql_wstring)
							end
							t_int := l_string_capacity
							if t_int = 0 then
								t_int := 10
							elseif t_int > Max_char_size then
								t_int := Max_char_size - 1
							end
							r_string.append (t_int.out)
							r_string.append (")")
						else
							r_string.append (" ")
							if l_is_string_8 then
								r_string.append (db_spec.sql_string2 (Max_char_size))
							else
								r_string.append (db_spec.sql_wstring2 (Max_char_size))
							end
						end
					elseif is_date (l_obj) then
						r_string.append (field_name (i, object))
						r_string.append (" ")
						r_string.append (db_spec.sql_name_datetime)
					elseif is_decimal_used and then is_decimal (l_obj) then
						r_string.append (field_name (i, object))
						r_string.append (" ")
						r_string.append (db_spec.sql_name_decimal)
						r_string.append (" (")
						r_string.append (default_decimal_presicion.out)
						r_string.append (", ")
						r_string.append (default_decimal_scale.out)
						r_string.append (")")
					else
						raise ("unknown type")
					end
				end
				if i /= field_count (object) then
					r_string.append (", ")
				end
				i := i + 1
			end
			r_string.extend (')')
			request_create.modify (r_string)
		end

	change_name (new_name: like repository_name)
			-- Set repository name with `repository_name'.
		require else
			new_name_not_void: new_name /= Void
		do
			repository_name := new_name
			if (not db_spec.sensitive_mixed) then
				repository_name.to_lower
			end
			table.wipe_out
		ensure then
			not exists
		end

feature -- Status report

	repository_name: STRING
			-- Repository name corresponding to table name in DB schema.
			-- Cannot be Unicode string, because Eiffel identifier's restriction.

	rep_qualifier: STRING
			-- Qualifier of the Repository.

	rep_owner: STRING
			-- Owner of the table.

	dimension: INTEGER
			-- Table column count.
		require else
			repository_exists: exists
		do
			Result := table.count
		end

	column_name (i :INTEGER): detachable STRING
			-- Name of i-th column of table-like repository.
		require else
			repository_exists: exists
			good_position: 0 < i and i <= dimension
		do
			if attached table.i_th (i).column_name as l_name then
				Result := l_name
			end
		end

	conforms (object: ANY): BOOLEAN
			-- Is the structure of repository_name the same
			-- as the structure of `object'.
		require else
			object_exists: object /= Void
		local
			i: INTEGER
			ft: INTEGER
			col_type: ANY
			t_string, f_name: STRING
			el: COLUMNS [G]
		do
			if field_count (object) = table.count then
				from
					i := 1
					Result := true
				until
					i > field_count (object) or not Result
				loop
					ft := field_type (i, object)
					if ft = 0 then
						create t_string.make (0)
						t_string.append (field_name(i,object))
						t_string.append (" does not exist")
						raise (t_string)
					end
					from
						f_name := field_name (i, object)
						table.start
					until
						table.after or f_name ~ table.item.column_name
					loop
						table.forth
					end
					if table.after then
						Result := false
					else
						el := table.item

						col_type := el.eiffel_type
						inspect ft
						when Integer_type then
							Result := col_type = {DB_TYPES}.integer_32_type
						when Real_type then
							Result := col_type = {DB_TYPES}.real_32_type
						when double_type then
							Result := col_type = {DB_TYPES}.real_64_type
						when Character_type then
							Result := col_type = {DB_TYPES}.string_type and el.data_length = 1
						when Boolean_type then
							Result := col_type = {DB_TYPES}.boolean_type
						else
							if is_string (field (i, object)) then
								Result := col_type = {DB_TYPES}.string_type
							elseif is_string32 (field (i, object)) then
								Result := col_type = {DB_TYPES}.string_32_type
							elseif is_date (field (i,object)) then
								Result := col_type = {DB_TYPES}.date_type
							elseif is_decimal_used and then is_decimal (field (i,object)) then
								Result := col_type = {DB_TYPES}.decimal_type
							else
								Result := false
							end
						end
					end
					i := i + 1
				end
			end

		end

	exists: BOOLEAN
			-- Does current repository exist in database schema?
		do
			Result := not table.is_empty
		end

	column_number: INTEGER
			-- Column Number.
		do
			Result := table.count
		ensure
			Result >= 0
		end

	column_i_th(i: INTEGER): COLUMNS[DATABASE]
			-- Column corresponding to indice 'i'.
		require
			indice_valid: i>=1 and i<=column_number
		do
			Result := table.i_th(i)
		ensure
			Result /= Void
		end


feature {NONE} -- Status report

	tmp_acc_col: detachable COLUMNS [G]
			-- Temporary column related information holder.

	table: ARRAYED_LIST [COLUMNS [G]]
			-- List of column related information.
			--| Cedric: LINKED_LIST replaced with an ARRAYED_LIST:
			--| no element move in the class, feature to access list
			--| elements is `column_i_th'.

	request_select: DB_SELECTION
			-- Selection utility object.
		once
			create Result.make
		end

	request_create: DB_CHANGE
			-- Modification utility object.
		once
			create Result.make
		end

	Selection_string: STRING
		do
			Result := db_spec.Selection_string (rep_qualifier, rep_owner, repository_name)
		end

	Max_char_size: INTEGER
		do
			Result := db_spec.Max_char_size
		end

note
	copyright:	"Copyright (c) 1984-2014, 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 -- class DATABASE_REPOSITORY