Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ public class Arguments implements GenerationConfig {
private boolean disableSetters = false;

@Parameter(names = { "-tv", "--target-version" }, description = "The target version for generated source files.")
private String targetVersion = "1.6";
private String targetVersion = "8";

@Parameter(names = { "-ida", "--include-dynamic-accessors" }, description = "Include dynamic getter, setter, and builder support on generated types.")
private boolean includeDynamicAccessors = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
package org.jsonschema2pojo.rules;

import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.util.Collection;
import java.util.Map;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
package org.jsonschema2pojo.rules;

import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.util.Collection;
import java.util.Map;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,34 @@ public ValidRule(RuleFactory ruleFactory) {

@Override
public JType apply(String nodeName, JsonNode node, JsonNode parent, JType type, Schema currentSchema) {
if (ruleFactory.getGenerationConfig().isIncludeJsr303Annotations() && type instanceof JClass jclass) {
if (!isContainer(jclass) && !isScalar(jclass)) {
return JAnnotatedClass.of(jclass).annotated(getValidClass());
}
if (null != node && node.has("existingJavaType")) {
return applyToExistingJavaType(jclass);
}
}
return type;
}

if (ruleFactory.getGenerationConfig().isIncludeJsr303Annotations()
&& type instanceof JClass jclass
&& !isContainer(jclass)
&& !isScalar(jclass)) {
return JAnnotatedClass.of(jclass).annotated(getValidClass());
} else {
return type;
private JClass applyToExistingJavaType(JClass jClass) {
if (jClass.isReference() && isContainer(jClass.erasure())) {
final var typeParameters = jClass.getTypeParameters();
if ((jClass.owner().ref(Iterable.class).isAssignableFrom(jClass.erasure())
|| jClass.owner().ref(Optional.class).isAssignableFrom(jClass.erasure()))
&& typeParameters.size() == 1
&& !isScalar(typeParameters.get(0))) {
return jClass.erasure().narrow(applyToExistingJavaType(typeParameters.get(0)));
} else if (jClass.owner().ref(Map.class).isAssignableFrom(jClass.erasure())
&& typeParameters.size() == 2 && !isScalar(typeParameters.get(1))) {
return jClass.erasure().narrow(typeParameters.get(0), applyToExistingJavaType(typeParameters.get(1)));
}
}
if (!isContainer(jClass) && !isScalar(jClass)) {
return JAnnotatedClass.of(jClass).annotated(getValidClass());
}
return jClass;
}

private boolean isContainer(JClass jclass) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**
* Copyright © 2010-2020 Nokia
*
* 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.example;

import jakarta.validation.constraints.Size;

public class ClassWithSizeAnnotation {

@Size(max = 2)
@javax.validation.constraints.Size(max = 2)
private final String text;

public ClassWithSizeAnnotation() {
text = "should fail";
}

public ClassWithSizeAnnotation(String text) {
this.text = text;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import org.apache.bval.jsr.ApacheValidationProvider;
Expand All @@ -47,6 +49,8 @@
import org.junit.jupiter.params.ParameterizedClass;
import org.junit.jupiter.params.provider.ValueSource;

import com.example.ClassWithSizeAnnotation;

@SuppressWarnings("rawtypes")
@ParameterizedClass
@ValueSource(booleans = { true, false })
Expand Down Expand Up @@ -631,7 +635,7 @@ public void jsr303ValidAnnotationOnAdditionalPropertiesWithItemTypeArray() throw
}

@Test
public void jsr303ValidAnnotationsOnExistingJavaType() throws Exception {
public void jsr303ValidAnnotationsOnExistingJavaTypeWithScalar() throws Exception {
ClassLoader resultsClassLoader = schemaRule.generateAndCompile(
"/schema/jsr303/validExistingJavaType.json", "com.example",
config("includeJsr303Annotations", true, "useJakartaValidation", useJakartaValidation));
Expand Down Expand Up @@ -700,6 +704,75 @@ public void jsr303ValidAnnotationNotAppliedToScalarTypes() throws Exception {
source, not(containsString("@Valid")));
}

@Test
public void jsr303AnnotationsValidatedForExistingJavaType() throws ReflectiveOperationException {
ClassLoader classLoader = schemaRule.generateAndCompile(
"/schema/jsr303/existingJavaTypeProperties.json",
"com.example",
config("includeJsr303Annotations", true, "useJakartaValidation", useJakartaValidation));

Class<?> clazz = classLoader.loadClass("com.example.ExistingJavaTypeProperties");
Object instance = clazz.getDeclaredConstructor().newInstance();

assertNumberOfConstraintViolationsOn(instance, is(0));

instance = createInstanceWithPropertyValue(
clazz,
"mapOfStringOptionalListOfExistingClasses",
Map.of("a", Optional.of(List.of(new ClassWithSizeAnnotation()))));
assertNumberOfConstraintViolationsOn(instance, is(1));

instance = createInstanceWithPropertyValue(
clazz,
"listOfOptionalStringExistingClassMaps",
List.of(Optional.of(Map.of("a", new ClassWithSizeAnnotation()))));
assertNumberOfConstraintViolationsOn(instance, is(1));

instance = createInstanceWithPropertyValue(
clazz,
"listOfOptionalStringObjectMaps",
List.of(Optional.of(Map.of("a", new ClassWithSizeAnnotation()))));
assertNumberOfConstraintViolationsOn(instance, is(1));

instance = createInstanceWithPropertyValue(clazz, "optionalOfExistingClass", Optional.of(new ClassWithSizeAnnotation()));
assertNumberOfConstraintViolationsOn(instance, is(1));

instance = createInstanceWithPropertyValue(clazz, "mapOfStringExistingJavaClasses", Map.of("a", new ClassWithSizeAnnotation()));
assertNumberOfConstraintViolationsOn(instance, is(1));

instance = createInstanceWithPropertyValue(clazz, "listOfObjects", List.of(new ClassWithSizeAnnotation()));
assertNumberOfConstraintViolationsOn(instance, is(1));

instance = createInstanceWithPropertyValue(clazz, "mapOfStringObject", Map.of("a", new ClassWithSizeAnnotation()));
assertNumberOfConstraintViolationsOn(instance, is(1));

instance = createInstanceWithPropertyValue(clazz, "primitiveLong", 1L);
assertNumberOfConstraintViolationsOn(instance, is(1));

instance = createInstanceWithPropertyValue(clazz, "rawOptional", Optional.of(new ClassWithSizeAnnotation()));
assertNumberOfConstraintViolationsOn(instance, is(0));

instance = createInstanceWithPropertyValue(clazz, "rawMap", Map.of("a", new ClassWithSizeAnnotation()));
assertNumberOfConstraintViolationsOn(instance, is(0));

instance = createInstanceWithPropertyValue(
clazz,
"existingTypeAsArrayItem",
List.of(Map.of("a", new ClassWithSizeAnnotation())));
assertNumberOfConstraintViolationsOn(instance, is(1));

instance = createInstanceWithPropertyValue(clazz, "itemOfExistingClass", new ClassWithSizeAnnotation());
assertNumberOfConstraintViolationsOn(instance, is(1));

// Note: at present arrays are generated as single item, once arrays are generated correctly below checks should be adjusted
instance = createInstanceWithPropertyValue(clazz, "arrayOfExistingClasses", new ClassWithSizeAnnotation());
assertNumberOfConstraintViolationsOn(instance, is(1));

// Validation does not cascade through multidimensional arrays at present
instance = createInstanceWithPropertyValue(clazz, "multiDimensionalArrayOfExistingClasses", new ClassWithSizeAnnotation());
assertNumberOfConstraintViolationsOn(instance, is(1));
}

private void assertNumberOfConstraintViolationsOn(Object instance, Matcher<Integer> matcher) {
final Set<?> violationsForValidInstance = useJakartaValidation ? validator.validate(instance) : javaxValidator.validate(instance);
final String validatorName = useJakartaValidation ? "jakarta/hibernate validator" : "javax/bval validator";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@
import org.robolectric.internal.ShadowProvider;
import org.robolectric.internal.bytecode.InstrumentationConfiguration;
import org.robolectric.internal.bytecode.Interceptors;
import org.robolectric.internal.bytecode.MutableClass;
import org.robolectric.internal.bytecode.ClassDetails;
import org.robolectric.internal.bytecode.ClassInstrumentor;
import org.robolectric.internal.bytecode.Sandbox;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
{
"type": "object",
"additionalProperties": false,
"properties": {
"mapOfStringOptionalListOfExistingClasses": {
"type": "object",
"existingJavaType": "java.util.Map<java.lang.String, java.util.Optional<java.util.List<com.example.ClassWithSizeAnnotation>>>"
},
"listOfOptionalStringExistingClassMaps": {
"type": "object",
"existingJavaType": "java.util.List<java.util.Optional<java.util.Map<java.lang.String, com.example.ClassWithSizeAnnotation>>>"
},
"listOfOptionalStringObjectMaps": {
"type": "object",
"existingJavaType": "java.util.List<java.util.Optional<java.util.Map<java.lang.String, java.lang.Object>>>"
},
"listOfOptionalStringStringMaps": {
"type": "object",
"existingJavaType": "java.util.List<java.util.Optional<java.util.Map<java.lang.String, java.lang.String>>>"
},
"optionalOfExistingClass": {
"type": "object",
"existingJavaType": "java.util.Optional<com.example.ClassWithSizeAnnotation>"
},
"mapOfStringExistingJavaClasses": {
"type": "object",
"existingJavaType": "java.util.Map<java.lang.String, com.example.ClassWithSizeAnnotation>"
},
"listOfObjects": {
"type": "object",
"existingJavaType": "java.util.List<java.lang.Object>"
},
"mapOfStringObject": {
"type": "object",
"existingJavaType": "java.util.Map<java.lang.String, java.lang.Object>"
},
"primitiveLong": {
"maximum": 0,
"existingJavaType": "long"
},
"rawOptional": {
"type": "object",
"existingJavaType": "java.util.Optional"
},
"rawMap": {
"type": "object",
"existingJavaType": "java.util.Map"
},
"existingTypeAsArrayItem": {
"type": "array",
"items": {
"existingJavaType": "java.util.Map<java.lang.String, com.example.ClassWithSizeAnnotation>"
}
},
"itemOfExistingClass": {
"type": "object",
"existingJavaType": "com.example.ClassWithSizeAnnotation"
},
"arrayOfExistingClasses": {
"type": "object",
"existingJavaType": "com.example.ClassWithSizeAnnotation[]"
},
"multiDimensionalArrayOfExistingClasses": {
"type": "object",
"existingJavaType": "com.example.ClassWithSizeAnnotation[][]"
}
}
}