Files
EWF/library/utility/general/error/src/error_handler.e
Jocelyn Fiat 3bb9101b07 Improved error library by refactorying the sync as two way propagation.
Now one can setup error handler propagation in one way, or two way (sync).
The "reset" applies in both way, even if this is a one way propagation to fit current existing usage.
Added optional id for the error handlers.
Feature renaming according to design changes.
Added related autotest cases.
2016-01-12 16:09:29 +01:00

529 lines
11 KiB
Plaintext

note
description : "[
Error handler or receiver.
]"
legal: "See notice at end of class."
status: "See notice at end of class."
date: "$Date: 2015-10-10 00:55:41 +0200 (sam., 10 oct. 2015) $"
revision: "$Revision: 97980 $"
class
ERROR_HANDLER
inherit
ANY
DEBUG_OUTPUT
create
make,
make_with_id
feature {NONE} -- Initialization
make
-- Initialize `Current'.
do
create {ARRAYED_LIST [ERROR]} errors.make (3)
create error_added_actions
end
make_with_id (a_id: READABLE_STRING_8)
-- Build `Current' with optional id `a_id'.
do
make
if a_id = Void then
id := Void
else
create id.make_from_string (a_id)
end
end
feature -- Access
id: detachable IMMUTABLE_STRING_8
-- Optional identifier for Current handler.
primary_error_code: INTEGER
-- Code of first error in `errors'
require
at_least_one_error: has_error
do
Result := errors.first.code
end
feature -- Status
has_error: BOOLEAN
-- Has error?
do
Result := count > 0
end
count: INTEGER
-- Number of error
do
Result := errors.count
end
is_synchronizing_with (other: ERROR_HANDLER): BOOLEAN
-- Is Current synchronizing with `other'?
-- i.e 2 way propagation.
do
Result := is_propagating_to (other) and other.is_propagating_to (Current)
end
is_propagating_to (other: ERROR_HANDLER): BOOLEAN
-- Is Current propagating error to `other'?
do
Result := attached propagations as lst and then lst.has (other)
end
feature {ERROR_HANDLER, ERROR_VISITOR} -- Restricted access
errors: LIST [ERROR]
-- Errors container
feature -- Status report
debug_output: STRING
-- String that should be displayed in debugger to represent `Current'.
do
if has_error then
Result := count.out + " errors"
else
Result := "no error"
end
if attached id as l_id then
Result.prepend ("[" + l_id + "] ")
end
if attached propagations as lst then
check not_empty: not lst.is_empty end
Result.append_character ('(')
Result.append (" -> ")
Result.append_integer (lst.count)
Result.append_character (':')
across
lst as ic
loop
if attached ic.item.id as l_id then
Result.append_character (' ')
Result.append (l_id)
end
end
Result.append_character (')')
end
end
feature -- Events
error_added_actions: ACTION_SEQUENCE [TUPLE [ERROR]]
-- Actions triggered when a new error is added
feature -- Synchronization
add_synchronization (h: ERROR_HANDLER)
-- Add synchronization between `h' and `Current'
--| the same handler can be added more than once
--| it will be synchronized only once
do
add_propagation (h)
h.add_propagation (Current)
end
remove_synchronization (h: ERROR_HANDLER)
-- Remove synchronization between `h' and `Current'
do
remove_propagation (h)
h.remove_propagation (Current)
end
feature -- One way synchronization: propagation
add_propagation (h: ERROR_HANDLER)
-- Add propagation from `Current' to `h'.
--| the same handler can be added more than once
--| it will be synchronized only once
local
lst: like propagations
do
h.register_propagator (Current)
lst := propagations
if lst = Void then
create {ARRAYED_LIST [ERROR_HANDLER]} lst.make (0)
lst.compare_references
propagations := lst
end
if not lst.has (h) then
lst.extend (h)
end
end
remove_propagation (h: ERROR_HANDLER)
-- Remove propagation from `Current' to `h'.
do
if attached propagations as lst and then not lst.is_empty then
lst.prune_all (h)
if lst.is_empty then
propagations := Void
end
end
h.unregister_propagator (Current)
end
feature {ERROR_HANDLER} -- Synchronization implementation
is_associated_with (h: ERROR_HANDLER): BOOLEAN
do
if attached propagators as lst then
Result := lst.has (h)
end
end
register_propagator (h: ERROR_HANDLER)
local
lst: like propagators
do
lst := propagators
if lst = Void then
create {ARRAYED_LIST [ERROR_HANDLER]} lst.make (1)
propagators := lst
end
if not lst.has (h) then
lst.extend (h)
end
end
unregister_propagator (h: ERROR_HANDLER)
local
lst: like propagators
do
lst := propagators
if lst /= Void then
lst.prune_all (h)
if lst.is_empty then
propagators := Void
end
end
end
propagators: detachable LIST [ERROR_HANDLER]
-- Handlers propagating to Current.
-- Needed for `reset'.
propagations: detachable LIST [ERROR_HANDLER]
-- Handlers receiving the propagation.
propagate_error_addition (e: ERROR; h_lst: LIST [ERROR_HANDLER])
-- Called by error_handler during synchronization process.
-- To prevent infinite cycle, if Currently synchronizing, the `propagations' is Void.
require
not h_lst.has (Current)
local
lst: like propagations
do
h_lst.extend (Current)
lst := propagations
propagations := Void
add_error (e)
if lst /= Void then
across
lst as c
loop
if not h_lst.has (c.item) then
c.item.propagate_error_addition (e, h_lst)
end
end
propagations := lst
else
--| propagating
end
end
propagate_errors_removal (errs: ITERABLE [ERROR]; h_lst: LIST [ERROR_HANDLER])
-- Called by error_handler during synchronization process.
-- To prevent infinite cycle, if Currently synchronizing, the `propagations' is Void.
require
not h_lst.has (Current)
local
lst: like propagations
do
h_lst.extend (Current)
lst := propagations
propagations := Void
across
errs as ic
loop
-- Question: should we use remove_error (ic.item) ?
errors.prune_all (ic.item)
end
if lst /= Void then
across
lst as c
loop
if not h_lst.has (c.item) then
c.item.propagate_errors_removal (errs, h_lst)
end
end
propagations := lst
else
--| propagating
end
end
backpropagate_reset (h_lst: LIST [ERROR_HANDLER])
-- Called by error_handler during propagation process.
-- To prevent infinite cycle, if Currently synchronizing, the `propagations' is Void.
require
not h_lst.has (Current)
local
lst: like propagations
do
h_lst.extend (Current)
lst := propagations
propagations := Void
reset
if lst /= Void then
across
lst as c
loop
if not h_lst.has (c.item) then
-- Reset c.item, even if this is not a two way synchronization!
c.item.backpropagate_reset (h_lst)
end
end
propagations := lst
else
--| propagating
end
end
feature {NONE} -- Event: implementation
on_error_added (e: ERROR)
-- Error `e' was just added.
local
sync_list: ARRAYED_LIST [ERROR_HANDLER]
do
error_added_actions.call ([e])
if attached propagations as lst then
propagations := Void
create sync_list.make (1 + lst.count)
sync_list.extend (Current)
across
lst as c
loop
if not sync_list.has (c.item) then
c.item.propagate_error_addition (e, sync_list)
end
end
propagations := lst
end
end
on_errors_removed (errs: ITERABLE [ERROR])
-- Errors `errs' were just removed.
local
sync_list: ARRAYED_LIST [ERROR_HANDLER]
lst: like propagations
do
lst := propagations
if lst /= Void then
propagations := Void
create sync_list.make (1 + lst.count)
sync_list.extend (Current)
across
lst as c
loop
if not sync_list.has (c.item) then
c.item.propagate_errors_removal (errs, sync_list)
end
end
propagations := lst
end
end
on_reset
-- `reset' was just called
local
sync_list: detachable ARRAYED_LIST [ERROR_HANDLER]
lst: detachable LIST [ERROR_HANDLER]
do
lst := propagators
if lst /= Void then
create sync_list.make (1 + lst.count)
sync_list.extend (Current)
propagators := Void
across
lst as c
loop
if not sync_list.has (c.item) then
c.item.backpropagate_reset (sync_list)
end
end
propagators := lst
end
-- lst := propagations
-- propagations := Void
-- if lst /= Void then
-- if sync_list = Void then
-- create sync_list.make (1 + lst.count)
-- sync_list.extend (Current)
-- end
-- across
-- lst as c
-- loop
-- if not sync_list.has (c.item) then
-- c.item.synchronize_reset_from (sync_list)
-- end
-- end
-- propagations := lst
-- end
end
feature -- Basic operation
add_error (a_error: ERROR)
-- Add `a_error' to the stack of error
do
errors.force (a_error)
on_error_added (a_error)
end
remove_error (a_error: ERROR)
-- Remove `a_error' from the stack of error.
-- And also propagate error removal.
do
if propagations /= Void then
on_errors_removed (<<a_error>>)
end
errors.prune_all (a_error)
end
add_error_details, add_custom_error (a_code: INTEGER; a_name: STRING; a_message: detachable READABLE_STRING_GENERAL)
-- Add custom error to the stack of error
local
e: ERROR_CUSTOM
do
create e.make (a_code, a_name, a_message)
add_error (e)
end
append (other: ERROR_HANDLER)
-- Append errors from `a_err_handler'
local
other_errs: LIST [ERROR]
do
other_errs := other.errors
if other_errs.count > 0 then
from
other_errs.start
until
other_errs.after
loop
add_error (other_errs.item)
other_errs.forth
end
end
ensure
other_error_appended: other.has_error implies has_error
new_count: count = old count + other.count
end
feature -- Access
as_single_error: detachable ERROR
-- All error(s) concatenated into one single error.
do
if count > 1 then
create {ERROR_GROUP} Result.make (errors)
elseif count > 0 then
Result := errors.first
end
ensure
has_error_implies_result_attached: has_error implies Result /= Void
end
as_string_representation: STRING_32
-- String representation of all error(s).
require
has_error
do
if attached as_single_error as e then
Result := e.string_representation
else
check has_error: False end
Result := {STRING_32} "Error occured"
end
end
feature -- Element changes
concatenate
-- Concatenate into a single error if any
do
if count > 1 and then attached as_single_error as e then
reset
add_error (e)
end
end
reset
-- Reset Current error handler.
-- And also reset recursively error handlers propagating to Current (i.e the propagators).
do
errors.wipe_out
on_reset
ensure
has_no_error: not has_error
count = 0
end
remove_all_errors
-- Remove all errors.
do
if errors.count > 0 then
on_errors_removed (errors)
errors.wipe_out
end
ensure
has_no_error: not has_error
count = 0
end
destroy
-- Destroy Current, and remove any propagations (in the two directions).
do
error_added_actions.wipe_out
if attached propagations as lst then
propagations := Void
across
lst as c
loop
c.item.remove_synchronization (Current)
end
end
reset
end
invariant
propagations_not_empty: attached propagations as lst implies not lst.is_empty
propagators_not_empty: attached propagators as lst implies not lst.is_empty
note
copyright: "2011-2016, Jocelyn Fiat, 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