| package com.fasterxml.jackson.databind.introspect; |
| |
| import java.lang.reflect.Field; |
| import java.lang.reflect.Modifier; |
| import java.util.*; |
| |
| import com.fasterxml.jackson.databind.AnnotationIntrospector; |
| import com.fasterxml.jackson.databind.JavaType; |
| import com.fasterxml.jackson.databind.introspect.ClassIntrospector.MixInResolver; |
| import com.fasterxml.jackson.databind.type.TypeFactory; |
| import com.fasterxml.jackson.databind.util.ClassUtil; |
| |
| public class AnnotatedFieldCollector |
| extends CollectorBase |
| { |
| // // // Configuration |
| |
| private final TypeFactory _typeFactory; |
| private final MixInResolver _mixInResolver; |
| |
| // // // Collected state |
| |
| AnnotatedFieldCollector(AnnotationIntrospector intr, |
| TypeFactory types, MixInResolver mixins) |
| { |
| super(intr); |
| _typeFactory = types; |
| _mixInResolver = (intr == null) ? null : mixins; |
| } |
| |
| public static List<AnnotatedField> collectFields(AnnotationIntrospector intr, |
| TypeResolutionContext tc, |
| MixInResolver mixins, TypeFactory types, |
| JavaType type) |
| { |
| return new AnnotatedFieldCollector(intr, types, mixins).collect(tc, type); |
| } |
| |
| List<AnnotatedField> collect(TypeResolutionContext tc, JavaType type) |
| { |
| Map<String,FieldBuilder> foundFields = _findFields(tc, type, null); |
| if (foundFields == null) { |
| return Collections.emptyList(); |
| } |
| List<AnnotatedField> result = new ArrayList<>(foundFields.size()); |
| for (FieldBuilder b : foundFields.values()) { |
| result.add(b.build()); |
| } |
| return result; |
| } |
| |
| private Map<String,FieldBuilder> _findFields(TypeResolutionContext tc, |
| JavaType type, Map<String,FieldBuilder> fields) |
| { |
| // First, a quick test: we only care for regular classes (not interfaces, |
| //primitive types etc), except for Object.class. A simple check to rule out |
| // other cases is to see if there is a super class or not. |
| JavaType parent = type.getSuperClass(); |
| if (parent == null) { |
| return fields; |
| } |
| final Class<?> cls = type.getRawClass(); |
| // Let's add super-class' fields first, then ours. |
| fields = _findFields(new TypeResolutionContext.Basic(_typeFactory, parent.getBindings()), |
| parent, fields); |
| for (Field f : ClassUtil.getDeclaredFields(cls)) { |
| // static fields not included (transients are at this point, filtered out later) |
| if (!_isIncludableField(f)) { |
| continue; |
| } |
| // Ok now: we can (and need) not filter out ignorable fields at this point; partly |
| // because mix-ins haven't been added, and partly because logic can be done |
| // when determining get/settability of the field. |
| if (fields == null) { |
| fields = new LinkedHashMap<>(); |
| } |
| FieldBuilder b = new FieldBuilder(tc, f); |
| if (_intr != null) { |
| b.annotations = collectAnnotations(b.annotations, f.getDeclaredAnnotations()); |
| } |
| fields.put(f.getName(), b); |
| } |
| // And then... any mix-in overrides? |
| if (_mixInResolver != null) { |
| Class<?> mixin = _mixInResolver.findMixInClassFor(cls); |
| if (mixin != null) { |
| _addFieldMixIns(mixin, cls, fields); |
| } |
| } |
| return fields; |
| } |
| |
| /** |
| * Method called to add field mix-ins from given mix-in class (and its fields) |
| * into already collected actual fields (from introspected classes and their |
| * super-classes) |
| */ |
| private void _addFieldMixIns(Class<?> mixInCls, Class<?> targetClass, |
| Map<String,FieldBuilder> fields) |
| { |
| List<Class<?>> parents = ClassUtil.findSuperClasses(mixInCls, targetClass, true); |
| for (Class<?> mixin : parents) { |
| for (Field mixinField : ClassUtil.getDeclaredFields(mixin)) { |
| // there are some dummy things (static, synthetic); better ignore |
| if (!_isIncludableField(mixinField)) { |
| continue; |
| } |
| String name = mixinField.getName(); |
| // anything to mask? (if not, quietly ignore) |
| FieldBuilder b = fields.get(name); |
| if (b != null) { |
| b.annotations = collectAnnotations(b.annotations, mixinField.getDeclaredAnnotations()); |
| } |
| } |
| } |
| } |
| |
| private boolean _isIncludableField(Field f) |
| { |
| // Most likely synthetic fields, if any, are to be skipped similar to methods |
| if (f.isSynthetic()) { |
| return false; |
| } |
| // Static fields are never included. Transient are (since 2.6), for |
| // purpose of propagating removal |
| int mods = f.getModifiers(); |
| if (Modifier.isStatic(mods)) { |
| return false; |
| } |
| return true; |
| } |
| |
| private final static class FieldBuilder { |
| public final TypeResolutionContext typeContext; |
| public final Field field; |
| |
| public AnnotationCollector annotations; |
| |
| public FieldBuilder(TypeResolutionContext tc, Field f) { |
| typeContext = tc; |
| field = f; |
| annotations = AnnotationCollector.emptyCollector(); |
| } |
| |
| public AnnotatedField build() { |
| return new AnnotatedField(typeContext, field, annotations.asAnnotationMap()); |
| } |
| } |
| } |