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

torand / openapi2java / 15494024830

06 Jun 2025 03:23PM UTC coverage: 82.388%. First build
15494024830

push

github

torand
refactor: replace util package with dep

503 of 716 branches covered (70.25%)

Branch coverage included in aggregate %.

7 of 14 new or added lines in 5 files covered. (50.0%)

1415 of 1612 relevant lines covered (87.78%)

4.77 hits per line

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

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

18
import io.github.torand.openapi2java.generators.Options;
19
import io.github.torand.openapi2java.model.PojoInfo;
20
import io.github.torand.openapi2java.model.PropertyInfo;
21
import io.github.torand.openapi2java.writers.BaseWriter;
22
import io.github.torand.openapi2java.writers.PojoWriter;
23

24
import java.io.Writer;
25
import java.util.List;
26
import java.util.Set;
27
import java.util.TreeSet;
28
import java.util.concurrent.atomic.AtomicInteger;
29
import java.util.function.Predicate;
30

31
import static io.github.torand.javacommons.collection.CollectionHelper.concatStream;
32
import static io.github.torand.javacommons.collection.CollectionHelper.nonEmpty;
33
import static io.github.torand.javacommons.collection.CollectionHelper.streamSafely;
34
import static io.github.torand.openapi2java.utils.KotlinTypeMapper.toKotlinNative;
35
import static java.util.Objects.nonNull;
36
import static java.util.function.Predicate.not;
37
import static java.util.stream.Collectors.joining;
38

39
/**
40
 * Writes Kotlin code for a pojo.
41
 */
42
public class KotlinPojoWriter extends BaseWriter implements PojoWriter {
43

44
    public KotlinPojoWriter(Writer writer, Options opts) {
45
        super(writer, opts);
4✔
46
    }
1✔
47

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

53
        Predicate<String> isModelType = qt -> isModelPackage(qt, pojoInfo.modelSubpackage);
10✔
54

55
        Set<String> imports = new TreeSet<>();
4✔
56
        imports.addAll(pojoInfo.imports);
5✔
57
        pojoInfo.properties.stream()
4✔
58
            .flatMap(p -> p.imports.stream())
7✔
59
            .forEach(imports::add);
4✔
60
        pojoInfo.properties.stream()
4✔
61
            .flatMap(p -> p.type.typeImports())
6✔
62
            .filter(not(isModelType))
4✔
63
            .forEach(imports::add);
4✔
64
        pojoInfo.properties.stream()
4✔
65
            .flatMap(p -> p.type.annotationImports())
6✔
66
            .filter(not(isModelType))
4✔
67
            .forEach(imports::add);
4✔
68

69
        imports.removeIf(i -> i.equals("java.util.List"));
8✔
70
        imports.removeIf(i -> i.equals("java.util.Map"));
8✔
71

72
        if (nonEmpty(imports)) {
3!
73
            imports.forEach(ti -> writeLine("import %s".formatted(ti)));
17✔
74
            writeNewLine();
2✔
75
        }
76

77
        if (pojoInfo.isDeprecated()) {
3!
78
            writeLine("@Deprecated(\"%s\")".formatted(pojoInfo.deprecationMessage));
×
79
        }
80

81
        pojoInfo.annotations.forEach(this::writeLine);
11✔
82
        writeLine("@JvmRecord");
5✔
83

84
        writeLine("data class %s (".formatted(pojoInfo.name));
13✔
85

86
        AtomicInteger propNo = new AtomicInteger(1);
5✔
87
        pojoInfo.properties.forEach(propInfo -> {
7✔
88
            writeNewLine();
2✔
89
            writePropertyAnnotationLines(propInfo);
3✔
90

91
            writeIndent(1);
3✔
92
            write("val %s: ", escapeReservedKeywords(propInfo.name));
11✔
93

94
            String typeName = toKotlinNative(propInfo.type.name);
5✔
95

96
            if (nonNull(propInfo.type.itemType)) {
5✔
97
                String itemTypeWithAnnotations = concatStream(propInfo.type.itemType.annotations, List.of(propInfo.type.itemType.name))
11✔
98
                    .collect(joining(" "));
4✔
99

100
                if (nonNull(propInfo.type.keyType)) {
5!
NEW
101
                    String keyTypeWithAnnotations = concatStream(propInfo.type.keyType.annotations, List.of(propInfo.type.keyType.name))
×
102
                        .collect(joining(" "));
×
103

104
                    write("%s<%s, %s>".formatted(typeName, keyTypeWithAnnotations, itemTypeWithAnnotations));
×
105
                } else {
×
106
                    write("%s<%s>".formatted(typeName, itemTypeWithAnnotations));
16✔
107
                }
108
            } else {
1✔
109
                write("%s".formatted(typeName));
12✔
110
            }
111

112
            if (!propInfo.required || propInfo.type.nullable) {
7!
113
                write("? = null");
5✔
114
            }
115

116
            if (propNo.getAndIncrement() < pojoInfo.properties.size()) {
6✔
117
                writeLine(",");
6✔
118
            } else {
119
                writeNewLine();
2✔
120
            }
121
        });
1✔
122

123
        writeLine(")");
5✔
124
    }
1✔
125

126
    private void writePropertyAnnotationLines(PropertyInfo propInfo) {
127
        if (propInfo.isDeprecated()) {
3✔
128
            writeIndent(1);
3✔
129
            writeLine("@Deprecated(\"%s\")".formatted(propInfo.deprecationMessage));
13✔
130
        }
131
        streamSafely(propInfo.annotations)
5✔
132
            .map(this::prefixPropertyAnnotation)
3✔
133
            .forEach(a -> {
1✔
134
                writeIndent(1);
3✔
135
                writeLine(a);
5✔
136
            });
1✔
137
        streamSafely(propInfo.type.annotations)
6✔
138
            .map(this::prefixPropertyAnnotation)
3✔
139
            .forEach(a -> {
1✔
140
                writeIndent(1);
3✔
141
                writeLine(a);
5✔
142
            });
1✔
143
    }
1✔
144

145
    private String prefixPropertyAnnotation(String annotation) {
146
        if (annotation.startsWith("@JsonProperty")) {
4✔
147
            return annotation;
2✔
148
        }
149

150
        return "@field:"+annotation.substring(1);
5✔
151
    }
152

153
    private boolean isModelPackage(String qualifiedType, String pojoModelSubpackage) {
154
        // Remove class name from qualifiedType value
155
        int lastDotIdx = qualifiedType.lastIndexOf(".");
4✔
156
        String typePackage = qualifiedType.substring(0, lastDotIdx);
5✔
157

158
        return opts.getModelPackage(pojoModelSubpackage).equals(typePackage);
7✔
159
    }
160

161
    private static String escapeReservedKeywords(String name) {
162
        return RESERVED_KEYWORDS.contains(name) ? "`%s`".formatted(name) : name;
6!
163
    }
164

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