package org.jboss.weld.annotated.slim.backed;
import static org.jboss.weld.util.reflection.Reflections.cast;
import java.io.ObjectInputStream;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Objects;
import java.util.Set;
import javax.enterprise.inject.spi.AnnotatedConstructor;
import javax.enterprise.inject.spi.AnnotatedField;
import javax.enterprise.inject.spi.AnnotatedMethod;
import org.jboss.weld.annotated.slim.AnnotatedTypeIdentifier;
import org.jboss.weld.annotated.slim.SlimAnnotatedType;
import org.jboss.weld.exceptions.InvalidObjectException;
import org.jboss.weld.logging.BeanLogger;
import org.jboss.weld.resources.ReflectionCache;
import org.jboss.weld.resources.SharedObjectCache;
import org.jboss.weld.util.LazyValueHolder;
import org.jboss.weld.util.Types;
import org.jboss.weld.util.collections.ImmutableSet;
import org.jboss.weld.util.reflection.Formats;
import org.jboss.weld.util.reflection.Reflections;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
@SuppressFBWarnings(value = { "SE_BAD_FIELD", "SE_NO_SUITABLE_CONSTRUCTOR", "SE_BAD_FIELD_STORE", "SE_NO_SERIALVERSIONID" }, justification = "False positive from FindBugs - serialization is handled by SerializationProxy.")
public class BackedAnnotatedType<X> extends BackedAnnotated implements SlimAnnotatedType<X>, Serializable {
public static <X> BackedAnnotatedType<X> of(Class<X> javaClass, SharedObjectCache sharedObjectCache, ReflectionCache reflectionCache, String contextId, String bdaId) {
return of(javaClass, javaClass, sharedObjectCache, reflectionCache, contextId, bdaId);
}
public static <X> BackedAnnotatedType<X> of(Class<X> javaClass, Type baseType, SharedObjectCache sharedObjectCache, ReflectionCache reflectionCache, String contextId, String bdaId) {
return of(javaClass, baseType, sharedObjectCache, reflectionCache, contextId, bdaId, null);
}
public static <X> BackedAnnotatedType<X> of(Class<X> javaClass, Type baseType, SharedObjectCache sharedObjectCache, ReflectionCache reflectionCache,
String contextId, String bdaId, String suffix) {
return new BackedAnnotatedType<X>(javaClass, baseType, sharedObjectCache, reflectionCache, contextId, bdaId, suffix);
}
private final Class<X> javaClass;
private final LazyValueHolder<Set<AnnotatedConstructor<X>>> constructors;
private final LazyValueHolder<Set<AnnotatedMethod<? super X>>> methods;
private final LazyValueHolder<Set<AnnotatedField<? super X>>> fields;
private final SharedObjectCache sharedObjectCache;
private final ReflectionCache reflectionCache;
private final AnnotatedTypeIdentifier identifier;
private BackedAnnotatedType(Class<X> rawType, Type baseType, SharedObjectCache sharedObjectCache, ReflectionCache reflectionCache, String contextId,
String bdaId, String suffix) {
super(baseType, sharedObjectCache);
this.javaClass = rawType;
this.sharedObjectCache = sharedObjectCache;
this.reflectionCache = reflectionCache;
this.constructors = new BackedAnnotatedConstructors();
this.fields = new BackedAnnotatedFields();
this.methods = new BackedAnnotatedMethods();
this.identifier = AnnotatedTypeIdentifier.forBackedAnnotatedType(contextId, rawType, baseType, bdaId, suffix);
}
@Override
protected LazyValueHolder<Set<Type>> initTypeClosure(Type baseType, SharedObjectCache cache) {
return cache.getTypeClosureHolder(Types.getCanonicalType(baseType));
}
@Override
protected AnnotatedElement getAnnotatedElement() {
return javaClass;
}
public Class<X> getJavaClass() {
return javaClass;
}
public Set<AnnotatedConstructor<X>> getConstructors() {
return constructors.get();
}
public Set<AnnotatedMethod<? super X>> getMethods() {
return methods.get();
}
public Set<AnnotatedField<? super X>> getFields() {
return fields.get();
}
public <T extends Annotation> T getAnnotation(Class<T> annotationType) {
for (Annotation annotation : getAnnotations()) {
if (annotation.annotationType().equals(annotationType)) {
return annotationType.cast(annotation);
}
}
return null;
}
public boolean isAnnotationPresent(Class<? extends Annotation> annotationType) {
return getAnnotation(annotationType) != null;
}
@Override
public Set<Annotation> getAnnotations() {
return reflectionCache.getBackedAnnotatedTypeAnnotationSet(javaClass);
}
@Override
public int hashCode() {
return identifier.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof BackedAnnotatedType<?>) {
BackedAnnotatedType<?> that = cast(obj);
return Objects.equals(this.identifier, that.identifier);
}
return false;
}
@Override
public String toString() {
return Formats.formatAnnotatedType(this);
}
@Override
public void clear() {
this.constructors.clear();
this.fields.clear();
this.methods.clear();
}
// Serialization
private Object writeReplace() throws ObjectStreamException {
return new SerializationProxy<X>(getIdentifier());
}
private void readObject(ObjectInputStream stream) throws InvalidObjectException {
throw BeanLogger.LOG.serializationProxyRequired();
}
// Lazy initialization
// Initialize eagerly since we want to discover CNFE at bootstrap
// After bootstrap, these holders are reset to conserve memory
private abstract class EagerlyInitializedLazyValueHolder<T> extends LazyValueHolder<T> {
public EagerlyInitializedLazyValueHolder() {
this.get();
}
}
private class BackedAnnotatedConstructors extends EagerlyInitializedLazyValueHolder<Set<AnnotatedConstructor<X>>> {
@Override
protected Set<AnnotatedConstructor<X>> computeValue() {
Constructor<?>[] declaredConstructors = SecurityActions.getDeclaredConstructors(javaClass);
ImmutableSet.Builder<AnnotatedConstructor<X>> constructors = ImmutableSet.builder();
for (Constructor<?> constructor : declaredConstructors) {
Constructor<X> c = Reflections.cast(constructor);
constructors.add(BackedAnnotatedConstructor.of(c, BackedAnnotatedType.this, sharedObjectCache));
}
return constructors.build();
}
}
private class BackedAnnotatedFields extends EagerlyInitializedLazyValueHolder<Set<AnnotatedField<? super X>>> {
@Override
protected Set<AnnotatedField<? super X>> computeValue() {
ImmutableSet.Builder<AnnotatedField<? super X>> fields = ImmutableSet.builder();
Class<? super X> clazz = javaClass;
while (clazz != Object.class && clazz != null) {
for (Field field : SecurityActions.getDeclaredFields(clazz)) {
fields.add(BackedAnnotatedField.of(field, BackedAnnotatedType.this, sharedObjectCache));
}
clazz = clazz.getSuperclass();
}
return fields.build();
}
}
private class BackedAnnotatedMethods extends EagerlyInitializedLazyValueHolder<Set<AnnotatedMethod<? super X>>> {
@Override
protected Set<AnnotatedMethod<? super X>> computeValue() {
ImmutableSet.Builder<AnnotatedMethod<? super X>> methods = ImmutableSet.builder();
Class<? super X> clazz = javaClass;
while (clazz != Object.class && clazz != null) {
for (Method method : SecurityActions.getDeclaredMethods(clazz)) {
methods.add(BackedAnnotatedMethod.of(method, BackedAnnotatedType.this, sharedObjectCache));
}
clazz = clazz.getSuperclass();
}
// Also add default methods
for (Class<?> interfaceClazz : Reflections.getInterfaceClosure(javaClass)) {
for (Method method : SecurityActions.getDeclaredMethods(interfaceClazz)) {
if (Reflections.isDefault(method)) {
methods.add(BackedAnnotatedMethod.of(method, BackedAnnotatedType.this, sharedObjectCache));
}
}
}
return methods.build();
}
}
public ReflectionCache getReflectionCache() {
return reflectionCache;
}
@Override
public AnnotatedTypeIdentifier getIdentifier() {
return identifier;
}
}