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