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());
}
}