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