cosmo warning skl

function varargout = cosmo_warning(message, varargin)
    % show a warning message; by default just once for each message
    %
    % cosmo_warning(message, ...)
    % cosmo_warning(state)
    % state=cosmo_warning()
    %
    % Inputs:
    %   message      warning message to be shown, or one of:
    %                'on'   : show all warning messages
    %                'off'  : show no warning messages
    %                'once' : show each warning message once [default]
    %                'reset':
    %   ...          if a warning message is provided according with
    %                placeholders as used in sprintf, then the subsequent
    %                arguments should contain their values
    %   state        if a struct, then this queries or sets the state of
    %                cosmo_warning.
    %
    % Notes:
    %   - this function works more or less like matlab's warning function,
    %     except that by default each warning is just shown once.
    %
    % #   For CoSMoMVPA's copyright information and license terms,   #
    % #   see the COPYING file distributed with CoSMoMVPA.           #

    if isempty(get_from_state('when'))
        set_default_state();
    end

    if nargin == 0
        varargout = {get_state()};
        return
    end

    if isstruct(message)
        set_state(message);
        return
    end

    lmessage = lower(message);

    switch lmessage
        case {'on', 'off', 'once'}
            update_state('when', lmessage);
            return

        case 'reset'
            set_default_state();
            return

        otherwise
            show_warning(message, varargin{:});
    end

function show_warning(message, varargin)
    [identifier, full_message] = get_identifier_and_message( ...
                                                            message, varargin{:});

    shown_warnings = get_from_state('shown_warnings');
    has_warning = cosmo_match({full_message}, shown_warnings);
    if ~has_warning
        shown_warnings{end + 1} = full_message;
        update_state('shown_warnings', shown_warnings);
    end

    when = get_from_state('when');
    switch when
        case 'once'
            do_show_warning = ~has_warning;

            me = mfilename();
            postfix = sprintf(['\n\nThis warning is shown only once, '...
                               'but the underlying issue may occur '...
                               'multiple times. To show each warning:\n'...
                               ' - every time:   %s(''on'')\n'...
                               ' - once:         %s(''once'')\n'...
                               ' - never:        %s(''off'')\n'], me, me, me);
            full_message = [full_message postfix];

        case 'off'
            do_show_warning = false;
        case 'on'
            do_show_warning = true;
        otherwise
            assert(false);
    end

    if do_show_warning
        state = warning(); % store state
        state_resetter = onCleanup(@()warning(state));

        warning('on', 'all');
        % avoid extra entry on the stack
        has_identifier = ~isempty(identifier);

        if has_identifier
            warning(identifier, '%s', full_message);
        else
            warning('%s', full_message);
        end
    end

function [identifier, full_message] = get_identifier_and_message( ...
                                                                 message, varargin)
    args = varargin;
    has_identifier = numel(args) > 0 && has_warning_identifier(message);
    if has_identifier
        identifier = message;
        message = args{1};
        args = args(2:end);
    else
        identifier = '';
    end

    if numel(args) > 0
        full_message = sprintf(message, args{:});
    else
        full_message = message;
    end

function tf = has_warning_identifier(s)
    alpha_num = '([a-z_A-Z0-9]+)';
    pat = sprintf('^%s(:%s)?:%s$', alpha_num, alpha_num, alpha_num);
    tf = ~isempty(regexp(s, pat, 'once'));

function s = get_state()
    s = get_or_set_state();

function set_state(s)
    get_or_set_state(s);

function set_default_state()
    s = struct();
    s.when = 'once';
    s.shown_warnings = cell(0);
    set_state(s);

function value = get_from_state(key)
    s = get_state();
    value = s.(key);

function update_state(key, value)
    s = get_state();
    s.(key) = value;
    set_state(s);

function varargout = get_or_set_state(s)
    persistent state
    switch nargin
        case 0
            % get state
            if isempty(state)
                set_default_state();
            end

            varargout = {state};

        case 1
            % set state
            state = s;
            varargout = {};

        otherwise
            assert(false);
    end