// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc.  All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd

// Author: kenton@google.com (Kenton Varda)
//  Based on original Protocol Buffers design by
//  Sanjay Ghemawat, Jeff Dean, and others.

#include "google/protobuf/compiler/java/helpers.h"

#include <algorithm>
#include <cstdint>
#include <limits>
#include <vector>

#include "absl/container/flat_hash_set.h"
#include "absl/log/absl_check.h"
#include "absl/log/absl_log.h"
#include "absl/status/status.h"
#include "absl/strings/ascii.h"
#include "absl/strings/escaping.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
#include "absl/strings/str_replace.h"
#include "absl/strings/str_split.h"
#include "absl/strings/string_view.h"
#include "absl/strings/substitute.h"
#include "google/protobuf/compiler/java/java_features.pb.h"
#include "google/protobuf/compiler/java/generator.h"
#include "google/protobuf/compiler/java/name_resolver.h"
#include "google/protobuf/compiler/versions.h"
#include "google/protobuf/descriptor.pb.h"
#include "google/protobuf/io/printer.h"
#include "google/protobuf/io/strtod.h"
#include "google/protobuf/wire_format.h"

// Must be last.
#include "google/protobuf/port_def.inc"

namespace google {
namespace protobuf {
namespace compiler {
namespace java {

using ::google::protobuf::internal::WireFormatLite;

const char kThickSeparator[] =
    "// ===================================================================\n";
const char kThinSeparator[] =
    "// -------------------------------------------------------------------\n";

void PrintGeneratedAnnotation(io::Printer* printer, char delimiter,
                              absl::string_view annotation_file,
                              Options options) {
  printer->Print("@com.google.protobuf.Generated\n");

  if (annotation_file.empty()) {
    return;
  }
  // Print javax.annotation.Generated to support Kythe indexing
  std::string ptemplate =
      "@javax.annotation.Generated(value=\"protoc\", comments=\"annotations:";
  ptemplate.push_back(delimiter);
  ptemplate.append("annotation_file");
  ptemplate.push_back(delimiter);
  ptemplate.append("\")\n");
  printer->Print(ptemplate, "annotation_file", annotation_file);
}

void PrintEnumVerifierLogic(
    io::Printer* printer, const FieldDescriptor* descriptor,
    const absl::flat_hash_map<absl::string_view, std::string>& variables,
    absl::string_view var_name, absl::string_view terminating_string,
    bool enforce_lite) {
  std::string enum_verifier_string =
      enforce_lite ? absl::StrCat(var_name, ".internalGetVerifier()")
                   : absl::StrCat(
                         "new com.google.protobuf.Internal.EnumVerifier() {\n"
                         "        @java.lang.Override\n"
                         "        public boolean isInRange(int number) {\n"
                         "          return ",
                         var_name,
                         ".forNumber(number) != null;\n"
                         "        }\n"
                         "      }");
  printer->Print(variables,
                 absl::StrCat(enum_verifier_string, terminating_string));
}

void PrintGencodeVersionValidator(io::Printer* printer, bool oss_runtime,
                                  absl::string_view java_class_name) {
  const auto& version = GetProtobufJavaVersion(oss_runtime);
  printer->Print(
      "com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion(\n"
      "  com.google.protobuf.RuntimeVersion.RuntimeDomain.$domain$,\n"
      "  $major$,\n"
      "  $minor$,\n"
      "  $patch$,\n"
      "  $suffix$,\n"
      "  $location$);\n",
      "domain", oss_runtime ? "PUBLIC" : "GOOGLE_INTERNAL", "major",
      absl::StrCat("/* major= */ ", version.major()), "minor",
      absl::StrCat("/* minor= */ ", version.minor()), "patch",
      absl::StrCat("/* patch= */ ", version.patch()), "suffix",
      absl::StrCat("/* suffix= */ \"", version.suffix(), "\""), "location",
      absl::StrCat(java_class_name, ".class.getName()"));
}

std::string UnderscoresToCamelCase(absl::string_view input,
                                   bool cap_next_letter) {
  ABSL_CHECK(!input.empty());
  std::string result;
  // Note:  I distrust ctype.h due to locales.
  for (int i = 0; i < input.size(); i++) {
    if ('a' <= input[i] && input[i] <= 'z') {
      if (cap_next_letter) {
        result += input[i] + ('A' - 'a');
      } else {
        result += input[i];
      }
      cap_next_letter = false;
    } else if ('A' <= input[i] && input[i] <= 'Z') {
      if (i == 0 && !cap_next_letter) {
        // Force first letter to lower-case unless explicitly told to
        // capitalize it.
        result += input[i] + ('a' - 'A');
      } else {
        // Capital letters after the first are left as-is.
        result += input[i];
      }
      cap_next_letter = false;
    } else if ('0' <= input[i] && input[i] <= '9') {
      result += input[i];
      cap_next_letter = true;
    } else {
      cap_next_letter = true;
    }
  }
  // Add a trailing "_" if the name should be altered.
  if (input[input.size() - 1] == '#') {
    result += '_';
  }
  return result;
}

std::string ToCamelCase(absl::string_view input, bool lower_first) {
  bool capitalize_next = !lower_first;
  std::string result;
  result.reserve(input.size());

  for (char i : input) {
    if (i == '_') {
      capitalize_next = true;
    } else if (capitalize_next) {
      result.push_back(absl::ascii_toupper(i));
      capitalize_next = false;
    } else {
      result.push_back(i);
    }
  }

  // Lower-case the first letter.
  if (lower_first && !result.empty()) {
    result[0] = absl::ascii_tolower(result[0]);
  }

  return result;
}

// Names that should be avoided as field names in Kotlin.
// All Kotlin hard keywords are in this list.
bool IsForbiddenKotlin(absl::string_view field_name) {
  static const auto& kKotlinForbiddenNames =
      *new absl::flat_hash_set<absl::string_view>({
          "as",      "as?",       "break",  "class", "continue", "do",
          "else",    "false",     "for",    "fun",   "if",       "in",
          "!in",     "interface", "is",     "!is",   "null",     "object",
          "package", "return",    "super",  "this",  "throw",    "true",
          "try",     "typealias", "typeof", "val",   "var",      "when",
          "while",
      });

  return kKotlinForbiddenNames.contains(field_name);
}

std::string EscapeKotlinKeywords(std::string name) {
  std::vector<std::string> escaped_packages;
  std::vector<std::string> packages = absl::StrSplit(name, ".");  // NOLINT
  for (absl::string_view package : packages) {
    if (IsForbiddenKotlin(package)) {
      escaped_packages.push_back(absl::StrCat("`", package, "`"));
    } else {
      escaped_packages.emplace_back(package);
    }
  }
  return absl::StrJoin(escaped_packages, ".");
}

std::string UniqueFileScopeIdentifier(const Descriptor* descriptor) {
  return absl::StrCat(
      "static_", absl::StrReplaceAll(descriptor->full_name(), {{".", "_"}}));
}

std::string CamelCaseFieldName(const FieldDescriptor* field) {
  std::string fieldName = UnderscoresToCamelCase(field);
  if ('0' <= fieldName[0] && fieldName[0] <= '9') {
    return absl::StrCat("_", fieldName);
  }
  return fieldName;
}

std::string FileClassName(const FileDescriptor* file, bool immutable) {
  return ClassNameResolver().GetFileClassName(file, immutable);
}

std::string JavaPackageToDir(std::string package_name) {
  std::string package_dir = absl::StrReplaceAll(package_name, {{".", "/"}});
  if (!package_dir.empty()) absl::StrAppend(&package_dir, "/");
  return package_dir;
}

std::string ExtraMessageInterfaces(const Descriptor* descriptor) {
  return absl::StrCat("// @@protoc_insertion_point(message_implements:",
                      descriptor->full_name(), ")");
}


std::string ExtraBuilderInterfaces(const Descriptor* descriptor) {
  return absl::StrCat("// @@protoc_insertion_point(builder_implements:",
                      descriptor->full_name(), ")");
}

std::string ExtraMessageOrBuilderInterfaces(const Descriptor* descriptor) {
  return absl::StrCat("// @@protoc_insertion_point(interface_extends:",
                      descriptor->full_name(), ")");
}

std::string FieldConstantName(const FieldDescriptor* field) {
  std::string name = absl::StrCat(field->name(), "_FIELD_NUMBER");
  absl::AsciiStrToUpper(&name);
  return name;
}

FieldDescriptor::Type GetType(const FieldDescriptor* field) {
  return field->type();
}

JavaType GetJavaType(const FieldDescriptor* field) {
  switch (GetType(field)) {
    case FieldDescriptor::TYPE_INT32:
    case FieldDescriptor::TYPE_UINT32:
    case FieldDescriptor::TYPE_SINT32:
    case FieldDescriptor::TYPE_FIXED32:
    case FieldDescriptor::TYPE_SFIXED32:
      return JAVATYPE_INT;

    case FieldDescriptor::TYPE_INT64:
    case FieldDescriptor::TYPE_UINT64:
    case FieldDescriptor::TYPE_SINT64:
    case FieldDescriptor::TYPE_FIXED64:
    case FieldDescriptor::TYPE_SFIXED64:
      return JAVATYPE_LONG;

    case FieldDescriptor::TYPE_FLOAT:
      return JAVATYPE_FLOAT;

    case FieldDescriptor::TYPE_DOUBLE:
      return JAVATYPE_DOUBLE;

    case FieldDescriptor::TYPE_BOOL:
      return JAVATYPE_BOOLEAN;

    case FieldDescriptor::TYPE_STRING:
      return JAVATYPE_STRING;

    case FieldDescriptor::TYPE_BYTES:
      return JAVATYPE_BYTES;

    case FieldDescriptor::TYPE_ENUM:
      return JAVATYPE_ENUM;

    case FieldDescriptor::TYPE_GROUP:
    case FieldDescriptor::TYPE_MESSAGE:
      return JAVATYPE_MESSAGE;

      // No default because we want the compiler to complain if any new
      // types are added.
  }

  ABSL_LOG(FATAL) << "Can't get here.";
  return JAVATYPE_INT;
}

absl::string_view PrimitiveTypeName(JavaType type) {
  switch (type) {
    case JAVATYPE_INT:
      return "int";
    case JAVATYPE_LONG:
      return "long";
    case JAVATYPE_FLOAT:
      return "float";
    case JAVATYPE_DOUBLE:
      return "double";
    case JAVATYPE_BOOLEAN:
      return "boolean";
    case JAVATYPE_STRING:
      return "java.lang.String";
    case JAVATYPE_BYTES:
      return "com.google.protobuf.ByteString";
    case JAVATYPE_ENUM:
      return {};
    case JAVATYPE_MESSAGE:
      return {};

      // No default because we want the compiler to complain if any new
      // JavaTypes are added.
  }

  ABSL_LOG(FATAL) << "Can't get here.";
  return {};
}

absl::string_view PrimitiveTypeName(const FieldDescriptor* descriptor) {
  return PrimitiveTypeName(GetJavaType(descriptor));
}

absl::string_view BoxedPrimitiveTypeName(JavaType type) {
  switch (type) {
    case JAVATYPE_INT:
      return "java.lang.Integer";
    case JAVATYPE_LONG:
      return "java.lang.Long";
    case JAVATYPE_FLOAT:
      return "java.lang.Float";
    case JAVATYPE_DOUBLE:
      return "java.lang.Double";
    case JAVATYPE_BOOLEAN:
      return "java.lang.Boolean";
    case JAVATYPE_STRING:
      return "java.lang.String";
    case JAVATYPE_BYTES:
      return "com.google.protobuf.ByteString";
    case JAVATYPE_ENUM:
      return {};
    case JAVATYPE_MESSAGE:
      return {};

      // No default because we want the compiler to complain if any new
      // JavaTypes are added.
  }

  ABSL_LOG(FATAL) << "Can't get here.";
  return {};
}

absl::string_view BoxedPrimitiveTypeName(const FieldDescriptor* descriptor) {
  return BoxedPrimitiveTypeName(GetJavaType(descriptor));
}

absl::string_view KotlinTypeName(JavaType type) {
  switch (type) {
    case JAVATYPE_INT:
      return "kotlin.Int";
    case JAVATYPE_LONG:
      return "kotlin.Long";
    case JAVATYPE_FLOAT:
      return "kotlin.Float";
    case JAVATYPE_DOUBLE:
      return "kotlin.Double";
    case JAVATYPE_BOOLEAN:
      return "kotlin.Boolean";
    case JAVATYPE_STRING:
      return "kotlin.String";
    case JAVATYPE_BYTES:
      return "com.google.protobuf.ByteString";
    case JAVATYPE_ENUM:
      return {};
    case JAVATYPE_MESSAGE:
      return {};

      // No default because we want the compiler to complain if any new
      // JavaTypes are added.
  }

  ABSL_LOG(FATAL) << "Can't get here.";
  return {};
}

std::string GetOneofStoredType(const FieldDescriptor* field) {
  const JavaType javaType = GetJavaType(field);
  switch (javaType) {
    case JAVATYPE_ENUM:
      return "java.lang.Integer";
    case JAVATYPE_MESSAGE:
      return ClassNameResolver().GetClassName(field->message_type(), true);
    default:
      return std::string(BoxedPrimitiveTypeName(javaType));
  }
}

absl::string_view FieldTypeName(FieldDescriptor::Type field_type) {
  switch (field_type) {
    case FieldDescriptor::TYPE_INT32:
      return "INT32";
    case FieldDescriptor::TYPE_UINT32:
      return "UINT32";
    case FieldDescriptor::TYPE_SINT32:
      return "SINT32";
    case FieldDescriptor::TYPE_FIXED32:
      return "FIXED32";
    case FieldDescriptor::TYPE_SFIXED32:
      return "SFIXED32";
    case FieldDescriptor::TYPE_INT64:
      return "INT64";
    case FieldDescriptor::TYPE_UINT64:
      return "UINT64";
    case FieldDescriptor::TYPE_SINT64:
      return "SINT64";
    case FieldDescriptor::TYPE_FIXED64:
      return "FIXED64";
    case FieldDescriptor::TYPE_SFIXED64:
      return "SFIXED64";
    case FieldDescriptor::TYPE_FLOAT:
      return "FLOAT";
    case FieldDescriptor::TYPE_DOUBLE:
      return "DOUBLE";
    case FieldDescriptor::TYPE_BOOL:
      return "BOOL";
    case FieldDescriptor::TYPE_STRING:
      return "STRING";
    case FieldDescriptor::TYPE_BYTES:
      return "BYTES";
    case FieldDescriptor::TYPE_ENUM:
      return "ENUM";
    case FieldDescriptor::TYPE_GROUP:
      return "GROUP";
    case FieldDescriptor::TYPE_MESSAGE:
      return "MESSAGE";

      // No default because we want the compiler to complain if any new
      // types are added.
  }

  ABSL_LOG(FATAL) << "Can't get here.";
  return {};
}

bool AllAscii(absl::string_view text) {
  for (int i = 0; i < text.size(); i++) {
    if ((text[i] & 0x80) != 0) {
      return false;
    }
  }
  return true;
}

std::string DefaultValue(const FieldDescriptor* field, bool immutable,
                         ClassNameResolver* name_resolver, Options options) {
  // Switch on CppType since we need to know which default_value_* method
  // of FieldDescriptor to call.
  switch (field->cpp_type()) {
    case FieldDescriptor::CPPTYPE_INT32:
      return absl::StrCat(field->default_value_int32());
    case FieldDescriptor::CPPTYPE_UINT32:
      // Need to print as a signed int since Java has no unsigned.
      return absl::StrCat(static_cast<int32_t>(field->default_value_uint32()));
    case FieldDescriptor::CPPTYPE_INT64:
      return absl::StrCat(field->default_value_int64(), "L");
    case FieldDescriptor::CPPTYPE_UINT64:
      return absl::StrCat(static_cast<int64_t>(field->default_value_uint64())) +
             "L";
    case FieldDescriptor::CPPTYPE_DOUBLE: {
      double value = field->default_value_double();
      if (value == std::numeric_limits<double>::infinity()) {
        return "Double.POSITIVE_INFINITY";
      } else if (value == -std::numeric_limits<double>::infinity()) {
        return "Double.NEGATIVE_INFINITY";
      } else if (value != value) {
        return "Double.NaN";
      } else {
        return absl::StrCat(io::SimpleDtoa(value), "D");
      }
    }
    case FieldDescriptor::CPPTYPE_FLOAT: {
      float value = field->default_value_float();
      if (value == std::numeric_limits<float>::infinity()) {
        return "Float.POSITIVE_INFINITY";
      } else if (value == -std::numeric_limits<float>::infinity()) {
        return "Float.NEGATIVE_INFINITY";
      } else if (value != value) {
        return "Float.NaN";
      } else {
        return absl::StrCat(io::SimpleFtoa(value), "F");
      }
    }
    case FieldDescriptor::CPPTYPE_BOOL:
      return field->default_value_bool() ? "true" : "false";
    case FieldDescriptor::CPPTYPE_STRING:
      if (GetType(field) == FieldDescriptor::TYPE_BYTES) {
        if (field->has_default_value()) {
          // See comments in Internal.java for gory details.
          return absl::Substitute(
              "com.google.protobuf.Internal.bytesDefaultValue(\"$0\")",
              absl::CEscape(field->default_value_string()));
        } else {
          return "com.google.protobuf.ByteString.EMPTY";
        }
      } else {
        if (AllAscii(field->default_value_string())) {
          // All chars are ASCII.  In this case CEscape() works fine.
          return absl::StrCat(
              "\"", absl::CEscape(field->default_value_string()), "\"");
        } else {
          // See comments in Internal.java for gory details.
          return absl::Substitute(
              "com.google.protobuf.Internal.stringDefaultValue(\"$0\")",
              absl::CEscape(field->default_value_string()));
        }
      }

    case FieldDescriptor::CPPTYPE_ENUM:
      return absl::StrCat(
          name_resolver->GetClassName(field->enum_type(), immutable), ".",
          field->default_value_enum()->name());

    case FieldDescriptor::CPPTYPE_MESSAGE:
      return absl::StrCat(
          name_resolver->GetClassName(field->message_type(), immutable),
          ".getDefaultInstance()");

      // No default because we want the compiler to complain if any new
      // types are added.
  }

  ABSL_LOG(FATAL) << "Can't get here.";
  return "";
}

bool IsDefaultValueJavaDefault(const FieldDescriptor* field) {
  // Switch on CppType since we need to know which default_value_* method
  // of FieldDescriptor to call.
  switch (field->cpp_type()) {
    case FieldDescriptor::CPPTYPE_INT32:
      return field->default_value_int32() == 0;
    case FieldDescriptor::CPPTYPE_UINT32:
      return field->default_value_uint32() == 0;
    case FieldDescriptor::CPPTYPE_INT64:
      return field->default_value_int64() == 0L;
    case FieldDescriptor::CPPTYPE_UINT64:
      return field->default_value_uint64() == 0L;
    case FieldDescriptor::CPPTYPE_DOUBLE:
      return field->default_value_double() == 0.0;
    case FieldDescriptor::CPPTYPE_FLOAT:
      return field->default_value_float() == 0.0;
    case FieldDescriptor::CPPTYPE_BOOL:
      return field->default_value_bool() == false;
    case FieldDescriptor::CPPTYPE_ENUM:
      return field->default_value_enum()->number() == 0;
    case FieldDescriptor::CPPTYPE_STRING:
    case FieldDescriptor::CPPTYPE_MESSAGE:
      return false;

      // No default because we want the compiler to complain if any new
      // types are added.
  }

  ABSL_LOG(FATAL) << "Can't get here.";
  return false;
}

bool IsByteStringWithCustomDefaultValue(const FieldDescriptor* field) {
  return GetJavaType(field) == JAVATYPE_BYTES &&
         !field->default_value_string().empty();
}

constexpr absl::string_view bit_masks[] = {
    "0x00000001", "0x00000002", "0x00000004", "0x00000008",
    "0x00000010", "0x00000020", "0x00000040", "0x00000080",

    "0x00000100", "0x00000200", "0x00000400", "0x00000800",
    "0x00001000", "0x00002000", "0x00004000", "0x00008000",

    "0x00010000", "0x00020000", "0x00040000", "0x00080000",
    "0x00100000", "0x00200000", "0x00400000", "0x00800000",

    "0x01000000", "0x02000000", "0x04000000", "0x08000000",
    "0x10000000", "0x20000000", "0x40000000", "0x80000000",
};

std::string GetBitFieldName(int index) {
  return absl::StrCat("bitField", index, "_");
}

std::string GetBitFieldNameForBit(int bitIndex) {
  return GetBitFieldName(bitIndex / 32);
}

namespace {

std::string GenerateGetBitInternal(absl::string_view prefix, int bitIndex) {
  std::string varName = absl::StrCat(prefix, GetBitFieldNameForBit(bitIndex));
  int bitInVarIndex = bitIndex % 32;

  return absl::StrCat("((", varName, " & ", bit_masks[bitInVarIndex],
                      ") != 0)");
}

std::string GenerateSetBitInternal(absl::string_view prefix, int bitIndex) {
  std::string varName = absl::StrCat(prefix, GetBitFieldNameForBit(bitIndex));
  int bitInVarIndex = bitIndex % 32;

  return absl::StrCat(varName, " |= ", bit_masks[bitInVarIndex]);
}

}  // namespace

std::string GenerateGetBit(int bitIndex) {
  return GenerateGetBitInternal("", bitIndex);
}

std::string GenerateSetBit(int bitIndex) {
  return GenerateSetBitInternal("", bitIndex);
}

std::string GenerateClearBit(int bitIndex) {
  std::string varName = GetBitFieldNameForBit(bitIndex);
  int bitInVarIndex = bitIndex % 32;

  return absl::StrCat(varName, " = (", varName, " & ~",
                      bit_masks[bitInVarIndex], ")");
}

std::string GenerateGetBitFromLocal(int bitIndex) {
  return GenerateGetBitInternal("from_", bitIndex);
}

std::string GenerateSetBitToLocal(int bitIndex) {
  return GenerateSetBitInternal("to_", bitIndex);
}

std::string GenerateGetBitMutableLocal(int bitIndex) {
  return GenerateGetBitInternal("mutable_", bitIndex);
}

std::string GenerateSetBitMutableLocal(int bitIndex) {
  return GenerateSetBitInternal("mutable_", bitIndex);
}

bool IsReferenceType(JavaType type) {
  switch (type) {
    case JAVATYPE_INT:
      return false;
    case JAVATYPE_LONG:
      return false;
    case JAVATYPE_FLOAT:
      return false;
    case JAVATYPE_DOUBLE:
      return false;
    case JAVATYPE_BOOLEAN:
      return false;
    case JAVATYPE_STRING:
      return true;
    case JAVATYPE_BYTES:
      return true;
    case JAVATYPE_ENUM:
      return true;
    case JAVATYPE_MESSAGE:
      return true;

      // No default because we want the compiler to complain if any new
      // JavaTypes are added.
  }

  ABSL_LOG(FATAL) << "Can't get here.";
  return false;
}

absl::string_view GetCapitalizedType(const FieldDescriptor* field,
                                     bool immutable, Options options) {
  switch (GetType(field)) {
    case FieldDescriptor::TYPE_INT32:
      return "Int32";
    case FieldDescriptor::TYPE_UINT32:
      return "UInt32";
    case FieldDescriptor::TYPE_SINT32:
      return "SInt32";
    case FieldDescriptor::TYPE_FIXED32:
      return "Fixed32";
    case FieldDescriptor::TYPE_SFIXED32:
      return "SFixed32";
    case FieldDescriptor::TYPE_INT64:
      return "Int64";
    case FieldDescriptor::TYPE_UINT64:
      return "UInt64";
    case FieldDescriptor::TYPE_SINT64:
      return "SInt64";
    case FieldDescriptor::TYPE_FIXED64:
      return "Fixed64";
    case FieldDescriptor::TYPE_SFIXED64:
      return "SFixed64";
    case FieldDescriptor::TYPE_FLOAT:
      return "Float";
    case FieldDescriptor::TYPE_DOUBLE:
      return "Double";
    case FieldDescriptor::TYPE_BOOL:
      return "Bool";
    case FieldDescriptor::TYPE_STRING:
      return "String";
    case FieldDescriptor::TYPE_BYTES: {
      return "Bytes";
    }
    case FieldDescriptor::TYPE_ENUM:
      return "Enum";
    case FieldDescriptor::TYPE_GROUP:
      return "Group";
    case FieldDescriptor::TYPE_MESSAGE:
      return "Message";

      // No default because we want the compiler to complain if any new
      // types are added.
  }

  ABSL_LOG(FATAL) << "Can't get here.";
  return {};
}

// For encodings with fixed sizes, returns that size in bytes.  Otherwise
// returns -1.
int FixedSize(FieldDescriptor::Type type) {
  switch (type) {
    case FieldDescriptor::TYPE_INT32:
      return -1;
    case FieldDescriptor::TYPE_INT64:
      return -1;
    case FieldDescriptor::TYPE_UINT32:
      return -1;
    case FieldDescriptor::TYPE_UINT64:
      return -1;
    case FieldDescriptor::TYPE_SINT32:
      return -1;
    case FieldDescriptor::TYPE_SINT64:
      return -1;
    case FieldDescriptor::TYPE_FIXED32:
      return WireFormatLite::kFixed32Size;
    case FieldDescriptor::TYPE_FIXED64:
      return WireFormatLite::kFixed64Size;
    case FieldDescriptor::TYPE_SFIXED32:
      return WireFormatLite::kSFixed32Size;
    case FieldDescriptor::TYPE_SFIXED64:
      return WireFormatLite::kSFixed64Size;
    case FieldDescriptor::TYPE_FLOAT:
      return WireFormatLite::kFloatSize;
    case FieldDescriptor::TYPE_DOUBLE:
      return WireFormatLite::kDoubleSize;

    case FieldDescriptor::TYPE_BOOL:
      return WireFormatLite::kBoolSize;
    case FieldDescriptor::TYPE_ENUM:
      return -1;

    case FieldDescriptor::TYPE_STRING:
      return -1;
    case FieldDescriptor::TYPE_BYTES:
      return -1;
    case FieldDescriptor::TYPE_GROUP:
      return -1;
    case FieldDescriptor::TYPE_MESSAGE:
      return -1;

      // No default because we want the compiler to complain if any new
      // types are added.
  }
  ABSL_LOG(FATAL) << "Can't get here.";
  return -1;
}

// Sort the fields of the given Descriptor by number into a new[]'d array
// and return it. The caller should delete the returned array.
const FieldDescriptor** SortFieldsByNumber(const Descriptor* descriptor) {
  const FieldDescriptor** fields =
      new const FieldDescriptor*[descriptor->field_count()];
  for (int i = 0; i < descriptor->field_count(); i++) {
    fields[i] = descriptor->field(i);
  }
  std::sort(fields, fields + descriptor->field_count(),
            FieldOrderingByNumber());
  return fields;
}

// Returns true if the message type has any required fields.  If it doesn't,
// we can optimize out calls to its isInitialized() method.
//
// already_seen is used to avoid checking the same type multiple times
// (and also to protect against recursion).
bool HasRequiredFields(const Descriptor* type,
                       absl::flat_hash_set<const Descriptor*>* already_seen) {
  if (already_seen->count(type) > 0) {
    // The type is already in cache.  This means that either:
    // a. The type has no required fields.
    // b. We are in the midst of checking if the type has required fields,
    //    somewhere up the stack.  In this case, we know that if the type
    //    has any required fields, they'll be found when we return to it,
    //    and the whole call to HasRequiredFields() will return true.
    //    Therefore, we don't have to check if this type has required fields
    //    here.
    return false;
  }
  already_seen->insert(type);

  // If the type has extensions, an extension with message type could contain
  // required fields, so we have to be conservative and assume such an
  // extension exists.
  if (type->extension_range_count() > 0) return true;

  for (int i = 0; i < type->field_count(); i++) {
    const FieldDescriptor* field = type->field(i);
    if (field->is_required()) {
      return true;
    }
    if (GetJavaType(field) == JAVATYPE_MESSAGE) {
      if (HasRequiredFields(field->message_type(), already_seen)) {
        return true;
      }
    }
  }

  return false;
}

bool HasRequiredFields(const Descriptor* type) {
  absl::flat_hash_set<const Descriptor*> already_seen;
  return HasRequiredFields(type, &already_seen);
}

bool IsRealOneof(const FieldDescriptor* descriptor) {
  return descriptor->real_containing_oneof();
}

bool HasRepeatedFields(const Descriptor* descriptor) {
  for (int i = 0; i < descriptor->field_count(); ++i) {
    const FieldDescriptor* field = descriptor->field(i);
    if (field->is_repeated()) {
      return true;
    }
  }
  return false;
}

// Encode an unsigned 32-bit value into a sequence of UTF-16 characters.
//
// If the value is in [0x0000, 0xD7FF], we encode it with a single character
// with the same numeric value.
//
// If the value is larger than 0xD7FF, we encode its lowest 13 bits into a
// character in the range [0xE000, 0xFFFF] by combining these 13 bits with
// 0xE000 using logic-or. Then we shift the value to the right by 13 bits, and
// encode the remaining value by repeating this same process until we get to
// a value in [0x0000, 0xD7FF] where we will encode it using a character with
// the same numeric value.
//
// Note that we only use code points in [0x0000, 0xD7FF] and [0xE000, 0xFFFF].
// There will be no surrogate pairs in the encoded character sequence.
void WriteUInt32ToUtf16CharSequence(uint32_t number,
                                    std::vector<uint16_t>* output) {
  // For values in [0x0000, 0xD7FF], only use one char to encode it.
  if (number < 0xD800) {
    output->push_back(static_cast<uint16_t>(number));
    return;
  }
  // Encode into multiple chars. All except the last char will be in the range
  // [0xE000, 0xFFFF], and the last char will be in the range [0x0000, 0xD7FF].
  // Note that we don't use any value in range [0xD800, 0xDFFF] because they
  // have to come in pairs and the encoding is just more space-efficient w/o
  // them.
  while (number >= 0xD800) {
    // [0xE000, 0xFFFF] can represent 13 bits of info.
    output->push_back(static_cast<uint16_t>(0xE000 | (number & 0x1FFF)));
    number >>= 13;
  }
  output->push_back(static_cast<uint16_t>(number));
}

// Escape a UTF-16 character to be embedded in a Java string.
void EscapeUtf16ToString(uint16_t code, std::string* output) {
  if (code == '\t') {
    output->append("\\t");
  } else if (code == '\b') {
    output->append("\\b");
  } else if (code == '\n') {
    output->append("\\n");
  } else if (code == '\r') {
    output->append("\\r");
  } else if (code == '\f') {
    output->append("\\f");
  } else if (code == '\'') {
    output->append("\\'");
  } else if (code == '\"') {
    output->append("\\\"");
  } else if (code == '\\') {
    output->append("\\\\");
  } else if (code >= 0x20 && code <= 0x7f) {
    output->push_back(static_cast<char>(code));
  } else {
    output->append(absl::StrFormat("\\u%04x", code));
  }
}

const FieldDescriptor* MapKeyField(const FieldDescriptor* descriptor) {
  ABSL_CHECK_EQ(FieldDescriptor::TYPE_MESSAGE, descriptor->type());
  const Descriptor* message = descriptor->message_type();
  ABSL_CHECK(message->options().map_entry());
  return message->map_key();
}

const FieldDescriptor* MapValueField(const FieldDescriptor* descriptor) {
  ABSL_CHECK_EQ(FieldDescriptor::TYPE_MESSAGE, descriptor->type());
  const Descriptor* message = descriptor->message_type();
  ABSL_CHECK(message->options().map_entry());
  return message->map_value();
}


namespace {

// Gets the value of `nest_in_file_class` feature and returns whether the
// generated class should be nested in the generated proto file Java class.
template <typename Descriptor>
inline bool NestInFileClass(const Descriptor& descriptor) {
  auto nest_in_file_class =
      JavaGenerator::GetResolvedSourceFeatureExtension(descriptor, pb::java)
          .nest_in_file_class();
  ABSL_CHECK(
      nest_in_file_class !=
      pb::JavaFeatures::NestInFileClassFeature::NEST_IN_FILE_CLASS_UNKNOWN);

  if (nest_in_file_class == pb::JavaFeatures::NestInFileClassFeature::LEGACY) {
    return !descriptor.file()->options().java_multiple_files();
  }
  return nest_in_file_class == pb::JavaFeatures::NestInFileClassFeature::YES;
}

// Returns whether the type should be nested in the file class for the given
// descriptor, depending on different Protobuf Java API versions.
// TODO: b/372482046 - Implement `nest_in_file_class` feature for mutable API.
template <typename Descriptor>
bool NestInFileClass(const Descriptor& descriptor, bool immutable) {
  (void)immutable;
  return NestInFileClass(descriptor);
}

template <typename Descriptor>
absl::Status ValidateNestInFileClassFeatureHelper(
    const Descriptor& descriptor) {
  if (descriptor.containing_type() != nullptr) {
    const pb::JavaFeatures& unresolved_features =
        JavaGenerator::GetUnresolvedSourceFeatures(descriptor, pb::java);
    if (unresolved_features.has_nest_in_file_class()) {
      return absl::FailedPreconditionError(absl::StrCat(
          "Feature next_in_file_class only applies to top-level types and is "
          "not allowed to be set on the nexted type: ",
          descriptor.full_name()));
    }
  }
  return absl::OkStatus();
}
}  // namespace

absl::Status ValidateNestInFileClassFeature(const Descriptor& descriptor) {
  return ValidateNestInFileClassFeatureHelper(descriptor);
}

absl::Status ValidateNestInFileClassFeature(const EnumDescriptor& descriptor) {
  return ValidateNestInFileClassFeatureHelper(descriptor);
}

bool NestedInFileClass(const Descriptor& descriptor, bool immutable) {
  ABSL_CHECK_OK(ValidateNestInFileClassFeature(descriptor));
  return NestInFileClass(descriptor, immutable);
}

bool NestedInFileClass(const EnumDescriptor& descriptor, bool immutable) {
  ABSL_CHECK_OK(ValidateNestInFileClassFeature(descriptor));
  return NestInFileClass(descriptor, immutable);
}

bool NestedInFileClass(const ServiceDescriptor& descriptor, bool immutable) {
  return NestInFileClass(descriptor, immutable);
}
}  // namespace java
}  // namespace compiler
}  // namespace protobuf
}  // namespace google

#include "google/protobuf/port_undef.inc"
