trans_gen_java/
lib.rs

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    // Class
102    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    // Fields
118    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    // Constructor
145    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    // Reading
172    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    // Writing
322    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}