| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF 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.apache.servicecomb.foundation.common.utils; |
| |
| import java.lang.invoke.CallSite; |
| import java.lang.invoke.LambdaMetafactory; |
| import java.lang.invoke.MethodHandle; |
| import java.lang.invoke.MethodHandles; |
| import java.lang.invoke.MethodHandles.Lookup; |
| import java.lang.invoke.MethodType; |
| import java.lang.reflect.Field; |
| import java.lang.reflect.Method; |
| import java.lang.reflect.Modifier; |
| import java.util.HashMap; |
| import java.util.Map; |
| |
| import org.apache.servicecomb.foundation.common.utils.bean.BoolGetter; |
| import org.apache.servicecomb.foundation.common.utils.bean.BoolSetter; |
| import org.apache.servicecomb.foundation.common.utils.bean.ByteGetter; |
| import org.apache.servicecomb.foundation.common.utils.bean.ByteSetter; |
| import org.apache.servicecomb.foundation.common.utils.bean.CharGetter; |
| import org.apache.servicecomb.foundation.common.utils.bean.CharSetter; |
| import org.apache.servicecomb.foundation.common.utils.bean.DoubleGetter; |
| import org.apache.servicecomb.foundation.common.utils.bean.DoubleSetter; |
| import org.apache.servicecomb.foundation.common.utils.bean.FloatGetter; |
| import org.apache.servicecomb.foundation.common.utils.bean.FloatSetter; |
| import org.apache.servicecomb.foundation.common.utils.bean.Getter; |
| import org.apache.servicecomb.foundation.common.utils.bean.IntGetter; |
| import org.apache.servicecomb.foundation.common.utils.bean.IntSetter; |
| import org.apache.servicecomb.foundation.common.utils.bean.LongGetter; |
| import org.apache.servicecomb.foundation.common.utils.bean.LongSetter; |
| import org.apache.servicecomb.foundation.common.utils.bean.Setter; |
| import org.apache.servicecomb.foundation.common.utils.bean.ShortGetter; |
| import org.apache.servicecomb.foundation.common.utils.bean.ShortSetter; |
| |
| import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition; |
| |
| public final class LambdaMetafactoryUtils { |
| private static final Lookup LOOKUP = MethodHandles.lookup(); |
| |
| private static final Map<Class<?>, Class<?>> GETTER_MAP = new HashMap<>(); |
| |
| private static final Map<Class<?>, Class<?>> SETTER_MAP = new HashMap<>(); |
| |
| static { |
| initGetterSetterMap(); |
| } |
| |
| private static void initGetterSetterMap() { |
| GETTER_MAP.put(boolean.class, BoolGetter.class); |
| GETTER_MAP.put(byte.class, ByteGetter.class); |
| GETTER_MAP.put(char.class, CharGetter.class); |
| GETTER_MAP.put(short.class, ShortGetter.class); |
| GETTER_MAP.put(int.class, IntGetter.class); |
| GETTER_MAP.put(long.class, LongGetter.class); |
| GETTER_MAP.put(float.class, FloatGetter.class); |
| GETTER_MAP.put(double.class, DoubleGetter.class); |
| |
| SETTER_MAP.put(boolean.class, BoolSetter.class); |
| SETTER_MAP.put(byte.class, ByteSetter.class); |
| SETTER_MAP.put(char.class, CharSetter.class); |
| SETTER_MAP.put(short.class, ShortSetter.class); |
| SETTER_MAP.put(int.class, IntSetter.class); |
| SETTER_MAP.put(long.class, LongSetter.class); |
| SETTER_MAP.put(float.class, FloatSetter.class); |
| SETTER_MAP.put(double.class, DoubleSetter.class); |
| } |
| |
| private LambdaMetafactoryUtils() { |
| } |
| |
| private static Method findAbstractMethod(Class<?> functionalInterface) { |
| for (Method method : functionalInterface.getMethods()) { |
| if ((method.getModifiers() & Modifier.ABSTRACT) != 0) { |
| return method; |
| } |
| } |
| |
| return null; |
| } |
| |
| @SuppressWarnings("unchecked") |
| public static <T> T createLambda(Object instance, Method instanceMethod, Class<?> functionalIntfCls) { |
| try { |
| Method intfMethod = findAbstractMethod(functionalIntfCls); |
| MethodHandle methodHandle = LOOKUP.unreflect(instanceMethod); |
| |
| MethodType intfMethodType = MethodType.methodType(intfMethod.getReturnType(), intfMethod.getParameterTypes()); |
| MethodType instanceMethodType = MethodType |
| .methodType(instanceMethod.getReturnType(), instanceMethod.getParameterTypes()); |
| CallSite callSite = LambdaMetafactory.metafactory( |
| LOOKUP, |
| intfMethod.getName(), |
| MethodType.methodType(functionalIntfCls, instance.getClass()), |
| intfMethodType, |
| methodHandle, |
| instanceMethodType); |
| |
| return (T) callSite.getTarget().bindTo(instance).invoke(); |
| } catch (Throwable e) { |
| throw new IllegalStateException("Failed to create lambda from " + instanceMethod, e); |
| } |
| } |
| |
| @SuppressWarnings("unchecked") |
| public static <T> T createLambda(Method instanceMethod, Class<?> functionalIntfCls) { |
| if (Modifier.isNative(instanceMethod.getModifiers())) { |
| // fix "Failed to create lambda from public final native java.lang.Class java.lang.Object.getClass()" |
| return null; |
| } |
| try { |
| Method intfMethod = findAbstractMethod(functionalIntfCls); |
| MethodHandle methodHandle = LOOKUP.unreflect(instanceMethod); |
| |
| MethodType intfMethodType = MethodType.methodType(intfMethod.getReturnType(), intfMethod.getParameterTypes()); |
| |
| // the return type of fluent setter is object instead of void, but we can assume the return type is void. it doesn't matter |
| MethodType instanceMethodType = MethodType |
| .methodType(intfMethod.getReturnType(), methodHandle.type().parameterList()); |
| CallSite callSite = LambdaMetafactory.metafactory( |
| LOOKUP, |
| intfMethod.getName(), |
| MethodType.methodType(functionalIntfCls), |
| intfMethodType, |
| methodHandle, |
| instanceMethodType); |
| |
| return (T) callSite.getTarget().invoke(); |
| } catch (Throwable e) { |
| throw new IllegalStateException("Failed to create lambda from " + instanceMethod, e); |
| } |
| } |
| |
| public static <T> T createGetter(Method getMethod) { |
| Class<?> getterCls = GETTER_MAP.getOrDefault(getMethod.getReturnType(), Getter.class); |
| return createLambda(getMethod, getterCls); |
| } |
| |
| @SuppressWarnings("unchecked") |
| public static Getter<Object, Object> createObjectGetter(Method getMethod) { |
| return createLambda(getMethod, Getter.class); |
| } |
| |
| public static Getter<Object, Object> createObjectGetter(BeanPropertyDefinition propertyDefinition) { |
| if (propertyDefinition.hasGetter()) { |
| return createObjectGetter(propertyDefinition.getGetter().getAnnotated()); |
| } |
| |
| return createGetter(propertyDefinition.getField().getAnnotated()); |
| } |
| |
| // slower than reflect directly |
| @SuppressWarnings("unchecked") |
| public static <C, F> Getter<C, F> createGetter(Field field) { |
| checkAccess(field); |
| return instance -> { |
| try { |
| return (F) field.get(instance); |
| } catch (IllegalAccessException e) { |
| throw new RuntimeException(e); |
| } |
| }; |
| } |
| |
| private static void checkAccess(Field field) { |
| // This check is not accurate. Most of time package visible and protected access can be ignored, so simply do this. |
| if (!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers())) { |
| throw new IllegalStateException( |
| String.format("Can not access field, a public field or accessor is required." |
| + "Declaring class is %s, field is %s", |
| field.getDeclaringClass().getName(), |
| field.getName())); |
| } |
| } |
| |
| public static <T> T createSetter(Method setMethod) { |
| Class<?> setterCls = SETTER_MAP.getOrDefault(setMethod.getParameterTypes()[0], Setter.class); |
| return createLambda(setMethod, setterCls); |
| } |
| |
| // just for avoid java 9~11 bug: https://bugs.openjdk.java.net/browse/JDK-8174983 |
| // otherwise can be replaced by: createLambda(setMethod, Setter.class) |
| @SuppressWarnings("unchecked") |
| public static Setter<Object, Object> createObjectSetter(Method setMethod) { |
| Object setter = createSetter(setMethod); |
| if (setter instanceof BoolSetter) { |
| return (Instance, value) -> ((BoolSetter) setter).set(Instance, (boolean) value); |
| } |
| if (setter instanceof ByteSetter) { |
| return (Instance, value) -> ((ByteSetter) setter).set(Instance, (byte) value); |
| } |
| if (setter instanceof CharSetter) { |
| return (Instance, value) -> ((CharSetter) setter).set(Instance, (char) value); |
| } |
| if (setter instanceof DoubleSetter) { |
| return (Instance, value) -> ((DoubleSetter) setter).set(Instance, (double) value); |
| } |
| if (setter instanceof FloatSetter) { |
| return (Instance, value) -> ((FloatSetter) setter).set(Instance, (float) value); |
| } |
| if (setter instanceof IntSetter) { |
| return (Instance, value) -> ((IntSetter) setter).set(Instance, (int) value); |
| } |
| if (setter instanceof LongSetter) { |
| return (Instance, value) -> ((LongSetter) setter).set(Instance, (long) value); |
| } |
| if (setter instanceof ShortSetter) { |
| return (Instance, value) -> ((ShortSetter) setter).set(Instance, (short) value); |
| } |
| |
| return (Setter<Object, Object>) setter; |
| } |
| |
| public static Setter<Object, Object> createObjectSetter(BeanPropertyDefinition propertyDefinition) { |
| if (propertyDefinition.hasSetter()) { |
| return createObjectSetter(propertyDefinition.getSetter().getAnnotated()); |
| } |
| |
| return createSetter(propertyDefinition.getField().getAnnotated()); |
| } |
| |
| // slower than reflect directly |
| public static <C, F> Setter<C, F> createSetter(Field field) { |
| checkAccess(field); |
| return (instance, value) -> { |
| try { |
| field.set(instance, value); |
| } catch (IllegalAccessException e) { |
| throw new RuntimeException(e); |
| } |
| }; |
| } |
| } |