/// <summary>
        /// Creates a strategy implementation that indicates to subscribers
        /// the statement results based on the select-clause columns.
        /// </summary>
        /// <param name="statement">The statement.</param>
        /// <param name="subscriber">to indicate to</param>
        /// <param name="selectClauseTypes">are the types of each column in the select clause</param>
        /// <param name="selectClauseColumns">the names of each column in the select clause</param>
        /// <param name="engineURI">The engine URI.</param>
        /// <param name="engineImportService">The engine import service.</param>
        /// <returns>
        /// strategy for dispatching naturals
        /// </returns>
        /// <exception cref="EPSubscriberException">
        /// </exception>
        /// <throws>EPSubscriberException if the subscriber is invalid</throws>
        public static ResultDeliveryStrategy Create(
            EPStatement statement,
            EPSubscriber subscriber,
            Type[] selectClauseTypes,
            string[] selectClauseColumns,
            string engineURI,
            EngineImportService engineImportService)
        {
            var subscriberObject = subscriber.Subscriber;
            var subscriberMethod = subscriber.SubscriberMethod;

            if (selectClauseTypes == null)
            {
                selectClauseTypes   = new Type[0];
                selectClauseColumns = new string[0];
            }

            var subscriberType = subscriberObject.GetType();

            if (subscriberMethod == null)
            {
                if (subscriberType.IsDelegate())
                {
                    subscriberMethod = "Invoke";
                }
                else
                {
                    subscriberMethod = "Update";
                }
            }

            // Locate Update methods
            MethodInfo subscriptionMethod = null;

            var updateMethods = subscriberType
                                .GetMethods()
                                .Where(method => (method.Name == subscriberMethod) && (method.IsPublic))
                                .OrderBy(method => IsFirstParameterEPStatement(method) ? 0 : 1)
                                .ToDictionary(method => method, GetMethodParameterTypesWithoutEPStatement);

            // none found
            if (updateMethods.Count == 0)
            {
                var message = "Subscriber object does not provide a public method by name '" + subscriberMethod + "'";
                throw new EPSubscriberException(message);
            }

            // match to parameters
            var isMapArrayDelivery    = false;
            var isObjectArrayDelivery = false;
            var isSingleRowMap        = false;
            var isSingleRowObjectArr  = false;
            var isTypeArrayDelivery   = false;

            // find an exact-matching method: no conversions and not even unboxing/boxing
            foreach (var methodNormParameterEntry in updateMethods)
            {
                var normalized = methodNormParameterEntry.Value;
                if (normalized.Length == selectClauseTypes.Length)
                {
                    var fits = true;
                    for (var i = 0; i < normalized.Length; i++)
                    {
                        if ((selectClauseTypes[i] != null) && (selectClauseTypes[i] != normalized[i]))
                        {
                            fits = false;
                            break;
                        }
                    }
                    if (fits)
                    {
                        subscriptionMethod = methodNormParameterEntry.Key;
                        break;
                    }
                }
            }

            // when not yet resolved, find an exact-matching method with boxing/unboxing
            if (subscriptionMethod == null)
            {
                foreach (var methodNormParameterEntry in updateMethods)
                {
                    var normalized = methodNormParameterEntry.Value;
                    if (normalized.Length == selectClauseTypes.Length)
                    {
                        var fits = true;
                        for (var i = 0; i < normalized.Length; i++)
                        {
                            var boxedExpressionType = selectClauseTypes[i].GetBoxedType();
                            var boxedParameterType  = normalized[i].GetBoxedType();
                            if ((boxedExpressionType != null) && (boxedExpressionType != boxedParameterType))
                            {
                                fits = false;
                                break;
                            }
                        }
                        if (fits)
                        {
                            subscriptionMethod = methodNormParameterEntry.Key;
                            break;
                        }
                    }
                }
            }

            // when not yet resolved, find assignment-compatible methods that may require widening (including Integer to Long etc.)
            var checkWidening = false;

            if (subscriptionMethod == null)
            {
                foreach (var methodNormParameterEntry in updateMethods)
                {
                    var normalized = methodNormParameterEntry.Value;
                    if (normalized.Length == selectClauseTypes.Length)
                    {
                        var fits = true;
                        for (var i = 0; i < normalized.Length; i++)
                        {
                            var parameterType       = normalized[i];
                            var selectClauseType    = selectClauseTypes[i];
                            var boxedExpressionType = selectClauseType.GetBoxedType();
                            var boxedParameterType  = parameterType.GetBoxedType();

                            if (((selectClauseType == null) || (!selectClauseType.IsAssignmentCompatible(parameterType))) &&
                                ((boxedExpressionType == null) || (!boxedExpressionType.IsAssignmentCompatible(boxedParameterType))))
                            {
                                fits = false;
                                break;
                            }
                        }
                        if (fits)
                        {
                            subscriptionMethod = methodNormParameterEntry.Key;
                            checkWidening      = true;
                            break;
                        }
                    }
                }
            }

            // when not yet resolved, find first-fit wildcard method
            if (subscriptionMethod == null)
            {
                foreach (var methodNormParameterEntry in updateMethods)
                {
                    var normalized = methodNormParameterEntry.Value;
                    if ((normalized.Length == 1) && (normalized[0] == typeof(DataMap)))
                    {
                        isSingleRowMap     = true;
                        subscriptionMethod = methodNormParameterEntry.Key;
                        break;
                    }
                    if ((normalized.Length == 1) && (normalized[0] == typeof(object[])))
                    {
                        isSingleRowObjectArr = true;
                        subscriptionMethod   = methodNormParameterEntry.Key;
                        break;
                    }

                    if ((normalized.Length == 2) && (normalized[0] == typeof(DataMap[])) &&
                        (normalized[1] == typeof(DataMap[])))
                    {
                        subscriptionMethod = methodNormParameterEntry.Key;
                        isMapArrayDelivery = true;
                        break;
                    }
                    if ((normalized.Length == 2) && (normalized[0] == typeof(object[][])) &&
                        (normalized[1] == typeof(object[][])))
                    {
                        subscriptionMethod    = methodNormParameterEntry.Key;
                        isObjectArrayDelivery = true;
                        break;
                    }
                    // Handle uniform underlying or column type array dispatch
                    if ((normalized.Length == 2) && (normalized[0].Equals(normalized[1])) && (normalized[0].IsArray) &&
                        (selectClauseTypes.Length == 1))
                    {
                        var componentType = normalized[0].GetElementType();
                        if (selectClauseTypes[0].IsAssignmentCompatible(componentType))
                        {
                            subscriptionMethod  = methodNormParameterEntry.Key;
                            isTypeArrayDelivery = true;
                            break;
                        }
                    }

                    if ((normalized.Length == 0) && (selectClauseTypes.Length == 1) && (selectClauseTypes[0] == null))
                    {
                        subscriptionMethod = methodNormParameterEntry.Key;
                    }
                }
            }

            if (subscriptionMethod == null)
            {
                if (updateMethods.Count > 1)
                {
                    var parametersDesc = TypeHelper.GetParameterAsString(selectClauseTypes);
                    var message        =
                        "No suitable subscriber method named 'Update' found, expecting a method that takes " +
                        selectClauseTypes.Length + " parameter of type " + parametersDesc;
                    throw new EPSubscriberException(message);
                }
                else
                {
                    var firstUpdateMethod        = updateMethods.First();
                    var parametersNormalized     = firstUpdateMethod.Value;
                    var parametersDescNormalized = TypeHelper.GetParameterAsString(selectClauseTypes);
                    if (parametersNormalized.Length != selectClauseTypes.Length)
                    {
                        if (selectClauseTypes.Length > 0)
                        {
                            var message =
                                "No suitable subscriber method named 'Update' found, expecting a method that takes " +
                                selectClauseTypes.Length + " parameter of type " + parametersDescNormalized;
                            throw new EPSubscriberException(message);
                        }
                        else
                        {
                            var message =
                                "No suitable subscriber method named 'Update' found, expecting a method that takes no parameters";
                            throw new EPSubscriberException(message);
                        }
                    }
                    for (var i = 0; i < parametersNormalized.Length; i++)
                    {
                        var boxedExpressionType = selectClauseTypes[i].GetBoxedType();
                        var boxedParameterType  = parametersNormalized[i].GetBoxedType();
                        if ((boxedExpressionType != null) &&
                            (!boxedExpressionType.IsAssignmentCompatible(boxedParameterType)))
                        {
                            var message = "Subscriber method named 'Update' for parameter number " + (i + 1) +
                                          " is not assignable, " +
                                          "expecting type '" + selectClauseTypes[i].GetParameterAsString() +
                                          "' but found type '"
                                          + parametersNormalized[i].GetParameterAsString() + "'";
                            throw new EPSubscriberException(message);
                        }
                    }
                }
            }

            var parameterTypes = subscriptionMethod.GetParameterTypes();

            // Invalid if there is a another footprint for the subscription method that does not include EPStatement if present
            var firstParameterIsEPStatement = IsFirstParameterEPStatement(subscriptionMethod);

            if (isMapArrayDelivery)
            {
                return(firstParameterIsEPStatement
                    ? new ResultDeliveryStrategyMapWStmt(statement, subscriberObject, subscriptionMethod, selectClauseColumns, engineImportService)
                    : new ResultDeliveryStrategyMap(statement, subscriberObject, subscriptionMethod, selectClauseColumns, engineImportService));
            }
            else if (isObjectArrayDelivery)
            {
                return(firstParameterIsEPStatement
                    ? new ResultDeliveryStrategyObjectArrWStmt(statement, subscriberObject, subscriptionMethod, engineImportService)
                    : new ResultDeliveryStrategyObjectArr(statement, subscriberObject, subscriptionMethod, engineImportService));
            }
            else if (isTypeArrayDelivery)
            {
                return(firstParameterIsEPStatement
                    ? new ResultDeliveryStrategyTypeArrWStmt(statement, subscriberObject, subscriptionMethod, parameterTypes[1].GetElementType(), engineImportService)
                    : new ResultDeliveryStrategyTypeArr(statement, subscriberObject, subscriptionMethod, parameterTypes[0].GetElementType(), engineImportService));
            }

            // Try to find the "start", "end" and "updateRStream" methods
            MethodInfo startMethod   = null;
            MethodInfo endMethod     = null;
            MethodInfo rStreamMethod = null;

            startMethod = subscriberObject.GetType().GetMethod("UpdateStart", new Type[] { typeof(EPStatement), typeof(int), typeof(int) });
            if (startMethod == null)
            {
                startMethod = subscriberObject.GetType().GetMethod("UpdateStart", new Type[] { typeof(int), typeof(int) });
            }

            endMethod = subscriberObject.GetType().GetMethod("UpdateEnd", new Type[] { typeof(EPStatement) });
            if (endMethod == null)
            {
                endMethod = subscriberObject.GetType().GetMethod("UpdateEnd");
            }

            // must be exactly the same footprint (may include EPStatement), since delivery convertor used for both
            rStreamMethod = subscriberObject.GetType().GetMethod("UpdateRStream", parameterTypes);
            if (rStreamMethod == null)
            {
                // we don't have an "updateRStream" expected, make sure there isn't one with/without EPStatement
                if (IsFirstParameterEPStatement(subscriptionMethod))
                {
                    var classes = updateMethods.Get(subscriptionMethod);
                    ValidateNonMatchUpdateRStream(subscriberObject, classes);
                }
                else
                {
                    var classes = new Type[parameterTypes.Length + 1];
                    classes[0] = typeof(EPStatement);
                    Array.Copy(parameterTypes, 0, classes, 1, parameterTypes.Length);
                    ValidateNonMatchUpdateRStream(subscriberObject, classes);
                }
            }

            DeliveryConvertor convertor;

            if (isSingleRowMap)
            {
                convertor = firstParameterIsEPStatement
                    ? (DeliveryConvertor) new DeliveryConvertorMapWStatement(selectClauseColumns, statement)
                    : (DeliveryConvertor) new DeliveryConvertorMap(selectClauseColumns);
            }
            else if (isSingleRowObjectArr)
            {
                convertor = firstParameterIsEPStatement
                    ? (DeliveryConvertor) new DeliveryConvertorObjectArrWStatement(statement)
                    : (DeliveryConvertor)DeliveryConvertorObjectArr.INSTANCE;
            }
            else
            {
                if (checkWidening)
                {
                    var normalizedParameters = updateMethods.Get(subscriptionMethod);
                    convertor = DetermineWideningDeliveryConvertor(
                        firstParameterIsEPStatement, statement, selectClauseTypes, normalizedParameters,
                        subscriptionMethod, engineURI);
                }
                else
                {
                    convertor = firstParameterIsEPStatement
                        ? (DeliveryConvertor) new DeliveryConvertorNullWStatement(statement)
                        : (DeliveryConvertor)DeliveryConvertorNull.INSTANCE;
                }
            }

            return(new ResultDeliveryStrategyImpl(
                       statement, subscriberObject, convertor, subscriptionMethod, startMethod, endMethod, rStreamMethod, engineImportService));
        }
예제 #2
0
        /// <summary>
        /// Creates a strategy implementation that indicates to subscribers the statement results based on the select-clause columns.
        /// </summary>
        /// <param name="statementName">Name of the statement.</param>
        /// <param name="subscriber">to indicate to</param>
        /// <param name="selectClauseTypes">are the types of each column in the select clause</param>
        /// <param name="selectClauseColumns">the names of each column in the select clause</param>
        /// <returns>strategy for dispatching naturals</returns>
        /// <throws>EPSubscriberException if the subscriber is invalid</throws>
        public static ResultDeliveryStrategy Create(
            string statementName,
            EPSubscriber subscriber,
            Type[] selectClauseTypes,
            string[] selectClauseColumns)
        {
            var subscriberObject = subscriber.Subscriber;
            var subscriberMethod = subscriber.SubscriberMethod;

            if (selectClauseTypes == null)
            {
                selectClauseTypes   = new Type[0];
                selectClauseColumns = new String[0];
            }

            var subscriberType = subscriberObject.GetType();

            if (subscriberMethod == null)
            {
                if (subscriberType.IsDelegate())
                {
                    subscriberMethod = "Invoke";
                }
                else
                {
                    subscriberMethod = "Update";
                }
            }

            // Locate Update methods
            MethodInfo        subscriptionMethod = null;
            List <MethodInfo> updateMethods;

            updateMethods = subscriberType
                            .GetMethods()
                            .Where(method => (method.Name == subscriberMethod) && (method.IsPublic))
                            .ToList();

            // none found
            if (updateMethods.Count == 0)
            {
                String message = "EPSubscriber object does not provide a public method by name '" + subscriberMethod + "'";
                throw new EPSubscriberException(message);
            }

            // match to parameters
            bool isMapArrayDelivery    = false;
            bool isObjectArrayDelivery = false;
            bool isSingleRowMap        = false;
            bool isSingleRowObjectArr  = false;
            bool isTypeArrayDelivery   = false;

            // find an exact-matching method: no conversions and not even unboxing/boxing
            foreach (MethodInfo method in updateMethods)
            {
                Type[] parameters = method.GetParameterTypes();
                if (parameters.Length == selectClauseTypes.Length)
                {
                    bool fits = true;
                    for (int i = 0; i < parameters.Length; i++)
                    {
                        if ((selectClauseTypes[i] != null) && (selectClauseTypes[i] != parameters[i]))
                        {
                            fits = false;
                            break;
                        }
                    }
                    if (fits)
                    {
                        subscriptionMethod = method;
                        break;
                    }
                }
            }

            // when not yet resolved, find an exact-matching method with boxing/unboxing
            if (subscriptionMethod == null)
            {
                foreach (MethodInfo method in updateMethods)
                {
                    Type[] parameters = method.GetParameterTypes();
                    if (parameters.Length == selectClauseTypes.Length)
                    {
                        bool fits = true;
                        for (int i = 0; i < parameters.Length; i++)
                        {
                            Type boxedExpressionType = selectClauseTypes[i].GetBoxedType();
                            Type boxedParameterType  = parameters[i].GetBoxedType();
                            if ((boxedExpressionType != null) && (boxedExpressionType != boxedParameterType))
                            {
                                fits = false;
                                break;
                            }
                        }
                        if (fits)
                        {
                            subscriptionMethod = method;
                            break;
                        }
                    }
                }
            }

            // when not yet resolved, find assignment-compatible methods that may require widening (including Integer to Long etc.)
            bool checkWidening = false;

            if (subscriptionMethod == null)
            {
                foreach (MethodInfo method in updateMethods)
                {
                    Type[] parameters = method.GetParameterTypes();
                    if (parameters.Length == selectClauseTypes.Length)
                    {
                        bool fits = true;
                        for (int i = 0; i < parameters.Length; i++)
                        {
                            Type boxedExpressionType = selectClauseTypes[i].GetBoxedType();
                            Type boxedParameterType  = parameters[i].GetBoxedType();
                            if ((boxedExpressionType != null) &&
                                (!boxedExpressionType.IsAssignmentCompatible(boxedParameterType)))
                            {
                                fits = false;
                                break;
                            }
                        }
                        if (fits)
                        {
                            subscriptionMethod = method;
                            checkWidening      = true;
                            break;
                        }
                    }
                }
            }

            // when not yet resolved, find first-fit wildcard method
            if (subscriptionMethod == null)
            {
                foreach (MethodInfo method in updateMethods)
                {
                    Type[] parameters = method.GetParameterTypes();
                    if ((parameters.Length == 1) && (parameters[0] == typeof(DataMap)))
                    {
                        isSingleRowMap     = true;
                        subscriptionMethod = method;
                        break;
                    }
                    if ((parameters.Length == 1) && (parameters[0] == typeof(Object[])))
                    {
                        isSingleRowObjectArr = true;
                        subscriptionMethod   = method;
                        break;
                    }

                    if ((parameters.Length == 2) && (parameters[0] == typeof(DataMap[])) &&
                        (parameters[1] == typeof(DataMap[])))
                    {
                        subscriptionMethod = method;
                        isMapArrayDelivery = true;
                        break;
                    }
                    if ((parameters.Length == 2) && (parameters[0] == typeof(Object[][])) &&
                        (parameters[1] == typeof(Object[][])))
                    {
                        subscriptionMethod    = method;
                        isObjectArrayDelivery = true;
                        break;
                    }
                    // Handle uniform underlying or column type array dispatch
                    if ((parameters.Length == 2) && (parameters[0].Equals(parameters[1])) && (parameters[0].IsArray) &&
                        (selectClauseTypes.Length == 1))
                    {
                        Type componentType = parameters[0].GetElementType();
                        if (selectClauseTypes[0].IsAssignmentCompatible(componentType))
                        {
                            subscriptionMethod  = method;
                            isTypeArrayDelivery = true;
                            break;
                        }
                    }

                    if ((parameters.Length == 0) && (selectClauseTypes.Length == 1) && (selectClauseTypes[0] == null))
                    {
                        subscriptionMethod = method;
                    }
                }
            }

            if (subscriptionMethod == null)
            {
                if (updateMethods.Count > 1)
                {
                    String parametersDesc = TypeHelper.GetParameterAsString(selectClauseTypes);
                    String message        =
                        "No suitable subscriber method named 'Update' found, expecting a method that takes " +
                        selectClauseTypes.Length + " parameter of type " + parametersDesc;
                    throw new EPSubscriberException(message);
                }
                else
                {
                    Type[] parameters     = updateMethods[0].GetParameterTypes();
                    String parametersDesc = TypeHelper.GetParameterAsString(selectClauseTypes);
                    if (parameters.Length != selectClauseTypes.Length)
                    {
                        if (selectClauseTypes.Length > 0)
                        {
                            String message =
                                "No suitable subscriber method named 'Update' found, expecting a method that takes " +
                                selectClauseTypes.Length + " parameter of type " + parametersDesc;
                            throw new EPSubscriberException(message);
                        }
                        else
                        {
                            String message =
                                "No suitable subscriber method named 'Update' found, expecting a method that takes no parameters";
                            throw new EPSubscriberException(message);
                        }
                    }
                    for (int i = 0; i < parameters.Length; i++)
                    {
                        Type boxedExpressionType = selectClauseTypes[i].GetBoxedType();
                        Type boxedParameterType  = parameters[i].GetBoxedType();
                        if ((boxedExpressionType != null) &&
                            (!boxedExpressionType.IsAssignmentCompatible(boxedParameterType)))
                        {
                            String message = "EPSubscriber method named 'Update' for parameter number " + (i + 1) +
                                             " is not assignable, " +
                                             "expecting type '" + selectClauseTypes[i].GetParameterAsString() +
                                             "' but found type '"
                                             + parameters[i].GetParameterAsString() + "'";
                            throw new EPSubscriberException(message);
                        }
                    }
                }
            }

            if (isMapArrayDelivery)
            {
                return(new ResultDeliveryStrategyMap(statementName, subscriberObject, subscriptionMethod, selectClauseColumns));
            }
            else if (isObjectArrayDelivery)
            {
                return(new ResultDeliveryStrategyObjectArr(statementName, subscriberObject, subscriptionMethod));
            }
            else if (isTypeArrayDelivery)
            {
                return(new ResultDeliveryStrategyTypeArr(statementName, subscriberObject, subscriptionMethod));
            }

            // Try to find the "start", "end" and "updateRStream" methods
            MethodInfo startMethod = subscriberObject.GetType().GetMethod(
                "UpdateStart", new Type[]
            {
                typeof(int), typeof(int)
            });
            MethodInfo endMethod = subscriberObject.GetType().GetMethod(
                "UpdateEnd");
            MethodInfo rStreamMethod = subscriberObject.GetType().GetMethod(
                "UpdateRStream", subscriptionMethod.GetParameterTypes());

            DeliveryConvertor convertor;

            if (isSingleRowMap)
            {
                convertor = new DeliveryConvertorMap(selectClauseColumns);
            }
            else if (isSingleRowObjectArr)
            {
                convertor = new DeliveryConvertorObjectArr();
            }
            else
            {
                if (checkWidening)
                {
                    convertor = DetermineWideningDeliveryConvertor(
                        selectClauseTypes, subscriptionMethod.GetParameterTypes(), subscriptionMethod);
                }
                else
                {
                    convertor = DeliveryConvertorNull.INSTANCE;
                }
            }

            return(new ResultDeliveryStrategyImpl(
                       statementName, subscriberObject, convertor, subscriptionMethod, startMethod, endMethod, rStreamMethod));
        }