global.namespace.neuron.di.internal.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.internal;
import global.namespace.neuron.di.internal.proxy.Proxies;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
import static java.lang.invoke.MethodHandles.*;
import static java.lang.reflect.Modifier.*;
import static java.util.Optional.empty;
import static java.util.Optional.*;
import static org.objectweb.asm.Type.getMethodDescriptor;
class Reflection {
private static final Lookup lookup = lookup();
private Reflection() {
}
@SuppressWarnings({"unchecked", "Since15"})
static Class extends C> defineSubclass(final Class clazz, final String name, final byte[] b) {
try {
return (Class extends C>) privateLookupIn(null != clazz.getClassLoader() ? clazz : Proxies.class, lookup)
.defineClass(b);
} catch (NoSuchMethodError e) {
return Reflection8.defineSubclass(clazz, name, b);
} catch (IllegalAccessException e) {
throw new AssertionError(e);
}
}
static Class> boxed(Class> clazz) {
if (clazz.isPrimitive()) {
if (clazz == Boolean.TYPE) {
return Boolean.class;
} else if (clazz == Character.TYPE) {
return Character.class;
} else if (clazz == Byte.TYPE) {
return Byte.class;
} else if (clazz == Double.TYPE) {
return Double.class;
} else if (clazz == Float.TYPE) {
return Float.class;
} else if (clazz == Long.TYPE) {
return Long.class;
} else if (clazz == Integer.TYPE) {
return Integer.class;
} else if (clazz == Short.TYPE) {
return Short.class;
} else {
assert clazz == Void.TYPE;
return Void.class;
}
} else {
return clazz;
}
}
static Collection overridableMethods(final Class> clazz) {
final Collection methods = overridableMethodsMap(clazz).values();
// VOLATILE methods are bridge methods inserted by the compiler, e.g. when inheriting from a generic superclass
// and specifying its type parameter in the subclass.
methods.removeIf(method -> 0 != (method.getModifiers() & (FINAL | VOLATILE)));
return methods;
}
private static Map overridableMethodsMap(final Class> clazz) {
final Map methods = new LinkedHashMap<>();
traverse(c -> {
for (final Method method : c.getDeclaredMethods()) {
if (0 == (method.getModifiers() & (PRIVATE | STATIC))) {
methods.merge(signature(method), method, Reflection::select);
}
}
}).accept(clazz);
return methods;
}
private static Method select(Method old, Method noo) {
return old.getDeclaringClass().isAssignableFrom(noo.getDeclaringClass()) &&
old.getReturnType().isAssignableFrom(noo.getReturnType())
? noo
: old;
}
/**
* Returns a consumer which applies the given consumer to all elements of the type hierarchy represented by its
* class parameter.
* The traversal starts with calling the given consumer for the given type, then applies itself recursively to the
* superclass of the given type (if existing) and finally to all of the interfaces implemented by the given type
* (if any).
* Note that due to interfaces, the type hierarchy can be a graph;
* the returned consumer will visit any interface at most once, however.
*/
private static Consumer> traverse(Consumer> consumer) {
return new Consumer>() {
final Set> visited = new HashSet<>();
@Override
public void accept(final Class> clazz) {
if (visited.add(clazz)) {
consumer.accept(clazz);
ofNullable(clazz.getSuperclass()).ifPresent(this);
for (Class> iface : clazz.getInterfaces()) {
accept(iface);
}
}
}
};
}
private static String signature(Method method) {
return method.getName() + getMethodDescriptor(method).replaceAll("\\).*", ")");
}
static Function> findAnnotation(Class what) {
return new Function>() {
final Set visited = new HashSet<>();
@SuppressWarnings("unchecked")
@Override
public Optional apply(final AnnotatedElement where) {
if (visited.add(where)) {
for (final Annotation a : where.getAnnotations()) {
if (what.isInstance(a)) {
return of((T) a);
}
final Optional here = apply(a.annotationType());
if (here.isPresent()) {
return here;
}
}
}
return empty();
}
};
}
}