package com.fasterxml.jackson.databind.introspect; import java.lang.annotation.Annotation; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import com.fasterxml.jackson.databind.util.Annotations; /** * Helper class used to collect annotations to be stored as * {@link com.fasterxml.jackson.databind.util.Annotations} (like {@link AnnotationMap}). * * @since 2.9 */ public abstract class AnnotationCollector { protected final static Annotations NO_ANNOTATIONS = new NoAnnotations(); /** * Optional data to carry along */ protected final Object _data; protected AnnotationCollector(Object d) { _data = d; } public static Annotations emptyAnnotations() { return NO_ANNOTATIONS; } public static AnnotationCollector emptyCollector() { return EmptyCollector.instance; } public static AnnotationCollector emptyCollector(Object data) { return new EmptyCollector(data); } public abstract Annotations asAnnotations(); public abstract AnnotationMap asAnnotationMap(); public Object getData() { return _data; } /* /********************************************************** /* API /********************************************************** */ public abstract boolean isPresent(Annotation ann); public abstract AnnotationCollector addOrOverride(Annotation ann); /* /********************************************************** /* Collector implementations /********************************************************** */ static class EmptyCollector extends AnnotationCollector { public final static EmptyCollector instance = new EmptyCollector(null); EmptyCollector(Object data) { super(data); } @Override public Annotations asAnnotations() { return NO_ANNOTATIONS; } @Override public AnnotationMap asAnnotationMap() { return new AnnotationMap(); } @Override public boolean isPresent(Annotation ann) { return false; } @Override public AnnotationCollector addOrOverride(Annotation ann) { return new OneCollector(_data, ann.annotationType(), ann); } } static class OneCollector extends AnnotationCollector { private Class _type; private Annotation _value; public OneCollector(Object data, Class type, Annotation value) { super(data); _type = type; _value = value; } @Override public Annotations asAnnotations() { return new OneAnnotation(_type, _value); } @Override public AnnotationMap asAnnotationMap() { return AnnotationMap.of(_type, _value); } @Override public boolean isPresent(Annotation ann) { return ann.annotationType() == _type; } @Override public AnnotationCollector addOrOverride(Annotation ann) { final Class type = ann.annotationType(); // true override? Just replace in-place, return if (_type == type) { _value = ann; return this; } return new NCollector(_data, _type, _value, type, ann); } } static class NCollector extends AnnotationCollector { protected final HashMap,Annotation> _annotations; public NCollector(Object data, Class type1, Annotation value1, Class type2, Annotation value2) { super(data); _annotations = new HashMap<>(); _annotations.put(type1, value1); _annotations.put(type2, value2); } @Override public Annotations asAnnotations() { if (_annotations.size() == 2) { Iterator,Annotation>> it = _annotations.entrySet().iterator(); Map.Entry,Annotation> en1 = it.next(), en2 = it.next(); return new TwoAnnotations(en1.getKey(), en1.getValue(), en2.getKey(), en2.getValue()); } return new AnnotationMap(_annotations); } @Override public AnnotationMap asAnnotationMap() { AnnotationMap result = new AnnotationMap(); for (Annotation ann : _annotations.values()) { result.add(ann); } return result; } @Override public boolean isPresent(Annotation ann) { return _annotations.containsKey(ann.annotationType()); } @Override public AnnotationCollector addOrOverride(Annotation ann) { _annotations.put(ann.annotationType(), ann); return this; } } /* /********************************************************** /* Annotations implementations /********************************************************** */ /** * Immutable implementation for case where no annotations are associated with * an annotatable entity. * * @since 2.9 */ public static class NoAnnotations implements Annotations, java.io.Serializable { private static final long serialVersionUID = 1L; NoAnnotations() { } @Override public A get(Class cls) { return null; } @Override public boolean has(Class cls) { return false; } @Override public boolean hasOneOf(Class[] annoClasses) { return false; } @Override public int size() { return 0; } } public static class OneAnnotation implements Annotations, java.io.Serializable { private static final long serialVersionUID = 1L; private final Class _type; private final Annotation _value; public OneAnnotation(Class type, Annotation value) { _type = type; _value = value; } @SuppressWarnings("unchecked") @Override public A get(Class cls) { if (_type == cls) { return (A) _value; } return null; } @Override public boolean has(Class cls) { return (_type == cls); } @Override public boolean hasOneOf(Class[] annoClasses) { for (Class cls : annoClasses) { if (cls == _type) { return true; } } return false; } @Override public int size() { return 1; } } public static class TwoAnnotations implements Annotations, java.io.Serializable { private static final long serialVersionUID = 1L; private final Class _type1, _type2; private final Annotation _value1, _value2; public TwoAnnotations(Class type1, Annotation value1, Class type2, Annotation value2) { _type1 = type1; _value1 = value1; _type2 = type2; _value2 = value2; } @SuppressWarnings("unchecked") @Override public A get(Class cls) { if (_type1 == cls) { return (A) _value1; } if (_type2 == cls) { return (A) _value2; } return null; } @Override public boolean has(Class cls) { return (_type1 == cls) || (_type2 == cls); } @Override public boolean hasOneOf(Class[] annoClasses) { for (Class cls : annoClasses) { if ((cls == _type1) || (cls == _type2)) { return true; } } return false; } @Override public int size() { return 2; } } }