@redocly/cli 2.71.2 → 2.72.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (159) hide show
  1. package/README.md +1 -1
  2. package/THIRD_PARTY_NOTICES +29 -7
  3. package/lib/chunks/{EHJ3FMW6.js → 2AW4F6ZZ.js} +1 -1
  4. package/lib/chunks/{3EQNI6CT.js → 2MF3MKAD.js} +1 -1
  5. package/lib/chunks/{UYZXI3NO.js → 2X2KHSF3.js} +1 -1
  6. package/lib/chunks/{YF4FMOTE.js → 3FA5W77S.js} +1 -1
  7. package/lib/chunks/{VPTYPV6P.js → 3FVHXIZN.js} +1 -1
  8. package/lib/chunks/{P5T6BIKW.js → 3N53VPSU.js} +1 -1
  9. package/lib/chunks/{KLGUZBJO.js → 3N73A66H.js} +1 -1
  10. package/lib/chunks/{JO76DPXR.js → 3WOXO2JS.js} +1 -1
  11. package/lib/chunks/{ORWIUECT.js → 4A5R4H5W.js} +1 -1
  12. package/lib/chunks/{NXREEI2S.js → 4LTPQZBP.js} +1 -1
  13. package/lib/chunks/{L325HIGK.js → 4QC7OVHI.js} +1 -1
  14. package/lib/chunks/{FDKY4PGX.js → 542GVIYM.js} +1 -1
  15. package/lib/chunks/5JF6EUWW.js +9 -0
  16. package/lib/chunks/{6X42UQ3O.js → 5MZU5MHZ.js} +1 -1
  17. package/lib/chunks/{DZT5NLUB.js → 5UPBGRBZ.js} +1 -1
  18. package/lib/chunks/{WO6IIVA3.js → 6LZX65VY.js} +8 -8
  19. package/lib/chunks/{NEWT5A6S.js → 6RTITFQA.js} +1 -1
  20. package/lib/chunks/{IQLQH23V.js → 6Y6B2SKE.js} +1 -1
  21. package/lib/chunks/765BYRQR.js +7 -0
  22. package/lib/chunks/{2TG3DP4I.js → 7NZS5HXU.js} +1 -1
  23. package/lib/chunks/{5CR6AOLY.js → A65OW7M7.js} +3 -3
  24. package/lib/chunks/ARUYNQHA.js +7 -0
  25. package/lib/chunks/BJ6Z66KZ.js +7 -0
  26. package/lib/chunks/{T77BOGYV.js → BWGBTYNU.js} +1 -1
  27. package/lib/chunks/{DM4EUYBB.js → BZTZIEWS.js} +2 -2
  28. package/lib/chunks/{4S6LRPCX.js → CBFNHKDJ.js} +1 -1
  29. package/lib/chunks/{OFIQ2EST.js → CYCPHJNX.js} +33 -33
  30. package/lib/chunks/DSBDKSRR.js +53 -0
  31. package/lib/chunks/E2CEKR6N.js +22 -0
  32. package/lib/chunks/{HRZLSSOF.js → EMWHLZB6.js} +1 -1
  33. package/lib/chunks/{DWXDCKZ7.js → ENIFTFYK.js} +1 -1
  34. package/lib/chunks/{BIS2ZFGS.js → ETJMQLWJ.js} +1 -1
  35. package/lib/chunks/{ADACRYTN.js → EU5WR2O3.js} +1 -1
  36. package/lib/chunks/{FAFMGLT6.js → FLQQ3VRQ.js} +1 -1
  37. package/lib/chunks/{BPJTNQBC.js → FZZACVYL.js} +1 -1
  38. package/lib/chunks/{N4GCIKY4.js → G7A4OE2M.js} +1 -1
  39. package/lib/chunks/{HP7UD4BA.js → GCAL2PYM.js} +1 -1
  40. package/lib/chunks/{MJ2NI6MR.js → GGHLD27S.js} +1 -1
  41. package/lib/chunks/{U4MI7HDM.js → GMPHU6KM.js} +1 -1
  42. package/lib/chunks/{HTFKP5HH.js → GWBLS542.js} +1 -1
  43. package/lib/chunks/{U2F3JOT2.js → GXILNSWK.js} +1 -1
  44. package/lib/chunks/{5AVTCY6F.js → HM7M6465.js} +1 -1
  45. package/lib/chunks/{5HPO77VR.js → I2JZXLJ6.js} +2 -2
  46. package/lib/chunks/I3YF3GEY.js +13 -0
  47. package/lib/chunks/{YWRFPTTI.js → IIQW437Z.js} +1 -1
  48. package/lib/chunks/{4H3DPQDW.js → IM4YIUUN.js} +1 -1
  49. package/lib/chunks/{LET2MPMU.js → JDMRVD45.js} +1 -1
  50. package/lib/chunks/{XVS7B4RZ.js → KIFXQTTL.js} +1 -1
  51. package/lib/chunks/{UVKUPD4U.js → L4VSY42B.js} +1 -1
  52. package/lib/chunks/LH3ONQEX.js +17 -0
  53. package/lib/chunks/{SSEMMWPY.js → LUNR7GV4.js} +1 -1
  54. package/lib/chunks/{LY6BDOEF.js → LY5MGNMA.js} +1 -1
  55. package/lib/chunks/{XQEXWC5L.js → MAACTVYP.js} +1 -1
  56. package/lib/chunks/{GFSMOHUE.js → MTKICX4T.js} +1 -1
  57. package/lib/chunks/{ZXOX7NF2.js → NMAWU5S4.js} +1 -1
  58. package/lib/chunks/{I4TK6NG7.js → PJHQKG7X.js} +1 -1
  59. package/lib/chunks/PTBPNZRZ.js +7 -0
  60. package/lib/chunks/{CZX7WE2R.js → PUZ5QSL3.js} +1 -1
  61. package/lib/chunks/{JRVXIEVC.js → PW2XFZOB.js} +1 -1
  62. package/lib/chunks/{TH44RKLO.js → QAT3DPT3.js} +1 -1
  63. package/lib/chunks/{QXXAC25O.js → R67IVFZH.js} +1 -1
  64. package/lib/chunks/RA4FAJM3.js +59 -0
  65. package/lib/chunks/{AZ5LIJEO.js → RF5LL2UZ.js} +1 -1
  66. package/lib/chunks/{JOUEVI3E.js → RQPH22PV.js} +1 -1
  67. package/lib/chunks/S3B562IT.js +7 -0
  68. package/lib/chunks/{3FR3456Q.js → S43EG4DB.js} +1 -1
  69. package/lib/chunks/{CER2D6ZD.js → S7VIXBKD.js} +7 -7
  70. package/lib/chunks/{VJZDR6KS.js → TFPMT2XD.js} +1529 -4
  71. package/lib/chunks/{AXGFQS75.js → U56BQ2BR.js} +2 -2
  72. package/lib/chunks/{KJNBOXDI.js → UOTHMCTM.js} +3 -3
  73. package/lib/chunks/{CR5TYFBN.js → VHZJNDWE.js} +2 -2
  74. package/lib/chunks/{BOTTA3HA.js → VOYPXV3Z.js} +1 -1
  75. package/lib/chunks/{UTEOWLD5.js → VS4MTPCM.js} +1 -1
  76. package/lib/chunks/{QTSECOQB.js → WEPGHS5R.js} +2 -2
  77. package/lib/chunks/{LKNL5LEV.js → WRKMMY3F.js} +1 -1
  78. package/lib/chunks/{7JS7JGWP.js → WTJ3S4D7.js} +1 -1
  79. package/lib/chunks/X22IMAJK.js +199 -0
  80. package/lib/chunks/{W7TN2XCW.js → X3FFOCXC.js} +1 -1
  81. package/lib/chunks/{CRJEY7P3.js → XTI7EXWX.js} +1 -1
  82. package/lib/chunks/{BH4FA7AH.js → YA5KTHFN.js} +1 -1
  83. package/lib/chunks/{Z6GFQV2Y.js → YA6D5EUI.js} +1 -1
  84. package/lib/chunks/{HMYFTLS7.js → ZFET6WME.js} +1 -1
  85. package/lib/chunks/{R32HBYOH.js → ZXB5O6JQ.js} +1 -1
  86. package/lib/eject-assets/AGENTS.md +1 -1
  87. package/lib/eject-assets/generators/cli/docs.ts +1 -1
  88. package/lib/eject-assets/generators/cli/engine-source.ts +1 -1
  89. package/lib/eject-assets/generators/cli/index.ts +2 -2
  90. package/lib/eject-assets/generators/cli/render.ts +1 -1
  91. package/lib/eject-assets/generators/go/client.ts +1 -1
  92. package/lib/eject-assets/generators/go/descriptor.ts +1 -1
  93. package/lib/eject-assets/generators/go/index.ts +2 -2
  94. package/lib/eject-assets/generators/go/models.ts +1 -1
  95. package/lib/eject-assets/generators/go/naming.ts +1 -1
  96. package/lib/eject-assets/generators/go/operations.ts +1 -1
  97. package/lib/eject-assets/generators/go/pagination.ts +1 -1
  98. package/lib/eject-assets/generators/go/types.ts +1 -1
  99. package/lib/eject-assets/generators/java/client.ts +66 -0
  100. package/lib/eject-assets/generators/java/descriptor.ts +72 -0
  101. package/lib/eject-assets/generators/java/index.ts +245 -0
  102. package/lib/eject-assets/generators/java/models.ts +362 -0
  103. package/lib/eject-assets/generators/java/naming.ts +135 -0
  104. package/lib/eject-assets/generators/java/operations.ts +300 -0
  105. package/lib/eject-assets/generators/java/pagination.ts +94 -0
  106. package/lib/eject-assets/generators/java/types.ts +103 -0
  107. package/lib/eject-assets/generators/mock/faker.ts +1 -1
  108. package/lib/eject-assets/generators/mock/index.ts +2 -2
  109. package/lib/eject-assets/generators/mock/render.ts +1 -1
  110. package/lib/eject-assets/generators/mock/sample.ts +1 -1
  111. package/lib/eject-assets/generators/mock/values.ts +1 -1
  112. package/lib/eject-assets/generators/php/client.ts +1 -1
  113. package/lib/eject-assets/generators/php/descriptor.ts +1 -1
  114. package/lib/eject-assets/generators/php/index.ts +2 -2
  115. package/lib/eject-assets/generators/php/models.ts +1 -1
  116. package/lib/eject-assets/generators/php/naming.ts +1 -1
  117. package/lib/eject-assets/generators/php/operations.ts +1 -1
  118. package/lib/eject-assets/generators/php/pagination.ts +1 -1
  119. package/lib/eject-assets/generators/php/types.ts +1 -1
  120. package/lib/eject-assets/generators/python/client.ts +1 -1
  121. package/lib/eject-assets/generators/python/descriptor.ts +1 -1
  122. package/lib/eject-assets/generators/python/index.ts +2 -2
  123. package/lib/eject-assets/generators/python/models.ts +1 -1
  124. package/lib/eject-assets/generators/python/naming.ts +1 -1
  125. package/lib/eject-assets/generators/python/operations.ts +1 -1
  126. package/lib/eject-assets/generators/python/pagination.ts +1 -1
  127. package/lib/eject-assets/generators/python/types.ts +1 -1
  128. package/lib/eject-assets/generators/swr/index.ts +2 -2
  129. package/lib/eject-assets/generators/swr/render.ts +1 -1
  130. package/lib/eject-assets/generators/tanstack-query/index.ts +2 -2
  131. package/lib/eject-assets/generators/tanstack-query/render.ts +1 -1
  132. package/lib/eject-assets/generators/transformers/index.ts +2 -2
  133. package/lib/eject-assets/generators/transformers/render.ts +1 -1
  134. package/lib/eject-assets/generators/typescript/banner.ts +1 -1
  135. package/lib/eject-assets/generators/typescript/client.ts +1 -1
  136. package/lib/eject-assets/generators/typescript/descriptor.ts +1 -1
  137. package/lib/eject-assets/generators/typescript/index.ts +2 -2
  138. package/lib/eject-assets/generators/typescript/inline-runtime.ts +1 -1
  139. package/lib/eject-assets/generators/typescript/operation-signature.ts +1 -1
  140. package/lib/eject-assets/generators/typescript/operation-types.ts +1 -1
  141. package/lib/eject-assets/generators/typescript/operations.ts +1 -1
  142. package/lib/eject-assets/generators/typescript/response-headers.ts +1 -1
  143. package/lib/eject-assets/generators/typescript/type-guards.ts +1 -1
  144. package/lib/eject-assets/generators/typescript/types.ts +1 -1
  145. package/lib/eject-assets/generators/zod/index.ts +2 -2
  146. package/lib/eject-assets/generators/zod/schemas.ts +1 -1
  147. package/lib/eject-assets/skills/client-generators/SKILL.md +1 -1
  148. package/lib/eject-assets/skills/java-generator/SKILL.md +161 -0
  149. package/lib/index.js +91 -90
  150. package/lib/skills/redocly-cli/SKILL.md +3 -1
  151. package/package.json +1 -1
  152. package/lib/chunks/2ZEKFWJQ.js +0 -13
  153. package/lib/chunks/3VOQMXIZ.js +0 -7
  154. package/lib/chunks/7GADNOVY.js +0 -9
  155. package/lib/chunks/FRQHCFKP.js +0 -199
  156. package/lib/chunks/J2EPFEBO.js +0 -17
  157. package/lib/chunks/JZATGVUO.js +0 -53
  158. package/lib/chunks/KNMNSDHR.js +0 -7
  159. package/lib/chunks/U5DIFWGH.js +0 -7
@@ -0,0 +1,362 @@
1
+ // Ejected from @redocly/client-generator@0.8.1 — the built-in "java" generator.
2
+ // This file is yours: edit freely; the generated client stays machine-owned and is
3
+ // rebuilt by `redocly generate-client`. Newer generator versions merge in with
4
+ // `redocly eject-generator java --update`.
5
+ // The `models` stage: named schemas as records with fromJson/toJson hydration,
6
+ // wire-backed enums, and sealed union interfaces with a parse dispatcher — plus the
7
+ // wire→typed value expressions the methods reuse.
8
+
9
+ import {
10
+ type ApiModel,
11
+ type DateType,
12
+ deref,
13
+ discriminatorCases,
14
+ enumValues,
15
+ flattenAllOf,
16
+ type PropertyModel,
17
+ type SchemaModel,
18
+ uniqueIdentifiers,
19
+ unwrapNullable,
20
+ } from '@redocly/client-generator';
21
+ import { JavaPrinter } from '@redocly/client-generator/printers/java';
22
+
23
+ import { JAVA, javaString, typeName } from './naming.ts';
24
+ import { classify, isDateFormat, javaType } from './types.ts';
25
+
26
+ /**
27
+ * Wire value → typed value expression over the runtime's JSON graph, or undefined when
28
+ * the raw `Object` is already the type. `depth` names the lambda parameters of nested
29
+ * collections (`item`, `item2`, …): Java rejects a lambda parameter that shadows one.
30
+ */
31
+ export function hydration(
32
+ schema: SchemaModel,
33
+ expr: string,
34
+ model: ApiModel,
35
+ // Required on purpose: a defaulted `'string'` let a call site forget it, and the method
36
+ // then returned a raw string where its own signature declared `OffsetDateTime`.
37
+ dateType: DateType,
38
+ depth = 0
39
+ ): string | undefined {
40
+ const bare = unwrapNullable(schema);
41
+ switch (bare.kind) {
42
+ case 'scalar':
43
+ if (dateType === 'Date' && bare.scalar === 'string' && isDateFormat(bare)) {
44
+ return bare.metadata?.format === 'date'
45
+ ? `asLocalDate(${expr})`
46
+ : `asOffsetDateTime(${expr})`;
47
+ }
48
+ return `${SCALAR_READERS[bare.scalar]}(${expr})`;
49
+ case 'enum':
50
+ return `${SCALAR_READERS[bare.scalar]}(${expr})`;
51
+ case 'literal':
52
+ return `${SCALAR_READERS[typeof bare.value === 'string' ? 'string' : typeof bare.value === 'boolean' ? 'boolean' : 'number']}(${expr})`;
53
+ case 'omit':
54
+ return hydration({ kind: 'ref', name: bare.base }, expr, model, dateType, depth);
55
+ case 'ref': {
56
+ const kind = classify(bare.name, model);
57
+ if (kind === 'record') return `${typeName(bare.name)}.fromJson(${expr})`;
58
+ if (kind === 'enum') {
59
+ const reader = enumValues(deref(bare, model)!)!.scalar === 'string' ? 'asString' : 'asLong';
60
+ return `${typeName(bare.name)}.fromWire(${reader}(${expr}))`;
61
+ }
62
+ if (kind === 'union') return `parse${typeName(bare.name)}(${expr})`;
63
+ const target = deref(bare, model);
64
+ return target === undefined ? undefined : hydration(target, expr, model, dateType, depth);
65
+ }
66
+ case 'array':
67
+ case 'record': {
68
+ const item = depth === 0 ? 'item' : `item${depth + 1}`;
69
+ const inner =
70
+ hydration(
71
+ bare.kind === 'array' ? bare.items : bare.value,
72
+ item,
73
+ model,
74
+ dateType,
75
+ depth + 1
76
+ ) ?? item;
77
+ return `${bare.kind === 'array' ? 'asList' : 'asMap'}(${expr}, ${item} -> ${inner})`;
78
+ }
79
+ case 'union':
80
+ case 'null':
81
+ case 'object':
82
+ case 'intersection':
83
+ case 'unknown':
84
+ return undefined;
85
+ }
86
+ }
87
+
88
+ const SCALAR_READERS = {
89
+ string: 'asString',
90
+ integer: 'asLong',
91
+ number: 'asDouble',
92
+ boolean: 'asBoolean',
93
+ };
94
+
95
+ /** The record components may not collide with the record's own methods or the locals of fromJson/toJson. */
96
+ // prettier-ignore
97
+ const RECORD_MEMBER_SLOTS = [
98
+ 'data', 'json', 'toJson', 'fromJson', 'hashCode', 'equals', 'toString', 'getClass', 'wait',
99
+ 'notify', 'notifyAll', 'clone', 'finalize',
100
+ ];
101
+
102
+ /** The sealed unions a record is a member of, so it declares `implements` for each. */
103
+ function unionsOf(name: string, model: ApiModel): string[] {
104
+ const unions: string[] = [];
105
+ for (const candidate of model.schemas) {
106
+ if (classify(candidate.name, model) !== 'union') continue;
107
+ const cases = discriminatorCases(candidate.schema, model)!;
108
+ if (cases.cases.some((entry) => entry.schemaName === name)) {
109
+ unions.push(typeName(candidate.name));
110
+ }
111
+ }
112
+ return unions;
113
+ }
114
+
115
+ function writeRecord(
116
+ printer: JavaPrinter,
117
+ name: string,
118
+ properties: PropertyModel[],
119
+ model: ApiModel,
120
+ dateType: DateType,
121
+ description?: string
122
+ ): void {
123
+ // A response never carries a `writeOnly` property, and responses decode into this same
124
+ // record, so the property is optional even when the description requires it.
125
+ const fields = properties.map((property) =>
126
+ property.writeOnly ? { ...property, required: false } : property
127
+ );
128
+ const components = uniqueIdentifiers(
129
+ fields.map((property) => property.name),
130
+ { style: 'camel', reserved: JAVA, taken: RECORD_MEMBER_SLOTS }
131
+ );
132
+ const record = typeName(name);
133
+ const interfaces = ['JsonValue', ...unionsOf(name, model)];
134
+ printer.doc(record, description);
135
+ printer.block(
136
+ `public record ${record}(`,
137
+ () => {
138
+ fields.forEach((property, index) => {
139
+ const comma = index < fields.length - 1 ? ',' : '';
140
+ printer.line(`${javaType(property.schema, model, dateType)} ${components[index]}${comma}`);
141
+ });
142
+ },
143
+ `) implements ${interfaces.join(', ')} {`
144
+ );
145
+ printer.indent(() => {
146
+ printer.block(
147
+ `public static ${record} fromJson(Object json) {`,
148
+ () => {
149
+ printer.line('Map<String, Object> data = asObject(json);');
150
+ printer.block(
151
+ 'if (data == null) {',
152
+ () => {
153
+ printer.line('return null;');
154
+ },
155
+ '}'
156
+ );
157
+ printer.block(
158
+ `return new ${record}(`,
159
+ () => {
160
+ fields.forEach((property, index) => {
161
+ const raw = `data.get(${javaString(property.name)})`;
162
+ const comma = index < fields.length - 1 ? ',' : '';
163
+ printer.line(`${hydration(property.schema, raw, model, dateType) ?? raw}${comma}`);
164
+ });
165
+ },
166
+ ');'
167
+ );
168
+ },
169
+ '}'
170
+ );
171
+ printer.blank();
172
+ printer.line('@Override');
173
+ printer.block(
174
+ 'public Map<String, Object> toJson() {',
175
+ () => {
176
+ printer.line('Map<String, Object> data = new LinkedHashMap<>();');
177
+ fields.forEach((property, index) => {
178
+ const put = `data.put(${javaString(property.name)}, ${components[index]});`;
179
+ if (property.required) {
180
+ printer.line(put);
181
+ } else {
182
+ printer.block(
183
+ `if (${components[index]} != null) {`,
184
+ () => {
185
+ printer.line(put);
186
+ },
187
+ '}'
188
+ );
189
+ }
190
+ });
191
+ printer.line('return data;');
192
+ },
193
+ '}'
194
+ );
195
+ });
196
+ printer.line('}');
197
+ printer.blank();
198
+ }
199
+
200
+ function writeEnum(
201
+ printer: JavaPrinter,
202
+ name: string,
203
+ values: Array<string | number | boolean>,
204
+ scalar: 'string' | 'integer',
205
+ description?: string
206
+ ): void {
207
+ const enumName = typeName(name);
208
+ const wireType = scalar === 'string' ? 'String' : 'Long';
209
+ const members = enumValues({ kind: 'enum', values, scalar })!.memberNames;
210
+ printer.doc(enumName, description);
211
+ printer.block(
212
+ `public enum ${enumName} implements WireValue {`,
213
+ () => {
214
+ values.forEach((value, index) => {
215
+ const literal = typeof value === 'string' ? javaString(value) : `${value}L`;
216
+ printer.line(`${members[index]}(${literal})${index < values.length - 1 ? ',' : ';'}`);
217
+ });
218
+ printer.blank();
219
+ printer.line(`private final ${wireType} wire;`);
220
+ printer.blank();
221
+ printer.block(
222
+ `${enumName}(${wireType} wire) {`,
223
+ () => {
224
+ printer.line('this.wire = wire;');
225
+ },
226
+ '}'
227
+ );
228
+ printer.blank();
229
+ printer.line('@Override');
230
+ printer.block(
231
+ `public ${wireType} wire() {`,
232
+ () => {
233
+ printer.line('return wire;');
234
+ },
235
+ '}'
236
+ );
237
+ printer.blank();
238
+ printer.line(
239
+ '/** The member for a wire value; null for null, an exception for an unknown value. */'
240
+ );
241
+ printer.block(
242
+ `public static ${enumName} fromWire(${wireType} wire) {`,
243
+ () => {
244
+ printer.block(
245
+ 'if (wire == null) {',
246
+ () => {
247
+ printer.line('return null;');
248
+ },
249
+ '}'
250
+ );
251
+ printer.block(
252
+ `for (${enumName} member : values()) {`,
253
+ () => {
254
+ printer.block(
255
+ 'if (member.wire.equals(wire)) {',
256
+ () => {
257
+ printer.line('return member;');
258
+ },
259
+ '}'
260
+ );
261
+ },
262
+ '}'
263
+ );
264
+ printer.line(`throw new IllegalArgumentException("Unknown ${enumName} value: " + wire);`);
265
+ },
266
+ '}'
267
+ );
268
+ },
269
+ '}'
270
+ );
271
+ printer.blank();
272
+ }
273
+
274
+ /** Write every named schema at the printer's current depth: records, enums, union dispatchers. */
275
+ export function writeJavaModels(
276
+ printer: JavaPrinter,
277
+ model: ApiModel,
278
+ dateType: DateType = 'string'
279
+ ): void {
280
+ for (const { name, schema } of model.schemas) {
281
+ const asEnum = enumValues(schema);
282
+ if (asEnum !== undefined && (asEnum.scalar === 'string' || asEnum.scalar === 'integer')) {
283
+ writeEnum(printer, name, asEnum.values, asEnum.scalar, schema.description);
284
+ continue;
285
+ }
286
+ if (schema.kind === 'object' || schema.kind === 'intersection') {
287
+ const flat = flattenAllOf(schema, model);
288
+ if (flat !== undefined) {
289
+ writeRecord(
290
+ printer,
291
+ name,
292
+ flat.properties,
293
+ model,
294
+ dateType,
295
+ flat.description ?? schema.description
296
+ );
297
+ continue;
298
+ }
299
+ }
300
+ const cases = discriminatorCases(schema, model);
301
+ if (cases !== undefined) {
302
+ const union = typeName(name);
303
+ const sealed = classify(name, model) === 'union';
304
+ const table = cases.cases
305
+ .map((entry) => `${entry.value} -> ${typeName(entry.schemaName)}`)
306
+ .join(', ');
307
+ // The table carries the description's own values, so it goes through the comment escaper.
308
+ printer.doc(union, `A discriminated union on ${javaString(cases.property)}: ${table}.`);
309
+ if (sealed) {
310
+ // The permitted records sit in the same compilation unit, so `permits` is implied.
311
+ printer.line(`public sealed interface ${union} extends JsonValue {}`);
312
+ printer.blank();
313
+ }
314
+ // A target without a record of its own keeps the raw value, so the union is `Object`.
315
+ printer.block(
316
+ `public static ${sealed ? union : 'Object'} parse${union}(Object json) {`,
317
+ () => {
318
+ printer.line('Map<String, Object> data = asObject(json);');
319
+ printer.block(
320
+ 'if (data == null) {',
321
+ () => {
322
+ printer.line('return null;');
323
+ },
324
+ '}'
325
+ );
326
+ printer.block(
327
+ `switch (String.valueOf(data.get(${javaString(cases.property)}))) {`,
328
+ () => {
329
+ for (const entry of cases.cases) {
330
+ const target = { kind: 'ref', name: entry.schemaName } as const;
331
+ const typed = hydration(target, 'data', model, dateType) ?? 'data';
332
+ printer.line(`case ${javaString(entry.value)}:`);
333
+ printer.indent(() => printer.line(`return ${typed};`));
334
+ }
335
+ printer.line('default:');
336
+ printer.indent(() =>
337
+ printer.line(
338
+ sealed
339
+ ? `throw new IllegalArgumentException("Unknown ${cases.property} value: " + data.get(${javaString(cases.property)}));`
340
+ : 'return data;'
341
+ )
342
+ );
343
+ },
344
+ '}'
345
+ );
346
+ },
347
+ '}'
348
+ );
349
+ printer.blank();
350
+ continue;
351
+ }
352
+ // Everything else (plain unions, aliases, records) has no Java declaration;
353
+ // references resolve to the underlying type via javaType.
354
+ }
355
+ }
356
+
357
+ /** Render every named schema at depth 0 — the models section on its own, for the render bars. */
358
+ export function renderJavaModels(model: ApiModel, dateType: DateType = 'string'): string {
359
+ const printer = new JavaPrinter();
360
+ writeJavaModels(printer, model, dateType);
361
+ return printer.toString();
362
+ }
@@ -0,0 +1,135 @@
1
+ // Ejected from @redocly/client-generator@0.8.1 — the built-in "java" generator.
2
+ // This file is yours: edit freely; the generated client stays machine-owned and is
3
+ // rebuilt by `redocly generate-client`. Newer generator versions merge in with
4
+ // `redocly eject-generator java --update`.
5
+ // The `naming` stage: the shared printer/naming instance, the string escaper, the
6
+ // enclosing class name, and the collision-free type/member/method identifiers every
7
+ // other stage builds on.
8
+
9
+ import {
10
+ type ApiModel,
11
+ casing,
12
+ identifierFor,
13
+ NotSupportedError,
14
+ type OperationModel,
15
+ RESERVED_WORDS,
16
+ } from '@redocly/client-generator';
17
+ import { JavaPrinter } from '@redocly/client-generator/printers/java';
18
+ import { JAVA_RUNTIME_SOURCE } from '@redocly/client-generator/runtime-sources';
19
+
20
+ export const JAVA = RESERVED_WORDS.java;
21
+
22
+ // Naming and escaping delegate to the printer — one implementation, one policy.
23
+ export const naming = new JavaPrinter();
24
+
25
+ /**
26
+ * The type names the runtime already uses inside the generated class — its own nested
27
+ * types and every JDK type it references, read off its source — plus the names the
28
+ * generated methods add. A schema that folds to one of them gets a `Model` suffix: a
29
+ * nested `record Config` would shadow the runtime's `Config`, and a nested `record Thread`
30
+ * (GitHub has one) would shadow `java.lang.Thread` for the runtime's retry sleep.
31
+ */
32
+ const TAKEN_TYPE_NAMES = new Set([
33
+ ...[
34
+ // Comments, char literals (a `'"'` would otherwise open a string), then strings go.
35
+ ...JAVA_RUNTIME_SOURCE.replace(/\/\*[\s\S]*?\*\/|\/\/.*$/gm, '')
36
+ .replace(/'(?:[^'\\]|\\.)'/g, "''")
37
+ .replace(/"(?:[^"\\]|\\.)*"/g, '""')
38
+ .matchAll(/\b[A-Z][A-Za-z0-9]*\b/g),
39
+ ].map((match) => match[0]),
40
+ 'Servers',
41
+ 'Void',
42
+ ]);
43
+
44
+ export function typeName(name: string): string {
45
+ const ident = naming.typeName(name);
46
+ return TAKEN_TYPE_NAMES.has(ident) ? `${ident}Model` : ident;
47
+ }
48
+
49
+ export function memberName(name: string): string {
50
+ return naming.memberName(name);
51
+ }
52
+
53
+ /** `"…"` with the escapes javac needs — safe for any spec-supplied text. */
54
+ export function javaString(value: string): string {
55
+ return naming.string(value);
56
+ }
57
+
58
+ /**
59
+ * The enclosing class — Java puts one public class per file, named after it — derived
60
+ * from the `--output` stem the way Python derives its module name (`openapi.client` →
61
+ * `OpenapiClient`). A schema of the same name cannot nest inside it, so that is rejected
62
+ * with the fix rather than renamed behind the user's back.
63
+ */
64
+ export function javaClassName(stem: string, model: ApiModel): string {
65
+ const name = identifierFor(stem, { style: 'pascal', reserved: JAVA });
66
+ // Inside a class named `Config` or `List`, those names mean the class itself.
67
+ if (TAKEN_TYPE_NAMES.has(name)) {
68
+ throw new NotSupportedError(
69
+ `The output file name "${stem}" gives the Java class the name "${name}", which the SDK already uses for one of its own types. Choose another --output name, for example "${stem}-sdk.ts".`
70
+ );
71
+ }
72
+ const clash = model.schemas.find((schema) => typeName(schema.name) === name);
73
+ if (clash !== undefined) {
74
+ throw new NotSupportedError(
75
+ `The output file name "${stem}" gives the Java class the same name as the "${clash.name}" schema, which cannot nest inside it. Choose another --output name, for example "${stem}-sdk.ts".`
76
+ );
77
+ }
78
+ return name;
79
+ }
80
+
81
+ /**
82
+ * The package clause the output declares, derived from the API title the way the PHP
83
+ * generator derives its namespace: the title's words in lowercase, run together, as Java
84
+ * package names are written (`Cafe Orders API` → `cafeordersapi`). A class in the default
85
+ * package cannot be imported from any named package, so there is always one. A name that
86
+ * cannot be a package as is follows Oracle's package-naming convention: a keyword gets a
87
+ * trailing underscore (`class_`), and so does `java`, which the JVM keeps for the JDK; a
88
+ * leading digit gets a leading underscore (`_3dprintingapi`).
89
+ */
90
+ export function javaPackageName(title: string): string {
91
+ const plain = plainPackageName(title);
92
+ if (plain === '') return 'api';
93
+ if (/^[0-9]/.test(plain)) return `_${plain}`;
94
+ return JAVA.has(plain) || plain === 'java' ? `${plain}_` : plain;
95
+ }
96
+
97
+ /** The title's words in lowercase, run together — the package before any convention applies. */
98
+ export function plainPackageName(title: string): string {
99
+ return casing.pascal(title).toLowerCase();
100
+ }
101
+
102
+ export function methodName(op: OperationModel): string {
103
+ return identifierFor(op.name, { style: 'camel', reserved: JAVA });
104
+ }
105
+
106
+ /** The methods every class inherits from `Object`; an operation named after one would override it. */
107
+ // prettier-ignore
108
+ const OBJECT_METHODS = [
109
+ 'getClass', 'hashCode', 'equals', 'clone', 'toString', 'notify', 'notifyAll', 'wait',
110
+ 'finalize',
111
+ ];
112
+
113
+ /** The methods an operation may add beside its own, all named after it. */
114
+ const METHOD_VARIANTS = ['', 'WithHeaders', 'Pages', 'Items'];
115
+
116
+ /**
117
+ * The method name for every operation, unique across the client — two operationIds may
118
+ * camel-case to one name (`get-user`, `getUser`). Each name also books its `WithHeaders`,
119
+ * `Pages`, and `Items` variants, so a later operation named like one of them moves aside
120
+ * (`listOrdersPages2`). Keyed by the IR name, which the sanitizer already made unique.
121
+ */
122
+ export function methodIdents(model: ApiModel): Map<string, string> {
123
+ const used = new Set(OBJECT_METHODS);
124
+ const idents = new Map<string, string>();
125
+ for (const op of model.services.flatMap((service) => service.operations)) {
126
+ const base = identifierFor(op.name, { style: 'camel', reserved: JAVA });
127
+ let ident = base;
128
+ for (let suffix = 2; METHOD_VARIANTS.some((variant) => used.has(ident + variant)); suffix++) {
129
+ ident = `${base}${suffix}`;
130
+ }
131
+ for (const variant of METHOD_VARIANTS) used.add(ident + variant);
132
+ idents.set(op.name, ident);
133
+ }
134
+ return idents;
135
+ }