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