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 <map>
36 #include <string>
37
38 #include <google/protobuf/compiler/java/java_context.h>
39 #include <google/protobuf/compiler/java/java_enum.h>
40 #include <google/protobuf/compiler/java/java_doc_comment.h>
41 #include <google/protobuf/compiler/java/java_helpers.h>
42 #include <google/protobuf/compiler/java/java_name_resolver.h>
43 #include <google/protobuf/io/printer.h>
44 #include <google/protobuf/descriptor.pb.h>
45 #include <google/protobuf/stubs/strutil.h>
46
47 namespace google {
48 namespace protobuf {
49 namespace compiler {
50 namespace java {
51
52 namespace {
EnumHasCustomOptions(const EnumDescriptor * descriptor)53 bool 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
EnumGenerator(const EnumDescriptor * descriptor,bool immutable_api,Context * context)63 EnumGenerator::EnumGenerator(const EnumDescriptor* descriptor,
64 bool immutable_api,
65 Context* context)
66 : descriptor_(descriptor), immutable_api_(immutable_api),
67 name_resolver_(context->GetNameResolver()) {
68 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
~EnumGenerator()84 EnumGenerator::~EnumGenerator() {}
85
Generate(io::Printer * printer)86 void EnumGenerator::Generate(io::Printer* printer) {
87 WriteEnumDocComment(printer, descriptor_);
88 if (HasDescriptorMethods(descriptor_)) {
89 printer->Print(
90 "public enum $classname$\n"
91 " implements com.google.protobuf.ProtocolMessageEnum {\n",
92 "classname", descriptor_->name());
93 } else {
94 printer->Print(
95 "public enum $classname$\n"
96 " implements com.google.protobuf.Internal.EnumLite {\n",
97 "classname", descriptor_->name());
98 }
99 printer->Indent();
100
101 for (int i = 0; i < canonical_values_.size(); i++) {
102 map<string, string> vars;
103 vars["name"] = canonical_values_[i]->name();
104 vars["index"] = SimpleItoa(canonical_values_[i]->index());
105 vars["number"] = SimpleItoa(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 printer->Print(vars,
111 "$name$($index$, $number$),\n");
112 }
113
114 printer->Print(
115 ";\n"
116 "\n");
117
118 // -----------------------------------------------------------------
119
120 for (int i = 0; i < aliases_.size(); i++) {
121 map<string, string> vars;
122 vars["classname"] = descriptor_->name();
123 vars["name"] = aliases_[i].value->name();
124 vars["canonical_name"] = aliases_[i].canonical_value->name();
125 WriteEnumValueDocComment(printer, aliases_[i].value);
126 printer->Print(vars,
127 "public static final $classname$ $name$ = $canonical_name$;\n");
128 }
129
130 for (int i = 0; i < descriptor_->value_count(); i++) {
131 map<string, string> vars;
132 vars["name"] = descriptor_->value(i)->name();
133 vars["number"] = SimpleItoa(descriptor_->value(i)->number());
134 WriteEnumValueDocComment(printer, descriptor_->value(i));
135 printer->Print(vars,
136 "public static final int $name$_VALUE = $number$;\n");
137 }
138 printer->Print("\n");
139
140 // -----------------------------------------------------------------
141
142 printer->Print(
143 "\n"
144 "public final int getNumber() { return value; }\n"
145 "\n"
146 "public static $classname$ valueOf(int value) {\n"
147 " switch (value) {\n",
148 "classname", descriptor_->name());
149 printer->Indent();
150 printer->Indent();
151
152 for (int i = 0; i < canonical_values_.size(); i++) {
153 printer->Print(
154 "case $number$: return $name$;\n",
155 "name", canonical_values_[i]->name(),
156 "number", SimpleItoa(canonical_values_[i]->number()));
157 }
158
159 printer->Outdent();
160 printer->Outdent();
161 printer->Print(
162 " default: return null;\n"
163 " }\n"
164 "}\n"
165 "\n"
166 "public static com.google.protobuf.Internal.EnumLiteMap<$classname$>\n"
167 " internalGetValueMap() {\n"
168 " return internalValueMap;\n"
169 "}\n"
170 "private static com.google.protobuf.Internal.EnumLiteMap<$classname$>\n"
171 " internalValueMap =\n"
172 " new com.google.protobuf.Internal.EnumLiteMap<$classname$>() {\n"
173 " public $classname$ findValueByNumber(int number) {\n"
174 " return $classname$.valueOf(number);\n"
175 " }\n"
176 " };\n"
177 "\n",
178 "classname", descriptor_->name());
179
180 // -----------------------------------------------------------------
181 // Reflection
182
183 if (HasDescriptorMethods(descriptor_)) {
184 printer->Print(
185 "public final com.google.protobuf.Descriptors.EnumValueDescriptor\n"
186 " getValueDescriptor() {\n"
187 " return getDescriptor().getValues().get(index);\n"
188 "}\n"
189 "public final com.google.protobuf.Descriptors.EnumDescriptor\n"
190 " getDescriptorForType() {\n"
191 " return getDescriptor();\n"
192 "}\n"
193 "public static final com.google.protobuf.Descriptors.EnumDescriptor\n"
194 " getDescriptor() {\n");
195
196 // TODO(kenton): Cache statically? Note that we can't access descriptors
197 // at module init time because it wouldn't work with descriptor.proto, but
198 // we can cache the value the first time getDescriptor() is called.
199 if (descriptor_->containing_type() == NULL) {
200 if (!MultipleJavaFiles(descriptor_->file(), immutable_api_)) {
201 printer->Print(
202 " return $file$.getDescriptor().getEnumTypes().get($index$);\n",
203 "file", name_resolver_->GetClassName(descriptor_->file(),
204 immutable_api_),
205 "index", SimpleItoa(descriptor_->index()));
206 } else {
207 printer->Indent();
208 if (EnumHasCustomOptions(descriptor_)) {
209 // We need to load the immutable classes in order to parse custom
210 // options. However, since file level enums (no outer class) are
211 // shared by immutable code and mutable code, the immutable classes
212 // may not exist. So we try to use Java reflection to retrieve the
213 // descriptor from immutable classes.
214 printer->Print(
215 "try {\n"
216 " java.lang.Class immutableFileClass =\n"
217 " java.lang.Class.forName(\"$immutable_file_class_name$\");\n"
218 " @java.lang.SuppressWarnings(\"unchecked\")\n"
219 " java.lang.reflect.Method m =\n"
220 " immutableFileClass.getMethod(\"getDescriptor\");\n"
221 " com.google.protobuf.Descriptors.FileDescriptor file =\n"
222 " (com.google.protobuf.Descriptors.FileDescriptor)\n"
223 " m.invoke(immutableFileClass);\n"
224 " return file.getEnumTypes().get($index$);\n"
225 "} catch (Exception e) {\n"
226 // Immutable classes cannot be found. Proceed as if custom options
227 // don't exist.
228 "}\n",
229 "immutable_file_class_name",
230 name_resolver_->GetImmutableClassName(descriptor_->file()),
231 "index", SimpleItoa(descriptor_->index()));
232 }
233 printer->Print(
234 "return $immutable_package$.$descriptor_class$.getDescriptor()\n"
235 " .getEnumTypes().get($index$);\n",
236 "immutable_package", FileJavaPackage(descriptor_->file(), true),
237 "descriptor_class",
238 name_resolver_->GetDescriptorClassName(descriptor_->file()),
239 "index", SimpleItoa(descriptor_->index()));
240 printer->Outdent();
241 }
242 } else {
243 printer->Print(
244 " return $parent$.$descriptor$.getEnumTypes().get($index$);\n",
245 "parent", name_resolver_->GetClassName(descriptor_->containing_type(),
246 immutable_api_),
247 "descriptor", descriptor_->containing_type()->options()
248 .no_standard_descriptor_accessor()
249 ? "getDefaultInstance().getDescriptorForType()"
250 : "getDescriptor()",
251 "index", SimpleItoa(descriptor_->index()));
252 }
253
254 printer->Print(
255 "}\n"
256 "\n"
257 "private static final $classname$[] VALUES = ",
258 "classname", descriptor_->name());
259
260 if (CanUseEnumValues()) {
261 // If the constants we are going to output are exactly the ones we
262 // have declared in the Java enum in the same order, then we can use
263 // the values() method that the Java compiler automatically generates
264 // for every enum.
265 printer->Print("values();\n");
266 } else {
267 printer->Print(
268 "{\n"
269 " ");
270 for (int i = 0; i < descriptor_->value_count(); i++) {
271 printer->Print("$name$, ",
272 "name", descriptor_->value(i)->name());
273 }
274 printer->Print(
275 "\n"
276 "};\n");
277 }
278
279 printer->Print(
280 "\n"
281 "public static $classname$ valueOf(\n"
282 " com.google.protobuf.Descriptors.EnumValueDescriptor desc) {\n"
283 " if (desc.getType() != getDescriptor()) {\n"
284 " throw new java.lang.IllegalArgumentException(\n"
285 " \"EnumValueDescriptor is not for this type.\");\n"
286 " }\n"
287 " return VALUES[desc.getIndex()];\n"
288 "}\n"
289 "\n",
290 "classname", descriptor_->name());
291
292 // index is only used for reflection; lite implementation does not need it
293 printer->Print("private final int index;\n");
294 }
295
296 // -----------------------------------------------------------------
297
298 printer->Print(
299 "private final int value;\n\n"
300 "private $classname$(int index, int value) {\n",
301 "classname", descriptor_->name());
302 if (HasDescriptorMethods(descriptor_)) {
303 printer->Print(" this.index = index;\n");
304 }
305 printer->Print(
306 " this.value = value;\n"
307 "}\n");
308
309 printer->Print(
310 "\n"
311 "// @@protoc_insertion_point(enum_scope:$full_name$)\n",
312 "full_name", descriptor_->full_name());
313
314 printer->Outdent();
315 printer->Print("}\n\n");
316 }
317
CanUseEnumValues()318 bool EnumGenerator::CanUseEnumValues() {
319 if (canonical_values_.size() != descriptor_->value_count()) {
320 return false;
321 }
322 for (int i = 0; i < descriptor_->value_count(); i++) {
323 if (descriptor_->value(i)->name() != canonical_values_[i]->name()) {
324 return false;
325 }
326 }
327 return true;
328 }
329
330 } // namespace java
331 } // namespace compiler
332 } // namespace protobuf
333 } // namespace google
334