blob: 5fc9b002fd6fe91b4f2509952be11aca96fecf9d [file] [log] [blame]
temporal40ee5512008-07-10 02:12:20 +00001// Protocol Buffers - Google's data interchange format
kenton@google.com24bf56f2008-09-24 20:31:01 +00002// Copyright 2008 Google Inc. All rights reserved.
Feng Xiaoe4288622014-10-01 16:26:23 -07003// https://developers.google.com/protocol-buffers/
temporal40ee5512008-07-10 02:12:20 +00004//
kenton@google.com24bf56f2008-09-24 20:31:01 +00005// Redistribution and use in source and binary forms, with or without
6// modification, are permitted provided that the following conditions are
7// met:
temporal40ee5512008-07-10 02:12:20 +00008//
kenton@google.com24bf56f2008-09-24 20:31:01 +00009// * 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.
temporal40ee5512008-07-10 02:12:20 +000018//
kenton@google.com24bf56f2008-09-24 20:31:01 +000019// 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.
temporal40ee5512008-07-10 02:12:20 +000030
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 <map>
36#include <string>
37
jieluo@google.com4de8f552014-07-18 00:47:59 +000038#include <google/protobuf/compiler/java/java_context.h>
temporal40ee5512008-07-10 02:12:20 +000039#include <google/protobuf/compiler/java/java_enum.h>
xiaofeng@google.comb55a20f2012-09-22 02:40:50 +000040#include <google/protobuf/compiler/java/java_doc_comment.h>
temporal40ee5512008-07-10 02:12:20 +000041#include <google/protobuf/compiler/java/java_helpers.h>
jieluo@google.com4de8f552014-07-18 00:47:59 +000042#include <google/protobuf/compiler/java/java_name_resolver.h>
temporal40ee5512008-07-10 02:12:20 +000043#include <google/protobuf/io/printer.h>
44#include <google/protobuf/descriptor.pb.h>
45#include <google/protobuf/stubs/strutil.h>
46
47namespace google {
48namespace protobuf {
49namespace compiler {
50namespace java {
51
jieluo@google.com4de8f552014-07-18 00:47:59 +000052namespace {
53bool EnumHasCustomOptions(const EnumDescriptor* descriptor) {
54 if (descriptor->options().unknown_fields().field_count() > 0) return true;
55 for (int i = 0; i < descriptor->value_count(); ++i) {
56 const EnumValueDescriptor* value = descriptor->value(i);
57 if (value->options().unknown_fields().field_count() > 0) return true;
58 }
59 return false;
60}
61} // namespace
62
63EnumGenerator::EnumGenerator(const EnumDescriptor* descriptor,
64 bool immutable_api,
65 Context* context)
66 : descriptor_(descriptor), immutable_api_(immutable_api),
67 name_resolver_(context->GetNameResolver()) {
temporal40ee5512008-07-10 02:12:20 +000068 for (int i = 0; i < descriptor_->value_count(); i++) {
69 const EnumValueDescriptor* value = descriptor_->value(i);
70 const EnumValueDescriptor* canonical_value =
71 descriptor_->FindValueByNumber(value->number());
72
73 if (value == canonical_value) {
74 canonical_values_.push_back(value);
75 } else {
76 Alias alias;
77 alias.value = value;
78 alias.canonical_value = canonical_value;
79 aliases_.push_back(alias);
80 }
81 }
82}
83
84EnumGenerator::~EnumGenerator() {}
85
86void EnumGenerator::Generate(io::Printer* printer) {
xiaofeng@google.comb55a20f2012-09-22 02:40:50 +000087 WriteEnumDocComment(printer, descriptor_);
Feng Xiaoe841bac2015-12-11 17:09:20 -080088 printer->Print(
89 "public enum $classname$\n"
90 " implements com.google.protobuf.ProtocolMessageEnum {\n",
91 "classname", descriptor_->name());
temporal40ee5512008-07-10 02:12:20 +000092 printer->Indent();
93
94 for (int i = 0; i < canonical_values_.size(); i++) {
95 map<string, string> vars;
96 vars["name"] = canonical_values_[i]->name();
97 vars["index"] = SimpleItoa(canonical_values_[i]->index());
98 vars["number"] = SimpleItoa(canonical_values_[i]->number());
xiaofeng@google.comb55a20f2012-09-22 02:40:50 +000099 WriteEnumValueDocComment(printer, canonical_values_[i]);
jieluo@google.com4de8f552014-07-18 00:47:59 +0000100 if (canonical_values_[i]->options().deprecated()) {
101 printer->Print("@java.lang.Deprecated\n");
102 }
temporal40ee5512008-07-10 02:12:20 +0000103 printer->Print(vars,
104 "$name$($index$, $number$),\n");
105 }
106
Feng Xiao6ef984a2014-11-10 17:34:54 -0800107 if (SupportUnknownEnumValue(descriptor_->file())) {
108 printer->Print("UNRECOGNIZED(-1, -1),\n");
109 }
110
temporal40ee5512008-07-10 02:12:20 +0000111 printer->Print(
112 ";\n"
113 "\n");
114
115 // -----------------------------------------------------------------
116
117 for (int i = 0; i < aliases_.size(); i++) {
118 map<string, string> vars;
119 vars["classname"] = descriptor_->name();
120 vars["name"] = aliases_[i].value->name();
121 vars["canonical_name"] = aliases_[i].canonical_value->name();
xiaofeng@google.comb55a20f2012-09-22 02:40:50 +0000122 WriteEnumValueDocComment(printer, aliases_[i].value);
temporal40ee5512008-07-10 02:12:20 +0000123 printer->Print(vars,
124 "public static final $classname$ $name$ = $canonical_name$;\n");
125 }
126
liujisi@google.com33165fe2010-11-02 13:14:58 +0000127 for (int i = 0; i < descriptor_->value_count(); i++) {
128 map<string, string> vars;
129 vars["name"] = descriptor_->value(i)->name();
130 vars["number"] = SimpleItoa(descriptor_->value(i)->number());
xiaofeng@google.comb55a20f2012-09-22 02:40:50 +0000131 WriteEnumValueDocComment(printer, descriptor_->value(i));
liujisi@google.com33165fe2010-11-02 13:14:58 +0000132 printer->Print(vars,
133 "public static final int $name$_VALUE = $number$;\n");
134 }
135 printer->Print("\n");
136
temporal40ee5512008-07-10 02:12:20 +0000137 // -----------------------------------------------------------------
138
139 printer->Print(
140 "\n"
Feng Xiao6ef984a2014-11-10 17:34:54 -0800141 "public final int getNumber() {\n");
142 if (SupportUnknownEnumValue(descriptor_->file())) {
143 printer->Print(
144 " if (index == -1) {\n"
145 " throw new java.lang.IllegalArgumentException(\n"
146 " \"Can't get the number of an unknown enum value.\");\n"
147 " }\n");
148 }
149 printer->Print(
150 " return value;\n"
151 "}\n"
temporal40ee5512008-07-10 02:12:20 +0000152 "\n"
153 "public static $classname$ valueOf(int value) {\n"
154 " switch (value) {\n",
155 "classname", descriptor_->name());
156 printer->Indent();
157 printer->Indent();
158
159 for (int i = 0; i < canonical_values_.size(); i++) {
160 printer->Print(
161 "case $number$: return $name$;\n",
162 "name", canonical_values_[i]->name(),
163 "number", SimpleItoa(canonical_values_[i]->number()));
164 }
165
166 printer->Outdent();
167 printer->Outdent();
168 printer->Print(
169 " default: return null;\n"
170 " }\n"
171 "}\n"
kenton@google.com80b1d622009-07-29 01:13:20 +0000172 "\n"
173 "public static com.google.protobuf.Internal.EnumLiteMap<$classname$>\n"
174 " internalGetValueMap() {\n"
175 " return internalValueMap;\n"
176 "}\n"
Feng Xiaoeee38b02015-08-22 18:25:48 -0700177 "private static final com.google.protobuf.Internal.EnumLiteMap<\n"
178 " $classname$> internalValueMap =\n"
kenton@google.com80b1d622009-07-29 01:13:20 +0000179 " new com.google.protobuf.Internal.EnumLiteMap<$classname$>() {\n"
180 " public $classname$ findValueByNumber(int number) {\n"
kenton@google.comab6950d2010-01-28 01:09:11 +0000181 " return $classname$.valueOf(number);\n"
kenton@google.com80b1d622009-07-29 01:13:20 +0000182 " }\n"
183 " };\n"
184 "\n",
185 "classname", descriptor_->name());
temporal40ee5512008-07-10 02:12:20 +0000186
187 // -----------------------------------------------------------------
188 // Reflection
189
kenton@google.com80b1d622009-07-29 01:13:20 +0000190 if (HasDescriptorMethods(descriptor_)) {
temporal40ee5512008-07-10 02:12:20 +0000191 printer->Print(
kenton@google.com80b1d622009-07-29 01:13:20 +0000192 "public final com.google.protobuf.Descriptors.EnumValueDescriptor\n"
193 " getValueDescriptor() {\n"
194 " return getDescriptor().getValues().get(index);\n"
195 "}\n"
196 "public final com.google.protobuf.Descriptors.EnumDescriptor\n"
197 " getDescriptorForType() {\n"
198 " return getDescriptor();\n"
199 "}\n"
200 "public static final com.google.protobuf.Descriptors.EnumDescriptor\n"
201 " getDescriptor() {\n");
202
203 // TODO(kenton): Cache statically? Note that we can't access descriptors
204 // at module init time because it wouldn't work with descriptor.proto, but
205 // we can cache the value the first time getDescriptor() is called.
206 if (descriptor_->containing_type() == NULL) {
jieluo@google.com4de8f552014-07-18 00:47:59 +0000207 if (!MultipleJavaFiles(descriptor_->file(), immutable_api_)) {
208 printer->Print(
209 " return $file$.getDescriptor().getEnumTypes().get($index$);\n",
210 "file", name_resolver_->GetClassName(descriptor_->file(),
211 immutable_api_),
212 "index", SimpleItoa(descriptor_->index()));
213 } else {
214 printer->Indent();
215 if (EnumHasCustomOptions(descriptor_)) {
216 // We need to load the immutable classes in order to parse custom
217 // options. However, since file level enums (no outer class) are
218 // shared by immutable code and mutable code, the immutable classes
219 // may not exist. So we try to use Java reflection to retrieve the
220 // descriptor from immutable classes.
221 printer->Print(
222 "try {\n"
223 " java.lang.Class immutableFileClass =\n"
224 " java.lang.Class.forName(\"$immutable_file_class_name$\");\n"
225 " @java.lang.SuppressWarnings(\"unchecked\")\n"
226 " java.lang.reflect.Method m =\n"
227 " immutableFileClass.getMethod(\"getDescriptor\");\n"
228 " com.google.protobuf.Descriptors.FileDescriptor file =\n"
229 " (com.google.protobuf.Descriptors.FileDescriptor)\n"
230 " m.invoke(immutableFileClass);\n"
231 " return file.getEnumTypes().get($index$);\n"
232 "} catch (Exception e) {\n"
233 // Immutable classes cannot be found. Proceed as if custom options
234 // don't exist.
235 "}\n",
236 "immutable_file_class_name",
237 name_resolver_->GetImmutableClassName(descriptor_->file()),
238 "index", SimpleItoa(descriptor_->index()));
239 }
240 printer->Print(
Feng Xiao6ef984a2014-11-10 17:34:54 -0800241 "return $immutable_package$.$descriptor_class$.$descriptor$\n"
jieluo@google.com4de8f552014-07-18 00:47:59 +0000242 " .getEnumTypes().get($index$);\n",
243 "immutable_package", FileJavaPackage(descriptor_->file(), true),
244 "descriptor_class",
245 name_resolver_->GetDescriptorClassName(descriptor_->file()),
Feng Xiao6ef984a2014-11-10 17:34:54 -0800246 "descriptor", "getDescriptor()",
jieluo@google.com4de8f552014-07-18 00:47:59 +0000247 "index", SimpleItoa(descriptor_->index()));
248 printer->Outdent();
249 }
kenton@google.com80b1d622009-07-29 01:13:20 +0000250 } else {
251 printer->Print(
jieluo@google.com4de8f552014-07-18 00:47:59 +0000252 " return $parent$.$descriptor$.getEnumTypes().get($index$);\n",
253 "parent", name_resolver_->GetClassName(descriptor_->containing_type(),
254 immutable_api_),
255 "descriptor", descriptor_->containing_type()->options()
256 .no_standard_descriptor_accessor()
257 ? "getDefaultInstance().getDescriptorForType()"
258 : "getDescriptor()",
259 "index", SimpleItoa(descriptor_->index()));
kenton@google.com80b1d622009-07-29 01:13:20 +0000260 }
261
temporal40ee5512008-07-10 02:12:20 +0000262 printer->Print(
kenton@google.com80b1d622009-07-29 01:13:20 +0000263 "}\n"
264 "\n"
xiaofeng@google.comb55a20f2012-09-22 02:40:50 +0000265 "private static final $classname$[] VALUES = ",
kenton@google.com80b1d622009-07-29 01:13:20 +0000266 "classname", descriptor_->name());
267
xiaofeng@google.comb55a20f2012-09-22 02:40:50 +0000268 if (CanUseEnumValues()) {
269 // If the constants we are going to output are exactly the ones we
270 // have declared in the Java enum in the same order, then we can use
271 // the values() method that the Java compiler automatically generates
272 // for every enum.
273 printer->Print("values();\n");
274 } else {
275 printer->Print(
276 "{\n"
277 " ");
278 for (int i = 0; i < descriptor_->value_count(); i++) {
279 printer->Print("$name$, ",
280 "name", descriptor_->value(i)->name());
281 }
282 printer->Print(
283 "\n"
284 "};\n");
kenton@google.com80b1d622009-07-29 01:13:20 +0000285 }
286
287 printer->Print(
288 "\n"
kenton@google.com80b1d622009-07-29 01:13:20 +0000289 "public static $classname$ valueOf(\n"
290 " com.google.protobuf.Descriptors.EnumValueDescriptor desc) {\n"
291 " if (desc.getType() != getDescriptor()) {\n"
292 " throw new java.lang.IllegalArgumentException(\n"
293 " \"EnumValueDescriptor is not for this type.\");\n"
Feng Xiao6ef984a2014-11-10 17:34:54 -0800294 " }\n",
295 "classname", descriptor_->name());
296 if (SupportUnknownEnumValue(descriptor_->file())) {
297 printer->Print(
298 " if (desc.getIndex() == -1) {\n"
299 " return UNRECOGNIZED;\n"
300 " }\n");
301 }
302 printer->Print(
kenton@google.com80b1d622009-07-29 01:13:20 +0000303 " return VALUES[desc.getIndex()];\n"
kenton@google.comab6950d2010-01-28 01:09:11 +0000304 "}\n"
Feng Xiao6ef984a2014-11-10 17:34:54 -0800305 "\n");
kenton@google.comab6950d2010-01-28 01:09:11 +0000306
kenton@google.comab6950d2010-01-28 01:09:11 +0000307 printer->Print("private final int index;\n");
temporal40ee5512008-07-10 02:12:20 +0000308 }
309
temporal40ee5512008-07-10 02:12:20 +0000310 // -----------------------------------------------------------------
311
312 printer->Print(
kenton@google.comab6950d2010-01-28 01:09:11 +0000313 "private final int value;\n\n"
314 "private $classname$(int index, int value) {\n",
temporal40ee5512008-07-10 02:12:20 +0000315 "classname", descriptor_->name());
kenton@google.comab6950d2010-01-28 01:09:11 +0000316 if (HasDescriptorMethods(descriptor_)) {
317 printer->Print(" this.index = index;\n");
318 }
319 printer->Print(
320 " this.value = value;\n"
321 "}\n");
temporal40ee5512008-07-10 02:12:20 +0000322
kenton@google.comfccb1462009-12-18 02:11:36 +0000323 printer->Print(
324 "\n"
325 "// @@protoc_insertion_point(enum_scope:$full_name$)\n",
326 "full_name", descriptor_->full_name());
327
temporal40ee5512008-07-10 02:12:20 +0000328 printer->Outdent();
329 printer->Print("}\n\n");
330}
331
xiaofeng@google.comb55a20f2012-09-22 02:40:50 +0000332bool EnumGenerator::CanUseEnumValues() {
333 if (canonical_values_.size() != descriptor_->value_count()) {
334 return false;
335 }
336 for (int i = 0; i < descriptor_->value_count(); i++) {
337 if (descriptor_->value(i)->name() != canonical_values_[i]->name()) {
338 return false;
339 }
340 }
341 return true;
342}
343
temporal40ee5512008-07-10 02:12:20 +0000344} // namespace java
345} // namespace compiler
346} // namespace protobuf
347} // namespace google