note description: "A sequence of actions to be performed on call." legal: "See notice at end of class." instructions: "[ Use features inherited from LIST to add/remove actions. An action is a procedure of ANY class that takes EVENT_DATA. When call is called the actions in the list will be executed in order stating at first. An action may call abort which will cause call to stop executing actions in the sequence. (Until the next call to call). Descendants may redefine initialize to arrange for call to be called by an event source. Use block, pause, flush and resume to change the behavior of call. eg. birthday_data: TUPLE [INTEGER, STRING] -- (age, name) birthday_actions: ACTION_SEQUENCE [like birthday_data] create birthday_actions.make ("birthday", <<"age","name">>) send_card (age: INTEGER, name, from: STRING) is ... buy_gift (age: INTEGER, name, gift, from: STRING) is ... birthday_actions.extend (agent send_card (?, ?, "Sam") birthday_actions.extend (agent buy_gift (?, ?, "Wine", "Sam") birthday_actions.call ([35, "Julia"]) causes call to: send_card (35, "Julia", "Sam") buy_gift (35, "Julia", "Wine", "Sam") ]" status: "See notice at end of class." keywords: event, action date: "$Date: 2018-11-14 15:15:17 +0000 (Wed, 14 Nov 2018) $" revision: "$Revision: 102463 $" class ACTION_SEQUENCE [EVENT_DATA -> detachable TUPLE create default_create end] inherit INTERACTIVE_LIST [PROCEDURE [EVENT_DATA]] rename make as arrayed_list_make redefine default_create, on_item_added_at, on_item_removed_at, prune end create default_create, make create {ACTION_SEQUENCE} arrayed_list_make, make_filled feature {NONE} -- Initialization default_create -- Begin in Normal_state. do arrayed_list_make (0) state := Normal_state end feature -- Miscellaneous on_item_added_at (an_item: like item; item_index: INTEGER_32) -- an_item has just been added at index item_index. do if count = 1 and not_empty_actions_internal /= Void then call_action_list (not_empty_actions) end end on_item_removed_at (an_item: like item; item_index: INTEGER_32) -- an_item has just been removed from index item_index. do if count = 0 and empty_actions_internal /= Void then call_action_list (empty_actions) end end feature -- Basic operations call (event_data: detachable EVENT_DATA) -- Call each procedure in order unless is_blocked. -- If is_paused delay execution until resume. -- Stop at current point in list on abort. local l_routines_snapshot: SPECIAL [like item] l_count: INTEGER_32 l_action: like item l_kamikazes: SPECIAL [like item] l_kamikazes_internal: like kamikazes_internal l_is_aborted_stack: like is_aborted_stack i: INTEGER_32 do if count > 0 then create l_routines_snapshot.make_empty (count); l_routines_snapshot.copy_data (area, 0, 0, count) l_kamikazes_internal := kamikazes_internal if l_kamikazes_internal /= Void and then not l_kamikazes_internal.is_empty then from l_kamikazes := l_kamikazes_internal.area l_count := l_kamikazes_internal.count i := 0 until i = l_count loop prune_all (l_kamikazes @ i) i := i + 1 end; l_kamikazes_internal.wipe_out end inspect state when Normal_state then from l_is_aborted_stack := is_aborted_stack; l_is_aborted_stack.extend (False) l_count := l_routines_snapshot.count i := 0 variant l_count - i until i = l_count or l_is_aborted_stack.item loop l_action := l_routines_snapshot @ i if l_action /= Void then l_action.call (event_data) end i := i + 1 end; l_is_aborted_stack.remove when Paused_state then call_buffer.extend (event_data) when Blocked_state then end end ensure is_aborted_stack_unchanged: (old is_aborted_stack) ~ is_aborted_stack end extend_kamikaze (an_item: like item) -- Extend an_item and remove it again after it is called. do extend (an_item) prune_when_called (an_item) end feature -- Access name: detachable STRING_8 -- Textual description. local i: like name_internal do i := name_internal if i /= Void then Result := i.twin end ensure equal_to_name_internal: Result ~ name_internal end event_data_names: detachable ARRAY [STRING_8] obsolete "Not implemented. To be removed. [2017-05-31]" -- Textual description of each event datum. local i: like event_data_names_internal do i := event_data_names_internal if i /= Void then Result := i.deep_twin end ensure equal_to_event_data_names_internal: deep_equal (Result, event_data_names_internal) end feature -- Status setting abort -- Abort the current call. -- (The current item.call will be completed.) require call_is_underway: call_is_underway do is_aborted_stack.replace (True) ensure is_aborted_set: is_aborted_stack.item end block -- Ignore subsequent calls. do state := Blocked_state ensure blocked_state: state = Blocked_state end pause -- Buffer subsequent calls for later execution. -- If is_blocked calls will simply be ignored. do state := Paused_state ensure paused_state: state = Paused_state end resume -- Used after block or pause to resume normal call -- execution. Executes any buffered calls. local l_call_buffer: like call_buffer do state := Normal_state from l_call_buffer := call_buffer until l_call_buffer.is_empty loop call (l_call_buffer.item); l_call_buffer.remove end ensure normal_state: state = Normal_state end flush -- Discard any buffered calls. do call_buffer.wipe_out ensure call_buffer_empty: call_buffer.is_empty end feature -- Status report state: INTEGER_32 -- One of Normal_state Paused_state or Blocked_state Normal_state: INTEGER_32 = 1 Paused_state: INTEGER_32 = 2 Blocked_state: INTEGER_32 = 3 call_is_underway: BOOLEAN -- Is call currently being executed? do Result := not is_aborted_stack.is_empty ensure Result = not is_aborted_stack.is_empty end feature -- Removal prune (v: like item) -- Remove first occurrence of v, if any, -- after cursor position. -- Move cursor to right neighbor. -- (or after if no right neighbor or v does not occur) local l_compare_objects: BOOLEAN l_kamikazes: like kamikazes_internal do Precursor (v) l_kamikazes := kamikazes_internal if l_kamikazes /= Void then if object_comparison then l_compare_objects := l_kamikazes.object_comparison; l_kamikazes.compare_objects; l_kamikazes.start; l_kamikazes.prune (v) if not l_compare_objects then l_kamikazes.compare_references end else l_kamikazes.prune (v) end end end prune_when_called (an_action: like item) -- Remove an_action after the next time it is called. require has (an_action) do kamikazes.extend (an_action) end feature -- Element status has_kamikaze_action (an_action: like item): BOOLEAN -- Return True is an_action is found and will be pruned when called. require has (an_action) do Result := has (an_action) and then kamikazes.has (an_action) end feature -- Event handling not_empty_actions: ARRAYED_LIST [PROCEDURE] -- Actions to be performed on transition from is_empty to not is_empty. do Result := not_empty_actions_internal if Result = Void then create Result.make (0) not_empty_actions_internal := Result end end empty_actions: ARRAYED_LIST [PROCEDURE] -- Actions to be performed on transition from not is_empty to is_empty. do Result := empty_actions_internal if Result = Void then create Result.make (0) empty_actions_internal := Result end end feature -- Obsolete make obsolete "Use `default_create`. [2017-05-31]" do default_create end set_source_connection_agent (a_source_connection_agent: PROCEDURE) obsolete "Use `not_empty_actions`. [2017-05-31]" -- Set a_source_connection_agent that will connect sequence to an -- actual event source. The agent will be called when the first action is -- added to the sequence. If there are already actions in the -- sequence the agent is called immediately. do not_empty_actions.extend (a_source_connection_agent) if not is_empty then a_source_connection_agent.call (Void) end end feature {NONE} -- Implementation call_action_list (actions: ARRAYED_LIST [PROCEDURE]) -- Call each action in actions. require actions_not_void: actions /= Void local snapshot: like actions i: INTEGER_32 do if not actions.is_empty then snapshot := actions.twin from i := 1 until i > snapshot.count loop if snapshot @ i /= Void then snapshot.i_th (i).call (Void) end i := i + 1 end end end is_aborted_stack: LINKED_STACK [BOOLEAN] -- item holds abort status of -- innermost of possibly recursive calls. do Result := is_aborted_stack_internal if Result = Void then create Result.make is_aborted_stack_internal := Result end end is_aborted_stack_internal: detachable like is_aborted_stack -- Internal storage for is_aborted_stack. call_buffer: LINKED_QUEUE [detachable EVENT_DATA] -- Holds calls made while is_paused -- to be executed on resume. do Result := call_buffer_internal if Result = Void then create Result.make call_buffer_internal := Result end end call_buffer_internal: detachable like call_buffer -- Internal storage for call_buffer. name_internal: detachable STRING_8 -- See name. event_data_names_internal: detachable ARRAY [STRING_8] -- See event_data_names. dummy_event_data_internal: detachable EVENT_DATA -- See dummy_event_data. kamikazes: ARRAYED_LIST [like item] -- Used by prune_when_called. do Result := kamikazes_internal if Result = Void then create Result.make (0) kamikazes_internal := Result end end kamikazes_internal: detachable like kamikazes -- Internal storage for kamikazes. not_empty_actions_internal: detachable like not_empty_actions -- Internal storage for not_empty_actions. empty_actions_internal: detachable like empty_actions -- Internal storage for empty_actions. new_filled_list (n: INTEGER_32): like Current obsolete "Use explicit creation instead. See also explanations for `duplicate`. [2018-11-30]" -- New list with n elements. do create Result.make_filled (n) end invariant is_aborted_stack_not_void: is_aborted_stack /= Void call_buffer_not_void: call_buffer /= Void valid_state: state = Normal_state or state = Paused_state or state = Blocked_state call_buffer_consistent: state = Normal_state implies call_buffer.is_empty not_empty_actions_not_void: not_empty_actions /= Void empty_actions_not_void: empty_actions /= Void note library: "EiffelBase: Library of reusable components for Eiffel." copyright: "Copyright (c) 1984-2018, 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 ACTION_SEQUENCE
Generated by ISE EiffelStudio