Repository: victools/jsonschema-generator
Pull request #18 (2020-01-03 11:54:46)

Context independent attributes


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0896382..d0682f4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
 ## [Unreleased]
+### Added
+- Extended API for defining context independent collection of schema attributes (for array/collection items that are not directly under a member)
+
+### Changed
+- BREAKING CHANGE: `TypeAttributeOverride`'s second parameter is now the new `TypeScope` wrapper instead of just a `ResolvedType`
 
 ## [3.5.0]
 ### Added
diff --git a/README.md b/README.md
index b7f9565..43b7eed 100644
--- a/README.md
+++ b/README.md
@@ -11,7 +11,7 @@ Creating JSON Schema (Draft 7) from your Java classes utilising Jackson (inspire
 <dependency>
     <groupId>com.github.victools</groupId>
     <artifactId>jsonschema-generator</artifactId>
-    <version>3.5.0</version>
+    <version>4.0.0</version>
 </dependency>
 ```
 
@@ -94,12 +94,16 @@ Some available modules are:
 ```java
 import com.github.victools.jsonschema.generator.FieldScope;
 import com.github.victools.jsonschema.generator.SchemaGeneratorConfigBuilder;
+import com.github.victools.jsonschema.generator.TypeScope;
 ```
 ```java
 SchemaGeneratorConfigBuilder configBuilder = new SchemaGeneratorConfigBuilder(objectMapper);
+configBuilder.forTypesInGeneral()
+    // populate the "title" of all schemas with a description of the java type
+    .withTitleResolver(TypeScope::getSimpleTypeDescription);
 configBuilder.forFields()
-    // populate the "title" of all fields with a description of the field's type
-    .withTitleResolver(FieldScope::getSimpleTypeDescription);
+    // show the original field name as the "description" (may differ from the overridden property name in the schema)
+    .withDescriptionResolver(FieldScope::getDeclaredName);
 ```
 
 ## Supported JSON Schema attributes
diff --git a/pom.xml b/pom.xml
index 650edbf..4f36b43 100644
--- a/pom.xml
+++ b/pom.xml
@@ -8,7 +8,7 @@
 
     <groupId>com.github.victools</groupId>
     <artifactId>jsonschema-generator</artifactId>
-    <version>3.5.1-SNAPSHOT</version>
+    <version>4.0.0-SNAPSHOT</version>
     <packaging>jar</packaging>
 
     <licenses>
diff --git a/src/main/java/com/github/victools/jsonschema/generator/ConfigFunction.java b/src/main/java/com/github/victools/jsonschema/generator/ConfigFunction.java
index 34abc16..9ee95dc 100644
--- a/src/main/java/com/github/victools/jsonschema/generator/ConfigFunction.java
+++ b/src/main/java/com/github/victools/jsonschema/generator/ConfigFunction.java
@@ -19,17 +19,17 @@ package com.github.victools.jsonschema.generator;
 /**
  * Functional interface for realising one of various configurations.
  *
- * @param <M> type of member the configuration applies to
+ * @param <S> type of scope/type representation the configuration applies to
  * @param <R> type of the configuration result
  */
 @FunctionalInterface
-public interface ConfigFunction<M extends MemberScope<?, ?>, R> {
+public interface ConfigFunction<S extends TypeScope, R> {
 
     /**
      * Applies this function to the given arguments.
      *
-     * @param member the member the configuration applies to
+     * @param target the targeted type representation the configuration applies to
      * @return the function result (may be null to indicate no specific configuration applies)
      */
-    R apply(M member);
+    R apply(S target);
 }
diff --git a/src/main/java/com/github/victools/jsonschema/generator/MemberScope.java b/src/main/java/com/github/victools/jsonschema/generator/MemberScope.java
index eb396f4..4b58859 100644
--- a/src/main/java/com/github/victools/jsonschema/generator/MemberScope.java
+++ b/src/main/java/com/github/victools/jsonschema/generator/MemberScope.java
@@ -31,7 +31,7 @@ import java.util.Optional;
  * @param <M> type of member in scope (i.e. {@link ResolvedField} or {@link ResolvedMethod}).
  * @param <T> type of java/reflection member in scope (i.e. {@link java.lang.reflect.Field FIeld} or {@link java.lang.reflect.Method Nethod}
  */
-public abstract class MemberScope<M extends ResolvedMember<T>, T extends Member> {
+public abstract class MemberScope<M extends ResolvedMember<T>, T extends Member> extends TypeScope {
 
     private final M member;
     private final ResolvedType overriddenType;
@@ -50,6 +50,7 @@ public abstract class MemberScope<M extends ResolvedMember<T>, T extends Member>
      */
     protected MemberScope(M member, ResolvedType overriddenType, String overriddenName,
             ResolvedTypeWithMembers declaringTypeMembers, TypeContext context) {
+        super(Optional.ofNullable(overriddenType).orElseGet(member::getType), context);
         this.member = member;
         this.overriddenType = overriddenType;
         this.overriddenName = overriddenName;
@@ -95,15 +96,6 @@ public abstract class MemberScope<M extends ResolvedMember<T>, T extends Member>
         return this.declaringTypeMembers;
     }
 
-    /**
-     * Getter for the overall type resolution context.
-     *
-     * @return type resolution context
-     */
-    public TypeContext getContext() {
-        return this.context;
-    }
-
     /**
      * Returns the type declared as the field's or method return value's type.
      *
@@ -124,18 +116,6 @@ public abstract class MemberScope<M extends ResolvedMember<T>, T extends Member>
         return this.overriddenType;
     }
 
-    /**
-     * Returns type of this member; if it has one, for methods this is the return type and for fields their field type.
-     *
-     * @return method return type or field type
-     * @see #getDeclaredType()
-     * @see #getOverriddenType()
-     */
-    public ResolvedType getType() {
-        return Optional.ofNullable(this.getOverriddenType())
-                .orElseGet(this::getDeclaredType);
-    }
-
     /**
      * Returns the member's name as specified in the declaring class.
      *
@@ -254,61 +234,6 @@ public abstract class MemberScope<M extends ResolvedMember<T>, T extends Member>
      */
     public abstract <A extends Annotation> A getAnnotationConsideringFieldAndGetter(Class<A> annotationClass);
 
-    /* ============================================== *
-     * Convenience methods for member in this context *
-     * ============================================== */
-    /**
-     * Determine whether this member's type should be treated as "{@value SchemaConstants#TAG_TYPE_ARRAY}" in the generated schema.
-     * <br>
-     * This is equivalent to calling: {@code scope.getContext().isContainerType(scope.getType())}
-     *
-     * @return whether this member's type is an array or sub type of {@link java.util.Collection Collection}
-     */
-    public boolean isContainerType() {
-        ResolvedType type = this.getType();
-        return type != null && this.getContext().isContainerType(type);
-    }
-
-    /**
-     * Identify the element/item type of the given "{@value SchemaConstants#TAG_TYPE_ARRAY}".
-     * <br>
-     * This is equivalent to calling: {@code scope.getContext().getContainerItemType(scope.getType())}
-     *
-     * @return type of elements/items
-     */
-    public ResolvedType getContainerItemType() {
-        ResolvedType type = this.getType();
-        return type == null ? null : this.getContext().getContainerItemType(type);
-    }
-
-    /**
-     * Constructing a string that represents this member's type (including possible type parameters and their actual types).
-     * <br>
-     * This is equivalent to calling: {@code scope.getContext().getSimpleTypeDescription(scope.getType())}
-     *
-     * @return resulting string
-     * @see #getType()
-     * @see TypeContext#getSimpleTypeDescription(ResolvedType)
-     */
-    public String getSimpleTypeDescription() {
-        ResolvedType type = this.getType();
-        return type == null ? "void" : this.getContext().getSimpleTypeDescription(type);
-    }
-
-    /**
-     * Constructing a string that fully represents this member's type (including possible type parameters and their actual types).
-     * <br>
-     * This is equivalent to calling: {@code scope.getContext().getFullTypeDescription(scope.getType())}
-     *
-     * @return resulting string
-     * @see #getType()
-     * @see TypeContext#getFullTypeDescription(ResolvedType)
-     */
-    public String getFullTypeDescription() {
-        ResolvedType type = this.getType();
-        return type == null ? "void" : this.getContext().getFullTypeDescription(type);
-    }
-
     /**
      * Returns the name to be used to reference this member in its parent's "properties".
      *
diff --git a/src/main/java/com/github/victools/jsonschema/generator/SchemaGeneratorConfig.java b/src/main/java/com/github/victools/jsonschema/generator/SchemaGeneratorConfig.java
index ba9de96..7315d77 100644
--- a/src/main/java/com/github/victools/jsonschema/generator/SchemaGeneratorConfig.java
+++ b/src/main/java/com/github/victools/jsonschema/generator/SchemaGeneratorConfig.java
@@ -210,6 +210,14 @@ public interface SchemaGeneratorConfig {
      */
     String resolveTitle(MethodScope method);
 
+    /**
+     * Determine the "title" of a context-independent type representation.
+     *
+     * @param scope context-independent type representation to determine "title" value for
+     * @return "title" in a JSON Schema (may be null)
+     */
+    String resolveTitleForType(TypeScope scope);
+
     /**
      * Determine the "description" of an object's field/property.
      *
@@ -226,6 +234,22 @@ public interface SchemaGeneratorConfig {
      */
     String resolveDescription(MethodScope method);
 
+    /**
+     * Determine the "description" of a context-independent type representation.
+     *
+     * @param scope context-independent type representation to determine "description" value for
+     * @return "description" in a JSON Schema (may be null)
+     */
+    String resolveDescriptionForType(TypeScope scope);
+
+    /**
+     * Determine the "default" value of an object's field/property.
+     *
+     * @param field object's field/property to determine "default" value for
+     * @return "default" in a JSON Schema (may be null)
+     */
+    Object resolveDefault(FieldScope field);
+
     /**
      * Determine the "default" value of a method's return value.
      *
@@ -235,12 +259,12 @@ public interface SchemaGeneratorConfig {
     Object resolveDefault(MethodScope method);
 
     /**
-     * Determine the "default" value of an object's field/property.
+     * Determine the "default" value of a context-independent type representation.
      *
-     * @param field object's field/property to determine "default" value for
+     * @param scope context-independent type representation to determine "default" value for
      * @return "default" in a JSON Schema (may be null)
      */
-    Object resolveDefault(FieldScope field);
+    Object resolveDefaultForType(TypeScope scope);
 
     /**
      * Determine the "enum"/"const" of an object's field/property.
@@ -258,6 +282,14 @@ public interface SchemaGeneratorConfig {
      */
     Collection<?> resolveEnum(MethodScope method);
 
+    /**
+     * Determine the "enum"/"const" of a context-independent type representation.
+     *
+     * @param scope context-independent type representation to determine "enum"/"const" value for
+     * @return "enum"/"const" in a JSON Schema (may be null)
+     */
+    Collection<?> resolveEnumForType(TypeScope scope);
+
     /**
      * Determine the "minLength" of an object's field/property.
      *
@@ -274,6 +306,14 @@ public interface SchemaGeneratorConfig {
      */
     Integer resolveStringMinLength(MethodScope method);
 
+    /**
+     * Determine the "minLength" of a context-independent type representation.
+     *
+     * @param scope context-independent type representation to determine "minLength" value for
+     * @return "minLength" in a JSON Schema (may be null)
+     */
+    Integer resolveStringMinLengthForType(TypeScope scope);
+
     /**
      * Determine the "maxLength" of an object's field/property.
      *
@@ -290,6 +330,14 @@ public interface SchemaGeneratorConfig {
      */
     Integer resolveStringMaxLength(MethodScope method);
 
+    /**
+     * Determine the "maxLength" of a context-independent type representation.
+     *
+     * @param scope context-independent type representation to determine "maxLength" value for
+     * @return "maxLength" in a JSON Schema (may be null)
+     */
+    Integer resolveStringMaxLengthForType(TypeScope scope);
+
     /**
      * Determine the "format" of an object's field/property.
      *
@@ -306,6 +354,14 @@ public interface SchemaGeneratorConfig {
      */
     String resolveStringFormat(MethodScope method);
 
+    /**
+     * Determine the "format" of a context-independent type representation.
+     *
+     * @param scope context-independent type representation to determine "format" value for
+     * @return "format" in a JSON Schema (may be null)
+     */
+    String resolveStringFormatForType(TypeScope scope);
+
     /**
      * Determine the "pattern" of an object's field/property.
      *
@@ -322,6 +378,14 @@ public interface SchemaGeneratorConfig {
      */
     String resolveStringPattern(MethodScope method);
 
+    /**
+     * Determine the "pattern" of a context-independent type representation.
+     *
+     * @param scope context-independent type representation to determine "pattern" value for
+     * @return "pattern" in a JSON Schema (may be null)
+     */
+    String resolveStringPatternForType(TypeScope scope);
+
     /**
      * Determine the "minimum" of an object's field/property.
      *
@@ -338,6 +402,14 @@ public interface SchemaGeneratorConfig {
      */
     BigDecimal resolveNumberInclusiveMinimum(MethodScope method);
 
+    /**
+     * Determine the "minimum" of a context-independent type representation.
+     *
+     * @param scope context-independent type representation to determine "minimum" value for
+     * @return "minimum" in a JSON Schema (may be null)
+     */
+    BigDecimal resolveNumberInclusiveMinimumForType(TypeScope scope);
+
     /**
      * Determine the "exclusiveMinimum" of an object's field/property.
      *
@@ -354,6 +426,14 @@ public interface SchemaGeneratorConfig {
      */
     BigDecimal resolveNumberExclusiveMinimum(MethodScope method);
 
+    /**
+     * Determine the "exclusiveMinimum" of a context-independent type representation.
+     *
+     * @param scope context-independent type representation to determine "exclusiveMinimum" value for
+     * @return "exclusiveMinimum" in a JSON Schema (may be null)
+     */
+    BigDecimal resolveNumberExclusiveMinimumForType(TypeScope scope);
+
     /**
      * Determine the "maximum" of an object's field/property.
      *
@@ -370,6 +450,14 @@ public interface SchemaGeneratorConfig {
      */
     BigDecimal resolveNumberInclusiveMaximum(MethodScope method);
 
+    /**
+     * Determine the "maximum" of a context-independent type representation.
+     *
+     * @param scope context-independent type representation to determine "maximum" value for
+     * @return "maximum" in a JSON Schema (may be null)
+     */
+    BigDecimal resolveNumberInclusiveMaximumForType(TypeScope scope);
+
     /**
      * Determine the "exclusiveMaximum" of an object's field/property.
      *
@@ -386,6 +474,14 @@ public interface SchemaGeneratorConfig {
      */
     BigDecimal resolveNumberExclusiveMaximum(MethodScope method);
 
+    /**
+     * Determine the "exclusiveMaximum" of a context-independent type representation.
+     *
+     * @param scope context-independent type representation to determine "exclusiveMaximum" value for
+     * @return "exclusiveMaximum" in a JSON Schema (may be null)
+     */
+    BigDecimal resolveNumberExclusiveMaximumForType(TypeScope scope);
+
     /**
      * Determine the "multipleOf" of an object's field/property.
      *
@@ -402,6 +498,14 @@ public interface SchemaGeneratorConfig {
      */
     BigDecimal resolveNumberMultipleOf(MethodScope method);
 
+    /**
+     * Determine the "multipleOf" of a context-independent type representation.
+     *
+     * @param scope context-independent type representation to determine "multipleOf" value for
+     * @return "multipleOf" in a JSON Schema (may be null)
+     */
+    BigDecimal resolveNumberMultipleOfForType(TypeScope scope);
+
     /**
      * Determine the "minItems" of an object's field/property.
      *
@@ -418,6 +522,14 @@ public interface SchemaGeneratorConfig {
      */
     Integer resolveArrayMinItems(MethodScope method);
 
+    /**
+     * Determine the "minItems" of a context-independent type representation.
+     *
+     * @param scope context-independent type representation to determine "minItems" value for
+     * @return "minItems" in a JSON Schema (may be null)
+     */
+    Integer resolveArrayMinItemsForType(TypeScope scope);
+
     /**
      * Determine the "maxItems" of an object's field/property.
      *
@@ -434,6 +546,14 @@ public interface SchemaGeneratorConfig {
      */
     Integer resolveArrayMaxItems(MethodScope method);
 
+    /**
+     * Determine the "maxItems" of a context-independent type representation.
+     *
+     * @param scope context-independent type representation to determine "maxItems" value for
+     * @return "maxItems" in a JSON Schema (may be null)
+     */
+    Integer resolveArrayMaxItemsForType(TypeScope scope);
+
     /**
      * Determine the "uniqueItems" of an object's field/property.
      *
@@ -449,4 +569,12 @@ public interface SchemaGeneratorConfig {
      * @return "uniqueItems" in a JSON Schema (may be null)
      */
     Boolean resolveArrayUniqueItems(MethodScope method);
+
+    /**
+     * Determine the "uniqueItems" of a context-independent type representation.
+     *
+     * @param scope context-independent type representation to determine "uniqueItems" value for
+     * @return "uniqueItems" in a JSON Schema (may be null)
+     */
+    Boolean resolveArrayUniqueItemsForType(TypeScope scope);
 }
diff --git a/src/main/java/com/github/victools/jsonschema/generator/SchemaGeneratorConfigBuilder.java b/src/main/java/com/github/victools/jsonschema/generator/SchemaGeneratorConfigBuilder.java
index f636993..16127ca 100644
--- a/src/main/java/com/github/victools/jsonschema/generator/SchemaGeneratorConfigBuilder.java
+++ b/src/main/java/com/github/victools/jsonschema/generator/SchemaGeneratorConfigBuilder.java
@@ -35,6 +35,7 @@ public class SchemaGeneratorConfigBuilder {
     private final OptionPreset preset;
 
     private final Map<Option, Boolean> options = new HashMap<>();
+    private final SchemaGeneratorTypeConfigPart<TypeScope> typesInGeneralConfigPart = new SchemaGeneratorTypeConfigPart<>();
     private final SchemaGeneratorConfigPart<FieldScope> fieldConfigPart = new SchemaGeneratorConfigPart<>();
     private final SchemaGeneratorConfigPart<MethodScope> methodConfigPart = new SchemaGeneratorConfigPart<>();
     private final List<CustomDefinitionProviderV2> customDefinitions = new ArrayList<>();
@@ -81,16 +82,28 @@ public class SchemaGeneratorConfigBuilder {
         // construct the actual configuration instance
         return new SchemaGeneratorConfigImpl(this.objectMapper,
                 completeSetOfOptions,
+                this.typesInGeneralConfigPart,
                 this.fieldConfigPart,
                 this.methodConfigPart,
                 this.customDefinitions,
                 this.typeAttributeOverrides);
     }
 
+    /**
+     * Get the part of this configuration builder dedicated to custom attribute look-ups for types in general, independent of the declaration context.
+     *
+     * @return configuration part responsible for handling types regardless of their declaration context
+     */
+    public SchemaGeneratorTypeConfigPart<TypeScope> forTypesInGeneral() {
+        return this.typesInGeneralConfigPart;
+    }
+
     /**
      * Get the part of this configuration builder dedicated to custom attribute look-ups for fields.
      *
      * @return configuration part responsible for handling of fields
+     * @see #forTypesInGeneral() : holding configurations also applying to methods or types that are not declared as member directly
+     * @see #forMethods() : holding configuration applying to methods
      */
     public SchemaGeneratorConfigPart<FieldScope> forFields() {
         return this.fieldConfigPart;
@@ -100,6 +113,8 @@ public class SchemaGeneratorConfigBuilder {
      * Get the part of this configuration builder dedicated to custom attribute look-ups for methods.
      *
      * @return configuration part responsible for handling of methods
+     * @see #forTypesInGeneral() : holding configurations also applying to fields or types that are not declared as member directly
+     * @see #forFields() : holding configuration applying to fields
      */
     public SchemaGeneratorConfigPart<MethodScope> forMethods() {
         return this.methodConfigPart;
diff --git a/src/main/java/com/github/victools/jsonschema/generator/SchemaGeneratorConfigPart.java b/src/main/java/com/github/victools/jsonschema/generator/SchemaGeneratorConfigPart.java
index 7d4d8fe..d87bf9d 100644
--- a/src/main/java/com/github/victools/jsonschema/generator/SchemaGeneratorConfigPart.java
+++ b/src/main/java/com/github/victools/jsonschema/generator/SchemaGeneratorConfigPart.java
@@ -22,7 +22,6 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
-import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 import java.util.function.Predicate;
@@ -33,24 +32,7 @@ import java.util.stream.Collectors;
  *
  * @param <M> type of the (resolved) member to analyse
  */
-public class SchemaGeneratorConfigPart<M extends MemberScope<?, ?>> {
-
-    /**
-     * Helper function for invoking a given function with the provided inputs or returning null no function returning anything but null themselves.
-     *
-     * @param <M> type of the provided member (to be forwarded as parameter to the given function)
-     * @param <R> type of the expected return value (of the given function)
-     * @param resolvers functions to invoke and return the first non-null result from
-     * @param member member (to be forwarded as first argument to a given function)
-     * @return return value of successfully invoked function or null
-     */
-    private static <M extends MemberScope<?, ?>, R> R getFirstDefinedValue(List<ConfigFunction<M, R>> resolvers, M member) {
-        return resolvers.stream()
-                .map(resolver -> resolver.apply(member))
-                .filter(Objects::nonNull)
-                .findFirst()
-                .orElse(null);
-    }
+public class SchemaGeneratorConfigPart<M extends MemberScope<?, ?>> extends SchemaGeneratorTypeConfigPart<M> {
 
     private final List<InstanceAttributeOverride<M>> instanceAttributeOverrides = new ArrayList<>();
 
@@ -68,39 +50,6 @@ public class SchemaGeneratorConfigPart<M extends MemberScope<?, ?>> {
     private final List<ConfigFunction<M, ResolvedType>> targetTypeOverrideResolvers = new ArrayList<>();
     private final List<ConfigFunction<M, String>> propertyNameOverrideResolvers = new ArrayList<>();
 
-    /*
-     * General fields independent of "type".
-     */
-    private final List<ConfigFunction<M, String>> titleResolvers = new ArrayList<>();
-    private final List<ConfigFunction<M, String>> descriptionResolvers = new ArrayList<>();
-    private final List<ConfigFunction<M, Object>> defaultResolvers = new ArrayList<>();
-    private final List<ConfigFunction<M, Collection<?>>> enumResolvers = new ArrayList<>();
-    private final List<ConfigFunction<M, Map<String, String>>> metadatas = new ArrayList<>();
-
-    /*
-     * Validation fields relating to a schema with "type": "string".
-     */
-    private final List<ConfigFunction<M, Integer>> stringMinLengthResolvers = new ArrayList<>();
-    private final List<ConfigFunction<M, Integer>> stringMaxLengthResolvers = new ArrayList<>();
-    private final List<ConfigFunction<M, String>> stringFormatResolvers = new ArrayList<>();
-    private final List<ConfigFunction<M, String>> stringPatternResolvers = new ArrayList<>();
-
-    /*
-     * Validation fields relating to a schema with "type": "integer" or "number".
-     */
-    private final List<ConfigFunction<M, BigDecimal>> numberInclusiveMinimumResolvers = new ArrayList<>();
-    private final List<ConfigFunction<M, BigDecimal>> numberExclusiveMinimumResolvers = new ArrayList<>();
-    private final List<ConfigFunction<M, BigDecimal>> numberInclusiveMaximumResolvers = new ArrayList<>();
-    private final List<ConfigFunction<M, BigDecimal>> numberExclusiveMaximumResolvers = new ArrayList<>();
-    private final List<ConfigFunction<M, BigDecimal>> numberMultipleOfResolvers = new ArrayList<>();
-
-    /*
-     * Validation fields relating to a schema with "type": "array".
-     */
-    private final List<ConfigFunction<M, Integer>> arrayMinItemsResolvers = new ArrayList<>();
-    private final List<ConfigFunction<M, Integer>> arrayMaxItemsResolvers = new ArrayList<>();
-    private final List<ConfigFunction<M, Boolean>> arrayUniqueItemsResolvers = new ArrayList<>();
-
     /**
      * Setter for override of attributes on a given JSON Schema node in the respective member.
      *
@@ -232,339 +181,83 @@ public class SchemaGeneratorConfigPart<M extends MemberScope<?, ?>> {
         return getFirstDefinedValue(this.propertyNameOverrideResolvers, member);
     }
 
-    /**
-     * Setter for "title" resolver.
-     *
-     * @param resolver how to determine the "title" of a JSON Schema
-     * @return this config part (for chaining)
-     */
+    @Override
     public SchemaGeneratorConfigPart<M> withTitleResolver(ConfigFunction<M, String> resolver) {
-        this.titleResolvers.add(resolver);
-        return this;
+        return (SchemaGeneratorConfigPart<M>) super.withTitleResolver(resolver);
     }
 
-    /**
-     * Determine the "title" of a given member.
-     *
-     * @param member member to determine "title" value for
-     * @return "title" in a JSON Schema (may be null)
-     */
-    public String resolveTitle(M member) {
-        return getFirstDefinedValue(this.titleResolvers, member);
-    }
-
-    /**
-     * Setter for "description" resolver.
-     *
-     * @param resolver how to determine the "description" of a JSON Schema
-     * @return this config part (for chaining)
-     */
+    @Override
     public SchemaGeneratorConfigPart<M> withDescriptionResolver(ConfigFunction<M, String> resolver) {
-        this.descriptionResolvers.add(resolver);
-        return this;
-    }
-
-    /**
-     * Determine the "description" of a given member.
-     *
-     * @param member member to determine "description" value for
-     * @return "description" in a JSON Schema (may be null)
-     */
-    public String resolveDescription(M member) {
-        return getFirstDefinedValue(this.descriptionResolvers, member);
+        return (SchemaGeneratorConfigPart<M>) super.withDescriptionResolver(resolver);
     }
 
-    /**
-     * Setter for "default" resolver.
-     *
-     * @param resolver how to determine the "default" of a JSON Schema
-     * @return this config part (for chaining)
-     */
+    @Override
     public SchemaGeneratorConfigPart<M> withDefaultResolver(ConfigFunction<M, Object> resolver) {
-        this.defaultResolvers.add(resolver);
-        return this;
+        return (SchemaGeneratorConfigPart<M>) super.withDefaultResolver(resolver);
     }
 
-    /**
-     * Determine the "default" of a given member.
-     *
-     * @param member member to determine "default" value for
-     * @return "default" in a JSON Schema (may be null)
-     */
-    public Object resolveDefault(M member) {
-        return getFirstDefinedValue(this.defaultResolvers, member);
-    }
-
-    /**
-     * Setter for "enum"/"const" resolver.
-     *
-     * @param resolver how to determine the "enum"/"const" of a JSON Schema
-     * @return this config part (for chaining)
-     */
+    @Override
     public SchemaGeneratorConfigPart<M> withEnumResolver(ConfigFunction<M, Collection<?>> resolver) {
-        this.enumResolvers.add(resolver);
-        return this;
-    }
-
-    /**
-     * Determine the "enum"/"const" of a given member.
-     *
-     * @param member member to determine "enum"/"const" value for
-     * @return "enum"/"const" in a JSON Schema (may be null)
-     */
-    public Collection<?> resolveEnum(M member) {
-        return getFirstDefinedValue(this.enumResolvers, member);
+        return (SchemaGeneratorConfigPart<M>) super.withEnumResolver(resolver);
     }
 
-    /**
-     * Setter for "minLength" resolver.
-     *
-     * @param resolver how to determine the "minLength" of a JSON Schema
-     * @return this config part (for chaining)
-     */
+    @Override
     public SchemaGeneratorConfigPart<M> withStringMinLengthResolver(ConfigFunction<M, Integer> resolver) {
-        this.stringMinLengthResolvers.add(resolver);
-        return this;
-    }
-
-    /**
-     * Determine the "minLength" of a given member.
-     *
-     * @param member member to determine "minLength" value for
-     * @return "minLength" in a JSON Schema (may be null)
-     */
-    public Integer resolveStringMinLength(M member) {
-        return getFirstDefinedValue(this.stringMinLengthResolvers, member);
+        return (SchemaGeneratorConfigPart<M>) super.withStringMinLengthResolver(resolver);
     }
 
-    /**
-     * Setter for "maxLength" resolver.
-     *
-     * @param resolver how to determine the "maxLength" of a JSON Schema
-     * @return this config part (for chaining)
-     */
+    @Override
     public SchemaGeneratorConfigPart<M> withStringMaxLengthResolver(ConfigFunction<M, Integer> resolver) {
-        this.stringMaxLengthResolvers.add(resolver);
-        return this;
-    }
-
-    /**
-     * Determine the "maxLength" of a given member.
-     *
-     * @param member member to determine "maxLength" value for
-     * @return "maxLength" in a JSON Schema (may be null)
-     */
-    public Integer resolveStringMaxLength(M member) {
-        return getFirstDefinedValue(this.stringMaxLengthResolvers, member);
+        return (SchemaGeneratorConfigPart<M>) super.withStringMaxLengthResolver(resolver);
     }
 
-    /**
-     * Setter for "format" resolver.
-     *
-     * @param resolver how to determine the "format" of a JSON Schema
-     * @return this config part (for chaining)
-     */
+    @Override
     public SchemaGeneratorConfigPart<M> withStringFormatResolver(ConfigFunction<M, String> resolver) {
-        this.stringFormatResolvers.add(resolver);
-        return this;
+        return (SchemaGeneratorConfigPart<M>) super.withStringFormatResolver(resolver);
     }
 
-    /**
-     * Determine the "format" of a given member.
-     *
-     * @param member member to determine "format" value for
-     * @return "format" in a JSON Schema (may be null)
-     */
-    public String resolveStringFormat(M member) {
-        return getFirstDefinedValue(this.stringFormatResolvers, member);
-    }
-
-    /**
-     * Setter for "format" resolver.
-     *
-     * @param resolver how to determine the "format" of a JSON Schema
-     * @return this config part (for chaining)
-     */
+    @Override
     public SchemaGeneratorConfigPart<M> withStringPatternResolver(ConfigFunction<M, String> resolver) {
-        this.stringPatternResolvers.add(resolver);
-        return this;
-    }
-
-    /**
-     * Determine the "format" of a given member.
-     *
-     * @param member member to determine "format" value for
-     * @return "format" in a JSON Schema (may be null)
-     */
-    public String resolveStringPattern(M member) {
-        return getFirstDefinedValue(this.stringPatternResolvers, member);
+        return (SchemaGeneratorConfigPart<M>) super.withStringPatternResolver(resolver);
     }
 
-    /**
-     * Setter for "minimum" resolver.
-     *
-     * @param resolver how to determine the "minimum" of a JSON Schema
-     * @return this config part (for chaining)
-     */
+    @Override
     public SchemaGeneratorConfigPart<M> withNumberInclusiveMinimumResolver(ConfigFunction<M, BigDecimal> resolver) {
-        this.numberInclusiveMinimumResolvers.add(resolver);
-        return this;
+        return (SchemaGeneratorConfigPart<M>) super.withNumberInclusiveMinimumResolver(resolver);
     }
 
-    /**
-     * Determine the "minimum" of a given member.
-     *
-     * @param member member to determine "minimum" value for
-     * @return "minimum" in a JSON Schema (may be null)
-     */
-    public BigDecimal resolveNumberInclusiveMinimum(M member) {
-        return getFirstDefinedValue(this.numberInclusiveMinimumResolvers, member);
-    }
-
-    /**
-     * Setter for "exclusiveMinimum" resolver.
-     *
-     * @param resolver how to determine the "exclusiveMinimum" of a JSON Schema
-     * @return this config part (for chaining)
-     */
+    @Override
     public SchemaGeneratorConfigPart<M> withNumberExclusiveMinimumResolver(ConfigFunction<M, BigDecimal> resolver) {
-        this.numberExclusiveMinimumResolvers.add(resolver);
-        return this;
-    }
-
-    /**
-     * Determine the "exclusiveMinimum" of a given member.
-     *
-     * @param member member to determine "exclusiveMinimum" value for
-     * @return "exclusiveMinimum" in a JSON Schema (may be null)
-     */
-    public BigDecimal resolveNumberExclusiveMinimum(M member) {
-        return getFirstDefinedValue(this.numberExclusiveMinimumResolvers, member);
+        return (SchemaGeneratorConfigPart<M>) super.withNumberExclusiveMinimumResolver(resolver);
     }
 
-    /**
-     * Setter for "maximum" resolver.
-     *
-     * @param resolver how to determine the "maximum" of a JSON Schema
-     * @return this config part (for chaining)
-     */
+    @Override
     public SchemaGeneratorConfigPart<M> withNumberInclusiveMaximumResolver(ConfigFunction<M, BigDecimal> resolver) {
-        this.numberInclusiveMaximumResolvers.add(resolver);
-        return this;
+        return (SchemaGeneratorConfigPart<M>) super.withNumberInclusiveMaximumResolver(resolver);
     }
 
-    /**
-     * Determine the "maximum" of a given member.
-     *
-     * @param member member to determine "maximum" value for
-     * @return "maximum" in a JSON Schema (may be null)
-     */
-    public BigDecimal resolveNumberInclusiveMaximum(M member) {
-        return getFirstDefinedValue(this.numberInclusiveMaximumResolvers, member);
-    }
-
-    /**
-     * Setter for "exclusiveMaximum" resolver.
-     *
-     * @param resolver how to determine the "exclusiveMaximum" of a JSON Schema
-     * @return this config part (for chaining)
-     */
+    @Override
     public SchemaGeneratorConfigPart<M> withNumberExclusiveMaximumResolver(ConfigFunction<M, BigDecimal> resolver) {
-        this.numberExclusiveMaximumResolvers.add(resolver);
-        return this;
+        return (SchemaGeneratorConfigPart<M>) super.withNumberExclusiveMaximumResolver(resolver);
     }
 
-    /**
-     * Determine the "exclusiveMaximum" of a given member.
-     *
-     * @param member member to determine "exclusiveMaximum" value for
-     * @return "exclusiveMaximum" in a JSON Schema (may be null)
-     */
-    public BigDecimal resolveNumberExclusiveMaximum(M member) {
-        return getFirstDefinedValue(this.numberExclusiveMaximumResolvers, member);
-    }
-
-    /**
-     * Setter for "multipleOf" resolver.
-     *
-     * @param resolver how to determine the "multipleOf" of a JSON Schema
-     * @return this config part (for chaining)
-     */
+    @Override
     public SchemaGeneratorConfigPart<M> withNumberMultipleOfResolver(ConfigFunction<M, BigDecimal> resolver) {
-        this.numberMultipleOfResolvers.add(resolver);
-        return this;
-    }
-
-    /**
-     * Determine the "multipleOf" of a given member.
-     *
-     * @param member member to determine "multipleOf" value for
-     * @return "multipleOf" in a JSON Schema (may be null)
-     */
-    public BigDecimal resolveNumberMultipleOf(M member) {
-        return getFirstDefinedValue(this.numberMultipleOfResolvers, member);
+        return (SchemaGeneratorConfigPart<M>) super.withNumberMultipleOfResolver(resolver);
     }
 
-    /**
-     * Setter for "minItems" resolver.
-     *
-     * @param resolver how to determine the "minItems" of a JSON Schema
-     * @return this config part (for chaining)
-     */
+    @Override
     public SchemaGeneratorConfigPart<M> withArrayMinItemsResolver(ConfigFunction<M, Integer> resolver) {
-        this.arrayMinItemsResolvers.add(resolver);
-        return this;
-    }
-
-    /**
-     * Determine the "minItems" of a given member.
-     *
-     * @param member member to determine "minItems" value for
-     * @return "minItems" in a JSON Schema (may be null)
-     */
-    public Integer resolveArrayMinItems(M member) {
-        return getFirstDefinedValue(this.arrayMinItemsResolvers, member);
+        return (SchemaGeneratorConfigPart<M>) super.withArrayMinItemsResolver(resolver);
     }
 
-    /**
-     * Setter for "maxItems" resolver.
-     *
-     * @param resolver how to determine the "maxItems" of a JSON Schema
-     * @return this config part (for chaining)
-     */
+    @Override
     public SchemaGeneratorConfigPart<M> withArrayMaxItemsResolver(ConfigFunction<M, Integer> resolver) {
-        this.arrayMaxItemsResolvers.add(resolver);
-        return this;
-    }
-
-    /**
-     * Determine the "maxItems" of a given member.
-     *
-     * @param member member to determine "maxItems" value for
-     * @return "maxItems" in a JSON Schema (may be null)
-     */
-    public Integer resolveArrayMaxItems(M member) {
-        return getFirstDefinedValue(this.arrayMaxItemsResolvers, member);
+        return (SchemaGeneratorConfigPart<M>) super.withArrayMaxItemsResolver(resolver);
     }
 
-    /**
-     * Setter for "uniqueItems" resolver.
-     *
-     * @param resolver how to determine the "uniqueItems" of a JSON Schema
-     * @return this config part (for chaining)
-     */
+    @Override
     public SchemaGeneratorConfigPart<M> withArrayUniqueItemsResolver(ConfigFunction<M, Boolean> resolver) {
-        this.arrayUniqueItemsResolvers.add(resolver);
-        return this;
-    }
-
-    /**
-     * Determine the "uniqueItems" of a given member.
-     *
-     * @param member member to determine "uniqueItems" value for
-     * @return "uniqueItems" in a JSON Schema (may be null)
-     */
-    public Boolean resolveArrayUniqueItems(M member) {
-        return getFirstDefinedValue(this.arrayUniqueItemsResolvers, member);
+        return (SchemaGeneratorConfigPart<M>) super.withArrayUniqueItemsResolver(resolver);
     }
 }
diff --git a/src/main/java/com/github/victools/jsonschema/generator/SchemaGeneratorTypeConfigPart.java b/src/main/java/com/github/victools/jsonschema/generator/SchemaGeneratorTypeConfigPart.java
new file mode 100644
index 0000000..f72a977
--- /dev/null
+++ b/src/main/java/com/github/victools/jsonschema/generator/SchemaGeneratorTypeConfigPart.java
@@ -0,0 +1,416 @@
+/*
+ * Copyright 2020 VicTools.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.victools.jsonschema.generator;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Generic collection of reflection based analysis for populating a JSON Schema.
+ *
+ * @param <S> type of the scope/type representation to analyse
+ */
+public class SchemaGeneratorTypeConfigPart<S extends TypeScope> {
+
+    /**
+     * Helper function for invoking a given function with the provided inputs or returning null no function returning anything but null themselves.
+     *
+     * @param <S> type of the targeted scope/type representation (to be forwarded as parameter to the given function)
+     * @param <R> type of the expected return value (of the given function)
+     * @param resolvers functions to invoke and return the first non-null result from
+     * @param scope targeted scope (to be forwarded as first argument to a given function)
+     * @return return value of successfully invoked function or null
+     */
+    protected static <S extends TypeScope, R> R getFirstDefinedValue(List<ConfigFunction<S, R>> resolvers, S scope) {
+        return resolvers.stream()
+                .map(resolver -> resolver.apply(scope))
+                .filter(Objects::nonNull)
+                .findFirst()
+                .orElse(null);
+    }
+
+    /*
+     * General fields independent of "type".
+     */
+    private final List<ConfigFunction<S, String>> titleResolvers = new ArrayList<>();
+    private final List<ConfigFunction<S, String>> descriptionResolvers = new ArrayList<>();
+    private final List<ConfigFunction<S, Object>> defaultResolvers = new ArrayList<>();
+    private final List<ConfigFunction<S, Collection<?>>> enumResolvers = new ArrayList<>();
+
+    /*
+     * Validation fields relating to a schema with "type": "string".
+     */
+    private final List<ConfigFunction<S, Integer>> stringMinLengthResolvers = new ArrayList<>();
+    private final List<ConfigFunction<S, Integer>> stringMaxLengthResolvers = new ArrayList<>();
+    private final List<ConfigFunction<S, String>> stringFormatResolvers = new ArrayList<>();
+    private final List<ConfigFunction<S, String>> stringPatternResolvers = new ArrayList<>();
+
+    /*
+     * Validation fields relating to a schema with "type": "integer" or "number".
+     */
+    private final List<ConfigFunction<S, BigDecimal>> numberInclusiveMinimumResolvers = new ArrayList<>();
+    private final List<ConfigFunction<S, BigDecimal>> numberExclusiveMinimumResolvers = new ArrayList<>();
+    private final List<ConfigFunction<S, BigDecimal>> numberInclusiveMaximumResolvers = new ArrayList<>();
+    private final List<ConfigFunction<S, BigDecimal>> numberExclusiveMaximumResolvers = new ArrayList<>();
+    private final List<ConfigFunction<S, BigDecimal>> numberMultipleOfResolvers = new ArrayList<>();
+
+    /*
+     * Validation fields relating to a schema with "type": "array".
+     */
+    private final List<ConfigFunction<S, Integer>> arrayMinItemsResolvers = new ArrayList<>();
+    private final List<ConfigFunction<S, Integer>> arrayMaxItemsResolvers = new ArrayList<>();
+    private final List<ConfigFunction<S, Boolean>> arrayUniqueItemsResolvers = new ArrayList<>();
+
+    /**
+     * Setter for "title" resolver.
+     *
+     * @param resolver how to determine the "title" of a JSON Schema
+     * @return this config part (for chaining)
+     */
+    public SchemaGeneratorTypeConfigPart<S> withTitleResolver(ConfigFunction<S, String> resolver) {
+        this.titleResolvers.add(resolver);
+        return this;
+    }
+
+    /**
+     * Determine the "title" of a given scope/type representation.
+     *
+     * @param scope scope to determine "title" value for
+     * @return "title" in a JSON Schema (may be null)
+     */
+    public String resolveTitle(S scope) {
+        return getFirstDefinedValue(this.titleResolvers, scope);
+    }
+
+    /**
+     * Setter for "description" resolver.
+     *
+     * @param resolver how to determine the "description" of a JSON Schema
+     * @return this config part (for chaining)
+     */
+    public SchemaGeneratorTypeConfigPart<S> withDescriptionResolver(ConfigFunction<S, String> resolver) {
+        this.descriptionResolvers.add(resolver);
+        return this;
+    }
+
+    /**
+     * Determine the "description" of a given scope/type representation.
+     *
+     * @param scope scope to determine "description" value for
+     * @return "description" in a JSON Schema (may be null)
+     */
+    public String resolveDescription(S scope) {
+        return getFirstDefinedValue(this.descriptionResolvers, scope);
+    }
+
+    /**
+     * Setter for "default" resolver.
+     *
+     * @param resolver how to determine the "default" of a JSON Schema
+     * @return this config part (for chaining)
+     */
+    public SchemaGeneratorTypeConfigPart<S> withDefaultResolver(ConfigFunction<S, Object> resolver) {
+        this.defaultResolvers.add(resolver);
+        return this;
+    }
+
+    /**
+     * Determine the "default" of a given scope/type representation.
+     *
+     * @param scope scope to determine "default" value for
+     * @return "default" in a JSON Schema (may be null)
+     */
+    public Object resolveDefault(S scope) {
+        return getFirstDefinedValue(this.defaultResolvers, scope);
+    }
+
+    /**
+     * Setter for "enum"/"const" resolver.
+     *
+     * @param resolver how to determine the "enum"/"const" of a JSON Schema
+     * @return this config part (for chaining)
+     */
+    public SchemaGeneratorTypeConfigPart<S> withEnumResolver(ConfigFunction<S, Collection<?>> resolver) {
+        this.enumResolvers.add(resolver);
+        return this;
+    }
+
+    /**
+     * Determine the "enum"/"const" of a given scope/type representation.
+     *
+     * @param scope scope to determine "enum"/"const" value for
+     * @return "enum"/"const" in a JSON Schema (may be null)
+     */
+    public Collection<?> resolveEnum(S scope) {
+        return getFirstDefinedValue(this.enumResolvers, scope);
+    }
+
+    /**
+     * Setter for "minLength" resolver.
+     *
+     * @param resolver how to determine the "minLength" of a JSON Schema
+     * @return this config part (for chaining)
+     */
+    public SchemaGeneratorTypeConfigPart<S> withStringMinLengthResolver(ConfigFunction<S, Integer> resolver) {
+        this.stringMinLengthResolvers.add(resolver);
+        return this;
+    }
+
+    /**
+     * Determine the "minLength" of a given scope/type representation.
+     *
+     * @param scope scope to determine "minLength" value for
+     * @return "minLength" in a JSON Schema (may be null)
+     */
+    public Integer resolveStringMinLength(S scope) {
+        return getFirstDefinedValue(this.stringMinLengthResolvers, scope);
+    }
+
+    /**
+     * Setter for "maxLength" resolver.
+     *
+     * @param resolver how to determine the "maxLength" of a JSON Schema
+     * @return this config part (for chaining)
+     */
+    public SchemaGeneratorTypeConfigPart<S> withStringMaxLengthResolver(ConfigFunction<S, Integer> resolver) {
+        this.stringMaxLengthResolvers.add(resolver);
+        return this;
+    }
+
+    /**
+     * Determine the "maxLength" of a given scope/type representation.
+     *
+     * @param scope scope to determine "maxLength" value for
+     * @return "maxLength" in a JSON Schema (may be null)
+     */
+    public Integer resolveStringMaxLength(S scope) {
+        return getFirstDefinedValue(this.stringMaxLengthResolvers, scope);
+    }
+
+    /**
+     * Setter for "format" resolver.
+     *
+     * @param resolver how to determine the "format" of a JSON Schema
+     * @return this config part (for chaining)
+     */
+    public SchemaGeneratorTypeConfigPart<S> withStringFormatResolver(ConfigFunction<S, String> resolver) {
+        this.stringFormatResolvers.add(resolver);
+        return this;
+    }
+
+    /**
+     * Determine the "format" of a given scope/type representation.
+     *
+     * @param scope scope to determine "format" value for
+     * @return "format" in a JSON Schema (may be null)
+     */
+    public String resolveStringFormat(S scope) {
+        return getFirstDefinedValue(this.stringFormatResolvers, scope);
+    }
+
+    /**
+     * Setter for "format" resolver.
+     *
+     * @param resolver how to determine the "format" of a JSON Schema
+     * @return this config part (for chaining)
+     */
+    public SchemaGeneratorTypeConfigPart<S> withStringPatternResolver(ConfigFunction<S, String> resolver) {
+        this.stringPatternResolvers.add(resolver);
+        return this;
+    }
+
+    /**
+     * Determine the "format" of a given scope/type representation.
+     *
+     * @param scope scope to determine "format" value for
+     * @return "format" in a JSON Schema (may be null)
+     */
+    public String resolveStringPattern(S scope) {
+        return getFirstDefinedValue(this.stringPatternResolvers, scope);
+    }
+
+    /**
+     * Setter for "minimum" resolver.
+     *
+     * @param resolver how to determine the "minimum" of a JSON Schema
+     * @return this config part (for chaining)
+     */
+    public SchemaGeneratorTypeConfigPart<S> withNumberInclusiveMinimumResolver(ConfigFunction<S, BigDecimal> resolver) {
+        this.numberInclusiveMinimumResolvers.add(resolver);
+        return this;
+    }
+
+    /**
+     * Determine the "minimum" of a given scope/type representation.
+     *
+     * @param scope scope to determine "minimum" value for
+     * @return "minimum" in a JSON Schema (may be null)
+     */
+    public BigDecimal resolveNumberInclusiveMinimum(S scope) {
+        return getFirstDefinedValue(this.numberInclusiveMinimumResolvers, scope);
+    }
+
+    /**
+     * Setter for "exclusiveMinimum" resolver.
+     *
+     * @param resolver how to determine the "exclusiveMinimum" of a JSON Schema
+     * @return this config part (for chaining)
+     */
+    public SchemaGeneratorTypeConfigPart<S> withNumberExclusiveMinimumResolver(ConfigFunction<S, BigDecimal> resolver) {
+        this.numberExclusiveMinimumResolvers.add(resolver);
+        return this;
+    }
+
+    /**
+     * Determine the "exclusiveMinimum" of a given scope/type representation.
+     *
+     * @param scope scope to determine "exclusiveMinimum" value for
+     * @return "exclusiveMinimum" in a JSON Schema (may be null)
+     */
+    public BigDecimal resolveNumberExclusiveMinimum(S scope) {
+        return getFirstDefinedValue(this.numberExclusiveMinimumResolvers, scope);
+    }
+
+    /**
+     * Setter for "maximum" resolver.
+     *
+     * @param resolver how to determine the "maximum" of a JSON Schema
+     * @return this config part (for chaining)
+     */
+    public SchemaGeneratorTypeConfigPart<S> withNumberInclusiveMaximumResolver(ConfigFunction<S, BigDecimal> resolver) {
+        this.numberInclusiveMaximumResolvers.add(resolver);
+        return this;
+    }
+
+    /**
+     * Determine the "maximum" of a given scope/type representation.
+     *
+     * @param scope scope to determine "maximum" value for
+     * @return "maximum" in a JSON Schema (may be null)
+     */
+    public BigDecimal resolveNumberInclusiveMaximum(S scope) {
+        return getFirstDefinedValue(this.numberInclusiveMaximumResolvers, scope);
+    }
+
+    /**
+     * Setter for "exclusiveMaximum" resolver.
+     *
+     * @param resolver how to determine the "exclusiveMaximum" of a JSON Schema
+     * @return this config part (for chaining)
+     */
+    public SchemaGeneratorTypeConfigPart<S> withNumberExclusiveMaximumResolver(ConfigFunction<S, BigDecimal> resolver) {
+        this.numberExclusiveMaximumResolvers.add(resolver);
+        return this;
+    }
+
+    /**
+     * Determine the "exclusiveMaximum" of a given scope/type representation.
+     *
+     * @param scope scope to determine "exclusiveMaximum" value for
+     * @return "exclusiveMaximum" in a JSON Schema (may be null)
+     */
+    public BigDecimal resolveNumberExclusiveMaximum(S scope) {
+        return getFirstDefinedValue(this.numberExclusiveMaximumResolvers, scope);
+    }
+
+    /**
+     * Setter for "multipleOf" resolver.
+     *
+     * @param resolver how to determine the "multipleOf" of a JSON Schema
+     * @return this config part (for chaining)
+     */
+    public SchemaGeneratorTypeConfigPart<S> withNumberMultipleOfResolver(ConfigFunction<S, BigDecimal> resolver) {
+        this.numberMultipleOfResolvers.add(resolver);
+        return this;
+    }
+
+    /**
+     * Determine the "multipleOf" of a given scope/type representation.
+     *
+     * @param scope scope to determine "multipleOf" value for
+     * @return "multipleOf" in a JSON Schema (may be null)
+     */
+    public BigDecimal resolveNumberMultipleOf(S scope) {
+        return getFirstDefinedValue(this.numberMultipleOfResolvers, scope);
+    }
+
+    /**
+     * Setter for "minItems" resolver.
+     *
+     * @param resolver how to determine the "minItems" of a JSON Schema
+     * @return this config part (for chaining)
+     */
+    public SchemaGeneratorTypeConfigPart<S> withArrayMinItemsResolver(ConfigFunction<S, Integer> resolver) {
+        this.arrayMinItemsResolvers.add(resolver);
+        return this;
+    }
+
+    /**
+     * Determine the "minItems" of a given scope/type representation.
+     *
+     * @param scope scope to determine "minItems" value for
+     * @return "minItems" in a JSON Schema (may be null)
+     */
+    public Integer resolveArrayMinItems(S scope) {
+        return getFirstDefinedValue(this.arrayMinItemsResolvers, scope);
+    }
+
+    /**
+     * Setter for "maxItems" resolver.
+     *
+     * @param resolver how to determine the "maxItems" of a JSON Schema
+     * @return this config part (for chaining)
+     */
+    public SchemaGeneratorTypeConfigPart<S> withArrayMaxItemsResolver(ConfigFunction<S, Integer> resolver) {
+        this.arrayMaxItemsResolvers.add(resolver);
+        return this;
+    }
+
+    /**
+     * Determine the "maxItems" of a given scope/type representation.
+     *
+     * @param scope scope to determine "maxItems" value for
+     * @return "maxItems" in a JSON Schema (may be null)
+     */
+    public Integer resolveArrayMaxItems(S scope) {
+        return getFirstDefinedValue(this.arrayMaxItemsResolvers, scope);
+    }
+
+    /**
+     * Setter for "uniqueItems" resolver.
+     *
+     * @param resolver how to determine the "uniqueItems" of a JSON Schema
+     * @return this config part (for chaining)
+     */
+    public SchemaGeneratorTypeConfigPart<S> withArrayUniqueItemsResolver(ConfigFunction<S, Boolean> resolver) {
+        this.arrayUniqueItemsResolvers.add(resolver);
+        return this;
+    }
+
+    /**
+     * Determine the "uniqueItems" of a given scope/type representation.
+     *
+     * @param scope scope to determine "uniqueItems" value for
+     * @return "uniqueItems" in a JSON Schema (may be null)
+     */
+    public Boolean resolveArrayUniqueItems(S scope) {
+        return getFirstDefinedValue(this.arrayUniqueItemsResolvers, scope);
+    }
+}
diff --git a/src/main/java/com/github/victools/jsonschema/generator/TypeAttributeOverride.java b/src/main/java/com/github/victools/jsonschema/generator/TypeAttributeOverride.java
index 558f522..9e8c68b 100644
--- a/src/main/java/com/github/victools/jsonschema/generator/TypeAttributeOverride.java
+++ b/src/main/java/com/github/victools/jsonschema/generator/TypeAttributeOverride.java
@@ -16,7 +16,6 @@
 
 package com.github.victools.jsonschema.generator;
 
-import com.fasterxml.classmate.ResolvedType;
 import com.fasterxml.jackson.databind.JavaType;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 
@@ -31,9 +30,9 @@ public interface TypeAttributeOverride {
      * E.g. "{@value SchemaConstants#TAG_FORMAT}", "pattern", "required"
      *
      * @param jsonSchemaTypeNode node to modify (the part that may be referenced multiple times)
-     * @param javaType the type associated with the JSON Schema node
+     * @param scope the type representation associated with the JSON Schema node
      * @param config applicable configuration
      */
-    void overrideTypeAttributes(ObjectNode jsonSchemaTypeNode, ResolvedType javaType, SchemaGeneratorConfig config);
+    void overrideTypeAttributes(ObjectNode jsonSchemaTypeNode, TypeScope scope, SchemaGeneratorConfig config);
 
 }
diff --git a/src/main/java/com/github/victools/jsonschema/generator/TypeContext.java b/src/main/java/com/github/victools/jsonschema/generator/TypeContext.java
index 269500e..bb6833d 100644
--- a/src/main/java/com/github/victools/jsonschema/generator/TypeContext.java
+++ b/src/main/java/com/github/victools/jsonschema/generator/TypeContext.java
@@ -92,6 +92,16 @@ public class TypeContext {
         return new MethodScope(method, declaringTypeMembers, this);
     }
 
+    /**
+     * Construct a {@link TypeScope} instance for the type.
+     *
+     * @param type targeted type
+     * @return created {@link TypeScope} instance
+     */
+    public TypeScope createTypeScope(ResolvedType type) {
+        return new TypeScope(type, this);
+    }
+
     /**
      * Determine whether a given type should be treated as "{@value SchemaConstants#TAG_TYPE_ARRAY}" in the generated schema.
      *
diff --git a/src/main/java/com/github/victools/jsonschema/generator/TypeScope.java b/src/main/java/com/github/victools/jsonschema/generator/TypeScope.java
new file mode 100644
index 0000000..60a13a0
--- /dev/null
+++ b/src/main/java/com/github/victools/jsonschema/generator/TypeScope.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2020 VicTools.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.victools.jsonschema.generator;
+
+import com.fasterxml.classmate.ResolvedType;
+
+/**
+ * Representation of a single type to represent as (sub) schema.
+ */
+public class TypeScope {
+
+    private final ResolvedType type;
+    private final TypeContext context;
+
+    /**
+     * Constructor.
+     *
+     * @param type targeted type
+     * @param context the overall type resolution context
+     */
+    protected TypeScope(ResolvedType type, TypeContext context) {
+        this.type = type;
+        this.context = context;
+    }
+
+    /**
+     * Getter for the overall type resolution context.
+     *
+     * @return type resolution context
+     */
+    public TypeContext getContext() {
+        return this.context;
+    }
+
+    /**
+     * Returns represented type; if there is one, for methods this is the return type and for fields their field type.
+     *
+     * @return represented type
+     */
+    public ResolvedType getType() {
+        return this.type;
+    }
+
+    /* ===================================== *
+     * Convenience methods for targeted type *
+     * ===================================== */
+    /**
+     * Determine whether this targeted type should be treated as "{@value SchemaConstants#TAG_TYPE_ARRAY}" in the generated schema.
+     * <br>
+     * This is equivalent to calling: {@code scope.getContext().isContainerType(scope.getType())}
+     *
+     * @return whether the targeted type is an array or sub type of {@link java.util.Collection Collection}
+     */
+    public boolean isContainerType() {
+        ResolvedType targetType = this.getType();
+        return targetType != null && this.getContext().isContainerType(targetType);
+    }
+
+    /**
+     * Identify the element/item type of the given "{@value SchemaConstants#TAG_TYPE_ARRAY}".
+     * <br>
+     * This is equivalent to calling: {@code scope.getContext().getContainerItemType(scope.getType())}
+     *
+     * @return type of elements/items
+     */
+    public ResolvedType getContainerItemType() {
+        ResolvedType targetType = this.getType();
+        return targetType == null ? null : this.getContext().getContainerItemType(targetType);
+    }
+
+    /**
+     * Constructing a string that represents this member's type (including possible type parameters and their actual types) – excluding package names.
+     * <br>
+     * This is equivalent to calling: {@code scope.getContext().getSimpleTypeDescription(scope.getType())}
+     *
+     * @return resulting string
+     * @see #getType()
+     * @see TypeContext#getSimpleTypeDescription(ResolvedType)
+     */
+    public String getSimpleTypeDescription() {
+        ResolvedType targetType = this.getType();
+        return targetType == null ? "void" : this.getContext().getSimpleTypeDescription(targetType);
+    }
+
+    /**
+     * Constructing a string that represents this member's type (including possible type parameters and their actual types) – including package names.
+     * <br>
+     * This is equivalent to calling: {@code scope.getContext().getFullTypeDescription(scope.getType())}
+     *
+     * @return resulting string
+     * @see #getType()
+     * @see TypeContext#getFullTypeDescription(ResolvedType)
+     */
+    public String getFullTypeDescription() {
+        ResolvedType targetType = this.getType();
+        return targetType == null ? "void" : this.getContext().getFullTypeDescription(targetType);
+    }
+
+    @Override
+    public String toString() {
+        return this.getSimpleTypeDescription();
+    }
+}
diff --git a/src/main/java/com/github/victools/jsonschema/generator/impl/AttributeCollector.java b/src/main/java/com/github/victools/jsonschema/generator/impl/AttributeCollector.java
index 64811fe..afbf2d4 100644
--- a/src/main/java/com/github/victools/jsonschema/generator/impl/AttributeCollector.java
+++ b/src/main/java/com/github/victools/jsonschema/generator/impl/AttributeCollector.java
@@ -24,6 +24,7 @@ import com.github.victools.jsonschema.generator.FieldScope;
 import com.github.victools.jsonschema.generator.MethodScope;
 import com.github.victools.jsonschema.generator.SchemaConstants;
 import com.github.victools.jsonschema.generator.SchemaGeneratorConfig;
+import com.github.victools.jsonschema.generator.TypeScope;
 import java.math.BigDecimal;
 import java.math.BigInteger;
 import java.util.Collection;
@@ -112,6 +113,35 @@ public class AttributeCollector {
         return node;
     }
 
+    /**
+     * Collect a given scope's general type attributes (i.e. everything not related to the structure).
+     *
+     * @param scope the scope/type representation for which to collect JSON schema attributes
+     * @param config configuration to apply when looking-up attribute values
+     * @return node holding all collected attributes (possibly empty)
+     */
+    public static ObjectNode collectTypeAttributes(TypeScope scope, SchemaGeneratorConfig config) {
+        ObjectNode node = config.createObjectNode();
+        AttributeCollector collector = new AttributeCollector(config.getObjectMapper());
+        collector.setTitle(node, config.resolveTitleForType(scope));
+        collector.setDescription(node, config.resolveDescriptionForType(scope));
+        collector.setDefault(node, config.resolveDefaultForType(scope));
+        collector.setEnum(node, config.resolveEnumForType(scope));
+        collector.setStringMinLength(node, config.resolveStringMinLengthForType(scope));
+        collector.setStringMaxLength(node, config.resolveStringMaxLengthForType(scope));
+        collector.setStringFormat(node, config.resolveStringFormatForType(scope));
+        collector.setStringPattern(node, config.resolveStringPatternForType(scope));
+        collector.setNumberInclusiveMinimum(node, config.resolveNumberInclusiveMinimumForType(scope));
+        collector.setNumberExclusiveMinimum(node, config.resolveNumberExclusiveMinimumForType(scope));
+        collector.setNumberInclusiveMaximum(node, config.resolveNumberInclusiveMaximumForType(scope));
+        collector.setNumberExclusiveMaximum(node, config.resolveNumberExclusiveMaximumForType(scope));
+        collector.setNumberMultipleOf(node, config.resolveNumberMultipleOfForType(scope));
+        collector.setArrayMinItems(node, config.resolveArrayMinItemsForType(scope));
+        collector.setArrayMaxItems(node, config.resolveArrayMaxItemsForType(scope));
+        collector.setArrayUniqueItems(node, config.resolveArrayUniqueItemsForType(scope));
+        return node;
+    }
+
     /**
      * Setter for "{@value SchemaConstants#TAG_TITLE}" attribute.
      *
diff --git a/src/main/java/com/github/victools/jsonschema/generator/impl/SchemaGenerationContextImpl.java b/src/main/java/com/github/victools/jsonschema/generator/impl/SchemaGenerationContextImpl.java
index 74fa772..5fc8ee7 100644
--- a/src/main/java/com/github/victools/jsonschema/generator/impl/SchemaGenerationContextImpl.java
+++ b/src/main/java/com/github/victools/jsonschema/generator/impl/SchemaGenerationContextImpl.java
@@ -34,6 +34,7 @@ import com.github.victools.jsonschema.generator.SchemaConstants;
 import com.github.victools.jsonschema.generator.SchemaGenerationContext;
 import com.github.victools.jsonschema.generator.SchemaGeneratorConfig;
 import com.github.victools.jsonschema.generator.TypeContext;
+import com.github.victools.jsonschema.generator.TypeScope;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
@@ -251,8 +252,15 @@ public class SchemaGenerationContextImpl implements SchemaGenerationContext {
                 this.generateObjectDefinition(targetType, definition);
             }
         }
+        TypeScope scope = this.typeContext.createTypeScope(targetType);
+        ObjectNode typeAttributes = AttributeCollector.collectTypeAttributes(scope, this.generatorConfig);
+        // ensure no existing attributes in the 'definition' are replaced, by way of first overriding any conflicts the other way around
+        typeAttributes.setAll(definition);
+        // apply merged attributes
+        definition.setAll(typeAttributes);
+        // apply overrides as the very last step
         this.generatorConfig.getTypeAttributeOverrides()
-                .forEach(override -> override.overrideTypeAttributes(definition, targetType, this.generatorConfig));
+                .forEach(override -> override.overrideTypeAttributes(definition, scope, this.generatorConfig));
     }
 
     /**
diff --git a/src/main/java/com/github/victools/jsonschema/generator/impl/SchemaGeneratorConfigImpl.java b/src/main/java/com/github/victools/jsonschema/generator/impl/SchemaGeneratorConfigImpl.java
index a3e5c2a..b53b252 100644
--- a/src/main/java/com/github/victools/jsonschema/generator/impl/SchemaGeneratorConfigImpl.java
+++ b/src/main/java/com/github/victools/jsonschema/generator/impl/SchemaGeneratorConfigImpl.java
@@ -29,7 +29,9 @@ import com.github.victools.jsonschema.generator.Option;
 import com.github.victools.jsonschema.generator.SchemaGenerationContext;
 import com.github.victools.jsonschema.generator.SchemaGeneratorConfig;
 import com.github.victools.jsonschema.generator.SchemaGeneratorConfigPart;
+import com.github.victools.jsonschema.generator.SchemaGeneratorTypeConfigPart;
 import com.github.victools.jsonschema.generator.TypeAttributeOverride;
+import com.github.victools.jsonschema.generator.TypeScope;
 import java.math.BigDecimal;
 import java.util.Collection;
 import java.util.Collections;
@@ -45,6 +47,7 @@ public class SchemaGeneratorConfigImpl implements SchemaGeneratorConfig {
 
     private final ObjectMapper objectMapper;
     private final Map<Option, Boolean> options;
+    private final SchemaGeneratorTypeConfigPart<TypeScope> typesInGeneralConfigPart;
     private final SchemaGeneratorConfigPart<FieldScope> fieldConfigPart;
     private final SchemaGeneratorConfigPart<MethodScope> methodConfigPart;
     private final List<CustomDefinitionProviderV2> customDefinitions;
@@ -55,6 +58,7 @@ public class SchemaGeneratorConfigImpl implements SchemaGeneratorConfig {
      *
      * @param objectMapper supplier for object and array nodes for the JSON structure being generated
      * @param options specifically configured settings/options (thereby overriding the default enabled/disabled flag)
+     * @param typesInGeneralConfigPart configuration part for context-independent attribute collection
      * @param fieldConfigPart configuration part for fields
      * @param methodConfigPart configuration part for methods
      * @param customDefinitions custom suppliers for a type's schema definition
@@ -62,12 +66,14 @@ public class SchemaGeneratorConfigImpl implements SchemaGeneratorConfig {
      */
     public SchemaGeneratorConfigImpl(ObjectMapper objectMapper,
             Map<Option, Boolean> options,
+            SchemaGeneratorTypeConfigPart<TypeScope> typesInGeneralConfigPart,
             SchemaGeneratorConfigPart<FieldScope> fieldConfigPart,
             SchemaGeneratorConfigPart<MethodScope> methodConfigPart,
             List<CustomDefinitionProviderV2> customDefinitions,
             List<TypeAttributeOverride> typeAttributeOverrides) {
         this.objectMapper = objectMapper;
         this.options = options;
+        this.typesInGeneralConfigPart = typesInGeneralConfigPart;
         this.fieldConfigPart = fieldConfigPart;
         this.methodConfigPart = methodConfigPart;
         this.customDefinitions = customDefinitions;
@@ -213,6 +219,11 @@ public class SchemaGeneratorConfigImpl implements SchemaGeneratorConfig {
         return this.methodConfigPart.resolveTitle(method);
     }
 
+    @Override
+    public String resolveTitleForType(TypeScope scope) {
+        return this.typesInGeneralConfigPart.resolveTitle(scope);
+    }
+
     @Override
     public String resolveDescription(FieldScope field) {
         return this.fieldConfigPart.resolveDescription(field);
@@ -224,8 +235,8 @@ public class SchemaGeneratorConfigImpl implements SchemaGeneratorConfig {
     }
 
     @Override
-    public Object resolveDefault(MethodScope method) {
-        return this.methodConfigPart.resolveDefault(method);
+    public String resolveDescriptionForType(TypeScope scope) {
+        return this.typesInGeneralConfigPart.resolveDescription(scope);
     }
 
     @Override
@@ -233,6 +244,16 @@ public class SchemaGeneratorConfigImpl implements SchemaGeneratorConfig {
         return this.fieldConfigPart.resolveDefault(field);
     }
 
+    @Override
+    public Object resolveDefault(MethodScope method) {
+        return this.methodConfigPart.resolveDefault(method);
+    }
+
+    @Override
+    public Object resolveDefaultForType(TypeScope scope) {
+        return this.typesInGeneralConfigPart.resolveDefault(scope);
+    }
+
     @Override
     public Collection<?> resolveEnum(FieldScope field) {
         return this.fieldConfigPart.resolveEnum(field);
@@ -243,6 +264,11 @@ public class SchemaGeneratorConfigImpl implements SchemaGeneratorConfig {
         return this.methodConfigPart.resolveEnum(method);
     }
 
+    @Override
+    public Collection<?> resolveEnumForType(TypeScope scope) {
+        return this.typesInGeneralConfigPart.resolveEnum(scope);
+    }
+
     @Override
     public Integer resolveStringMinLength(FieldScope field) {
         return this.fieldConfigPart.resolveStringMinLength(field);
@@ -253,6 +279,11 @@ public class SchemaGeneratorConfigImpl implements SchemaGeneratorConfig {
         return this.methodConfigPart.resolveStringMinLength(method);
     }
 
+    @Override
+    public Integer resolveStringMinLengthForType(TypeScope scope) {
+        return this.typesInGeneralConfigPart.resolveStringMinLength(scope);
+    }
+
     @Override
     public Integer resolveStringMaxLength(FieldScope field) {
         return this.fieldConfigPart.resolveStringMaxLength(field);
@@ -263,6 +294,11 @@ public class SchemaGeneratorConfigImpl implements SchemaGeneratorConfig {
         return this.methodConfigPart.resolveStringMaxLength(method);
     }
 
+    @Override
+    public Integer resolveStringMaxLengthForType(TypeScope scope) {
+        return this.typesInGeneralConfigPart.resolveStringMaxLength(scope);
+    }
+
     @Override
     public String resolveStringFormat(FieldScope field) {
         return this.fieldConfigPart.resolveStringFormat(field);
@@ -273,6 +309,11 @@ public class SchemaGeneratorConfigImpl implements SchemaGeneratorConfig {
         return this.methodConfigPart.resolveStringFormat(method);
     }
 
+    @Override
+    public String resolveStringFormatForType(TypeScope scope) {
+        return this.typesInGeneralConfigPart.resolveStringFormat(scope);
+    }
+
     @Override
     public String resolveStringPattern(FieldScope field) {
         return this.fieldConfigPart.resolveStringPattern(field);
@@ -283,6 +324,11 @@ public class SchemaGeneratorConfigImpl implements SchemaGeneratorConfig {
         return this.methodConfigPart.resolveStringPattern(method);
     }
 
+    @Override
+    public String resolveStringPatternForType(TypeScope scope) {
+        return this.typesInGeneralConfigPart.resolveStringPattern(scope);
+    }
+
     @Override
     public BigDecimal resolveNumberInclusiveMinimum(FieldScope field) {
         return this.fieldConfigPart.resolveNumberInclusiveMinimum(field);
@@ -293,6 +339,11 @@ public class SchemaGeneratorConfigImpl implements SchemaGeneratorConfig {
         return this.methodConfigPart.resolveNumberInclusiveMinimum(method);
     }
 
+    @Override
+    public BigDecimal resolveNumberInclusiveMinimumForType(TypeScope scope) {
+        return this.typesInGeneralConfigPart.resolveNumberInclusiveMinimum(scope);
+    }
+
     @Override
     public BigDecimal resolveNumberExclusiveMinimum(FieldScope field) {
         return this.fieldConfigPart.resolveNumberExclusiveMinimum(field);
@@ -303,6 +354,11 @@ public class SchemaGeneratorConfigImpl implements SchemaGeneratorConfig {
         return this.methodConfigPart.resolveNumberExclusiveMinimum(method);
     }
 
+    @Override
+    public BigDecimal resolveNumberExclusiveMinimumForType(TypeScope scope) {
+        return this.typesInGeneralConfigPart.resolveNumberExclusiveMinimum(scope);
+    }
+
     @Override
     public BigDecimal resolveNumberInclusiveMaximum(FieldScope field) {
         return this.fieldConfigPart.resolveNumberInclusiveMaximum(field);
@@ -313,6 +369,11 @@ public class SchemaGeneratorConfigImpl implements SchemaGeneratorConfig {
         return this.methodConfigPart.resolveNumberInclusiveMaximum(method);
     }
 
+    @Override
+    public BigDecimal resolveNumberInclusiveMaximumForType(TypeScope scope) {
+        return this.typesInGeneralConfigPart.resolveNumberInclusiveMaximum(scope);
+    }
+
     @Override
     public BigDecimal resolveNumberExclusiveMaximum(FieldScope field) {
         return this.fieldConfigPart.resolveNumberExclusiveMaximum(field);
@@ -323,6 +384,11 @@ public class SchemaGeneratorConfigImpl implements SchemaGeneratorConfig {
         return this.methodConfigPart.resolveNumberExclusiveMaximum(method);
     }
 
+    @Override
+    public BigDecimal resolveNumberExclusiveMaximumForType(TypeScope scope) {
+        return this.typesInGeneralConfigPart.resolveNumberExclusiveMaximum(scope);
+    }
+
     @Override
     public BigDecimal resolveNumberMultipleOf(FieldScope field) {
         return this.fieldConfigPart.resolveNumberMultipleOf(field);
@@ -333,6 +399,11 @@ public class SchemaGeneratorConfigImpl implements SchemaGeneratorConfig {
         return this.methodConfigPart.resolveNumberMultipleOf(method);
     }
 
+    @Override
+    public BigDecimal resolveNumberMultipleOfForType(TypeScope scope) {
+        return this.typesInGeneralConfigPart.resolveNumberMultipleOf(scope);
+    }
+
     @Override
     public Integer resolveArrayMinItems(FieldScope field) {
         return this.fieldConfigPart.resolveArrayMinItems(field);
@@ -343,6 +414,11 @@ public class SchemaGeneratorConfigImpl implements SchemaGeneratorConfig {
         return this.methodConfigPart.resolveArrayMinItems(method);
     }
 
+    @Override
+    public Integer resolveArrayMinItemsForType(TypeScope scope) {
+        return this.typesInGeneralConfigPart.resolveArrayMinItems(scope);
+    }
+
     @Override
     public Integer resolveArrayMaxItems(FieldScope field) {
         return this.fieldConfigPart.resolveArrayMaxItems(field);
@@ -353,6 +429,11 @@ public class SchemaGeneratorConfigImpl implements SchemaGeneratorConfig {
         return this.methodConfigPart.resolveArrayMaxItems(method);
     }
 
+    @Override
+    public Integer resolveArrayMaxItemsForType(TypeScope scope) {
+        return this.typesInGeneralConfigPart.resolveArrayMaxItems(scope);
+    }
+
     @Override
     public Boolean resolveArrayUniqueItems(FieldScope field) {
         return this.fieldConfigPart.resolveArrayUniqueItems(field);
@@ -362,4 +443,9 @@ public class SchemaGeneratorConfigImpl implements SchemaGeneratorConfig {
     public Boolean resolveArrayUniqueItems(MethodScope method) {
         return this.methodConfigPart.resolveArrayUniqueItems(method);
     }
+
+    @Override
+    public Boolean resolveArrayUniqueItemsForType(TypeScope scope) {
+        return this.typesInGeneralConfigPart.resolveArrayUniqueItems(scope);
+    }
 }
diff --git a/src/main/java/com/github/victools/jsonschema/generator/impl/module/EnumModule.java b/src/main/java/com/github/victools/jsonschema/generator/impl/module/EnumModule.java
index 3ce830c..d1f8f38 100644
--- a/src/main/java/com/github/victools/jsonschema/generator/impl/module/EnumModule.java
+++ b/src/main/java/com/github/victools/jsonschema/generator/impl/module/EnumModule.java
@@ -21,12 +21,12 @@ import com.fasterxml.classmate.members.RawField;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import com.github.victools.jsonschema.generator.CustomDefinition;
-import com.github.victools.jsonschema.generator.CustomDefinitionProvider;
+import com.github.victools.jsonschema.generator.CustomDefinitionProviderV2;
 import com.github.victools.jsonschema.generator.MethodScope;
 import com.github.victools.jsonschema.generator.Module;
 import com.github.victools.jsonschema.generator.SchemaConstants;
+import com.github.victools.jsonschema.generator.SchemaGenerationContext;
 import com.github.victools.jsonschema.generator.SchemaGeneratorConfigBuilder;
-import com.github.victools.jsonschema.generator.TypeContext;
 import com.github.victools.jsonschema.generator.impl.AttributeCollector;
 import java.util.List;
 import java.util.stream.Collectors;
@@ -117,7 +117,7 @@ public class EnumModule implements Module {
     /**
      * Implementation of the {@link CustomDefinitionProvider} interface for treating enum types as plain strings.
      */
-    private static class EnumAsStringDefinitionProvider implements CustomDefinitionProvider {
+    private static class EnumAsStringDefinitionProvider implements CustomDefinitionProviderV2 {
 
         private final ObjectMapper objectMapper;
 
@@ -131,7 +131,7 @@ public class EnumModule implements Module {
         }
 
         @Override
-        public CustomDefinition provideCustomSchemaDefinition(ResolvedType javaType, TypeContext context) {
+        public CustomDefinition provideCustomSchemaDefinition(ResolvedType javaType, SchemaGenerationContext context) {
             if (javaType.isInstanceOf(Enum.class)) {
                 ObjectNode customNode = this.objectMapper.createObjectNode()
                         .put(SchemaConstants.TAG_TYPE, SchemaConstants.TAG_TYPE_STRING);
diff --git a/src/main/java/com/github/victools/jsonschema/generator/impl/module/SimpleTypeModule.java b/src/main/java/com/github/victools/jsonschema/generator/impl/module/SimpleTypeModule.java
index 01fdd8c..58e2b47 100644
--- a/src/main/java/com/github/victools/jsonschema/generator/impl/module/SimpleTypeModule.java
+++ b/src/main/java/com/github/victools/jsonschema/generator/impl/module/SimpleTypeModule.java
@@ -20,12 +20,12 @@ import com.fasterxml.classmate.ResolvedType;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import com.github.victools.jsonschema.generator.CustomDefinition;
-import com.github.victools.jsonschema.generator.CustomDefinitionProvider;
+import com.github.victools.jsonschema.generator.CustomDefinitionProviderV2;
 import com.github.victools.jsonschema.generator.MemberScope;
 import com.github.victools.jsonschema.generator.Module;
 import com.github.victools.jsonschema.generator.SchemaConstants;
+import com.github.victools.jsonschema.generator.SchemaGenerationContext;
 import com.github.victools.jsonschema.generator.SchemaGeneratorConfigBuilder;
-import com.github.victools.jsonschema.generator.TypeContext;
 import java.math.BigDecimal;
 import java.math.BigInteger;
 import java.time.Instant;
@@ -186,7 +186,7 @@ public class SimpleTypeModule implements Module {
     /**
      * Implementation of the {@link CustomDefinitionProvider} interface for applying fixed schema definitions for simple java types.
      */
-    private class SimpleTypeDefinitionProvider implements CustomDefinitionProvider {
+    private class SimpleTypeDefinitionProvider implements CustomDefinitionProviderV2 {
 
         private final ObjectMapper objectMapper;
 
@@ -200,7 +200,7 @@ public class SimpleTypeModule implements Module {
         }
 
         @Override
-        public CustomDefinition provideCustomSchemaDefinition(ResolvedType javaType, TypeContext context) {
+        public CustomDefinition provideCustomSchemaDefinition(ResolvedType javaType, SchemaGenerationContext context) {
 
             if (!javaType.getTypeParameters().isEmpty()) {
                 return null;


Test patch:
diff --git a/src/test/java/com/github/victools/jsonschema/generator/SchemaGeneratorTest.java b/src/test/java/com/github/victools/jsonschema/generator/SchemaGeneratorTest.java
index 03f2d15..517bba1 100644
--- a/src/test/java/com/github/victools/jsonschema/generator/SchemaGeneratorTest.java
+++ b/src/test/java/com/github/victools/jsonschema/generator/SchemaGeneratorTest.java
@@ -146,27 +146,32 @@ public class SchemaGeneratorTest {
         Assert.assertEquals("custom override of Integer", result.get("$comment").asText());
     }
 
-    private static void populateConfigPart(SchemaGeneratorConfigPart<?> configPart, String descriptionPrefix) {
+    private static void populateTypeConfigPart(SchemaGeneratorTypeConfigPart<?> configPart, String descriptionPrefix) {
         configPart
                 .withArrayMinItemsResolver(member -> member.isContainerType() ? 2 : null)
                 .withArrayMaxItemsResolver(member -> member.isContainerType() ? 100 : null)
                 .withArrayUniqueItemsResolver(member -> member.isContainerType() ? false : null)
                 .withDefaultResolver(member -> member.getType().isInstanceOf(Number.class) ? 1 : null)
-                .withDescriptionResolver(member -> descriptionPrefix + member.getContext().getSimpleTypeDescription(member.getType()))
+                .withDescriptionResolver(member -> descriptionPrefix + member.getSimpleTypeDescription())
                 .withEnumResolver(member -> member.getType().isInstanceOf(Number.class) ? Arrays.asList(1, 2, 3, 4, 5) : null)
                 .withEnumResolver(member -> member.getType().isInstanceOf(String.class) ? Arrays.asList("constant string value") : null)
-                .withNullableCheck(member -> !member.getName().startsWith("nested"))
                 .withNumberExclusiveMaximumResolver(member -> member.getType().isInstanceOf(Number.class) ? BigDecimal.TEN.add(BigDecimal.ONE) : null)
                 .withNumberExclusiveMinimumResolver(member -> member.getType().isInstanceOf(Number.class) ? BigDecimal.ZERO : null)
                 .withNumberInclusiveMaximumResolver(member -> member.getType().isInstanceOf(Number.class) ? BigDecimal.TEN : null)
                 .withNumberInclusiveMinimumResolver(member -> member.getType().isInstanceOf(Number.class) ? BigDecimal.ONE : null)
                 .withNumberMultipleOfResolver(member -> member.getType().isInstanceOf(Number.class) ? BigDecimal.ONE : null)
-                .withRequiredCheck(member -> member.getName().startsWith("nested"))
                 .withStringFormatResolver(member -> member.getType().isInstanceOf(String.class) ? "date" : null)
                 .withStringMaxLengthResolver(member -> member.getType().isInstanceOf(String.class) ? 256 : null)
                 .withStringMinLengthResolver(member -> member.getType().isInstanceOf(String.class) ? 1 : null)
                 .withStringPatternResolver(member -> member.getType().isInstanceOf(String.class) ? "^.{1,256}$" : null)
-                .withTitleResolver(member -> member.getContext().getSimpleTypeDescription(member.getType()));
+                .withTitleResolver(member -> member.getSimpleTypeDescription());
+    }
+
+    private static void populateConfigPart(SchemaGeneratorConfigPart<? extends MemberScope<?, ?>> configPart, String descriptionPrefix) {
+        populateTypeConfigPart(configPart, descriptionPrefix);
+        configPart
+                .withNullableCheck(member -> !member.getName().startsWith("nested"))
+                .withRequiredCheck(member -> member.getName().startsWith("nested"));
     }
 
     Object parametersForTestGenerateSchema() {
@@ -179,14 +184,17 @@ public class SchemaGeneratorTest {
             }
             return null;
         });
+        Module typeInGeneralModule = configBuilder -> populateTypeConfigPart(configBuilder.forTypesInGeneral(), "for type in general: ");
         Module methodModule = configBuilder -> populateConfigPart(configBuilder.forMethods(), "looked-up from method: ");
         Module fieldModule = configBuilder -> populateConfigPart(configBuilder.forFields(), "looked-up from field: ");
         return new Object[][]{
             {"testclass1-FULL_DOCUMENTATION", OptionPreset.FULL_DOCUMENTATION, TestClass1.class, neutralModule},
+            {"testclass1-FULL_DOCUMENTATION-attributes", OptionPreset.FULL_DOCUMENTATION, TestClass1.class, typeInGeneralModule},
             {"testclass1-JAVA_OBJECT-attributes", OptionPreset.JAVA_OBJECT, TestClass1.class, methodModule},
             {"testclass1-PLAIN_JSON-attributes", OptionPreset.PLAIN_JSON, TestClass1.class, fieldModule},
             {"testclass2-array", OptionPreset.FULL_DOCUMENTATION, TestClass2[].class, neutralModule},
             {"testclass3-FULL_DOCUMENTATION", OptionPreset.FULL_DOCUMENTATION, TestClass3.class, neutralModule},
+            {"testclass3-FULL_DOCUMENTATION-attributes", OptionPreset.FULL_DOCUMENTATION, TestClass3.class, typeInGeneralModule},
             {"testclass3-JAVA_OBJECT-attributes", OptionPreset.JAVA_OBJECT, TestClass3.class, methodModule},
             {"testclass3-PLAIN_JSON-attributes", OptionPreset.PLAIN_JSON, TestClass3.class, fieldModule}
         };
diff --git a/src/test/java/com/github/victools/jsonschema/generator/impl/SchemaGeneratorConfigImplTest.java b/src/test/java/com/github/victools/jsonschema/generator/impl/SchemaGeneratorConfigImplTest.java
index 0a78216..e9c52d7 100644
--- a/src/test/java/com/github/victools/jsonschema/generator/impl/SchemaGeneratorConfigImplTest.java
+++ b/src/test/java/com/github/victools/jsonschema/generator/impl/SchemaGeneratorConfigImplTest.java
@@ -28,7 +28,9 @@ import com.github.victools.jsonschema.generator.InstanceAttributeOverride;
 import com.github.victools.jsonschema.generator.MethodScope;
 import com.github.victools.jsonschema.generator.Option;
 import com.github.victools.jsonschema.generator.SchemaGeneratorConfigPart;
+import com.github.victools.jsonschema.generator.SchemaGeneratorTypeConfigPart;
 import com.github.victools.jsonschema.generator.TypeAttributeOverride;
+import com.github.victools.jsonschema.generator.TypeScope;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
@@ -51,6 +53,7 @@ public class SchemaGeneratorConfigImplTest extends AbstractTypeAwareTest {
     private SchemaGeneratorConfigImpl instance;
     private ObjectMapper objectMapper;
     private Map<Option, Boolean> options;
+    private SchemaGeneratorTypeConfigPart<TypeScope> typesInGeneralConfigPart;
     private SchemaGeneratorConfigPart<FieldScope> fieldConfigPart;
     private SchemaGeneratorConfigPart<MethodScope> methodConfigPart;
     private List<CustomDefinitionProviderV2> customDefinitions;
@@ -65,12 +68,13 @@ public class SchemaGeneratorConfigImplTest extends AbstractTypeAwareTest {
     public void setUp() {
         this.objectMapper = Mockito.mock(ObjectMapper.class);
         this.options = new HashMap<>();
+        this.typesInGeneralConfigPart = Mockito.mock(SchemaGeneratorTypeConfigPart.class);
         this.fieldConfigPart = Mockito.mock(SchemaGeneratorConfigPart.class);
         this.methodConfigPart = Mockito.mock(SchemaGeneratorConfigPart.class);
         this.customDefinitions = new ArrayList<>();
         this.typeAttributeOverrides = new ArrayList<>();
 
-        this.instance = new SchemaGeneratorConfigImpl(this.objectMapper, this.options,
+        this.instance = new SchemaGeneratorConfigImpl(this.objectMapper, this.options, this.typesInGeneralConfigPart,
                 this.fieldConfigPart, this.methodConfigPart, this.customDefinitions, this.typeAttributeOverrides);
     }
 
@@ -202,7 +206,7 @@ public class SchemaGeneratorConfigImplTest extends AbstractTypeAwareTest {
 
     @Test
     public void testGetTypeAttributeOverrides() {
-        TypeAttributeOverride typeOverride = (typeNode, javaType, c) -> typeNode.put("$comment", javaType.getTypeName());
+        TypeAttributeOverride typeOverride = (typeNode, type, c) -> typeNode.put("$comment", type.getSimpleTypeDescription());
         this.typeAttributeOverrides.add(typeOverride);
 
         List<TypeAttributeOverride> result = this.instance.getTypeAttributeOverrides();
diff --git a/src/test/java/com/github/victools/jsonschema/generator/impl/module/SimpleTypeModuleTest.java b/src/test/java/com/github/victools/jsonschema/generator/impl/module/SimpleTypeModuleTest.java
index 0a2bd84..64477cc 100644
--- a/src/test/java/com/github/victools/jsonschema/generator/impl/module/SimpleTypeModuleTest.java
+++ b/src/test/java/com/github/victools/jsonschema/generator/impl/module/SimpleTypeModuleTest.java
@@ -16,7 +16,7 @@
 
 package com.github.victools.jsonschema.generator.impl.module;
 
-import com.github.victools.jsonschema.generator.CustomDefinitionProvider;
+import com.github.victools.jsonschema.generator.CustomDefinitionProviderV2;
 import com.github.victools.jsonschema.generator.FieldScope;
 import com.github.victools.jsonschema.generator.MethodScope;
 import com.github.victools.jsonschema.generator.SchemaGeneratorConfigBuilder;
@@ -54,7 +54,7 @@ public class SimpleTypeModuleTest {
         Mockito.verify(this.builder, Mockito.times(2)).forMethods();
 
         Mockito.verify(this.builder).getObjectMapper();
-        Mockito.verify(this.builder).with(Mockito.any(CustomDefinitionProvider.class));
+        Mockito.verify(this.builder).with(Mockito.any(CustomDefinitionProviderV2.class));
 
         Mockito.verifyNoMoreInteractions(this.builder);
     }
diff --git a/src/test/resources/com/github/victools/jsonschema/generator/testclass1-FULL_DOCUMENTATION-attributes.json b/src/test/resources/com/github/victools/jsonschema/generator/testclass1-FULL_DOCUMENTATION-attributes.json
new file mode 100644
index 0000000..b59cdbf
--- /dev/null
+++ b/src/test/resources/com/github/victools/jsonschema/generator/testclass1-FULL_DOCUMENTATION-attributes.json
@@ -0,0 +1,48 @@
+{
+    "type": "object",
+    "properties": {
+        "CONSTANT": {
+            "type": "integer",
+            "const": 5
+        },
+        "genericArray": {
+            "type": ["array", "null"],
+            "items": {
+                "type": "string",
+                "title": "String",
+                "description": "for type in general: String",
+                "const": "constant string value",
+                "minLength": 1,
+                "maxLength": 256,
+                "format": "date",
+                "pattern": "^.{1,256}$"
+            },
+            "title": "String[]<String>",
+            "description": "for type in general: String[]<String>",
+            "minItems": 2,
+            "maxItems": 100,
+            "uniqueItems": false
+        },
+        "genericValue": {
+            "type": ["string", "null"]
+        },
+        "ignoredInternalValue": {
+            "type": ["integer", "null"]
+        },
+        "primitiveValue": {
+            "type": "integer"
+        },
+        "calculateSomething(Number, Number)": false,
+        "getGenericValue()": {
+            "type": ["string", "null"]
+        },
+        "getPrimitiveValue()": {
+            "type": "integer"
+        },
+        "isSimpleTestClass()": {
+            "type": "boolean"
+        }
+    },
+    "title": "TestClass1",
+    "description": "for type in general: TestClass1"
+}
diff --git a/src/test/resources/com/github/victools/jsonschema/generator/testclass3-FULL_DOCUMENTATION-attributes.json b/src/test/resources/com/github/victools/jsonschema/generator/testclass3-FULL_DOCUMENTATION-attributes.json
new file mode 100644
index 0000000..ba4ab59
--- /dev/null
+++ b/src/test/resources/com/github/victools/jsonschema/generator/testclass3-FULL_DOCUMENTATION-attributes.json
@@ -0,0 +1,333 @@
+{
+    "definitions": {
+        "Optional<String> (nullable)": {
+            "type": ["object", "null"],
+            "properties": {
+                "get()": {
+                    "type": ["string", "null"]
+                },
+                "isPresent()": {
+                    "type": "boolean"
+                },
+                "orElse(String)": {
+                    "type": ["string", "null"]
+                }
+            },
+            "title": "Optional<String>",
+            "description": "for type in general: Optional<String>"
+        },
+        "RoundingMode": {
+            "type": "object",
+            "properties": {
+                "name": {
+                    "type": ["string", "null"]
+                },
+                "oldMode": {
+                    "type": "integer"
+                },
+                "ordinal": {
+                    "type": "integer"
+                },
+                "compareTo(RoundingMode)": {
+                    "type": "integer"
+                },
+                "name()": {
+                    "type": "string",
+                    "enum": ["UP", "DOWN", "CEILING", "FLOOR", "HALF_UP", "HALF_DOWN", "HALF_EVEN", "UNNECESSARY"]
+                },
+                "valueOf(String)": {
+                    "$ref": "#/definitions/RoundingMode (nullable)"
+                },
+                "valueOf(int)": {
+                    "$ref": "#/definitions/RoundingMode (nullable)"
+                },
+                "values()": {
+                    "type": ["array", "null"],
+                    "items": {
+                        "$ref": "#/definitions/RoundingMode"
+                    },
+                    "title": "RoundingMode[]",
+                    "description": "for type in general: RoundingMode[]",
+                    "minItems": 2,
+                    "maxItems": 100,
+                    "uniqueItems": false
+                }
+            },
+            "title": "RoundingMode",
+            "description": "for type in general: RoundingMode"
+        },
+        "RoundingMode (nullable)": {
+            "oneOf": [{
+                    "type": "null"
+                }, {
+                    "$ref": "#/definitions/RoundingMode"
+                }]
+        },
+        "TestClass1": {
+            "type": "object",
+            "properties": {
+                "CONSTANT": {
+                    "type": "integer",
+                    "const": 5
+                },
+                "genericArray": {
+                    "type": ["array", "null"],
+                    "items": {
+                        "type": "string",
+                        "title": "String",
+                        "description": "for type in general: String",
+                        "const": "constant string value",
+                        "minLength": 1,
+                        "maxLength": 256,
+                        "format": "date",
+                        "pattern": "^.{1,256}$"
+                    },
+                    "title": "String[]<String>",
+                    "description": "for type in general: String[]<String>",
+                    "minItems": 2,
+                    "maxItems": 100,
+                    "uniqueItems": false
+                },
+                "genericValue": {
+                    "type": ["string", "null"]
+                },
+                "ignoredInternalValue": {
+                    "type": ["integer", "null"]
+                },
+                "primitiveValue": {
+                    "type": "integer"
+                },
+                "calculateSomething(Number, Number)": false,
+                "getGenericValue()": {
+                    "type": ["string", "null"]
+                },
+                "getPrimitiveValue()": {
+                    "type": "integer"
+                },
+                "isSimpleTestClass()": {
+                    "type": "boolean"
+                }
+            },
+            "title": "TestClass1",
+            "description": "for type in general: TestClass1"
+        },
+        "TestClass2<Long>": {
+            "type": "object",
+            "properties": {
+                "genericArray": {
+                    "type": ["array", "null"],
+                    "items": {
+                        "type": "integer",
+                        "title": "Long",
+                        "description": "for type in general: Long",
+                        "default": 1,
+                        "enum": [1, 2, 3, 4, 5],
+                        "minimum": 1,
+                        "exclusiveMinimum": 0,
+                        "maximum": 1E+1,
+                        "exclusiveMaximum": 11,
+                        "multipleOf": 1
+                    },
+                    "title": "Long[]<Long>",
+                    "description": "for type in general: Long[]<Long>",
+                    "minItems": 2,
+                    "maxItems": 100,
+                    "uniqueItems": false
+                },
+                "genericValue": {
+                    "type": ["integer", "null"]
+                },
+                "getGenericValue()": {
+                    "type": ["integer", "null"]
+                }
+            },
+            "title": "TestClass2<Long>",
+            "description": "for type in general: TestClass2<Long>"
+        },
+        "TestClass2<Long> (nullable)": {
+            "oneOf": [{
+                    "type": "null"
+                }, {
+                    "$ref": "#/definitions/TestClass2<Long>"
+                }]
+        },
+        "TestClass2<String>": {
+            "type": "object",
+            "properties": {
+                "genericArray": {
+                    "type": ["array", "null"],
+                    "items": {
+                        "type": "string",
+                        "title": "String",
+                        "description": "for type in general: String",
+                        "const": "constant string value",
+                        "minLength": 1,
+                        "maxLength": 256,
+                        "format": "date",
+                        "pattern": "^.{1,256}$"
+                    },
+                    "title": "String[]<String>",
+                    "description": "for type in general: String[]<String>",
+                    "minItems": 2,
+                    "maxItems": 100,
+                    "uniqueItems": false
+                },
+                "genericValue": {
+                    "type": ["string", "null"]
+                },
+                "getGenericValue()": {
+                    "type": ["string", "null"]
+                }
+            },
+            "title": "TestClass2<String>",
+            "description": "for type in general: TestClass2<String>"
+        },
+        "TestClass2<String> (nullable)": {
+            "oneOf": [{
+                    "type": "null"
+                }, {
+                    "$ref": "#/definitions/TestClass2<String>"
+                }]
+        },
+        "TestClass2<TestClass1[]> (nullable)": {
+            "type": ["object", "null"],
+            "properties": {
+                "genericArray": {
+                    "type": ["array", "null"],
+                    "items": {
+                        "type": "array",
+                        "items": {
+                            "$ref": "#/definitions/TestClass1"
+                        },
+                        "title": "TestClass1[]",
+                        "description": "for type in general: TestClass1[]",
+                        "minItems": 2,
+                        "maxItems": 100,
+                        "uniqueItems": false
+                    },
+                    "title": "TestClass1[][]<TestClass1[]>",
+                    "description": "for type in general: TestClass1[][]<TestClass1[]>",
+                    "minItems": 2,
+                    "maxItems": 100,
+                    "uniqueItems": false
+                },
+                "genericValue": {
+                    "type": ["array", "null"],
+                    "items": {
+                        "$ref": "#/definitions/TestClass1"
+                    },
+                    "title": "TestClass1[]",
+                    "description": "for type in general: TestClass1[]",
+                    "minItems": 2,
+                    "maxItems": 100,
+                    "uniqueItems": false
+                },
+                "getGenericValue()": {
+                    "type": ["array", "null"],
+                    "items": {
+                        "$ref": "#/definitions/TestClass1"
+                    },
+                    "title": "TestClass1[]",
+                    "description": "for type in general: TestClass1[]",
+                    "minItems": 2,
+                    "maxItems": 100,
+                    "uniqueItems": false
+                }
+            },
+            "title": "TestClass2<TestClass1[]>",
+            "description": "for type in general: TestClass2<TestClass1[]>"
+        },
+        "TestClass2<TestClass2<String>> (nullable)": {
+            "type": ["object", "null"],
+            "properties": {
+                "genericArray": {
+                    "type": ["array", "null"],
+                    "items": {
+                        "$ref": "#/definitions/TestClass2<String>"
+                    },
+                    "title": "TestClass2[]<TestClass2<String>>",
+                    "description": "for type in general: TestClass2[]<TestClass2<String>>",
+                    "minItems": 2,
+                    "maxItems": 100,
+                    "uniqueItems": false
+                },
+                "genericValue": {
+                    "$ref": "#/definitions/TestClass2<String> (nullable)"
+                },
+                "getGenericValue()": {
+                    "$ref": "#/definitions/TestClass2<String> (nullable)"
+                }
+            },
+            "title": "TestClass2<TestClass2<String>>",
+            "description": "for type in general: TestClass2<TestClass2<String>>"
+        },
+        "TestClass4<String> (nullable)": {
+            "type": ["object", "null"],
+            "properties": {
+                "DEFAULT_ROUNDING_MODE": {
+                    "allOf": [{
+                            "$ref": "#/definitions/RoundingMode"
+                        }, {
+                            "const": "HALF_UP"
+                        }]
+                },
+                "class2OfClass2OfT": {
+                    "$ref": "#/definitions/TestClass2<TestClass2<String>> (nullable)"
+                },
+                "optionalT": {
+                    "$ref": "#/definitions/Optional<String> (nullable)"
+                },
+                "getClass2OfClass2OfT()": {
+                    "$ref": "#/definitions/TestClass2<TestClass2<String>> (nullable)"
+                }
+            },
+            "title": "TestClass4<String>",
+            "description": "for type in general: TestClass4<String>"
+        }
+    },
+    "type": "object",
+    "properties": {
+        "class4": {
+            "$ref": "#/definitions/TestClass4<String> (nullable)"
+        },
+        "nestedClass1Array": {
+            "$ref": "#/definitions/TestClass2<TestClass1[]> (nullable)"
+        },
+        "nestedLong": {
+            "$ref": "#/definitions/TestClass2<Long> (nullable)"
+        },
+        "nestedLongList": {
+            "type": ["array", "null"],
+            "items": {
+                "$ref": "#/definitions/TestClass2<Long>"
+            },
+            "title": "List<TestClass2<Long>>",
+            "description": "for type in general: List<TestClass2<Long>>",
+            "minItems": 2,
+            "maxItems": 100,
+            "uniqueItems": false
+        },
+        "getClass4()": {
+            "$ref": "#/definitions/TestClass4<String> (nullable)"
+        },
+        "getNestedClass1Array()": {
+            "$ref": "#/definitions/TestClass2<TestClass1[]> (nullable)"
+        },
+        "getNestedLong()": {
+            "$ref": "#/definitions/TestClass2<Long> (nullable)"
+        },
+        "getNestedLongList()": {
+            "type": ["array", "null"],
+            "items": {
+                "$ref": "#/definitions/TestClass2<Long>"
+            },
+            "title": "List<TestClass2<Long>>",
+            "description": "for type in general: List<TestClass2<Long>>",
+            "minItems": 2,
+            "maxItems": 100,
+            "uniqueItems": false
+        }
+    },
+    "title": "TestClass3",
+    "description": "for type in general: TestClass3"
+}