trans_gen/gens/java/
mod.rs

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    // Class
99    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    // Fields
115    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    // Constructor
142    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    // Reading
169    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    // Writing
320    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}