• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

torand / jsonschema2java / 18509998011

14 Oct 2025 09:01PM UTC coverage: 82.516% (+3.7%) from 78.786%
18509998011

push

github

torand
Merge remote-tracking branch 'origin/main'

* origin/main:
  chore(deps): bump org.jacoco:jacoco-maven-plugin from 0.8.13 to 0.8.14

311 of 417 branches covered (74.58%)

Branch coverage included in aggregate %.

850 of 990 relevant lines covered (85.86%)

4.99 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.37
/src/main/java/io/github/torand/jsonschema2java/collectors/TypeInfoCollector.java
1
/*
2
 * Copyright (c) 2024-2025 Tore Eide Andersen
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
package io.github.torand.jsonschema2java.collectors;
17

18
import io.github.torand.jsonschema2java.generators.Options;
19
import io.github.torand.jsonschema2java.model.AnnotationInfo;
20
import io.github.torand.jsonschema2java.model.TypeInfo;
21
import io.github.torand.jsonschema2java.utils.JsonSchemaDef;
22

23
import java.net.URI;
24
import java.util.ArrayList;
25
import java.util.List;
26
import java.util.Optional;
27

28
import static io.github.torand.javacommons.collection.CollectionHelper.isEmpty;
29
import static io.github.torand.javacommons.lang.Exceptions.illegalStateException;
30
import static io.github.torand.javacommons.lang.StringHelper.nonBlank;
31
import static io.github.torand.jsonschema2java.collectors.Extensions.*;
32
import static io.github.torand.jsonschema2java.collectors.TypeInfoCollector.NullabilityResolution.FORCE_NOT_NULLABLE;
33
import static io.github.torand.jsonschema2java.collectors.TypeInfoCollector.NullabilityResolution.FORCE_NULLABLE;
34
import static io.github.torand.jsonschema2java.utils.StringUtils.getClassNameFromFqn;
35
import static io.github.torand.jsonschema2java.utils.StringUtils.joinCsv;
36
import static java.lang.Boolean.TRUE;
37
import static java.util.Objects.nonNull;
38
import static java.util.function.Predicate.not;
39

40
/**
41
 * Collects information about a type from a schema.
42
 */
43
public class TypeInfoCollector extends BaseCollector {
44
    public enum NullabilityResolution {FROM_SCHEMA, FORCE_NULLABLE, FORCE_NOT_NULLABLE}
21✔
45

46
    private final SchemaResolver schemaResolver;
47

48
    public TypeInfoCollector(Options opts, SchemaResolver schemaResolver) {
49
        super(opts);
3✔
50
        this.schemaResolver = schemaResolver;
3✔
51
    }
1✔
52

53
    public TypeInfo getTypeInfo(JsonSchemaDef schema) {
54
        return getTypeInfo(schema, NullabilityResolution.FROM_SCHEMA);
5✔
55
    }
56

57
    public TypeInfo getTypeInfo(JsonSchemaDef schema, NullabilityResolution nullabilityResolution) {
58
        if (!schema.hasTypes()) {
3✔
59

60
            boolean nullable = isNullable(schema, nullabilityResolution);
5✔
61

62
            if (schema.hasAnyOf()) {
3!
63
                throw new IllegalStateException("Schema 'anyOf' not supported");
×
64
            }
65

66
            if (schema.hasOneOf()) {
3✔
67
                // Limited support for 'oneOf' in properties: use the first non-nullable subschema
68
                JsonSchemaDef subSchema = getNonNullableSubSchema(schema.oneOf().toList())
8✔
69
                    .orElseThrow(illegalStateException("Schema 'oneOf' must contain a non-nullable sub-schema"));
4✔
70

71
                return getTypeInfo(subSchema, nullable ? FORCE_NULLABLE : FORCE_NOT_NULLABLE);
8!
72
            }
73

74
            if (schema.allOf().count() == 1) {
6!
75
                // 'allOf' only supported if it contains single type
76
                JsonSchemaDef firstSchema = schema.allOf().findFirst().orElseThrow();
×
77
                return getTypeInfo(firstSchema, nullable ? FORCE_NULLABLE : FORCE_NOT_NULLABLE);
×
78
            }
79

80
            URI ref = schema.ref();
3✔
81
            if (nonNull(ref)) {
3!
82
                TypeInfo typeInfo;
83

84
                if (schemaResolver.isPrimitiveType(ref)) {
5✔
85
                    JsonSchemaDef refSchema = schemaResolver.getOrThrow(ref);
5✔
86
                    typeInfo = getTypeInfo(refSchema, nullable ? FORCE_NULLABLE : FORCE_NOT_NULLABLE);
9✔
87
                } else {
1✔
88
                    typeInfo = new TypeInfo()
4✔
89
                        .withName(SchemaResolver.getTypeName(ref) + opts.pojoNameSuffix())
7✔
90
                        .withNullable(nullable);
2✔
91

92
                    String modelSubpackage = schemaResolver.getModelSubpackage(ref).orElse(null);
8✔
93
                    typeInfo = typeInfo.withAddedNormalImport(opts.getModelPackage(modelSubpackage) + "." + typeInfo.name());
10✔
94

95
                    if (!schemaResolver.isEnumType(schema.ref())) {
6✔
96
                        AnnotationInfo validAnnotation = getValidAnnotation();
3✔
97
                        typeInfo = typeInfo.withAddedAnnotation(validAnnotation);
4✔
98
                    }
99
                    if (!nullable) {
2!
100
                        AnnotationInfo notNullAnnotation = getNotNullAnnotation();
3✔
101
                        typeInfo = typeInfo.withAddedAnnotation(notNullAnnotation);
4✔
102
                    }
103
                }
104

105
                if (nonBlank(schema.description())) {
4✔
106
                    typeInfo = typeInfo.withDescription(schema.description());
5✔
107
                }
108

109
                return typeInfo;
2✔
110
            } else if (schema.hasAllOf()) {
×
111
                throw new IllegalStateException("No types, no $ref: %s".formatted(schema.toString()));
×
112
            }
113
        }
114

115
        return getJsonType(schema, nullabilityResolution);
5✔
116
    }
117

118
    public Optional<JsonSchemaDef> getNonNullableSubSchema(List<JsonSchemaDef> subSchemas) {
119
        return subSchemas.stream()
5✔
120
            .filter(not(this::isNullable))
2✔
121
            .findFirst();
1✔
122
    }
123

124
    private TypeInfo getJsonType(JsonSchemaDef schema, NullabilityResolution nullabilityResolution) {
125
        TypeInfo typeInfo = new TypeInfo()
4✔
126
            .withDescription(schema.description())
3✔
127
            .withPrimitive(true)
4✔
128
            .withNullable(isNullable(schema, nullabilityResolution));
3✔
129

130
        String jsonType = schema.types()
4✔
131
            .filter(not("null"::equals))
2✔
132
            .findFirst()
7✔
133
            .orElseThrow(illegalStateException("Unexpected types: %s", schema.toString()));
6✔
134

135
        if ("string".equals(jsonType)) {
4✔
136
            typeInfo = populateJsonStringType(typeInfo, schema);
6✔
137
        } else if ("number".equals(jsonType)) {
4✔
138
            typeInfo = populateJsonNumberType(typeInfo, schema);
6✔
139
        } else if ("integer".equals(jsonType)) {
4✔
140
            typeInfo = populateJsonIntegerType(typeInfo, schema);
6✔
141
        } else if ("boolean".equals(jsonType)) {
4✔
142
            typeInfo = populateJsonBooleanType(typeInfo);
5✔
143
        } else if ("array".equals(jsonType)) {
4✔
144
            typeInfo = populateJsonArrayType(typeInfo, schema);
6✔
145
        } else if ("object".equals(jsonType) && isEmpty(schema.properties()) && schema.additionalProperties() instanceof JsonSchemaDef) {
12!
146
            typeInfo = populateJsonMapType(typeInfo, schema);
6✔
147
        } else {
148
            // Schema not expected to be defined "inline" using type 'object'
149
            throw new IllegalStateException("Unexpected schema: %s".formatted(schema.toString()));
×
150
        }
151

152
        Optional<String> maybeJsonSerializer = schema.extensions().getString(EXT_JSON_SERIALIZER);
5✔
153
        if (maybeJsonSerializer.isPresent()) {
3✔
154
            AnnotationInfo jsonSerializeAnnotation = getJsonSerializeAnnotation(maybeJsonSerializer.get());
6✔
155
            typeInfo = typeInfo.withAddedAnnotation(jsonSerializeAnnotation);
4✔
156
        }
157

158
        Optional<String> maybeValidationConstraint = schema.extensions().getString(EXT_VALIDATION_CONSTRAINT);
5✔
159
        if (maybeValidationConstraint.isPresent()) {
3✔
160
            AnnotationInfo validationConstraintAnnotation = new AnnotationInfo(
8✔
161
                "@%s".formatted(getClassNameFromFqn(maybeValidationConstraint.get())),
6✔
162
                maybeValidationConstraint.get()
4✔
163
            );
164
            typeInfo = typeInfo.withAddedAnnotation(validationConstraintAnnotation);
4✔
165
        }
166

167
        return typeInfo;
2✔
168
    }
169

170
    private TypeInfo populateJsonStringType(TypeInfo typeInfo, JsonSchemaDef schema) {
171
        if ("uri".equals(schema.format())) {
5✔
172
            typeInfo = typeInfo.withName("URI")
4✔
173
                .withSchemaFormat(schema.format())
3✔
174
                .withAddedNormalImport("java.net.URI");
2✔
175
            if (!typeInfo.nullable() && opts.addJakartaBeanValidationAnnotations()) {
7!
176
                AnnotationInfo notNullAnnotation = getNotNullAnnotation();
3✔
177
                typeInfo = typeInfo.withAddedAnnotation(notNullAnnotation);
4✔
178
            }
1✔
179
        } else if ("uuid".equals(schema.format())) {
5✔
180
            typeInfo = typeInfo.withName("UUID")
4✔
181
                .withSchemaFormat(schema.format())
3✔
182
                .withAddedNormalImport("java.util.UUID");
2✔
183
            if (!typeInfo.nullable() && opts.addJakartaBeanValidationAnnotations()) {
7!
184
                AnnotationInfo notNullAnnotation = getNotNullAnnotation();
3✔
185
                typeInfo = typeInfo.withAddedAnnotation(notNullAnnotation);
4✔
186
            }
1✔
187
        } else if ("duration".equals(schema.format())) {
5✔
188
            typeInfo = typeInfo.withName("Duration")
4✔
189
                .withSchemaFormat(schema.format())
3✔
190
                .withAddedNormalImport("java.time.Duration");
2✔
191
            if (!typeInfo.nullable() && opts.addJakartaBeanValidationAnnotations()) {
7!
192
                AnnotationInfo notNullAnnotation = getNotNullAnnotation();
3✔
193
                typeInfo = typeInfo.withAddedAnnotation(notNullAnnotation);
4✔
194
            }
1✔
195
        } else if ("date".equals(schema.format())) {
5✔
196
            typeInfo = typeInfo.withName("LocalDate")
4✔
197
                .withSchemaFormat(schema.format())
3✔
198
                .withAddedNormalImport("java.time.LocalDate");
2✔
199
            if (!typeInfo.nullable() && opts.addJakartaBeanValidationAnnotations()) {
7!
200
                AnnotationInfo notNullAnnotation = getNotNullAnnotation();
3✔
201
                typeInfo = typeInfo.withAddedAnnotation(notNullAnnotation);
4✔
202
            }
203
            AnnotationInfo jsonFormatAnnotation = getJsonFormatAnnotation("yyyy-MM-dd");
4✔
204
            typeInfo = typeInfo.withAddedAnnotation(jsonFormatAnnotation);
4✔
205
        } else if ("date-time".equals(schema.format())) {
6✔
206
            typeInfo = typeInfo.withName("LocalDateTime")
4✔
207
                .withSchemaFormat(schema.format())
3✔
208
                .withAddedNormalImport("java.time.LocalDateTime");
2✔
209
            if (!typeInfo.nullable() && opts.addJakartaBeanValidationAnnotations()) {
7!
210
                AnnotationInfo notNullAnnotation = getNotNullAnnotation();
3✔
211
                typeInfo = typeInfo.withAddedAnnotation(notNullAnnotation);
4✔
212
            }
213
            AnnotationInfo jsonFormatAnnotation = getJsonFormatAnnotation("yyyy-MM-dd'T'HH:mm:ss");
4✔
214
            typeInfo = typeInfo.withAddedAnnotation(jsonFormatAnnotation);
4✔
215
        } else if ("email".equals(schema.format())) {
6✔
216
            typeInfo = typeInfo.withName("String")
4✔
217
                .withSchemaFormat(schema.format());
3✔
218
            if (opts.addJakartaBeanValidationAnnotations()) {
4!
219
                if (!typeInfo.nullable()) {
3✔
220
                    AnnotationInfo notBlankAnnotation = getNotBlankAnnotation();
3✔
221
                    typeInfo = typeInfo.withAddedAnnotation(notBlankAnnotation);
4✔
222
                }
223
                AnnotationInfo emailAnnotation = getEmailAnnotation();
3✔
224
                typeInfo = typeInfo.withAddedAnnotation(emailAnnotation);
4✔
225
            }
1✔
226
        } else if ("binary".equals(schema.format())) {
5✔
227
            typeInfo = typeInfo.withName("byte[]")
4✔
228
                .withSchemaFormat(schema.format());
3✔
229
            if (opts.addJakartaBeanValidationAnnotations()) {
4!
230
                if (!typeInfo.nullable()) {
3!
231
                    AnnotationInfo notEmptyAnnotation = getNotEmptyAnnotation();
3✔
232
                    typeInfo = typeInfo.withAddedAnnotation(notEmptyAnnotation);
4✔
233
                }
234
                if (nonNull(schema.minItems()) || nonNull(schema.maxItems())) {
8!
235
                    AnnotationInfo sizeAnnotaion = getArraySizeAnnotation(schema);
×
236
                    typeInfo = typeInfo.withAddedAnnotation(sizeAnnotaion);
×
237
                }
×
238
            }
239
        } else {
240
            typeInfo = typeInfo.withName("String")
4✔
241
                .withSchemaFormat(schema.format());
3✔
242
            if (opts.addJakartaBeanValidationAnnotations()) {
4!
243
                if (!typeInfo.nullable()) {
3✔
244
                    AnnotationInfo notBlankAnnotation = getNotBlankAnnotation();
3✔
245
                    typeInfo = typeInfo.withAddedAnnotation(notBlankAnnotation);
4✔
246
                }
247
                if (nonBlank(schema.pattern())) {
4✔
248
                    typeInfo = typeInfo.withSchemaPattern(schema.pattern())
6✔
249
                        .withAddedAnnotation(getPatternAnnotation(schema));
3✔
250
                }
251
                if (nonNull(schema.minLength()) || nonNull(schema.maxLength())) {
8✔
252
                    AnnotationInfo sizeAnnotation = getStringSizeAnnotation(schema);
4✔
253
                    typeInfo = typeInfo.withAddedAnnotation(sizeAnnotation);
4✔
254
                }
255
            }
256
        }
257

258
        return typeInfo;
2✔
259
    }
260

261
    private TypeInfo populateJsonNumberType(TypeInfo typeInfo, JsonSchemaDef schema) {
262
        if ("double".equals(schema.format())) {
5✔
263
            typeInfo = typeInfo.withName("Double");
5✔
264
        } else if ("float".equals(schema.format())) {
5✔
265
            typeInfo = typeInfo.withName("Float");
5✔
266
        } else {
267
            typeInfo = typeInfo.withName("BigDecimal")
4✔
268
                .withAddedNormalImport("java.math.BigDecimal");
2✔
269
        }
270
        typeInfo = typeInfo.withSchemaFormat(schema.format());
5✔
271
        if (opts.addJakartaBeanValidationAnnotations()) {
4!
272
            if (!typeInfo.nullable()) {
3✔
273
                AnnotationInfo notNullAnnotation = getNotNullAnnotation();
3✔
274
                typeInfo = typeInfo.withAddedAnnotation(notNullAnnotation);
4✔
275
            }
276
            if ("BigDecimal".equals(typeInfo.name())) {
5✔
277
                if (nonNull(schema.minimum())) {
4✔
278
                    AnnotationInfo minAnnotation = getMinAnnotation(schema);
4✔
279
                    typeInfo = typeInfo.withAddedAnnotation(minAnnotation);
4✔
280
                }
281
                if (nonNull(schema.maximum())) {
4✔
282
                    AnnotationInfo maxAnnotation = getMaxAnnotation(schema);
4✔
283
                    typeInfo = typeInfo.withAddedAnnotation(maxAnnotation);
4✔
284
                }
285
            }
286
        }
287

288
        return typeInfo;
2✔
289
    }
290

291
    private TypeInfo populateJsonIntegerType(TypeInfo typeInfo, JsonSchemaDef schema) {
292
        typeInfo = typeInfo.withName("int64".equals(schema.format()) ? "Long" :"Integer")
11✔
293
            .withSchemaFormat(schema.format());
3✔
294

295
        if (opts.addJakartaBeanValidationAnnotations()) {
4!
296
            if (!typeInfo.nullable()) {
3✔
297
                AnnotationInfo notNullAnnotation = getNotNullAnnotation();
3✔
298
                typeInfo = typeInfo.withAddedAnnotation(notNullAnnotation);
4✔
299
            }
300
            if (nonNull(schema.minimum())) {
4✔
301
                AnnotationInfo minAnnotation = getMinAnnotation(schema);
4✔
302
                typeInfo = typeInfo.withAddedAnnotation(minAnnotation);
4✔
303
            }
304
            if (nonNull(schema.maximum())) {
4✔
305
                AnnotationInfo maxAnnotation = getMaxAnnotation(schema);
4✔
306
                typeInfo = typeInfo.withAddedAnnotation(maxAnnotation);
4✔
307
            }
308
        }
309

310
        return typeInfo;
2✔
311
    }
312

313
    private TypeInfo populateJsonBooleanType(TypeInfo typeInfo) {
314
        typeInfo = typeInfo.withName("Boolean");
4✔
315
        if (!typeInfo.nullable() && opts.addJakartaBeanValidationAnnotations()) {
7!
316
            AnnotationInfo notNullAnnotation = getNotNullAnnotation();
3✔
317
            typeInfo = typeInfo.withAddedAnnotation(notNullAnnotation);
4✔
318
        }
319

320
        return typeInfo;
2✔
321
    }
322

323
    private TypeInfo populateJsonArrayType(TypeInfo typeInfo, JsonSchemaDef schema) {
324
        typeInfo = typeInfo.withPrimitive(false);
4✔
325
        if (TRUE.equals(schema.uniqueItems())) {
6✔
326
            typeInfo = typeInfo.withName("Set")
4✔
327
                .withAddedNormalImport("java.util.Set");
3✔
328
        } else {
329
            typeInfo = typeInfo.withName("List")
4✔
330
                .withAddedNormalImport("java.util.List");
2✔
331
        }
332

333
        if (opts.addJakartaBeanValidationAnnotations()) {
4!
334
            AnnotationInfo validAnnotation = getValidAnnotation();
3✔
335
            typeInfo = typeInfo.withAddedAnnotation(validAnnotation);
4✔
336
        }
337

338
        TypeInfo itemType = getTypeInfo(schema.items());
5✔
339

340
        if (opts.addJakartaBeanValidationAnnotations()) {
4!
341
            if (!typeInfo.nullable()) {
3✔
342
                AnnotationInfo notNullAnnotation = getNotNullAnnotation();
3✔
343
                typeInfo = typeInfo.withAddedAnnotation(notNullAnnotation);
4✔
344
            }
345
            if (nonNull(schema.minItems()) || nonNull(schema.maxItems())) {
8✔
346
                AnnotationInfo sizeAnnotation = getArraySizeAnnotation(schema);
4✔
347
                typeInfo = typeInfo.withAddedAnnotation(sizeAnnotation);
4✔
348
            }
349
        }
350

351
        return typeInfo.withItemType(itemType);
4✔
352
    }
353

354
    private TypeInfo populateJsonMapType(TypeInfo typeInfo, JsonSchemaDef schema) {
355
        typeInfo = typeInfo.withName("Map")
4✔
356
            .withAddedNormalImport("java.util.Map");
2✔
357

358
        if (opts.addJakartaBeanValidationAnnotations()) {
4!
359
            AnnotationInfo validAnnotation = getValidAnnotation();
3✔
360
            typeInfo = typeInfo.withAddedAnnotation(validAnnotation);
4✔
361
        }
362

363
        TypeInfo keyTypeInfo = new TypeInfo().withName("String");
6✔
364
        if (opts.addJakartaBeanValidationAnnotations()) {
4!
365
            AnnotationInfo notBlankAnnotation = getNotBlankAnnotation();
3✔
366
            keyTypeInfo = keyTypeInfo.withAddedAnnotation(notBlankAnnotation);
4✔
367
        }
368

369
        typeInfo = typeInfo.withKeyType(keyTypeInfo)
5✔
370
            .withItemType(getTypeInfo((JsonSchemaDef)schema.additionalProperties()));
5✔
371

372
        if (opts.addJakartaBeanValidationAnnotations()) {
4!
373
            if (!typeInfo.nullable()) {
3!
374
                AnnotationInfo notNullAnnotation = getNotNullAnnotation();
3✔
375
                typeInfo = typeInfo.withAddedAnnotation(notNullAnnotation);
4✔
376
            }
377
            if (nonNull(schema.minItems()) || nonNull(schema.maxItems())) {
4!
378
                AnnotationInfo sizeAnnotation = getArraySizeAnnotation(schema);
4✔
379
                typeInfo = typeInfo.withAddedAnnotation(sizeAnnotation);
4✔
380
            }
381
        }
382

383
        return typeInfo;
2✔
384
    }
385

386
    private boolean isNullable(JsonSchemaDef schema, NullabilityResolution resolution) {
387
        return switch(resolution) {
4✔
388
            case FROM_SCHEMA -> isNullable(schema);
4✔
389
            case FORCE_NULLABLE -> true;
2✔
390
            case FORCE_NOT_NULLABLE -> false;
1✔
391
        };
392
    }
393

394
    public boolean isNullable(JsonSchemaDef schema) {
395
        if (!schema.hasTypes()) {
3✔
396
            if (schema.hasAllOf()) {
3!
397
                return schema.allOf().allMatch(this::isNullable);
×
398
            } else if (schema.hasOneOf()) {
3✔
399
                return schema.oneOf().anyMatch(this::isNullable);
6✔
400
            } else if (nonNull(schema.ref())) {
4!
401
                return isNullableByExtension(schema);
4✔
402
            } else {
403
                throw new IllegalStateException("No types, no $ref: %s".formatted(schema.toString()));
×
404
            }
405
        }
406

407
        return schema.hasType("null") || isNullableByExtension(schema);
12✔
408
    }
409

410
    private boolean isNullableByExtension(JsonSchemaDef schema) {
411
        return schema.extensions().getBoolean(EXT_NULLABLE).orElse(false);
10✔
412
    }
413

414
    private AnnotationInfo getJsonSerializeAnnotation(String jsonSerializer) {
415
        return new AnnotationInfo("@JsonSerialize(using = %s)".formatted(getJsonSerializerClass(jsonSerializer)))
15✔
416
            .withAddedNormalImport("com.fasterxml.jackson.databind.annotation.JsonSerialize")
2✔
417
            .withAddedNormalImport(jsonSerializer);
1✔
418
    }
419

420
    private AnnotationInfo getJsonFormatAnnotation(String pattern) {
421
        return new AnnotationInfo(
10✔
422
            "@JsonFormat(pattern = \"%s\")".formatted(pattern),
3✔
423
            "com.fasterxml.jackson.annotation.JsonFormat");
424
    }
425

426
    private AnnotationInfo getValidAnnotation() {
427
        return new AnnotationInfo(
6✔
428
            "@Valid",
429
            "jakarta.validation.Valid");
430
    }
431

432
    private AnnotationInfo getNotNullAnnotation() {
433
        return new AnnotationInfo(
6✔
434
            "@NotNull",
435
            "jakarta.validation.constraints.NotNull");
436
    }
437

438
    private AnnotationInfo getNotBlankAnnotation() {
439
        return new AnnotationInfo(
6✔
440
            "@NotBlank",
441
            "jakarta.validation.constraints.NotBlank");
442
    }
443

444
    private AnnotationInfo getNotEmptyAnnotation() {
445
        return new AnnotationInfo(
6✔
446
            "@NotEmpty",
447
            "jakarta.validation.constraints.NotEmpty");
448
    }
449

450
    private AnnotationInfo getMinAnnotation(JsonSchemaDef schema) {
451
        return new AnnotationInfo(
9✔
452
            "@Min(%d)".formatted(schema.minimum().longValue()),
7✔
453
            "jakarta.validation.constraints.Min");
454
    }
455

456
    private AnnotationInfo getMaxAnnotation(JsonSchemaDef schema) {
457
        return new AnnotationInfo(
9✔
458
            "@Max(%d)".formatted(schema.maximum().longValue()),
7✔
459
            "jakarta.validation.constraints.Max");
460
    }
461

462
    private AnnotationInfo getPatternAnnotation(JsonSchemaDef schema) {
463
        return new AnnotationInfo(
9✔
464
            "@Pattern(regexp = \"%s\")".formatted(schema.pattern()),
5✔
465
            "jakarta.validation.constraints.Pattern");
466
    }
467

468
    private AnnotationInfo getEmailAnnotation() {
469
        return new AnnotationInfo(
6✔
470
            "@Email",
471
            "jakarta.validation.constraints.Email");
472
    }
473

474
    private AnnotationInfo getArraySizeAnnotation(JsonSchemaDef schema) {
475
        List<String> sizeParams = new ArrayList<>();
4✔
476
        if (nonNull(schema.minItems())) {
4✔
477
            sizeParams.add("min = %d".formatted(schema.minItems()));
12✔
478
        }
479
        if (nonNull(schema.maxItems())) {
4✔
480
            sizeParams.add("max = %d".formatted(schema.maxItems()));
12✔
481
        }
482
        return new AnnotationInfo(
9✔
483
            "@Size(%s)".formatted(joinCsv(sizeParams)),
5✔
484
            "jakarta.validation.constraints.Size");
485
    }
486

487
    private AnnotationInfo getStringSizeAnnotation(JsonSchemaDef schema) {
488
        List<String> sizeParams = new ArrayList<>();
4✔
489
        if (nonNull(schema.minLength())) {
4✔
490
            sizeParams.add("min = %d".formatted(schema.minLength()));
12✔
491
        }
492
        if (nonNull(schema.maxLength())) {
4✔
493
            sizeParams.add("max = %d".formatted(schema.maxLength()));
12✔
494
        }
495
        return new AnnotationInfo(
9✔
496
            "@Size(%s)".formatted(joinCsv(sizeParams)),
5✔
497
            "jakarta.validation.constraints.Size");
498
    }
499

500
    private String getJsonSerializerClass(String jsonSerializerFqn) {
501
        String className = getClassNameFromFqn(jsonSerializerFqn);
3✔
502
        return formatClassRef(className);
4✔
503
    }
504
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc