JacksonInject.java

package com.fasterxml.jackson.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Jackson-specific annotation used for indicating that value of
 * annotated property will be "injected", i.e. set based on value
 * configured by <code>ObjectMapper</code> (usually on per-call basis).
 * Usually property is not deserialized from JSON, although it is possible
 * to have injected value as default and still allow optional override
 * from JSON.
 */
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface JacksonInject
{
    /**
     * Logical id of the value to inject; if not specified (or specified
     * as empty String), will use id based on declared type of property.
     *
     * @return Logical id of the value to inject
     */
    public String value() default "";

    /**
     * Whether matching value from input (if any) is used for annotated property or not;
     * if disabled (`OptBoolean.FALSE`), input value (if any) will be ignored;
     * otherwise it will override injected value.
     *<p>
     * Default is `OptBoolean.DEFAULT`, which translates to `OptBoolean.TRUE`: this is
     * for backwards compatibility (2.8 and earlier always allow binding input value).
     *
     * @return {@link OptBoolean#TRUE} to enable use of value from input instead of
     *    injected value, if available; {@link OptBoolean#FALSE} if injected value will
     *    always be used regardless of input.
     *
     * @since 2.9
     */
    public OptBoolean useInput() default OptBoolean.DEFAULT;

    /**
     * Whether to throw an exception when the {@code ObjectMapper} does not find
     * the value to inject.
     *<p>
     * Default is {@code OptBoolean.DEFAULT} for backwards-compatibility: in this
     * case {@code ObjectMapper} defaults are used (which in turn are same
     * as {code OptBoolean.FALSE}).
     *
     * @return {@link OptBoolean#FALSE} to throw an exception; {@link OptBoolean#TRUE}
     * to avoid throwing it; or {@link OptBoolean#DEFAULT} to use configure defaults
     * (which are same as {@link OptBoolean#FALSE} for Jackson 2.x)
     *
     * @since 2.20
     */
    public OptBoolean optional() default OptBoolean.DEFAULT;

    /*
    /**********************************************************
    /* Value class used to enclose information, allow for
    /* merging of layered configuration settings, and eventually
    /* decouple higher level handling from Annotation types
    /* (which can not be implemented etc.)
    /**********************************************************
     */

    /**
     * Helper class used to contain information from a single {@link JacksonInject}
     * annotation, as well as to provide possible overrides from non-annotation sources.
     *
     * @since 2.9
     */
    public static class Value
        implements JacksonAnnotationValue<JacksonInject>,
            java.io.Serializable
    {
        private static final long serialVersionUID = 1L;

        protected final static Value EMPTY = new Value(null, null, null);

        /**
         * Id to use to access injected value; if `null`, "default" name, derived
         * from accessor will be used.
         */
        protected final Object _id;

        protected final Boolean _useInput;

        protected final Boolean _optional;

        protected Value(Object id, Boolean useInput, Boolean optional) {
            _id = id;
            _useInput = useInput;
            _optional = optional;
        }

        @Override
        public Class<JacksonInject> valueFor() {
            return JacksonInject.class;
        }

        /*
        /**********************************************************
        /* Factory methods
        /**********************************************************
         */

        public static Value empty() {
            return EMPTY;
        }

        @Deprecated //since 2.20
        public static Value construct(Object id, Boolean useInput) {
            return construct(id, useInput, null);
        }

        /**
         * @since 2.20
         */
        public static Value construct(Object id, Boolean useInput, Boolean optional) {
            if ("".equals(id)) {
                id = null;
            }
            if (_empty(id, useInput, optional)) {
                return EMPTY;
            }
            return new Value(id, useInput, optional);
        }

        public static Value from(JacksonInject src) {
            if (src == null) {
                return EMPTY;
            }
            return construct(src.value(), src.useInput().asBoolean(), src.optional().asBoolean());
        }

        public static Value forId(Object id) {
            return construct(id, null, null);
        }

        /*
        /**********************************************************
        /* Mutant factory methods
        /**********************************************************
         */

        public Value withId(Object id) {
            if (id == null) {
                if (_id == null) {
                    return this;
                }
            } else if (id.equals(_id)) {
                return this;
            }
            return new Value(id, _useInput, _optional);
        }

        public Value withUseInput(Boolean useInput) {
            if (useInput == null) {
                if (_useInput == null) {
                    return this;
                }
            } else if (useInput.equals(_useInput)) {
                return this;
            }
            return new Value(_id, useInput, _optional);
        }

        public Value withOptional(Boolean optional) {
            if (optional == null) {
                if (_optional == null) {
                    return this;
                }
            } else if (optional.equals(_optional)) {
                return this;
            }
            return new Value(_id, _useInput, optional);
        }

        /*
        /**********************************************************
        /* Accessors
        /**********************************************************
         */

        public Object getId() { return _id; }
        public Boolean getUseInput() { return _useInput; }
        public Boolean getOptional() { return _optional; }

        public boolean hasId() {
            return _id != null;
        }

        public boolean willUseInput(boolean defaultSetting) {
            return (_useInput == null) ? defaultSetting : _useInput.booleanValue();
        }

        /*
        /**********************************************************
        /* Std method overrides
        /**********************************************************
         */

        @Override
        public String toString() {
            return String.format("JacksonInject.Value(id=%s,useInput=%s,optional=%s)",
                    _id, _useInput, _optional);
        }

        @Override
        public int hashCode() {
            int h = 1;
            if (_id != null) {
                h += _id.hashCode();
            }
            if (_useInput != null) {
                h += _useInput.hashCode();
            }
            if (_optional != null) {
                h += _optional.hashCode();
            }
            return h;
        }

        @Override
        public boolean equals(Object o) {
            if (o == this) return true;
            if (o == null) return false;
            if (o.getClass() == getClass()) {
                Value other = (Value) o;

                return (_id == null && other._id == null
                        || _id != null && _id.equals(other._id))
                        && (_useInput == null && other._useInput == null
                        || _useInput != null && _useInput.equals(other._useInput))
                        && (_optional == null && other._optional == null
                        || _optional != null && _optional.equals(other._optional));
            }
            return false;
        }

        /*
        /**********************************************************
        /* Other
        /**********************************************************
         */

        private static boolean _empty(Object id, Boolean useInput, Boolean optional) {
            return (id == null) && (useInput == null) && optional == null;
        }
    }
}