Skip to main content

serde_generate/
java.rs

1// Copyright (c) Facebook, Inc. and its affiliates
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4use crate::{
5    common,
6    indent::{IndentConfig, IndentedWriter},
7    CodeGeneratorConfig, Encoding,
8};
9use heck::CamelCase;
10use include_dir::include_dir as include_directory;
11use serde_reflection::{ContainerFormat, Format, FormatHolder, Named, Registry, VariantFormat};
12use std::{
13    collections::{BTreeMap, HashMap},
14    io::{Result, Write},
15    path::PathBuf,
16};
17
18/// Main configuration object for code-generation in Java.
19pub struct CodeGenerator<'a> {
20    /// Language-independent configuration.
21    config: &'a CodeGeneratorConfig,
22    /// Mapping from external type names to fully-qualified class names (e.g. "MyClass" -> "com.my_org.my_package.MyClass").
23    /// Derived from `config.external_definitions`.
24    external_qualified_names: HashMap<String, String>,
25}
26
27/// Shared state for the code generation of a Java source file.
28struct JavaEmitter<'a, T> {
29    /// Writer.
30    out: IndentedWriter<T>,
31    /// Generator.
32    generator: &'a CodeGenerator<'a>,
33    /// Current namespace (e.g. vec!["com", "my_org", "my_package", "MyClass"])
34    current_namespace: Vec<String>,
35    /// Current (non-qualified) generated class names that could clash with names in the registry
36    /// (e.g. "Builder" or variant classes).
37    /// * We count multiplicities to allow inplace backtracking.
38    /// * Names in the registry are assumed to never clash.
39    current_reserved_names: HashMap<String, usize>,
40}
41
42impl<'a> CodeGenerator<'a> {
43    /// Create a Java code generator for the given config.
44    pub fn new(config: &'a CodeGeneratorConfig) -> Self {
45        if config.enums.c_style {
46            panic!("Java does not support generating c-style enums");
47        }
48        let mut external_qualified_names = HashMap::new();
49        for (namespace, names) in &config.external_definitions {
50            for name in names {
51                external_qualified_names.insert(name.to_string(), format!("{namespace}.{name}"));
52            }
53        }
54        Self {
55            config,
56            external_qualified_names,
57        }
58    }
59
60    /// Output class definitions for ` registry` in separate source files.
61    /// Source files will be created in a subdirectory of `install_dir` corresponding to the given
62    /// package name (if any, otherwise `install_dir` it self).
63    pub fn write_source_files(
64        &self,
65        install_dir: std::path::PathBuf,
66        registry: &Registry,
67    ) -> Result<()> {
68        let current_namespace = self
69            .config
70            .module_name
71            .split('.')
72            .map(String::from)
73            .collect::<Vec<_>>();
74
75        let mut dir_path = install_dir;
76        for part in &current_namespace {
77            dir_path = dir_path.join(part);
78        }
79        std::fs::create_dir_all(&dir_path)?;
80
81        for (name, format) in registry {
82            self.write_container_class(&dir_path, current_namespace.clone(), name, format)?;
83        }
84        if self.config.serialization {
85            self.write_helper_class(&dir_path, current_namespace, registry)?;
86        }
87        Ok(())
88    }
89
90    fn write_container_class(
91        &self,
92        dir_path: &std::path::Path,
93        current_namespace: Vec<String>,
94        name: &str,
95        format: &ContainerFormat,
96    ) -> Result<()> {
97        let mut file = std::fs::File::create(dir_path.join(name.to_string() + ".java"))?;
98        let mut emitter = JavaEmitter {
99            out: IndentedWriter::new(&mut file, IndentConfig::Space(4)),
100            generator: self,
101            current_namespace,
102            current_reserved_names: HashMap::new(),
103        };
104
105        emitter.output_preamble()?;
106        emitter.output_container(name, format)
107    }
108
109    fn write_helper_class(
110        &self,
111        dir_path: &std::path::Path,
112        current_namespace: Vec<String>,
113        registry: &Registry,
114    ) -> Result<()> {
115        let mut file = std::fs::File::create(dir_path.join("TraitHelpers.java"))?;
116        let mut emitter = JavaEmitter {
117            out: IndentedWriter::new(&mut file, IndentConfig::Space(4)),
118            generator: self,
119            current_namespace,
120            current_reserved_names: HashMap::new(),
121        };
122
123        emitter.output_preamble()?;
124        emitter.output_trait_helpers(registry)
125    }
126}
127
128impl<'a, T> JavaEmitter<'a, T>
129where
130    T: Write,
131{
132    fn output_preamble(&mut self) -> Result<()> {
133        writeln!(self.out, "package {};\n", self.generator.config.module_name)?;
134        Ok(())
135    }
136
137    /// Compute a safe reference to the registry type `name` in the given context.
138    /// If `name` is not marked as "reserved" (e.g. "Builder"), we compare the global
139    /// name `self.qualified_names[name]` with the current namespace and try to use the
140    /// short string `name` if possible.
141    fn quote_qualified_name(&self, name: &str) -> String {
142        let qname = self
143            .generator
144            .external_qualified_names
145            .get(name)
146            .cloned()
147            .unwrap_or_else(|| format!("{}.{}", self.generator.config.module_name, name));
148        let mut path = qname.split('.').collect::<Vec<_>>();
149        if path.len() <= 1 {
150            return qname;
151        }
152        let name = path.pop().unwrap();
153        if self.current_reserved_names.contains_key(name) {
154            return qname;
155        }
156        for (index, element) in path.iter().enumerate() {
157            match self.current_namespace.get(index) {
158                Some(e) if e == element => (),
159                _ => {
160                    return qname;
161                }
162            }
163        }
164        name.to_string()
165    }
166
167    fn output_comment(&mut self, name: &str) -> std::io::Result<()> {
168        let mut path = self.current_namespace.clone();
169        path.push(name.to_string());
170        if let Some(doc) = self.generator.config.comments.get(&path) {
171            let text = textwrap::indent(doc, " * ").replace("\n\n", "\n *\n");
172            writeln!(self.out, "/**\n{text} */")?;
173        }
174        Ok(())
175    }
176
177    fn output_custom_code(&mut self) -> std::io::Result<()> {
178        if let Some(code) = self
179            .generator
180            .config
181            .custom_code
182            .get(&self.current_namespace)
183        {
184            writeln!(self.out, "\n{code}")?;
185        }
186        Ok(())
187    }
188
189    fn quote_type(&self, format: &Format) -> String {
190        use Format::*;
191        match format {
192            TypeName(x) => self.quote_qualified_name(x),
193            Unit => "com.novi.serde.Unit".into(),
194            Bool => "Boolean".into(),
195            I8 => "Byte".into(),
196            I16 => "Short".into(),
197            I32 => "Integer".into(),
198            I64 => "Long".into(),
199            I128 => "java.math.@com.novi.serde.Int128 BigInteger".into(),
200            U8 => "@com.novi.serde.Unsigned Byte".into(),
201            U16 => "@com.novi.serde.Unsigned Short".into(),
202            U32 => "@com.novi.serde.Unsigned Integer".into(),
203            U64 => "@com.novi.serde.Unsigned Long".into(),
204            U128 => "java.math.@com.novi.serde.Unsigned @com.novi.serde.Int128 BigInteger".into(),
205            F32 => "Float".into(),
206            F64 => "Double".into(),
207            Char => "Character".into(),
208            Str => "String".into(),
209            Bytes => "com.novi.serde.Bytes".into(),
210
211            Option(format) => format!("java.util.Optional<{}>", self.quote_type(format)),
212            Seq(format) => format!("java.util.List<{}>", self.quote_type(format)),
213            Map { key, value } => format!(
214                "java.util.Map<{}, {}>",
215                self.quote_type(key),
216                self.quote_type(value)
217            ),
218            Tuple(formats) => format!(
219                "com.novi.serde.Tuple{}<{}>",
220                formats.len(),
221                self.quote_types(formats)
222            ),
223            TupleArray { content, size } => format!(
224                "java.util.@com.novi.serde.ArrayLen(length={}) List<{}>",
225                size,
226                self.quote_type(content)
227            ),
228            Variable(_) => panic!("unexpected value"),
229        }
230    }
231
232    fn enter_class(&mut self, name: &str, reserved_subclass_names: &[&str]) {
233        self.out.indent();
234        self.current_namespace.push(name.to_string());
235        for name in reserved_subclass_names {
236            let entry = self
237                .current_reserved_names
238                .entry(name.to_string())
239                .or_insert(0);
240            *entry += 1;
241        }
242    }
243
244    fn leave_class(&mut self, reserved_subclass_names: &[&str]) {
245        self.out.unindent();
246        self.current_namespace.pop();
247        for name in reserved_subclass_names {
248            let entry = self.current_reserved_names.get_mut(*name).unwrap();
249            *entry -= 1;
250            if *entry == 0 {
251                self.current_reserved_names.remove(*name);
252            }
253        }
254    }
255
256    fn quote_types(&self, formats: &[Format]) -> String {
257        formats
258            .iter()
259            .map(|f| self.quote_type(f))
260            .collect::<Vec<_>>()
261            .join(", ")
262    }
263
264    fn output_trait_helpers(&mut self, registry: &Registry) -> Result<()> {
265        let mut subtypes = BTreeMap::new();
266        for format in registry.values() {
267            format
268                .visit(&mut |f| {
269                    if Self::needs_helper(f) {
270                        subtypes.insert(common::mangle_type(f), f.clone());
271                    }
272                    Ok(())
273                })
274                .unwrap();
275        }
276        writeln!(self.out, "final class TraitHelpers {{")?;
277        let reserved_names = &[];
278        self.enter_class("TraitHelpers", reserved_names);
279        for (mangled_name, subtype) in &subtypes {
280            self.output_serialization_helper(mangled_name, subtype)?;
281            self.output_deserialization_helper(mangled_name, subtype)?;
282        }
283        self.leave_class(reserved_names);
284        writeln!(self.out, "}}\n")
285    }
286
287    fn needs_helper(format: &Format) -> bool {
288        use Format::*;
289        matches!(
290            format,
291            Option(_) | Seq(_) | Map { .. } | Tuple(_) | TupleArray { .. }
292        )
293    }
294
295    fn quote_serialize_value(&self, value: &str, format: &Format) -> String {
296        use Format::*;
297        match format {
298            TypeName(_) => format!("{value}.serialize(serializer);"),
299            Unit => format!("serializer.serialize_unit({value});"),
300            Bool => format!("serializer.serialize_bool({value});"),
301            I8 => format!("serializer.serialize_i8({value});"),
302            I16 => format!("serializer.serialize_i16({value});"),
303            I32 => format!("serializer.serialize_i32({value});"),
304            I64 => format!("serializer.serialize_i64({value});"),
305            I128 => format!("serializer.serialize_i128({value});"),
306            U8 => format!("serializer.serialize_u8({value});"),
307            U16 => format!("serializer.serialize_u16({value});"),
308            U32 => format!("serializer.serialize_u32({value});"),
309            U64 => format!("serializer.serialize_u64({value});"),
310            U128 => format!("serializer.serialize_u128({value});"),
311            F32 => format!("serializer.serialize_f32({value});"),
312            F64 => format!("serializer.serialize_f64({value});"),
313            Char => format!("serializer.serialize_char({value});"),
314            Str => format!("serializer.serialize_str({value});"),
315            Bytes => format!("serializer.serialize_bytes({value});"),
316            _ => format!(
317                "{}.serialize_{}({}, serializer);",
318                self.quote_qualified_name("TraitHelpers"),
319                common::mangle_type(format),
320                value
321            ),
322        }
323    }
324
325    fn quote_deserialize(&self, format: &Format) -> String {
326        use Format::*;
327        match format {
328            TypeName(name) => format!(
329                "{}.deserialize(deserializer)",
330                self.quote_qualified_name(name)
331            ),
332            Unit => "deserializer.deserialize_unit()".to_string(),
333            Bool => "deserializer.deserialize_bool()".to_string(),
334            I8 => "deserializer.deserialize_i8()".to_string(),
335            I16 => "deserializer.deserialize_i16()".to_string(),
336            I32 => "deserializer.deserialize_i32()".to_string(),
337            I64 => "deserializer.deserialize_i64()".to_string(),
338            I128 => "deserializer.deserialize_i128()".to_string(),
339            U8 => "deserializer.deserialize_u8()".to_string(),
340            U16 => "deserializer.deserialize_u16()".to_string(),
341            U32 => "deserializer.deserialize_u32()".to_string(),
342            U64 => "deserializer.deserialize_u64()".to_string(),
343            U128 => "deserializer.deserialize_u128()".to_string(),
344            F32 => "deserializer.deserialize_f32()".to_string(),
345            F64 => "deserializer.deserialize_f64()".to_string(),
346            Char => "deserializer.deserialize_char()".to_string(),
347            Str => "deserializer.deserialize_str()".to_string(),
348            Bytes => "deserializer.deserialize_bytes()".to_string(),
349            _ => format!(
350                "{}.deserialize_{}(deserializer)",
351                self.quote_qualified_name("TraitHelpers"),
352                common::mangle_type(format),
353            ),
354        }
355    }
356
357    fn output_serialization_helper(&mut self, name: &str, format0: &Format) -> Result<()> {
358        use Format::*;
359
360        write!(
361            self.out,
362            "static void serialize_{}({} value, com.novi.serde.Serializer serializer) throws com.novi.serde.SerializationError {{",
363            name,
364            self.quote_type(format0)
365        )?;
366        self.out.indent();
367        match format0 {
368            Option(format) => {
369                write!(
370                    self.out,
371                    r#"
372if (value.isPresent()) {{
373    serializer.serialize_option_tag(true);
374    {}
375}} else {{
376    serializer.serialize_option_tag(false);
377}}
378"#,
379                    self.quote_serialize_value("value.get()", format)
380                )?;
381            }
382
383            Seq(format) => {
384                write!(
385                    self.out,
386                    r#"
387serializer.serialize_len(value.size());
388for ({} item : value) {{
389    {}
390}}
391"#,
392                    self.quote_type(format),
393                    self.quote_serialize_value("item", format)
394                )?;
395            }
396
397            Map { key, value } => {
398                write!(
399                    self.out,
400                    r#"
401serializer.serialize_len(value.size());
402int[] offsets = new int[value.size()];
403int count = 0;
404for (java.util.Map.Entry<{}, {}> entry : value.entrySet()) {{
405    offsets[count++] = serializer.get_buffer_offset();
406    {}
407    {}
408}}
409serializer.sort_map_entries(offsets);
410"#,
411                    self.quote_type(key),
412                    self.quote_type(value),
413                    self.quote_serialize_value("entry.getKey()", key),
414                    self.quote_serialize_value("entry.getValue()", value)
415                )?;
416            }
417
418            Tuple(formats) => {
419                writeln!(self.out)?;
420                for (index, format) in formats.iter().enumerate() {
421                    let expr = format!("value.field{index}");
422                    writeln!(self.out, "{}", self.quote_serialize_value(&expr, format))?;
423                }
424            }
425
426            TupleArray { content, size } => {
427                write!(
428                    self.out,
429                    r#"
430if (value.size() != {0}) {{
431    throw new java.lang.IllegalArgumentException("Invalid length for fixed-size array: " + value.size() + " instead of "+ {0});
432}}
433for ({1} item : value) {{
434    {2}
435}}
436"#,
437                    size,
438                    self.quote_type(content),
439                    self.quote_serialize_value("item", content),
440                )?;
441            }
442
443            _ => panic!("unexpected case"),
444        }
445        self.out.unindent();
446        writeln!(self.out, "}}\n")
447    }
448
449    fn output_deserialization_helper(&mut self, name: &str, format0: &Format) -> Result<()> {
450        use Format::*;
451
452        write!(
453        self.out,
454        "static {} deserialize_{}(com.novi.serde.Deserializer deserializer) throws com.novi.serde.DeserializationError {{",
455        self.quote_type(format0),
456        name,
457    )?;
458        self.out.indent();
459        match format0 {
460            Option(format) => {
461                write!(
462                    self.out,
463                    r#"
464boolean tag = deserializer.deserialize_option_tag();
465if (!tag) {{
466    return java.util.Optional.empty();
467}} else {{
468    return java.util.Optional.of({});
469}}
470"#,
471                    self.quote_deserialize(format),
472                )?;
473            }
474
475            Seq(format) => {
476                write!(
477                    self.out,
478                    r#"
479long length = deserializer.deserialize_len();
480java.util.List<{0}> obj = new java.util.ArrayList<{0}>((int) length);
481for (long i = 0; i < length; i++) {{
482    obj.add({1});
483}}
484return obj;
485"#,
486                    self.quote_type(format),
487                    self.quote_deserialize(format)
488                )?;
489            }
490
491            Map { key, value } => {
492                write!(
493                    self.out,
494                    r#"
495long length = deserializer.deserialize_len();
496java.util.Map<{0}, {1}> obj = new java.util.HashMap<{0}, {1}>();
497int previous_key_start = 0;
498int previous_key_end = 0;
499for (long i = 0; i < length; i++) {{
500    int key_start = deserializer.get_buffer_offset();
501    {0} key = {2};
502    int key_end = deserializer.get_buffer_offset();
503    if (i > 0) {{
504        deserializer.check_that_key_slices_are_increasing(
505            new com.novi.serde.Slice(previous_key_start, previous_key_end),
506            new com.novi.serde.Slice(key_start, key_end));
507    }}
508    previous_key_start = key_start;
509    previous_key_end = key_end;
510    {1} value = {3};
511    obj.put(key, value);
512}}
513return obj;
514"#,
515                    self.quote_type(key),
516                    self.quote_type(value),
517                    self.quote_deserialize(key),
518                    self.quote_deserialize(value),
519                )?;
520            }
521
522            Tuple(formats) => {
523                write!(
524                    self.out,
525                    r#"
526return new {}({}
527);
528"#,
529                    self.quote_type(format0),
530                    formats
531                        .iter()
532                        .map(|f| format!("\n    {}", self.quote_deserialize(f)))
533                        .collect::<Vec<_>>()
534                        .join(",")
535                )?;
536            }
537
538            TupleArray { content, size } => {
539                write!(
540                    self.out,
541                    r#"
542java.util.List<{0}> obj = new java.util.ArrayList<{0}>({1});
543for (long i = 0; i < {1}; i++) {{
544    obj.add({2});
545}}
546return obj;
547"#,
548                    self.quote_type(content),
549                    size,
550                    self.quote_deserialize(content)
551                )?;
552            }
553
554            _ => panic!("unexpected case"),
555        }
556        self.out.unindent();
557        writeln!(self.out, "}}\n")
558    }
559
560    fn output_variant(
561        &mut self,
562        base: &str,
563        index: u32,
564        name: &str,
565        variant: &VariantFormat,
566    ) -> Result<()> {
567        use VariantFormat::*;
568        let fields = match variant {
569            Unit => Vec::new(),
570            NewType(format) => vec![Named {
571                name: "value".to_string(),
572                value: format.as_ref().clone(),
573            }],
574            Tuple(formats) => formats
575                .iter()
576                .enumerate()
577                .map(|(i, f)| Named {
578                    name: format!("field{i}"),
579                    value: f.clone(),
580                })
581                .collect(),
582            Struct(fields) => fields.clone(),
583            Variable(_) => panic!("incorrect value"),
584        };
585        self.output_struct_or_variant_container(Some(base), Some(index), name, &fields)
586    }
587
588    fn output_variants(
589        &mut self,
590        base: &str,
591        variants: &BTreeMap<u32, Named<VariantFormat>>,
592    ) -> Result<()> {
593        for (index, variant) in variants {
594            self.output_variant(base, *index, &variant.name, &variant.value)?;
595        }
596        Ok(())
597    }
598
599    fn output_struct_or_variant_container(
600        &mut self,
601        variant_base: Option<&str>,
602        variant_index: Option<u32>,
603        name: &str,
604        fields: &[Named<Format>],
605    ) -> Result<()> {
606        // Beginning of class
607        writeln!(self.out)?;
608        if let Some(base) = variant_base {
609            self.output_comment(name)?;
610            writeln!(
611                self.out,
612                "public static final class {name} extends {base} {{"
613            )?;
614        } else {
615            self.output_comment(name)?;
616            writeln!(self.out, "public final class {name} {{")?;
617        }
618        let reserved_names = &["Builder"];
619        self.enter_class(name, reserved_names);
620        // Fields
621        for field in fields {
622            self.output_comment(&field.name)?;
623            writeln!(
624                self.out,
625                "public final {} {};",
626                self.quote_type(&field.value),
627                field.name
628            )?;
629        }
630        if !fields.is_empty() {
631            writeln!(self.out)?;
632        }
633        // Constructor.
634        writeln!(
635            self.out,
636            "public {}({}) {{",
637            name,
638            fields
639                .iter()
640                .map(|f| format!("{} {}", self.quote_type(&f.value), &f.name))
641                .collect::<Vec<_>>()
642                .join(", ")
643        )?;
644        self.out.indent();
645        for field in fields {
646            writeln!(
647                self.out,
648                "java.util.Objects.requireNonNull({0}, \"{0} must not be null\");",
649                &field.name
650            )?;
651        }
652        for field in fields {
653            writeln!(self.out, "this.{} = {};", &field.name, &field.name)?;
654        }
655        self.out.unindent();
656        writeln!(self.out, "}}")?;
657        // Serialize
658        if self.generator.config.serialization {
659            writeln!(
660                self.out,
661                "\npublic void serialize(com.novi.serde.Serializer serializer) throws com.novi.serde.SerializationError {{",
662            )?;
663            self.out.indent();
664            writeln!(self.out, "serializer.increase_container_depth();")?;
665            if let Some(index) = variant_index {
666                writeln!(self.out, "serializer.serialize_variant_index({index});")?;
667            }
668            for field in fields {
669                writeln!(
670                    self.out,
671                    "{}",
672                    self.quote_serialize_value(&field.name, &field.value)
673                )?;
674            }
675            writeln!(self.out, "serializer.decrease_container_depth();")?;
676            self.out.unindent();
677            writeln!(self.out, "}}")?;
678
679            if variant_index.is_none() {
680                for encoding in &self.generator.config.encodings {
681                    self.output_class_serialize_for_encoding(*encoding)?;
682                }
683            }
684        }
685        // Deserialize (struct) or Load (variant)
686        if self.generator.config.serialization {
687            if variant_index.is_none() {
688                writeln!(
689                    self.out,
690                    "\npublic static {name} deserialize(com.novi.serde.Deserializer deserializer) throws com.novi.serde.DeserializationError {{",
691                )?;
692            } else {
693                writeln!(
694                    self.out,
695                    "\nstatic {name} load(com.novi.serde.Deserializer deserializer) throws com.novi.serde.DeserializationError {{",
696                )?;
697            }
698            self.out.indent();
699            writeln!(self.out, "deserializer.increase_container_depth();")?;
700            writeln!(self.out, "Builder builder = new Builder();")?;
701            for field in fields {
702                writeln!(
703                    self.out,
704                    "builder.{} = {};",
705                    field.name,
706                    self.quote_deserialize(&field.value)
707                )?;
708            }
709            writeln!(self.out, "deserializer.decrease_container_depth();")?;
710            writeln!(self.out, "return builder.build();")?;
711            self.out.unindent();
712            writeln!(self.out, "}}")?;
713
714            if variant_index.is_none() {
715                for encoding in &self.generator.config.encodings {
716                    self.output_class_deserialize_for_encoding(name, *encoding)?;
717                }
718            }
719        }
720        // Equality
721        write!(self.out, "\npublic boolean equals(Object obj) {{")?;
722        self.out.indent();
723        writeln!(
724            self.out,
725            r#"
726if (this == obj) return true;
727if (obj == null) return false;
728if (getClass() != obj.getClass()) return false;
729{name} other = ({name}) obj;"#,
730        )?;
731        for field in fields {
732            writeln!(
733                self.out,
734                "if (!java.util.Objects.equals(this.{0}, other.{0})) {{ return false; }}",
735                &field.name,
736            )?;
737        }
738        writeln!(self.out, "return true;")?;
739        self.out.unindent();
740        writeln!(self.out, "}}")?;
741        // Hashing
742        writeln!(self.out, "\npublic int hashCode() {{")?;
743        self.out.indent();
744        writeln!(self.out, "int value = 7;",)?;
745        for field in fields {
746            writeln!(
747                self.out,
748                "value = 31 * value + (this.{0} != null ? this.{0}.hashCode() : 0);",
749                &field.name
750            )?;
751        }
752        writeln!(self.out, "return value;")?;
753        self.out.unindent();
754        writeln!(self.out, "}}")?;
755        // Builder
756        self.output_struct_or_variant_container_builder(name, fields)?;
757        // Custom code
758        self.output_custom_code()?;
759        // End of class
760        self.leave_class(reserved_names);
761        writeln!(self.out, "}}")
762    }
763
764    fn output_struct_or_variant_container_builder(
765        &mut self,
766        name: &str,
767        fields: &[Named<Format>],
768    ) -> Result<()> {
769        // Beginning of builder class
770        writeln!(self.out)?;
771        writeln!(self.out, "public static final class Builder {{")?;
772        let reserved_names = &[];
773        self.enter_class("Builder", reserved_names);
774        // Fields
775        for field in fields {
776            writeln!(
777                self.out,
778                "public {} {};",
779                self.quote_type(&field.value),
780                field.name
781            )?;
782        }
783        if !fields.is_empty() {
784            writeln!(self.out)?;
785        }
786        // Finalization
787        writeln!(
788            self.out,
789            r#"public {0} build() {{
790    return new {0}({1}
791    );
792}}"#,
793            name,
794            fields
795                .iter()
796                .map(|f| format!("\n        {}", f.name))
797                .collect::<Vec<_>>()
798                .join(",")
799        )?;
800        // Custom code
801        self.output_custom_code()?;
802        // End of class
803        self.leave_class(reserved_names);
804        writeln!(self.out, "}}")
805    }
806
807    fn output_enum_container(
808        &mut self,
809        name: &str,
810        variants: &BTreeMap<u32, Named<VariantFormat>>,
811    ) -> Result<()> {
812        writeln!(self.out)?;
813        self.output_comment(name)?;
814        writeln!(self.out, "public abstract class {name} {{")?;
815        let reserved_names = variants
816            .values()
817            .map(|v| v.name.as_str())
818            .collect::<Vec<_>>();
819        self.enter_class(name, &reserved_names);
820        if self.generator.config.serialization {
821            writeln!(
822                self.out,
823                "\nabstract public void serialize(com.novi.serde.Serializer serializer) throws com.novi.serde.SerializationError;"
824            )?;
825            write!(
826                self.out,
827                "\npublic static {name} deserialize(com.novi.serde.Deserializer deserializer) throws com.novi.serde.DeserializationError {{"
828            )?;
829            self.out.indent();
830            writeln!(
831                self.out,
832                r#"
833int index = deserializer.deserialize_variant_index();
834switch (index) {{"#,
835            )?;
836            self.out.indent();
837            for (index, variant) in variants {
838                writeln!(
839                    self.out,
840                    "case {}: return {}.load(deserializer);",
841                    index, variant.name,
842                )?;
843            }
844            writeln!(
845                self.out,
846                "default: throw new com.novi.serde.DeserializationError(\"Unknown variant index for {name}: \" + index);",
847            )?;
848            self.out.unindent();
849            writeln!(self.out, "}}")?;
850            self.out.unindent();
851            writeln!(self.out, "}}")?;
852
853            for encoding in &self.generator.config.encodings {
854                self.output_class_serialize_for_encoding(*encoding)?;
855                self.output_class_deserialize_for_encoding(name, *encoding)?;
856            }
857        }
858
859        self.output_variants(name, variants)?;
860        self.leave_class(&reserved_names);
861        writeln!(self.out, "}}\n")
862    }
863
864    fn output_class_serialize_for_encoding(&mut self, encoding: Encoding) -> Result<()> {
865        writeln!(
866            self.out,
867            r#"
868public byte[] {0}Serialize() throws com.novi.serde.SerializationError {{
869    com.novi.serde.Serializer serializer = new com.novi.{0}.{1}Serializer();
870    serialize(serializer);
871    return serializer.get_bytes();
872}}"#,
873            encoding.name(),
874            encoding.name().to_camel_case()
875        )
876    }
877
878    fn output_class_deserialize_for_encoding(
879        &mut self,
880        name: &str,
881        encoding: Encoding,
882    ) -> Result<()> {
883        writeln!(
884            self.out,
885            r#"
886public static {0} {1}Deserialize(byte[] input) throws com.novi.serde.DeserializationError {{
887    if (input == null) {{
888         throw new com.novi.serde.DeserializationError("Cannot deserialize null array");
889    }}
890    com.novi.serde.Deserializer deserializer = new com.novi.{1}.{2}Deserializer(input);
891    {0} value = deserialize(deserializer);
892    if (deserializer.get_buffer_offset() < input.length) {{
893         throw new com.novi.serde.DeserializationError("Some input bytes were not read");
894    }}
895    return value;
896}}"#,
897            name,
898            encoding.name(),
899            encoding.name().to_camel_case()
900        )
901    }
902
903    fn output_container(&mut self, name: &str, format: &ContainerFormat) -> Result<()> {
904        use ContainerFormat::*;
905        let fields = match format {
906            UnitStruct => Vec::new(),
907            NewTypeStruct(format) => vec![Named {
908                name: "value".to_string(),
909                value: format.as_ref().clone(),
910            }],
911            TupleStruct(formats) => formats
912                .iter()
913                .enumerate()
914                .map(|(i, f)| Named {
915                    name: format!("field{i}"),
916                    value: f.clone(),
917                })
918                .collect::<Vec<_>>(),
919            Struct(fields) => fields.clone(),
920            Enum(variants) => {
921                self.output_enum_container(name, variants)?;
922                return Ok(());
923            }
924        };
925        self.output_struct_or_variant_container(None, None, name, &fields)
926    }
927}
928
929/// Installer for generated source files in Java.
930pub struct Installer {
931    install_dir: PathBuf,
932}
933
934impl Installer {
935    pub fn new(install_dir: PathBuf) -> Self {
936        Installer { install_dir }
937    }
938
939    fn install_runtime(
940        &self,
941        source_dir: include_dir::Dir,
942        path: &str,
943    ) -> std::result::Result<(), Box<dyn std::error::Error>> {
944        let dir_path = self.install_dir.join(path);
945        std::fs::create_dir_all(&dir_path)?;
946        for entry in source_dir.files() {
947            let mut file = std::fs::File::create(dir_path.join(entry.path()))?;
948            file.write_all(entry.contents())?;
949        }
950        Ok(())
951    }
952}
953
954impl crate::SourceInstaller for Installer {
955    type Error = Box<dyn std::error::Error>;
956
957    fn install_module(
958        &self,
959        config: &CodeGeneratorConfig,
960        registry: &Registry,
961    ) -> std::result::Result<(), Self::Error> {
962        let generator = CodeGenerator::new(config);
963        generator.write_source_files(self.install_dir.clone(), registry)?;
964        Ok(())
965    }
966
967    fn install_serde_runtime(&self) -> std::result::Result<(), Self::Error> {
968        self.install_runtime(
969            include_directory!("runtime/java/com/novi/serde"),
970            "com/novi/serde",
971        )
972    }
973
974    fn install_bincode_runtime(&self) -> std::result::Result<(), Self::Error> {
975        self.install_runtime(
976            include_directory!("runtime/java/com/novi/bincode"),
977            "com/novi/bincode",
978        )
979    }
980
981    fn install_bcs_runtime(&self) -> std::result::Result<(), Self::Error> {
982        self.install_runtime(
983            include_directory!("runtime/java/com/novi/bcs"),
984            "com/novi/bcs",
985        )
986    }
987}