All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.fasterxml.jackson.databind.jsontype.BasicPolymorphicTypeValidator Maven / Gradle / Ivy

There is a newer version: 2.17.0
Show newest version
package com.fasterxml.jackson.databind.jsontype;

import java.util.*;
import java.util.regex.Pattern;

import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.cfg.MapperConfig;

/**
 * Standard {@link BasicPolymorphicTypeValidator} implementation that users may want
 * to use for constructing validators based on simple class hierarchy and/or name patterns
 * to allow and/or deny certain subtypes.
 *

* Most commonly this is used to allow known safe subtypes based on common super type * or Java package name. *
* For example: *

 *
* * @since 2.10 */ public class BasicPolymorphicTypeValidator extends PolymorphicTypeValidator.Base implements java.io.Serializable { private static final long serialVersionUID = 1L; /* /********************************************************** /* Helper classes: matchers /********************************************************** */ /** * General matcher interface (predicate) for validating class values * (base type or resolved subtype) */ protected abstract static class TypeMatcher { public abstract boolean match(Class clazz); } /** * General matcher interface (predicate) for validating unresolved * subclass class name. */ protected abstract static class NameMatcher { public abstract boolean match(String clazzName); } /* /********************************************************** /* Builder class for configuring instances /********************************************************** */ /** * Builder class for configuring and constructing immutable * {@link BasicPolymorphicTypeValidator} instances. Criteria for allowing * polymorphic subtypes is specified by adding rules in priority order, starting * with the rules to evaluate first: when a matching rule is found, its status * ({@link PolymorphicTypeValidator.Validity#ALLOWED} or {@link PolymorphicTypeValidator.Validity#DENIED}) is used and no further * rules are checked. */ public static class Builder { protected Set> _invalidBaseTypes; /** * Collected matchers for base types to allow. */ protected List _baseTypeMatchers; /** * Collected name-based matchers for sub types to allow. */ protected List _subTypeNameMatchers; /** * Collected Class-based matchers for sub types to allow. */ protected List _subTypeClassMatchers; protected Builder() { } // // Methods for checking solely by base type (before subtype even considered) /** * Method for appending matcher that will allow all subtypes in cases where * nominal base type is specified class, or one of its subtypes. * For example, call to *
         *    builder.allowIfBaseType(MyBaseType.class)
         *
* would indicate that any polymorphic properties where declared base type * is {@code MyBaseType} (or subclass thereof) would allow all legal (assignment-compatible) * subtypes. */ public Builder allowIfBaseType(final Class baseOfBase) { return _appendBaseMatcher(new TypeMatcher() { @Override public boolean match(Class clazz) { return baseOfBase.isAssignableFrom(clazz); } }); } /** * Method for appending matcher that will allow all subtypes in cases where * nominal base type's class name matches given {@link Pattern} * For example, call to *
         *    builder.allowIfBaseType(Pattern.compile("com\\.mycompany\\..*")
         *
* would indicate that any polymorphic properties where declared base type * is in package {@code com.mycompany} would allow all legal (assignment-compatible) * subtypes. *

* NOTE! {@link Pattern} match is applied using * * if (patternForBase.matcher(typeId).matches()) { } * * that is, it must match the whole class name, not just part. */ public Builder allowIfBaseType(final Pattern patternForBase) { return _appendBaseMatcher(new TypeMatcher() { @Override public boolean match(Class clazz) { return patternForBase.matcher(clazz.getName()).matches(); } }); } /** * Method for appending matcher that will allow all subtypes in cases where * nominal base type's class name starts with specific prefix. * For example, call to *

         *    builder.allowIfBaseType("com.mycompany.")
         *
* would indicate that any polymorphic properties where declared base type * is in package {@code com.mycompany} would allow all legal (assignment-compatible) * subtypes. */ public Builder allowIfBaseType(final String prefixForBase) { return _appendBaseMatcher(new TypeMatcher() { @Override public boolean match(Class clazz) { return clazz.getName().startsWith(prefixForBase); } }); } /** * Method for appending matcher that will mark any polymorphic properties with exact * specific class to be invalid. * For example, call to *
         *    builder.denyforExactBaseType(Object.class)
         *
* would indicate that any polymorphic properties where declared base type * is {@code java.lang.Object} * would be deemed invalid, and attempt to deserialize values of such types * should result in an exception. */ public Builder denyForExactBaseType(final Class baseTypeToDeny) { if (_invalidBaseTypes == null) { _invalidBaseTypes = new HashSet<>(); } _invalidBaseTypes.add(baseTypeToDeny); return this; } // // Methods for considering subtype (base type was not enough) /** * Method for appending matcher that will allow specific subtype (regardless * of declared base type) if it is {@code subTypeBase} or its subtype. * For example, call to *
         *    builder.allowIfSubType(MyImplType.class)
         *
* would indicate that any polymorphic values with type of * is {@code MyImplType} (or subclass thereof) * would be allowed. */ public Builder allowIfSubType(final Class subTypeBase) { return _appendSubClassMatcher(new TypeMatcher() { @Override public boolean match(Class clazz) { return subTypeBase.isAssignableFrom(clazz); } }); } /** * Method for appending matcher that will allow specific subtype (regardless * of declared base type) in cases where subclass name matches given {@link Pattern}. * For example, call to *
         *    builder.allowIfSubType(Pattern.compile("com\\.mycompany\\.")
         *
* would indicate that any polymorphic values in package {@code com.mycompany} * would be allowed. *

* NOTE! {@link Pattern} match is applied using * * if (patternForSubType.matcher(typeId).matches()) { } * * that is, it must match the whole class name, not just part. */ public Builder allowIfSubType(final Pattern patternForSubType) { return _appendSubNameMatcher(new NameMatcher() { @Override public boolean match(String clazzName) { return patternForSubType.matcher(clazzName).matches(); } }); } /** * Method for appending matcher that will allow specific subtype (regardless * of declared base type) * in cases where subclass name starts with specified prefix * For example, call to *

         *    builder.allowIfSubType("com.mycompany.")
         *
* would indicate that any polymorphic values in package {@code com.mycompany} * would be allowed. */ public Builder allowIfSubType(final String prefixForSubType) { return _appendSubNameMatcher(new NameMatcher() { @Override public boolean match(String clazzName) { return clazzName.startsWith(prefixForSubType); } }); } public BasicPolymorphicTypeValidator build() { return new BasicPolymorphicTypeValidator(_invalidBaseTypes, (_baseTypeMatchers == null) ? null : _baseTypeMatchers.toArray(new TypeMatcher[0]), (_subTypeNameMatchers == null) ? null : _subTypeNameMatchers.toArray(new NameMatcher[0]), (_subTypeClassMatchers == null) ? null : _subTypeClassMatchers.toArray(new TypeMatcher[0]) ); } protected Builder _appendBaseMatcher(TypeMatcher matcher) { if (_baseTypeMatchers == null) { _baseTypeMatchers = new ArrayList<>(); } _baseTypeMatchers.add(matcher); return this; } protected Builder _appendSubNameMatcher(NameMatcher matcher) { if (_subTypeNameMatchers == null) { _subTypeNameMatchers = new ArrayList<>(); } _subTypeNameMatchers.add(matcher); return this; } protected Builder _appendSubClassMatcher(TypeMatcher matcher) { if (_subTypeClassMatchers == null) { _subTypeClassMatchers = new ArrayList<>(); } _subTypeClassMatchers.add(matcher); return this; } } /* /********************************************************** /* Actual implementation /********************************************************** */ /** * Set of specifically denied base types to indicate that use of specific * base types is not allowed: most commonly used to fully block use of * {@link java.lang.Object} as the base type. */ protected final Set> _invalidBaseTypes; /** * Set of matchers that can validate all values of polymorphic properties * that match specified allowed base types. */ protected final TypeMatcher[] _baseTypeMatchers; /** * Set of matchers that can validate specific values of polymorphic properties * that match subtype class name criteria. */ protected final NameMatcher[] _subTypeNameMatchers; /** * Set of matchers that can validate specific values of polymorphic properties * that match subtype class criteria. */ protected final TypeMatcher[] _subClassMatchers; protected BasicPolymorphicTypeValidator(Set> invalidBaseTypes, TypeMatcher[] baseTypeMatchers, NameMatcher[] subTypeNameMatchers, TypeMatcher[] subClassMatchers) { _invalidBaseTypes = invalidBaseTypes; _baseTypeMatchers = baseTypeMatchers; _subTypeNameMatchers = subTypeNameMatchers; _subClassMatchers = subClassMatchers; } public static Builder builder() { return new Builder(); } @Override public Validity validateBaseType(MapperConfig ctxt, JavaType baseType) { final Class rawBase = baseType.getRawClass(); if (_invalidBaseTypes != null) { if (_invalidBaseTypes.contains(rawBase)) { return Validity.DENIED; } } if (_baseTypeMatchers != null) { for (TypeMatcher m : _baseTypeMatchers) { if (m.match(rawBase)) { return Validity.ALLOWED; } } } return Validity.INDETERMINATE; } @Override public Validity validateSubClassName(MapperConfig ctxt, JavaType baseType, String subClassName) throws JsonMappingException { if (_subTypeNameMatchers != null) { for (NameMatcher m : _subTypeNameMatchers) { if (m.match(subClassName)) { return Validity.ALLOWED; } } } // could not yet decide, so: return Validity.INDETERMINATE; } @Override public Validity validateSubType(MapperConfig ctxt, JavaType baseType, JavaType subType) throws JsonMappingException { if (_subClassMatchers != null) { final Class subClass = subType.getRawClass(); for (TypeMatcher m : _subClassMatchers) { if (m.match(subClass)) { return Validity.ALLOWED; } } } // could not decide, callers gets to decide; usually will deny return Validity.INDETERMINATE; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy