Skip to content
This repository was archived by the owner on Mar 30, 2020. It is now read-only.

Commit 6f58eef

Browse files
initial module implementation
1 parent 8920cfe commit 6f58eef

File tree

2 files changed

+282
-0
lines changed

2 files changed

+282
-0
lines changed

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

77
## [Unreleased]
8+
### Added
9+
- Indicate a field/method to be nullable if `@Null` is present
10+
- Indicate a field/method to be not nullable if `@NotNull`, `@NotEmpty` or `@NotBlank` is present
11+
- Indicate an array's "minItems" according to `@Size` or `@NotEmpty`
12+
- Indicate an array's "maxItems" according to `@Size`
13+
- Indicate a string's "minLength" according to `@Size`, `@NotEmpty` or `@NotBlank`
14+
- Indicate a string's "maxLength" according to `@Size`
15+
- Indicate a number's "minimum" (inclusive) according to `@Min`, `@DecimalMin` or `@PositiveOrZero`
16+
- Indicate a number's "exclusiveMinimum" according to `@DecimalMin` or `@Positive`
17+
- Indicate a number's "maximum" (inclusive) according to `@Max`, `@DecimalMax` or `@NegativeOrZero`
18+
- Indicate a number's "exclusiveMaximum" according to `@DecimalMax` or `@Negative`
Lines changed: 271 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,271 @@
1+
/*
2+
* Copyright 2019 VicTools.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.github.victools.jsonschema.module.javax.validation;
18+
19+
import com.github.victools.jsonschema.generator.JavaType;
20+
import com.github.victools.jsonschema.generator.Module;
21+
import com.github.victools.jsonschema.generator.SchemaGeneratorConfigBuilder;
22+
import com.github.victools.jsonschema.generator.SchemaGeneratorConfigPart;
23+
import com.github.victools.jsonschema.generator.impl.ReflectionTypeUtils;
24+
import java.lang.reflect.AccessibleObject;
25+
import java.math.BigDecimal;
26+
import javax.validation.constraints.DecimalMax;
27+
import javax.validation.constraints.DecimalMin;
28+
import javax.validation.constraints.Max;
29+
import javax.validation.constraints.Min;
30+
import javax.validation.constraints.Negative;
31+
import javax.validation.constraints.NegativeOrZero;
32+
import javax.validation.constraints.NotBlank;
33+
import javax.validation.constraints.NotEmpty;
34+
import javax.validation.constraints.NotNull;
35+
import javax.validation.constraints.Null;
36+
import javax.validation.constraints.Positive;
37+
import javax.validation.constraints.PositiveOrZero;
38+
import javax.validation.constraints.Size;
39+
40+
/**
41+
* With this module, the base assumption for the nullable indication is that all fields and methods are nullable unless annotated otherwise.
42+
*/
43+
public class JavaxValidationModule implements Module {
44+
45+
@Override
46+
public void applyToConfigBuilder(SchemaGeneratorConfigBuilder builder) {
47+
this.applyToConfigPart(builder.forFields());
48+
this.applyToConfigPart(builder.forMethods());
49+
}
50+
51+
/**
52+
* Apply the various annotation-based resolvers for the given configuration part (this expected to be executed for both fields and methods).
53+
*
54+
* @param configPart config builder part to add configurations to
55+
*/
56+
private void applyToConfigPart(SchemaGeneratorConfigPart<? extends AccessibleObject> configPart) {
57+
configPart.withNullableCheck(this::isNullable);
58+
configPart.withArrayMinItemsResolver(this::resolveArrayMinItems);
59+
configPart.withArrayMaxItemsResolver(this::resolveArrayMaxItems);
60+
configPart.withStringMinLengthResolver(this::resolveStringMinLength);
61+
configPart.withStringMaxLengthResolver(this::resolveStringMaxLength);
62+
configPart.withNumberInclusiveMinimumResolver(this::resolveNumberInclusiveMinimum);
63+
configPart.withNumberExclusiveMinimumResolver(this::resolveNumberExclusiveMinimum);
64+
configPart.withNumberInclusiveMaximumResolver(this::resolveNumberInclusiveMaximum);
65+
configPart.withNumberExclusiveMaximumResolver(this::resolveNumberExclusiveMaximum);
66+
}
67+
68+
/**
69+
* Determine whether a given field or method is annotated to be not nullable.
70+
*
71+
* @param fieldOrMethod the field or method to check
72+
* @param type field's or method return value's type
73+
* @return whether the field/method is specifically annotated as nullable or not (returns null if not specified: assumption it is nullable then)
74+
*/
75+
private Boolean isNullable(AccessibleObject fieldOrMethod, JavaType type) {
76+
Boolean result;
77+
if (fieldOrMethod.isAnnotationPresent(NotNull.class)
78+
|| fieldOrMethod.isAnnotationPresent(NotBlank.class)
79+
|| fieldOrMethod.isAnnotationPresent(NotEmpty.class)) {
80+
// field is specifically NOT nullable
81+
result = Boolean.FALSE;
82+
} else if (fieldOrMethod.isAnnotationPresent(Null.class)) {
83+
// field is specifically null (and thereby nullable)
84+
result = Boolean.TRUE;
85+
} else {
86+
result = null;
87+
}
88+
return result;
89+
}
90+
91+
/**
92+
* Determine a given array type's minimum number of items.
93+
*
94+
* @param fieldOrMethod the field or method to check
95+
* @param type field's or method return value's type
96+
* @return specified minimum number of array items (or null)
97+
* @see Size
98+
*/
99+
private Integer resolveArrayMinItems(AccessibleObject fieldOrMethod, JavaType type) {
100+
if (ReflectionTypeUtils.isArrayType(type)) {
101+
Size sizeAnnotation = fieldOrMethod.getAnnotation(Size.class);
102+
if (sizeAnnotation != null && sizeAnnotation.min() > 0) {
103+
// minimum length greater than the default 0 was specified
104+
return sizeAnnotation.min();
105+
}
106+
if (fieldOrMethod.isAnnotationPresent(NotEmpty.class)) {
107+
return 1;
108+
}
109+
}
110+
return null;
111+
}
112+
113+
/**
114+
* Determine a given array type's maximum number of items.
115+
*
116+
* @param fieldOrMethod the field or method to check
117+
* @param type field's or method return value's type
118+
* @return specified maximum number of array items (or null)
119+
* @see Size
120+
*/
121+
private Integer resolveArrayMaxItems(AccessibleObject fieldOrMethod, JavaType type) {
122+
if (ReflectionTypeUtils.isArrayType(type)) {
123+
Size sizeAnnotation = fieldOrMethod.getAnnotation(Size.class);
124+
if (sizeAnnotation != null && sizeAnnotation.max() < 2147483647) {
125+
// maximum length below the default 2147483647 was specified
126+
return sizeAnnotation.max();
127+
}
128+
}
129+
return null;
130+
}
131+
132+
/**
133+
* Determine a given text type's minimum number of characters.
134+
*
135+
* @param fieldOrMethod the field or method to check
136+
* @param type field's or method return value's type
137+
* @return specified minimum number of characters (or null)
138+
* @see Size
139+
* @see NotEmpty
140+
* @see NotBlank
141+
*/
142+
private Integer resolveStringMinLength(AccessibleObject fieldOrMethod, JavaType type) {
143+
Class<?> rawType = ReflectionTypeUtils.getRawType(type.getResolvedType());
144+
if (CharSequence.class.isAssignableFrom(rawType)) {
145+
Size sizeAnnotation = fieldOrMethod.getAnnotation(Size.class);
146+
if (sizeAnnotation != null && sizeAnnotation.min() > 0) {
147+
// minimum length greater than the default 0 was specified
148+
return sizeAnnotation.min();
149+
}
150+
if (fieldOrMethod.isAnnotationPresent(NotEmpty.class)
151+
|| fieldOrMethod.isAnnotationPresent(NotBlank.class)) {
152+
return 1;
153+
}
154+
}
155+
return null;
156+
}
157+
158+
/**
159+
* Determine a given text type's maximum number of characters.
160+
*
161+
* @param fieldOrMethod the field or method to check
162+
* @param type field's or method return value's type
163+
* @return specified minimum number of characters (or null)
164+
* @see Size
165+
*/
166+
private Integer resolveStringMaxLength(AccessibleObject fieldOrMethod, JavaType type) {
167+
Class<?> rawType = ReflectionTypeUtils.getRawType(type.getResolvedType());
168+
if (CharSequence.class.isAssignableFrom(rawType)) {
169+
Size sizeAnnotation = fieldOrMethod.getAnnotation(Size.class);
170+
if (sizeAnnotation != null && sizeAnnotation.max() < 2147483647) {
171+
// maximum length below the default 2147483647 was specified
172+
return sizeAnnotation.max();
173+
}
174+
}
175+
return null;
176+
}
177+
178+
/**
179+
* Determine a number type's minimum (inclusive) value.
180+
*
181+
* @param fieldOrMethod the field or method to check
182+
* @param type field's or method return value's type
183+
* @return specified inclusive minimum value (or null)
184+
* @see Min
185+
* @see DecimalMin
186+
* @see PositiveOrZero
187+
*/
188+
private BigDecimal resolveNumberInclusiveMinimum(AccessibleObject fieldOrMethod, JavaType type) {
189+
Min minAnnotation = fieldOrMethod.getAnnotation(Min.class);
190+
if (minAnnotation != null) {
191+
return new BigDecimal(minAnnotation.value());
192+
}
193+
DecimalMin decimalMinAnnotation = fieldOrMethod.getAnnotation(DecimalMin.class);
194+
if (decimalMinAnnotation != null && decimalMinAnnotation.inclusive()) {
195+
return new BigDecimal(decimalMinAnnotation.value());
196+
}
197+
PositiveOrZero positiveAnnotation = fieldOrMethod.getAnnotation(PositiveOrZero.class);
198+
if (positiveAnnotation != null) {
199+
return BigDecimal.ZERO;
200+
}
201+
return null;
202+
}
203+
204+
/**
205+
* Determine a number type's minimum (exclusive) value.
206+
*
207+
* @param fieldOrMethod the field or method to check
208+
* @param type field's or method return value's type
209+
* @return specified exclusive minimum value (or null)
210+
* @see DecimalMin
211+
* @see Positive
212+
*/
213+
private BigDecimal resolveNumberExclusiveMinimum(AccessibleObject fieldOrMethod, JavaType type) {
214+
DecimalMin decimalMinAnnotation = fieldOrMethod.getAnnotation(DecimalMin.class);
215+
if (decimalMinAnnotation != null && !decimalMinAnnotation.inclusive()) {
216+
return new BigDecimal(decimalMinAnnotation.value());
217+
}
218+
Positive positiveAnnotation = fieldOrMethod.getAnnotation(Positive.class);
219+
if (positiveAnnotation != null) {
220+
return BigDecimal.ZERO;
221+
}
222+
return null;
223+
}
224+
225+
/**
226+
* Determine a number type's maximum (inclusive) value.
227+
*
228+
* @param fieldOrMethod the field or method to check
229+
* @param type field's or method return value's type
230+
* @return specified inclusive maximum value (or null)
231+
* @see Max
232+
* @see DecimalMax#inclusive()
233+
* @see NegativeOrZero
234+
*/
235+
private BigDecimal resolveNumberInclusiveMaximum(AccessibleObject fieldOrMethod, JavaType type) {
236+
Max maxAnnotation = fieldOrMethod.getAnnotation(Max.class);
237+
if (maxAnnotation != null) {
238+
return new BigDecimal(maxAnnotation.value());
239+
}
240+
DecimalMax decimalMaxAnnotation = fieldOrMethod.getAnnotation(DecimalMax.class);
241+
if (decimalMaxAnnotation != null && decimalMaxAnnotation.inclusive()) {
242+
return new BigDecimal(decimalMaxAnnotation.value());
243+
}
244+
NegativeOrZero negativeAnnotation = fieldOrMethod.getAnnotation(NegativeOrZero.class);
245+
if (negativeAnnotation != null) {
246+
return BigDecimal.ZERO;
247+
}
248+
return null;
249+
}
250+
251+
/**
252+
* Determine a number type's maximum (exclusive) value.
253+
*
254+
* @param fieldOrMethod the field or method to check
255+
* @param type field's or method return value's type
256+
* @return specified exclusive maximum value (or null)
257+
* @see DecimalMax#inclusive()
258+
* @see Negative
259+
*/
260+
private BigDecimal resolveNumberExclusiveMaximum(AccessibleObject fieldOrMethod, JavaType type) {
261+
DecimalMax decimalMaxAnnotation = fieldOrMethod.getAnnotation(DecimalMax.class);
262+
if (decimalMaxAnnotation != null && !decimalMaxAnnotation.inclusive()) {
263+
return new BigDecimal(decimalMaxAnnotation.value());
264+
}
265+
Negative negativeAnnotation = fieldOrMethod.getAnnotation(Negative.class);
266+
if (negativeAnnotation != null) {
267+
return BigDecimal.ZERO;
268+
}
269+
return null;
270+
}
271+
}

0 commit comments

Comments
 (0)