• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc.  All rights reserved.
3 //
4 // Use of this source code is governed by a BSD-style
5 // license that can be found in the LICENSE file or at
6 // https://developers.google.com/open-source/licenses/bsd
7 
8 // Author: kenton@google.com (Kenton Varda)
9 //  Based on original Protocol Buffers design by
10 //  Sanjay Ghemawat, Jeff Dean, and others.
11 
12 #include "google/protobuf/compiler/java/full/enum.h"
13 
14 #include <string>
15 #include <utility>
16 #include <vector>
17 
18 #include "absl/container/flat_hash_map.h"
19 #include "absl/strings/str_cat.h"
20 #include "google/protobuf/compiler/java/context.h"
21 #include "google/protobuf/compiler/java/doc_comment.h"
22 #include "google/protobuf/compiler/java/helpers.h"
23 #include "google/protobuf/compiler/java/internal_helpers.h"
24 #include "google/protobuf/compiler/java/name_resolver.h"
25 #include "google/protobuf/descriptor.h"
26 #include "google/protobuf/descriptor.pb.h"
27 #include "google/protobuf/io/printer.h"
28 
29 // Must be last.
30 #include "google/protobuf/port_def.inc"
31 
32 namespace google {
33 namespace protobuf {
34 namespace compiler {
35 namespace java {
36 
EnumNonLiteGenerator(const EnumDescriptor * descriptor,bool immutable_api,Context * context)37 EnumNonLiteGenerator::EnumNonLiteGenerator(const EnumDescriptor* descriptor,
38                                            bool immutable_api, Context* context)
39     : descriptor_(descriptor),
40       immutable_api_(immutable_api),
41       context_(context),
42       name_resolver_(context->GetNameResolver()) {
43   for (int i = 0; i < descriptor_->value_count(); i++) {
44     const EnumValueDescriptor* value = descriptor_->value(i);
45     const EnumValueDescriptor* canonical_value =
46         descriptor_->FindValueByNumber(value->number());
47 
48     if (value == canonical_value) {
49       canonical_values_.push_back(value);
50     } else {
51       Alias alias;
52       alias.value = value;
53       alias.canonical_value = canonical_value;
54       aliases_.push_back(alias);
55     }
56   }
57 }
58 
Generate(io::Printer * printer)59 void EnumNonLiteGenerator::Generate(io::Printer* printer) {
60   WriteEnumDocComment(printer, descriptor_, context_->options());
61   MaybePrintGeneratedAnnotation(context_, printer, descriptor_, immutable_api_);
62 
63 
64   if (!context_->options().opensource_runtime) {
65     printer->Print("@com.google.protobuf.Internal.ProtoNonnullApi\n");
66   }
67   printer->Print(
68       "$deprecation$public enum $classname$\n"
69       "    implements com.google.protobuf.ProtocolMessageEnum {\n",
70       "classname", descriptor_->name(), "deprecation",
71       descriptor_->options().deprecated() ? "@java.lang.Deprecated " : "");
72   printer->Annotate("classname", descriptor_);
73   printer->Indent();
74 
75   bool ordinal_is_index = true;
76   std::string index_text = "ordinal()";
77   for (int i = 0; i < canonical_values_.size(); i++) {
78     if (canonical_values_[i]->index() != i) {
79       ordinal_is_index = false;
80       index_text = "index";
81       break;
82     }
83   }
84 
85   for (int i = 0; i < canonical_values_.size(); i++) {
86     absl::flat_hash_map<absl::string_view, std::string> vars;
87     vars["name"] = canonical_values_[i]->name();
88     vars["index"] = absl::StrCat(canonical_values_[i]->index());
89     vars["number"] = absl::StrCat(canonical_values_[i]->number());
90     WriteEnumValueDocComment(printer, canonical_values_[i],
91                              context_->options());
92     if (canonical_values_[i]->options().deprecated()) {
93       printer->Print("@java.lang.Deprecated\n");
94     }
95     if (ordinal_is_index) {
96       printer->Print(vars, "$name$($number$),\n");
97     } else {
98       printer->Print(vars, "$name$($index$, $number$),\n");
99     }
100     printer->Annotate("name", canonical_values_[i]);
101   }
102 
103   if (!descriptor_->is_closed()) {
104     if (ordinal_is_index) {
105       printer->Print("${$UNRECOGNIZED$}$(-1),\n", "{", "", "}", "");
106     } else {
107       printer->Print("${$UNRECOGNIZED$}$(-1, -1),\n", "{", "", "}", "");
108     }
109     printer->Annotate("{", "}", descriptor_);
110   }
111 
112   printer->Print(
113       ";\n"
114       "\n");
115 
116   // -----------------------------------------------------------------
117 
118   printer->Print("static {\n");
119   printer->Indent();
120   PrintGencodeVersionValidator(printer, context_->options().opensource_runtime,
121                                descriptor_->name());
122   printer->Outdent();
123   printer->Print("}\n");
124 
125   for (int i = 0; i < aliases_.size(); i++) {
126     absl::flat_hash_map<absl::string_view, std::string> vars;
127     vars["classname"] = descriptor_->name();
128     vars["name"] = aliases_[i].value->name();
129     vars["canonical_name"] = aliases_[i].canonical_value->name();
130     WriteEnumValueDocComment(printer, aliases_[i].value, context_->options());
131     printer->Print(
132         vars, "public static final $classname$ $name$ = $canonical_name$;\n");
133     printer->Annotate("name", aliases_[i].value);
134   }
135 
136   for (int i = 0; i < descriptor_->value_count(); i++) {
137     absl::flat_hash_map<absl::string_view, std::string> vars;
138     vars["name"] = descriptor_->value(i)->name();
139     vars["number"] = absl::StrCat(descriptor_->value(i)->number());
140     vars["{"] = "";
141     vars["}"] = "";
142     vars["deprecation"] = descriptor_->value(i)->options().deprecated()
143                               ? "@java.lang.Deprecated "
144                               : "";
145     WriteEnumValueDocComment(printer, descriptor_->value(i),
146                              context_->options());
147     printer->Print(vars,
148                    "$deprecation$public static final int ${$$name$_VALUE$}$ = "
149                    "$number$;\n");
150     printer->Annotate("{", "}", descriptor_->value(i));
151   }
152   printer->Print("\n");
153 
154   // -----------------------------------------------------------------
155 
156   printer->Print(
157       "\n"
158       "public final int getNumber() {\n");
159   if (!descriptor_->is_closed()) {
160     if (ordinal_is_index) {
161       printer->Print(
162           "  if (this == UNRECOGNIZED) {\n"
163           "    throw new java.lang.IllegalArgumentException(\n"
164           "        \"Can't get the number of an unknown enum value.\");\n"
165           "  }\n");
166     } else {
167       printer->Print(
168           "  if (index == -1) {\n"
169           "    throw new java.lang.IllegalArgumentException(\n"
170           "        \"Can't get the number of an unknown enum value.\");\n"
171           "  }\n");
172     }
173   }
174   printer->Print(
175       "  return value;\n"
176       "}\n"
177       "\n");
178   if (context_->options().opensource_runtime) {
179     printer->Print(
180         "/**\n"
181         " * @param value The numeric wire value of the corresponding enum "
182         "entry.\n"
183         " * @return The enum associated with the given numeric wire value.\n"
184         " * @deprecated Use {@link #forNumber(int)} instead.\n"
185         " */\n"
186         "@java.lang.Deprecated\n"
187         "public static $classname$ valueOf(int value) {\n"
188         "  return forNumber(value);\n"
189         "}\n"
190         "\n",
191         "classname", descriptor_->name());
192   }
193   printer->Print(
194       "/**\n"
195       " * @param value The numeric wire value of the corresponding enum "
196       "entry.\n"
197       " * @return The enum associated with the given numeric wire value.\n"
198       " */\n");
199   if (!context_->options().opensource_runtime) {
200     printer->Print("@com.google.protobuf.Internal.ProtoMethodMayReturnNull\n");
201   }
202   printer->Print(
203       "public static $classname$ forNumber(int value) {\n"
204       "  switch (value) {\n",
205       "classname", descriptor_->name());
206   printer->Indent();
207   printer->Indent();
208 
209   for (int i = 0; i < canonical_values_.size(); i++) {
210     printer->Print("case $number$: return $name$;\n", "name",
211                    canonical_values_[i]->name(), "number",
212                    absl::StrCat(canonical_values_[i]->number()));
213   }
214 
215   printer->Outdent();
216   printer->Outdent();
217   printer->Print(
218       "    default: return null;\n"
219       "  }\n"
220       "}\n"
221       "\n"
222       "public static com.google.protobuf.Internal.EnumLiteMap<$classname$>\n"
223       "    internalGetValueMap() {\n"
224       "  return internalValueMap;\n"
225       "}\n"
226       "private static final com.google.protobuf.Internal.EnumLiteMap<\n"
227       "    $classname$> internalValueMap =\n"
228       "      new com.google.protobuf.Internal.EnumLiteMap<$classname$>() {\n"
229       "        public $classname$ findValueByNumber(int number) {\n"
230       "          return $classname$.forNumber(number);\n"
231       "        }\n"
232       "      };\n"
233       "\n",
234       "classname", descriptor_->name());
235 
236   // -----------------------------------------------------------------
237   // Reflection
238 
239   if (HasDescriptorMethods(descriptor_, context_->EnforceLite())) {
240     printer->Print(
241         "public final com.google.protobuf.Descriptors.EnumValueDescriptor\n"
242         "    getValueDescriptor() {\n");
243     if (!descriptor_->is_closed()) {
244       if (ordinal_is_index) {
245         printer->Print(
246             "  if (this == UNRECOGNIZED) {\n"
247             "    throw new java.lang.IllegalStateException(\n"
248             "        \"Can't get the descriptor of an unrecognized enum "
249             "value.\");\n"
250             "  }\n");
251       } else {
252         printer->Print(
253             "  if (index == -1) {\n"
254             "    throw new java.lang.IllegalStateException(\n"
255             "        \"Can't get the descriptor of an unrecognized enum "
256             "value.\");\n"
257             "  }\n");
258       }
259     }
260     printer->Print(
261         "  return getDescriptor().getValues().get($index_text$);\n"
262         "}\n"
263         "public final com.google.protobuf.Descriptors.EnumDescriptor\n"
264         "    getDescriptorForType() {\n"
265         "  return getDescriptor();\n"
266         "}\n"
267         "public static final com.google.protobuf.Descriptors.EnumDescriptor\n"
268         "    getDescriptor() {\n",
269         "index_text", index_text);
270 
271     // TODO:  Cache statically?  Note that we can't access descriptors
272     //   at module init time because it wouldn't work with descriptor.proto, but
273     //   we can cache the value the first time getDescriptor() is called.
274     if (descriptor_->containing_type() == nullptr) {
275       // The class generated for the File fully populates the descriptor with
276       // extensions in both the mutable and immutable cases. (In the mutable api
277       // this is accomplished by attempting to load the immutable outer class).
278       printer->Print(
279           "  return $file$.getDescriptor().getEnumTypes().get($index$);\n",
280           "file",
281           name_resolver_->GetClassName(descriptor_->file(), immutable_api_),
282           "index", absl::StrCat(descriptor_->index()));
283     } else {
284       printer->Print(
285           "  return $parent$.$descriptor$.getEnumTypes().get($index$);\n",
286           "parent",
287           name_resolver_->GetClassName(descriptor_->containing_type(),
288                                        immutable_api_),
289           "descriptor",
290           descriptor_->containing_type()
291                   ->options()
292                   .no_standard_descriptor_accessor()
293               ? "getDefaultInstance().getDescriptorForType()"
294               : "getDescriptor()",
295           "index", absl::StrCat(descriptor_->index()));
296     }
297 
298     printer->Print(
299         "}\n"
300         "\n"
301         "private static final $classname$[] VALUES = ",
302         "classname", descriptor_->name());
303 
304     if (CanUseEnumValues()) {
305       // If the constants we are going to output are exactly the ones we
306       // have declared in the Java enum in the same order, then we can use
307       // the values() method that the Java compiler automatically generates
308       // for every enum.
309       printer->Print("values();\n");
310     } else {
311       printer->Print("getStaticValuesArray();\n");
312       printer->Print("private static $classname$[] getStaticValuesArray() {\n",
313                      "classname", descriptor_->name());
314       printer->Indent();
315       printer->Print(
316           "return new $classname$[] {\n"
317           "  ",
318           "classname", descriptor_->name());
319       for (int i = 0; i < descriptor_->value_count(); i++) {
320         printer->Print("$name$, ", "name", descriptor_->value(i)->name());
321       }
322       printer->Print(
323           "\n"
324           "};\n");
325       printer->Outdent();
326       printer->Print("}");
327     }
328 
329     printer->Print(
330         "\n"
331         "public static $classname$ valueOf(\n"
332         "    com.google.protobuf.Descriptors.EnumValueDescriptor desc) {\n"
333         "  if (desc.getType() != getDescriptor()) {\n"
334         "    throw new java.lang.IllegalArgumentException(\n"
335         "      \"EnumValueDescriptor is not for this type.\");\n"
336         "  }\n",
337         "classname", descriptor_->name());
338     if (!descriptor_->is_closed()) {
339       printer->Print(
340           "  if (desc.getIndex() == -1) {\n"
341           "    return UNRECOGNIZED;\n"
342           "  }\n");
343     }
344     printer->Print(
345         "  return VALUES[desc.getIndex()];\n"
346         "}\n"
347         "\n");
348 
349     if (!ordinal_is_index) {
350       printer->Print("private final int index;\n");
351     }
352   }
353 
354   // -----------------------------------------------------------------
355 
356   printer->Print("private final int value;\n\n");
357 
358   if (ordinal_is_index) {
359     printer->Print("private $classname$(int value) {\n", "classname",
360                    descriptor_->name());
361   } else {
362     printer->Print("private $classname$(int index, int value) {\n", "classname",
363                    descriptor_->name());
364   }
365   if (HasDescriptorMethods(descriptor_, context_->EnforceLite()) &&
366       !ordinal_is_index) {
367     printer->Print("  this.index = index;\n");
368   }
369   printer->Print(
370       "  this.value = value;\n"
371       "}\n");
372 
373   printer->Print(
374       "\n"
375       "// @@protoc_insertion_point(enum_scope:$full_name$)\n",
376       "full_name", descriptor_->full_name());
377 
378   printer->Outdent();
379   printer->Print("}\n\n");
380 }
381 
CanUseEnumValues()382 bool EnumNonLiteGenerator::CanUseEnumValues() {
383   if (canonical_values_.size() != descriptor_->value_count()) {
384     return false;
385   }
386   for (int i = 0; i < descriptor_->value_count(); i++) {
387     if (descriptor_->value(i)->name() != canonical_values_[i]->name()) {
388       return false;
389     }
390   }
391   return true;
392 }
393 
394 }  // namespace java
395 }  // namespace compiler
396 }  // namespace protobuf
397 }  // namespace google
398 
399 #include "google/protobuf/port_undef.inc"
400