aconfig: Java codegen iteration 1

This change includes
    - refactor generated java code to generate
        - Flags.java to support the static API
	- FeatureFlagsImpl.java to support injection API
	- FeatureFlags.java interface

Bug: 279483816
Test: atest aconfig.test aconfig.test.java
Change-Id: If0d4baf317b9174635cd0fff3832ab7091ee52ed
diff --git a/tools/aconfig/src/codegen_java.rs b/tools/aconfig/src/codegen_java.rs
index cf025cb..54fa0dc 100644
--- a/tools/aconfig/src/codegen_java.rs
+++ b/tools/aconfig/src/codegen_java.rs
@@ -24,43 +24,59 @@
 use crate::codegen;
 use crate::commands::OutputFile;
 
-pub fn generate_java_code(cache: &Cache) -> Result<OutputFile> {
+pub fn generate_java_code(cache: &Cache) -> Result<Vec<OutputFile>> {
     let package = cache.package();
     let class_elements: Vec<ClassElement> =
         cache.iter().map(|item| create_class_element(package, item)).collect();
-    let readwrite = class_elements.iter().any(|item| item.readwrite);
-    let context = Context { package: package.to_string(), readwrite, class_elements };
+    let is_read_write = class_elements.iter().any(|item| item.is_read_write);
+    let context = Context { package_name: package.to_string(), is_read_write, class_elements };
+
+    let java_files = vec!["Flags.java", "FeatureFlagsImpl.java", "FeatureFlags.java"];
+
     let mut template = TinyTemplate::new();
-    template.add_template("java_code_gen", include_str!("../templates/java.template"))?;
-    let contents = template.render("java_code_gen", &context)?;
-    let mut path: PathBuf = package.split('.').collect();
-    // TODO: Allow customization of the java class name
-    path.push("Flags.java");
-    Ok(OutputFile { contents: contents.into(), path })
+    template.add_template("Flags.java", include_str!("../templates/Flags.java.template"))?;
+    template.add_template(
+        "FeatureFlagsImpl.java",
+        include_str!("../templates/FeatureFlagsImpl.java.template"),
+    )?;
+    template.add_template(
+        "FeatureFlags.java",
+        include_str!("../templates/FeatureFlags.java.template"),
+    )?;
+
+    let path: PathBuf = package.split('.').collect();
+    java_files
+        .iter()
+        .map(|file| {
+            Ok(OutputFile {
+                contents: template.render(file, &context)?.into(),
+                path: path.join(file),
+            })
+        })
+        .collect::<Result<Vec<OutputFile>>>()
 }
 
 #[derive(Serialize)]
 struct Context {
-    pub package: String,
-    pub readwrite: bool,
+    pub package_name: String,
+    pub is_read_write: bool,
     pub class_elements: Vec<ClassElement>,
 }
 
 #[derive(Serialize)]
 struct ClassElement {
-    pub method_name: String,
-    pub readwrite: bool,
     pub default_value: String,
     pub device_config_namespace: String,
     pub device_config_flag: String,
+    pub flag_name_constant_suffix: String,
+    pub is_read_write: bool,
+    pub method_name: String,
 }
 
 fn create_class_element(package: &str, item: &Item) -> ClassElement {
     let device_config_flag = codegen::create_device_config_ident(package, &item.name)
         .expect("values checked at cache creation time");
     ClassElement {
-        method_name: item.name.replace('-', "_"),
-        readwrite: item.permission == Permission::ReadWrite,
         default_value: if item.state == FlagState::Enabled {
             "true".to_string()
         } else {
@@ -68,78 +84,100 @@
         },
         device_config_namespace: item.namespace.clone(),
         device_config_flag,
+        flag_name_constant_suffix: item.name.to_ascii_uppercase(),
+        is_read_write: item.permission == Permission::ReadWrite,
+        method_name: item.name.clone(),
     }
 }
 
 #[cfg(test)]
 mod tests {
     use super::*;
-    use crate::aconfig::{FlagDeclaration, FlagValue};
-    use crate::cache::CacheBuilder;
-    use crate::commands::Source;
+    use std::collections::HashMap;
 
     #[test]
     fn test_generate_java_code() {
-        let package = "com.example";
-        let mut builder = CacheBuilder::new(package.to_string()).unwrap();
-        builder
-            .add_flag_declaration(
-                Source::File("test.txt".to_string()),
-                FlagDeclaration {
-                    name: "test".to_string(),
-                    namespace: "ns".to_string(),
-                    description: "buildtime enable".to_string(),
-                },
-            )
-            .unwrap()
-            .add_flag_declaration(
-                Source::File("test2.txt".to_string()),
-                FlagDeclaration {
-                    name: "test2".to_string(),
-                    namespace: "ns".to_string(),
-                    description: "runtime disable".to_string(),
-                },
-            )
-            .unwrap()
-            .add_flag_value(
-                Source::Memory,
-                FlagValue {
-                    package: package.to_string(),
-                    name: "test".to_string(),
-                    state: FlagState::Disabled,
-                    permission: Permission::ReadOnly,
-                },
-            )
-            .unwrap();
-        let cache = builder.build();
-        let expect_content = r#"package com.example;
-
-        import android.provider.DeviceConfig;
-
+        let cache = crate::test::create_cache();
+        let generated_files = generate_java_code(&cache).unwrap();
+        let expect_flags_content = r#"
+        package com.android.aconfig.test;
         public final class Flags {
-
-            public static boolean test() {
-                return false;
+            public static boolean disabled_ro() {
+                return FEATURE_FLAGS.disabled_ro();
             }
-
-            public static boolean test2() {
-                return DeviceConfig.getBoolean(
-                    "ns",
-                    "com.example.test2",
-                    false
-                );
+            public static boolean disabled_rw() {
+                return FEATURE_FLAGS.disabled_rw();
             }
+            public static boolean enabled_ro() {
+                return FEATURE_FLAGS.enabled_ro();
+            }
+            public static boolean enabled_rw() {
+                return FEATURE_FLAGS.enabled_rw();
+            }
+            private static FeatureFlags FEATURE_FLAGS = new FeatureFlagsImpl();
 
         }
         "#;
-        let file = generate_java_code(&cache).unwrap();
-        assert_eq!("com/example/Flags.java", file.path.to_str().unwrap());
-        assert_eq!(
-            None,
-            crate::test::first_significant_code_diff(
-                expect_content,
-                &String::from_utf8(file.contents).unwrap()
-            )
-        );
+        let expected_featureflagsimpl_content = r#"
+        package com.android.aconfig.test;
+        import android.provider.DeviceConfig;
+        public final class FeatureFlagsImpl implements FeatureFlags {
+            @Override
+            public boolean disabled_ro() {
+                return false;
+            }
+            @Override
+            public boolean disabled_rw() {
+                return DeviceConfig.getBoolean(
+                    "aconfig_test",
+                    "com.android.aconfig.test.disabled_rw",
+                    false
+                );
+            }
+            @Override
+            public boolean enabled_ro() {
+                return true;
+            }
+            @Override
+            public boolean enabled_rw() {
+                return DeviceConfig.getBoolean(
+                    "aconfig_test",
+                    "com.android.aconfig.test.enabled_rw",
+                    true
+                );
+            }
+        }
+        "#;
+        let expected_featureflags_content = r#"
+        package com.android.aconfig.test;
+        public interface FeatureFlags {
+            boolean disabled_ro();
+            boolean disabled_rw();
+            boolean enabled_ro();
+            boolean enabled_rw();
+        }
+        "#;
+        let mut file_set = HashMap::from([
+            ("com/android/aconfig/test/Flags.java", expect_flags_content),
+            ("com/android/aconfig/test/FeatureFlagsImpl.java", expected_featureflagsimpl_content),
+            ("com/android/aconfig/test/FeatureFlags.java", expected_featureflags_content),
+        ]);
+
+        for file in generated_files {
+            let file_path = file.path.to_str().unwrap();
+            assert!(file_set.contains_key(file_path), "Cannot find {}", file_path);
+            assert_eq!(
+                None,
+                crate::test::first_significant_code_diff(
+                    file_set.get(file_path).unwrap(),
+                    &String::from_utf8(file.contents.clone()).unwrap()
+                ),
+                "File {} content is not correct",
+                file_path
+            );
+            file_set.remove(file_path);
+        }
+
+        assert!(file_set.is_empty());
     }
 }