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: [email protected] (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
51 namespace google {
52 namespace protobuf {
53 namespace compiler {
54 namespace java {
55
EnumGenerator(const EnumDescriptor * descriptor,bool immutable_api,Context * context)56 EnumGenerator::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(i);
64 const EnumValueDescriptor* canonical_value =
65 descriptor_->FindValueByNumber(value->number());
66
67 if (value == canonical_value) {
68 canonical_values_.push_back(value);
69 } else {
70 Alias alias;
71 alias.value = value;
72 alias.canonical_value = canonical_value;
73 aliases_.push_back(alias);
74 }
75 }
76 }
77
~EnumGenerator()78 EnumGenerator::~EnumGenerator() {}
79
Generate(io::Printer * printer)80 void EnumGenerator::Generate(io::Printer* printer) {
81 WriteEnumDocComment(printer, descriptor_);
82 MaybePrintGeneratedAnnotation(context_, printer, descriptor_, immutable_api_);
83 printer->Print(
84 "$deprecation$public enum $classname$\n"
85 " implements com.google.protobuf.ProtocolMessageEnum {\n",
86 "classname", descriptor_->name(), "deprecation",
87 descriptor_->options().deprecated() ? "@java.lang.Deprecated " : "");
88 printer->Annotate("classname", 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(canonical_values_[i]->index());
105 vars["number"] = StrCat(canonical_values_[i]->number());
106 WriteEnumValueDocComment(printer, canonical_values_[i]);
107 if (canonical_values_[i]->options().deprecated()) {
108 printer->Print("@java.lang.Deprecated\n");
109 }
110 if (ordinal_is_index) {
111 printer->Print(vars, "$name$($number$),\n");
112 } else {
113 printer->Print(vars, "$name$($index$, $number$),\n");
114 }
115 printer->Annotate("name", canonical_values_[i]);
116 }
117
118 if (SupportUnknownEnumValue(descriptor_->file())) {
119 if (ordinal_is_index) {
120 printer->Print("${$UNRECOGNIZED$}$(-1),\n", "{", "", "}", "");
121 } else {
122 printer->Print("${$UNRECOGNIZED$}$(-1, -1),\n", "{", "", "}", "");
123 }
124 printer->Annotate("{", "}", descriptor_);
125 }
126
127 printer->Print(
128 ";\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, aliases_[i].value);
139 printer->Print(
140 vars, "public static final $classname$ $name$ = $canonical_name$;\n");
141 printer->Annotate("name", 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(i)->name();
147 vars["number"] = StrCat(descriptor_->value(i)->number());
148 vars["{"] = "";
149 vars["}"] = "";
150 vars["deprecation"] = descriptor_->value(i)->options().deprecated()
151 ? "@java.lang.Deprecated "
152 : "";
153 WriteEnumValueDocComment(printer, descriptor_->value(i));
154 printer->Print(vars,
155 "$deprecation$public static final int ${$$name$_VALUE$}$ = "
156 "$number$;\n");
157 printer->Annotate("{", "}", descriptor_->value(i));
158 }
159 printer->Print("\n");
160
161 // -----------------------------------------------------------------
162
163 printer->Print(
164 "\n"
165 "public final int getNumber() {\n");
166 if (SupportUnknownEnumValue(descriptor_->file())) {
167 if (ordinal_is_index) {
168 printer->Print(
169 " 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 " 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 " 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 "classname", descriptor_->name());
204 printer->Indent();
205 printer->Indent();
206
207 for (int i = 0; i < canonical_values_.size(); i++) {
208 printer->Print("case $number$: return $name$;\n", "name",
209 canonical_values_[i]->name(), "number",
210 StrCat(canonical_values_[i]->number()));
211 }
212
213 printer->Outdent();
214 printer->Outdent();
215 printer->Print(
216 " 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 "classname", descriptor_->name());
233
234 // -----------------------------------------------------------------
235 // Reflection
236
237 if (HasDescriptorMethods(descriptor_, context_->EnforceLite())) {
238 printer->Print(
239 "public final com.google.protobuf.Descriptors.EnumValueDescriptor\n"
240 " getValueDescriptor() {\n");
241 if (SupportUnknownEnumValue(descriptor_->file())) {
242 if (ordinal_is_index) {
243 printer->Print(
244 " 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 " 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 " 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 "index_text", 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 " return $file$.getDescriptor().getEnumTypes().get($index$);\n",
278 "file",
279 name_resolver_->GetClassName(descriptor_->file(), immutable_api_),
280 "index", StrCat(descriptor_->index()));
281 } else {
282 printer->Print(
283 " return $parent$.$descriptor$.getEnumTypes().get($index$);\n",
284 "parent",
285 name_resolver_->GetClassName(descriptor_->containing_type(),
286 immutable_api_),
287 "descriptor",
288 descriptor_->containing_type()
289 ->options()
290 .no_standard_descriptor_accessor()
291 ? "getDefaultInstance().getDescriptorForType()"
292 : "getDescriptor()",
293 "index", StrCat(descriptor_->index()));
294 }
295
296 printer->Print(
297 "}\n"
298 "\n"
299 "private static final $classname$[] VALUES = ",
300 "classname", 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("values();\n");
308 } else {
309 printer->Print("getStaticValuesArray();\n");
310 printer->Print("private static $classname$[] getStaticValuesArray() {\n",
311 "classname", descriptor_->name());
312 printer->Indent();
313 printer->Print(
314 "return new $classname$[] {\n"
315 " ",
316 "classname", descriptor_->name());
317 for (int i = 0; i < descriptor_->value_count(); i++) {
318 printer->Print("$name$, ", "name", descriptor_->value(i)->name());
319 }
320 printer->Print(
321 "\n"
322 "};\n");
323 printer->Outdent();
324 printer->Print("}");
325 }
326
327 printer->Print(
328 "\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 "classname", descriptor_->name());
336 if (SupportUnknownEnumValue(descriptor_->file())) {
337 printer->Print(
338 " if (desc.getIndex() == -1) {\n"
339 " return UNRECOGNIZED;\n"
340 " }\n");
341 }
342 printer->Print(
343 " return VALUES[desc.getIndex()];\n"
344 "}\n"
345 "\n");
346
347 if (!ordinal_is_index) {
348 printer->Print("private final int index;\n");
349 }
350 }
351
352 // -----------------------------------------------------------------
353
354 printer->Print("private final int value;\n\n");
355
356 if (ordinal_is_index) {
357 printer->Print("private $classname$(int value) {\n", "classname",
358 descriptor_->name());
359 } else {
360 printer->Print("private $classname$(int index, int value) {\n", "classname",
361 descriptor_->name());
362 }
363 if (HasDescriptorMethods(descriptor_, context_->EnforceLite()) &&
364 !ordinal_is_index) {
365 printer->Print(" this.index = index;\n");
366 }
367 printer->Print(
368 " this.value = value;\n"
369 "}\n");
370
371 printer->Print(
372 "\n"
373 "// @@protoc_insertion_point(enum_scope:$full_name$)\n",
374 "full_name", descriptor_->full_name());
375
376 printer->Outdent();
377 printer->Print("}\n\n");
378 }
379
CanUseEnumValues()380 bool 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(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