diff --git a/src/google/protobuf/compiler/java/enum.cc b/src/google/protobuf/compiler/java/enum.cc index eb7803a57a76e..787c7628cc17d 100644 --- a/src/google/protobuf/compiler/java/enum.cc +++ b/src/google/protobuf/compiler/java/enum.cc @@ -11,6 +11,8 @@ #include "google/protobuf/compiler/java/enum.h" +#include +#include #include #include "absl/container/flat_hash_map.h" @@ -30,6 +32,9 @@ namespace protobuf { namespace compiler { namespace java { +// Max number of constants in a generated Java class. +const int kMaxEnums = 1700; + EnumGenerator::EnumGenerator(const EnumDescriptor* descriptor, bool immutable_api, Context* context) : descriptor_(descriptor), @@ -376,6 +381,394 @@ void EnumGenerator::Generate(io::Printer* printer) { printer->Print("}\n\n"); } +void EnumGenerator::GenerateLarge(io::Printer* printer) { + WriteEnumDocComment(printer, descriptor_, context_->options()); + MaybePrintGeneratedAnnotation(context_, printer, descriptor_, immutable_api_); + int interface_count = ceil((double)canonical_values_.size() / kMaxEnums); + if (!context_->options().opensource_runtime) { + printer->Print("@com.google.protobuf.Internal.ProtoNonnullApi\n"); + } + + std::string interface_names; + for (int count = 0; count < interface_count; count++) { + absl::StrAppend(&interface_names, ", ", descriptor_->name(), count); + } + printer->Print( + "$deprecation$public class $classname$\n" + " implements com.google.protobuf.ProtocolMessageEnum" + "$interface_names$ {\n", + "classname", descriptor_->name(), "deprecation", + descriptor_->options().deprecated() ? "@java.lang.Deprecated \n" : "", + "interface_names", interface_names); + printer->Annotate("classname", descriptor_); + printer->Indent(); + + bool ordinal_is_index = true; + std::string index_text = "ordinal()"; + for (int i = 0; i < (int)canonical_values_.size(); i++) { + if (canonical_values_[i]->index() != i) { + ordinal_is_index = false; + index_text = "index"; + break; + } + } + + printer->Print("static {\n"); + printer->Indent(); + PrintGencodeVersionValidator(printer, context_->options().opensource_runtime, + descriptor_->name()); + printer->Outdent(); + printer->Print("}\n"); + + for (int i = 0; i < aliases_.size(); i++) { + absl::flat_hash_map vars; + vars["classname"] = descriptor_->name(); + vars["name"] = aliases_[i].value->name(); + vars["canonical_name"] = aliases_[i].canonical_value->name(); + WriteEnumValueDocComment(printer, aliases_[i].value, context_->options()); + printer->Print( + vars, "public static final $classname$ $name$ = $canonical_name$;\n"); + printer->Annotate("name", aliases_[i].value); + } + + printer->Print( + "public static final $classname$ UNRECOGNIZED = new $classname$(-1, " + "$ordinal$, \"UNRECOGNIZED\");\n", + "classname", descriptor_->name(), "ordinal", + absl::StrCat(canonical_values_.size())); + + printer->Print( + "\n" + "public final int getNumber() {\n"); + if (!descriptor_->is_closed()) { + if (ordinal_is_index) { + printer->Print( + " if (this == UNRECOGNIZED) {\n" + " throw new java.lang.IllegalArgumentException(\n" + " \"Can't get the number of an unknown enum value.\");\n" + " }\n"); + } else { + printer->Print( + " if (index == -1) {\n" + " throw new java.lang.IllegalArgumentException(\n" + " \"Can't get the number of an unknown enum value.\");\n" + " }\n"); + } + } + printer->Print( + " return value;\n" + "}\n" + "\n"); + + // forNumber method start here + if (context_->options().opensource_runtime) { + printer->Print( + "/**\n" + " * @param value The numeric wire value of the corresponding enum " + "entry.\n" + " * @return The enum associated with the given numeric wire value.\n" + " * @deprecated Use {@link #forNumber(int)} instead.\n" + " */\n" + "@java.lang.Deprecated\n" + "public static $classname$ valueOf(int value) {\n" + " return forNumber(value);\n" + "}\n" + "\n", + "classname", descriptor_->name()); + } + printer->Print( + "/**\n" + " * @param value The numeric wire value of the corresponding enum " + "entry.\n" + " * @return The enum associated with the given numeric wire value.\n" + " */\n"); + if (!context_->options().opensource_runtime) { + printer->Print("@com.google.protobuf.Internal.ProtoMethodMayReturnNull\n"); + } + printer->Print("public static $classname$ forNumber(int value) {\n", + "classname", descriptor_->name()); + printer->Indent(); + for (int count = 0; count < interface_count; count++) { + printer->Print( + "if ($classname$$count$.forNumber$count$(value) != null) {\n", + "classname", descriptor_->name(), "count", absl::StrCat(count)); + printer->Indent(); + printer->Print("return $classname$$count$.forNumber$count$(value);\n", + "classname", descriptor_->name(), "count", + absl::StrCat(count)); + printer->Outdent(); + printer->Print("}\n"); + } + printer->Print("return null;\n"); + printer->Outdent(); + printer->Print("}\n\n"); + + printer->Print( + "/**\n" + " * @param name The string name of the corresponding enum " + "entry.\n" + " * @return The enum associated with the given string name.\n" + " */\n"); + if (!context_->options().opensource_runtime) { + printer->Print("@com.google.protobuf.Internal.ProtoMethodMayReturnNull\n"); + } + + printer->Print("public static $classname$ valueOf(String name) {\n", + "classname", descriptor_->name()); + printer->Indent(); + for (int count = 0; count < interface_count; count++) { + printer->Print("if ($classname$$count$.valueOf$count$(name) != null) {\n", + "classname", descriptor_->name(), "count", + absl::StrCat(count)); + printer->Indent(); + printer->Print("return $classname$$count$.valueOf$count$(name);\n", + "classname", descriptor_->name(), "count", + absl::StrCat(count)); + printer->Outdent(); + printer->Print("}\n"); + } + printer->Print( + " throw new java.lang.IllegalArgumentException(\n" + " \"No enum constant $classname$.\" + name);\n"); + printer->Outdent(); + printer->Print("}\n\n"); + + printer->Print("public static $classname$[] values() {\n", "classname", + descriptor_->name()); + printer->Indent(); + printer->Print("int ordinal = 0;\n"); + printer->Print("$classname$[] values = new $classname$[$size$];\n", + "classname", descriptor_->name(), "size", + absl::StrCat(descriptor_->value_count() + 1)); + + for (int count = 0; count < interface_count; count++) { + printer->Print( + "$classname$[] values$count$ = $classname$$count$.values$count$();\n" + "for (int i = 0; i < values$count$.length; i++) {\n" + " values[ordinal] = values$count$[i];\n" + " ordinal++;\n" + "}\n\n", + "classname", descriptor_->name(), "count", absl::StrCat(count)); + } + printer->Print("values[$last_index$] = UNRECOGNIZED;\n", "last_index", + absl::StrCat(descriptor_->value_count())); + printer->Print("return values;\n"); + printer->Print("}\n\n"); + + printer->Print( + "\n" + "public static " + "com.google.protobuf.Internal.EnumLiteMap<$classname$>\n" + " internalGetValueMap() {\n" + " return internalValueMap;\n" + "}\n" + "private static final com.google.protobuf.Internal.EnumLiteMap<\n" + " $classname$> internalValueMap =\n" + " new " + "com.google.protobuf.Internal.EnumLiteMap<$classname$>() {\n" + " public $classname$ findValueByNumber(int number) {\n" + " return $classname$.forNumber(number);\n" + " }\n" + " };\n" + "\n", + "classname", descriptor_->name()); + + // ----------------------------------------------------------------- + // Reflection + + if (HasDescriptorMethods(descriptor_, context_->EnforceLite())) { + printer->Print( + "public final com.google.protobuf.Descriptors.EnumValueDescriptor\n" + " getValueDescriptor() {\n"); + if (!descriptor_->is_closed()) { + if (ordinal_is_index) { + printer->Print( + " if (this == UNRECOGNIZED) {\n" + " throw new java.lang.IllegalStateException(\n" + " \"Can't get the descriptor of an unrecognized enum " + "value.\");\n" + " }\n"); + } else { + printer->Print( + " if (index == -1) {\n" + " throw new java.lang.IllegalStateException(\n" + " \"Can't get the descriptor of an unrecognized enum " + "value.\");\n" + " }\n"); + } + } + printer->Print( + " return getDescriptor().getValues().get($index_text$);\n" + "}\n" + "public final com.google.protobuf.Descriptors.EnumDescriptor\n" + " getDescriptorForType() {\n" + " return getDescriptor();\n" + "}\n" + "public static final com.google.protobuf.Descriptors.EnumDescriptor\n" + " getDescriptor() {\n", + "index_text", index_text); + + // TODO: Cache statically? Note that we can't access descriptors + // at module init time because it wouldn't work with descriptor.proto, but + // we can cache the value the first time getDescriptor() is called. + if (descriptor_->containing_type() == nullptr) { + // The class generated for the File fully populates the descriptor with + // extensions in both the mutable and immutable cases. (In the mutable api + // this is accomplished by attempting to load the immutable outer class). + printer->Print( + " return $file$.getDescriptor().getEnumTypes().get($index$);\n", + "file", + name_resolver_->GetClassName(descriptor_->file(), immutable_api_), + "index", absl::StrCat(descriptor_->index())); + } else { + printer->Print( + " return $parent$.$descriptor$.getEnumTypes().get($index$);\n", + "parent", + name_resolver_->GetClassName(descriptor_->containing_type(), + immutable_api_), + "descriptor", + descriptor_->containing_type() + ->options() + .no_standard_descriptor_accessor() + ? "getDefaultInstance().getDescriptorForType()" + : "getDescriptor()", + "index", absl::StrCat(descriptor_->index())); + } + + printer->Print( + "}\n" + "\n"); + + printer->Print( + "\n" + "public static $classname$ valueOf(\n" + " com.google.protobuf.Descriptors.EnumValueDescriptor desc) {\n" + " if (desc.getType() != getDescriptor()) {\n" + " throw new java.lang.IllegalArgumentException(\n" + " \"EnumValueDescriptor is not for this type.\");\n" + " }\n", + "classname", descriptor_->name()); + if (!descriptor_->is_closed()) { + printer->Print( + " if (desc.getIndex() == -1) {\n" + " return UNRECOGNIZED;\n" + " }\n"); + } + printer->Print( + " return forNumber(desc.getIndex());\n" + "}\n" + "\n"); + + if (!ordinal_is_index) { + printer->Print("private final int index;\n"); + } + } + + printer->Print("\n"); + printer->Print( + "private final int value;\nprivate final String name;\nprivate final int " + "ordinal;\n"); + printer->Print( + "$classname$(int v, int o, String n){\n this.value = v;\n this.ordinal = " + "o;\n this.name=n;\n}", + "classname", descriptor_->name()); + printer->Print("\npublic int ordinal(){return ordinal;}\n"); + printer->Print("\npublic int value(){return value;}\n"); + + printer->Print("\npublic String name(){return name;}\n"); + + printer->Print( + "\n" + "@Override\n" + "public String toString() {\n" + " return name;\n" + "}\n"); + + printer->Print("}\n\n"); + for (int count = 0; count < interface_count; count++) { + printer->Print("interface $classname$$count$ { \n", "classname", + descriptor_->name(), "count", absl::StrCat(count)); + + int start = count * kMaxEnums; + for (int i = start; + i < std::min(start + kMaxEnums, (int)canonical_values_.size()); i++) { + absl::flat_hash_map vars; + vars["name"] = descriptor_->value(i)->name(); + vars["number"] = absl::StrCat(descriptor_->value(i)->number()); + vars["ordinal"] = absl::StrCat(descriptor_->value(i)->index()); + vars["deprecation"] = descriptor_->value(i)->options().deprecated() + ? "@java.lang.Deprecated " + : ""; + WriteEnumValueDocComment(printer, descriptor_->value(i), + context_->options()); + vars["classname"] = descriptor_->name(); + printer->Print(vars, + "$deprecation$public static final $classname$ $name$ = " + "new $classname$($number$, $ordinal$, \"$name$\");\n"); + } + printer->Print("\n"); + if (!context_->options().opensource_runtime) { + printer->Print( + "@com.google.protobuf.Internal.ProtoMethodMayReturnNull\n"); + } + + printer->Print( + "static $classname$ valueOf$count$(String name) {\n" + " switch (name) {\n", + "classname", descriptor_->name(), "count", absl::StrCat(count)); + printer->Indent(); + printer->Indent(); + for (int i = start; + i < std::min(start + kMaxEnums, (int)canonical_values_.size()); i++) { + printer->Print("case \"$name$\": return $name$;\n", "name", + canonical_values_[i]->name()); + } + printer->Print("default: return null;\n"); + printer->Outdent(); + printer->Print(" }\n"); + printer->Outdent(); + printer->Print("}\n\n"); + + if (!context_->options().opensource_runtime) { + printer->Print( + "@com.google.protobuf.Internal.ProtoMethodMayReturnNull\n"); + } + printer->Print( + "static $classname$ forNumber$count$(int value) {\n" + " switch (value) {\n", + "classname", descriptor_->name(), "count", absl::StrCat(count)); + printer->Indent(); + printer->Indent(); + + for (int i = start; + i < std::min(start + kMaxEnums, (int)canonical_values_.size()); i++) { + printer->Print("case $number$: return $name$;\n", "name", + canonical_values_[i]->name(), "number", + absl::StrCat(canonical_values_[i]->number())); + } + printer->Print("default: return null;\n"); + printer->Outdent(); + printer->Print(" }\n"); + printer->Outdent(); + printer->Print("}\n\n"); + + printer->Print("static $classname$[] values$count$() {\n", "classname", + descriptor_->name(), "count", absl::StrCat(count)); + printer->Indent(); + printer->Print("return new $classname$[] {", "classname", + descriptor_->name()); + for (int i = start; + i < std::min(start + kMaxEnums, (int)canonical_values_.size()); i++) { + printer->Print("$name$, ", "name", canonical_values_[i]->name()); + } + printer->Print("};\n"); + printer->Outdent(); + printer->Print("}\n\n"); + + printer->Print("}\n"); + } +} + bool EnumGenerator::CanUseEnumValues() { if (canonical_values_.size() != descriptor_->value_count()) { return false; diff --git a/src/google/protobuf/compiler/java/enum.h b/src/google/protobuf/compiler/java/enum.h index 4c020b76a8c39..b8e1136edb648 100644 --- a/src/google/protobuf/compiler/java/enum.h +++ b/src/google/protobuf/compiler/java/enum.h @@ -45,6 +45,7 @@ class EnumGenerator { ~EnumGenerator(); void Generate(io::Printer* printer); + void GenerateLarge(io::Printer* printer); private: const EnumDescriptor* descriptor_; diff --git a/src/google/protobuf/compiler/java/helpers.h b/src/google/protobuf/compiler/java/helpers.h index ed27eae1d5559..e7e8b792d36fe 100644 --- a/src/google/protobuf/compiler/java/helpers.h +++ b/src/google/protobuf/compiler/java/helpers.h @@ -387,6 +387,12 @@ inline bool CheckUtf8(const FieldDescriptor* descriptor) { descriptor->file()->options().java_string_check_utf8(); } +inline bool CheckLargeEnum(const EnumDescriptor* descriptor) { + return JavaGenerator::GetResolvedSourceFeatures(*descriptor) + .GetExtension(pb::java) + .large_enum(); +} + void WriteUInt32ToUtf16CharSequence(uint32_t number, std::vector* output);