1 package com.fasterxml.jackson.databind.introspect; 2 3 import java.lang.reflect.Field; 4 import java.lang.reflect.Modifier; 5 import java.util.*; 6 7 import com.fasterxml.jackson.databind.AnnotationIntrospector; 8 import com.fasterxml.jackson.databind.JavaType; 9 import com.fasterxml.jackson.databind.introspect.ClassIntrospector.MixInResolver; 10 import com.fasterxml.jackson.databind.type.TypeFactory; 11 import com.fasterxml.jackson.databind.util.ClassUtil; 12 13 public class AnnotatedFieldCollector 14 extends CollectorBase 15 { 16 // // // Configuration 17 18 private final TypeFactory _typeFactory; 19 private final MixInResolver _mixInResolver; 20 21 /** 22 * @since 2.11 23 */ 24 private final boolean _collectAnnotations; 25 26 // // // Collected state 27 AnnotatedFieldCollector(AnnotationIntrospector intr, TypeFactory types, MixInResolver mixins, boolean collectAnnotations)28 AnnotatedFieldCollector(AnnotationIntrospector intr, 29 TypeFactory types, MixInResolver mixins, boolean collectAnnotations) 30 { 31 super(intr); 32 _typeFactory = types; 33 _mixInResolver = (intr == null) ? null : mixins; 34 _collectAnnotations = collectAnnotations; 35 } 36 collectFields(AnnotationIntrospector intr, TypeResolutionContext tc, MixInResolver mixins, TypeFactory types, JavaType type, boolean collectAnnotations)37 public static List<AnnotatedField> collectFields(AnnotationIntrospector intr, 38 TypeResolutionContext tc, 39 MixInResolver mixins, TypeFactory types, 40 JavaType type, boolean collectAnnotations) 41 { 42 return new AnnotatedFieldCollector(intr, types, mixins, collectAnnotations) 43 .collect(tc, type); 44 } 45 collect(TypeResolutionContext tc, JavaType type)46 List<AnnotatedField> collect(TypeResolutionContext tc, JavaType type) 47 { 48 Map<String,FieldBuilder> foundFields = _findFields(tc, type, null); 49 if (foundFields == null) { 50 return Collections.emptyList(); 51 } 52 List<AnnotatedField> result = new ArrayList<>(foundFields.size()); 53 for (FieldBuilder b : foundFields.values()) { 54 result.add(b.build()); 55 } 56 return result; 57 } 58 _findFields(TypeResolutionContext tc, JavaType type, Map<String,FieldBuilder> fields)59 private Map<String,FieldBuilder> _findFields(TypeResolutionContext tc, 60 JavaType type, Map<String,FieldBuilder> fields) 61 { 62 // First, a quick test: we only care for regular classes (not interfaces, 63 //primitive types etc), except for Object.class. A simple check to rule out 64 // other cases is to see if there is a super class or not. 65 JavaType parent = type.getSuperClass(); 66 if (parent == null) { 67 return fields; 68 } 69 final Class<?> cls = type.getRawClass(); 70 // Let's add super-class' fields first, then ours. 71 fields = _findFields(new TypeResolutionContext.Basic(_typeFactory, parent.getBindings()), 72 parent, fields); 73 for (Field f : cls.getDeclaredFields()) { 74 // static fields not included (transients are at this point, filtered out later) 75 if (!_isIncludableField(f)) { 76 continue; 77 } 78 // Ok now: we can (and need) not filter out ignorable fields at this point; partly 79 // because mix-ins haven't been added, and partly because logic can be done 80 // when determining get/settability of the field. 81 if (fields == null) { 82 fields = new LinkedHashMap<>(); 83 } 84 FieldBuilder b = new FieldBuilder(tc, f); 85 if (_collectAnnotations) { 86 b.annotations = collectAnnotations(b.annotations, f.getDeclaredAnnotations()); 87 } 88 fields.put(f.getName(), b); 89 } 90 // And then... any mix-in overrides? 91 if ((fields != null) && (_mixInResolver != null)) { 92 Class<?> mixin = _mixInResolver.findMixInClassFor(cls); 93 if (mixin != null) { 94 _addFieldMixIns(mixin, cls, fields); 95 } 96 } 97 return fields; 98 } 99 100 /** 101 * Method called to add field mix-ins from given mix-in class (and its fields) 102 * into already collected actual fields (from introspected classes and their 103 * super-classes) 104 */ _addFieldMixIns(Class<?> mixInCls, Class<?> targetClass, Map<String,FieldBuilder> fields)105 private void _addFieldMixIns(Class<?> mixInCls, Class<?> targetClass, 106 Map<String,FieldBuilder> fields) 107 { 108 List<Class<?>> parents = ClassUtil.findSuperClasses(mixInCls, targetClass, true); 109 for (Class<?> mixin : parents) { 110 for (Field mixinField : mixin.getDeclaredFields()) { 111 // there are some dummy things (static, synthetic); better ignore 112 if (!_isIncludableField(mixinField)) { 113 continue; 114 } 115 String name = mixinField.getName(); 116 // anything to mask? (if not, quietly ignore) 117 FieldBuilder b = fields.get(name); 118 if (b != null) { 119 b.annotations = collectAnnotations(b.annotations, mixinField.getDeclaredAnnotations()); 120 } 121 } 122 } 123 } 124 _isIncludableField(Field f)125 private boolean _isIncludableField(Field f) 126 { 127 // Most likely synthetic fields, if any, are to be skipped similar to methods 128 if (f.isSynthetic()) { 129 return false; 130 } 131 // Static fields are never included. Transient are (since 2.6), for 132 // purpose of propagating removal 133 int mods = f.getModifiers(); 134 if (Modifier.isStatic(mods)) { 135 return false; 136 } 137 return true; 138 } 139 140 private final static class FieldBuilder { 141 public final TypeResolutionContext typeContext; 142 public final Field field; 143 144 public AnnotationCollector annotations; 145 FieldBuilder(TypeResolutionContext tc, Field f)146 public FieldBuilder(TypeResolutionContext tc, Field f) { 147 typeContext = tc; 148 field = f; 149 annotations = AnnotationCollector.emptyCollector(); 150 } 151 build()152 public AnnotatedField build() { 153 return new AnnotatedField(typeContext, field, annotations.asAnnotationMap()); 154 } 155 } 156 } 157