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