Jackson Annotations Source Code

Jackson is "the Java JSON library" or "the best JSON parser for Java". Or simply as "JSON for Java".

Jackson Annotations Source Code files are provided in the source packge (jackson-annotations-2.12.4-sources.jar). You can download it at Jackson Maven Website.

You can also browse Jackson Annotations Source Code below:

✍: FYIcenter.com

com/fasterxml/jackson/databind/introspect/DefaultAccessorNamingStrategy.java

package com.fasterxml.jackson.databind.introspect;

import java.util.HashSet;
import java.util.Set;

import com.fasterxml.jackson.databind.AnnotationIntrospector;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
import com.fasterxml.jackson.databind.cfg.MapperConfig;
import com.fasterxml.jackson.databind.jdk14.JDK14Util;

/**
 * Default {@link AccessorNamingStrategy} used by Jackson: to be used either as-is,
 * or as base-class with overrides.
 *
 * @since 2.12
 */
public class DefaultAccessorNamingStrategy
    extends AccessorNamingStrategy
{
    /**
     * Definition of a handler API to use for checking whether given base name
     * (remainder of accessor method name after removing prefix) is acceptable
     * based on various rules.
     *
     * @since 2.12
     */
    public interface BaseNameValidator {
        public boolean accept(char firstChar, String basename, int offset);
    }

    protected final MapperConfig<?> _config;
    protected final AnnotatedClass _forClass;

    /**
     * Optional validator for checking that base name
     */
    protected final BaseNameValidator _baseNameValidator;

    protected final boolean _stdBeanNaming;

    protected final String _getterPrefix;
    protected final String _isGetterPrefix;

    /**
     * Prefix used by auto-detected mutators ("setters"): usually "set",
     * but differs for builder objects ("with" by default).
     */
    protected final String _mutatorPrefix;

    protected DefaultAccessorNamingStrategy(MapperConfig<?> config, AnnotatedClass forClass,
            String mutatorPrefix, String getterPrefix, String isGetterPrefix,
            BaseNameValidator baseNameValidator)
    {
        _config = config;
        _forClass = forClass;

        _stdBeanNaming = config.isEnabled(MapperFeature.USE_STD_BEAN_NAMING);
        _mutatorPrefix = mutatorPrefix;
        _getterPrefix = getterPrefix;
        _isGetterPrefix = isGetterPrefix;
        _baseNameValidator = baseNameValidator;
    }
    
    @Override
    public String findNameForIsGetter(AnnotatedMethod am, String name)
    {
        if (_isGetterPrefix != null) {
            final Class<?> rt = am.getRawType();
            if (rt == Boolean.class || rt == Boolean.TYPE) {
                if (name.startsWith(_isGetterPrefix)) {
                    return _stdBeanNaming
                            ? stdManglePropertyName(name, 2)
                            : legacyManglePropertyName(name, 2);
                }
            }
        }
        return null;
    }

        @Override
    public String findNameForRegularGetter(AnnotatedMethod am, String name)
    {
        if ((_getterPrefix != null) && name.startsWith(_getterPrefix)) {
            // 16-Feb-2009, tatu: To handle [JACKSON-53], need to block CGLib-provided
            // method "getCallbacks". Not sure of exact safe criteria to get decent
            // coverage without false matches; but for now let's assume there is
            // no reason to use any such getter from CGLib.
            if ("getCallbacks".equals(name)) {
                if (_isCglibGetCallbacks(am)) {
                    return null;
                }
            } else if ("getMetaClass".equals(name)) {
                // 30-Apr-2009, tatu: Need to suppress serialization of a cyclic reference
                if (_isGroovyMetaClassGetter(am)) {
                    return null;
                }
            }
            return _stdBeanNaming
                    ? stdManglePropertyName(name, _getterPrefix.length())
                    : legacyManglePropertyName(name, _getterPrefix.length());
        }
        return null;
    }

    @Override
    public String findNameForMutator(AnnotatedMethod am, String name)
    {
        if ((_mutatorPrefix != null) && name.startsWith(_mutatorPrefix)) {
            return _stdBeanNaming
                    ? stdManglePropertyName(name, _mutatorPrefix.length())
                    : legacyManglePropertyName(name, _mutatorPrefix.length());
        }
        return null;
    }

    // Default implementation simply returns name as-is
    @Override
    public String modifyFieldName(AnnotatedField field, String name) {
        return name;
    }

    /*
    /**********************************************************************
    /* Name-mangling methods copied in 2.12 from "BeanUtil"
    /**********************************************************************
     */

    /**
     * Method called to figure out name of the property, given 
     * corresponding suggested name based on a method or field name.
     *
     * @param basename Name of accessor/mutator method, not including prefix
     *  ("get"/"is"/"set")
     */
    protected String legacyManglePropertyName(final String basename, final int offset)
    {
        final int end = basename.length();
        if (end == offset) { // empty name, nope
            return null;
        }
        char c = basename.charAt(offset);
        // 12-Oct-2020, tatu: Additional configurability; allow checking that
        //    base name is acceptable (currently just by checking first character)
        if (_baseNameValidator != null) {
            if (!_baseNameValidator.accept(c, basename, offset)) {
                return null;
            }
        }

        // next check: is the first character upper case? If not, return as is
        char d = Character.toLowerCase(c);
        
        if (c == d) {
            return basename.substring(offset);
        }
        // otherwise, lower case initial chars. Common case first, just one char
        StringBuilder sb = new StringBuilder(end - offset);
        sb.append(d);
        int i = offset+1;
        for (; i < end; ++i) {
            c = basename.charAt(i);
            d = Character.toLowerCase(c);
            if (c == d) {
                sb.append(basename, i, end);
                break;
            }
            sb.append(d);
        }
        return sb.toString();
    }

    protected String stdManglePropertyName(final String basename, final int offset)
    {
        final int end = basename.length();
        if (end == offset) { // empty name, nope
            return null;
        }
        // first: if it doesn't start with capital, return as-is
        char c0 = basename.charAt(offset);
        // 12-Oct-2020, tatu: Additional configurability; allow checking that
        //    base name is acceptable (currently just by checking first character)
        if (_baseNameValidator != null) {
            if (!_baseNameValidator.accept(c0, basename, offset)) {
                return null;
            }
        }

        char c1 = Character.toLowerCase(c0);
        if (c0 == c1) {
            return basename.substring(offset);
        }
        // 17-Dec-2014, tatu: As per [databind#653], need to follow more
        //   closely Java Beans spec; specifically, if two first are upper-case,
        //   then no lower-casing should be done.
        if ((offset + 1) < end) {
            if (Character.isUpperCase(basename.charAt(offset+1))) {
                return basename.substring(offset);
            }
        }
        StringBuilder sb = new StringBuilder(end - offset);
        sb.append(c1);
        sb.append(basename, offset+1, end);
        return sb.toString();
    }

    /*
    /**********************************************************************
    /* Legacy methods moved in 2.12 from "BeanUtil" -- are these still needed?
    /**********************************************************************
     */

    // This method was added to address the need to weed out CGLib-injected
    // "getCallbacks" method. 
    // At this point caller has detected a potential getter method with
    // name "getCallbacks" and we need to determine if it is indeed injected
    // by Cglib. We do this by verifying that the  result type is "net.sf.cglib.proxy.Callback[]"
    protected boolean _isCglibGetCallbacks(AnnotatedMethod am)
    {
        Class<?> rt = am.getRawType();
        // Ok, first: must return an array type
        if (rt.isArray()) {
            // And that type needs to be "net.sf.cglib.proxy.Callback".
            // Theoretically could just be a type that implements it, but
            // for now let's keep things simple, fix if need be.

            Class<?> compType = rt.getComponentType();
            // Actually, let's just verify it's a "net.sf.cglib.*" class/interface
            final String className = compType.getName();
            if (className.contains(".cglib")) {
                return className.startsWith("net.sf.cglib")
                    // also, as per [JACKSON-177]
                    || className.startsWith("org.hibernate.repackage.cglib")
                    // and [core#674]
                    || className.startsWith("org.springframework.cglib");
            }
        }
        return false;
    }

    // Another helper method to deal with Groovy's problematic metadata accessors
    protected boolean _isGroovyMetaClassGetter(AnnotatedMethod am) {
        return am.getRawType().getName().startsWith("groovy.lang");
    }

    /*
    /**********************************************************************
    /* Standard Provider implementation
    /**********************************************************************
     */

    /**
     * Provider for {@link DefaultAccessorNamingStrategy}.
     *<p>
     * Default instance will use following default prefixes:
     *<ul>
     * <li>Setter for regular POJOs: "set"
     *  </li>
     * <li>Builder-mutator: "with"
     *  </li>
     * <li>Regular getter: "get"
     *  </li>
     * <li>Is-getter (for Boolean values): "is"
     *  </li>
     *</ul>
     * and no additional restrictions on base names accepted (configurable for
     * limits using {@link BaseNameValidator}), allowing names like
     * "get_value()" and "getvalue()".
     */
    public static class Provider
        extends AccessorNamingStrategy.Provider
        implements java.io.Serializable
    {
        private static final long serialVersionUID = 1L;

        protected final String _setterPrefix;
        protected final String _withPrefix;

        protected final String _getterPrefix;
        protected final String _isGetterPrefix;

        protected final BaseNameValidator _baseNameValidator;

        public Provider() {
            this("set", JsonPOJOBuilder.DEFAULT_WITH_PREFIX,
                    "get", "is", null);
        }

        protected Provider(Provider p,
                String setterPrefix, String withPrefix,
                String getterPrefix, String isGetterPrefix)
        {
            this(setterPrefix, withPrefix, getterPrefix, isGetterPrefix,
                    p._baseNameValidator);
        }

        protected Provider(Provider p, BaseNameValidator vld)
        {
            this(p._setterPrefix, p._withPrefix,
                    p._getterPrefix, p._isGetterPrefix, vld);
        }

        protected Provider(String setterPrefix, String withPrefix,
                String getterPrefix, String isGetterPrefix,
                BaseNameValidator vld)
        {
            _setterPrefix = setterPrefix;
            _withPrefix = withPrefix;
            _getterPrefix = getterPrefix;
            _isGetterPrefix = isGetterPrefix;
            _baseNameValidator = vld;
        }

        /**
         * Mutant factory for changing the prefix used for "setter"
         * methods
         *
         * @param prefix Prefix to use; or empty String {@code ""} to not use
         *   any prefix (meaning signature-compatible method name is used as
         *   the property basename (and subject to name mangling)),
         *   or {@code null} to prevent name-based detection.
         *
         * @return Provider instance with specified setter-prefix
         */
        public Provider withSetterPrefix(String prefix) {
            return new Provider(this,
                    prefix, _withPrefix, _getterPrefix, _isGetterPrefix);
        }
        
        /**
         * Mutant factory for changing the prefix used for Builders
         * (from default {@link JsonPOJOBuilder#DEFAULT_WITH_PREFIX})
         *
         * @param prefix Prefix to use; or empty String {@code ""} to not use
         *   any prefix (meaning signature-compatible method name is used as
         *   the property basename (and subject to name mangling)),
         *   or {@code null} to prevent name-based detection.
         *
         * @return Provider instance with specified with-prefix
         */
        public Provider withBuilderPrefix(String prefix) {
            return new Provider(this,
                    _setterPrefix, prefix, _getterPrefix, _isGetterPrefix);
        }

        /**
         * Mutant factory for changing the prefix used for "getter"
         * methods
         *
         * @param prefix Prefix to use; or empty String {@code ""} to not use
         *   any prefix (meaning signature-compatible method name is used as
         *   the property basename (and subject to name mangling)),
         *   or {@code null} to prevent name-based detection.
         *
         * @return Provider instance with specified getter-prefix
         */
        public Provider withGetterPrefix(String prefix) {
            return new Provider(this,
                    _setterPrefix, _withPrefix, prefix, _isGetterPrefix);
        }

        /**
         * Mutant factory for changing the prefix used for "is-getter"
         * methods (getters that return boolean/Boolean value).
         *
         * @param prefix Prefix to use; or empty String {@code ""} to not use
         *   any prefix (meaning signature-compatible method name is used as
         *   the property basename (and subject to name mangling)).
         *   or {@code null} to prevent name-based detection.
         *
         * @return Provider instance with specified is-getter-prefix
         */
        public Provider withIsGetterPrefix(String prefix) {
            return new Provider(this,
                    _setterPrefix, _withPrefix, _getterPrefix, prefix);
        }

        /**
         * Mutant factory for changing the rules regarding which characters
         * are allowed as the first character of property base name, after
         * checking and removing prefix.
         *<p>
         * For example, consider "getter" method candidate (no arguments, has return
         * type) named {@code getValue()} is considered, with "getter-prefix"
         * defined as {@code get}, then base name is {@code Value} and the
         * first character to consider is {@code V}. Upper-case letters are
         * always accepted so this is fine.
         * But with similar settings, method {@code get_value()} would only be
         * recognized as getter if {@code allowNonLetterFirstChar} is set to
         * {@code true}: otherwise it will not be considered a getter-method.
         * Similarly "is-getter" candidate method with name {@code island()}
         * would only be considered if {@code allowLowerCaseFirstChar} is set
         * to {@code true}.
         *
         * @param allowLowerCaseFirstChar Whether base names that start with lower-case
         *    letter (like {@code "a"} or {@code "b"}) are accepted as valid or not:
         *    consider difference between "setter-methods" {@code setValue()} and {@code setvalue()}.
         * @param allowNonLetterFirstChar  Whether base names that start with non-letter
         *    character (like {@code "_"} or number {@code 1}) are accepted as valid or not:
         *    consider difference between "setter-methods" {@code setValue()} and {@code set_value()}.
         *
         * @return Provider instance with specified validity rules
         */
        public Provider withFirstCharAcceptance(boolean allowLowerCaseFirstChar,
                boolean allowNonLetterFirstChar) {
            return withBaseNameValidator(
                    FirstCharBasedValidator.forFirstNameRule(allowLowerCaseFirstChar, allowNonLetterFirstChar));
        }

        /**
         * Mutant factory for specifying validator that is used to further verify that
         * base name derived from accessor name is acceptable: this can be used to add
         * further restrictions such as limit that the first character of the base name
         * is an upper-case letter.
         *
         * @param vld Validator to use, if any; {@code null} to indicate no additional rules
         *
         * @return Provider instance with specified base name validator to use, if any
         */
        public Provider withBaseNameValidator(BaseNameValidator vld) {
            return new Provider(this, vld);
        }
        
        @Override
        public AccessorNamingStrategy forPOJO(MapperConfig<?> config, AnnotatedClass targetClass)
        {
            return new DefaultAccessorNamingStrategy(config, targetClass,
                    _setterPrefix, _getterPrefix, _isGetterPrefix,
                    _baseNameValidator);
        }

        @Override
        public AccessorNamingStrategy forBuilder(MapperConfig<?> config,
                AnnotatedClass builderClass, BeanDescription valueTypeDesc)
        {
            AnnotationIntrospector ai = config.isAnnotationProcessingEnabled() ? config.getAnnotationIntrospector() : null;
            JsonPOJOBuilder.Value builderConfig = (ai == null) ? null : ai.findPOJOBuilderConfig(builderClass);
            String mutatorPrefix = (builderConfig == null) ? _withPrefix : builderConfig.withPrefix;
            return new DefaultAccessorNamingStrategy(config, builderClass,
                    mutatorPrefix, _getterPrefix, _isGetterPrefix,
                    _baseNameValidator);
        }

        @Override
        public AccessorNamingStrategy forRecord(MapperConfig<?> config, AnnotatedClass recordClass)
        {
            return new RecordNaming(config, recordClass);
        }
    }

    /**
     * Simple implementation of {@link BaseNameValidator} that checks the
     * first character and nothing else.
     *<p>
     * Instances are to be constructed using method
     * {@link FirstCharBasedValidator#forFirstNameRule}.
     */
    public static class FirstCharBasedValidator
        implements BaseNameValidator
    {
        private final boolean _allowLowerCaseFirstChar;
        private final boolean _allowNonLetterFirstChar;

        protected FirstCharBasedValidator(boolean allowLowerCaseFirstChar,
                boolean allowNonLetterFirstChar) {
            _allowLowerCaseFirstChar = allowLowerCaseFirstChar;
            _allowNonLetterFirstChar = allowNonLetterFirstChar;
        }

        /**
         * Factory method to use for getting an instance with specified first-character
         * restrictions, if any; or {@code null} if no checking is needed.
         *
         * @param allowLowerCaseFirstChar Whether base names that start with lower-case
         *    letter (like {@code "a"} or {@code "b"}) are accepted as valid or not:
         *    consider difference between "setter-methods" {@code setValue()} and {@code setvalue()}.
         * @param allowNonLetterFirstChar  Whether base names that start with non-letter
         *    character (like {@code "_"} or number {@code 1}) are accepted as valid or not:
         *    consider difference between "setter-methods" {@code setValue()} and {@code set_value()}.
         * 
         * @return Validator instance to use, if any; {@code null} to indicate no additional
         *   rules applied (case when both arguments are {@code false})
         */
        public static BaseNameValidator forFirstNameRule(boolean allowLowerCaseFirstChar,
                boolean allowNonLetterFirstChar) {
            if (!allowLowerCaseFirstChar && !allowNonLetterFirstChar) {
                return null;
            }
            return new FirstCharBasedValidator(allowLowerCaseFirstChar,
                    allowNonLetterFirstChar);
        }
            
        @Override
        public boolean accept(char firstChar, String basename, int offset) {
            // Ok, so... If UTF-16 letter, then check whether lc allowed
            // (title-case and upper-case both assumed to be acceptable by default)
            if (Character.isLetter(firstChar)) {
                return _allowLowerCaseFirstChar || !Character.isLowerCase(firstChar);
            }
            // Otherwise, non-letter checking applied
            return _allowNonLetterFirstChar;
        }
    }
    
    /**
     * Implementation used for supporting "non-prefix" naming convention of
     * Java 14 {@code java.lang.Record} types, and in particular find default
     * accessors for declared record fields.
     *<p>
     * Current / initial implementation will also recognize additional "normal"
     * getters ("get"-prefix) and is-getters ("is"-prefix and boolean return value)
     * by name.
     */
    public static class RecordNaming
        extends DefaultAccessorNamingStrategy
    {
        /**
         * Names of actual Record fields from definition; auto-detected.
         */
        protected final Set<String> _fieldNames;

        public RecordNaming(MapperConfig<?> config, AnnotatedClass forClass) {
            super(config, forClass,
                    // no setters for (immutable) Records:
                    null,
                    // trickier: regular fields are ok (handled differently), but should
                    // we also allow getter discovery? For now let's do so
                    "get", "is", null);
            _fieldNames = new HashSet<>();
            for (String name : JDK14Util.getRecordFieldNames(forClass.getRawType())) {
                _fieldNames.add(name);
            }
        }

        @Override
        public String findNameForRegularGetter(AnnotatedMethod am, String name)
        {
            // By default, field names are un-prefixed, but verify so that we will not
            // include "toString()" or additional custom methods (unless latter are
            // annotated for inclusion)
            if (_fieldNames.contains(name)) {
                return name;
            }
            // but also allow auto-detecting additional getters, if any?
            return super.findNameForRegularGetter(am, name);
        }
    }
}

com/fasterxml/jackson/databind/introspect/DefaultAccessorNamingStrategy.java

 

⇒ Jackson Dataformat Extensions

⇐ Jackson Data Binding Source Code

⇑ Downloading and Reviewing jackson-*.jar

⇑⇑ Jackson - Java JSON library

2022-02-19, 36205👍, 0💬