global.namespace.neuron.di.java.Reflection Maven / Gradle / Ivy
/*
* Copyright © 2016 - 2019 Schlichtherle IT Services
*
* Licensed 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
*
* https://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 global.namespace.neuron.di.java;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.invoke.MethodType;
import java.lang.reflect.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import static java.lang.invoke.MethodHandles.publicLookup;
import static java.lang.invoke.MethodType.methodType;
import static java.util.Collections.synchronizedMap;
import static java.util.Optional.*;
class Reflection {
private static final MethodType acceptsNothingAndReturnsObject = methodType(Object.class);
private static final MethodType acceptsObjectAndReturnsObject = methodType(Object.class, Object.class);
private static final Lookup publicLookup = publicLookup();
private static final Map, Map>
classIndex = synchronizedMap(new WeakHashMap<>());
private static volatile Map> lookupIndex;
private static volatile Map publicLookupMethodHandleFactories;
private Reflection() {
}
/**
* Recursively searches for the named {@code member} in the given {@code object} and if found, returns a
* corresponding {@link MethodHandle} created using the the given {@code lookup}.
*
* @throws BreedingException if the named {@code member} is not found in the given {@code object} or if the given
* {@code lookup} has no access to it.
*/
static MethodHandle methodHandle(final String member, final Object object, final Lookup lookup) {
final Class> clazz = object.getClass();
final MethodHandleMetaFactory mhmf = classIndex
.computeIfAbsent(clazz, c -> new ConcurrentHashMap<>())
.computeIfAbsent(member, m -> methodHandleMetaFactory(m, clazz));
Map mhfs;
if (lookup.equals(publicLookup)) {
if (null == (mhfs = publicLookupMethodHandleFactories)) {
synchronized (classIndex) {
if (null == (mhfs = publicLookupMethodHandleFactories)) {
publicLookupMethodHandleFactories = mhfs = synchronizedMap(new WeakHashMap<>());
}
}
}
} else {
Map> li;
if (null == (li = lookupIndex)) {
synchronized (classIndex) {
if (null == (li = lookupIndex)) {
lookupIndex = li = synchronizedMap(new WeakHashMap<>());
}
}
}
mhfs = li.computeIfAbsent(lookup, l -> new ConcurrentHashMap<>());
}
return mhfs.computeIfAbsent(mhmf, mf -> mf.methodHandleFactory(lookup)).methodHandle(object);
}
private static MethodHandleMetaFactory methodHandleMetaFactory(String member, Class> clazz) {
class MethodHandleMetaFactoryFinder implements Function, Optional> {
private final Set> interfaces = new HashSet<>();
@Override
public Optional apply(final Class> c) {
Optional method;
try {
final Method m = c.getDeclaredMethod(member);
if (isPublic(c) || isStatic(m)) {
return of(methodHandleMetaFactory(m, Reflection::unreflectMethod));
}
method = of(m);
} catch (NoSuchMethodException e) {
try {
return of(methodHandleMetaFactory(c.getDeclaredField(member), Reflection::unreflectField));
} catch (NoSuchFieldException ignored) {
}
method = empty();
}
Optional superResult = ofNullable(c.getSuperclass()).flatMap(this);
if (superResult.isPresent()) {
return superResult;
}
for (final Class> iface : c.getInterfaces()) {
if (!interfaces.contains(iface)) {
if ((superResult = apply(iface)).isPresent()) {
return superResult;
}
interfaces.add(iface);
}
}
return method.map(m -> methodHandleMetaFactory(m, Reflection::unreflectMethod));
}
}
return new MethodHandleMetaFactoryFinder()
.apply(clazz)
.orElseThrow(() ->
new BreedingException("A member named `" + member + "` neither exists in `" + clazz + "` nor in any of its superclasses and interfaces."));
}
private static
MethodHandleMetaFactory methodHandleMetaFactory(final M member, final Unreflect unreflect) {
member.setAccessible(true);
if (isStatic(member)) {
return lookup -> {
final MethodHandle mh = unreflect.methodHandle(member, lookup).asType(acceptsNothingAndReturnsObject);
return ignored -> mh;
};
} else {
return lookup -> {
final MethodHandle mh = unreflect.methodHandle(member, lookup).asType(acceptsObjectAndReturnsObject);
return mh::bindTo;
};
}
}
private static MethodHandle unreflectMethod(final Method method, final Lookup lookup) {
try {
return lookup.unreflect(method);
} catch (IllegalAccessException e) {
throw new BreedingException(e);
}
}
private static MethodHandle unreflectField(final Field field, final Lookup lookup) {
try {
return lookup.unreflectGetter(field);
} catch (IllegalAccessException e) {
throw new BreedingException(e);
}
}
private static boolean isPublic(Class> clazz) {
return Modifier.isPublic(clazz.getModifiers());
}
private static boolean isStatic(Member member) {
return Modifier.isStatic(member.getModifiers());
}
private interface Unreflect {
MethodHandle methodHandle(M member, Lookup lookup);
}
private interface MethodHandleMetaFactory {
MethodHandleFactory methodHandleFactory(Lookup lookup);
}
private interface MethodHandleFactory {
MethodHandle methodHandle(Object object);
}
}