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
Merged-In: I043c1320821c1821faede60fa8608ff7cc79bd6c
Change-Id: I043c1320821c1821faede60fa8608ff7cc79bd6c
(cherry picked from commit 9034500d0796fd0ba4ab6598af3e477a642451c4)
diff --git a/aidl_checkapi.cpp b/aidl_checkapi.cpp
index cf9f59b..b2a884c 100644
--- a/aidl_checkapi.cpp
+++ b/aidl_checkapi.cpp
@@ -48,7 +48,7 @@
   // - a new implementation might start accepting null values (add @nullable)
   static const set<std::string> kIgnoreAnnotations{
       "nullable",
-      "JavaDebug",
+      "JavaDerive",
   };
   set<AidlAnnotation> annotations;
   for (const AidlAnnotation& annotation : node.GetAnnotations()) {
diff --git a/aidl_language.cpp b/aidl_language.cpp
index 0887a25..be1221d 100644
--- a/aidl_language.cpp
+++ b/aidl_language.cpp
@@ -131,7 +131,7 @@
 static const string kJavaStableParcelable("JavaOnlyStableParcelable");
 static const string kHide("Hide");
 static const string kBacking("Backing");
-static const string kJavaDebug("JavaDebug");
+static const string kJavaDerive("JavaDerive");
 
 static const std::map<string, std::map<std::string, std::string>> kAnnotationParameters{
     {kNullable, {}},
@@ -146,7 +146,7 @@
     {kJavaStableParcelable, {}},
     {kHide, {}},
     {kBacking, {{"type", "String"}}},
-    {kJavaDebug, {}}};
+    {kJavaDerive, {{"toString", "boolean"}}}};
 
 AidlAnnotation* AidlAnnotation::Parse(
     const AidlLocation& location, const string& name,
@@ -310,8 +310,8 @@
   return HasAnnotation(annotations_, kHide);
 }
 
-bool AidlAnnotatable::IsJavaDebug() const {
-  return GetAnnotation(annotations_, kJavaDebug);
+const AidlAnnotation* AidlAnnotatable::JavaDerive() const {
+  return GetAnnotation(annotations_, kJavaDerive);
 }
 
 void AidlAnnotatable::DumpAnnotations(CodeWriter* writer) const {
diff --git a/aidl_language.h b/aidl_language.h
index c2d6a1e..920970f 100644
--- a/aidl_language.h
+++ b/aidl_language.h
@@ -244,7 +244,7 @@
   bool IsVintfStability() const;
   bool IsStableApiParcelable(Options::Language lang) const;
   bool IsHide() const;
-  bool IsJavaDebug() const;
+  const AidlAnnotation* JavaDerive() const;
 
   void DumpAnnotations(CodeWriter* writer) const;
 
diff --git a/aidl_unittest.cpp b/aidl_unittest.cpp
index a675628..fea3419 100644
--- a/aidl_unittest.cpp
+++ b/aidl_unittest.cpp
@@ -398,9 +398,9 @@
   EXPECT_NE(nullptr, Parse("a/IFoo.aidl", oneway_method, typenames_, Options::Language::JAVA));
 }
 
-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_));
 
diff --git a/code_writer.h b/code_writer.h
index d890d52..08516bf 100644
--- a/code_writer.h
+++ b/code_writer.h
@@ -39,6 +39,14 @@
   // The buffer gets updated only after Close() is called or the CodeWriter
   // is deleted -- much like a real file.
   static CodeWriterPtr ForString(std::string* buf);
+  // Run a Code Generater (which accepts CodeWriter& as a first parameter)
+  // and return a result as a string.
+  template <typename... Args>
+  static std::string RunWith(void (*gen)(CodeWriter&, Args...), Args&&... args) {
+    std::string code;
+    (*gen)(*ForString(&code), std::forward<Args>(args)...);
+    return code;
+  }
   // Write a formatted string to this writer in the usual printf sense.
   // Returns false on error.
   virtual bool Write(const char* format, ...) __attribute__((format(printf, 2, 3)));
diff --git a/generate_java.cpp b/generate_java.cpp
index 1065a55..f120ac5 100644
--- a/generate_java.cpp
+++ b/generate_java.cpp
@@ -33,6 +33,49 @@
 using ::android::aidl::java::Variable;
 using std::string;
 
+namespace {
+using android::aidl::java::CodeGeneratorContext;
+using android::aidl::java::ConstantValueDecorator;
+
+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 {
 namespace aidl {
 namespace java {
@@ -227,31 +270,8 @@
 
   parcel_class->elements.push_back(read_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 = std::make_shared<Method>();
   describe_contents_method->modifiers = PUBLIC | OVERRIDE;
diff --git a/tests/android/aidl/tests/GenericStructuredParcelable.aidl b/tests/android/aidl/tests/GenericStructuredParcelable.aidl
new file mode 100644
index 0000000..af93692
--- /dev/null
+++ b/tests/android/aidl/tests/GenericStructuredParcelable.aidl
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.aidl.tests;
+
+@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 cdb8051..2ad414c 100644
--- a/tests/android/aidl/tests/ParcelableForToString.aidl
+++ b/tests/android/aidl/tests/ParcelableForToString.aidl
@@ -19,7 +19,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 50a748f..5e316ee 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)
 parcelable StructuredParcelable {
     int[] shouldContainThreeFs;
     int f;