migrate @JavaDebug to @JavaDerive(toString=true)

This will make it easier to add a new sythetic methods in the future.

Bug: 171271915
Test: aidl_unittests / aidl_integration_test
Change-Id: I043c1320821c1821faede60fa8608ff7cc79bd6c
diff --git a/aidl_checkapi.cpp b/aidl_checkapi.cpp
index a539589..c4229b6 100644
--- a/aidl_checkapi.cpp
+++ b/aidl_checkapi.cpp
@@ -51,7 +51,7 @@
   // - a new implementation might start accepting null values (add @nullable)
   static const set<AidlAnnotation::Type> kIgnoreAnnotations{
       AidlAnnotation::Type::NULLABLE,
-      AidlAnnotation::Type::JAVA_DEBUG,
+      AidlAnnotation::Type::JAVA_DERIVE,
       AidlAnnotation::Type::JAVA_ONLY_IMMUTABLE,
   };
   set<AidlAnnotation> annotations;
diff --git a/aidl_language.cpp b/aidl_language.cpp
index 5162960..841e730 100644
--- a/aidl_language.cpp
+++ b/aidl_language.cpp
@@ -132,7 +132,7 @@
       {AidlAnnotation::Type::HIDE, "Hide", {}, false},
       {AidlAnnotation::Type::BACKING, "Backing", {{"type", "String"}}, false},
       {AidlAnnotation::Type::JAVA_PASSTHROUGH, "JavaPassthrough", {{"annotation", "String"}}, true},
-      {AidlAnnotation::Type::JAVA_DEBUG, "JavaDebug", {}, false},
+      {AidlAnnotation::Type::JAVA_DERIVE, "JavaDerive", {{"toString", "boolean"}}, false},
       {AidlAnnotation::Type::JAVA_ONLY_IMMUTABLE, "JavaOnlyImmutable", {}, false},
       {AidlAnnotation::Type::FIXED_SIZE, "FixedSize", {}, false},
       {AidlAnnotation::Type::DESCRIPTOR, "Descriptor", {{"value", "String"}}, false},
@@ -338,8 +338,8 @@
   return GetAnnotation(annotations_, AidlAnnotation::Type::HIDE);
 }
 
-bool AidlAnnotatable::IsJavaDebug() const {
-  return GetAnnotation(annotations_, AidlAnnotation::Type::JAVA_DEBUG);
+const AidlAnnotation* AidlAnnotatable::JavaDerive() const {
+  return GetAnnotation(annotations_, AidlAnnotation::Type::JAVA_DERIVE);
 }
 
 std::string AidlAnnotatable::GetDescriptor() const {
@@ -886,7 +886,7 @@
           AidlAnnotation::Type::UNSUPPORTED_APP_USAGE,
           AidlAnnotation::Type::HIDE,
           AidlAnnotation::Type::JAVA_PASSTHROUGH,
-          AidlAnnotation::Type::JAVA_DEBUG,
+          AidlAnnotation::Type::JAVA_DERIVE,
           AidlAnnotation::Type::JAVA_ONLY_IMMUTABLE,
           AidlAnnotation::Type::FIXED_SIZE,
           AidlAnnotation::Type::RUST_DERIVE};
diff --git a/aidl_language.h b/aidl_language.h
index 1e8b600..73f2d66 100644
--- a/aidl_language.h
+++ b/aidl_language.h
@@ -165,7 +165,7 @@
     NULLABLE,
     UTF8_IN_CPP,
     JAVA_PASSTHROUGH,
-    JAVA_DEBUG,
+    JAVA_DERIVE,
     JAVA_ONLY_IMMUTABLE,
     FIXED_SIZE,
     DESCRIPTOR,
@@ -240,7 +240,7 @@
   bool IsFixedSize() const;
   bool IsStableApiParcelable(Options::Language lang) const;
   bool IsHide() const;
-  bool IsJavaDebug() const;
+  const AidlAnnotation* JavaDerive() const;
   std::string GetDescriptor() const;
 
   void DumpAnnotations(CodeWriter* writer) const;
diff --git a/aidl_unittest.cpp b/aidl_unittest.cpp
index ab98eef..a734b77 100644
--- a/aidl_unittest.cpp
+++ b/aidl_unittest.cpp
@@ -459,7 +459,7 @@
   const string method = "package a; @nullable parcelable IFoo { String a; String b; }";
   const string expected_stderr =
       "ERROR: a/IFoo.aidl:1.32-37: 'nullable' is not a supported annotation for this node. "
-      "It must be one of: Hide, UnsupportedAppUsage, VintfStability, JavaPassthrough, JavaDebug, "
+      "It must be one of: Hide, UnsupportedAppUsage, VintfStability, JavaPassthrough, JavaDerive, "
       "JavaOnlyImmutable, FixedSize, RustDerive\n";
   CaptureStderr();
   EXPECT_EQ(nullptr, Parse("a/IFoo.aidl", method, typenames_, GetLanguage(), &error));
@@ -580,9 +580,9 @@
   EXPECT_EQ(expected_stderr, GetCapturedStderr());
 }
 
-TEST_F(AidlTest, ParsesJavaDebugAnnotation) {
+TEST_F(AidlTest, ParsesJavaDeriveAnnotation) {
   io_delegate_.SetFileContents("a/IFoo.aidl", R"(package a;
-    @JavaDebug parcelable IFoo { int a; float b; })");
+    @JavaDerive(toString=true) parcelable IFoo { int a; float b; })");
   Options java_options = Options::From("aidl --lang=java -o out a/IFoo.aidl");
   EXPECT_EQ(0, ::android::aidl::compile_aidl(java_options, io_delegate_));
 
@@ -598,26 +598,37 @@
   EXPECT_EQ(0, ::android::aidl::compile_aidl(ndk_options, io_delegate_));
 }
 
-TEST_F(AidlTest, RejectsJavaDebugAnnotation) {
+TEST_F(AidlTest, RejectsJavaDeriveAnnotation) {
   {
-    io_delegate_.SetFileContents("a/IFoo.aidl", "package a; @JavaDebug interface IFoo{}");
+    io_delegate_.SetFileContents("a/Foo.aidl",
+                                 "package a; @JavaDerive(blah=true) parcelable Foo{}");
+    Options java_options = Options::From("aidl --lang=java -o out a/Foo.aidl");
+    CaptureStderr();
+    EXPECT_NE(0, ::android::aidl::compile_aidl(java_options, io_delegate_));
+    const std::string expected_stderr =
+        "ERROR: a/Foo.aidl:1.11-34: Parameter blah not supported for annotation JavaDerive.";
+    EXPECT_THAT(GetCapturedStderr(), testing::HasSubstr(expected_stderr));
+  }
+
+  {
+    io_delegate_.SetFileContents("a/IFoo.aidl", "package a; @JavaDerive interface IFoo{}");
     Options java_options = Options::From("aidl --lang=java -o out a/IFoo.aidl");
     CaptureStderr();
     EXPECT_NE(0, ::android::aidl::compile_aidl(java_options, io_delegate_));
     const std::string expected_stderr =
-        "ERROR: a/IFoo.aidl:1.22-32: 'JavaDebug' is not a supported annotation for this node. "
+        "ERROR: a/IFoo.aidl:1.23-33: 'JavaDerive' is not a supported annotation for this node. "
         "It must be one of: Hide, UnsupportedAppUsage, VintfStability, JavaPassthrough, "
         "Descriptor\n";
     EXPECT_EQ(expected_stderr, GetCapturedStderr());
   }
 
   {
-    io_delegate_.SetFileContents("a/IFoo.aidl", "package a; @JavaDebug enum IFoo { A=1, }");
+    io_delegate_.SetFileContents("a/IFoo.aidl", "package a; @JavaDerive enum IFoo { A=1, }");
     Options java_options = Options::From("aidl --lang=java -o out a/IFoo.aidl");
     CaptureStderr();
     EXPECT_NE(0, ::android::aidl::compile_aidl(java_options, io_delegate_));
     const std::string expected_stderr =
-        "ERROR: a/IFoo.aidl:1.27-32: 'JavaDebug' is not a supported annotation for this node. "
+        "ERROR: a/IFoo.aidl:1.28-33: 'JavaDerive' is not a supported annotation for this node. "
         "It must be one of: Backing, Hide, VintfStability, JavaPassthrough\n";
     EXPECT_EQ(expected_stderr, GetCapturedStderr());
   }
diff --git a/generate_java.cpp b/generate_java.cpp
index 9b4191e..4d2d4f3 100644
--- a/generate_java.cpp
+++ b/generate_java.cpp
@@ -41,6 +41,9 @@
 using std::vector;
 
 namespace {
+using android::aidl::java::CodeGeneratorContext;
+using android::aidl::java::ConstantValueDecorator;
+
 // join two non-empty strings according to `camelCase` naming.
 inline string camelcase_join(const string& a, const string& b, const AidlNode& context) {
   AIDL_FATAL_IF(b.size() <= 0 || a.size() <= 0, context) << "Name cannot be empty.";
@@ -204,6 +207,44 @@
     GenerateDescribeContentsHelper(out, describers);
   }
 }
+
+void GenerateToString(CodeWriter& out, const AidlStructuredParcelable& parcel,
+                      const AidlTypenames& typenames) {
+  out << "@Override\n";
+  out << "public String toString() {\n";
+  out.Indent();
+  out << "java.util.StringJoiner _aidl_sj = new java.util.StringJoiner(";
+  out << "\", \", \"{\", \"}\");\n";
+  for (const auto& field : parcel.GetFields()) {
+    CodeGeneratorContext ctx{
+        .writer = out,
+        .typenames = typenames,
+        .type = field->GetType(),
+        .var = field->GetName(),
+    };
+    out << "_aidl_sj.add(\"" << field->GetName() << ": \" + (";
+    ToStringFor(ctx);
+    out << "));\n";
+  }
+  out << "return \"" << parcel.GetCanonicalName() << "\" + _aidl_sj.toString()  ;\n";
+  out.Dedent();
+  out << "}\n";
+}
+
+template <typename ParcelableType>
+void GenerateDerivedMethods(CodeWriter& out, const ParcelableType& parcel,
+                            const AidlTypenames& typenames) {
+  if (auto java_derive = parcel.JavaDerive(); java_derive) {
+    auto synthetic_methods = java_derive->AnnotationParams(ConstantValueDecorator);
+    for (const auto& [method_name, generate] : synthetic_methods) {
+      if (generate == "true") {
+        if (method_name == "toString") {
+          GenerateToString(out, parcel, typenames);
+        }
+      }
+    }
+  }
+}
 }  // namespace
 
 namespace android {
@@ -537,31 +578,8 @@
 
   parcel_class->elements.push_back(read_or_create_method);
 
-  if (parcel->IsJavaDebug()) {
-    out.str("");
-    out << "@Override\n";
-    out << "public String toString() {\n";
-    out << "  java.util.StringJoiner _aidl_sj = new java.util.StringJoiner(";
-    out << "\", \", \"{\", \"}\");\n";
-    for (const auto& field : parcel->GetFields()) {
-      std::string code;
-      CodeWriterPtr writer = CodeWriter::ForString(&code);
-      CodeGeneratorContext context{
-          .writer = *(writer.get()),
-          .typenames = typenames,
-          .type = field->GetType(),
-          .parcel = parcel_variable->name,
-          .var = field->GetName(),
-          .is_classloader_created = &is_classloader_created,
-      };
-      ToStringFor(context);
-      writer->Close();
-      out << "  _aidl_sj.add(\"" << field->GetName() << ": \" + (" << code << "));\n";
-    }
-    out << "  return \"" << parcel->GetCanonicalName() << "\" + _aidl_sj.toString()  ;\n";
-    out << "}\n";
-    parcel_class->elements.push_back(std::make_shared<LiteralClassElement>(out.str()));
-  }
+  auto method = CodeWriter::RunWith(GenerateDerivedMethods, *parcel, typenames);
+  parcel_class->elements.push_back(std::make_shared<LiteralClassElement>(method));
 
   auto describe_contents_method =
       CodeWriter::RunWith(GenerateParcelableDescribeContents, *parcel, typenames);
diff --git a/tests/android/aidl/tests/GenericStructuredParcelable.aidl b/tests/android/aidl/tests/GenericStructuredParcelable.aidl
index 6e71be5..af93692 100644
--- a/tests/android/aidl/tests/GenericStructuredParcelable.aidl
+++ b/tests/android/aidl/tests/GenericStructuredParcelable.aidl
@@ -16,7 +16,7 @@
 
 package android.aidl.tests;
 
-@JavaDebug
+@JavaDerive(toString=true)
 parcelable GenericStructuredParcelable<T, U, B> {
   int a;
   int b;
diff --git a/tests/android/aidl/tests/OtherParcelableForToString.aidl b/tests/android/aidl/tests/OtherParcelableForToString.aidl
index 8bff893..f042ab5 100644
--- a/tests/android/aidl/tests/OtherParcelableForToString.aidl
+++ b/tests/android/aidl/tests/OtherParcelableForToString.aidl
@@ -16,7 +16,7 @@
 
 package android.aidl.tests;
 
-@JavaDebug
+@JavaDerive(toString=true)
 parcelable OtherParcelableForToString {
     String field;
 }
diff --git a/tests/android/aidl/tests/ParcelableForToString.aidl b/tests/android/aidl/tests/ParcelableForToString.aidl
index 3647a34..cc9440b 100644
--- a/tests/android/aidl/tests/ParcelableForToString.aidl
+++ b/tests/android/aidl/tests/ParcelableForToString.aidl
@@ -21,7 +21,7 @@
 import android.aidl.tests.OtherParcelableForToString;
 import android.aidl.tests.IntEnum;
 
-@JavaDebug
+@JavaDerive(toString=true)
 parcelable ParcelableForToString {
     int intValue;
     int[] intArray;
diff --git a/tests/android/aidl/tests/StructuredParcelable.aidl b/tests/android/aidl/tests/StructuredParcelable.aidl
index 514d728..a50bb15 100644
--- a/tests/android/aidl/tests/StructuredParcelable.aidl
+++ b/tests/android/aidl/tests/StructuredParcelable.aidl
@@ -21,7 +21,7 @@
 import android.aidl.tests.LongEnum;
 import android.aidl.tests.ConstantExpressionEnum;
 
-@JavaDebug
+@JavaDerive(toString=true)
 @RustDerive(Clone=true, PartialEq=true)
 parcelable StructuredParcelable {
     int[] shouldContainThreeFs;