• 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

70.16
/src/main/java/io/github/torand/openapi2java/writers/java/JavaPojoWriter.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.java;
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.concurrent.atomic.AtomicInteger;
28
import java.util.stream.Stream;
29

30
import static io.github.torand.javacommons.collection.CollectionHelper.nonEmpty;
31
import static io.github.torand.javacommons.stream.StreamHelper.streamSafely;
32
import static java.util.Objects.nonNull;
33
import static java.util.function.Predicate.not;
34
import static java.util.stream.Collectors.joining;
35

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

41
    public JavaPojoWriter(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
        writeNonJavaImports(pojoInfo);
3✔
51
        writeJavaImports(pojoInfo);
3✔
52

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

58
        pojoInfo.annotations().forEach(a -> writeLine(a.annotation()));
12✔
59

60
        if (opts.pojosAsRecords()) {
4!
61
            writeLine("public record %s (".formatted(pojoInfo.name()));
14✔
62
        } else {
NEW
63
            writeLine("public class %s {".formatted(pojoInfo.name()));
×
64
        }
65

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

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

76
                if (!opts.pojosAsRecords()) {
4!
77
                    write("public ");
×
78
                }
79

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

NEW
84
                    write("%s<%s, %s> %s".formatted(propInfo.type().name(), keyTypeWithAnnotations, itemTypeWithAnnotations, propInfo.name()));
×
85
                } else {
×
86
                    write("%s<%s> %s".formatted(propInfo.type().name(), itemTypeWithAnnotations, propInfo.name()));
23✔
87
                }
88
            } else {
1✔
89
                if (opts.pojosAsRecords()) {
4!
90
                    write("%s %s".formatted(propInfo.type().name(), propInfo.name()));
20✔
91
                } else {
NEW
92
                    write("public %s %s".formatted(propInfo.type().name(), propInfo.name()));
×
93
                }
94
            }
95

96
            if (opts.pojosAsRecords()) {
4!
97
                if (propNo.getAndIncrement() < pojoInfo.properties().size()) {
6✔
98
                    writeLine(",");
6✔
99
                } else {
100
                    writeNewLine();
3✔
101
                }
102
            } else {
103
                writeLine(";");
×
104
            }
105
        });
1✔
106

107
        if (opts.pojosAsRecords()) {
4!
108
            writeLine(") {");
5✔
109
            writeNewLine();
2✔
110
            writeLine("}");
6✔
111
        } else {
112
            writeNewLine();
×
NEW
113
            writeNoArgConstructor(pojoInfo.name());
×
114
            writeNewLine();
×
NEW
115
            writeParameterizedConstructor(pojoInfo.name(), pojoInfo.properties());
×
116
            writeLine("}");
×
117
        }
118
    }
1✔
119

120
    private void writeJavaImports(PojoInfo pojoInfo) {
121
        List<String> imports = pojoInfo.aggregatedNormalImports().stream()
5✔
122
            .filter(this::isJavaPackage)
3✔
123
            .map("import %s;"::formatted)
10✔
124
            .toList();
2✔
125

126
        if (nonEmpty(imports)) {
3✔
127
            imports.forEach(this::writeLine);
10✔
128
            writeNewLine();
2✔
129
        }
130
    }
1✔
131

132
    private void writeNonJavaImports(PojoInfo pojoInfo) {
133
        List<String> imports = pojoInfo.aggregatedNormalImports().stream()
5✔
134
            .filter(not(this::isJavaPackage))
5✔
135
            .filter(ni -> !isInPackage(ni, pojoInfo.modelSubpackage()))
13✔
136
            .map("import %s;"::formatted)
10✔
137
            .toList();
2✔
138

139
        if (nonEmpty(imports)) {
3!
140
            imports.forEach(this::writeLine);
10✔
141
            writeNewLine();
2✔
142
        }
143
    }
1✔
144

145
    private void writeNoArgConstructor(String name) {
146
        writeIndent(1);
×
147
        writeLine("public %s() {", name);
×
148
        writeIndent(1);
×
149
        writeLine("}");
×
150
    }
×
151

152
    private void writeParameterizedConstructor(String name, List<PropertyInfo> props) {
153
        writeIndent(1);
×
NEW
154
        writeLine("public %s(%s) {", name, props.stream().map(p -> p.type().getFullName() + " " + p.name()).collect(joining(", ")));
×
155
        props.forEach(p -> {
×
156
            writeIndent(2);
×
NEW
157
            writeLine("this.%s = %s;", p.name(), p.name());
×
158
        });
×
159
        writeIndent(1);
×
160
        writeLine("}");
×
161
    }
×
162

163
    private void writePropertyAnnotationLines(PropertyInfo propInfo) {
164
        if (propInfo.isDeprecated()) {
3✔
165
            writeIndent(1);
3✔
166
            writeLine("/// @deprecated %s".formatted(propInfo.deprecationMessage()));
13✔
167
            writeIndent(1);
3✔
168
            writeLine("@Deprecated");
5✔
169
        }
170
        propInfo.annotations().forEach(a -> {
5✔
171
            writeIndent(1);
3✔
172
            writeLine(a.annotation());
6✔
173
        });
1✔
174
        propInfo.type().annotations().forEach(a -> {
6✔
175
            writeIndent(1);
3✔
176
            writeLine(a.annotation());
6✔
177
        });
1✔
178
    }
1✔
179

180
    private boolean isInPackage(String qualifiedType, String pojoModelSubpackage) {
181
        // Remove class name from qualifiedType value
182
        int lastDotIdx = qualifiedType.lastIndexOf(".");
4✔
183
        String typePackage = qualifiedType.substring(0, lastDotIdx);
5✔
184

185
        return opts.getModelPackage(pojoModelSubpackage).equals(typePackage);
7✔
186
    }
187

188
    private boolean isJavaPackage(String qualifiedType) {
189
        return qualifiedType.startsWith("java.");
4✔
190
    }
191
}
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