union supports @JavaDerive(toString=true)

When annotated with @JavaDerive(toString=true), the compiler generates
toString() for union. And the string representation for a union is look
like a constructor call.

  @JavaDerive(toString=true)
  union Foo {
    int num;
    String str;
  }

  Union.n(42).toString() == "Union.n(42)"

Bug: 171271915
Test: aidl_unittests / aidl_integration_test
Change-Id: Ie1a631070456cc53f3f9f8069c2b16916a8b4723
diff --git a/aidl_language.cpp b/aidl_language.cpp
index 841e730..0fa2dd1 100644
--- a/aidl_language.cpp
+++ b/aidl_language.cpp
@@ -1150,7 +1150,7 @@
 
 std::set<AidlAnnotation::Type> AidlUnionDecl::GetSupportedAnnotations() const {
   return {AidlAnnotation::Type::VINTF_STABILITY, AidlAnnotation::Type::HIDE,
-          AidlAnnotation::Type::JAVA_PASSTHROUGH};
+          AidlAnnotation::Type::JAVA_PASSTHROUGH, AidlAnnotation::Type::JAVA_DERIVE};
 }
 
 void AidlUnionDecl::Dump(CodeWriter* writer) const {
diff --git a/aidl_to_java.cpp b/aidl_to_java.cpp
index 9fa691a..ba00dee 100644
--- a/aidl_to_java.cpp
+++ b/aidl_to_java.cpp
@@ -768,8 +768,6 @@
 
 void ToStringFor(const CodeGeneratorContext& c) {
   if (c.type.IsArray()) {
-    // Arrays can be null
-    c.writer << c.var << " == null ? \"null\" : ";
     c.writer << "java.util.Arrays.toString(" << c.var << ")";
     return;
   }
diff --git a/aidl_unittest.cpp b/aidl_unittest.cpp
index a734b77..7803521 100644
--- a/aidl_unittest.cpp
+++ b/aidl_unittest.cpp
@@ -580,24 +580,48 @@
   EXPECT_EQ(expected_stderr, GetCapturedStderr());
 }
 
-TEST_F(AidlTest, ParsesJavaDeriveAnnotation) {
-  io_delegate_.SetFileContents("a/IFoo.aidl", R"(package a;
-    @JavaDerive(toString=true) parcelable IFoo { int a; float b; })");
-  Options java_options = Options::From("aidl --lang=java -o out a/IFoo.aidl");
+TEST_F(AidlTest, ParcelableSupportJavaDeriveToString) {
+  io_delegate_.SetFileContents("a/Foo.aidl", R"(package a;
+    @JavaDerive(toString=true) parcelable Foo { int a; float b; })");
+  Options java_options = Options::From("aidl --lang=java -o out a/Foo.aidl");
   EXPECT_EQ(0, ::android::aidl::compile_aidl(java_options, io_delegate_));
 
   string java_out;
-  EXPECT_TRUE(io_delegate_.GetWrittenContents("out/a/IFoo.java", &java_out));
+  EXPECT_TRUE(io_delegate_.GetWrittenContents("out/a/Foo.java", &java_out));
   EXPECT_THAT(java_out, testing::HasSubstr("public String toString() {"));
 
   // Other backends shouldn't be bothered
-  Options cpp_options = Options::From("aidl --lang=cpp -o out -h out a/IFoo.aidl");
+  Options cpp_options = Options::From("aidl --lang=cpp -o out -h out a/Foo.aidl");
   EXPECT_EQ(0, ::android::aidl::compile_aidl(cpp_options, io_delegate_));
 
-  Options ndk_options = Options::From("aidl --lang=ndk -o out -h out a/IFoo.aidl");
+  Options ndk_options = Options::From("aidl --lang=ndk -o out -h out a/Foo.aidl");
   EXPECT_EQ(0, ::android::aidl::compile_aidl(ndk_options, io_delegate_));
 }
 
+TEST_F(AidlTest, UnionSupportJavaDeriveToString) {
+  io_delegate_.SetFileContents("a/Foo.aidl", R"(package a;
+    @JavaDerive(toString=true) union Foo { int a; int[] b; })");
+  CaptureStderr();
+  Options java_options = Options::From("aidl --lang=java -o out a/Foo.aidl");
+  EXPECT_EQ(0, ::android::aidl::compile_aidl(java_options, io_delegate_));
+  EXPECT_EQ("", GetCapturedStderr());
+
+  const string expected_to_string_method = R"--(
+  @Override
+  public String toString() {
+    switch (_tag) {
+    case a: return "a.Foo.a(" + (getA()) + ")";
+    case b: return "a.Foo.b(" + (java.util.Arrays.toString(getB())) + ")";
+    }
+    throw new IllegalStateException("unknown field: " + _tag);
+  }
+)--";
+
+  string java_out;
+  EXPECT_TRUE(io_delegate_.GetWrittenContents("out/a/Foo.java", &java_out));
+  EXPECT_THAT(java_out, testing::HasSubstr(expected_to_string_method));
+}
+
 TEST_F(AidlTest, RejectsJavaDeriveAnnotation) {
   {
     io_delegate_.SetFileContents("a/Foo.aidl",
diff --git a/generate_java.cpp b/generate_java.cpp
index 4d2d4f3..069a20d 100644
--- a/generate_java.cpp
+++ b/generate_java.cpp
@@ -231,6 +231,30 @@
   out << "}\n";
 }
 
+void GenerateToString(CodeWriter& out, const AidlUnionDecl& parcel,
+                      const AidlTypenames& typenames) {
+  out << "@Override\n";
+  out << "public String toString() {\n";
+  out.Indent();
+  out << "switch (_tag) {\n";
+  for (const auto& field : parcel.GetFields()) {
+    CodeGeneratorContext ctx{
+        .writer = out,
+        .typenames = typenames,
+        .type = field->GetType(),
+        .var = getter_name(*field) + "()",
+    };
+    out << "case " << field->GetName() << ": return \"" << parcel.GetCanonicalName() << "."
+        << field->GetName() << "(\" + (";
+    ToStringFor(ctx);
+    out << ") + \")\";\n";
+  }
+  out << "}\n";
+  out << "throw new IllegalStateException(\"unknown field: \" + _tag);\n";
+  out.Dedent();
+  out << "}\n";
+}
+
 template <typename ParcelableType>
 void GenerateDerivedMethods(CodeWriter& out, const ParcelableType& parcel,
                             const AidlTypenames& typenames) {
@@ -798,6 +822,7 @@
 
   GenerateParcelableDescribeContents(out, *decl, typenames);
   out << "\n";
+  GenerateDerivedMethods(out, *decl, typenames);
 
   // helper: _assertTag
   out << "private void _assertTag(" + tag_type + " tag) {\n";