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

torand / jsonschema2java / 18399814123

10 Oct 2025 07:35AM UTC coverage: 78.214% (-0.9%) from 79.157%
18399814123

push

github

torand
fix: bean validation annotations on primitive subtypes of compound pojo property types now generated

287 of 413 branches covered (69.49%)

Branch coverage included in aggregate %.

440 of 533 new or added lines in 26 files covered. (82.55%)

1 existing line in 1 file now uncovered.

808 of 987 relevant lines covered (81.86%)

4.75 hits per line

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

92.59
/src/main/java/io/github/torand/jsonschema2java/writers/kotlin/KotlinPojoWriter.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.writers.kotlin;
17

18
import io.github.torand.jsonschema2java.generators.Options;
19
import io.github.torand.jsonschema2java.model.AnnotatedTypeName;
20
import io.github.torand.jsonschema2java.model.AnnotationInfo;
21
import io.github.torand.jsonschema2java.model.PojoInfo;
22
import io.github.torand.jsonschema2java.model.PropertyInfo;
23
import io.github.torand.jsonschema2java.writers.BaseWriter;
24
import io.github.torand.jsonschema2java.writers.PojoWriter;
25

26
import java.io.Writer;
27
import java.util.List;
28
import java.util.Set;
29
import java.util.concurrent.atomic.AtomicInteger;
30

31
import static io.github.torand.javacommons.collection.CollectionHelper.nonEmpty;
32
import static io.github.torand.javacommons.stream.StreamHelper.streamSafely;
33
import static io.github.torand.jsonschema2java.utils.KotlinTypeMapper.toKotlinNative;
34
import static java.util.function.Predicate.not;
35

36
/**
37
 * Writes Kotlin code for a pojo.
38
 */
39
public class KotlinPojoWriter extends BaseWriter implements PojoWriter {
40

41
    public KotlinPojoWriter(Writer writer, Options opts) {
42
        super(writer, opts);
4✔
43
    }
1✔
44

45
    @Override
46
    public void write(PojoInfo pojoInfo) {
47
        writeLine("package %s", opts.getModelPackage(pojoInfo.modelSubpackage()));
13✔
48
        writeNewLine();
2✔
49

50
        writeImports(pojoInfo);
3✔
51

52
        if (pojoInfo.isDeprecated()) {
3!
NEW
53
            writeLine("@Deprecated(\"%s\")".formatted(pojoInfo.deprecationMessage()));
×
54
        }
55

56
        pojoInfo.annotations().forEach(a -> writeLine(a.annotation()));
12✔
57
        writeLine("@JvmRecord");
5✔
58

59
        writeLine("data class %s (".formatted(pojoInfo.name()));
13✔
60

61
        AtomicInteger propNo = new AtomicInteger(1);
5✔
62
        pojoInfo.properties().forEach(propInfo -> {
7✔
63
            writeNewLine();
2✔
64
            writePropertyAnnotationLines(propInfo);
3✔
65
            writePropertyTypeAndName(propInfo);
3✔
66

67
            if (propNo.getAndIncrement() < pojoInfo.properties().size()) {
6✔
68
                writeLine(",");
6✔
69
            } else {
70
                writeNewLine();
2✔
71
            }
72
        });
1✔
73

74
        writeLine(")");
5✔
75
    }
1✔
76

77
    private void writeImports(PojoInfo pojoInfo) {
78
        List<String> imports = pojoInfo.aggregatedNormalImports().stream()
6✔
79
            .filter(ni -> !isInPackage(ni, pojoInfo.modelSubpackage()))
13✔
80
            .filter(not("java.util.List"::equals))
4✔
81
            .filter(not("java.util.Map"::equals))
4✔
82
            .map("import %s"::formatted)
10✔
83
            .toList();
2✔
84

85
        if (nonEmpty(imports)) {
3!
86
            imports.forEach(this::writeLine);
10✔
87
            writeNewLine();
2✔
88
        }
89
    }
1✔
90

91
    private void writePropertyAnnotationLines(PropertyInfo propInfo) {
92
        if (propInfo.isDeprecated()) {
3✔
93
            writeIndent(1);
3✔
94
            writeLine("@Deprecated(\"%s\")".formatted(propInfo.deprecationMessage()));
13✔
95
        }
96
        streamSafely(propInfo.annotations())
4✔
97
            .map(AnnotationInfo::annotation)
3✔
98
            .map(this::prefixPropertyAnnotation)
3✔
99
            .forEach(a -> {
1✔
100
                writeIndent(1);
3✔
101
                writeLine(a);
5✔
102
            });
1✔
103
    }
1✔
104

105
    private void writePropertyTypeAndName(PropertyInfo propInfo) {
106
        AnnotatedTypeName annotatedTypeName = propInfo.type().getAnnotatedFullName();
4✔
107

108
        annotatedTypeName.annotations()
4✔
109
            .map(this::prefixPropertyAnnotation)
3✔
110
            .forEach(a -> {
1✔
111
                writeIndent(1);
3✔
112
                writeLine(a);
5✔
113
            });
1✔
114

115
        writeIndent(1);
3✔
116
        write("val %s: ", escapeReservedKeywords(propInfo.name()));
11✔
117
        write(toKotlinNative(annotatedTypeName.typeName()));
7✔
118

119
        if (!propInfo.required() || propInfo.type().nullable()) {
7✔
120
            write("? = null");
5✔
121
        }
122
    }
1✔
123

124
    private String prefixPropertyAnnotation(String annotation) {
125
        if (annotation.startsWith("@JsonProperty")) {
4!
126
            return annotation;
×
127
        }
128

129
        return "@field:"+annotation.substring(1);
5✔
130
    }
131

132
    private boolean isInPackage(String qualifiedType, String pojoModelSubpackage) {
133
        // Remove class name from qualifiedType value
134
        int lastDotIdx = qualifiedType.lastIndexOf(".");
4✔
135
        String typePackage = qualifiedType.substring(0, lastDotIdx);
5✔
136

137
        return opts.getModelPackage(pojoModelSubpackage).equals(typePackage);
7✔
138
    }
139

140
    private static String escapeReservedKeywords(String name) {
141
        return RESERVED_KEYWORDS.contains(name) ? "`%s`".formatted(name) : name;
6!
142
    }
143

144
    private static final Set<String> RESERVED_KEYWORDS = Set.of(
117✔
145
        "as", "break", "class", "continue", "do", "else", "false", "for", "fun", "if", "in", "interface",
146
        "is", "null", "object", "package", "return", "super", "this", "throw", "true", "try", "typealias",
147
        "typeof", "val", "var", "when", "while"
148
    );
149
}
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