All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.tangosol.io.pof.reflect.internal.InvocationStrategies Maven / Gradle / Ivy

There is a newer version: 24.09
Show newest version
/*
 * Copyright (c) 2000, 2020, Oracle and/or its affiliates.
 *
 * Licensed under the Universal Permissive License v 1.0 as shown at
 * http://oss.oracle.com/licenses/upl.
 */
package com.tangosol.io.pof.reflect.internal;

import com.tangosol.util.Base;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

/**
 * {@link InvocationStrategies} contains two {@link InvocationStrategy}
 * implementations that abstract the underlying mechanisms to retrieve and
 * set a property's value.
 *
 * @author hr
 *
 * @since 3.7.1
 */
public class InvocationStrategies
    {

    // ----- inner class: FieldInvocationStrategy ---------------------------

    /**
     * A FieldInvocationStrategy uses a {@link Field} to dynamically invoke
     * gets and sets on the field.
     *
     * @author hr
     *
     * @since  3.7.1
     *
     * @param   containing type
     * @param    property type
     */
    public static class FieldInvocationStrategy
            implements InvocationStrategy
        {

        // ----- constructors -----------------------------------------------

        /**
         * FieldInvocationStrategy must be initialized with an appropriate
         * Field.
         *
         * @param field  the field that will be used to get and set values
         * @throws IllegalArgumentException iff {@code field} is null or a
         *         security manager is restricting access to the field
         */
        public FieldInvocationStrategy(Field field)
            {
            if (field == null)
                {
                throw new IllegalArgumentException("A non-null field must be supplied to a FieldInvocationStrategy");
                }

            m_field = field;
            if ((field.getModifiers() & Modifier.PUBLIC) != Modifier.PUBLIC)
                {
                try
                    {
                    field.setAccessible(true);
                    }
                catch (SecurityException se)
                    {
                    throw new IllegalArgumentException("A security manager has been registered "
                            + "with access to field " + field.getName() + "being rejected", se);
                    }

                }
            }

        // ----- InvocationStrategy interface -------------------------------

        /**
         * {@inheritDoc}
         */
        public T get(PT container)
            {
            try
                {
                return (T) m_field.get(container);
                }
            catch (IllegalAccessException e)
                {
                throw Base.ensureRuntimeException(e,
                        "AttributeMetadata accessor is unable to access field " + m_field.getName());
                }
            }

        /**
         * {@inheritDoc}
         */
        public void set(PT container, T value)
            {
            try
                {
                // do not attempt to set the value iff it is null and a primitive
                // as coercion of null boxed type to a primitive target produces a NPE
                if (value != null || !m_field.getType().isPrimitive())
                    {
                    m_field.set(container, value);
                    }
                }
            catch (IllegalAccessException e)
                {
                throw Base.ensureRuntimeException(e, "AttributeMetadata accessor "
                        + "is unable to set field " + m_field.getName() + " to value " + value);
                }
            }

        // ----- data members -----------------------------------------------

        /**
         * The {@link Field} this strategy will use to get and set values.
         */
        private final Field m_field;
        }

    // ----- inner class: MethodInvocationStrategy --------------------------

    /**
     * A MethodInvocationStrategy uses {@link Method}s to dynamically invoke
     * getter and setter methods to retrieve and set property values.
     *
     * @author hr
     *
     * @since  3.7.1
     *
     * @param   containing type
     * @param    property type
     */
    public static class MethodInvocationStrategy
            implements InvocationStrategy
        {

        // ----- constructors -----------------------------------------------

        /**
         * Based on either the getter or setter derive the missing/
         * complimenting accessor from the class provided.
         *
         * @param method  getter or setter
         */
        public MethodInvocationStrategy(Method method)
            {
            Method methGet = null;
            Method methSet = null;
            if (method != null)
                {
                Method  methComp = getCompliment(method);
                boolean fSetter  = method.getReturnType() == null || Void.TYPE.equals(method.getReturnType());

                methGet = fSetter ? methComp : method;
                methSet = fSetter ? method   : methComp;
                }
            initialize(methGet, methSet);
            }

        /**
         * Construct with the get and set methods.
         *
         * @param methGetter  T getX() method
         * @param methSetter  void setX(T a)
         */
        public MethodInvocationStrategy(Method methGetter, Method methSetter)
            {
            initialize(methGetter, methSetter);
            }

        // ----- InvocationStrategy interface -------------------------------

        /**
         * {@inheritDoc}
         */
        public T get(PT container)
            {
            T value;
            try
                {
                value = (T) m_methGetter.invoke(container);
                }
            catch (IllegalAccessException e)
                {
                throw Base.ensureRuntimeException(e, "AttributeMetadata accessor is unable to access get accessor " + m_methGetter.getName());
                }
            catch (InvocationTargetException e)
                {
                throw Base.ensureRuntimeException(e, "AttributeMetadata received an error when attempting to access get accessor " + m_methGetter.getName());
                }
            return value;
            }

        /**
         * {@inheritDoc}
         */
        public void set(PT container, T value)
            {
            try
                {
                // do not attempt to set the value iff it is null and a primitive
                // as coercion of null boxed type to a primitive target produces a NPE
                if (value != null || !m_methSetter.getParameterTypes()[0].isPrimitive())
                    {
                    m_methSetter.invoke(container, value);
                    }
                }
            catch (IllegalAccessException e)
                {
                throw Base.ensureRuntimeException(e, "AttributeMetadata accessor is unable to access set accessor" + m_methSetter.getName());
                }
            catch (InvocationTargetException e)
                {
                throw Base.ensureRuntimeException(e, "AttributeMetadata accessor is unable to invoke set accessor" + m_methSetter.getName() + " with value "+value);
                }
            }

        // ----- accessors --------------------------------------------------

        /**
         * Return the class definition of the type that will be returned.
         *
         * @return the class definition of the type this invocation strategy
         *         acts upon
         */
        public Class getType()
            {
            return (Class) m_methGetter.getReturnType();
            }

        // ----- helpers ----------------------------------------------------

        /**
         * Determine the complement of the provided method in terms of
         * accessors, i.e. if a set method return the corresponding get or is
         * and vice versa.
         *
         * @param method  the method to determine the compliment of
         *
         * @return the method that compliments the method passed
         *
         * @throws IllegalArgumentException
         * @throws RuntimeException iff the compliment method could not be
         *         determined by
         *         {@link Class#getDeclaredMethod(String, Class[])}
         */
        protected Method getCompliment(Method method)
            {
            if (method == null)
                {
                return null;
                }

            int nSetter = 0;
            if (method.getReturnType() == null || Void.TYPE.equals(method.getReturnType())) // setter
                {
                if (method.getParameterTypes().length <= 0)
                    {
                    throw new IllegalArgumentException("Method (" + method + ") should have a parameter");
                    }
                nSetter = Boolean.TYPE .equals(method.getParameterTypes()[0])
                          || Boolean.class.equals(method.getParameterTypes()[0])
                          ? 2 : 1;
                }

            String sMethodName = String.format("%s%s",
                    nSetter == 2 ? "is" : nSetter == 1 ? "get" : "set",
                    method.getName().substring(method.getName().startsWith("is") ? 2 : 3));

            Method methComp;
            try
                {
                methComp = nSetter == 0
                    ? method.getDeclaringClass().getDeclaredMethod(sMethodName, method.getReturnType())
                    : method.getDeclaringClass().getDeclaredMethod(sMethodName);

                }
            catch (NoSuchMethodException e)
                {
                throw Base.ensureRuntimeException(e,
                        "An error occurred in discovering the compliment of method = " + method
                        + ", assuming compliment method name is " + sMethodName);
                }

            if (methComp == null)
                {
                throw Base.ensureRuntimeException(new NoSuchMethodException(
                        "Could not derive the compliment method of " + method
                        + ", assuming compliment method name is " + sMethodName));
                }

            return methComp;
            }

        /**
         * Initialize ensures both accessors are not null and if private that
         * accessibility can be set.
         *
         * @param methGetter  the get accessor
         * @param methSetter  the set accessor
         * @throws IllegalArgumentException iff getter or setter are null or
         *         not accessible
         */
        protected void initialize(Method methGetter, Method methSetter)
            {
            if (methGetter == null || methSetter == null)
                {
                throw new IllegalArgumentException("The getter or setter can not "
                        + "be null in constructing a MethodInvocationStrategy");
                }

            ensureAccessible(methGetter);
            ensureAccessible(methSetter);

            m_methGetter = methGetter;
            m_methSetter = methSetter;
            }

        /**
         * Ensures accessibility on the passed method.
         *
         * @param method  the method to set accessibility on
         *
         * @throws IllegalArgumentException  if an exception was thrown in
         *         calling {@link Method#setAccessible(boolean)}
         */
        protected void ensureAccessible(Method method)
            {
            if ((method.getModifiers() & Modifier.PUBLIC) != Modifier.PUBLIC)
                {
                try
                    {
                    method.setAccessible(true);
                    }
                catch (SecurityException se)
                    {
                    throw new IllegalArgumentException("A security manager has been registered "
                            + "with access to method " + method.getName() + " being rejected", se);
                    }
                }
            }

        // ----- data members -----------------------------------------------

        /**
         * The {@link Method} this strategy will use to get the value.
         */
        private Method m_methGetter;

        /**
         * The {@link Method} this strategy will use to set a value.
         */
        private Method m_methSetter;
        }
    }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy