/* * Copyright 2014 mango.jfaster.org * * The Mango Project licenses this file to you under the Apache License, * version 2.0 (the "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. */ package org.jfaster.mango.invoker; import org.jfaster.mango.annotation.Setter; import org.jfaster.mango.exception.UncheckedException; import org.jfaster.mango.util.reflect.Reflection; import org.jfaster.mango.util.reflect.TokenTuple; import org.jfaster.mango.util.reflect.TypeToken; import org.jfaster.mango.util.reflect.Types; import javax.annotation.Nullable; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Type; /** * @author ash */ public class FunctionalSetterInvoker extends MethodNamedObject implements SetterInvoker { private FunctionAdapter functionAdapter; private Type parameterType; private Class<?> parameterRawType; private Type runtimeOutputType; private Class<?> runtimeOutputRawType; private FunctionalSetterInvoker(String name, Method method) { super(name, method); TypeToken<?> parameterToken = TypeToken.of(method.getGenericParameterTypes()[0]); runtimeOutputType = parameterToken.getType(); runtimeOutputRawType = parameterToken.getRawType(); Setter setterAnno = method.getAnnotation(Setter.class); if (setterAnno != null) { // 启用函数式调用功能 Class<? extends DummySetterFunction<?, ?>> funcClass = setterAnno.value(); if (SetterFunction.class.isAssignableFrom(funcClass)) { functionAdapter = new SetterFunctionAdapter((SetterFunction) Reflection.instantiateClass(funcClass)); } else if (RuntimeSetterFunction.class.isAssignableFrom(funcClass)) { functionAdapter = new RuntimeSetterFunctionAdapter((RuntimeSetterFunction) Reflection.instantiateClass(funcClass)); } else { throw new IllegalArgumentException("error func class '" + funcClass + "'"); } TokenTuple tokenTuple = TypeToken.of(funcClass).resolveFatherClassTuple(DummySetterFunction.class); TypeToken<?> inputToken = tokenTuple.getFirst(); TypeToken<?> outputToken = tokenTuple.getSecond(); TypeToken<?> wrapParameterToken = parameterToken.wrap(); if (functionAdapter instanceof RuntimeSetterFunctionAdapter) { // 针对继承RuntimeSetterFunction的 if (!outputToken.isAssignableFrom(wrapParameterToken)) { throw new ClassCastException("function[" + functionAdapter.getFunction().getClass() + "] " + "on method[" + method + "] error, function's outputType[" + outputToken.getType() + "] " + "must be assignable from method's parameterType[" + parameterToken.getType() + "]"); } } else { // 针对继承LiteSetterFunction的 if (!wrapParameterToken.isAssignableFrom(outputToken)) { throw new ClassCastException("function[" + functionAdapter.getFunction().getClass() + "] " + "on method[" + method + "] error, method's parameterType[" + parameterToken.getType() + "] " + "must be assignable from function's outputType[" + outputToken.getType() + "]"); } } parameterToken = inputToken; } parameterType = parameterToken.getType(); parameterRawType = parameterToken.getRawType(); } public static FunctionalSetterInvoker create(String name, Method method) { return new FunctionalSetterInvoker(name, method); } @Override public void invoke(Object object, @Nullable Object parameter) { try { if (functionAdapter != null) { parameter = functionAdapter.apply(parameter, runtimeOutputType); } if (parameter == null && runtimeOutputRawType.isPrimitive()) { throw new NullPointerException("property " + getName() + " of " + object.getClass() + " is primitive, can not be assigned to null"); } if (parameter != null && !Types.isAssignable(runtimeOutputRawType, parameter.getClass())) { throw new ClassCastException("cannot convert value of type [" + parameter.getClass().getName() + "] to required type [" + runtimeOutputRawType.getName() + "] " + "for property '" + getName() + "' of " + object.getClass()); } method.invoke(object, parameter); } catch (IllegalAccessException e) { throw new UncheckedException(e.getMessage(), e.getCause()); } catch (InvocationTargetException e) { throw new UncheckedException(e.getMessage(), e.getCause()); } } @Override public Type getParameterType() { return parameterType; } @Override public Class<?> getParameterRawType() { return parameterRawType; } interface FunctionAdapter { @Nullable public Object apply(@Nullable Object input, Type runtimeOutputType); public DummySetterFunction getFunction(); } static class SetterFunctionAdapter implements FunctionAdapter { private final SetterFunction function; SetterFunctionAdapter(SetterFunction function) { this.function = function; } @SuppressWarnings("unchecked") @Nullable @Override public Object apply(@Nullable Object input, Type runtimeOutputType) { return function.apply(input); } @Override public DummySetterFunction getFunction() { return function; } } static class RuntimeSetterFunctionAdapter implements FunctionAdapter { private final RuntimeSetterFunction function; RuntimeSetterFunctionAdapter(RuntimeSetterFunction function) { this.function = function; } @SuppressWarnings("unchecked") @Nullable @Override public Object apply(@Nullable Object input, Type runtimeOutputType) { return function.apply(input, runtimeOutputType); } @Override public DummySetterFunction getFunction() { return function; } } }