package com.fasterxml.jackson.annotation; import java.lang.annotation.*; import java.util.Locale; import java.util.TimeZone; /** * General-purpose annotation used for configuring details of how * values of properties are to be serialized. * Unlike most other Jackson annotations, annotation does not * have specific universal interpretation: instead, effect depends on datatype * of property being annotated (or more specifically, deserializer * and serializer being used). *
* Common uses include choosing between alternate representations -- for example, * whether {@link java.util.Date} is to be serialized as number (Java timestamp) * or String (such as ISO-8601 compatible time value) -- as well as configuring * exact details with {@link #pattern} property. *
* As of Jackson 2.6, known special handling includes: *
toString());
 *    but it is also possible to use {@link Shape#OBJECT} to serialize (but not deserialize)
 *    {@link java.lang.Enum}s as JSON Objects (as if they were POJOs). NOTE: serialization
 *     as JSON Object only works with class annotation; 
 *    will not work as per-property annotation.
 *   
     * NOTE: default here does NOT mean JVM defaults but Jackson databindings
     * default, usually UTC, but may be changed on ObjectMapper.
     */
    public final static String DEFAULT_TIMEZONE = "##default";
    
    /**
     * Datatype-specific additional piece of configuration that may be used
     * to further refine formatting aspects. This may, for example, determine
     * low-level format String used for {@link java.util.Date} serialization;
     * however, exact use is determined by specific JsonSerializer
     */
    public String pattern() default "";
    /**
     * Structure to use for serialization: definition of mapping depends on datatype,
     * but usually has straight-forward counterpart in data format (JSON).
     * Note that commonly only a subset of shapes is available; and if 'invalid' value
     * is chosen, defaults are usually used.
     */
    public Shape shape() default Shape.ANY;
    /**
     * {@link java.util.Locale} to use for serialization (if needed).
     * Special value of {@link #DEFAULT_LOCALE}
     * can be used to mean "just use the default", where default is specified
     * by the serialization context, which in turn defaults to system
     * defaults ({@link java.util.Locale#getDefault()}) unless explicitly
     * set to another locale.
     */
    public String locale() default DEFAULT_LOCALE;
    
    /**
     * {@link java.util.TimeZone} to use for serialization (if needed).
     * Special value of {@link #DEFAULT_TIMEZONE}
     * can be used to mean "just use the default", where default is specified
     * by the serialization context, which in turn defaults to system
     * default (UTC) unless explicitly set to another timezone.
     */
    public String timezone() default DEFAULT_TIMEZONE;
    /**
     * Property that indicates whether "lenient" handling should be enabled or
     * disabled. This is relevant mostly for deserialization of some textual
     * datatypes, especially date/time types.
     *
     * Note that underlying default setting depends on datatype (or more precisely
     * deserializer for it): for most date/time types, default is for leniency
     * to be enabled.
     * 
     * @since 2.9
     */
    public OptBoolean lenient() default OptBoolean.DEFAULT;
    /**
     * Set of {@link JsonFormat.Feature}s to explicitly enable with respect
     * to handling of annotated property. This will have precedence over possible
     * global configuration.
     *
     * @since 2.6
     */
    public JsonFormat.Feature[] with() default { };
    /**
     * Set of {@link JsonFormat.Feature}s to explicitly disable with respect
     * to handling of annotated property. This will have precedence over possible
     * global configuration.
     *
     * @since 2.6
     */
    public JsonFormat.Feature[] without() default { };
    /*
    /**********************************************************
    /* Value enumeration(s), value class(es)
    /**********************************************************
     */
    /**
     * Value enumeration used for indicating preferred Shape; translates
     * loosely to JSON types, with some extra values to indicate less precise
     * choices (i.e. allowing one of multiple actual shapes)
     */
    public enum Shape
    {
        /**
         * Marker enum value that indicates "whatever" choice, meaning that annotation
         * does NOT specify shape to use.
         * Note that this is different from {@link Shape#NATURAL}, which
         * specifically instructs use of the "natural" shape for datatype.
         */
        ANY,
        /**
         * Marker enum value that indicates the "default" choice for given datatype;
         * for example, JSON String for {@link java.lang.String}, or JSON Number
         * for Java numbers.
         * Note that this is different from {@link Shape#ANY} in that this is actual
         * explicit choice that overrides possible default settings.
         *
         * @since 2.8
         */
        NATURAL,
        
        /**
         * Value that indicates shape should not be structural (that is, not
         * {@link #ARRAY} or {@link #OBJECT}, but can be any other shape.
         */
        SCALAR,
        /**
         * Value that indicates that (JSON) Array type should be used.
         */
        ARRAY,
        
        /**
         * Value that indicates that (JSON) Object type should be used.
         */
        OBJECT,
        /**
         * Value that indicates that a numeric (JSON) type should be used
         * (but does not specify whether integer or floating-point representation
         * should be used)
         */
        NUMBER,
        /**
         * Value that indicates that floating-point numeric type should be used
         */
        NUMBER_FLOAT,
        /**
         * Value that indicates that integer number type should be used
         * (and not {@link #NUMBER_FLOAT}).
         */
        NUMBER_INT,
        /**
         * Value that indicates that (JSON) String type should be used.
         */
        STRING,
        
        /**
         * Value that indicates that (JSON) boolean type
         * (true, false) should be used.
         */
        BOOLEAN,
        /**
         * Value that indicates that Binary type (native, if format supports it;
         * encoding using Base64 if only textual types supported) should be used.
         *
         * @since 2.10
         */
        BINARY
        ;
        public boolean isNumeric() {
            return (this == NUMBER) || (this == NUMBER_INT) || (this == NUMBER_FLOAT);
        }
        public boolean isStructured() {
            return (this == OBJECT) || (this == ARRAY);
        }
    }
    /**
     * Set of features that can be enabled/disabled for property annotated.
     * These often relate to specific SerializationFeature
     * or DeserializationFeature, as noted by entries.
     *
     * Note that whether specific setting has an effect depends on whether
     * JsonSerializer / JsonDeserializer being used
     * takes the format setting into account. If not, please file an issue
     * for adding support via issue tracker for package that has handlers
     * (if you know which one; if not, just use `jackson-databind`).
     *
     * @since 2.6
     */
    public enum Feature {
        /**
         * Override for DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY
         * which will allow deserialization of JSON non-array values into single-element
         * Java arrays and {@link java.util.Collection}s.
         */
        ACCEPT_SINGLE_VALUE_AS_ARRAY,
        /**
         * Override for MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES,
         * which allows case-insensitive matching of property names (but NOT values,
         * see {@link #ACCEPT_CASE_INSENSITIVE_VALUES} for that).
         *
         * Only affects deserialization, has no effect on serialization.
         * 
         * @since 2.8
         */
        ACCEPT_CASE_INSENSITIVE_PROPERTIES,
        /**
         * Override for MapperFeature.ACCEPT_CASE_INSENSITIVE_VALUES,
         * which allows case-sensitive matching of (some) property values, such
         * as {@code Enum}s.
         * Only affects deserialization, has no effect on serialization.
         * 
         * @since 2.10
         */
        ACCEPT_CASE_INSENSITIVE_VALUES,
        /**
         * Override for SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS,
         * similar constraints apply.
         */
        WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS,
        /**
         * Override for SerializationFeature.WRITE_DATES_WITH_ZONE_ID,
         * similar constraints apply.
         */
        WRITE_DATES_WITH_ZONE_ID,
        /**
         * Override for SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED
         * which will force serialization of single-element arrays and {@link java.util.Collection}s
         * as that single element and excluding array wrapper.
         */
        WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED,
        /**
         * Override for SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS,
         * enabling of which will force sorting of {@link java.util.Map} keys before
         * serialization.
         */
        WRITE_SORTED_MAP_ENTRIES,
        /**
         * Override for DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIMEZONE
         * that specifies whether context provided timezone
         * DeserializationContext.getTimeZone() should be used to adjust Date/Time
         * values on deserialization, even if value itself contains timezone informatio
         *
         * NOTE: due to limitations of "old" JDK date/time types (that is,
         * {@link java.util.Date} and {@link java.util.Calendar}), this setting is only
         * applicable to Joda and Java 8 date/time values,
         * but not to java.util.Date or java.util.Calendar.
         *
         * @since 2.8
         */
        ADJUST_DATES_TO_CONTEXT_TIME_ZONE
    }
    /**
     * Helper class that encapsulates information equivalent to {@link java.lang.Boolean}
     * valued {@link java.util.EnumMap}.
     *
     * @since 2.6
     */
    public static class Features
    {
        private final int _enabled, _disabled;
        private final static Features EMPTY = new Features(0, 0);
        
        private Features(int e, int d) {
            _enabled = e;
            _disabled = d;
        }
        public static Features empty() {
            return EMPTY;
        }
        
        public static Features construct(JsonFormat f) {
            return construct(f.with(), f.without());
        }
        
        public static Features construct(Feature[] enabled, Feature[] disabled)
        {
            int e = 0;
            for (Feature f : enabled) {
                e |= (1 << f.ordinal());
            }
            int d = 0;
            for (Feature f : disabled) {
                d |= (1 << f.ordinal());
            }
            return new Features(e, d);
        }
        public Features withOverrides(Features overrides) {
            // Cheap checks first: maybe one is empty?
            if (overrides == null) {
                return this;
            }
            int overrideD = overrides._disabled;
            int overrideE = overrides._enabled;
            if ((overrideD == 0) && (overrideE == 0)) {
                return this;
            }
            if ((_enabled == 0) && (_disabled == 0)) {
                return overrides;
            }
            // If not, calculate combination with overrides
            int newE = (_enabled & ~overrideD) | overrideE;
            int newD = (_disabled & ~overrideE) | overrideD;
            
            // one more thing; no point in creating new instance if there's no change
            if ((newE == _enabled) && (newD == _disabled)) {
                return this;
            }
            
            return new Features(newE, newD);
        }
        public Features with(Feature...features) {
            int e = _enabled;
            for (Feature f : features) {
                e |= (1 << f.ordinal());
            }
            return (e == _enabled) ? this : new Features(e, _disabled);
        }
        public Features without(Feature...features) {
            int d = _disabled;
            for (Feature f : features) {
                d |= (1 << f.ordinal());
            }
            return (d == _disabled) ? this : new Features(_enabled, d);
        }
        public Boolean get(Feature f) {
            int mask = (1 << f.ordinal());
            if ((_disabled & mask) != 0) {
                return Boolean.FALSE;
            }
            if ((_enabled & mask) != 0) {
                return Boolean.TRUE;
            }
            return null;
        }
        @Override
        public String toString() {
            if (this == EMPTY) {
                return "EMPTY";
            }
            return String.format("(enabled=0x%x,disabled=0x%x)", _enabled, _disabled);
        }
        @Override
        public int hashCode() {
            return _disabled + _enabled;
        }
        @Override
        public boolean equals(Object o) {
            if (o == this) return true;
            if (o == null) return false;
            if (o.getClass() != getClass()) return false;
            Features other = (Features) o;
            return (other._enabled == _enabled) && (other._disabled == _disabled);
        }
    }
    /**
     * Helper class used to contain information from a single {@link JsonFormat}
     * annotation.
     */
    public static class Value
        implements JacksonAnnotationValue
         *   Boolean.TRUE.equals(getLenient())
         *
         * that is, returns {@code true} if (and only if) leniency has been explicitly
         * set to {code true}; but not if it is undefined.
         *
         * @since 2.9
         */
        public boolean isLenient() {
            return Boolean.TRUE.equals(_lenient);
        }
        /**
         * Alternate access (compared to {@link #getTimeZone()}) which is useful
         * when caller just wants time zone id to convert, but not as JDK
         * provided {@link TimeZone}
         * 
         * @since 2.4
         */
        public String timeZoneAsString() {
            if (_timezone != null) {
                return _timezone.getID();
            }
            return _timezoneStr;
        }
        
        public TimeZone getTimeZone() {
            TimeZone tz = _timezone;
            if (tz == null) {
                if (_timezoneStr == null) {
                    return null;
                }
                tz = TimeZone.getTimeZone(_timezoneStr);
                _timezone = tz;
            }
            return tz;
        }
        /**
         * @since 2.4
         */
        public boolean hasShape() { return _shape != Shape.ANY; }
        
        /**
         * @since 2.4
         */
        public boolean hasPattern() {
            return (_pattern != null) && (_pattern.length() > 0);
        }
        
        /**
         * @since 2.4
         */
        public boolean hasLocale() { return _locale != null; }
        /**
         * @since 2.4
         */
        public boolean hasTimeZone() {
            return (_timezone != null) || (_timezoneStr != null && !_timezoneStr.isEmpty());
        }
        /**
         * Accessor for checking whether there is a setting for leniency.
         * NOTE: does NOT mean that `lenient` is `true` necessarily; just that
         * it has been set.
         *
         * @since 2.9
         */
        public boolean hasLenient() {
            return _lenient != null;
        }
        /**
         * Accessor for checking whether this format value has specific setting for
         * given feature. Result is 3-valued with either `null`, {@link Boolean#TRUE} or
         * {@link Boolean#FALSE}, indicating 'yes/no/dunno' choices, where `null` ("dunno")
         * indicates that the default handling should be used based on global defaults,
         * and there is no format override.
         *
         * @since 2.6
         */
        public Boolean getFeature(JsonFormat.Feature f) {
            return _features.get(f);
        }
        /**
         * Accessor for getting full set of features enabled/disabled.
         *
         * @since 2.8
         */
        public Features getFeatures() {
            return _features;
        }
        @Override
        public String toString() {
            return String.format("JsonFormat.Value(pattern=%s,shape=%s,lenient=%s,locale=%s,timezone=%s,features=%s)",
                    _pattern, _shape, _lenient, _locale, _timezoneStr, _features);
        }
        @Override
        public int hashCode() {
             int hash = (_timezoneStr == null) ? 1 : _timezoneStr.hashCode();
             if (_pattern != null) {
                 hash ^= _pattern.hashCode();
             }
             hash += _shape.hashCode();
             if (_lenient != null) {
                 hash ^= _lenient.hashCode();
             }
             if (_locale != null) {
                 hash += _locale.hashCode();
             }
             hash ^= _features.hashCode();
             return hash;
        }
        @Override
        public boolean equals(Object o) {
            if (o == this) return true;
            if (o == null) return false;
            if (o.getClass() != getClass()) return false;
            Value other = (Value) o;
            if ((_shape != other._shape) 
                    || !_features.equals(other._features)) {
                return false;
            }
            return _equal(_lenient, other._lenient)
                    && _equal(_timezoneStr, other._timezoneStr)
                    && _equal(_pattern, other._pattern)
                    && _equal(_timezone, other._timezone)
                    && _equal(_locale, other._locale);
        }
        private static