<commit_before>﻿namespace Miruken.Callback.Policy
{
    using System;
    using System.Linq;

    public delegate bool AcceptResultDelegate(
        object result, MethodBinding binding);

    public delegate PolicyMethodBinding BindMethodDelegate(
        CallbackPolicy policy,
        PolicyMethodBindingInfo policyMethodBindingInfo);

    public class PolicyMethodBindingInfo
    {
        public PolicyMethodBindingInfo(
            MethodRule rule, MethodDispatch dispatch,
            DefinitionAttribute definition)
        {
            Rule          = rule;
            Dispatch      = dispatch;
            Definition    = definition;
            InKey         = definition.InKey;
            OutKey        = definition.OutKey;
            CallbackIndex = null;
        }

        public object              InKey;
        public object              OutKey;
        public int?                CallbackIndex;
        public MethodRule          Rule           { get; }
        public MethodDispatch      Dispatch       { get; }
        public DefinitionAttribute Definition     { get; }
    }

    public class PolicyMethodBinding : MethodBinding
    {
        public PolicyMethodBinding(CallbackPolicy policy,
                                   PolicyMethodBindingInfo bindingInfo)
            : base(bindingInfo.Dispatch)
        {
            Policy        = policy;
            Rule          = bindingInfo.Rule;
            Definition    = bindingInfo.Definition;
            CallbackIndex = bindingInfo.CallbackIndex;
            Key           = policy.CreateKey(bindingInfo);
        }

        public MethodRule          Rule          { get; }
        public DefinitionAttribute Definition    { get; }
        public CallbackPolicy      Policy        { get; }
        public int?                CallbackIndex { get; }
        public object              Key           { get; }

        public override bool Dispatch(object target, object callback, 
            IHandler composer, ResultsDelegate results = null)
        {
            if (Definition?.Approve(callback, this) == false)
                return false;
            object result;
            var resultType = Policy.ResultType?.Invoke(callback);
            return Invoke(target, callback, composer, resultType,
                results, out result);
        }

        public Type CloseHandlerType(Type handlerType, object key)
        {
            var type = key as Type;
            if (type == null || !handlerType.IsGenericTypeDefinition)
                return handlerType;
            var index = CallbackIndex;
            if (!index.HasValue) return null;
            var callback = Dispatcher.Arguments[index.Value];
            var mapping  = new GenericMapping(
                handlerType.GetGenericArguments(),
                new [] { callback });
            if (mapping.Complete)
            {
                var closed = mapping.MapTypes(new[] { type });
                return handlerType.MakeGenericType(closed);
            }
            return null;
        }

        private bool Invoke(object target, object callback,
            IHandler composer, Type resultType, ResultsDelegate results,
            out object result)
        {
            var args = ResolveArgs(callback, composer);
            if (args == null)
            {
                result = null;
                return false;
            }

            if ((callback as IFilterCallback)?.AllowFiltering == false)
            {
                result = Dispatcher.Invoke(target, args, resultType);
                return true;
            }

            Type callbackType;
            var dispatcher     = Dispatcher.CloseDispatch(args, resultType);
            var actualCallback = GetCallbackInfo(
                callback, args, dispatcher, out callbackType);
            var returnType     = dispatcher.ReturnType;
            var logicalType    = dispatcher.LogicalReturnType;

            var filters = composer
                .GetOrderedFilters(this, callbackType, logicalType, Filters,
                    FilterAttribute.GetFilters(target.GetType(), true), 
                    Policy.Filters)
                .ToArray();

            object baseResult = null;

            if (filters.Length == 0)
                result = baseResult = dispatcher.Invoke(target, args, resultType);
            else if (!MethodPipeline.GetPipeline(callbackType, logicalType)
                .Invoke(this, target, actualCallback, comp => 
                    baseResult = dispatcher.Invoke(
                        target, UpdateArgs(args, composer, comp),
                        resultType), composer, filters, out result))
                return false;

            var accepted = Policy.AcceptResult?.Invoke(baseResult, this)
                        ?? baseResult != null;
            if (accepted && (result != null))
            {
                var asyncCallback = callback as IAsyncCallback;
                result = CoerceResult(result, returnType, asyncCallback?.WantsAsync);
                return results?.Invoke(result, Definition.Strict) != false;
            }

            return accepted;
        }

        private object[] ResolveArgs(object callback, IHandler composer)<commit_msg>Use pipeline result if target never called<commit_after>﻿namespace Miruken.Callback.Policy
{
    using System;
    using System.Linq;

    public delegate bool AcceptResultDelegate(
        object result, MethodBinding binding);

    public delegate PolicyMethodBinding BindMethodDelegate(
        CallbackPolicy policy,
        PolicyMethodBindingInfo policyMethodBindingInfo);

    public class PolicyMethodBindingInfo
    {
        public PolicyMethodBindingInfo(
            MethodRule rule, MethodDispatch dispatch,
            DefinitionAttribute definition)
        {
            Rule          = rule;
            Dispatch      = dispatch;
            Definition    = definition;
            InKey         = definition.InKey;
            OutKey        = definition.OutKey;
            CallbackIndex = null;
        }

        public object              InKey;
        public object              OutKey;
        public int?                CallbackIndex;
        public MethodRule          Rule           { get; }
        public MethodDispatch      Dispatch       { get; }
        public DefinitionAttribute Definition     { get; }
    }

    public class PolicyMethodBinding : MethodBinding
    {
        public PolicyMethodBinding(CallbackPolicy policy,
                                   PolicyMethodBindingInfo bindingInfo)
            : base(bindingInfo.Dispatch)
        {
            Policy        = policy;
            Rule          = bindingInfo.Rule;
            Definition    = bindingInfo.Definition;
            CallbackIndex = bindingInfo.CallbackIndex;
            Key           = policy.CreateKey(bindingInfo);
        }

        public MethodRule          Rule          { get; }
        public DefinitionAttribute Definition    { get; }
        public CallbackPolicy      Policy        { get; }
        public int?                CallbackIndex { get; }
        public object              Key           { get; }

        public override bool Dispatch(object target, object callback, 
            IHandler composer, ResultsDelegate results = null)
        {
            if (Definition?.Approve(callback, this) == false)
                return false;
            object result;
            var resultType = Policy.ResultType?.Invoke(callback);
            return Invoke(target, callback, composer, resultType,
                results, out result);
        }

        public Type CloseHandlerType(Type handlerType, object key)
        {
            var type = key as Type;
            if (type == null || !handlerType.IsGenericTypeDefinition)
                return handlerType;
            var index = CallbackIndex;
            if (!index.HasValue) return null;
            var callback = Dispatcher.Arguments[index.Value];
            var mapping  = new GenericMapping(
                handlerType.GetGenericArguments(),
                new [] { callback });
            if (mapping.Complete)
            {
                var closed = mapping.MapTypes(new[] { type });
                return handlerType.MakeGenericType(closed);
            }
            return null;
        }

        private bool Invoke(object target, object callback,
            IHandler composer, Type resultType, ResultsDelegate results,
            out object result)
        {
            var args = ResolveArgs(callback, composer);
            if (args == null)
            {
                result = null;
                return false;
            }

            if ((callback as IFilterCallback)?.AllowFiltering == false)
            {
                result = Dispatcher.Invoke(target, args, resultType);
                return true;
            }

            Type callbackType;
            var dispatcher     = Dispatcher.CloseDispatch(args, resultType);
            var actualCallback = GetCallbackInfo(
                callback, args, dispatcher, out callbackType);
            var returnType     = dispatcher.ReturnType;
            var logicalType    = dispatcher.LogicalReturnType;

            var filters = composer
                .GetOrderedFilters(this, callbackType, logicalType, Filters,
                    FilterAttribute.GetFilters(target.GetType(), true), 
                    Policy.Filters)
                .ToArray();

            object baseResult = this;

            if (filters.Length == 0)
                result = baseResult = dispatcher.Invoke(target, args, resultType);
            else if (!MethodPipeline.GetPipeline(callbackType, logicalType)
                .Invoke(this, target, actualCallback, comp => 
                    baseResult = dispatcher.Invoke(
                        target, UpdateArgs(args, composer, comp),
                        resultType), composer, filters, out result))
                return false;

            var testResult = ReferenceEquals(baseResult, this) ? result : baseResult;
            var accepted   = Policy.AcceptResult?.Invoke(testResult, this)
                          ?? testResult != null;
            if (accepted && (result != null))
            {
                var asyncCallback = callback as IAsyncCallback;
                result = CoerceResult(result, returnType, asyncCallback?.WantsAsync);
                return results?.Invoke(result, Definition.Strict) != false;
            }

            return accepted;
        }

        private object[] ResolveArgs(object callback, IHandler composer)
{'fraction_non_alphanumeric': 0.0688715587710232, 'fraction_numerical': 0.0001879169407122052, 'mean_word_length': 1.553742802303263, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3744794', 'n_tokens_mistral': 2382, 'n_tokens_neox': 2267, 'n_words': 635}