• 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

89.36
/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.AnnotationInfo;
20
import io.github.torand.openapi2java.model.PojoInfo;
21
import io.github.torand.openapi2java.model.PropertyInfo;
22
import io.github.torand.openapi2java.writers.BaseWriter;
23
import io.github.torand.openapi2java.writers.PojoWriter;
24

25
import java.io.Writer;
26
import java.util.List;
27
import java.util.Set;
28
import java.util.concurrent.atomic.AtomicInteger;
29
import java.util.stream.Stream;
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.openapi2java.utils.KotlinTypeMapper.toKotlinNative;
34
import static java.util.Objects.nonNull;
35
import static java.util.function.Predicate.not;
36
import static java.util.stream.Collectors.joining;
37

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

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

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

52
        writeImports(pojoInfo);
3✔
53

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

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

61
        writeLine("data class %s (".formatted(pojoInfo.name()));
13✔
62

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

68
            writeIndent(1);
3✔
69
            write("val %s: ", escapeReservedKeywords(propInfo.name()));
11✔
70

71
            String typeName = toKotlinNative(propInfo.type().name());
5✔
72

73
            if (nonNull(propInfo.type().itemType())) {
5✔
74
                String itemTypeWithAnnotations = Stream.concat(streamSafely(propInfo.type().itemType().annotations()).map(AnnotationInfo::annotation), Stream.of(propInfo.type().itemType().name()))
14✔
75
                    .collect(joining(" "));
4✔
76

77
                if (nonNull(propInfo.type().keyType())) {
5!
NEW
78
                    String keyTypeWithAnnotations = Stream.concat(streamSafely(propInfo.type().keyType().annotations()).map(AnnotationInfo::annotation), Stream.of(propInfo.type().keyType().name()))
×
UNCOV
79
                        .collect(joining(" "));
×
80

81
                    write("%s<%s, %s>".formatted(typeName, keyTypeWithAnnotations, itemTypeWithAnnotations));
×
82
                } else {
×
83
                    write("%s<%s>".formatted(typeName, itemTypeWithAnnotations));
16✔
84
                }
85
            } else {
1✔
86
                write("%s".formatted(typeName));
12✔
87
            }
88

89
            if (!propInfo.required() || propInfo.type().nullable()) {
7!
90
                write("? = null");
5✔
91
            }
92

93
            if (propNo.getAndIncrement() < pojoInfo.properties().size()) {
6✔
94
                writeLine(",");
6✔
95
            } else {
96
                writeNewLine();
2✔
97
            }
98
        });
1✔
99

100
        writeLine(")");
5✔
101
    }
1✔
102

103
    private void writeImports(PojoInfo pojoInfo) {
104
        List<String> imports = pojoInfo.aggregatedNormalImports().stream()
6✔
105
            .filter(ni -> !isInPackage(ni, pojoInfo.modelSubpackage()))
13✔
106
            .filter(not("java.util.List"::equals))
4✔
107
            .filter(not("java.util.Map"::equals))
4✔
108
            .map("import %s"::formatted)
10✔
109
            .toList();
2✔
110

111
        if (nonEmpty(imports)) {
3!
112
            imports.forEach(this::writeLine);
10✔
113
            writeNewLine();
2✔
114
        }
115
    }
1✔
116

117
    private void writePropertyAnnotationLines(PropertyInfo propInfo) {
118
        if (propInfo.isDeprecated()) {
3✔
119
            writeIndent(1);
3✔
120
            writeLine("@Deprecated(\"%s\")".formatted(propInfo.deprecationMessage()));
13✔
121
        }
122
        streamSafely(propInfo.annotations())
4✔
123
            .map(AnnotationInfo::annotation)
3✔
124
            .map(this::prefixPropertyAnnotation)
3✔
125
            .forEach(a -> {
1✔
126
                writeIndent(1);
3✔
127
                writeLine(a);
5✔
128
            });
1✔
129
        streamSafely(propInfo.type().annotations())
5✔
130
            .map(AnnotationInfo::annotation)
3✔
131
            .map(this::prefixPropertyAnnotation)
3✔
132
            .forEach(a -> {
1✔
133
                writeIndent(1);
3✔
134
                writeLine(a);
5✔
135
            });
1✔
136
    }
1✔
137

138
    private String prefixPropertyAnnotation(String annotation) {
139
        if (annotation.startsWith("@JsonProperty")) {
4✔
140
            return annotation;
2✔
141
        }
142

143
        return "@field:"+annotation.substring(1);
5✔
144
    }
145

146
    private boolean isInPackage(String qualifiedType, String pojoModelSubpackage) {
147
        // Remove class name from qualifiedType value
148
        int lastDotIdx = qualifiedType.lastIndexOf(".");
4✔
149
        String typePackage = qualifiedType.substring(0, lastDotIdx);
5✔
150

151
        return opts.getModelPackage(pojoModelSubpackage).equals(typePackage);
7✔
152
    }
153

154
    private static String escapeReservedKeywords(String name) {
155
        return RESERVED_KEYWORDS.contains(name) ? "`%s`".formatted(name) : name;
6!
156
    }
157

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