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 | |
51 | namespace google { |
52 | namespace protobuf { |
53 | namespace compiler { |
54 | namespace java { |
55 | |
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(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 | |
78 | EnumGenerator::~EnumGenerator() {} |
79 | |
80 | void 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 | |
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(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 | |