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

torand / openapi2java / 18402175194

10 Oct 2025 09:15AM UTC coverage: 81.86% (-0.04%) from 81.898%
18402175194

push

github

torand
test: add test case for primitive array subtype

541 of 779 branches covered (69.45%)

Branch coverage included in aggregate %.

1616 of 1856 relevant lines covered (87.07%)

5.09 hits per line

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

83.3
/src/main/java/io/github/torand/openapi2java/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.openapi2java.collectors;
17

18
import io.github.torand.openapi2java.generators.Options;
19
import io.github.torand.openapi2java.model.AnnotationInfo;
20
import io.github.torand.openapi2java.model.TypeInfo;
21
import io.swagger.v3.oas.models.media.Schema;
22
import io.swagger.v3.oas.models.media.StringSchema;
23

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.collection.CollectionHelper.nonEmpty;
30
import static io.github.torand.javacommons.lang.Exceptions.illegalStateException;
31
import static io.github.torand.javacommons.lang.StringHelper.nonBlank;
32
import static io.github.torand.javacommons.stream.StreamHelper.streamSafely;
33
import static io.github.torand.openapi2java.collectors.Extensions.*;
34
import static io.github.torand.openapi2java.collectors.TypeInfoCollector.NullabilityResolution.FORCE_NOT_NULLABLE;
35
import static io.github.torand.openapi2java.collectors.TypeInfoCollector.NullabilityResolution.FORCE_NULLABLE;
36
import static io.github.torand.openapi2java.utils.StringUtils.getClassNameFromFqn;
37
import static io.github.torand.openapi2java.utils.StringUtils.joinCsv;
38
import static java.lang.Boolean.TRUE;
39
import static java.util.Objects.nonNull;
40
import static java.util.function.Predicate.not;
41

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

48
    private final SchemaResolver schemaResolver;
49

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

55
    public <T> TypeInfo getTypeInfo(Schema<T> schema) {
56
        return getTypeInfo(schema, NullabilityResolution.FROM_SCHEMA);
5✔
57
    }
58

59
    public TypeInfo getTypeInfo(Schema<?> schema, NullabilityResolution nullabilityResolution) {
60
        if (isEmpty(schema.getTypes())) {
4✔
61

62
            boolean nullable = isNullable(schema, nullabilityResolution);
5✔
63

64
            if (nonNull(schema.getAnyOf())) {
4!
65
                throw new IllegalStateException("Schema 'anyOf' not supported");
×
66
            }
67

68
            if (nonNull(schema.getOneOf())) {
4✔
69
                // Limited support for 'oneOf' in properties: use the first non-nullable subschema
70
                List<Schema<?>> oneOfSchemas = (List<Schema<?>>)(Object)schema.getOneOf();
4✔
71
                Schema<?> subSchema = getNonNullableSubSchema(oneOfSchemas)
6✔
72
                    .orElseThrow(illegalStateException("Schema 'oneOf' must contain a non-nullable sub-schema"));
4✔
73

74
                return getTypeInfo(subSchema, nullable ? FORCE_NULLABLE : FORCE_NOT_NULLABLE);
8!
75
            }
76

77
            if (nonNull(schema.getAllOf()) && schema.getAllOf().size() == 1) {
4!
78
                // 'allOf' only supported if it contains single type
79
                return getTypeInfo(schema.getAllOf().get(0), nullable ? FORCE_NULLABLE : FORCE_NOT_NULLABLE);
×
80
            }
81

82
            String ref = schema.get$ref();
3✔
83
            if (nonBlank(ref)) {
3!
84
                TypeInfo typeInfo;
85

86
                if (schemaResolver.isPrimitiveType(ref)) {
5✔
87
                    Schema<?> refSchema = schemaResolver.getOrThrow(ref);
5✔
88
                    typeInfo = getTypeInfo(refSchema, nullable ? FORCE_NULLABLE : FORCE_NOT_NULLABLE);
9✔
89
                } else {
1✔
90
                    typeInfo = new TypeInfo()
6✔
91
                        .withName(schemaResolver.getTypeName(ref) + opts.pojoNameSuffix())
7✔
92
                        .withNullable(nullable);
2✔
93

94
                    String modelSubpackage = schemaResolver.getModelSubpackage(ref).orElse(null);
8✔
95
                    typeInfo = typeInfo.withAddedNormalImport(opts.getModelPackage(modelSubpackage) + "." + typeInfo.name());
10✔
96
                    if (!schemaResolver.isEnumType(schema.get$ref())) {
6✔
97
                        AnnotationInfo validAnnotation = getValidAnnotation();
3✔
98
                        typeInfo = typeInfo.withAddedAnnotation(validAnnotation);
4✔
99
                    }
100
                    if (!nullable) {
2!
101
                        AnnotationInfo notNullAnnotation = getNotNullAnnotation();
3✔
102
                        typeInfo = typeInfo.withAddedAnnotation(notNullAnnotation);
4✔
103
                    }
104
                }
105

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

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

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

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

125
    private TypeInfo getJsonType(Schema<?> schema, NullabilityResolution nullabilityResolution) {
126
        TypeInfo typeInfo = new TypeInfo()
4✔
127
            .withDescription(schema.getDescription())
3✔
128
            .withPrimitive(true)
4✔
129
            .withNullable(isNullable(schema, nullabilityResolution));
3✔
130

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

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

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

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

168
        return typeInfo;
2✔
169
    }
170

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

259
        return typeInfo;
2✔
260
    }
261

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

289
        return typeInfo;
2✔
290
    }
291

292
    private TypeInfo populateJsonIntegerType(TypeInfo typeInfo, Schema<?> schema) {
293
        typeInfo = typeInfo.withName("int64".equals(schema.getFormat()) ? "Long" :"Integer")
11✔
294
            .withSchemaFormat(schema.getFormat());
3✔
295

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

311
        return typeInfo;
2✔
312
    }
313

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

321
        return typeInfo;
2✔
322
    }
323

324
    private TypeInfo populateJsonArrayType(TypeInfo typeInfo, Schema<?> schema) {
325
        typeInfo = typeInfo.withPrimitive(false);
4✔
326
        if (TRUE.equals(schema.getUniqueItems())) {
5✔
327
            typeInfo = typeInfo.withName("Set")
4✔
328
                .withAddedNormalImport("java.util.Set");
3✔
329
        } else {
330
            typeInfo = typeInfo.withName("List")
4✔
331
                .withAddedNormalImport("java.util.List");
2✔
332
        }
333

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

339
        TypeInfo itemType = getTypeInfo(schema.getItems());
5✔
340

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

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

355
    private TypeInfo populateJsonMapType(TypeInfo typeInfo, Schema<?> schema) {
356
        typeInfo = typeInfo.withName("Map")
×
357
            .withAddedNormalImport("java.util.Map");
×
358

359
        if (opts.addJakartaBeanValidationAnnotations()) {
×
360
            AnnotationInfo validAnnotation = getValidAnnotation();
×
361
            typeInfo = typeInfo.withAddedAnnotation(validAnnotation);
×
362
        }
363

364
        typeInfo = typeInfo.withKeyType(getTypeInfo(new StringSchema()))
×
365
            .withItemType(getTypeInfo((Schema<?>)schema.getAdditionalProperties()));
×
366

367
        if (opts.addJakartaBeanValidationAnnotations()) {
×
368
            if (!typeInfo.nullable()) {
×
369
                AnnotationInfo notNullAnnotation = getNotNullAnnotation();
×
370
                typeInfo = typeInfo.withAddedAnnotation(notNullAnnotation);
×
371
            }
372
            if (nonNull(schema.getMinItems()) || nonNull(schema.getMaxItems())) {
×
373
                AnnotationInfo sizeAnnotation = getArraySizeAnnotation(schema);
×
374
                typeInfo = typeInfo.withAddedAnnotation(sizeAnnotation);
×
375
            }
376
        }
377

378
        return typeInfo;
×
379
    }
380

381
    private boolean isNullable(Schema<?> schema, NullabilityResolution resolution) {
382
        return switch(resolution) {
4✔
383
            case FROM_SCHEMA -> isNullable(schema);
4✔
384
            case FORCE_NULLABLE -> true;
2✔
385
            case FORCE_NOT_NULLABLE -> false;
1✔
386
        };
387
    }
388

389
    public boolean isNullable(Schema<?> schema) {
390
        if (isEmpty(schema.getTypes())) {
4✔
391
            if (nonEmpty(schema.getAllOf())) {
4!
392
                return schema.getAllOf().stream().allMatch(this::isNullable);
×
393
            } else if (nonEmpty(schema.getOneOf())) {
4✔
394
                return schema.getOneOf().stream().anyMatch(this::isNullable);
7✔
395
            } else if (nonBlank(schema.get$ref())) {
4!
396
                return isNullableByExtension(schema);
4✔
397
            } else {
398
                throw new IllegalStateException("No types, no ref: %s".formatted(schema.toString()));
×
399
            }
400
        }
401

402
        // schema.getNullable() populated by OpenAPI 3.0.x only
403
        return schema.getTypes().contains("null") || TRUE.equals(schema.getNullable()) || isNullableByExtension(schema);
18✔
404
    }
405

406
    private boolean isNullableByExtension(Schema<?> schema) {
407
        return extensions(schema.getExtensions()).getBoolean(EXT_NULLABLE).orElse(false);
11✔
408
    }
409

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

416
    private AnnotationInfo getJsonFormatAnnotation(String pattern) {
417
        return new AnnotationInfo(
10✔
418
            "@JsonFormat(pattern = \"%s\")".formatted(pattern),
3✔
419
            "com.fasterxml.jackson.annotation.JsonFormat");
420
    }
421

422
    private AnnotationInfo getValidAnnotation() {
423
        return new AnnotationInfo(
6✔
424
            "@Valid",
425
            "jakarta.validation.Valid");
426
    }
427

428
    private AnnotationInfo getNotNullAnnotation() {
429
        return new AnnotationInfo(
6✔
430
            "@NotNull",
431
            "jakarta.validation.constraints.NotNull");
432
    }
433

434
    private AnnotationInfo getNotBlankAnnotation() {
435
        return new AnnotationInfo(
6✔
436
            "@NotBlank",
437
            "jakarta.validation.constraints.NotBlank");
438
    }
439

440
    private AnnotationInfo getNotEmptyAnnotation() {
441
        return new AnnotationInfo(
6✔
442
            "@NotEmpty",
443
            "jakarta.validation.constraints.NotEmpty");
444
    }
445

446
    private AnnotationInfo getMinAnnotation(Schema<?> schema) {
447
        return new AnnotationInfo(
9✔
448
            "@Min(%d)".formatted(schema.getMinimum().longValue()),
7✔
449
            "jakarta.validation.constraints.Min");
450
    }
451

452
    private AnnotationInfo getMaxAnnotation(Schema<?> schema) {
453
        return new AnnotationInfo(
9✔
454
            "@Max(%d)".formatted(schema.getMaximum().longValue()),
7✔
455
            "jakarta.validation.constraints.Max");
456
    }
457

458
    private AnnotationInfo getPatternAnnotation(Schema<?> schema) {
459
        return new AnnotationInfo(
9✔
460
            "@Pattern(regexp = \"%s\")".formatted(schema.getPattern()),
5✔
461
            "jakarta.validation.constraints.Pattern");
462
    }
463

464
    private AnnotationInfo getEmailAnnotation() {
465
        return new AnnotationInfo(
6✔
466
            "@Email",
467
            "jakarta.validation.constraints.Email");
468
    }
469

470
    private AnnotationInfo getArraySizeAnnotation(Schema<?> schema) {
471
        List<String> sizeParams = new ArrayList<>();
4✔
472
        if (nonNull(schema.getMinItems())) {
4✔
473
            sizeParams.add("min = %d".formatted(schema.getMinItems()));
12✔
474
        }
475
        if (nonNull(schema.getMaxItems())) {
4✔
476
            sizeParams.add("max = %d".formatted(schema.getMaxItems()));
12✔
477
        }
478
        return new AnnotationInfo(
9✔
479
            "@Size(%s)".formatted(joinCsv(sizeParams)),
5✔
480
            "jakarta.validation.constraints.Size");
481
    }
482

483
    private AnnotationInfo getStringSizeAnnotation(Schema<?> schema) {
484
        List<String> sizeParams = new ArrayList<>();
4✔
485
        if (nonNull(schema.getMinLength())) {
4✔
486
            sizeParams.add("min = %d".formatted(schema.getMinLength()));
12✔
487
        }
488
        if (nonNull(schema.getMaxLength())) {
4✔
489
            sizeParams.add("max = %d".formatted(schema.getMaxLength()));
12✔
490
        }
491
        return new AnnotationInfo(
9✔
492
            "@Size(%s)".formatted(joinCsv(sizeParams)),
5✔
493
            "jakarta.validation.constraints.Size");
494
    }
495

496
    private String getJsonSerializerClass(String jsonSerializerFqn) {
497
        String className = getClassNameFromFqn(jsonSerializerFqn);
3✔
498
        return formatClassRef(className);
4✔
499
    }
500
}
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