package org.codehaus.jackson.map;

import java.lang.annotation.Annotation;
import java.util.*;

import org.codehaus.jackson.type.JavaType;
import org.codehaus.jackson.map.JsonDeserializer;
import org.codehaus.jackson.map.JsonSerializer;
import org.codehaus.jackson.map.annotate.JsonSerialize;
import org.codehaus.jackson.map.deser.ValueInstantiator;
import org.codehaus.jackson.map.introspect.*;
import org.codehaus.jackson.map.jsontype.NamedType;
import org.codehaus.jackson.map.jsontype.TypeResolverBuilder;

/**
 * Abstract class that defines API used for introspecting annotation-based
 * configuration for serialization and deserialization. Separated
 * so that different sets of annotations can be supported, and support
 * plugged-in dynamically.
 *<p>
 * NOTE: due to rapid addition of new methods (and changes to existing methods),
 * it is <b>strongly</b> recommended that custom implementations should not directly
 * extend this class, but rather extend {@link NopAnnotationIntrospector}.
 * This way added methods will not break backwards compatibility of custom annotation
 * introspectors.
 */
public abstract class AnnotationIntrospector
{    
    /*
    /**********************************************************
    /* Helper types
    /**********************************************************
     */

    /**
     * Value type used with managed and back references; contains type and
     * logic name, used to link related references
     * 
     * @since 1.6
     */
    public static class ReferenceProperty
    {
        public enum Type {
            /**
             * Reference property that Jackson manages and that is serialized normally (by serializing
             * reference object), but is used for resolving back references during
             * deserialization.
             * Usually this can be defined by using
             * {@link org.codehaus.jackson.annotate.JsonManagedReference}
             */
            MANAGED_REFERENCE
    
            /**
             * Reference property that Jackson manages by suppressing it during serialization,
             * and reconstructing during deserialization.
             * Usually this can be defined by using
             * {@link org.codehaus.jackson.annotate.JsonBackReference}
             */
            ,BACK_REFERENCE
            ;
        }

        private final Type _type;
        private final String _name;

        public ReferenceProperty(Type t, String n) {
            _type = t;
            _name = n;
        }

        public static ReferenceProperty managed(String name) { return new ReferenceProperty(Type.MANAGED_REFERENCE, name); }
        public static ReferenceProperty back(String name) { return new ReferenceProperty(Type.BACK_REFERENCE, name); }
        
        public Type getType() { return _type; }
        public String getName() { return _name; }

        public boolean isManagedReference() { return _type == Type.MANAGED_REFERENCE; }
        public boolean isBackReference() { return _type == Type.BACK_REFERENCE; }
    }
    
    /*
    /**********************************************************
    /* Factory methods
    /**********************************************************
     */
    
    /**
     * Factory method for accessing "no operation" implementation
     * of introspector: instance that will never find any annotation-based
     * configuration.
     * 
     * @since 1.3
     */
    public static AnnotationIntrospector nopInstance() {
        return NopAnnotationIntrospector.instance;
    }

    public static AnnotationIntrospector pair(AnnotationIntrospector a1, AnnotationIntrospector a2) {
        return new Pair(a1, a2);
    }

    /*
    /**********************************************************
    /* Access to possibly chained introspectors (1.7)
    /**********************************************************
     */

    /**
     * Method that can be used to collect all "real" introspectors that
     * this introspector contains, if any; or this introspector
     * if it is not a container. Used to get access to all container
     * introspectors in their priority order.
     *<p>
     * Default implementation returns a Singleton list with this introspector
     * as contents.
     * This usually works for sub-classes, except for proxy or delegating "container
     * introspectors" which need to override implementation.
     */
    public Collection<AnnotationIntrospector> allIntrospectors() {
        return Collections.singletonList(this);
    }
    
    /**
     * Method that can be used to collect all "real" introspectors that
     * this introspector contains, if any; or this introspector
     * if it is not a container. Used to get access to all container
     * introspectors in their priority order.
     *<p>
     * Default implementation adds this introspector in result; this usually
     * works for sub-classes, except for proxy or delegating "container
     * introspectors" which need to override implementation.
     */
    public Collection<AnnotationIntrospector> allIntrospectors(Collection<AnnotationIntrospector> result) {
        result.add(this);
        return result;
    }
    
    /*
    /**********************************************************
    /* Generic annotation properties, lookup
    /**********************************************************
    */

    /**
     * Method called by framework to determine whether given annotation
     * is handled by this introspector.
     */
    public abstract boolean isHandled(Annotation ann);
    
    /*
    /**********************************************************
    /* General class annotations
    /**********************************************************
    */

    /**
     * Method that checks whether specified class has annotations
     * that indicate that it is (or is not) cachable. Exact
     * semantics depend on type of class annotated and using
     * class (factory or provider).
     *<p>
     * Currently only used
     * with deserializers, to determine whether provider
     * should cache instances, and if no annotations are found,
     * assumes non-cachable instances.
     *
     * @return True, if class is considered cachable within context,
     *   False if not, and null if introspector does not care either
     *   way.
     */
    public Boolean findCachability(AnnotatedClass ac) {
        return null;
    }

    /**
     * Method for locating name used as "root name" (for use by
     * some serializers when outputting root-level object -- mostly
     * for XML compatibility purposes) for given class, if one
     * is defined. Returns null if no declaration found; can return
     * explicit empty String, which is usually ignored as well as null.
     *
     * @since 1.3
     */
    public abstract String findRootName(AnnotatedClass ac);

    /**
     * Method for finding list of properties to ignore for given class
     * (null is returned if not specified).
     * List of property names is applied
     * after other detection mechanisms, to filter out these specific
     * properties from being serialized and deserialized.
     * 
     * @since 1.4
     */
    public abstract String[] findPropertiesToIgnore(AnnotatedClass ac);

    /**
     * Method for checking whether an annotation indicates that all unknown properties
     * 
     * @since 1.4
     */
    public abstract Boolean findIgnoreUnknownProperties(AnnotatedClass ac);

    /**
     * Method for checking whether properties that have specified type
     * (class, not generics aware) should be completely ignored for
     * serialization and deserialization purposes.
     * 
     * @param ac Type to check
     * 
     * @return Boolean.TRUE if properties of type should be ignored;
     *   Boolean.FALSE if they are not to be ignored, null for default
     *   handling (which is 'do not ignore')
     *   
     * @since 1.7
     */
    public Boolean isIgnorableType(AnnotatedClass ac) {
        return null;
    }

    /**
     * Method for finding if annotated class has associated filter; and if so,
     * to return id that is used to locate filter.
     * 
     * @return Id of the filter to use for filtering properties of annotated
     *    class, if any; or null if none found.
     */
    public Object findFilterId(AnnotatedClass ac) {
        return null;
    }
    
    /*
    /**********************************************************
    /* Property auto-detection
    /**********************************************************
     */

    /**
     * Method for checking if annotations indicate changes to minimum visibility levels
     * needed for auto-detecting property elements (fields, methods, constructors).
     * A baseline checker is given, and introspector is to either return it as is (if
     * no annotations are found), or build and return a derived instance (using checker's build
     * methods).
     *
     *  @since 1.5
     */
    public VisibilityChecker<?> findAutoDetectVisibility(AnnotatedClass ac, VisibilityChecker<?> checker) {
        return checker;
    }
    
    /*
    /**********************************************************
    /* Class annotations for Polymorphic type handling (1.5+)
    /**********************************************************
    */
    
    /**
     * Method for checking if given class has annotations that indicate
     * that specific type resolver is to be used for handling instances.
     * This includes not only
     * instantiating resolver builder, but also configuring it based on
     * relevant annotations (not including ones checked with a call to
     * {@link #findSubtypes}
     * 
     * @param config Configuration settings in effect (for serialization or deserialization)
     * @param ac Annotated class to check for annotations
     * @param baseType Base java type of value for which resolver is to be found
     * 
     * @return Type resolver builder for given type, if one found; null if none
     * 
     * @since 1.5 -- although changed in 1.8 to pass configuration object
     */
    public TypeResolverBuilder<?> findTypeResolver(MapperConfig<?> config,
            AnnotatedClass ac, JavaType baseType) {
        return null;
    }

    /**
     * Method for checking if given property entity (field or method) has annotations
     * that indicate that specific type resolver is to be used for handling instances.
     * This includes not only
     * instantiating resolver builder, but also configuring it based on
     * relevant annotations (not including ones checked with a call to
     * {@link #findSubtypes}
     * 
     * @param config Configuration settings in effect (for serialization or deserialization)
     * @param am Annotated member (field or method) to check for annotations
     * @param baseType Base java type of property for which resolver is to be found
     * 
     * @return Type resolver builder for properties of given entity, if one found;
     *    null if none
     * 
     * @since 1.5 -- although changed in 1.8 to pass configuration object
     */
    public TypeResolverBuilder<?> findPropertyTypeResolver(MapperConfig<?> config,
            AnnotatedMember am, JavaType baseType) {
        return null;
    }

    /**
     * Method for checking if given structured property entity (field or method that
     * has nominal value of Map, Collection or array type) has annotations
     * that indicate that specific type resolver is to be used for handling type
     * information of contained values.
     * This includes not only
     * instantiating resolver builder, but also configuring it based on
     * relevant annotations (not including ones checked with a call to
     * {@link #findSubtypes}
     * 
     * @param config Configuration settings in effect (for serialization or deserialization)
     * @param am Annotated member (field or method) to check for annotations
     * @param containerType Type of property for which resolver is to be found (must be a container type)
     * 
     * @return Type resolver builder for values contained in properties of given entity,
     *    if one found; null if none
     * 
     * @since 1.5 -- although changed in 1.8 to pass configuration object
     */    
    public TypeResolverBuilder<?> findPropertyContentTypeResolver(MapperConfig<?> config,
            AnnotatedMember am, JavaType containerType) {
        return null;
    }

    /**
     * Method for locating annotation-specified subtypes related to annotated
     * entity (class, method, field). Note that this is only guaranteed to be
     * a list of directly
     * declared subtypes, no recursive processing is guarantees (i.e. caller
     * has to do it if/as necessary)
     * 
     * @param a Annotated entity (class, field/method) to check for annotations
     * 
     * @since 1.5
     */
    public List<NamedType> findSubtypes(Annotated a) {
        return null;
    }

    /**
     * Method for checking if specified type has explicit name.
     * 
     * @param ac Class to check for type name annotations
     * 
     * @since 1.5
     */
    public String findTypeName(AnnotatedClass ac) {
        return null;
    }

    /*
    /**********************************************************
    /* General member (field, method/constructor) annotations
    /**********************************************************
     */

    /**
     * Note: defined as non-abstract to reduce fragility between
     * versions.
     * 
     * @since 1.6
     */
    public ReferenceProperty findReferenceType(AnnotatedMember member) {
        return null;
    }

    /**
     * Method called to check whether given property is marked to be "unwrapped"
     * when being serialized (and appropriately handled in reverse direction,
     * i.e. expect unwrapped representation during deserialization)
     * 
     * @since 1.9
     */
    public Boolean shouldUnwrapProperty(AnnotatedMember member) {
        return null;
    }

    /**
     * Method called to check whether given property is marked to
     * be ignored; but NOT to determine if it should necessarily
     * be ignored, since that may depend on other factors.
     *<p>
     * Default implementation calls existing 'isIgnored' methods
     * such as {@link #isIgnorableField(AnnotatedField)} and
     * {@link #isIgnorableMethod(AnnotatedMethod)}.
     * 
     * @since 1.9
     */
    public boolean hasIgnoreMarker(AnnotatedMember m)
    {
        /* For maximum backwards compatibility, we better call
         * existing methods.
         */
        /* TODO: For 2.0, replace with simple 'return false;'
         */
        if (m instanceof AnnotatedMethod) {
            return isIgnorableMethod((AnnotatedMethod) m);
        }
        if (m instanceof AnnotatedField) {
            return isIgnorableField((AnnotatedField) m);
        }
        if (m instanceof AnnotatedConstructor) {
            return isIgnorableConstructor((AnnotatedConstructor) m);
        }
        return false;
    }

    /**
     * Method called to find out whether given member expectes a value
     * to be injected, and if so, what is the identifier of the value
     * to use during injection.
     * Type if identifier needs to be compatible with provider of
     * values (of type {@link InjectableValues}); often a simple String
     * id is used.
     * 
     * @param m Member to check
     * 
     * @return Identifier of value to inject, if any; null if no injection
     *   indicator is found
     */
    public Object findInjectableValueId(AnnotatedMember m) {
        return null;
    }
    
    /*
    /**********************************************************
    /* General method annotations
    /**********************************************************
     */

    /**
     * Method for checking whether there is an annotation that
     * indicates that given method should be ignored for all
     * operations (serialization, deserialization).
     *<p>
     * Note that this method should <b>ONLY</b> return true for such
     * explicit ignoral cases; and not if method just happens not to
     * be visible for annotation processor.
     *
     * @return True, if an annotation is found to indicate that the
     *    method should be ignored; false if not.
     */
    public abstract boolean isIgnorableMethod(AnnotatedMethod m);

    /**
     * @since 1.2
     */
    public abstract boolean isIgnorableConstructor(AnnotatedConstructor c);

    /*
    /**********************************************************
    /* General field annotations
    /**********************************************************
     */

    /**
     * Method for checking whether there is an annotation that
     * indicates that given field should be ignored for all
     * operations (serialization, deserialization).
     *
     * @return True, if an annotation is found to indicate that the
     *    field should be ignored; false if not.
     */
    public abstract boolean isIgnorableField(AnnotatedField f);

    /*
    /**********************************************************
    /* Serialization: general annotations
    /**********************************************************
     */

    /**
     * Method for getting a serializer definition on specified method
     * or field. Type of definition is either instance (of type
     * {@link JsonSerializer}) or Class (of type
     * <code>Class<JsonSerializer></code>); if value of different
     * type is returned, a runtime exception may be thrown by caller.
     *<p>
     * Note: this variant was briefly deprecated for 1.7; should not be
     */
    public abstract Object findSerializer(Annotated am);

    /**
     * Method for getting a serializer definition for keys of associated <code>Map</code> property.
     * Type of definition is either instance (of type
     * {@link JsonSerializer}) or Class (of type
     * <code>Class<JsonSerializer></code>); if value of different
     * type is returned, a runtime exception may be thrown by caller.
     * 
     * @since 1.8
     */
    public Class<? extends JsonSerializer<?>> findKeySerializer(Annotated am) {
        return null;
    }

    /**
     * Method for getting a serializer definition for content (values) of
     * associated <code>Collection</code>, <code>array</code> or <code>Map</code> property.
     * Type of definition is either instance (of type
     * {@link JsonSerializer}) or Class (of type
     * <code>Class<JsonSerializer></code>); if value of different
     * type is returned, a runtime exception may be thrown by caller.
     * 
     * @since 1.8
     */
    public Class<? extends JsonSerializer<?>> findContentSerializer(Annotated am) {
        return null;
    }
    
    /**
     * Method for checking whether given annotated entity (class, method,
     * field) defines which Bean/Map properties are to be included in
     * serialization.
     * If no annotation is found, method should return given second
     * argument; otherwise value indicated by the annotation
     *
     * @return Enumerated value indicating which properties to include
     *   in serialization
     */
    public JsonSerialize.Inclusion findSerializationInclusion(Annotated a, JsonSerialize.Inclusion defValue) {
        return defValue;
    }

    /**
     * Method for accessing annotated type definition that a
     * method/field can have, to be used as the type for serialization
     * instead of the runtime type.
     * Type returned (if any) needs to be widening conversion (super-type).
     * Declared return type of the method is also considered acceptable.
     *
     * @return Class to use instead of runtime type
     */
    public abstract Class<?> findSerializationType(Annotated a);

    /**
     * Method for finding possible widening type definition that a property
     * value can have, to define less specific key type to use for serialization.
     * It should be only be used with {@link java.util.Map} types.
     * 
     * @return Class specifying more general type to use instead of
     *   declared type, if annotation found; null if not
     */
    public Class<?> findSerializationKeyType(Annotated am, JavaType baseType) {
        return null;
    }

    /**
     * Method for finding possible widening type definition that a property
     * value can have, to define less specific key type to use for serialization.
     * It should be only used with structured types (arrays, collections, maps).
     * 
     * @return Class specifying more general type to use instead of
     *   declared type, if annotation found; null if not
     */
    public Class<?> findSerializationContentType(Annotated am, JavaType baseType) {
        return null;
    }
    
    /**
     * Method for accessing declared typing mode annotated (if any).
     * This is used for type detection, unless more granular settings
     * (such as actual exact type; or serializer to use which means
     * no type information is needed) take precedence.
     *
     * @since 1.2
     *
     * @return Typing mode to use, if annotation is found; null otherwise
     */
    public abstract JsonSerialize.Typing findSerializationTyping(Annotated a);

    /**
     * Method for checking if annotated serializable property (represented by
     * field or getter method) has definitions for views it is to be included
     * in. If null is returned, no view definitions exist and property is always
     * included; otherwise it will only be included for views included in returned
     * array. View matches are checked using class inheritance rules (sub-classes
     * inherit inclusions of super-classes)
     * 
     * @param a Annotated serializable property (field or getter method)
     * @return Array of views (represented by classes) that the property is included in;
     *    if null, always included (same as returning array containing <code>Object.class</code>)
     */
    public abstract Class<?>[] findSerializationViews(Annotated a);
    
    /*
    /**********************************************************
    /* Serialization: class annotations
    /**********************************************************
     */

    /**
     * Method for accessing defined property serialization order (which may be
     * partial). May return null if no ordering is defined.
     * 
     * @since 1.4
     */
    public abstract String[] findSerializationPropertyOrder(AnnotatedClass ac);

    /**
     * Method for checking whether an annotation indicates that serialized properties
     * for which no explicit is defined should be alphabetically (lexicograpically)
     * ordered
     * 
     * @since 1.4
     */
    public abstract Boolean findSerializationSortAlphabetically(AnnotatedClass ac);
    
    /*
    /**********************************************************
    /* Serialization: method annotations
    /**********************************************************
     */

    /**
     * Method for checking whether given method has an annotation
     * that suggests property name associated with method that
     * may be a "getter". Should return null if no annotation
     * is found; otherwise a non-null String.
     * If non-null value is returned, it is used as the property
     * name, except for empty String ("") which is taken to mean
     * "use standard bean name detection if applicable;
     * method name if not".
     */
    public abstract String findGettablePropertyName(AnnotatedMethod am);

    /**
     * Method for checking whether given method has an annotation
     * that suggests that the return value of annotated method
     * should be used as "the value" of the object instance; usually
     * serialized as a primitive value such as String or number.
     *
     * @return True if such annotation is found (and is not disabled);
     *   false if no enabled annotation is found
     */
    public abstract boolean hasAsValueAnnotation(AnnotatedMethod am);

    /**
     * Method for determining the String value to use for serializing
     * given enumeration entry; used when serializing enumerations
     * as Strings (the standard method).
     *
     * @return Serialized enum value.
     */
    public abstract String findEnumValue(Enum<?> value);

    /*
    /**********************************************************
    /* Serialization: field annotations
    /**********************************************************
     */

    /**
     * Method for checking whether given member field represent
     * a serializable logical property; and if so, returns the
     * name of that property.
     * Should return null if no annotation is found (indicating it
     * is not a serializable field); otherwise a non-null String.
     * If non-null value is returned, it is used as the property
     * name, except for empty String ("") which is taken to mean
     * "use the field name as is".
     */
    public abstract String findSerializablePropertyName(AnnotatedField af);

    /*
    /**********************************************************
    /* Deserialization: general annotations
    /**********************************************************
     */

    /**
     * Method for getting a deserializer definition on specified method
     * or field.
     * Type of definition is either instance (of type
     * {@link JsonDeserializer}) or Class (of type
     * <code>Class<JsonDeserializer></code>); if value of different
     * type is returned, a runtime exception may be thrown by caller.
     *<p>
     * Note: this variant was briefly deprecated for 1.7; but it turns out
     * we really should not try to push BeanProperty through at this point
     */
    public abstract Object findDeserializer(Annotated am);

    /**
     * Method for getting a deserializer definition for keys of
     * associated <code>Map</code> property.
     * Type of definition is either instance (of type
     * {@link JsonDeserializer}) or Class (of type
     * <code>Class<JsonDeserializer></code>); if value of different
     * type is returned, a runtime exception may be thrown by caller.
     * 
     * @since 1.3
     */
    public abstract Class<? extends KeyDeserializer> findKeyDeserializer(Annotated am);

    /**
     * Method for getting a deserializer definition for content (values) of
     * associated <code>Collection</code>, <code>array</code> or
     * <code>Map</code> property.
     * Type of definition is either instance (of type
     * {@link JsonDeserializer}) or Class (of type
     * <code>Class<JsonDeserializer></code>); if value of different
     * type is returned, a runtime exception may be thrown by caller.
     * 
     * @since 1.3
     */
    public abstract Class<? extends JsonDeserializer<?>> findContentDeserializer(Annotated am);

    /**
     * Method for accessing annotated type definition that a
     * method can have, to be used as the type for serialization
     * instead of the runtime type.
     * Type must be a narrowing conversion
     * (i.e.subtype of declared type).
     * Declared return type of the method is also considered acceptable.
     *
     * @param baseType Assumed type before considering annotations
     * @param propName Logical property name of the property that uses
     *    type, if known; null for types not associated with property
     *
     * @return Class to use for deserialization instead of declared type
     */
    public abstract Class<?> findDeserializationType(Annotated am, JavaType baseType,
            String propName);

    /**
     * Method for accessing additional narrowing type definition that a
     * method can have, to define more specific key type to use.
     * It should be only be used with {@link java.util.Map} types.
     * 
     * @param baseKeyType Assumed key type before considering annotations
     * @param propName Logical property name of the property that uses
     *    type, if known; null for types not associated with property
     *
     * @return Class specifying more specific type to use instead of
     *   declared type, if annotation found; null if not
     */
    public abstract Class<?> findDeserializationKeyType(Annotated am, JavaType baseKeyType,
            String propName);

    /**
     * Method for accessing additional narrowing type definition that a
     * method can have, to define more specific content type to use;
     * content refers to Map values and Collection/array elements.
     * It should be only be used with Map, Collection and array types.
     * 
     * @param baseContentType Assumed content (value) type before considering annotations
     * @param propName Logical property name of the property that uses
     *    type, if known; null for types not associated with property
     *
     * @return Class specifying more specific type to use instead of
     *   declared type, if annotation found; null if not
     */
    public abstract Class<?> findDeserializationContentType(Annotated am, JavaType baseContentType,
            String propName);

    /*
    /**********************************************************
    /* Deserialization: class annotations
    /**********************************************************
    */

    /**
     * Method getting {@link ValueInstantiator} to use for given
     * type (class): return value can either be an instance of
     * instantiator, or class of instantiator to create.
     * 
     * @since 1.9
     */
    public Object findValueInstantiator(AnnotatedClass ac) {
        return null;
    }
    
    /*
    /**********************************************************
    /* Deserialization: method annotations
    /**********************************************************
     */

    /**
     * Method for checking whether given method has an annotation
     * that suggests property name associated with method that
     * may be a "setter". Should return null if no annotation
     * is found; otherwise a non-null String.
     * If non-null value is returned, it is used as the property
     * name, except for empty String ("") which is taken to mean
     * "use standard bean name detection if applicable;
     * method name if not".
     */
    public abstract String findSettablePropertyName(AnnotatedMethod am);

    /**
     * Method for checking whether given method has an annotation
     * that suggests that the method is to serve as "any setter";
     * method to be used for setting values of any properties for
     * which no dedicated setter method is found.
     *
     * @return True if such annotation is found (and is not disabled),
     *   false otherwise
     */
    public boolean hasAnySetterAnnotation(AnnotatedMethod am) {
        return false;
    }

    /**
     * Method for checking whether given method has an annotation
     * that suggests that the method is to serve as "any setter";
     * method to be used for accessing set of miscellaneous "extra"
     * properties, often bound with matching "any setter" method.
     *
     * @return True if such annotation is found (and is not disabled),
     *   false otherwise
     *   
     * @since 1.6
     */
    public boolean hasAnyGetterAnnotation(AnnotatedMethod am) {
        return false;
    }
    
    /**
     * Method for checking whether given annotated item (method, constructor)
     * has an annotation
     * that suggests that the method is a "creator" (aka factory)
     * method to be used for construct new instances of deserialized
     * values.
     *
     * @return True if such annotation is found (and is not disabled),
     *   false otherwise
     */
    public boolean hasCreatorAnnotation(Annotated a) {
        return false;
    }

    /*
    /**********************************************************
    /* Deserialization: field annotations
    /**********************************************************
     */

    /**
     * Method for checking whether given member field represent
     * a deserializable logical property; and if so, returns the
     * name of that property.
     * Should return null if no annotation is found (indicating it
     * is not a deserializable field); otherwise a non-null String.
     * If non-null value is returned, it is used as the property
     * name, except for empty String ("") which is taken to mean
     * "use the field name as is".
     */
    public abstract String findDeserializablePropertyName(AnnotatedField af);

    /*
    /**********************************************************
    /* Deserialization: parameter annotations (for
    /* creator method parameters)
    /**********************************************************
     */

    /**
     * Method for checking whether given set of annotations indicates
     * property name for associated parameter.
     * No actual parameter object can be passed since JDK offers no
     * representation; just annotations.
     */
    public abstract String findPropertyNameForParam(AnnotatedParameter param);

    /*
    /**********************************************************
    /* Helper classes
    /**********************************************************
     */

    /**
     * Helper class that allows using 2 introspectors such that one
     * introspector acts as the primary one to use; and second one
     * as a fallback used if the primary does not provide conclusive
     * or useful result for a method.
     *<p>
     * An obvious consequence of priority is that it is easy to construct
     * longer chains of introspectors by linking multiple pairs.
     * Currently most likely combination is that of using the default
     * Jackson provider, along with JAXB annotation introspector (available
     * since version 1.1).
     */
    public static class Pair
        extends AnnotationIntrospector
    {
        protected final AnnotationIntrospector _primary, _secondary;

        public Pair(AnnotationIntrospector p,
                    AnnotationIntrospector s)
        {
            _primary = p;
            _secondary = s;
        }

        /**
         * Helper method for constructing a Pair from two given introspectors (if
         * neither is null); or returning non-null introspector if one is null
         * (and return just null if both are null)
         * 
         * @since 1.7
         */
        public static AnnotationIntrospector create(AnnotationIntrospector primary,
                AnnotationIntrospector secondary)
        {
            if (primary == null) {
                return secondary;
            }
            if (secondary == null) {
                return primary;
            }
            return new Pair(primary, secondary);
        }

        @Override
        public Collection<AnnotationIntrospector> allIntrospectors() {
            return allIntrospectors(new ArrayList<AnnotationIntrospector>());
        }

        @Override
        public Collection<AnnotationIntrospector> allIntrospectors(Collection<AnnotationIntrospector> result)
        {
            _primary.allIntrospectors(result);
            _secondary.allIntrospectors(result);
            return result;
        }
        
        // // // Generic annotation properties, lookup
        
        @Override
        public boolean isHandled(Annotation ann)
        {
            return _primary.isHandled(ann) || _secondary.isHandled(ann);
        }

        /*
        /******************************************************
        /* General class annotations
        /******************************************************
         */

        @Override
        public Boolean findCachability(AnnotatedClass ac)
        {
            Boolean result = _primary.findCachability(ac);
            if (result == null) {
                result = _secondary.findCachability(ac);
            }
            return result;
        }

        @Override
        public String findRootName(AnnotatedClass ac)
        {
            String name1 = _primary.findRootName(ac);
            if (name1 == null) {
                return _secondary.findRootName(ac);
            } else if (name1.length() > 0) {
                return name1;
            }
            // name1 is empty; how about secondary?
            String name2 = _secondary.findRootName(ac);
            return (name2 == null) ? name1 : name2;
        }

        @Override
        public String[] findPropertiesToIgnore(AnnotatedClass ac)
        {
            String[] result = _primary.findPropertiesToIgnore(ac);
            if (result == null) {
                result = _secondary.findPropertiesToIgnore(ac);
            }
            return result;            
        }

        @Override
        public Boolean findIgnoreUnknownProperties(AnnotatedClass ac)
        {
            Boolean result = _primary.findIgnoreUnknownProperties(ac);
            if (result == null) {
                result = _secondary.findIgnoreUnknownProperties(ac);
            }
            return result;
        }        

        @Override
        public Boolean isIgnorableType(AnnotatedClass ac)
        {
            Boolean result = _primary.isIgnorableType(ac);
            if (result == null) {
                result = _secondary.isIgnorableType(ac);
            }
            return result;
        }

        @Override
        public Object findFilterId(AnnotatedClass ac)
        {
            Object id = _primary.findFilterId(ac);
            if (id == null) {
                id = _secondary.findFilterId(ac);
            }
            return id;
        }
        
        /*
        /******************************************************
        /* Property auto-detection
        /******************************************************
        */
        
        @Override
        public VisibilityChecker<?> findAutoDetectVisibility(AnnotatedClass ac,
            VisibilityChecker<?> checker)
        {
            /* Note: to have proper priorities, we must actually call delegatees
             * in reverse order:
             */
            checker = _secondary.findAutoDetectVisibility(ac, checker);
            return _primary.findAutoDetectVisibility(ac, checker);
        }

        /*
        /******************************************************
        /* Type handling
        /******************************************************
        */
        
        @Override
        public TypeResolverBuilder<?> findTypeResolver(MapperConfig<?> config,
                AnnotatedClass ac, JavaType baseType)
        {
            TypeResolverBuilder<?> b = _primary.findTypeResolver(config, ac, baseType);
            if (b == null) {
                b = _secondary.findTypeResolver(config, ac, baseType);
            }
            return b;
        }

        @Override
        public TypeResolverBuilder<?> findPropertyTypeResolver(MapperConfig<?> config,
                AnnotatedMember am, JavaType baseType)
        {
            TypeResolverBuilder<?> b = _primary.findPropertyTypeResolver(config, am, baseType);
            if (b == null) {
                b = _secondary.findPropertyTypeResolver(config, am, baseType);
            }
            return b;
        }

        @Override
        public TypeResolverBuilder<?> findPropertyContentTypeResolver(MapperConfig<?> config,
                AnnotatedMember am, JavaType baseType)
        {
            TypeResolverBuilder<?> b = _primary.findPropertyContentTypeResolver(config, am, baseType);
            if (b == null) {
                b = _secondary.findPropertyContentTypeResolver(config, am, baseType);
            }
            return b;
        }
        
        @Override
        public List<NamedType> findSubtypes(Annotated a)
        {
            List<NamedType> types1 = _primary.findSubtypes(a);
            List<NamedType> types2 = _secondary.findSubtypes(a);
            if (types1 == null || types1.isEmpty()) return types2;
            if (types2 == null || types2.isEmpty()) return types1;
            ArrayList<NamedType> result = new ArrayList<NamedType>(types1.size() + types2.size());
            result.addAll(types1);
            result.addAll(types2);
            return result;
        }

        @Override
        public String findTypeName(AnnotatedClass ac)
        {
            String name = _primary.findTypeName(ac);
            if (name == null || name.length() == 0) {
                name = _secondary.findTypeName(ac);                
            }
            return name;
        }
        
        // // // General member (field, method/constructor) annotations
        
        @Override        
        public ReferenceProperty findReferenceType(AnnotatedMember member)
        {
            ReferenceProperty ref = _primary.findReferenceType(member);
            if (ref == null) {
                ref = _secondary.findReferenceType(member);
            }
            return ref; 
        }

        @Override        
        public Boolean shouldUnwrapProperty(AnnotatedMember member)
        {
            Boolean value = _primary.shouldUnwrapProperty(member);
            if (value == null) {
                value = _secondary.shouldUnwrapProperty(member);
            }
            return value;
        }

        @Override
        public Object findInjectableValueId(AnnotatedMember m)
        {
            Object value = _primary.findInjectableValueId(m);
            if (value == null) {
                value = _secondary.findInjectableValueId(m);
            }
            return value;
        }
        
        @Override
        public boolean hasIgnoreMarker(AnnotatedMember m) {
            return _primary.hasIgnoreMarker(m) || _secondary.hasIgnoreMarker(m);
        }
        
        // // // General method annotations

        @Override
        public boolean isIgnorableMethod(AnnotatedMethod m) {
            return _primary.isIgnorableMethod(m) || _secondary.isIgnorableMethod(m);
        }
        
        @Override
        public boolean isIgnorableConstructor(AnnotatedConstructor c) {
            return _primary.isIgnorableConstructor(c) || _secondary.isIgnorableConstructor(c);
        }

        // // // General field annotations

        @Override
        public boolean isIgnorableField(AnnotatedField f)
        {
            return _primary.isIgnorableField(f) || _secondary.isIgnorableField(f);
        }

        // // // Serialization: general annotations

        @Override
        public Object findSerializer(Annotated am)
        {
            Object result = _primary.findSerializer(am);
            if (result == null) {
                result = _secondary.findSerializer(am);
            }
            return result;
        }
        
        @Override
        public Class<? extends JsonSerializer<?>> findKeySerializer(Annotated a)
        {
            Class<? extends JsonSerializer<?>> result = _primary.findKeySerializer(a);
            if (result == null || result == JsonSerializer.None.class) {
                result = _secondary.findKeySerializer(a);
            }
            return result;
        }

        @Override
        public Class<? extends JsonSerializer<?>> findContentSerializer(Annotated a)
        {
            Class<? extends JsonSerializer<?>> result = _primary.findContentSerializer(a);
            if (result == null || result == JsonSerializer.None.class) {
                result = _secondary.findContentSerializer(a);
            }
            return result;
        }
        
        @Override
        public JsonSerialize.Inclusion findSerializationInclusion(Annotated a,
                JsonSerialize.Inclusion defValue)
        {
            /* This is bit trickier: need to combine results in a meaningful
             * way. Seems like it should be a disjoint; that is, most
             * restrictive value should be returned.
             * For enumerations, comparison is done by indexes, which
             * works: largest value is the last one, which is the most
             * restrictive value as well.
             */
            /* 09-Mar-2010, tatu: Actually, as per [JACKSON-256], it is probably better to just
             *    use strict overriding. Simpler, easier to understand.
             */
            // note: call secondary first, to give lower priority
            defValue = _secondary.findSerializationInclusion(a, defValue);
            defValue = _primary.findSerializationInclusion(a, defValue);
            return defValue;
        }
        
        @Override
        public Class<?> findSerializationType(Annotated a)
        {
            Class<?> result = _primary.findSerializationType(a);
            if (result == null) {
                result = _secondary.findSerializationType(a);
            }
            return result;
        }

        @Override
        public Class<?> findSerializationKeyType(Annotated am, JavaType baseType)
        {
            Class<?> result = _primary.findSerializationKeyType(am, baseType);
            if (result == null) {
                result = _secondary.findSerializationKeyType(am, baseType);
            }
            return result;
        }

        @Override
        public Class<?> findSerializationContentType(Annotated am, JavaType baseType)
        {
            Class<?> result = _primary.findSerializationContentType(am, baseType);
            if (result == null) {
                result = _secondary.findSerializationContentType(am, baseType);
            }
            return result;
        }
        
        @Override
        public JsonSerialize.Typing findSerializationTyping(Annotated a)
        {
            JsonSerialize.Typing result = _primary.findSerializationTyping(a);
            if (result == null) {
                result = _secondary.findSerializationTyping(a);
            }
            return result;
        }

        @Override
        public Class<?>[] findSerializationViews(Annotated a)
        {
            /* Theoretically this could be trickier, if multiple introspectors
             * return non-null entries. For now, though, we'll just consider
             * first one to return non-null to win.
             */
            Class<?>[] result = _primary.findSerializationViews(a);
            if (result == null) {
                result = _secondary.findSerializationViews(a);
            }
            return result;
        }
        
        // // // Serialization: class annotations

        @Override
        public String[] findSerializationPropertyOrder(AnnotatedClass ac) {
            String[] result = _primary.findSerializationPropertyOrder(ac);
            if (result == null) {
                result = _secondary.findSerializationPropertyOrder(ac);
            }
            return result;            
        }

        /**
         * Method for checking whether an annotation indicates that serialized properties
         * for which no explicit is defined should be alphabetically (lexicograpically)
         * ordered
         */
        @Override
        public Boolean findSerializationSortAlphabetically(AnnotatedClass ac) {
            Boolean result = _primary.findSerializationSortAlphabetically(ac);
            if (result == null) {
                result = _secondary.findSerializationSortAlphabetically(ac);
            }
            return result;            
        }

        // // // Serialization: method annotations
        
        @Override
        public String findGettablePropertyName(AnnotatedMethod am)
        {
            String result = _primary.findGettablePropertyName(am);
            if (result == null) {
                result = _secondary.findGettablePropertyName(am);
            } else if (result.length() == 0) {
                /* Empty String is a default; can be overridden by
                 * more explicit answer from secondary entry
                 */
                String str2 = _secondary.findGettablePropertyName(am);
                if (str2 != null) {
                    result = str2;
                }
            }
            return result;
        }
        
        @Override
        public boolean hasAsValueAnnotation(AnnotatedMethod am)
        {
            return _primary.hasAsValueAnnotation(am) || _secondary.hasAsValueAnnotation(am);
        }
        
        @Override
        public String findEnumValue(Enum<?> value)
        {
            String result = _primary.findEnumValue(value);
            if (result == null) {
                result = _secondary.findEnumValue(value);
            }
            return result;
        }        

        // // // Serialization: field annotations

        @Override
        public String findSerializablePropertyName(AnnotatedField af)
        {
            String result = _primary.findSerializablePropertyName(af);
            if (result == null) {
                result = _secondary.findSerializablePropertyName(af);
            } else if (result.length() == 0) {
                /* Empty String is a default; can be overridden by
                 * more explicit answer from secondary entry
                 */
                String str2 = _secondary.findSerializablePropertyName(af);
                if (str2 != null) {
                    result = str2;
                }
            }
            return result;
        }

        // // // Deserialization: general annotations

        @Override
        public Object findDeserializer(Annotated am)
        {
            Object result = _primary.findDeserializer(am);
            if (result == null) {
                result = _secondary.findDeserializer(am);
            }
            return result;
        }
        
        @Override
        public Class<? extends KeyDeserializer> findKeyDeserializer(Annotated am)
        {
            Class<? extends KeyDeserializer> result = _primary.findKeyDeserializer(am);
            if (result == null || result == KeyDeserializer.None.class) {
                result = _secondary.findKeyDeserializer(am);
            }
            return result;
        }

        @Override
        public Class<? extends JsonDeserializer<?>> findContentDeserializer(Annotated am)
        {
            Class<? extends JsonDeserializer<?>> result = _primary.findContentDeserializer(am);
            if (result == null || result == JsonDeserializer.None.class) {
                result = _secondary.findContentDeserializer(am);
            }
            return result;
        }
        
        @Override
        public Class<?> findDeserializationType(Annotated am, JavaType baseType,
                String propName)
        {
            Class<?> result = _primary.findDeserializationType(am, baseType, propName);
            if (result == null) {
                result = _secondary.findDeserializationType(am, baseType, propName);
            }
            return result;
        }

        @Override
        public Class<?> findDeserializationKeyType(Annotated am, JavaType baseKeyType,
                String propName)
        {
            Class<?> result = _primary.findDeserializationKeyType(am, baseKeyType, propName);
            if (result == null) {
                result = _secondary.findDeserializationKeyType(am, baseKeyType, propName);
            }
            return result;
        }

        @Override
        public Class<?> findDeserializationContentType(Annotated am, JavaType baseContentType,
                String propName)
        {
            Class<?> result = _primary.findDeserializationContentType(am, baseContentType, propName);
            if (result == null) {
                result = _secondary.findDeserializationContentType(am, baseContentType, propName);
            }
            return result;
        }

        // // // Deserialization: class annotations

        @Override
        public Object findValueInstantiator(AnnotatedClass ac)
        {
            Object result = _primary.findValueInstantiator(ac);
            if (result == null) {
                result = _secondary.findValueInstantiator(ac);
            }
            return result;
        }

        // // // Deserialization: method annotations

        @Override
        public String findSettablePropertyName(AnnotatedMethod am)
        {
            String result = _primary.findSettablePropertyName(am);
            if (result == null) {
                result = _secondary.findSettablePropertyName(am);
            } else if (result.length() == 0) {
                /* Empty String is a default; can be overridden by
                 * more explicit answer from secondary entry
                 */
                String str2 = _secondary.findSettablePropertyName(am);
                if (str2 != null) {
                    result = str2;
                }
            }
            return result;
        }
        
        @Override
        public boolean hasAnySetterAnnotation(AnnotatedMethod am)
        {
            return _primary.hasAnySetterAnnotation(am) || _secondary.hasAnySetterAnnotation(am);
        }

        @Override
        public boolean hasAnyGetterAnnotation(AnnotatedMethod am)
        {
            return _primary.hasAnyGetterAnnotation(am) || _secondary.hasAnyGetterAnnotation(am);
        }
        
        @Override
        public boolean hasCreatorAnnotation(Annotated a)
        {
            return _primary.hasCreatorAnnotation(a) || _secondary.hasCreatorAnnotation(a);
        }
        
        // // // Deserialization: field annotations

        @Override
        public String findDeserializablePropertyName(AnnotatedField af)
        {
            String result = _primary.findDeserializablePropertyName(af);
            if (result == null) {
                result = _secondary.findDeserializablePropertyName(af);
            } else if (result.length() == 0) {
                /* Empty String is a default; can be overridden by
                 * more explicit answer from secondary entry
                 */
                String str2 = _secondary.findDeserializablePropertyName(af);
                if (str2 != null) {
                    result = str2;
                }
            }
            return result;
        }

        // // // Deserialization: parameter annotations (for creators)

        @Override
        public String findPropertyNameForParam(AnnotatedParameter param)
        {
            String result = _primary.findPropertyNameForParam(param);
            if (result == null) {
                result = _secondary.findPropertyNameForParam(param);
            }
            return result;
        }
    }

}
