note description: "[ A list of {FIELD} where each {FIELD} describes the placement of a {CONTROL} withing an {EDITOR} ]" 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/fields/schema.e $" date: "$Date: 2012-03-16 14:05:07 -0400 (Fri, 16 Mar 2012) $" revision: "$Revision: 7 $" class SCHEMA inherit LINKED_LIST [FIELD] rename item as field redefine default_create, make, copy, is_equal, -- Redefine the following in order to set the time the schema was -- changed so the dialog editor can use it to determine if it needs -- to redraw the view. extend, put_front, put_left, put_right, merge_left, merge_right, -- Need to redefine the following in order to preserve the invariant -- dealing with the keys (key fields must reside in current). If a -- field is removed from Current it can no longer be used as a key. -- Other features are defined in terms of this, so these four should -- take care of all features from LINKED_LIST. remove, replace, remove_right, wipe_out select make end LINKED_LIST [FIELD] -- inherited identically as above except for `make' rename make as list_make, item as field redefine default_create, -- make, copy, is_equal, -- Redefine the following in order to set the time the schema was -- changed so the dialog editor can use it to determine if it needs -- to redraw the view. extend, put_front, put_left, put_right, merge_left, merge_right, -- Need to redefine the following in order to preserve the invariant -- dealing with the keys (key fields must reside in current). If a -- field is removed from Current it can no longer be used as a key. -- Other features are defined in terms of this, so these four should -- take care of all features from LINKED_LIST. remove, replace, remove_right, wipe_out end EDITABLE redefine default_create, copy, is_equal end create default_create create {LINKED_LIST} make feature {NONE} -- Initialization default_create -- Create an instance -- Redefined because `new_chain' from LINKED_LIST uses `make'. do list_make Precursor {EDITABLE} create keys_imp.make create mandatory_fields.make create time_modified.set_now_utc_fine extend_mandatory (Name_field) end make -- Called by `new_chain' from LINKED_LIST do Precursor {LINKED_LIST} default_create end feature -- Access identifying_field: FIELD -- The field used to get a string which will identify an object. do Result := identifying_field_ref.field end keys: LINKED_SET [STRING] -- The keys to use in comparison of EDITABLEs using Current. -- NOTE: Even though FIELDs are used in `keys_imp', ultimately the label -- of the FIELD will be used to access data in an EDITABLE, hence the result type. do create Result.make from keys_imp.start until keys_imp.exhausted loop Result.extend (keys_imp.item.label) keys_imp.forth end end key_fields: LINKED_SET [FIELD] -- The fields which are used as keys do create Result.make from keys_imp.start until keys_imp.exhausted loop Result.extend (keys_imp.item) keys_imp.forth end end Name_field: STRING_FIELD -- The FIELD used to access the "name" of the object. once create Result Result.set_label ("Name: ") end time_modified: YMDHMS_TIME -- Time at which a modification was made (such as adding -- or removing a field). feature -- Element change set_identifying_field (a_field: STRING_FIELD) -- Change the `user_identifying_field' require field_exists: a_field /= Void do identifying_field_ref.set_field (a_field) ensure field_was_assigned: identifying_field = a_field end feature -- Basic operations put_front (a_field: like field) -- Add `a_field' to beginning. -- Do not move cursor. -- Reset `time_modified'. do Precursor {LINKED_LIST} (a_field) time_modified.set_now_utc_fine end extend (a_field: like field) -- Add `a_field' to the end. -- Do not move cursor. -- Reset `time_modified'. do Precursor {LINKED_LIST} (a_field) time_modified.set_now_utc_fine end extend_mandatory (a_field: like field) -- Add `a_field' to Current and make it a manditory field. -- Reset `time_modified'. require is_extendible: extendible do check field_exists: a_field /= Void end mandatory_fields.extend (a_field) extend (a_field) ensure has_field: has (a_field) has_as_mandatory: has_mandatory (a_field) end put_left (a_field: like field) -- Add `a_field' to the left of cursor position. -- Do not move cursor. -- Reset `time_modified'. do Precursor {LINKED_LIST} (a_field) time_modified.set_now_utc_fine end put_right (a_field: like field) -- Add `a_field' to the left of cursor position. -- Do not move cursor. -- Reset `time_modified'. do Precursor {LINKED_LIST} (a_field) time_modified.set_now_utc_fine end merge_left (a_other: like Current) -- Merge `a_other' into current structure before cursor -- position. Do not move cursor. Empty `other'. -- Reset `time_modified'. do Precursor {LINKED_LIST} (a_other) time_modified.set_now_utc_fine end merge_right (a_other: like Current) -- Merge `a_other' into current structure after cursor -- position. Do not move cursor. Empty `other'. -- Reset `time_modified'. do Precursor {LINKED_LIST} (a_other) time_modified.set_now_utc_fine end remove -- Remove current item. -- Move cursor to right neighbor -- (or `after' if no right neighbor). -- Reset `time_modified'. -- Also, redefined to preserve invariant. do if attached {COMPARABLE_FIELD} field as cf then keys_imp.prune (cf) else -- Do not have to worry about it, because if `v' is not -- a COMPARABLE_FIELD it would never be in `keys' anyway. end Precursor {LINKED_LIST} time_modified.set_now_utc_fine end replace (v: like field) -- Replace current item by `v'. -- Reset `time_modified'. -- Also, redefined to preserve invariant. do if attached {COMPARABLE_FIELD} v as cf then keys_imp.prune (cf) else -- Do not have to worry about it, because if `v' is not -- a COMPARABLE_FIELD it would never be in `keys' anyway. end Precursor {LINKED_LIST} (v) time_modified.set_now_utc_fine end remove_right -- Remove item to the right of cursor position. -- Do not move cursor. -- Reset `time_modified'. -- Also, redefined to preserve invariant. do if attached {COMPARABLE_FIELD} i_th (index + 1) as cf then keys_imp.prune (cf) else -- Do not have to worry about it, because if `v' is not -- a COMPARABLE_FIELD it would never be in `keys' anyway. end Precursor {LINKED_LIST} time_modified.set_now_utc_fine end wipe_out -- Remove all items. -- Reset `time_modified'. -- Also, redefined to preserve invariant. do keys_imp.wipe_out Precursor {LINKED_LIST} time_modified.set_now_utc_fine end add_key_field (a_field: COMPARABLE_FIELD) -- Add `a_field' to use in comparisons require field_exists: a_field /= Void has_field: has (a_field) do keys_imp.extend (a_field) ensure has_key: keys_imp.has (a_field) end prune_key_field (a_field: COMPARABLE_FIELD) -- Remove `a_field' so it no longer is used in comparisons require field_exists: a_field /= Void do keys_imp.prune (a_field) ensure removed: not keys_imp.has (a_field) end promote_key_field (a_field: COMPARABLE_FIELD) -- Move `a_field' toward the beginning of `keys' by one, so it -- will be a "more important" key. require field_exists: a_field /= Void has_as_key: key_fields.has (a_field) local i: INTEGER do if keys_imp.count > 1 then i := keys_imp.index_of (a_field, 1) if i > 1 then go_i_th (i - 1) swap (i) end end ensure field_was_promoted: end demote_key_field (a_field: COMPARABLE_FIELD) -- Move `a_field' toward the end of `keys' by one, so it -- will be a "less important" key. require field_exists: a_field /= Void has_as_key: key_fields.has (a_field) local i: INTEGER do if keys_imp.count > 1 then i := keys_imp.index_of (a_field, 1) if i < count then go_i_th (i + 1) swap (i) end end ensure field_was_demoted: end set_key_primary (a_field: COMPARABLE_FIELD) -- Move `a_field' toward the end of `keys' by one, so it -- will be a "less important" key. require field_exists: a_field /= Void has_as_key: key_fields.has (a_field) local i: INTEGER do if keys_imp.count > 1 then i := keys_imp.index_of (a_field, 1) if i /= 1 then go_i_th (1) swap (i) end end ensure field_is_primary: key_fields.index_of (a_field, 1) = 1 end feature -- Querry has_mandatory (a_field: like field): BOOLEAN -- Is `a_field' required to always be in this schema? -- (I.e. can it never be deleted?) require field_exists: a_field /= Void do Result := mandatory_fields.has (a_field) ensure definition: Result = mandatory_fields.has (a_field) end feature -- Duplication copy (other: like Current) -- Update current object using fields of object attached -- to `other', so as to yield equal objects. -- Redefined to merge the copy operations from both ancestors. -- NOTE: Feature `infix "<"' from VIEWABLE only checks the `id' -- and assumes if the `id' (and hence the `time_stamp' from both -- objects are equal the object must have been copied and is -- therefore equal. do Precursor {LINKED_LIST} (other) Precursor {EDITABLE} (other) end feature -- Comparison is_equal (other: like Current): BOOLEAN -- Is Current equal to `other'? do -- Precursor {VIEWABLE} uses feature `infix "<"' to do the -- comparison, and there is no less than feature in LINKED_LIST, -- therefore this feature makes the BIG assumption that if -- the `id' and `time_stamp' (from VIEWABLE) of both objects -- are the same then it must have been a copy. There is no other -- way to make the `time_stamps' equal. Result := Precursor {EDITABLE} (other) end feature {NONE} -- Implementation keys_imp: LINKED_SET [COMPARABLE_FIELD] -- Set of fields whose associated values can be used to compare -- one EDITABLE to another in feature `infix "<"' from EDITABLE. -- The order implies the more significant fields. mandatory_fields: LINKED_SET [FIELD] -- Set of fields which can not be deleted from Current. identifying_field_ref: FIELD_REF -- The field to be used as `identifying_field' as set by the user -- in place of the default field. once create Result.set_field (Name_field) end invariant keys_imp_exists: keys_imp /= Void manditory_fields_exists: mandatory_fields /= Void keys_reside_in_current: key_fields.for_all (agent has) mandatory_fields_reside_in_current: mandatory_fields.for_all (agent has) end