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
37#include <google/protobuf/compiler/cpp/cpp_enum.h>
38#include <google/protobuf/compiler/cpp/cpp_helpers.h>
39#include <google/protobuf/io/printer.h>
40#include <google/protobuf/stubs/strutil.h>
41
42namespace google {
43namespace protobuf {
44namespace compiler {
45namespace cpp {
46
47namespace {
48// The GOOGLE_ARRAYSIZE constant is the max enum value plus 1. If the max enum value
49// is ::google::protobuf::kint32max, GOOGLE_ARRAYSIZE will overflow. In such cases we should omit the
50// generation of the GOOGLE_ARRAYSIZE constant.
51bool ShouldGenerateArraySize(const EnumDescriptor* descriptor) {
52  int32 max_value = descriptor->value(0)->number();
53  for (int i = 0; i < descriptor->value_count(); i++) {
54    if (descriptor->value(i)->number() > max_value) {
55      max_value = descriptor->value(i)->number();
56    }
57  }
58  return max_value != ::google::protobuf::kint32max;
59}
60}  // namespace
61
62EnumGenerator::EnumGenerator(const EnumDescriptor* descriptor,
63                             const Options& options)
64  : descriptor_(descriptor),
65    classname_(ClassName(descriptor, false)),
66    options_(options),
67    generate_array_size_(ShouldGenerateArraySize(descriptor)) {
68}
69
70EnumGenerator::~EnumGenerator() {}
71
72void EnumGenerator::FillForwardDeclaration(
73    map<string, const EnumDescriptor*>* enum_names) {
74  if (!options_.proto_h) {
75    return;
76  }
77  (*enum_names)[classname_] = descriptor_;
78}
79
80void EnumGenerator::GenerateDefinition(io::Printer* printer) {
81  map<string, string> vars;
82  vars["classname"] = classname_;
83  vars["short_name"] = descriptor_->name();
84  vars["enumbase"] = classname_ + (options_.proto_h ? " : int" : "");
85
86  printer->Print(vars, "enum $enumbase$ {\n");
87  printer->Annotate("enumbase", descriptor_);
88  printer->Indent();
89
90  const EnumValueDescriptor* min_value = descriptor_->value(0);
91  const EnumValueDescriptor* max_value = descriptor_->value(0);
92
93  for (int i = 0; i < descriptor_->value_count(); i++) {
94    vars["name"] = EnumValueName(descriptor_->value(i));
95    // In C++, an value of -2147483648 gets interpreted as the negative of
96    // 2147483648, and since 2147483648 can't fit in an integer, this produces a
97    // compiler warning.  This works around that issue.
98    vars["number"] = Int32ToString(descriptor_->value(i)->number());
99    vars["prefix"] = (descriptor_->containing_type() == NULL) ?
100      "" : classname_ + "_";
101    vars["deprecation"] = descriptor_->value(i)->options().deprecated() ?
102        " PROTOBUF_DEPRECATED" : "";
103
104    if (i > 0) printer->Print(",\n");
105    printer->Print(vars, "$prefix$$name$$deprecation$ = $number$");
106
107    if (descriptor_->value(i)->number() < min_value->number()) {
108      min_value = descriptor_->value(i);
109    }
110    if (descriptor_->value(i)->number() > max_value->number()) {
111      max_value = descriptor_->value(i);
112    }
113  }
114
115  if (HasPreservingUnknownEnumSemantics(descriptor_->file())) {
116    // For new enum semantics: generate min and max sentinel values equal to
117    // INT32_MIN and INT32_MAX
118    if (descriptor_->value_count() > 0) printer->Print(",\n");
119    printer->Print(vars,
120        "$classname$_$prefix$INT_MIN_SENTINEL_DO_NOT_USE_ = ::google::protobuf::kint32min,\n"
121        "$classname$_$prefix$INT_MAX_SENTINEL_DO_NOT_USE_ = ::google::protobuf::kint32max");
122  }
123
124  printer->Outdent();
125  printer->Print("\n};\n");
126
127  vars["min_name"] = EnumValueName(min_value);
128  vars["max_name"] = EnumValueName(max_value);
129
130  if (options_.dllexport_decl.empty()) {
131    vars["dllexport"] = "";
132  } else {
133    vars["dllexport"] = options_.dllexport_decl + " ";
134  }
135
136  printer->Print(vars,
137    "$dllexport$bool $classname$_IsValid(int value);\n"
138    "const $classname$ $prefix$$short_name$_MIN = $prefix$$min_name$;\n"
139    "const $classname$ $prefix$$short_name$_MAX = $prefix$$max_name$;\n");
140
141  if (generate_array_size_) {
142    printer->Print(vars,
143      "const int $prefix$$short_name$_ARRAYSIZE = "
144      "$prefix$$short_name$_MAX + 1;\n\n");
145  }
146
147  if (HasDescriptorMethods(descriptor_->file(), options_)) {
148    printer->Print(vars,
149      "$dllexport$const ::google::protobuf::EnumDescriptor* $classname$_descriptor();\n");
150    // The _Name and _Parse methods
151    printer->Print(
152        vars,
153        "inline const ::std::string& $classname$_Name($classname$ value) {\n"
154        "  return ::google::protobuf::internal::NameOfEnum(\n"
155        "    $classname$_descriptor(), value);\n"
156        "}\n");
157    printer->Print(vars,
158      "inline bool $classname$_Parse(\n"
159      "    const ::std::string& name, $classname$* value) {\n"
160      "  return ::google::protobuf::internal::ParseNamedEnum<$classname$>(\n"
161      "    $classname$_descriptor(), name, value);\n"
162      "}\n");
163  }
164}
165
166void EnumGenerator::
167GenerateGetEnumDescriptorSpecializations(io::Printer* printer) {
168  printer->Print(
169      "template <> struct is_proto_enum< $classname$> : ::google::protobuf::internal::true_type "
170      "{};\n",
171      "classname", ClassName(descriptor_, true));
172  if (HasDescriptorMethods(descriptor_->file(), options_)) {
173    printer->Print(
174      "template <>\n"
175      "inline const EnumDescriptor* GetEnumDescriptor< $classname$>() {\n"
176      "  return $classname$_descriptor();\n"
177      "}\n",
178      "classname", ClassName(descriptor_, true));
179  }
180}
181
182void EnumGenerator::GenerateSymbolImports(io::Printer* printer) {
183  map<string, string> vars;
184  vars["nested_name"] = descriptor_->name();
185  vars["classname"] = classname_;
186  vars["constexpr"] = options_.proto_h ? "constexpr " : "";
187  printer->Print(vars, "typedef $classname$ $nested_name$;\n");
188
189  for (int j = 0; j < descriptor_->value_count(); j++) {
190    vars["tag"] = EnumValueName(descriptor_->value(j));
191    vars["deprecated_attr"] = descriptor_->value(j)->options().deprecated() ?
192      "PROTOBUF_DEPRECATED_ATTR " : "";
193    printer->Print(vars,
194      "$deprecated_attr$static $constexpr$const $nested_name$ $tag$ =\n"
195      "  $classname$_$tag$;\n");
196  }
197
198  printer->Print(vars,
199    "static inline bool $nested_name$_IsValid(int value) {\n"
200    "  return $classname$_IsValid(value);\n"
201    "}\n"
202    "static const $nested_name$ $nested_name$_MIN =\n"
203    "  $classname$_$nested_name$_MIN;\n"
204    "static const $nested_name$ $nested_name$_MAX =\n"
205    "  $classname$_$nested_name$_MAX;\n");
206  if (generate_array_size_) {
207    printer->Print(vars,
208      "static const int $nested_name$_ARRAYSIZE =\n"
209      "  $classname$_$nested_name$_ARRAYSIZE;\n");
210  }
211
212  if (HasDescriptorMethods(descriptor_->file(), options_)) {
213    printer->Print(vars,
214      "static inline const ::google::protobuf::EnumDescriptor*\n"
215      "$nested_name$_descriptor() {\n"
216      "  return $classname$_descriptor();\n"
217      "}\n");
218    printer->Print(vars,
219                   "static inline const ::std::string& "
220                   "$nested_name$_Name($nested_name$ value) {"
221                   "\n"
222                   "  return $classname$_Name(value);\n"
223                   "}\n");
224    printer->Print(vars,
225      "static inline bool $nested_name$_Parse(const ::std::string& name,\n"
226      "    $nested_name$* value) {\n"
227      "  return $classname$_Parse(name, value);\n"
228      "}\n");
229  }
230}
231
232void EnumGenerator::GenerateDescriptorInitializer(
233    io::Printer* printer, int index) {
234  map<string, string> vars;
235  vars["classname"] = classname_;
236  vars["index"] = SimpleItoa(index);
237
238  if (descriptor_->containing_type() == NULL) {
239    printer->Print(vars,
240      "$classname$_descriptor_ = file->enum_type($index$);\n");
241  } else {
242    vars["parent"] = ClassName(descriptor_->containing_type(), false);
243    printer->Print(vars,
244      "$classname$_descriptor_ = $parent$_descriptor_->enum_type($index$);\n");
245  }
246}
247
248void EnumGenerator::GenerateMethods(io::Printer* printer) {
249  map<string, string> vars;
250  vars["classname"] = classname_;
251  vars["constexpr"] = options_.proto_h ? "constexpr " : "";
252
253  if (HasDescriptorMethods(descriptor_->file(), options_)) {
254    printer->Print(vars,
255      "const ::google::protobuf::EnumDescriptor* $classname$_descriptor() {\n"
256      "  protobuf_AssignDescriptorsOnce();\n"
257      "  return $classname$_descriptor_;\n"
258      "}\n");
259  }
260
261  printer->Print(vars,
262    "bool $classname$_IsValid(int value) {\n"
263    "  switch(value) {\n");
264
265  // Multiple values may have the same number.  Make sure we only cover
266  // each number once by first constructing a set containing all valid
267  // numbers, then printing a case statement for each element.
268
269  set<int> numbers;
270  for (int j = 0; j < descriptor_->value_count(); j++) {
271    const EnumValueDescriptor* value = descriptor_->value(j);
272    numbers.insert(value->number());
273  }
274
275  for (set<int>::iterator iter = numbers.begin();
276       iter != numbers.end(); ++iter) {
277    printer->Print(
278      "    case $number$:\n",
279      "number", Int32ToString(*iter));
280  }
281
282  printer->Print(vars,
283    "      return true;\n"
284    "    default:\n"
285    "      return false;\n"
286    "  }\n"
287    "}\n"
288    "\n");
289
290  if (descriptor_->containing_type() != NULL) {
291    // We need to "define" the static constants which were declared in the
292    // header, to give the linker a place to put them.  Or at least the C++
293    // standard says we have to.  MSVC actually insists that we do _not_ define
294    // them again in the .cc file, prior to VC++ 2015.
295    printer->Print("#if !defined(_MSC_VER) || _MSC_VER >= 1900\n");
296
297    vars["parent"] = ClassName(descriptor_->containing_type(), false);
298    vars["nested_name"] = descriptor_->name();
299    for (int i = 0; i < descriptor_->value_count(); i++) {
300      vars["value"] = EnumValueName(descriptor_->value(i));
301      printer->Print(vars,
302        "$constexpr$const $classname$ $parent$::$value$;\n");
303    }
304    printer->Print(vars,
305      "const $classname$ $parent$::$nested_name$_MIN;\n"
306      "const $classname$ $parent$::$nested_name$_MAX;\n");
307    if (generate_array_size_) {
308      printer->Print(vars,
309        "const int $parent$::$nested_name$_ARRAYSIZE;\n");
310    }
311
312    printer->Print("#endif  // !defined(_MSC_VER) || _MSC_VER >= 1900\n");
313  }
314}
315
316}  // namespace cpp
317}  // namespace compiler
318}  // namespace protobuf
319}  // namespace google
320