Repository: victools/jsonschema-generator
Pull request #32 (2020-02-24 21:21:00)

feat: default for additional/patternProperties
Changes made in the interest of making the usage of `additionalProperties`/`patternProperties` more convenient (as per #22):
- Specifically omit both newly supported keywords in case of the custom definitions for simple types (i.e. the non-`object` ones as well as the base `Object.class`).
- Introduce new `Option.FORBIDDEN_ADDITIONAL_PROPERTIES_BY_DEFAULT` that applies to all definitions except for the specifically ones mentioned above and anything with `TypeScope.isContainerType()` being `true`.


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9da8e8d..093568c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 ### Added
 - Support for "additionalProperties" property via `SchemaGeneratorTypeConfigPart.withAdditionalPropertiesResolver()`
 - Support for "patternProperties" property via `SchemaGeneratorTypeConfigPart.withPatternPropertiesResolver()`
+- Introduce new `Option.FORBIDDEN_ADDITIONAL_PROPERTIES_BY_DEFAULT` for more convenient usage
 - Offer `TypeScope.getTypeParameterFor()` and `TypeContext.getTypeParameterFor()` convenience methods
 
 ## [4.1.0] - 2020-02-18
diff --git a/README.md b/README.md
index cf5d6c2..f01d43e 100644
--- a/README.md
+++ b/README.md
@@ -132,27 +132,16 @@ import com.github.victools.jsonschema.generator.SchemaGeneratorConfigBuilder;
 import java.util.Map;
 ```
 ```java
-SchemaGeneratorConfigBuilder configBuilder = new SchemaGeneratorConfigBuilder(objectMapper);
+SchemaGeneratorConfigBuilder configBuilder = new SchemaGeneratorConfigBuilder(objectMapper)
+    .with(Option.FORBIDDEN_ADDITIONAL_PROPERTIES_BY_DEFAULT);
 configBuilder.forTypesInGeneral()
     .withAdditionalPropertiesResolver((scope) -> {
         if (scope.getType().isInstanceOf(Map.class)) {
             // within a Map<Key, Value> allow additionalProperties of the Value type
+            // if no type parameters are defined, this will result in additionalProperties to be omitted (by way of return Object.class)
             return scope.getTypeParameterFor(Map.class, 1);
         }
-        if (scope.getType().isPrimitive()
-                || scope.getType().isInstanceOf(Number.class)
-                || scope.getType().isInstanceOf(CharSequence.class)) {
-            // explicitly cause the "additionalProperties" to be omitted for these non-object types – only do this if you are sure
-            // when in doubt: rather return null
-            return Object.class;
-        }
-        if (scope.isContainerType()) {
-            // mark this resolver as neutral, i.e. falling back on what the next resolver may decide
-            // if no other resolver provides a specific value, the default logic applies: omitting the "additionalProperties"
-            return null;
-        }
-        // set "additionalProperties" to "false" for all remaining objects
-        return Void.class;
+        return null;
     });
 ```
 
diff --git a/src/main/java/com/github/victools/jsonschema/generator/Option.java b/src/main/java/com/github/victools/jsonschema/generator/Option.java
index 2a5b52b..028e102 100644
--- a/src/main/java/com/github/victools/jsonschema/generator/Option.java
+++ b/src/main/java/com/github/victools/jsonschema/generator/Option.java
@@ -16,6 +16,7 @@
 
 package com.github.victools.jsonschema.generator;
 
+import com.github.victools.jsonschema.generator.impl.module.AdditionalPropertiesModule;
 import com.github.victools.jsonschema.generator.impl.module.ConstantValueModule;
 import com.github.victools.jsonschema.generator.impl.module.EnumModule;
 import com.github.victools.jsonschema.generator.impl.module.FieldExclusionModule;
@@ -193,6 +194,12 @@ public enum Option {
      * Default: false (disabled)
      */
     NULLABLE_METHOD_RETURN_VALUES_BY_DEFAULT(null, null),
+    /**
+     * Whether a schema's "additionalProperties" should be set to "false" if no specific configuration says otherwise.
+     * <br>
+     * Default: false (omitting the "additionalProperties" keyword and thereby allowing any additional properties in an object schema)
+     */
+    FORBIDDEN_ADDITIONAL_PROPERTIES_BY_DEFAULT(AdditionalPropertiesModule::forbiddenForAllObjectsButContainers, null),
     /**
      * Whether all referenced objects should be listed in the schema's "definitions", otherwise single occurrences are defined in-line.
      * <br>
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 8f72293..abc3811 100644
--- a/src/main/java/com/github/victools/jsonschema/generator/SchemaGeneratorConfigBuilder.java
+++ b/src/main/java/com/github/victools/jsonschema/generator/SchemaGeneratorConfigBuilder.java
@@ -21,11 +21,11 @@ import com.github.victools.jsonschema.generator.impl.SchemaGeneratorConfigImpl;
 import java.util.ArrayList;
 import java.util.EnumSet;
 import java.util.HashMap;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
-import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
 /**
@@ -72,25 +72,23 @@ public class SchemaGeneratorConfigBuilder {
      */
     public SchemaGeneratorConfig build() {
         // apply the configurations associated with enabled/disabled options
-        Map<Option, Boolean> completeSetOfOptions = EnumSet.allOf(Option.class).stream()
-                .collect(Collectors.toMap(
-                        option -> option,
-                        option -> this.options.getOrDefault(option, this.preset.isOptionEnabledByDefault(option))));
-        Set<Option> enabledOptions = completeSetOfOptions.entrySet()
-                .stream()
-                .filter(Map.Entry::getValue)
-                .map(Map.Entry::getKey)
+        EnumSet<Option> allOptions = EnumSet.allOf(Option.class);
+        Set<Option> enabledOptions = EnumSet.allOf(Option.class).stream()
+                .filter(option -> this.options.getOrDefault(option, this.preset.isOptionEnabledByDefault(option)))
                 .collect(Collectors.toSet());
-        Predicate<Option> isValid = (configuredOption) -> enabledOptions.stream().noneMatch(enabledOne -> enabledOne.isOverriding(configuredOption));
-        completeSetOfOptions.entrySet()
-                .stream()
-                .filter(setting -> !setting.getValue() || isValid.test(setting.getKey()))
+        Map<Option, Boolean> validOptions = allOptions.stream()
+                .filter((configuredOption) -> enabledOptions.stream().noneMatch(enabledOne -> enabledOne.isOverriding(configuredOption)))
+                .collect(Collectors.toMap(option -> option, enabledOptions::contains, (first, second) -> first, LinkedHashMap::new));
+
+        validOptions.entrySet().stream()
                 .map(setting -> setting.getKey().getModule(setting.getValue()))
                 .filter(Objects::nonNull)
                 .forEach(this::with);
+        // discard invalid enabled options
+        enabledOptions.retainAll(validOptions.keySet());
         // construct the actual configuration instance
         return new SchemaGeneratorConfigImpl(this.objectMapper,
-                completeSetOfOptions,
+                enabledOptions,
                 this.typesInGeneralConfigPart,
                 this.fieldConfigPart,
                 this.methodConfigPart,
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 ca4067e..fcb05a1 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
@@ -40,6 +40,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
+import java.util.Set;
 
 /**
  * Default implementation of a schema generator's configuration.
@@ -47,7 +48,7 @@ import java.util.Optional;
 public class SchemaGeneratorConfigImpl implements SchemaGeneratorConfig {
 
     private final ObjectMapper objectMapper;
-    private final Map<Option, Boolean> options;
+    private final Set<Option> enabledOptions;
     private final SchemaGeneratorTypeConfigPart<TypeScope> typesInGeneralConfigPart;
     private final SchemaGeneratorConfigPart<FieldScope> fieldConfigPart;
     private final SchemaGeneratorConfigPart<MethodScope> methodConfigPart;
@@ -58,7 +59,7 @@ public class SchemaGeneratorConfigImpl implements SchemaGeneratorConfig {
      * Constructor of a configuration instance.
      *
      * @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 enabledOptions enabled settings/options (either by default or explicitly set)
      * @param typesInGeneralConfigPart configuration part for context-independent attribute collection
      * @param fieldConfigPart configuration part for fields
      * @param methodConfigPart configuration part for methods
@@ -66,14 +67,14 @@ public class SchemaGeneratorConfigImpl implements SchemaGeneratorConfig {
      * @param typeAttributeOverrides applicable type attribute overrides
      */
     public SchemaGeneratorConfigImpl(ObjectMapper objectMapper,
-            Map<Option, Boolean> options,
+            Set<Option> enabledOptions,
             SchemaGeneratorTypeConfigPart<TypeScope> typesInGeneralConfigPart,
             SchemaGeneratorConfigPart<FieldScope> fieldConfigPart,
             SchemaGeneratorConfigPart<MethodScope> methodConfigPart,
             List<CustomDefinitionProviderV2> customDefinitions,
             List<TypeAttributeOverride> typeAttributeOverrides) {
         this.objectMapper = objectMapper;
-        this.options = options;
+        this.enabledOptions = enabledOptions;
         this.typesInGeneralConfigPart = typesInGeneralConfigPart;
         this.fieldConfigPart = fieldConfigPart;
         this.methodConfigPart = methodConfigPart;
@@ -88,7 +89,7 @@ public class SchemaGeneratorConfigImpl implements SchemaGeneratorConfig {
      * @return whether the given generator option is enabled
      */
     private boolean isOptionEnabled(Option setting) {
-        return this.options.getOrDefault(setting, false);
+        return this.enabledOptions.contains(setting);
     }
 
     @Override
diff --git a/src/main/java/com/github/victools/jsonschema/generator/impl/module/AdditionalPropertiesModule.java b/src/main/java/com/github/victools/jsonschema/generator/impl/module/AdditionalPropertiesModule.java
new file mode 100644
index 0000000..e4b1a44
--- /dev/null
+++ b/src/main/java/com/github/victools/jsonschema/generator/impl/module/AdditionalPropertiesModule.java
@@ -0,0 +1,71 @@
+/*
+ * 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.impl.module;
+
+import com.github.victools.jsonschema.generator.Module;
+import com.github.victools.jsonschema.generator.SchemaGeneratorConfigBuilder;
+import com.github.victools.jsonschema.generator.TypeScope;
+import java.lang.reflect.Type;
+import java.util.function.Predicate;
+
+/**
+ * Default module being included if {@code Option.FORBIDDEN_ADDITIONAL_PROPERTIES_BY_DEFAULT} is enabled.
+ */
+public class AdditionalPropertiesModule implements Module {
+
+    /**
+     * Create module instance that forbids additional properties everywhere but on container types.
+     * <br>
+     * This assumes that the respective {@link SimpleTypeModule} instance is being applied first and already enforces the "additionProperties" keyword
+     * to be omitted on other non-object schemas.
+     *
+     * @return module instance
+     */
+    public static AdditionalPropertiesModule forbiddenForAllObjectsButContainers() {
+        return new AdditionalPropertiesModule(scope -> !scope.isContainerType());
+    }
+
+    private final Predicate<TypeScope> exclusionCheck;
+
+    /**
+     * Constructor.
+     *
+     * @param exclusionCheck determining whether additionalProperties should be forbidden on a given scope
+     */
+    public AdditionalPropertiesModule(Predicate<TypeScope> exclusionCheck) {
+        this.exclusionCheck = exclusionCheck;
+    }
+
+    @Override
+    public void applyToConfigBuilder(SchemaGeneratorConfigBuilder builder) {
+        builder.forTypesInGeneral()
+                .withAdditionalPropertiesResolver(this::resolveAdditionalProperties);
+    }
+
+    /**
+     * Either forbid additionProperties (according to the specified check) or leave it up to other configurations.
+     *
+     * @param scope type scope for which to determine whether additionalProperties should be forbidden
+     * @return either Void.class to indicate forbidden additionalProperties or null
+     */
+    private Type resolveAdditionalProperties(TypeScope scope) {
+        if (this.exclusionCheck.test(scope)) {
+            return Void.class;
+        }
+        return null;
+    }
+}
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 58e2b47..043e158 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
@@ -26,6 +26,8 @@ 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.TypeScope;
+import java.lang.reflect.Type;
 import java.math.BigDecimal;
 import java.math.BigInteger;
 import java.time.Instant;
@@ -37,6 +39,7 @@ import java.time.OffsetTime;
 import java.time.ZoneId;
 import java.time.ZonedDateTime;
 import java.util.Calendar;
+import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.Map;
@@ -179,12 +182,43 @@ public class SimpleTypeModule implements Module {
                 .withNullableCheck(this::isNullableType);
         builder.forMethods()
                 .withNullableCheck(this::isNullableType);
+        builder.forTypesInGeneral()
+                .withAdditionalPropertiesResolver(this::resolveAdditionalProperties)
+                .withPatternPropertiesResolver(this::resolvePatternProperties);
 
         builder.with(new SimpleTypeDefinitionProvider(builder.getObjectMapper()));
     }
 
     /**
-     * Implementation of the {@link CustomDefinitionProvider} interface for applying fixed schema definitions for simple java types.
+     * Specifically omit the "additionalProperties" keyword for non-object types.
+     *
+     * @param scope the scope to check for fixed non-object types
+     * @return either Object.class to cause omission of the "additonalProperties" keyword or null to leave it up to following configurations
+     */
+    private Type resolveAdditionalProperties(TypeScope scope) {
+        if (scope.getType().getTypeParameters().isEmpty() && this.fixedJsonSchemaTypes.containsKey(scope.getType().getErasedType())) {
+            // indicate no specific additionalProperties type - thereby causing it to be omitted from the generated schema
+            return Object.class;
+        }
+        return null;
+    }
+
+    /**
+     * Specifically omit the "patternProperties" keyword for non-object types.
+     *
+     * @param scope the scope to check for fixed non-object types
+     * @return either an empty map to cause omission of the "patternProperties" keyword or null to leave it up to following configurations
+     */
+    private Map<String, Type> resolvePatternProperties(TypeScope scope) {
+        if (scope.getType().getTypeParameters().isEmpty() && this.fixedJsonSchemaTypes.containsKey(scope.getType().getErasedType())) {
+            // indicate no specific patternProperties - thereby causing it to be omitted from the generated schema
+            return Collections.emptyMap();
+        }
+        return null;
+    }
+
+    /**
+     * Implementation of the {@link CustomDefinitionProviderV2} interface for applying fixed schema definitions for simple java types.
      */
     private class SimpleTypeDefinitionProvider implements CustomDefinitionProviderV2 {
 
@@ -201,7 +235,6 @@ public class SimpleTypeModule implements Module {
 
         @Override
         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 176172d..9ebbae4 100644
--- a/src/test/java/com/github/victools/jsonschema/generator/SchemaGeneratorTest.java
+++ b/src/test/java/com/github/victools/jsonschema/generator/SchemaGeneratorTest.java
@@ -29,6 +29,7 @@ import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
@@ -84,6 +85,18 @@ public class SchemaGeneratorTest {
         Assert.assertEquals(expectedJsonSchemaType, result.get(SchemaConstants.TAG_TYPE).asText());
     }
 
+    @Test
+    @Parameters(method = "parametersForTestGenerateSchema_SimpleType")
+    public void testGenerateSchema_SimpleType_withAdditionalPropertiesOption(Class<?> targetType, String expectedJsonSchemaType) throws Exception {
+        SchemaGeneratorConfig config = new SchemaGeneratorConfigBuilder(new ObjectMapper())
+                .with(Option.FORBIDDEN_ADDITIONAL_PROPERTIES_BY_DEFAULT)
+                .build();
+        SchemaGenerator generator = new SchemaGenerator(config);
+        JsonNode result = generator.generateSchema(targetType);
+        Assert.assertEquals(1, result.size());
+        Assert.assertEquals(expectedJsonSchemaType, result.get(SchemaConstants.TAG_TYPE).asText());
+    }
+
     @Test
     public void testGenerateSchema_CustomDefinition() throws Exception {
         CustomDefinitionProviderV2 customDefinitionProvider = (javaType, context) -> javaType.getErasedType() == Integer.class
@@ -175,23 +188,25 @@ public class SchemaGeneratorTest {
     }
 
     private static Type resolveAdditionalProperties(TypeScope scope) {
-        if (scope.isContainerType() || scope.getType().isPrimitive()
-                || scope.getType().isInstanceOf(Number.class) || scope.getType().isInstanceOf(CharSequence.class)) {
-            return Object.class;
+        if (scope.getType().isInstanceOf(TestClass2.class)) {
+            return Void.class;
         }
         if (scope.getType().isInstanceOf(TestClass4.class)) {
             return scope.getTypeParameterFor(TestClass4.class, 1);
         }
-        return Void.class;
+        return null;
     }
 
     private static Map<String, Type> resolvePatternProperties(TypeScope scope) {
-        if (!scope.getType().isInstanceOf(TestClass2.class)) {
-            return null;
+        if (scope.getType().isInstanceOf(TestClass2.class)) {
+            Map<String, Type> patternProperties = new HashMap<>();
+            patternProperties.put("^generic.+$", scope.getTypeParameterFor(TestClass2.class, 0));
+            return patternProperties;
+        }
+        if (scope.getType().isInstanceOf(TestClass4.class)) {
+            return Collections.emptyMap();
         }
-        Map<String, Type> patternProperties = new HashMap<>();
-        patternProperties.put("^generic.+$", scope.getTypeParameterFor(TestClass2.class, 0));
-        return patternProperties;
+        return null;
     }
 
     private static void populateConfigPart(SchemaGeneratorConfigPart<? extends MemberScope<?, ?>> configPart, String descriptionPrefix) {
@@ -211,7 +226,8 @@ public class SchemaGeneratorTest {
             }
             return null;
         });
-        Module typeInGeneralModule = configBuilder -> populateTypeConfigPart(configBuilder.forTypesInGeneral(), "for type in general: ");
+        Module typeInGeneralModule = configBuilder -> populateTypeConfigPart(
+                configBuilder.with(Option.FORBIDDEN_ADDITIONAL_PROPERTIES_BY_DEFAULT).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: ");
         Module enumToStringModule = configBuilder -> configBuilder.with(Option.FLATTENED_ENUMS_FROM_TOSTRING);
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 e9c52d7..c1d57a7 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
@@ -33,9 +33,9 @@ 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;
+import java.util.HashSet;
 import java.util.List;
-import java.util.Map;
+import java.util.Set;
 import junitparams.JUnitParamsRunner;
 import junitparams.Parameters;
 import org.junit.Assert;
@@ -52,7 +52,7 @@ public class SchemaGeneratorConfigImplTest extends AbstractTypeAwareTest {
 
     private SchemaGeneratorConfigImpl instance;
     private ObjectMapper objectMapper;
-    private Map<Option, Boolean> options;
+    private Set<Option> enabledOptions;
     private SchemaGeneratorTypeConfigPart<TypeScope> typesInGeneralConfigPart;
     private SchemaGeneratorConfigPart<FieldScope> fieldConfigPart;
     private SchemaGeneratorConfigPart<MethodScope> methodConfigPart;
@@ -67,14 +67,14 @@ public class SchemaGeneratorConfigImplTest extends AbstractTypeAwareTest {
     @SuppressWarnings("unchecked")
     public void setUp() {
         this.objectMapper = Mockito.mock(ObjectMapper.class);
-        this.options = new HashMap<>();
+        this.enabledOptions = new HashSet<>();
         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.typesInGeneralConfigPart,
+        this.instance = new SchemaGeneratorConfigImpl(this.objectMapper, this.enabledOptions, this.typesInGeneralConfigPart,
                 this.fieldConfigPart, this.methodConfigPart, this.customDefinitions, this.typeAttributeOverrides);
     }
 
@@ -86,18 +86,11 @@ public class SchemaGeneratorConfigImplTest extends AbstractTypeAwareTest {
 
     @Test
     public void testShouldCreateDefinitionsForAllObjects_optionEnabled() {
-        this.options.put(Option.DEFINITIONS_FOR_ALL_OBJECTS, Boolean.TRUE);
+        this.enabledOptions.add(Option.DEFINITIONS_FOR_ALL_OBJECTS);
         boolean specificallyEnabled = this.instance.shouldCreateDefinitionsForAllObjects();
         Assert.assertTrue(specificallyEnabled);
     }
 
-    @Test
-    public void testShouldCreateDefinitionsForAllObjects_optionDisabled() {
-        this.options.put(Option.DEFINITIONS_FOR_ALL_OBJECTS, Boolean.FALSE);
-        boolean specificallyDisabled = this.instance.shouldCreateDefinitionsForAllObjects();
-        Assert.assertFalse(specificallyDisabled);
-    }
-
     @Test
     public void testCreateObjectNode() {
         ObjectNode node = Mockito.mock(ObjectNode.class);
@@ -150,25 +143,22 @@ public class SchemaGeneratorConfigImplTest extends AbstractTypeAwareTest {
 
     Object parametersForTestIsNullable() {
         return new Object[][]{
-            {null, null, false},
-            {null, Boolean.TRUE, true},
-            {null, Boolean.FALSE, false},
-            {Boolean.TRUE, null, true},
-            {Boolean.TRUE, Boolean.TRUE, true},
-            {Boolean.TRUE, Boolean.FALSE, true},
-            {Boolean.FALSE, null, false},
-            {Boolean.FALSE, Boolean.TRUE, false},
-            {Boolean.FALSE, Boolean.FALSE, false}
+            {null, true, true},
+            {null, false, false},
+            {Boolean.TRUE, true, true},
+            {Boolean.TRUE, false, true},
+            {Boolean.FALSE, true, false},
+            {Boolean.FALSE, false, false}
         };
     }
 
     @Test
     @Parameters(method = "parametersForTestIsNullable")
-    public void testIsNullableField(Boolean configResult, Boolean optionEnabled, boolean expectedResult) throws Exception {
+    public void testIsNullableField(Boolean configResult, boolean optionEnabled, boolean expectedResult) throws Exception {
         FieldScope field = this.getTestClassField("field");
         Mockito.when(this.fieldConfigPart.isNullable(field)).thenReturn(configResult);
-        if (optionEnabled != null) {
-            this.options.put(Option.NULLABLE_FIELDS_BY_DEFAULT, optionEnabled);
+        if (optionEnabled) {
+            this.enabledOptions.add(Option.NULLABLE_FIELDS_BY_DEFAULT);
         }
         boolean result = this.instance.isNullable(field);
         Assert.assertEquals(expectedResult, result);
@@ -176,11 +166,11 @@ public class SchemaGeneratorConfigImplTest extends AbstractTypeAwareTest {
 
     @Test
     @Parameters(method = "parametersForTestIsNullable")
-    public void testIsNullableMethod(Boolean configResult, Boolean optionEnabled, boolean expectedResult) throws Exception {
+    public void testIsNullableMethod(Boolean configResult, boolean optionEnabled, boolean expectedResult) throws Exception {
         MethodScope method = this.getTestClassMethod("getField");
         Mockito.when(this.methodConfigPart.isNullable(method)).thenReturn(configResult);
-        if (optionEnabled != null) {
-            this.options.put(Option.NULLABLE_METHOD_RETURN_VALUES_BY_DEFAULT, optionEnabled);
+        if (optionEnabled) {
+            this.enabledOptions.add(Option.NULLABLE_METHOD_RETURN_VALUES_BY_DEFAULT);
         }
         boolean result = this.instance.isNullable(method);
         Assert.assertEquals(expectedResult, result);
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 64477cc..2253764 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
@@ -21,22 +21,32 @@ import com.github.victools.jsonschema.generator.FieldScope;
 import com.github.victools.jsonschema.generator.MethodScope;
 import com.github.victools.jsonschema.generator.SchemaGeneratorConfigBuilder;
 import com.github.victools.jsonschema.generator.SchemaGeneratorConfigPart;
+import com.github.victools.jsonschema.generator.SchemaGeneratorTypeConfigPart;
+import com.github.victools.jsonschema.generator.TypeScope;
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
 import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnitRunner;
 
 /**
  * Test for {@link SimpleTypeModule} class.
  */
+@RunWith(MockitoJUnitRunner.class)
 public class SimpleTypeModuleTest {
 
     private SimpleTypeModule instance;
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
     private SchemaGeneratorConfigBuilder builder;
+    @Mock(answer = Answers.RETURNS_SELF)
+    private SchemaGeneratorTypeConfigPart<TypeScope> typeConfigPart;
 
     @Before
     public void setUp() {
         this.instance = new SimpleTypeModule();
-        this.builder = Mockito.mock(SchemaGeneratorConfigBuilder.class, Mockito.RETURNS_DEEP_STUBS);
+        Mockito.when(this.builder.forTypesInGeneral()).thenReturn(this.typeConfigPart);
     }
 
     @Test
@@ -53,6 +63,11 @@ public class SimpleTypeModuleTest {
         Mockito.verifyNoMoreInteractions(methodConfigPart);
         Mockito.verify(this.builder, Mockito.times(2)).forMethods();
 
+        Mockito.verify(this.typeConfigPart).withAdditionalPropertiesResolver(Mockito.any());
+        Mockito.verify(this.typeConfigPart).withPatternPropertiesResolver(Mockito.any());
+        Mockito.verifyNoMoreInteractions(this.typeConfigPart);
+        Mockito.verify(this.builder).forTypesInGeneral();
+
         Mockito.verify(this.builder).getObjectMapper();
         Mockito.verify(this.builder).with(Mockito.any(CustomDefinitionProviderV2.class));
 
diff --git a/src/test/resources/com/github/victools/jsonschema/generator/testclass3-JAVA_OBJECT-methodattributes.json b/src/test/resources/com/github/victools/jsonschema/generator/testclass3-JAVA_OBJECT-methodattributes.json
index a76f572..d443431 100644
--- a/src/test/resources/com/github/victools/jsonschema/generator/testclass3-JAVA_OBJECT-methodattributes.json
+++ b/src/test/resources/com/github/victools/jsonschema/generator/testclass3-JAVA_OBJECT-methodattributes.json
@@ -23,8 +23,7 @@
                             "$ref": "#/definitions/RoundingMode-nullable"
                         }, {
                             "title": "RoundingMode",
-                            "description": "looked-up from method: RoundingMode",
-                            "additionalProperties": false
+                            "description": "looked-up from method: RoundingMode"
                         }]
                 },
                 "valueOf(int)": {
@@ -32,8 +31,7 @@
                             "$ref": "#/definitions/RoundingMode-nullable"
                         }, {
                             "title": "RoundingMode",
-                            "description": "looked-up from method: RoundingMode",
-                            "additionalProperties": false
+                            "description": "looked-up from method: RoundingMode"
                         }]
                 },
                 "values()": {