1// Protocol Buffers - Google's data interchange format
2// Copyright 2008 Google Inc. All rights reserved.
3// https://developers.google.com/protocol-buffers/
4//
5// Redistribution and use in source and binary forms, with or without
6// modification, are permitted provided that the following conditions are
7// met:
8//
9// * Redistributions of source code must retain the above copyright
10// notice, this list of conditions and the following disclaimer.
11// * Redistributions in binary form must reproduce the above
12// copyright notice, this list of conditions and the following disclaimer
13// in the documentation and/or other materials provided with the
14// distribution.
15// * Neither the name of Google Inc. nor the names of its
16// contributors may be used to endorse or promote products derived from
17// this software without specific prior written permission.
18//
19// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31// Author: kenton@google.com (Kenton Varda)
32// Based on original Protocol Buffers design by
33// Sanjay Ghemawat, Jeff Dean, and others.
34
35#include <google/protobuf/compiler/java/enum.h>
36
37#include <map>
38#include <string>
39
40#include <google/protobuf/io/printer.h>
41#include <google/protobuf/stubs/strutil.h>
42#include <google/protobuf/compiler/java/context.h>
43#include <google/protobuf/compiler/java/doc_comment.h>
44#include <google/protobuf/compiler/java/helpers.h>
45#include <google/protobuf/compiler/java/name_resolver.h>
46#include <google/protobuf/descriptor.pb.h>
47
48// Must be last.
49#include <google/protobuf/port_def.inc>
50
51namespace google {
52namespace protobuf {
53namespace compiler {
54namespace java {
55
56EnumGenerator::EnumGenerator(const EnumDescriptor* descriptor,
57 bool immutable_api, Context* context)
58 : descriptor_(descriptor),
59 immutable_api_(immutable_api),
60 context_(context),
61 name_resolver_(context->GetNameResolver()) {
62 for (int i = 0; i < descriptor_->value_count(); i++) {
63 const EnumValueDescriptor* value = descriptor_->value(index: i);
64 const EnumValueDescriptor* canonical_value =
65 descriptor_->FindValueByNumber(number: value->number());
66
67 if (value == canonical_value) {
68 canonical_values_.push_back(x: value);
69 } else {
70 Alias alias;
71 alias.value = value;
72 alias.canonical_value = canonical_value;
73 aliases_.push_back(x: alias);
74 }
75 }
76}
77
78EnumGenerator::~EnumGenerator() {}
79
80void EnumGenerator::Generate(io::Printer* printer) {
81 WriteEnumDocComment(printer, enum_: descriptor_);
82 MaybePrintGeneratedAnnotation(context: context_, printer, descriptor: descriptor_, immutable: immutable_api_);
83 printer->Print(
84 text: "$deprecation$public enum $classname$\n"
85 " implements com.google.protobuf.ProtocolMessageEnum {\n",
86 args: "classname", args: descriptor_->name(), args: "deprecation",
87 args: descriptor_->options().deprecated() ? "@java.lang.Deprecated " : "");
88 printer->Annotate(varname: "classname", descriptor: descriptor_);
89 printer->Indent();
90
91 bool ordinal_is_index = true;
92 std::string index_text = "ordinal()";
93 for (int i = 0; i < canonical_values_.size(); i++) {
94 if (canonical_values_[i]->index() != i) {
95 ordinal_is_index = false;
96 index_text = "index";
97 break;
98 }
99 }
100
101 for (int i = 0; i < canonical_values_.size(); i++) {
102 std::map<std::string, std::string> vars;
103 vars["name"] = canonical_values_[i]->name();
104 vars["index"] = StrCat(a: canonical_values_[i]->index());
105 vars["number"] = StrCat(a: canonical_values_[i]->number());
106 WriteEnumValueDocComment(printer, value: canonical_values_[i]);
107 if (canonical_values_[i]->options().deprecated()) {
108 printer->Print(text: "@java.lang.Deprecated\n");
109 }
110 if (ordinal_is_index) {
111 printer->Print(variables: vars, text: "$name$($number$),\n");
112 } else {
113 printer->Print(variables: vars, text: "$name$($index$, $number$),\n");
114 }
115 printer->Annotate(varname: "name", descriptor: canonical_values_[i]);
116 }
117
118 if (SupportUnknownEnumValue(descriptor: descriptor_->file())) {
119 if (ordinal_is_index) {
120 printer->Print(text: "${$UNRECOGNIZED$}$(-1),\n", args: "{", args: "", args: "}", args: "");
121 } else {
122 printer->Print(text: "${$UNRECOGNIZED$}$(-1, -1),\n", args: "{", args: "", args: "}", args: "");
123 }
124 printer->Annotate(begin_varname: "{", end_varname: "}", descriptor: descriptor_);
125 }
126
127 printer->Print(
128 text: ";\n"
129 "\n");
130
131 // -----------------------------------------------------------------
132
133 for (int i = 0; i < aliases_.size(); i++) {
134 std::map<std::string, std::string> vars;
135 vars["classname"] = descriptor_->name();
136 vars["name"] = aliases_[i].value->name();
137 vars["canonical_name"] = aliases_[i].canonical_value->name();
138 WriteEnumValueDocComment(printer, value: aliases_[i].value);
139 printer->Print(
140 variables: vars, text: "public static final $classname$ $name$ = $canonical_name$;\n");
141 printer->Annotate(varname: "name", descriptor: aliases_[i].value);
142 }
143
144 for (int i = 0; i < descriptor_->value_count(); i++) {
145 std::map<std::string, std::string> vars;
146 vars["name"] = descriptor_->value(index: i)->name();
147 vars["number"] = StrCat(a: descriptor_->value(index: i)->number());
148 vars["{"] = "";
149 vars["}"] = "";
150 vars["deprecation"] = descriptor_->value(index: i)->options().deprecated()
151 ? "@java.lang.Deprecated "
152 : "";
153 WriteEnumValueDocComment(printer, value: descriptor_->value(index: i));
154 printer->Print(variables: vars,
155 text: "$deprecation$public static final int ${$$name$_VALUE$}$ = "
156 "$number$;\n");
157 printer->Annotate(begin_varname: "{", end_varname: "}", descriptor: descriptor_->value(index: i));
158 }
159 printer->Print(text: "\n");
160
161 // -----------------------------------------------------------------
162
163 printer->Print(
164 text: "\n"
165 "public final int getNumber() {\n");
166 if (SupportUnknownEnumValue(descriptor: descriptor_->file())) {
167 if (ordinal_is_index) {
168 printer->Print(
169 text: " if (this == UNRECOGNIZED) {\n"
170 " throw new java.lang.IllegalArgumentException(\n"
171 " \"Can't get the number of an unknown enum value.\");\n"
172 " }\n");
173 } else {
174 printer->Print(
175 text: " if (index == -1) {\n"
176 " throw new java.lang.IllegalArgumentException(\n"
177 " \"Can't get the number of an unknown enum value.\");\n"
178 " }\n");
179 }
180 }
181 printer->Print(
182 text: " return value;\n"
183 "}\n"
184 "\n"
185 "/**\n"
186 " * @param value The numeric wire value of the corresponding enum "
187 "entry.\n"
188 " * @return The enum associated with the given numeric wire value.\n"
189 " * @deprecated Use {@link #forNumber(int)} instead.\n"
190 " */\n"
191 "@java.lang.Deprecated\n"
192 "public static $classname$ valueOf(int value) {\n"
193 " return forNumber(value);\n"
194 "}\n"
195 "\n"
196 "/**\n"
197 " * @param value The numeric wire value of the corresponding enum "
198 "entry.\n"
199 " * @return The enum associated with the given numeric wire value.\n"
200 " */\n"
201 "public static $classname$ forNumber(int value) {\n"
202 " switch (value) {\n",
203 args: "classname", args: descriptor_->name());
204 printer->Indent();
205 printer->Indent();
206
207 for (int i = 0; i < canonical_values_.size(); i++) {
208 printer->Print(text: "case $number$: return $name$;\n", args: "name",
209 args: canonical_values_[i]->name(), args: "number",
210 args: StrCat(a: canonical_values_[i]->number()));
211 }
212
213 printer->Outdent();
214 printer->Outdent();
215 printer->Print(
216 text: " default: return null;\n"
217 " }\n"
218 "}\n"
219 "\n"
220 "public static com.google.protobuf.Internal.EnumLiteMap<$classname$>\n"
221 " internalGetValueMap() {\n"
222 " return internalValueMap;\n"
223 "}\n"
224 "private static final com.google.protobuf.Internal.EnumLiteMap<\n"
225 " $classname$> internalValueMap =\n"
226 " new com.google.protobuf.Internal.EnumLiteMap<$classname$>() {\n"
227 " public $classname$ findValueByNumber(int number) {\n"
228 " return $classname$.forNumber(number);\n"
229 " }\n"
230 " };\n"
231 "\n",
232 args: "classname", args: descriptor_->name());
233
234 // -----------------------------------------------------------------
235 // Reflection
236
237 if (HasDescriptorMethods(descriptor_, enforce_lite: context_->EnforceLite())) {
238 printer->Print(
239 text: "public final com.google.protobuf.Descriptors.EnumValueDescriptor\n"
240 " getValueDescriptor() {\n");
241 if (SupportUnknownEnumValue(descriptor: descriptor_->file())) {
242 if (ordinal_is_index) {
243 printer->Print(
244 text: " if (this == UNRECOGNIZED) {\n"
245 " throw new java.lang.IllegalStateException(\n"
246 " \"Can't get the descriptor of an unrecognized enum "
247 "value.\");\n"
248 " }\n");
249 } else {
250 printer->Print(
251 text: " if (index == -1) {\n"
252 " throw new java.lang.IllegalStateException(\n"
253 " \"Can't get the descriptor of an unrecognized enum "
254 "value.\");\n"
255 " }\n");
256 }
257 }
258 printer->Print(
259 text: " return getDescriptor().getValues().get($index_text$);\n"
260 "}\n"
261 "public final com.google.protobuf.Descriptors.EnumDescriptor\n"
262 " getDescriptorForType() {\n"
263 " return getDescriptor();\n"
264 "}\n"
265 "public static final com.google.protobuf.Descriptors.EnumDescriptor\n"
266 " getDescriptor() {\n",
267 args: "index_text", args: index_text);
268
269 // TODO(kenton): Cache statically? Note that we can't access descriptors
270 // at module init time because it wouldn't work with descriptor.proto, but
271 // we can cache the value the first time getDescriptor() is called.
272 if (descriptor_->containing_type() == NULL) {
273 // The class generated for the File fully populates the descriptor with
274 // extensions in both the mutable and immutable cases. (In the mutable api
275 // this is accomplished by attempting to load the immutable outer class).
276 printer->Print(
277 text: " return $file$.getDescriptor().getEnumTypes().get($index$);\n",
278 args: "file",
279 args: name_resolver_->GetClassName(descriptor: descriptor_->file(), immutable: immutable_api_),
280 args: "index", args: StrCat(a: descriptor_->index()));
281 } else {
282 printer->Print(
283 text: " return $parent$.$descriptor$.getEnumTypes().get($index$);\n",
284 args: "parent",
285 args: name_resolver_->GetClassName(descriptor: descriptor_->containing_type(),
286 immutable: immutable_api_),
287 args: "descriptor",
288 args: descriptor_->containing_type()
289 ->options()
290 .no_standard_descriptor_accessor()
291 ? "getDefaultInstance().getDescriptorForType()"
292 : "getDescriptor()",
293 args: "index", args: StrCat(a: descriptor_->index()));
294 }
295
296 printer->Print(
297 text: "}\n"
298 "\n"
299 "private static final $classname$[] VALUES = ",
300 args: "classname", args: descriptor_->name());
301
302 if (CanUseEnumValues()) {
303 // If the constants we are going to output are exactly the ones we
304 // have declared in the Java enum in the same order, then we can use
305 // the values() method that the Java compiler automatically generates
306 // for every enum.
307 printer->Print(text: "values();\n");
308 } else {
309 printer->Print(text: "getStaticValuesArray();\n");
310 printer->Print(text: "private static $classname$[] getStaticValuesArray() {\n",
311 args: "classname", args: descriptor_->name());
312 printer->Indent();
313 printer->Print(
314 text: "return new $classname$[] {\n"
315 " ",
316 args: "classname", args: descriptor_->name());
317 for (int i = 0; i < descriptor_->value_count(); i++) {
318 printer->Print(text: "$name$, ", args: "name", args: descriptor_->value(index: i)->name());
319 }
320 printer->Print(
321 text: "\n"
322 "};\n");
323 printer->Outdent();
324 printer->Print(text: "}");
325 }
326
327 printer->Print(
328 text: "\n"
329 "public static $classname$ valueOf(\n"
330 " com.google.protobuf.Descriptors.EnumValueDescriptor desc) {\n"
331 " if (desc.getType() != getDescriptor()) {\n"
332 " throw new java.lang.IllegalArgumentException(\n"
333 " \"EnumValueDescriptor is not for this type.\");\n"
334 " }\n",
335 args: "classname", args: descriptor_->name());
336 if (SupportUnknownEnumValue(descriptor: descriptor_->file())) {
337 printer->Print(
338 text: " if (desc.getIndex() == -1) {\n"
339 " return UNRECOGNIZED;\n"
340 " }\n");
341 }
342 printer->Print(
343 text: " return VALUES[desc.getIndex()];\n"
344 "}\n"
345 "\n");
346
347 if (!ordinal_is_index) {
348 printer->Print(text: "private final int index;\n");
349 }
350 }
351
352 // -----------------------------------------------------------------
353
354 printer->Print(text: "private final int value;\n\n");
355
356 if (ordinal_is_index) {
357 printer->Print(text: "private $classname$(int value) {\n", args: "classname",
358 args: descriptor_->name());
359 } else {
360 printer->Print(text: "private $classname$(int index, int value) {\n", args: "classname",
361 args: descriptor_->name());
362 }
363 if (HasDescriptorMethods(descriptor_, enforce_lite: context_->EnforceLite()) &&
364 !ordinal_is_index) {
365 printer->Print(text: " this.index = index;\n");
366 }
367 printer->Print(
368 text: " this.value = value;\n"
369 "}\n");
370
371 printer->Print(
372 text: "\n"
373 "// @@protoc_insertion_point(enum_scope:$full_name$)\n",
374 args: "full_name", args: descriptor_->full_name());
375
376 printer->Outdent();
377 printer->Print(text: "}\n\n");
378}
379
380bool EnumGenerator::CanUseEnumValues() {
381 if (canonical_values_.size() != descriptor_->value_count()) {
382 return false;
383 }
384 for (int i = 0; i < descriptor_->value_count(); i++) {
385 if (descriptor_->value(index: i)->name() != canonical_values_[i]->name()) {
386 return false;
387 }
388 }
389 return true;
390}
391
392} // namespace java
393} // namespace compiler
394} // namespace protobuf
395} // namespace google
396
397#include <google/protobuf/port_undef.inc>
398