1use super::*;
2
3fn conv(name: &str) -> String {
4 name.replace("Bool", "Boolean")
5 .replace("Int32", "Int")
6 .replace("Int64", "Long")
7 .replace("Float32", "Float")
8 .replace("Float64", "Double")
9}
10
11pub struct Generator {
12 files: HashMap<String, String>,
13}
14
15fn type_name(schema: &Schema) -> String {
16 format!("{}{}", type_name_prearray(schema), type_post_array(schema))
17}
18
19fn type_name_obj(schema: &Schema) -> String {
20 match schema {
21 Schema::Bool => "Boolean".to_owned(),
22 Schema::Int32 => "Integer".to_owned(),
23 Schema::Int64 => "Long".to_owned(),
24 Schema::Float32 => "Float".to_owned(),
25 Schema::Float64 => "Double".to_owned(),
26 _ => type_name(schema),
27 }
28}
29
30fn type_name_prearray_obj(schema: &Schema) -> String {
31 match schema {
32 Schema::Bool => "Boolean".to_owned(),
33 Schema::Int32 => "Integer".to_owned(),
34 Schema::Int64 => "Long".to_owned(),
35 Schema::Float32 => "Float".to_owned(),
36 Schema::Float64 => "Double".to_owned(),
37 _ => type_name_prearray(schema),
38 }
39}
40
41fn type_name_prearray(schema: &Schema) -> String {
42 match schema {
43 Schema::Bool => "boolean".to_owned(),
44 Schema::Int32 => "int".to_owned(),
45 Schema::Int64 => "long".to_owned(),
46 Schema::Float32 => "float".to_owned(),
47 Schema::Float64 => "double".to_owned(),
48 Schema::String => "String".to_owned(),
49 Schema::Struct(Struct { name, .. })
50 | Schema::OneOf {
51 base_name: name, ..
52 }
53 | Schema::Enum {
54 base_name: name, ..
55 } => format!("model.{}", name.camel_case(conv)),
56 Schema::Option(inner) => type_name_obj(inner),
57 Schema::Vec(inner) => type_name_prearray_obj(inner),
58 Schema::Map(key, value) => format!(
59 "java.util.Map<{}, {}>",
60 type_name_obj(key),
61 type_name_obj(value)
62 ),
63 }
64}
65
66fn type_post_array(schema: &Schema) -> String {
67 match schema {
68 Schema::Vec(inner) => format!("[]{}", type_post_array(inner)),
69 _ => String::new(),
70 }
71}
72
73fn index_var_name(index_var: &mut usize) -> String {
74 let result = "ijk".chars().nth(*index_var).unwrap();
75 *index_var += 1;
76 result.to_string()
77}
78
79fn var_name(name: &str) -> &str {
80 match name.rfind('.') {
81 Some(index) => &name[(index + 1)..],
82 None => name,
83 }
84}
85
86fn getter_prefix(schema: &Schema) -> &'static str {
87 match schema {
88 Schema::Bool => "is",
89 _ => "get",
90 }
91}
92
93fn write_struct(
94 writer: &mut Writer,
95 struc: &Struct,
96 base: Option<(&Name, usize)>,
97) -> std::fmt::Result {
98 if let Some((base_name, _)) = base {
100 writeln!(
101 writer,
102 "public static class {} extends {} {{",
103 struc.name.camel_case(conv),
104 base_name.camel_case(conv)
105 )?;
106 } else {
107 writeln!(writer, "public class {} {{", struc.name.camel_case(conv))?;
108 }
109 writer.inc_ident();
110 if let Some((_, tag)) = base {
111 writeln!(writer, "public static final int TAG = {};", tag)?;
112 }
113
114 for field in &struc.fields {
116 writeln!(
117 writer,
118 "private {} {};",
119 type_name(&field.schema),
120 field.name.mixed_case(conv)
121 )?;
122 writeln!(
123 writer,
124 "public {} {}{}() {{ return {}; }}",
125 type_name(&field.schema),
126 getter_prefix(&field.schema),
127 field.name.camel_case(conv),
128 field.name.mixed_case(conv)
129 )?;
130 writeln!(
131 writer,
132 "public void set{}({} {}) {{ this.{} = {}; }}",
133 field.name.camel_case(conv),
134 type_name(&field.schema),
135 field.name.mixed_case(conv),
136 field.name.mixed_case(conv),
137 field.name.mixed_case(conv)
138 )?;
139 }
140
141 writeln!(writer, "public {}() {{}}", struc.name.camel_case(conv))?;
143 if !struc.fields.is_empty() {
144 write!(writer, "public {}(", struc.name.camel_case(conv))?;
145 for (index, field) in struc.fields.iter().enumerate() {
146 if index > 0 {
147 write!(writer, ", ")?;
148 }
149 write!(
150 writer,
151 "{} {}",
152 type_name(&field.schema),
153 field.name.mixed_case(conv)
154 )?;
155 }
156 writeln!(writer, ") {{")?;
157 for field in &struc.fields {
158 writeln!(
159 writer,
160 " this.{} = {};",
161 field.name.mixed_case(conv),
162 field.name.mixed_case(conv)
163 )?;
164 }
165 writeln!(writer, "}}")?;
166 }
167
168 writeln!(
170 writer,
171 "public static {} readFrom(java.io.InputStream stream) throws java.io.IOException {{",
172 struc.name.camel_case(conv)
173 )?;
174 writer.inc_ident();
175 writeln!(
176 writer,
177 "{} result = new {}();",
178 struc.name.camel_case(conv),
179 struc.name.camel_case(conv)
180 )?;
181 for field in &struc.fields {
182 fn assign(
183 writer: &mut Writer,
184 to: &str,
185 schema: &Schema,
186 index_var: &mut usize,
187 ) -> std::fmt::Result {
188 match schema {
189 Schema::Bool => {
190 writeln!(writer, "{} = StreamUtil.readBoolean(stream);", to)?;
191 }
192 Schema::Int32 => {
193 writeln!(writer, "{} = StreamUtil.readInt(stream);", to)?;
194 }
195 Schema::Int64 => {
196 writeln!(writer, "{} = StreamUtil.readLong(stream);", to)?;
197 }
198 Schema::Float32 => {
199 writeln!(writer, "{} = StreamUtil.readFloat(stream);", to)?;
200 }
201 Schema::Float64 => {
202 writeln!(writer, "{} = StreamUtil.readDouble(stream);", to)?;
203 }
204 Schema::String => {
205 writeln!(writer, "{} = StreamUtil.readString(stream);", to)?;
206 }
207 Schema::Struct(Struct { name, .. })
208 | Schema::OneOf {
209 base_name: name, ..
210 } => {
211 writeln!(
212 writer,
213 "{} = model.{}.readFrom(stream);",
214 to,
215 name.camel_case(conv)
216 )?;
217 }
218 Schema::Option(inner) => {
219 writeln!(writer, "if (StreamUtil.readBoolean(stream)) {{")?;
220 writer.inc_ident();
221 assign(writer, to, inner, index_var)?;
222 writer.dec_ident();
223 writeln!(writer, "}} else {{")?;
224 writeln!(writer, " {} = null;", to)?;
225 writeln!(writer, "}}")?;
226 }
227 Schema::Vec(inner) => {
228 writeln!(
229 writer,
230 "{} = new {}[StreamUtil.readInt(stream)]{};",
231 to,
232 type_name_prearray(inner),
233 type_post_array(inner)
234 )?;
235 let index_var_name = index_var_name(index_var);
236 writeln!(
237 writer,
238 "for (int {} = 0; {} < {}.length; {}++) {{",
239 index_var_name, index_var_name, to, index_var_name
240 )?;
241 writer.inc_ident();
242 assign(
243 writer,
244 &format!("{}[{}]", to, index_var_name),
245 inner,
246 index_var,
247 )?;
248 writer.dec_ident();
249 writeln!(writer, "}}")?;
250 }
251 Schema::Map(key_type, value_type) => {
252 let to_size = format!("{}Size", var_name(to));
253 writeln!(writer, "int {} = StreamUtil.readInt(stream);", to_size)?;
254 writeln!(writer, "{} = new java.util.HashMap<>({});", to, to_size)?;
255 let index_var_name = index_var_name(index_var);
256 writeln!(
257 writer,
258 "for (int {} = 0; {} < {}; {}++) {{",
259 index_var_name, index_var_name, to_size, index_var_name
260 )?;
261 writer.inc_ident();
262 writeln!(writer, "{} {}Key;", type_name(key_type), var_name(to))?;
263 assign(writer, &format!("{}Key", var_name(to)), key_type, index_var)?;
264 writeln!(writer, "{} {}Value;", type_name(value_type), var_name(to))?;
265 assign(
266 writer,
267 &format!("{}Value", var_name(to)),
268 value_type,
269 index_var,
270 )?;
271 writeln!(
272 writer,
273 "{}.put({}Key, {}Value);",
274 to,
275 var_name(to),
276 var_name(to)
277 )?;
278 writer.dec_ident();
279 writeln!(writer, "}}")?;
280 }
281 Schema::Enum {
282 documentation: _,
283 base_name,
284 variants,
285 } => {
286 writeln!(writer, "switch (StreamUtil.readInt(stream)) {{")?;
287 for (tag, variant) in variants.iter().enumerate() {
288 writeln!(writer, "case {}:", tag)?;
289 writeln!(
290 writer,
291 " {} = model.{}.{};",
292 to,
293 base_name.camel_case(conv),
294 variant.name.shouty_snake_case(conv)
295 )?;
296 writeln!(writer, " break;")?;
297 }
298 writeln!(writer, "default:")?;
299 writeln!(
300 writer,
301 " throw new java.io.IOException(\"Unexpected tag value\");"
302 )?;
303 writeln!(writer, "}}")?;
304 }
305 }
306 Ok(())
307 }
308 assign(
309 writer,
310 &format!("result.{}", field.name.mixed_case(conv)),
311 &field.schema,
312 &mut 0,
313 )?;
314 }
315 writeln!(writer, "return result;")?;
316 writer.dec_ident();
317 writeln!(writer, "}}")?;
318
319 if base.is_some() {
321 writeln!(writer, "@Override")?;
322 }
323 writeln!(
324 writer,
325 "public void writeTo(java.io.OutputStream stream) throws java.io.IOException {{",
326 )?;
327 writer.inc_ident();
328 if base.is_some() {
329 writeln!(writer, "StreamUtil.writeInt(stream, TAG);")?;
330 }
331 if let Some(magic) = struc.magic {
332 writeln!(writer, "StreamUtil.writeInt(stream, {});", magic)?;
333 }
334 for field in &struc.fields {
335 fn write(writer: &mut Writer, value: &str, schema: &Schema) -> std::fmt::Result {
336 match schema {
337 Schema::Bool => {
338 writeln!(writer, "StreamUtil.writeBoolean(stream, {});", value)?;
339 }
340 Schema::Int32 => {
341 writeln!(writer, "StreamUtil.writeInt(stream, {});", value)?;
342 }
343 Schema::Int64 => {
344 writeln!(writer, "StreamUtil.writeLong(stream, {});", value)?;
345 }
346 Schema::Float32 => {
347 writeln!(writer, "StreamUtil.writeFloat(stream, {});", value)?;
348 }
349 Schema::Float64 => {
350 writeln!(writer, "StreamUtil.writeDouble(stream, {});", value)?;
351 }
352 Schema::String => {
353 writeln!(writer, "StreamUtil.writeString(stream, {});", value)?;
354 }
355 Schema::Struct(_) | Schema::OneOf { .. } => {
356 writeln!(writer, "{}.writeTo(stream);", value)?;
357 }
358 Schema::Option(inner) => {
359 writeln!(writer, "if ({} == null) {{", value)?;
360 writeln!(writer, " StreamUtil.writeBoolean(stream, false);")?;
361 writeln!(writer, "}} else {{")?;
362 writer.inc_ident();
363 writeln!(writer, "StreamUtil.writeBoolean(stream, true);")?;
364 write(writer, value, inner)?;
365 writer.dec_ident();
366 writeln!(writer, "}}")?;
367 }
368 Schema::Vec(inner) => {
369 writeln!(writer, "StreamUtil.writeInt(stream, {}.length);", value)?;
370 writeln!(
371 writer,
372 "for ({} {}Element : {}) {{",
373 type_name(inner),
374 var_name(value),
375 value
376 )?;
377 writer.inc_ident();
378 write(writer, &format!("{}Element", var_name(value)), inner)?;
379 writer.dec_ident();
380 writeln!(writer, "}}")?;
381 }
382 Schema::Map(key_type, value_type) => {
383 writeln!(writer, "StreamUtil.writeInt(stream, {}.size());", value)?;
384 writeln!(
385 writer,
386 "for (java.util.Map.Entry<{}, {}> {}Entry : {}.entrySet()) {{",
387 type_name_obj(key_type),
388 type_name_obj(value_type),
389 var_name(value),
390 value
391 )?;
392 writer.inc_ident();
393 writeln!(
394 writer,
395 "{} {}Key = {}Entry.getKey();",
396 type_name(key_type),
397 var_name(value),
398 var_name(value)
399 )?;
400 writeln!(
401 writer,
402 "{} {}Value = {}Entry.getValue();",
403 type_name(value_type),
404 var_name(value),
405 var_name(value)
406 )?;
407 write(writer, &format!("{}Key", var_name(value)), key_type)?;
408 write(writer, &format!("{}Value", var_name(value)), value_type)?;
409 writer.dec_ident();
410 writeln!(writer, "}}")?;
411 }
412 Schema::Enum { .. } => {
413 writeln!(writer, "StreamUtil.writeInt(stream, {}.tag);", value)?;
414 }
415 }
416 Ok(())
417 }
418 write(writer, &field.name.mixed_case(conv), &field.schema)?;
419 }
420 writer.dec_ident();
421 writeln!(writer, "}}")?;
422 writer.dec_ident();
423 writeln!(writer, "}}")?;
424 Ok(())
425}
426
427impl crate::Generator for Generator {
428 type Options = ();
429 fn new(_name: &str, _version: &str, _: ()) -> Self {
430 let mut files = HashMap::new();
431 files.insert(
432 "util/StreamUtil.java".to_owned(),
433 include_str!("StreamUtil.java").to_owned(),
434 );
435 Self { files }
436 }
437 fn result(self) -> GenResult {
438 self.files.into()
439 }
440 fn add_only(&mut self, schema: &Schema) {
441 match schema {
442 Schema::Enum {
443 documentation: _,
444 base_name,
445 variants,
446 } => {
447 let file_name = format!("model/{}.java", base_name.camel_case(conv));
448 let mut writer = Writer::new();
449 writeln!(writer, "package model;").unwrap();
450 writeln!(writer).unwrap();
451 writeln!(writer, "import util.StreamUtil;").unwrap();
452 writeln!(writer).unwrap();
453 writeln!(writer, "public enum {} {{", base_name.camel_case(conv)).unwrap();
454 writer.inc_ident();
455 for (index, variant) in variants.iter().enumerate() {
456 writeln!(
457 writer,
458 "{}({}){}",
459 variant.name.shouty_snake_case(conv),
460 index,
461 if index + 1 < variants.len() { "," } else { ";" }
462 )
463 .unwrap();
464 }
465 writeln!(writer, "public int tag;").unwrap();
466 writeln!(writer, "{}(int tag) {{", base_name.camel_case(conv)).unwrap();
467 writeln!(writer, " this.tag = tag;").unwrap();
468 writeln!(writer, "}}").unwrap();
469 writer.dec_ident();
470 writeln!(writer, "}}").unwrap();
471 self.files.insert(file_name, writer.get());
472 }
473 Schema::Struct(struc) => {
474 let file_name = format!("model/{}.java", struc.name.camel_case(conv));
475 let mut writer = Writer::new();
476 writeln!(writer, "package model;").unwrap();
477 writeln!(writer).unwrap();
478 writeln!(writer, "import util.StreamUtil;").unwrap();
479 writeln!(writer).unwrap();
480 write_struct(&mut writer, struc, None).unwrap();
481 self.files.insert(file_name, writer.get());
482 }
483 Schema::OneOf {
484 documentation: _,
485 base_name,
486 variants,
487 } => {
488 let file_name = format!("model/{}.java", base_name.camel_case(conv));
489 let mut writer = Writer::new();
490 writeln!(writer, "package model;").unwrap();
491 writeln!(writer).unwrap();
492 writeln!(writer, "import util.StreamUtil;").unwrap();
493 writeln!(writer).unwrap();
494 writeln!(
495 &mut writer,
496 "public abstract class {} {{",
497 base_name.camel_case(conv)
498 )
499 .unwrap();
500 {
501 writer.inc_ident();
502 writeln!(
503 &mut writer,
504 "public abstract void writeTo(java.io.OutputStream stream) throws java.io.IOException;"
505 )
506 .unwrap();
507 writeln!(
508 &mut writer,
509 "public static {} readFrom(java.io.InputStream stream) throws java.io.IOException {{",
510 base_name.camel_case(conv)
511 )
512 .unwrap();
513 {
514 writer.inc_ident();
515 writeln!(&mut writer, "switch (StreamUtil.readInt(stream)) {{").unwrap();
516 writer.inc_ident();
517 for variant in variants {
518 writeln!(&mut writer, "case {}.TAG:", variant.name.camel_case(conv))
519 .unwrap();
520 writeln!(
521 &mut writer,
522 " return {}.readFrom(stream);",
523 variant.name.camel_case(conv)
524 )
525 .unwrap();
526 }
527 writeln!(&mut writer, "default:").unwrap();
528 writeln!(
529 &mut writer,
530 " throw new java.io.IOException(\"Unexpected tag value\");"
531 )
532 .unwrap();
533 writer.dec_ident();
534 writeln!(&mut writer, "}}").unwrap();
535 writer.dec_ident();
536 }
537 writeln!(&mut writer, "}}").unwrap();
538 for (tag, variant) in variants.iter().enumerate() {
539 writeln!(&mut writer).unwrap();
540 write_struct(&mut writer, variant, Some((base_name, tag))).unwrap();
541 }
542 writer.dec_ident();
543 }
544 writeln!(&mut writer, "}}").unwrap();
545 self.files.insert(file_name, writer.get());
546 }
547 Schema::Bool
548 | Schema::Int32
549 | Schema::Int64
550 | Schema::Float32
551 | Schema::Float64
552 | Schema::String
553 | Schema::Option(_)
554 | Schema::Vec(_)
555 | Schema::Map(_, _) => {}
556 }
557 }
558}