Package com.espertech.esper.core.service

Source Code of com.espertech.esper.core.service.ResultDeliveryStrategyFactory

/**************************************************************************************
* Copyright (C) 2008 EsperTech, Inc. All rights reserved.                            *
* http://esper.codehaus.org                                                          *
* http://www.espertech.com                                                           *
* ---------------------------------------------------------------------------------- *
* The software in this package is published under the terms of the GPL license       *
* a copy of which has been included with this distribution in the license.txt file.  *
**************************************************************************************/
package com.espertech.esper.core.service;

import com.espertech.esper.client.EPException;
import com.espertech.esper.client.EPSubscriberException;
import com.espertech.esper.epl.expression.ExprValidationException;
import com.espertech.esper.util.JavaClassHelper;
import com.espertech.esper.util.TypeWidener;
import com.espertech.esper.util.TypeWidenerFactory;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Map;

/**
* Factory for creating a dispatch strategy based on the subscriber object
* and the columns produced by a select-clause.
*/
public class ResultDeliveryStrategyFactory
{
    /**
     * Creates a strategy implementation that indicates to subscribers
     * the statement results based on the select-clause columns.
     * @param subscriber to indicate to
     * @param selectClauseTypes are the types of each column in the select clause
     * @param selectClauseColumns the names of each column in the select clause
     * @return strategy for dispatching naturals
     * @throws EPSubscriberException if the subscriber is invalid
     */
    public static ResultDeliveryStrategy create(String statementName, Object subscriber,
                                                        Class[] selectClauseTypes,
                                                        String[] selectClauseColumns)
            throws EPSubscriberException
    {
        if (selectClauseTypes == null) {
            selectClauseTypes = new Class[0];
            selectClauseColumns = new String[0];
        }

        // Locate update methods
        Method subscriptionMethod = null;
        ArrayList<Method> updateMethods = new ArrayList<Method>();
        for (Method method : subscriber.getClass().getMethods())
        {
            if ((method.getName().equals("update")) &&
                (Modifier.isPublic(method.getModifiers())))
            {
                updateMethods.add(method);
            }
        }

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

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

        // find an exact-matching method: no conversions and not even unboxing/boxing
        for (Method method : updateMethods)
        {
            Class[] parameters = method.getParameterTypes();
            if (parameters.length == selectClauseTypes.length) {
                boolean 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) {
            for (Method method : updateMethods)
            {
                Class[] parameters = method.getParameterTypes();
                if (parameters.length == selectClauseTypes.length) {
                    boolean fits = true;
                    for (int i = 0; i < parameters.length; i++) {
                        Class boxedExpressionType = JavaClassHelper.getBoxedType(selectClauseTypes[i]);
                        Class boxedParameterType = JavaClassHelper.getBoxedType(parameters[i]);
                        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.)
        boolean checkWidening = false;
        if (subscriptionMethod == null) {
            for (Method method : updateMethods) {
                Class[] parameters = method.getParameterTypes();
                if (parameters.length == selectClauseTypes.length) {
                    boolean fits = true;
                    for (int i = 0; i < parameters.length; i++) {
                        Class boxedExpressionType = JavaClassHelper.getBoxedType(selectClauseTypes[i]);
                        Class boxedParameterType = JavaClassHelper.getBoxedType(parameters[i]);
                        if ((boxedExpressionType != null) && (!JavaClassHelper.isAssignmentCompatible(boxedExpressionType, boxedParameterType))) {
                            fits = false;
                            break;
                        }
                    }
                    if (fits) {
                        subscriptionMethod = method;
                        checkWidening = true;
                        break;
                    }
                }
            }
        }

        // when not yet resolved, find first-fit wildcard method
        if (subscriptionMethod == null) {
            for (Method method : updateMethods)
            {
                Class[] parameters = method.getParameterTypes();
                if ((parameters.length == 1) && (parameters[0] == Map.class))
                {
                    isSingleRowMap = true;
                    subscriptionMethod = method;
                    break;
                }
                if ((parameters.length == 1) && (parameters[0] == Object[].class))
                {
                    isSingleRowObjectArr = true;
                    subscriptionMethod = method;
                    break;
                }

                if ((parameters.length == 2) && (parameters[0] == Map[].class) && (parameters[1] == Map[].class))
                {
                    subscriptionMethod = method;
                    isMapArrayDelivery = true;
                    break;
                }
                if ((parameters.length == 2) && (parameters[0] == Object[][].class) && (parameters[1] == Object[][].class))
                {
                    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))
                {
                    Class componentType = parameters[0].getComponentType();
                    if (JavaClassHelper.isAssignmentCompatible(selectClauseTypes[0], componentType))
                    {
                        subscriptionMethod = method;
                        isTypeArrayDelivery = true;
                        break;
                    }
                }

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

        if (subscriptionMethod == null)
        {
            if (updateMethods.size() > 1)
            {
                String parametersDesc = JavaClassHelper.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
            {
                Class[] parameters = updateMethods.get(0).getParameterTypes();
                String parametersDesc = JavaClassHelper.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++)
                {
                    Class boxedExpressionType = JavaClassHelper.getBoxedType(selectClauseTypes[i]);
                    Class boxedParameterType = JavaClassHelper.getBoxedType(parameters[i]);
                    if ((boxedExpressionType != null) && (!JavaClassHelper.isAssignmentCompatible(boxedExpressionType, boxedParameterType)))
                    {
                        String message = "Subscriber method named 'update' for parameter number " + (i + 1) + " is not assignable, " +
                                "expecting type '" + JavaClassHelper.getParameterAsString(selectClauseTypes[i]) + "' but found type '"
                                + JavaClassHelper.getParameterAsString(parameters[i]) + "'";
                        throw new EPSubscriberException(message);
                    }
                }
            }
        }

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

        // Try to find the "start", "end" and "updateRStream" methods
        Method startMethod = null;
        Method endMethod = null;
        Method rStreamMethod = null;
        try {
            startMethod = subscriber.getClass().getMethod("updateStart", int.class, int.class);
        }
        catch (NoSuchMethodException e) {
            // expected
        }

        try {
            endMethod = subscriber.getClass().getMethod("updateEnd");
        }
        catch (NoSuchMethodException e) {
            // expected
        }

        try {
            rStreamMethod = subscriber.getClass().getMethod("updateRStream", subscriptionMethod.getParameterTypes());
        }
        catch (NoSuchMethodException e) {
            // expected
        }

        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, subscriber, convertor, subscriptionMethod, startMethod, endMethod, rStreamMethod);
    }

    private static DeliveryConvertor determineWideningDeliveryConvertor(Class[] selectClauseTypes, Class[] parameterTypes, Method method) {
        boolean needWidener = false;
        for (int i = 0; i < selectClauseTypes.length; i++) {
            TypeWidener optionalWidener = getWidener(i, selectClauseTypes[i], parameterTypes[i], method);
            if (optionalWidener != null) {
                needWidener = true;
                break;
            }
        }
        if (!needWidener) {
            return DeliveryConvertorNull.INSTANCE;
        }
        TypeWidener[] wideners = new TypeWidener[selectClauseTypes.length];
        for (int i = 0; i < selectClauseTypes.length; i++) {
            wideners[i] = getWidener(i, selectClauseTypes[i], parameterTypes[i], method);
        }
        return new DeliveryConvertorWidener(wideners);
    }

    private static TypeWidener getWidener(int columnNum, Class selectClauseType, Class parameterType, Method method) {
        if (selectClauseType == null || parameterType == null) {
            return null;
        }
        if (selectClauseType == parameterType) {
            return null;
        }
        try {
            return TypeWidenerFactory.getCheckPropertyAssignType("Select-Clause Column " + columnNum, selectClauseType, parameterType, "Method Parameter " + columnNum);
        }
        catch (ExprValidationException e) {
            throw new EPException("Unexpected exception assigning select clause columns to subscriber method " + method + ": " + e.getMessage(), e);
        }
    }


}
TOP

Related Classes of com.espertech.esper.core.service.ResultDeliveryStrategyFactory

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.