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

torand / openapi2java / 18202480060

02 Oct 2025 06:44PM UTC coverage: 81.561% (-0.8%) from 82.388%
18202480060

push

github

web-flow
Merge pull request #48 from torand/refactor-info-classes

Refactor info classes

507 of 736 branches covered (68.89%)

Branch coverage included in aggregate %.

825 of 934 new or added lines in 38 files covered. (88.33%)

11 existing lines in 7 files now uncovered.

1541 of 1775 relevant lines covered (86.82%)

5.09 hits per line

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

83.41
/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.joinCsv;
37
import static java.lang.Boolean.TRUE;
38
import static java.util.Objects.nonNull;
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(SchemaResolver schemaResolver, Options opts) {
49
        super(opts);
3✔
50
        this.schemaResolver = schemaResolver;
3✔
51
    }
1✔
52

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

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

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

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

66
            if (nonNull(schema.getOneOf())) {
4✔
67
                // Limited support for 'oneOf' in properties: use the first non-nullable subschema
68
                Schema<?> subSchema = getNonNullableSubSchema(schema.getOneOf())
7✔
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 (nonNull(schema.getAllOf()) && schema.getAllOf().size() == 1) {
4!
75
                // 'allOf' only supported if it contains single type
76
                return getTypeInfo(schema.getAllOf().get(0), nullable ? FORCE_NULLABLE : FORCE_NOT_NULLABLE);
×
77
            }
78

79
            String $ref = schema.get$ref();
3✔
80
            if (nonBlank($ref)) {
3!
81
                TypeInfo typeInfo;
82

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

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

103
                if (nonBlank(schema.getDescription())) {
4✔
104
                    typeInfo = typeInfo.withDescription(schema.getDescription());
5✔
105
                }
106

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

113
        return getJsonType(schema, nullabilityResolution);
5✔
114
    }
115

116
    public Optional<Schema> getNonNullableSubSchema(List<Schema> subSchemas) {
117
        return subSchemas.stream()
5✔
118
            .filter(subSchema -> !isNullable(subSchema))
9✔
119
            .findFirst();
1✔
120
    }
121

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

128
        String jsonType = streamSafely(schema.getTypes())
4✔
129
            .filter(t -> !"null".equals(t))
8!
130
            .findFirst()
7✔
131
            .orElseThrow(illegalStateException("Unexpected types: %s", schema.toString()));
6✔
132

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

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

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

165
        return typeInfo;
2✔
166
    }
167

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

256
        return typeInfo;
2✔
257
    }
258

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

286
        return typeInfo;
2✔
287
    }
288

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

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

308
        return typeInfo;
2✔
309
    }
310

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

318
        return typeInfo;
2✔
319
    }
320

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

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

336
        TypeInfo itemType = getTypeInfo(schema.getItems()).withNoAnnotations();
6✔
337

338
        if (opts.addJakartaBeanValidationAnnotations()) {
4!
339
            AnnotationInfo itemNotNullAnnotation = getNotNullAnnotation();
3✔
340
            itemType = itemType.withAddedAnnotation(itemNotNullAnnotation);
4✔
341

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) {
NEW
356
        typeInfo = typeInfo.withName("Map")
×
NEW
357
            .withAddedNormalImport("java.util.Map");
×
358

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

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

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

NEW
378
        return typeInfo;
×
379
    }
380

381
    private String getClassNameFromFqn(String fqn) {
382
        int lastDot = fqn.lastIndexOf(".");
4✔
383
        if (lastDot == -1) {
3!
384
            throw new IllegalStateException("Unexpected fully qualified class name: %s".formatted(fqn));
×
385
        }
386
        return fqn.substring(lastDot+1);
6✔
387
    }
388

389
    private boolean isNullable(Schema<?> schema, NullabilityResolution resolution) {
390
        return switch(resolution) {
4✔
391
            case FROM_SCHEMA -> isNullable(schema);
4✔
392
            case FORCE_NULLABLE -> true;
2✔
393
            case FORCE_NOT_NULLABLE -> false;
1✔
394
        };
395
    }
396

397
    public boolean isNullable(Schema<?> schema) {
398
        if (isEmpty(schema.getTypes())) {
4✔
399
            if (nonEmpty(schema.getAllOf())) {
4!
400
                return schema.getAllOf().stream().allMatch(this::isNullable);
×
401
            } else if (nonEmpty(schema.getOneOf())) {
4✔
402
                return schema.getOneOf().stream().anyMatch(this::isNullable);
7✔
403
            } else if (nonBlank(schema.get$ref())) {
4!
404
                return isNullableByExtension(schema);
4✔
405
            } else {
406
                throw new IllegalStateException("No types, no $ref: %s".formatted(schema.toString()));
×
407
            }
408
        }
409

410
        // schema.getNullable() populated by OpenAPI 3.0.x only
411
        return schema.getTypes().contains("null") || TRUE.equals(schema.getNullable()) || isNullableByExtension(schema);
18✔
412
    }
413

414
    private boolean isNullableByExtension(Schema<?> schema) {
415
        return extensions(schema.getExtensions()).getBoolean(EXT_NULLABLE).orElse(false);
11✔
416
    }
417

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

424
    private AnnotationInfo getJsonFormatAnnotation(String pattern) {
425
        return new AnnotationInfo(
10✔
426
            "@JsonFormat(pattern = \"%s\")".formatted(pattern),
3✔
427
            "com.fasterxml.jackson.annotation.JsonFormat");
428
    }
429

430
    private AnnotationInfo getValidAnnotation() {
431
        return new AnnotationInfo(
6✔
432
            "@Valid",
433
            "jakarta.validation.Valid");
434
    }
435

436
    private AnnotationInfo getNotNullAnnotation() {
437
        return new AnnotationInfo(
6✔
438
            "@NotNull",
439
            "jakarta.validation.constraints.NotNull");
440
    }
441

442
    private AnnotationInfo getNotBlankAnnotation() {
443
        return new AnnotationInfo(
6✔
444
            "@NotBlank",
445
            "jakarta.validation.constraints.NotBlank");
446
    }
447

448
    private AnnotationInfo getNotEmptyAnnotation() {
449
        return new AnnotationInfo(
6✔
450
            "@NotEmpty",
451
            "jakarta.validation.constraints.NotEmpty");
452
    }
453

454
    private AnnotationInfo getMinAnnotation(Schema<?> schema) {
455
        return new AnnotationInfo(
9✔
456
            "@Min(%d)".formatted(schema.getMinimum().longValue()),
7✔
457
            "jakarta.validation.constraints.Min");
458
    }
459

460
    private AnnotationInfo getMaxAnnotation(Schema<?> schema) {
461
        return new AnnotationInfo(
9✔
462
            "@Max(%d)".formatted(schema.getMaximum().longValue()),
7✔
463
            "jakarta.validation.constraints.Max");
464
    }
465

466
    private AnnotationInfo getPatternAnnotation(Schema<?> schema) {
467
        return new AnnotationInfo(
9✔
468
            "@Pattern(regexp = \"%s\")".formatted(schema.getPattern()),
5✔
469
            "jakarta.validation.constraints.Pattern");
470
    }
471

472
    private AnnotationInfo getEmailAnnotation() {
473
        return new AnnotationInfo(
6✔
474
            "@Email",
475
            "jakarta.validation.constraints.Email");
476
    }
477

478
    private AnnotationInfo getArraySizeAnnotation(Schema<?> schema) {
479
        List<String> sizeParams = new ArrayList<>();
4✔
480
        if (nonNull(schema.getMinItems())) {
4✔
481
            sizeParams.add("min = %d".formatted(schema.getMinItems()));
12✔
482
        }
483
        if (nonNull(schema.getMaxItems())) {
4✔
484
            sizeParams.add("max = %d".formatted(schema.getMaxItems()));
12✔
485
        }
486
        return new AnnotationInfo(
9✔
487
            "@Size(%s)".formatted(joinCsv(sizeParams)),
5✔
488
            "jakarta.validation.constraints.Size");
489
    }
490

491
    private AnnotationInfo getStringSizeAnnotation(Schema<?> schema) {
492
        List<String> sizeParams = new ArrayList<>();
4✔
493
        if (nonNull(schema.getMinLength())) {
4✔
494
            sizeParams.add("min = %d".formatted(schema.getMinLength()));
12✔
495
        }
496
        if (nonNull(schema.getMaxLength())) {
4✔
497
            sizeParams.add("max = %d".formatted(schema.getMaxLength()));
12✔
498
        }
499
        return new AnnotationInfo(
9✔
500
            "@Size(%s)".formatted(joinCsv(sizeParams)),
5✔
501
            "jakarta.validation.constraints.Size");
502
    }
503

504
    private String getJsonSerializerClass(String jsonSerializerFqn) {
505
        String className = getClassNameFromFqn(jsonSerializerFqn);
4✔
506
        return opts.useKotlinSyntax() ? className+"::class" : className+".class";
10✔
507
    }
508
}
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