/*
 * Copyright (C) 2018, 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.
 */

#include "aidl_to_java.h"
#include "aidl_language.h"
#include "aidl_typenames.h"
#include "logging.h"

#include <android-base/strings.h>

#include <cassert>
#include <functional>
#include <iostream>
#include <map>
#include <string>
#include <vector>

namespace android {
namespace aidl {
namespace java {

using android::base::Join;

using std::endl;
using std::function;
using std::map;
using std::string;
using std::vector;

std::string ConstantValueDecorator(const AidlTypeSpecifier& /*type*/,
                                   const std::string& raw_value) {
  // no difference
  return raw_value;
};

const string& JavaNameOf(const AidlTypeSpecifier& aidl) {
  CHECK(aidl.IsResolved()) << aidl.ToString();

  // map from AIDL built-in type name to the corresponding Java type name
  static map<string, string> m = {
      {"void", "void"},
      {"boolean", "boolean"},
      {"byte", "byte"},
      {"char", "char"},
      {"int", "int"},
      {"long", "long"},
      {"float", "float"},
      {"double", "double"},
      {"String", "java.lang.String"},
      {"List", "java.util.List"},
      {"Map", "java.util.Map"},
      {"IBinder", "android.os.IBinder"},
      {"FileDescriptor", "java.io.FileDescriptor"},
      {"CharSequence", "java.lang.CharSequence"},
      {"ParcelFileDescriptor", "android.os.ParcelFileDescriptor"},
  };
  const string& aidl_name = aidl.GetName();
  if (m.find(aidl_name) != m.end()) {
    CHECK(AidlTypenames::IsBuiltinTypename(aidl_name));
    return m[aidl_name];
  } else {
    // 'foo.bar.IFoo' in AIDL maps to 'foo.bar.IFoo' in Java
    return aidl_name;
  }
}

string JavaSignatureOf(const AidlTypeSpecifier& aidl) {
  string ret = JavaNameOf(aidl);
  if (aidl.IsGeneric()) {
    vector<string> arg_names;
    for (const auto& ta : aidl.GetTypeParameters()) {
      arg_names.emplace_back(JavaSignatureOf(*ta));
    }
    ret += "<" + Join(arg_names, ",") + ">";
  }
  if (aidl.IsArray()) {
    ret += "[]";
  }
  return ret;
}

string DefaultJavaValueOf(const AidlTypeSpecifier& aidl) {
  static map<string, string> m = {
      {"boolean", "false"}, {"byte", "0"},     {"char", R"('\u0000')"}, {"int", "0"},
      {"long", "0L"},       {"float", "0.0f"}, {"double", "0.0d"},
  };
  const string& name = aidl.GetName();
  assert(name != "void");

  if (!aidl.IsArray() && m.find(name) != m.end()) {
    CHECK(AidlTypenames::IsBuiltinTypename(name));
    return m[name];
  } else {
    return "null";
  }
}

// These are supported by AIDL syntax, but are unsupported by the AIDL compiler
static bool IsMarshallingUnsupportedFor(const AidlTypeSpecifier& aidl,
                                        const AidlTypenames& typenames) {
  const string name = aidl.GetName();

  // List<T> is support only for String, Binder, ParcelFileDescriptor and Parcelable.
  if (name == "List" && aidl.IsGeneric()) {
    const string& contained_type = aidl.GetTypeParameters().at(0)->GetName();
    if (AidlTypenames::IsBuiltinTypename(contained_type)) {
      if (contained_type != "String" && contained_type != "IBinder" &&
          contained_type != "ParcelFileDescriptor") {
        return true;
      }
    } else {
      const AidlDefinedType* t = typenames.TryGetDefinedType(contained_type);
      if (t != nullptr && t->AsInterface() != nullptr) {
        return true;
      }
    }
  }

  // List[], Map[], CharSequence[] are not supported.
  if (AidlTypenames::IsBuiltinTypename(name) && aidl.IsArray()) {
    if (name == "List" || name == "Map" || name == "CharSequence") {
      return true;
    }
  }

  // T[] is not supported for interfaces
  const AidlDefinedType* t = typenames.TryGetDefinedType(name);
  if (aidl.IsArray() && t != nullptr && t->AsInterface() != nullptr) {
    return true;
  }

  return false;
}

static bool EnsureCodegenIsSupported(const CodeGeneratorContext& c) {
  if (IsMarshallingUnsupportedFor(c.type, c.typenames)) {
    AIDL_ERROR(c.type) << c.type.ToString() << "' is not yet supported.";
    return false;
  }
  return true;
}

static string GetFlagFor(const CodeGeneratorContext& c) {
  if (c.is_return_value) {
    return "android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE";
  } else {
    return "0";
  }
}

bool WriteToParcelFor(const CodeGeneratorContext& c) {
  if (!EnsureCodegenIsSupported(c)) {
    return false;
  }
  static map<string, function<void(const CodeGeneratorContext&)>> method_map{
      {"boolean",
       [](const CodeGeneratorContext& c) {
         c.writer << c.parcel << ".writeInt(((" << c.var << ")?(1):(0)));\n";
       }},
      {"boolean[]",
       [](const CodeGeneratorContext& c) {
         c.writer << c.parcel << ".writeBooleanArray(" << c.var << ");\n";
       }},
      {"byte",
       [](const CodeGeneratorContext& c) {
         c.writer << c.parcel << ".writeByte(" << c.var << ");\n";
       }},
      {"byte[]",
       [](const CodeGeneratorContext& c) {
         c.writer << c.parcel << ".writeByteArray(" << c.var << ");\n";
       }},
      {"char",
       [](const CodeGeneratorContext& c) {
         c.writer << c.parcel << ".writeInt(((int)" << c.var << "));\n";
       }},
      {"char[]",
       [](const CodeGeneratorContext& c) {
         c.writer << c.parcel << ".writeCharArray(" << c.var << ");\n";
       }},
      {"int",
       [](const CodeGeneratorContext& c) {
         c.writer << c.parcel << ".writeInt(" << c.var << ");\n";
       }},
      {"int[]",
       [](const CodeGeneratorContext& c) {
         c.writer << c.parcel << ".writeIntArray(" << c.var << ");\n";
       }},
      {"long",
       [](const CodeGeneratorContext& c) {
         c.writer << c.parcel << ".writeLong(" << c.var << ");\n";
       }},
      {"long[]",
       [](const CodeGeneratorContext& c) {
         c.writer << c.parcel << ".writeLongArray(" << c.var << ");\n";
       }},
      {"float",
       [](const CodeGeneratorContext& c) {
         c.writer << c.parcel << ".writeFloat(" << c.var << ");\n";
       }},
      {"float[]",
       [](const CodeGeneratorContext& c) {
         c.writer << c.parcel << ".writeFloatArray(" << c.var << ");\n";
       }},
      {"double",
       [](const CodeGeneratorContext& c) {
         c.writer << c.parcel << ".writeDouble(" << c.var << ");\n";
       }},
      {"double[]",
       [](const CodeGeneratorContext& c) {
         c.writer << c.parcel << ".writeDoubleArray(" << c.var << ");\n";
       }},
      {"String",
       [](const CodeGeneratorContext& c) {
         c.writer << c.parcel << ".writeString(" << c.var << ");\n";
       }},
      {"String[]",
       [](const CodeGeneratorContext& c) {
         c.writer << c.parcel << ".writeStringArray(" << c.var << ");\n";
       }},
      {"List",
       [](const CodeGeneratorContext& c) {
         if (c.type.IsGeneric()) {
           const string& contained_type = c.type.GetTypeParameters().at(0)->GetName();
           if (AidlTypenames::IsBuiltinTypename(contained_type)) {
             if (contained_type == "String") {
               c.writer << c.parcel << ".writeStringList(" << c.var << ");\n";
             } else if (contained_type == "IBinder") {
               c.writer << c.parcel << ".writeBinderList(" << c.var << ");\n";
             }
           } else {
             const AidlDefinedType* t = c.typenames.TryGetDefinedType(contained_type);
             CHECK(t != nullptr) << "Unknown type: " << contained_type << endl;
             if (t->AsParcelable() != nullptr) {
               c.writer << c.parcel << ".writeTypedList(" << c.var << ");\n";
             }
           }
         } else {
           c.writer << c.parcel << ".writeList(" << c.var << ");\n";
         }
       }},
      {"Map",
       [](const CodeGeneratorContext& c) {
         c.writer << c.parcel << ".writeMap(" << c.var << ");\n";
       }},
      {"IBinder",
       [](const CodeGeneratorContext& c) {
         c.writer << c.parcel << ".writeStrongBinder(" << c.var << ");\n";
       }},
      {"IBinder[]",
       [](const CodeGeneratorContext& c) {
         c.writer << c.parcel << ".writeBinderArray(" << c.var << ");\n";
       }},
      {"FileDescriptor",
       [](const CodeGeneratorContext& c) {
         c.writer << c.parcel << ".writeRawFileDescriptor(" << c.var << ");\n";
       }},
      {"FileDescriptor[]",
       [](const CodeGeneratorContext& c) {
         c.writer << c.parcel << ".writeRawFileDescriptorArray(" << c.var << ");\n";
       }},
      {"ParcelFileDescriptor",
       [](const CodeGeneratorContext& c) {
         // This is same as writeTypedObject which was introduced with SDK 23.
         // Keeping below code so that the generated code is buildable with older SDK.
         c.writer << "if ((" << c.var << "!=null)) {\n";
         c.writer.Indent();
         c.writer << c.parcel << ".writeInt(1);\n";
         c.writer << c.var << ".writeToParcel(" << c.parcel << ", " << GetFlagFor(c) << ");\n";
         c.writer.Dedent();
         c.writer << "}\n";
         c.writer << "else {\n";
         c.writer.Indent();
         c.writer << c.parcel << ".writeInt(0);\n";
         c.writer.Dedent();
         c.writer << "}\n";
       }},
      {"ParcelFileDescriptor[]",
       [](const CodeGeneratorContext& c) {
         c.writer << c.parcel << ".writeTypedArray(" << c.var << ", " << GetFlagFor(c) << ");\n";
       }},
      {"CharSequence",
       [](const CodeGeneratorContext& c) {
         // TextUtils.writeToParcel does not accept null. So, we need to handle
         // the case here.
         c.writer << "if (" << c.var << "!=null) {\n";
         c.writer.Indent();
         c.writer << c.parcel << ".writeInt(1);\n";
         c.writer << "android.text.TextUtils.writeToParcel(" << c.var << ", " << c.parcel << ", "
                  << GetFlagFor(c) << ");\n";
         c.writer.Dedent();
         c.writer << "}\n";
         c.writer << "else {\n";
         c.writer.Indent();
         c.writer << c.parcel << ".writeInt(0);\n";
         c.writer.Dedent();
         c.writer << "}\n";
       }},
  };
  const string type_name = c.type.GetName() + (c.type.IsArray() ? "[]" : "");
  const auto found = method_map.find(type_name);
  if (found != method_map.end()) {
    found->second(c);
  } else {
    const AidlDefinedType* t = c.typenames.TryGetDefinedType(c.type.GetName());
    CHECK(t != nullptr) << "Unknown type: " << c.type.GetName() << endl;
    if (t->AsInterface() != nullptr) {
      if (!c.type.IsArray()) {
        // Why don't we use writeStrongInterface which does the exact same thing?
        // Keeping below code just not to break unit tests.
        c.writer << c.parcel << ".writeStrongBinder((((" << c.var << "!=null))?"
                 << "(" << c.var << ".asBinder()):(null)));\n";
      }
    } else if (t->AsParcelable() != nullptr) {
      if (c.type.IsArray()) {
        c.writer << c.parcel << ".writeTypedArray(" << c.var << ", " << GetFlagFor(c) << ");\n";
      } else {
        // This is same as writeTypedObject.
        // Keeping below code just not to break tests.
        c.writer << "if ((" << c.var << "!=null)) {\n";
        c.writer.Indent();
        c.writer << c.parcel << ".writeInt(1);\n";
        c.writer << c.var << ".writeToParcel(" << c.parcel << ", " << GetFlagFor(c) << ");\n";
        c.writer.Dedent();
        c.writer << "}\n";
        c.writer << "else {\n";
        c.writer.Indent();
        c.writer << c.parcel << ".writeInt(0);\n";
        c.writer.Dedent();
        c.writer << "}\n";
      }
    }
  }
  return true;
}

// Ensures that a variable is initialized to refer to the classloader
// of the current object and returns the name of the variable.
static string EnsureAndGetClassloader(CodeGeneratorContext& c) {
  CHECK(c.is_classloader_created != nullptr);
  if (!*(c.is_classloader_created)) {
    c.writer << "java.lang.ClassLoader cl = "
             << "(java.lang.ClassLoader)this.getClass().getClassLoader();\n";
    *(c.is_classloader_created) = true;
  }
  return "cl";
}

bool CreateFromParcelFor(const CodeGeneratorContext& c) {
  if (!EnsureCodegenIsSupported(c)) {
    return false;
  }
  static map<string, function<void(const CodeGeneratorContext&)>> method_map{
      {"boolean",
       [](const CodeGeneratorContext& c) {
         c.writer << c.var << " = (0!=" << c.parcel << ".readInt());\n";
       }},
      {"boolean[]",
       [](const CodeGeneratorContext& c) {
         c.writer << c.var << " = " << c.parcel << ".createBooleanArray();\n";
       }},
      {"byte",
       [](const CodeGeneratorContext& c) {
         c.writer << c.var << " = " << c.parcel << ".readByte();\n";
       }},
      {"byte[]",
       [](const CodeGeneratorContext& c) {
         c.writer << c.var << " = " << c.parcel << ".createByteArray();\n";
       }},
      {"char",
       [](const CodeGeneratorContext& c) {
         c.writer << c.var << " = (char)" << c.parcel << ".readInt();\n";
       }},
      {"char[]",
       [](const CodeGeneratorContext& c) {
         c.writer << c.var << " = " << c.parcel << ".createCharArray();\n";
       }},
      {"int",
       [](const CodeGeneratorContext& c) {
         c.writer << c.var << " = " << c.parcel << ".readInt();\n";
       }},
      {"int[]",
       [](const CodeGeneratorContext& c) {
         c.writer << c.var << " = " << c.parcel << ".createIntArray();\n";
       }},
      {"long",
       [](const CodeGeneratorContext& c) {
         c.writer << c.var << " = " << c.parcel << ".readLong();\n";
       }},
      {"long[]",
       [](const CodeGeneratorContext& c) {
         c.writer << c.var << " = " << c.parcel << ".createLongArray();\n";
       }},
      {"float",
       [](const CodeGeneratorContext& c) {
         c.writer << c.var << " = " << c.parcel << ".readFloat();\n";
       }},
      {"float[]",
       [](const CodeGeneratorContext& c) {
         c.writer << c.var << " = " << c.parcel << ".createFloatArray();\n";
       }},
      {"double",
       [](const CodeGeneratorContext& c) {
         c.writer << c.var << " = " << c.parcel << ".readDouble();\n";
       }},
      {"double[]",
       [](const CodeGeneratorContext& c) {
         c.writer << c.var << " = " << c.parcel << ".createDoubleArray();\n";
       }},
      {"String",
       [](const CodeGeneratorContext& c) {
         c.writer << c.var << " = " << c.parcel << ".readString();\n";
       }},
      {"String[]",
       [](const CodeGeneratorContext& c) {
         c.writer << c.var << " = " << c.parcel << ".createStringArray();\n";
       }},
      {"List",
       [](const CodeGeneratorContext& c) {
         if (c.type.IsGeneric()) {
           const string& contained_type = c.type.GetTypeParameters().at(0)->GetName();
           if (AidlTypenames::IsBuiltinTypename(contained_type)) {
             if (contained_type == "String") {
               c.writer << c.var << " = " << c.parcel << ".createStringArrayList();\n";
             } else if (contained_type == "IBinder") {
               c.writer << c.var << " = " << c.parcel << ".createBinderArrayList();\n";
             }
           } else {
             const AidlDefinedType* t = c.typenames.TryGetDefinedType(contained_type);
             CHECK(t != nullptr) << "Unknown type: " << contained_type << endl;
             if (t->AsParcelable() != nullptr) {
               c.writer << c.var << " = " << c.parcel << ".createTypedArrayList("
                        << JavaNameOf(*(c.type.GetTypeParameters().at(0))) << ".CREATOR);\n";
             }
           }
         } else {
           const string classloader = EnsureAndGetClassloader(const_cast<CodeGeneratorContext&>(c));
           c.writer << c.var << " = " << c.parcel << ".readArrayList(" << classloader << ");\n";
         }
       }},
      {"Map",
       [](const CodeGeneratorContext& c) {
         const string classloader = EnsureAndGetClassloader(const_cast<CodeGeneratorContext&>(c));
         c.writer << c.var << " = " << c.parcel << ".readHashMap(" << classloader << ");\n";
       }},
      {"IBinder",
       [](const CodeGeneratorContext& c) {
         c.writer << c.var << " = " << c.parcel << ".readStrongBinder();\n";
       }},
      {"IBinder[]",
       [](const CodeGeneratorContext& c) {
         c.writer << c.var << " = " << c.parcel << ".createBinderArray();\n";
       }},
      {"FileDescriptor",
       [](const CodeGeneratorContext& c) {
         c.writer << c.var << " = " << c.parcel << ".readRawFileDescriptor();\n";
       }},
      {"FileDescriptor[]",
       [](const CodeGeneratorContext& c) {
         c.writer << c.var << " = " << c.parcel << ".createRawFileDescriptorArray();\n";
       }},
      {"ParcelFileDescriptor",
       [](const CodeGeneratorContext& c) {
         // This is same as readTypedObject which was introduced with SDK 23.
         // Keeping below code so that the generated code is buildable with older SDK.
         c.writer << "if ((0!=" << c.parcel << ".readInt())) {\n";
         c.writer.Indent();
         c.writer << c.var << " = " << "android.os.ParcelFileDescriptor.CREATOR.createFromParcel(" << c.parcel
                  << ");\n";
         c.writer.Dedent();
         c.writer << "}\n";
         c.writer << "else {\n";
         c.writer.Indent();
         c.writer << c.var << " = null;\n";
         c.writer.Dedent();
         c.writer << "}\n";
       }},
      {"ParcelFileDescriptor[]",
       [](const CodeGeneratorContext& c) {
         c.writer << c.var << " = " << c.parcel
                  << ".createTypedArray(android.os.ParcelFileDescriptor.CREATOR);\n";
       }},
      {"CharSequence",
       [](const CodeGeneratorContext& c) {
         // We have written 0 for null CharSequence.
         c.writer << "if (0!=" << c.parcel << ".readInt()) {\n";
         c.writer.Indent();
         c.writer << c.var << " = android.text.TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel("
                  << c.parcel << ");\n";
         c.writer.Dedent();
         c.writer << "}\n";
         c.writer << "else {\n";
         c.writer.Indent();
         c.writer << c.var << " = null;\n";
         c.writer.Dedent();
         c.writer << "}\n";
       }},
  };
  const string type_name = c.type.GetName() + (c.type.IsArray() ? "[]" : "");
  const auto found = method_map.find(type_name);
  if (found != method_map.end()) {
    found->second(c);
  } else {
    const AidlDefinedType* t = c.typenames.TryGetDefinedType(c.type.GetName());
    CHECK(t != nullptr) << "Unknown type: " << c.type.GetName() << endl;
    if (t->AsInterface() != nullptr) {
      if (!c.type.IsArray()) {
        c.writer << c.var << " = " << c.type.GetName() << ".Stub.asInterface(" << c.parcel
                 << ".readStrongBinder());\n";
      }
    } else if (t->AsParcelable() != nullptr || t->AsStructuredParcelable() != nullptr) {
      if (c.type.IsArray()) {
        c.writer << c.var << " = " << c.parcel << ".createTypedArray(" << JavaNameOf(c.type)
                 << ".CREATOR);\n";
      } else {
        // This is same as readTypedObject.
        // Keeping below code just not to break unit tests.
        c.writer << "if ((0!=" << c.parcel << ".readInt())) {\n";
        c.writer.Indent();
        c.writer << c.var << " = " << c.type.GetName() << ".CREATOR.createFromParcel(" << c.parcel
                 << ");\n";
        c.writer.Dedent();
        c.writer << "}\n";
        c.writer << "else {\n";
        c.writer.Indent();
        c.writer << c.var << " = null;\n";
        c.writer.Dedent();
        c.writer << "}\n";
      }
    }
  }
  return true;
}

bool ReadFromParcelFor(const CodeGeneratorContext& c) {
  if (!EnsureCodegenIsSupported(c)) {
    return false;
  }
  static map<string, function<void(const CodeGeneratorContext&)>> method_map{
      {"boolean[]",
       [](const CodeGeneratorContext& c) {
         c.writer << c.parcel << ".readBooleanArray(" << c.var << ");\n";
       }},
      {"byte[]",
       [](const CodeGeneratorContext& c) {
         c.writer << c.parcel << ".readByteArray(" << c.var << ");\n";
       }},
      {"char[]",
       [](const CodeGeneratorContext& c) {
         c.writer << c.parcel << ".readCharArray(" << c.var << ");\n";
       }},
      {"int[]",
       [](const CodeGeneratorContext& c) {
         c.writer << c.parcel << ".readIntArray(" << c.var << ");\n";
       }},
      {"long[]",
       [](const CodeGeneratorContext& c) {
         c.writer << c.parcel << ".readLongArray(" << c.var << ");\n";
       }},
      {"float[]",
       [](const CodeGeneratorContext& c) {
         c.writer << c.parcel << ".readFloatArray(" << c.var << ");\n";
       }},
      {"double[]",
       [](const CodeGeneratorContext& c) {
         c.writer << c.parcel << ".readDoubleArray(" << c.var << ");\n";
       }},
      {"String[]",
       [](const CodeGeneratorContext& c) {
         c.writer << c.parcel << ".readStringArray(" << c.var << ");\n";
       }},
      {"List",
       [](const CodeGeneratorContext& c) {
         if (c.type.IsGeneric()) {
           const string& contained_type = c.type.GetTypeParameters().at(0)->GetName();
           if (AidlTypenames::IsBuiltinTypename(contained_type)) {
             if (contained_type == "String") {
               c.writer << c.parcel << ".readStringList(" << c.var << ");\n";
             } else if (contained_type == "IBinder") {
               c.writer << c.parcel << ".readBinderList(" << c.var << ");\n";
             }
           } else {
             const AidlDefinedType* t = c.typenames.TryGetDefinedType(contained_type);
             CHECK(t != nullptr) << "Unknown type: " << contained_type << endl;
             if (t->AsParcelable() != nullptr) {
               c.writer << c.parcel << ".readTypedList(" << c.var << ", "
                        << JavaNameOf(*(c.type.GetTypeParameters().at(0))) << ".CREATOR);\n";
             }
           }
         } else {
           const string classloader = EnsureAndGetClassloader(const_cast<CodeGeneratorContext&>(c));
           c.writer << c.parcel << ".readList(" << c.var << ", " << classloader << ");\n";
         }
       }},
      {"Map",
       [](const CodeGeneratorContext& c) {
         const string classloader = EnsureAndGetClassloader(const_cast<CodeGeneratorContext&>(c));
         c.writer << c.var << " = " << c.parcel << ".readHashMap(" << classloader << ");\n";
       }},
      {"IBinder[]",
       [](const CodeGeneratorContext& c) {
         c.writer << c.var << " = " << c.parcel << ".createBinderArray();\n";
       }},
      {"FileDescriptor[]",
       [](const CodeGeneratorContext& c) {
         c.writer << c.var << " = " << c.parcel << ".createRawFileDescriptorArray();\n";
       }},
      {"ParcelFileDescriptor",
       [](const CodeGeneratorContext& c) {
         c.writer << "if ((0!=" << c.parcel << ".readInt())) {\n";
         c.writer.Indent();
         c.writer << c.var << " = " << "android.os.ParcelFileDescriptor.CREATOR.createFromParcel(" << c.parcel << ");\n";
         c.writer.Dedent();
         c.writer << "}\n";
       }},
      {"ParcelFileDescriptor[]",
       [](const CodeGeneratorContext& c) {
         c.writer << c.parcel << ".readTypedArray(" << c.var
                  << ", android.os.ParcelFileDescriptor.CREATOR);\n";
       }},
  };
  const string type_name = c.type.GetName() + (c.type.IsArray() ? "[]" : "");
  const auto& found = method_map.find(type_name);
  if (found != method_map.end()) {
    found->second(c);
  } else {
    const AidlDefinedType* t = c.typenames.TryGetDefinedType(c.type.GetName());
    CHECK(t != nullptr) << "Unknown type: " << c.type.GetName() << endl;
    if (t->AsParcelable() != nullptr) {
      if (c.type.IsArray()) {
        c.writer << c.parcel << ".readTypedArray(" << c.var << ", " << c.type.GetName()
                 << ".CREATOR);\n";
      } else {
        c.writer << "if ((0!=" << c.parcel << ".readInt())) {\n";
        c.writer.Indent();
        c.writer << c.var << ".readFromParcel(" << c.parcel << ");\n";
        c.writer.Dedent();
        c.writer << "}\n";
      }
    }
  }
  return true;
}

}  // namespace java
}  // namespace aidl
}  // namespace android