/* * Copyright (c) 2006 Rogério Liesenfeld * This file is subject to the terms of the MIT license (see LICENSE.txt). */ package mockit.internal.injection; import java.lang.annotation.*; import java.lang.reflect.*; import java.util.*; import javax.annotation.*; import javax.ejb.*; import javax.enterprise.inject.*; import javax.enterprise.util.*; import javax.inject.*; import javax.persistence.*; import javax.servlet.*; import static java.lang.Character.toUpperCase; import static mockit.internal.util.ClassLoad.*; import static mockit.internal.reflection.MethodReflection.*; import static mockit.internal.reflection.ParameterReflection.*; import static mockit.internal.util.Utilities.*; public final class InjectionPoint { public enum KindOfInjectionPoint { NotAnnotated, Required, Optional, WithValue } @Nullable public static final Class<? extends Annotation> INJECT_CLASS; @Nullable private static final Class<? extends Annotation> INSTANCE_CLASS; @Nullable private static final Class<? extends Annotation> EJB_CLASS; @Nullable public static final Class<? extends Annotation> PERSISTENCE_UNIT_CLASS; @Nullable public static final Class<?> SERVLET_CLASS; @Nullable public static final Class<?> CONVERSATION_CLASS; static { INJECT_CLASS = searchTypeInClasspath("javax.inject.Inject"); INSTANCE_CLASS = searchTypeInClasspath("javax.enterprise.inject.Instance"); EJB_CLASS = searchTypeInClasspath("javax.ejb.EJB"); PERSISTENCE_UNIT_CLASS = searchTypeInClasspath("javax.persistence.PersistenceUnit"); SERVLET_CLASS = searchTypeInClasspath("javax.servlet.Servlet"); CONVERSATION_CLASS = searchTypeInClasspath("javax.enterprise.context.Conversation"); } @Nonnull public final Type type; @Nullable public final String name; @Nullable private final String normalizedName; public InjectionPoint(@Nonnull Type type) { this(type, null); } public InjectionPoint(@Nonnull Type type, @Nullable String name) { this.type = type; this.name = name; normalizedName = name == null ? null : convertToLegalJavaIdentifierIfNeeded(name); } @Nonnull public static String convertToLegalJavaIdentifierIfNeeded(@Nonnull String name) { if (name.indexOf('-') < 0 && name.indexOf('.') < 0) { return name; } StringBuilder identifier = new StringBuilder(name); for (int i = name.length() - 1; i >= 0; i--) { char c = identifier.charAt(i); if (c == '-' || c == '.') { identifier.deleteCharAt(i); c = identifier.charAt(i); identifier.setCharAt(i, toUpperCase(c)); } } return identifier.toString(); } @Override @SuppressWarnings("EqualsWhichDoesntCheckParameterClass") public boolean equals(Object other) { if (this == other) return true; InjectionPoint otherIP = (InjectionPoint) other; if (type instanceof TypeVariable<?> || otherIP.type instanceof TypeVariable<?>) { return false; } String thisName = normalizedName; String otherName = otherIP.normalizedName; if (thisName != null && !thisName.equals(otherName)) { return false; } Class<?> thisClass = getClassType(type); Class<?> otherClass = getClassType(otherIP.type); return thisClass.isAssignableFrom(otherClass); } @Override public int hashCode() { return 31 * type.hashCode() + (normalizedName != null ? normalizedName.hashCode() : 0); } public static boolean isServlet(@Nonnull Class<?> aClass) { return SERVLET_CLASS != null && Servlet.class.isAssignableFrom(aClass); } @Nonnull public static Object wrapInProviderIfNeeded(@Nonnull Type type, @Nonnull final Object value) { if (INJECT_CLASS != null && type instanceof ParameterizedType && !(value instanceof Provider)) { Type parameterizedType = ((ParameterizedType) type).getRawType(); if (parameterizedType == Provider.class) { return new Provider<Object>() { @Override public Object get() { return value; } }; } if (INSTANCE_CLASS != null && parameterizedType == Instance.class) { @SuppressWarnings("unchecked") List<Object> values = (List<Object>) value; return new Listed(values); } } return value; } private static final class Listed implements Instance<Object> { @Nonnull private final List<Object> instances; Listed(@Nonnull List<Object> instances) { this.instances = instances; } @Override public Instance<Object> select(Annotation... annotations) { return null; } @Override public <U> Instance<U> select(Class<U> uClass, Annotation... annotations) { return null; } @Override public <U> Instance<U> select(TypeLiteral<U> tl, Annotation... annotations) { return null; } @Override public boolean isUnsatisfied() { return false; } @Override public boolean isAmbiguous() { return false; } @Override public void destroy(Object instance) {} @Override public Iterator<Object> iterator() { return instances.iterator(); } @Override public Object get() { throw new RuntimeException("Unexpected"); } } @Nonnull public static KindOfInjectionPoint kindOfInjectionPoint(@Nonnull AccessibleObject fieldOrConstructor) { Annotation[] annotations = fieldOrConstructor.getDeclaredAnnotations(); if (annotations.length == 0) { return KindOfInjectionPoint.NotAnnotated; } if (INJECT_CLASS != null && isAnnotated(annotations, Inject.class)) { return KindOfInjectionPoint.Required; } KindOfInjectionPoint kind = isAutowired(annotations); if (kind != KindOfInjectionPoint.NotAnnotated || fieldOrConstructor instanceof Constructor) { return kind; } if (hasValue(annotations)) { return KindOfInjectionPoint.WithValue; } if (isRequired(annotations)) { return KindOfInjectionPoint.Required; } return KindOfInjectionPoint.NotAnnotated; } private static boolean isAnnotated( @Nonnull Annotation[] declaredAnnotations, @Nonnull Class<? extends Annotation> annotationOfInterest) { Annotation annotation = getAnnotation(declaredAnnotations, annotationOfInterest); return annotation != null; } @Nullable private static <A extends Annotation> A getAnnotation( @Nonnull Annotation[] declaredAnnotations, @Nonnull Class<A> annotationOfInterest) { for (Annotation declaredAnnotation : declaredAnnotations) { if (declaredAnnotation.annotationType() == annotationOfInterest) { //noinspection unchecked return (A) declaredAnnotation; } } return null; } @Nonnull private static KindOfInjectionPoint isAutowired(@Nonnull Annotation[] declaredAnnotations) { for (Annotation declaredAnnotation : declaredAnnotations) { Class<? extends Annotation> annotationType = declaredAnnotation.annotationType(); if (annotationType.getName().endsWith(".Autowired")) { Boolean required = invokePublicIfAvailable(annotationType, declaredAnnotation, "required", NO_PARAMETERS); return required != null && required ? KindOfInjectionPoint.Required : KindOfInjectionPoint.Optional; } } return KindOfInjectionPoint.NotAnnotated; } private static boolean hasValue(@Nonnull Annotation[] declaredAnnotations) { for (Annotation declaredAnnotation : declaredAnnotations) { Class<? extends Annotation> annotationType = declaredAnnotation.annotationType(); if (annotationType.getName().endsWith(".Value")) { return true; } } return false; } private static boolean isRequired(@Nonnull Annotation[] annotations) { return isAnnotated(annotations, Resource.class) || EJB_CLASS != null && isAnnotated(annotations, EJB.class) || PERSISTENCE_UNIT_CLASS != null && ( isAnnotated(annotations, PersistenceContext.class) || isAnnotated(annotations, PersistenceUnit.class) ); } @Nullable public static Object getValueFromAnnotation(@Nonnull Field field) { String value = null; for (Annotation declaredAnnotation : field.getDeclaredAnnotations()) { Class<? extends Annotation> annotationType = declaredAnnotation.annotationType(); if (annotationType.getName().endsWith(".Value")) { value = invokePublicIfAvailable(annotationType, declaredAnnotation, "value", NO_PARAMETERS); break; } } Object convertedValue = convertFromString(field.getType(), value); return convertedValue; } @Nonnull public static Type getTypeOfInjectionPointFromVarargsParameter(@Nonnull Type parameterType) { if (parameterType instanceof Class<?>) { return ((Class<?>) parameterType).getComponentType(); } return ((GenericArrayType) parameterType).getGenericComponentType(); } @Nullable public static String getQualifiedName(@Nonnull Annotation[] annotationsOnInjectionPoint) { for (Annotation annotation : annotationsOnInjectionPoint) { Class<? extends Annotation> annotationType = annotation.annotationType(); String annotationName = annotationType.getName(); if ("javax.annotation.Resource javax.ejb.EJB".contains(annotationName)) { String name = readAnnotationAttribute(annotation, "name"); if (name.isEmpty()) { name = readAnnotationAttribute(annotation, "lookup"); name = getNameFromJNDILookup(name); } return name; } if ("javax.inject.Named".equals(annotationName) || annotationName.endsWith(".Qualifier")) { String qualifiedName = readAnnotationAttribute(annotation, "value"); return qualifiedName; } } return null; } @Nonnull public static String getNameFromJNDILookup(@Nonnull String jndiLookup) { int p = jndiLookup.lastIndexOf('/'); if (p >= 0) { jndiLookup = jndiLookup.substring(p + 1); } return jndiLookup; } }