/*
* Copyright (c) 1998-2011 Caucho Technology -- all rights reserved
*
* This file is part of Resin(R) Open Source
*
* Each copy or derived work must preserve the copyright notice and this
* notice unmodified.
*
* Resin Open Source is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Resin Open Source is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
* of NON-INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with Resin Open Source; if not, write to the
*
* Free Software Foundation, Inc.
* 59 Temple Place, Suite 330
* Boston, MA 02111-1307 USA
*
* @author Scott Ferguson
*/
package com.caucho.config.reflect;
import java.lang.annotation.Annotation;
import java.lang.annotation.Inherited;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.enterprise.context.NormalScope;
import javax.enterprise.inject.Specializes;
import javax.enterprise.inject.spi.AnnotatedConstructor;
import javax.enterprise.inject.spi.AnnotatedField;
import javax.enterprise.inject.spi.AnnotatedMethod;
import javax.enterprise.inject.spi.AnnotatedType;
import javax.inject.Named;
import javax.inject.Qualifier;
import javax.inject.Scope;
import com.caucho.config.ConfigException;
import com.caucho.config.inject.InjectManager;
import com.caucho.config.reflect.BaseType.ClassFill;
import com.caucho.util.L10N;
/**
* Read-only introspected annotated type.
*/
public class ReflectionAnnotatedType<T>
extends ReflectionAnnotated
implements AnnotatedType<T>
{
private static final L10N L = new L10N(ReflectionAnnotatedType.class);
private static final Logger log
= Logger.getLogger(ReflectionAnnotatedType.class.getName());
private Class<T> _javaClass;
private HashMap<String,BaseType> _paramMap = new HashMap<String,BaseType>();
private ReflectionAnnotatedType<?> _parentType;
private Set<AnnotatedConstructor<T>> _constructorSet
= new LinkedHashSet<AnnotatedConstructor<T>>();
private Set<AnnotatedField<? super T>> _fieldSet
= new LinkedHashSet<AnnotatedField<? super T>>();
private Set<AnnotatedMethod<? super T>> _methodSet
= new LinkedHashSet<AnnotatedMethod<? super T>>();
private volatile boolean _isIntrospected;
ReflectionAnnotatedType(InjectManager manager,
BaseType type)
{
super(type,
type.getTypeClosure(manager),
type.getRawClass().getDeclaredAnnotations());
_javaClass = (Class<T>) type.getRawClass();
Class<?> parentClass = _javaClass.getSuperclass();
if (parentClass != null && ! parentClass.equals(Object.class)) {
_parentType = ReflectionAnnotatedFactory.introspectType(parentClass);
}
if (getBaseTypeImpl().getParamMap() != null)
_paramMap.putAll(getBaseTypeImpl().getParamMap());
introspectInheritedAnnotations(_javaClass);
if (_javaClass.isAnnotationPresent(Specializes.class))
introspectSpecializesAnnotations(_javaClass);
}
/**
* Returns the concrete Java class
*/
@Override
public Class<T> getJavaClass()
{
return _javaClass;
}
public ReflectionAnnotatedType<?> getParentType()
{
return _parentType;
}
@Override
public HashMap<String,BaseType> getBaseTypeParamMap()
{
return _paramMap;
}
/**
* Returns the abstract introspected constructors
*/
@Override
public Set<AnnotatedConstructor<T>> getConstructors()
{
introspect();
return _constructorSet;
}
/**
* Returns the abstract introspected methods
*/
@Override
public Set<AnnotatedMethod<? super T>> getMethods()
{
introspect();
return _methodSet;
}
/**
* Returns the matching method, creating one if necessary.
*/
public AnnotatedMethod<? super T> createMethod(Method method)
{
for (AnnotatedMethod<? super T> annMethod : _methodSet) {
if (AnnotatedMethodImpl.isMatch(annMethod.getJavaMember(), method)) {
return annMethod;
}
}
AnnotatedMethod<T> annMethod = new AnnotatedMethodImpl<T>(this, null, method);
_methodSet.add(annMethod);
return annMethod;
}
/**
* Returns the abstract introspected fields
*/
@Override
public Set<AnnotatedField<? super T>> getFields()
{
introspect();
return _fieldSet;
}
private void introspect()
{
if (_isIntrospected)
return;
synchronized (this) {
if (! _isIntrospected) {
introspect(_javaClass);
_isIntrospected = true;
}
}
}
private void introspect(Class<T> cl)
{
try {
/*
introspectInheritedAnnotations(cl);
if (cl.isAnnotationPresent(Specializes.class))
introspectSpecializesAnnotations(cl);
*/
introspectFields(cl);
introspectMethods(cl, null);
if (! cl.isInterface()) {
for (Constructor<?> ctor : cl.getDeclaredConstructors()) {
_constructorSet.add(new AnnotatedConstructorImpl(this, ctor));
}
if (_constructorSet.size() == 0) {
try {
Constructor<T> ctor = cl.getConstructor(new Class[0]);
_constructorSet.add(new AnnotatedConstructorImpl<T>(this, ctor));
} catch (NoSuchMethodException e) {
log.log(Level.FINE, e.toString(), e);
}
}
}
} catch (ConfigException e) {
throw e;
} catch (Exception e) {
throw ConfigException.create(L.l("{0} introspection failed: {1}", cl.getName(), e), e);
}
}
private void introspectFields(Class<?> cl)
{
if (cl == null)
return;
introspectFields(cl.getSuperclass());
for (Field field : cl.getDeclaredFields()) {
try {
// ioc/0p23
_fieldSet.add(new AnnotatedFieldImpl<T>(this, field));
} catch (ConfigException e) {
throw e;
} catch (Throwable e) {
throw ConfigException.create(L.l("{0}: {1}\n", field, e), e);
}
}
}
private void introspectMethods(Class<?> cl,
HashMap<String,BaseType> paramMap)
{
if (cl == null)
return;
for (Method method : cl.getDeclaredMethods()) {
if (method.getDeclaringClass().equals(Object.class))
continue;
if (method.isSynthetic() || method.isBridge()) {
continue;
}
// ejb/4018
// ejb/8501
// hasBeanAnnotation(method)
if (true
|| hasBeanAnnotation(method)
|| ! Modifier.isPrivate(method.getModifiers())) {
AnnotatedMethod<?> childMethod = null;
if (! Modifier.isPrivate(method.getModifiers())) {
childMethod = AnnotatedTypeUtil.findMethod(_methodSet, method);
}
if (! isMethodOverride(method, childMethod)) {
_methodSet.add(new AnnotatedMethodImpl<T>(this,
null,
method,
method.getAnnotations(),
paramMap));
}
/*
else if (! isParent)
continue;
*/
else if (childMethod instanceof AnnotatedMethodImpl<?>){
AnnotatedMethodImpl<?> childMethodImpl
= (AnnotatedMethodImpl<?>) childMethod;
// ejb/1290
for (Annotation ann : method.getAnnotations()) {
Class<?> annType = ann.annotationType();
if (childMethodImpl.isAnnotationPresent(ann.annotationType()))
continue;
if (! annType.isAnnotationPresent(Inherited.class))
continue;
childMethodImpl.addAnnotation(ann);
}
}
}
}
// ejb/4050
if (cl.isInterface()) {
for (Type superInterface : cl.getGenericInterfaces()) {
if (superInterface instanceof Class<?>)
introspectMethods((Class<?>) superInterface, null);
else {
BaseType type = BaseType.create(superInterface, null, null,
ClassFill.SOURCE);
introspectMethods(type.getRawClass(), type.getParamMap());
}
}
}
else {
introspectParentMethods(_parentType);
}
}
private boolean isMethodOverride(Method parentMethod,
AnnotatedMethod<?> childMethod)
{
if (childMethod == null)
return false;
if (Modifier.isPrivate(parentMethod.getModifiers()))
return false;
if (Modifier.isPublic(parentMethod.getModifiers())
|| Modifier.isProtected(parentMethod.getModifiers())) {
return true;
}
String parentPkg = parentMethod.getDeclaringClass().getPackage().getName();
String childPkg = childMethod.getJavaMember().getDeclaringClass().getPackage().getName();
// ioc/011b
return parentPkg.equals(childPkg);
}
private void introspectParentMethods(AnnotatedType<?> parentType)
{
if (parentType == null)
return;
for (AnnotatedMethod<?> annMethod : parentType.getMethods()) {
Method javaMethod = annMethod.getJavaMember();
if (hasBeanAnnotation(javaMethod)
|| ! Modifier.isPrivate(javaMethod.getModifiers())) {
AnnotatedMethod<?> childMethod = null;
if (! Modifier.isPrivate(javaMethod.getModifiers()))
childMethod = AnnotatedTypeUtil.findMethod(_methodSet, javaMethod);
if (! isMethodOverride(javaMethod, childMethod)) {
_methodSet.add((AnnotatedMethod<? super T>) annMethod);
}
/*
else if (! isParent)
continue;
*/
else if (childMethod instanceof AnnotatedMethodImpl<?>){
AnnotatedMethodImpl<?> childMethodImpl
= (AnnotatedMethodImpl<?>) childMethod;
// ejb/1290
for (Annotation ann : annMethod.getAnnotations()) {
Class<?> annType = ann.annotationType();
if (childMethodImpl.isAnnotationPresent(ann.annotationType()))
continue;
// ioc/0a02
if (! annType.isAnnotationPresent(Inherited.class)
|| annType.equals(PostConstruct.class)
|| annType.equals(PreDestroy.class))
continue;
childMethodImpl.addAnnotation(ann);
}
}
}
}
}
private void introspectInheritedAnnotations(Class<?> cl)
{
introspectInheritedAnnotations(cl, false, false);
}
private void introspectInheritedAnnotations(Class<?> cl,
boolean isScope,
boolean isQualifier)
{
if (cl == null)
return;
for (Annotation ann : cl.getDeclaredAnnotations()) {
Class<? extends Annotation> annType = ann.annotationType();
if ((ann.annotationType().isAnnotationPresent(Scope.class)
|| ann.annotationType().isAnnotationPresent(NormalScope.class))) {
if (isScope)
continue;
isScope = true;
}
if (ann.annotationType().isAnnotationPresent(Qualifier.class)) {
if (isQualifier)
continue;
isQualifier = true;
}
if (cl == _javaClass)
continue;
// ejb/1062
if (! annType.isAnnotationPresent(Inherited.class)) {
continue;
}
if (isAnnotationPresent(annType)) {
continue;
}
/*
if (ann.annotationType().isAnnotationPresent(DeploymentType.class)
&& hasMetaAnnotation(getAnnotations(), DeploymentType.class)) {
continue;
}
*/
addAnnotation(ann);
}
introspectInheritedAnnotations(cl.getSuperclass(), isScope, isQualifier);
}
private void introspectSpecializesAnnotations(Class<?> cl)
{
Class<?> parentClass = cl.getSuperclass();
if (parentClass == null)
return;
if (cl.isAnnotationPresent(Named.class))
throw new ConfigException(L.l("'{0}' is an invalid @Specializes bean because it has a @Named annotation.",
cl.getName()));
if (isMetaAnnotationPresent(cl.getAnnotations(), Qualifier.class)) {
// isQualifierPresent = true;
/*
throw new ConfigException(L.l("'{0}' is an invalid @Specializes bean because it has a @Qualifier annotation.",
cl.getName()));
*/
}
for (Annotation ann : parentClass.getDeclaredAnnotations()) {
Class<? extends Annotation> annType = ann.annotationType();
/*
if (! isQualifierPresent && annType.isAnnotationPresent(Qualifier.class)) {
addAnnotation(ann);
}
*/
if (annType.isAnnotationPresent(Qualifier.class)) {
addAnnotation(ann);
}
else if (Named.class.equals(annType)) {
addAnnotation(ann);
}
}
}
private boolean hasBeanAnnotation(Method method)
{
if (hasBeanAnnotation(method.getAnnotations()))
return true;
Annotation [][]paramAnn = method.getParameterAnnotations();
if (paramAnn != null) {
for (int i = 0; i < paramAnn.length; i++) {
if (hasBeanAnnotation(paramAnn[i]))
return true;
}
}
return false;
}
private boolean hasBeanAnnotation(Annotation []annotations)
{
if (annotations == null)
return false;
for (Annotation ann : annotations) {
if (isBeanAnnotation(ann.annotationType()))
return true;
for (Annotation metaAnn : ann.annotationType().getAnnotations()) {
if (isBeanAnnotation(metaAnn.annotationType()))
return true;
}
}
return false;
}
private boolean isMetaAnnotationPresent(Annotation []annotations,
Class<? extends Annotation> metaAnnType)
{
if (annotations == null)
return false;
for (Annotation ann : annotations) {
if (ann.annotationType().isAnnotationPresent(metaAnnType))
return true;
}
return false;
}
private boolean isBeanAnnotation(Class<?> annType)
{
String name = annType.getName();
return name.startsWith("javax.");
}
}