package com.fasterxml.classmate;

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.Type;
import java.util.*;

import com.fasterxml.classmate.members.*;
import com.fasterxml.classmate.util.MethodKey;

Class that contains information about fully resolved members of a type; resolution meaning that masking is handled for methods, and all inheritable annotations are flattened using optional overrides as well ("mix-in annotations"). Instances are created by MemberResolver.

Note that instances are not thread-safe, as the expectation is that instances will not be shared (unlike raw members or resolved types)

/** * Class that contains information about fully resolved members of a * type; resolution meaning that masking is handled for methods, and * all inheritable annotations are flattened using optional overrides * as well ("mix-in annotations"). * Instances are created by {@link com.fasterxml.classmate.MemberResolver}. *<p> * Note that instances are not thread-safe, as the expectation is that instances * will not be shared (unlike raw members or resolved types) */
public class ResolvedTypeWithMembers { private final static ResolvedType[] NO_RESOLVED_TYPES = new ResolvedType[0]; private final static ResolvedMethod[] NO_RESOLVED_METHODS = new ResolvedMethod[0]; private final static ResolvedField[] NO_RESOLVED_FIELDS = new ResolvedField[0]; private final static ResolvedConstructor[] NO_RESOLVED_CONSTRUCTORS = new ResolvedConstructor[0];
Default annotation configuration is to ignore all annotations types.
/** * Default annotation configuration is to ignore all annotations types. */
protected final static AnnotationConfiguration DEFAULT_ANNOTATION_CONFIG = new AnnotationConfiguration.StdConfiguration(AnnotationInclusion.DONT_INCLUDE);
Need to be able to resolve member types still
/** * Need to be able to resolve member types still */
protected final TypeResolver _typeResolver;
Handler for resolving annotation information
/** * Handler for resolving annotation information */
protected final AnnotationHandler _annotationHandler;
Leaf of the type hierarchy, i.e. type from which this hierarchy was generated.
/** * Leaf of the type hierarchy, i.e. type from which this hierarchy * was generated. */
protected final HierarchicType _mainType;
All types that hierarchy contains, in order of increasing precedence (that is, later entries override members of earlier members)
/** * All types that hierarchy contains, in order of increasing precedence * (that is, later entries override members of earlier members) */
protected final HierarchicType[] _types;
Filter to use for selecting fields to include
/** * Filter to use for selecting fields to include */
protected Filter<RawField> _fieldFilter;
Filter to use for selecting constructors to include
/** * Filter to use for selecting constructors to include */
protected Filter<RawConstructor> _constructorFilter;
Filter to use for selecting methods to include
/** * Filter to use for selecting methods to include */
protected Filter<RawMethod> _methodFilter; /* /********************************************************************** /* Lazily constructed members /********************************************************************** */ protected ResolvedMethod[] _staticMethods = null; protected ResolvedField[] _staticFields = null; protected ResolvedMethod[] _memberMethods = null; protected ResolvedField[] _memberFields = null; protected ResolvedConstructor[] _constructors = null; /* /********************************************************************** /* Life cycle at this point /********************************************************************** */ public ResolvedTypeWithMembers(TypeResolver typeResolver, AnnotationConfiguration annotationConfig, HierarchicType mainType, HierarchicType[] types, Filter<RawConstructor> constructorFilter, Filter<RawField> fieldFilter, Filter<RawMethod> methodFilter) { _typeResolver = typeResolver; _mainType = mainType; _types = types; if (annotationConfig == null) { annotationConfig = DEFAULT_ANNOTATION_CONFIG; } _annotationHandler = new AnnotationHandler(annotationConfig); _constructorFilter = constructorFilter; _fieldFilter = fieldFilter; _methodFilter = methodFilter; } /* /********************************************************************** /* Public API, access to component types /********************************************************************** */ public int size() { return _types.length; }
Accessor for getting full type hierarchy as priority-ordered list, from the lowest precedence to highest precedence (main type, its mix-in overrides)
/** * Accessor for getting full type hierarchy as priority-ordered list, from * the lowest precedence to highest precedence (main type, its mix-in overrides) */
public List<HierarchicType> allTypesAndOverrides() { return Arrays.asList(_types); }
Accessor for getting subset of type hierarchy which only contains main type and possible overrides (mix-ins) it has, but not supertypes or their overrides.
/** * Accessor for getting subset of type hierarchy which only contains main type * and possible overrides (mix-ins) it has, but not supertypes or their overrides. */
public List<HierarchicType> mainTypeAndOverrides() { List<HierarchicType> l = Arrays.asList(_types); int end = _mainType.getPriority() + 1; if (end < l.size()) { l = l.subList(0, end); } return l; }
Accessor for finding just overrides for the main type (if any).
/** * Accessor for finding just overrides for the main type (if any). */
public List<HierarchicType> overridesOnly() { int index = _mainType.getPriority(); if (index == 0) { return Collections.emptyList(); } List<HierarchicType> l = Arrays.asList(_types); return l.subList(0, index); } /* /********************************************************************** /* Public API, actual resolution of members /********************************************************************** */
Method for finding all static fields of the main type (except for ones possibly filtered out by filter) and applying annotation overrides, if any, to annotations.
Since:1.2.0
/** * Method for finding all static fields of the main type (except for ones * possibly filtered out by filter) and applying annotation overrides, if any, * to annotations. * * @since 1.2.0 */
public ResolvedField[] getStaticFields() { if (_staticFields == null) { _staticFields = resolveStaticFields(); } return _staticFields; }
Method for finding all static methods of the main type (except for ones possibly filtered out by filter) and applying annotation overrides, if any, to annotations.
/** * Method for finding all static methods of the main type (except for ones * possibly filtered out by filter) and applying annotation overrides, if any, * to annotations. */
public ResolvedMethod[] getStaticMethods() { if (_staticMethods == null) { _staticMethods = resolveStaticMethods(); } return _staticMethods; } public ResolvedField[] getMemberFields() { if (_memberFields == null) { _memberFields = resolveMemberFields(); } return _memberFields; } public ResolvedMethod[] getMemberMethods() { if (_memberMethods == null) { _memberMethods = resolveMemberMethods(); } return _memberMethods; } public ResolvedConstructor[] getConstructors() { if (_constructors == null) { _constructors = resolveConstructors(); } return _constructors; } /* /********************************************************************** /* Internal methods: actual resolution /********************************************************************** */
Method that will actually resolve full information (types, annotations) for constructors of the main type.
/** * Method that will actually resolve full information (types, annotations) * for constructors of the main type. */
protected ResolvedConstructor[] resolveConstructors() { // First get static methods for main type, filter LinkedHashMap<MethodKey, ResolvedConstructor> constructors = new LinkedHashMap<MethodKey, ResolvedConstructor>(); for (RawConstructor constructor : _mainType.getType().getConstructors()) { // no filter for constructors (yet?) if (_constructorFilter == null || _constructorFilter.include(constructor)) { constructors.put(constructor.createKey(), resolveConstructor(constructor)); } } // then apply overrides (mix-ins): for (HierarchicType type : overridesOnly()) { for (RawConstructor raw : type.getType().getConstructors()) { ResolvedConstructor constructor = constructors.get(raw.createKey()); // must override something, otherwise to ignore if (constructor != null) { for (Annotation ann : raw.getAnnotations()) { if (_annotationHandler.includeMethodAnnotation(ann)) { constructor.applyOverride(ann); } } // and parameter annotations Annotation[][] params = raw.getRawMember().getParameterAnnotations(); for (int i = 0; i < params.length; i++) { for (Annotation annotation : params[i]) { if (_annotationHandler.includeParameterAnnotation(annotation)) { constructor.applyParamOverride(i, annotation); } } } } } } if (constructors.size() == 0) { return NO_RESOLVED_CONSTRUCTORS; } return constructors.values().toArray(new ResolvedConstructor[0]); }
Method for fully resolving field definitions and associated annotations. Neither field definitions nor associated annotations inherit, but we may still need to add annotation overrides, as well as filter out filters and annotations that caller is not interested in.
/** * Method for fully resolving field definitions and associated annotations. * Neither field definitions nor associated annotations inherit, but we may * still need to add annotation overrides, as well as filter out filters * and annotations that caller is not interested in. */
protected ResolvedField[] resolveMemberFields() { LinkedHashMap<String, ResolvedField> fields = new LinkedHashMap<String, ResolvedField>(); /* Fields need different handling: must start from bottom; and annotations only get added * as overrides, never as defaults. And sub-classes fully mask fields. This makes * handling bit simpler than that of member methods. */ for (int typeIndex = _types.length; --typeIndex >= 0; ) { HierarchicType thisType = _types[typeIndex]; // If it's just a mix-in, add annotations as overrides if (thisType.isMixin()) { for (RawField raw : thisType.getType().getMemberFields()) { if ((_fieldFilter != null) && !_fieldFilter.include(raw)) { continue; } ResolvedField field = fields.get(raw.getName()); if (field != null) { for (Annotation ann : raw.getAnnotations()) { if (_annotationHandler.includeMethodAnnotation(ann)) { field.applyOverride(ann); } } } } } else { // If actual type, add fields, masking whatever might have existed before: for (RawField field : thisType.getType().getMemberFields()) { if ((_fieldFilter != null) && !_fieldFilter.include(field)) { continue; } fields.put(field.getName(), resolveField(field)); } } } // and that's it? if (fields.size() == 0) { return NO_RESOLVED_FIELDS; } return fields.values().toArray(new ResolvedField[0]); } protected ResolvedMethod[] resolveMemberMethods() { LinkedHashMap<MethodKey, ResolvedMethod> methods = new LinkedHashMap<MethodKey, ResolvedMethod>(); LinkedHashMap<MethodKey, Annotations> overrides = new LinkedHashMap<MethodKey, Annotations>(); LinkedHashMap<MethodKey, Annotations[]> paramOverrides = new LinkedHashMap<MethodKey, Annotations[]>(); /* Member methods are handled from top to bottom; and annotations are tracked * alongside (for overrides), as well as "merged down" for inheritable * annotations. */ for (HierarchicType type : allTypesAndOverrides()) { for (RawMethod method : type.getType().getMemberMethods()) { // First: ignore methods caller is not interested if (_methodFilter != null && !_methodFilter.include(method)) { continue; } MethodKey key = method.createKey(); ResolvedMethod old = methods.get(key); // Ok, now, mix-ins only contribute annotations; whereas 'real' types methods if (type.isMixin()) { // mix-in: only get annotations for (Annotation ann : method.getAnnotations()) { // If already have a method, must be inheritable to include if (old != null) { if (!methodCanInherit(ann)) { continue; } // and if so, apply as default (i.e. do not override) old.applyDefault(ann); } else { // If no method, need to add to annotation override map Annotations oldAnn = overrides.get(key); if (oldAnn == null) { oldAnn = new Annotations(); oldAnn.add(ann); overrides.put(key, oldAnn); } else { oldAnn.addAsDefault(ann); } } } // override argument annotations final Annotation[][] argAnnotations = method.getRawMember().getParameterAnnotations(); if (old == null) { // no method (yet), add argument annotations to override map Annotations[] oldParamAnns = paramOverrides.get(key); if (oldParamAnns == null) { // no existing argument annotations for method oldParamAnns = new Annotations[argAnnotations.length]; for (int i = 0; i < argAnnotations.length; i++) { oldParamAnns[i] = new Annotations(); for (final Annotation annotation : argAnnotations[i]) { if (parameterCanInherit(annotation)) { oldParamAnns[i].add(annotation); } } } paramOverrides.put(key, oldParamAnns); } else { for (int i = 0; i < argAnnotations.length; i++) { for (final Annotation annotation : argAnnotations[i]) { if (parameterCanInherit(annotation)) { oldParamAnns[i].addAsDefault(annotation); } } } } } else { // already have a method, apply argument annotations as defaults for (int i = 0; i < argAnnotations.length; i++) { for (final Annotation annotation : argAnnotations[i]) { if (parameterCanInherit(annotation)) { old.applyParamDefault(i, annotation); } } } } } else { // "real" methods; add if not present, possibly add defaults as well if (old == null) { // new one to add ResolvedMethod newMethod = resolveMethod(method); methods.put(key, newMethod); // But we may also have annotation overrides, so: Annotations overrideAnn = overrides.get(key); if (overrideAnn != null) { newMethod.applyOverrides(overrideAnn); } // and apply parameter annotation overrides Annotations[] annotations = paramOverrides.get(key); if (annotations != null) { for (int i = 0; i < annotations.length; i++) { newMethod.applyParamOverrides(i, annotations[i]); } } } else { // method masked by something else? can only contribute annotations for (Annotation ann : method.getAnnotations()) { if (methodCanInherit(ann)) { old.applyDefault(ann); } } // and parameter annotations final Annotation[][] parameterAnnotations = method.getRawMember().getParameterAnnotations(); for (int i = 0; i < parameterAnnotations.length; i++) { for (final Annotation annotation : parameterAnnotations[i]) { if (parameterCanInherit(annotation)) { old.applyParamDefault(i, annotation); } } } } } } } if (methods.size() == 0) { return NO_RESOLVED_METHODS; } return methods.values().toArray(new ResolvedMethod[0]); }
Method for fully resolving static field definitions and associated annotations. Neither field definitions nor associated annotations inherit, but we may still need to add annotation overrides, as well as filter out filters and annotations that caller is not interested in.
Since:1.2.0
/** * Method for fully resolving static field definitions and associated annotations. * Neither field definitions nor associated annotations inherit, but we may * still need to add annotation overrides, as well as filter out filters * and annotations that caller is not interested in. * * @since 1.2.0 */
protected ResolvedField[] resolveStaticFields() { // First get static methods for main type, filter LinkedHashMap<String, ResolvedField> fields = new LinkedHashMap<String, ResolvedField>(); for (RawField field : _mainType.getType().getStaticFields()) { if (_fieldFilter == null || _fieldFilter.include(field)) { fields.put(field.getName(), resolveField(field)); } } // then apply overrides (mix-ins): for (HierarchicType type : overridesOnly()) { for (RawField raw : type.getType().getStaticFields()) { ResolvedField field = fields.get(raw.getName()); // must override something, otherwise to ignore if (field != null) { for (Annotation ann : raw.getAnnotations()) { if (_annotationHandler.includeFieldAnnotation(ann)) { field.applyOverride(ann); } } } } } // and that's it? if (fields.isEmpty()) { return NO_RESOLVED_FIELDS; } return fields.values().toArray(new ResolvedField[0]); }
Method that will actually resolve full information (types, annotations) for static methods, using configured filter.
/** * Method that will actually resolve full information (types, annotations) * for static methods, using configured filter. */
protected ResolvedMethod[] resolveStaticMethods() { // First get static methods for main type, filter LinkedHashMap<MethodKey, ResolvedMethod> methods = new LinkedHashMap<MethodKey, ResolvedMethod>(); for (RawMethod method : _mainType.getType().getStaticMethods()) { if (_methodFilter == null || _methodFilter.include(method)) { methods.put(method.createKey(), resolveMethod(method)); } } // then apply overrides (mix-ins): for (HierarchicType type : overridesOnly()) { for (RawMethod raw : type.getType().getStaticMethods()) { ResolvedMethod method = methods.get(raw.createKey()); // must override something, otherwise to ignore if (method != null) { for (Annotation ann : raw.getAnnotations()) { if (_annotationHandler.includeMethodAnnotation(ann)) { method.applyOverride(ann); } } } } } if (methods.size() == 0) { return NO_RESOLVED_METHODS; } return methods.values().toArray(new ResolvedMethod[0]); } /* /********************************************************************** /* Helper methods /********************************************************************** */
Method for resolving individual constructor completely
/** * Method for resolving individual constructor completely */
protected ResolvedConstructor resolveConstructor(RawConstructor raw) { final ResolvedType context = raw.getDeclaringType(); final TypeBindings bindings = context.getTypeBindings(); Constructor<?> ctor = raw.getRawMember(); Type[] rawTypes = ctor.getGenericParameterTypes(); ResolvedType[] argTypes; if (rawTypes == null || rawTypes.length == 0) { argTypes = NO_RESOLVED_TYPES; } else { argTypes = new ResolvedType[rawTypes.length]; for (int i = 0, len = rawTypes.length; i < len; ++i) { argTypes[i] = _typeResolver.resolve(bindings, rawTypes[i]); } } // And then annotations Annotations anns = new Annotations(); for (Annotation ann : ctor.getAnnotations()) { if (_annotationHandler.includeConstructorAnnotation(ann)) { anns.add(ann); } } ResolvedConstructor constructor = new ResolvedConstructor(context, anns, ctor, argTypes); // and parameter annotations Annotation[][] annotations = ctor.getParameterAnnotations(); for (int i = 0; i < argTypes.length; i++) { for (Annotation ann : annotations[i]) { constructor.applyParamOverride(i, ann); } } return constructor; }
Method for resolving individual field completely
/** * Method for resolving individual field completely */
protected ResolvedField resolveField(RawField raw) { final ResolvedType context = raw.getDeclaringType(); Field field = raw.getRawMember(); ResolvedType type = _typeResolver.resolve(context.getTypeBindings(), field.getGenericType()); // And then annotations Annotations anns = new Annotations(); for (Annotation ann : field.getAnnotations()) { if (_annotationHandler.includeFieldAnnotation(ann)) { anns.add(ann); } } return new ResolvedField(context, anns, field, type); }
Method for resolving individual method completely
/** * Method for resolving individual method completely */
protected ResolvedMethod resolveMethod(RawMethod raw) { final ResolvedType context = raw.getDeclaringType(); final TypeBindings bindings = context.getTypeBindings(); Method m = raw.getRawMember(); Type rawType = m.getGenericReturnType(); ResolvedType rt = (rawType == Void.TYPE) ? null : _typeResolver.resolve(bindings, rawType); Type[] rawTypes = m.getGenericParameterTypes(); ResolvedType[] argTypes; if (rawTypes == null || rawTypes.length == 0) { argTypes = NO_RESOLVED_TYPES; } else { argTypes = new ResolvedType[rawTypes.length]; for (int i = 0, len = rawTypes.length; i < len; ++i) { argTypes[i] = _typeResolver.resolve(bindings, rawTypes[i]); } } // And then annotations Annotations anns = new Annotations(); for (Annotation ann : m.getAnnotations()) { if (_annotationHandler.includeMethodAnnotation(ann)) { anns.add(ann); } } ResolvedMethod method = new ResolvedMethod(context, anns, m, rt, argTypes); // and argument annotations Annotation[][] annotations = m.getParameterAnnotations(); for (int i = 0; i < argTypes.length; i++) { for (Annotation ann : annotations[i]) { method.applyParamOverride(i, ann); } } return method; } protected boolean methodCanInherit(Annotation annotation) { AnnotationInclusion annotationInclusion = _annotationHandler.methodInclusion(annotation); if (annotationInclusion == AnnotationInclusion.INCLUDE_AND_INHERIT_IF_INHERITED) { return annotation.annotationType().isAnnotationPresent(Inherited.class); } return (annotationInclusion == AnnotationInclusion.INCLUDE_AND_INHERIT); } protected boolean parameterCanInherit(Annotation annotation) { AnnotationInclusion annotationInclusion = _annotationHandler.parameterInclusion(annotation); if (annotationInclusion == AnnotationInclusion.INCLUDE_AND_INHERIT_IF_INHERITED) { return annotation.annotationType().isAnnotationPresent(Inherited.class); } return (annotationInclusion == AnnotationInclusion.INCLUDE_AND_INHERIT); } /* /********************************************************************** /* Helper types /********************************************************************** */
Helper class we use to reduce number of calls to AnnotationConfiguration; mostly because determination may be expensive.
/** * Helper class we use to reduce number of calls to {@link AnnotationConfiguration}; * mostly because determination may be expensive. */
private final static class AnnotationHandler { private final AnnotationConfiguration _annotationConfig; private HashMap<Class<? extends Annotation>, AnnotationInclusion> _fieldInclusions; private HashMap<Class<? extends Annotation>, AnnotationInclusion> _constructorInclusions; private HashMap<Class<? extends Annotation>, AnnotationInclusion> _methodInclusions; private HashMap<Class<? extends Annotation>, AnnotationInclusion> _parameterInclusions; public AnnotationHandler(AnnotationConfiguration annotationConfig) { _annotationConfig = annotationConfig; } public boolean includeConstructorAnnotation(Annotation ann) { Class<? extends Annotation> annType = ann.annotationType(); if (_constructorInclusions == null) { _constructorInclusions = new HashMap<Class<? extends Annotation>, AnnotationInclusion>(); } else { AnnotationInclusion incl = _constructorInclusions.get(annType); if (incl != null) { return (incl != AnnotationInclusion.DONT_INCLUDE); } } AnnotationInclusion incl = _annotationConfig.getInclusionForConstructor(annType); _constructorInclusions.put(annType, incl); return (incl != AnnotationInclusion.DONT_INCLUDE); } public boolean includeFieldAnnotation(Annotation ann) { Class<? extends Annotation> annType = ann.annotationType(); if (_fieldInclusions == null) { _fieldInclusions = new HashMap<Class<? extends Annotation>, AnnotationInclusion>(); } else { AnnotationInclusion incl = _fieldInclusions.get(annType); if (incl != null) { return (incl != AnnotationInclusion.DONT_INCLUDE); } } AnnotationInclusion incl = _annotationConfig.getInclusionForField(annType); _fieldInclusions.put(annType, incl); return (incl != AnnotationInclusion.DONT_INCLUDE); } public boolean includeMethodAnnotation(Annotation ann) { return methodInclusion(ann) != AnnotationInclusion.DONT_INCLUDE; } public AnnotationInclusion methodInclusion(Annotation ann) { Class<? extends Annotation> annType = ann.annotationType(); if (_methodInclusions == null) { _methodInclusions = new HashMap<Class<? extends Annotation>, AnnotationInclusion>(); } else { AnnotationInclusion incl = _methodInclusions.get(annType); if (incl != null) { return incl; } } AnnotationInclusion incl = _annotationConfig.getInclusionForMethod(annType); _methodInclusions.put(annType, incl); return incl; } public boolean includeParameterAnnotation(Annotation ann) { return parameterInclusion(ann) != AnnotationInclusion.DONT_INCLUDE; } public AnnotationInclusion parameterInclusion(Annotation ann) { Class<? extends Annotation> annType = ann.annotationType(); if (_parameterInclusions == null) { _parameterInclusions = new HashMap<Class<? extends Annotation>, AnnotationInclusion>(); } else { AnnotationInclusion incl = _parameterInclusions.get(annType); if (incl != null) { return incl; } } AnnotationInclusion incl = _annotationConfig.getInclusionForParameter(annType); _parameterInclusions.put(annType, incl); return incl; } } }