1// Protocol Buffers - Google's data interchange format
2// Copyright 2008 Google Inc.  All rights reserved.
3// http://code.google.com/p/protobuf/
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 <limits>
36#include <vector>
37#include <google/protobuf/stubs/hash.h>
38
39#include <google/protobuf/compiler/cpp/cpp_helpers.h>
40#include <google/protobuf/stubs/common.h>
41#include <google/protobuf/stubs/strutil.h>
42#include <google/protobuf/stubs/substitute.h>
43
44
45namespace google {
46namespace protobuf {
47namespace compiler {
48namespace cpp {
49
50namespace {
51
52string DotsToUnderscores(const string& name) {
53  return StringReplace(name, ".", "_", true);
54}
55
56string DotsToColons(const string& name) {
57  return StringReplace(name, ".", "::", true);
58}
59
60const char* const kKeywordList[] = {
61  "and", "and_eq", "asm", "auto", "bitand", "bitor", "bool", "break", "case",
62  "catch", "char", "class", "compl", "const", "const_cast", "continue",
63  "default", "delete", "do", "double", "dynamic_cast", "else", "enum",
64  "explicit", "extern", "false", "float", "for", "friend", "goto", "if",
65  "inline", "int", "long", "mutable", "namespace", "new", "not", "not_eq",
66  "operator", "or", "or_eq", "private", "protected", "public", "register",
67  "reinterpret_cast", "return", "short", "signed", "sizeof", "static",
68  "static_cast", "struct", "switch", "template", "this", "throw", "true", "try",
69  "typedef", "typeid", "typename", "union", "unsigned", "using", "virtual",
70  "void", "volatile", "wchar_t", "while", "xor", "xor_eq"
71};
72
73hash_set<string> MakeKeywordsMap() {
74  hash_set<string> result;
75  for (int i = 0; i < GOOGLE_ARRAYSIZE(kKeywordList); i++) {
76    result.insert(kKeywordList[i]);
77  }
78  return result;
79}
80
81hash_set<string> kKeywords = MakeKeywordsMap();
82
83string UnderscoresToCamelCase(const string& input, bool cap_next_letter) {
84  string result;
85  // Note:  I distrust ctype.h due to locales.
86  for (int i = 0; i < input.size(); i++) {
87    if ('a' <= input[i] && input[i] <= 'z') {
88      if (cap_next_letter) {
89        result += input[i] + ('A' - 'a');
90      } else {
91        result += input[i];
92      }
93      cap_next_letter = false;
94    } else if ('A' <= input[i] && input[i] <= 'Z') {
95      // Capital letters are left as-is.
96      result += input[i];
97      cap_next_letter = false;
98    } else if ('0' <= input[i] && input[i] <= '9') {
99      result += input[i];
100      cap_next_letter = true;
101    } else {
102      cap_next_letter = true;
103    }
104  }
105  return result;
106}
107
108}  // namespace
109
110const char kThickSeparator[] =
111  "// ===================================================================\n";
112const char kThinSeparator[] =
113  "// -------------------------------------------------------------------\n";
114
115string ClassName(const Descriptor* descriptor, bool qualified) {
116
117  // Find "outer", the descriptor of the top-level message in which
118  // "descriptor" is embedded.
119  const Descriptor* outer = descriptor;
120  while (outer->containing_type() != NULL) outer = outer->containing_type();
121
122  const string& outer_name = outer->full_name();
123  string inner_name = descriptor->full_name().substr(outer_name.size());
124
125  if (qualified) {
126    return "::" + DotsToColons(outer_name) + DotsToUnderscores(inner_name);
127  } else {
128    return outer->name() + DotsToUnderscores(inner_name);
129  }
130}
131
132string ClassName(const EnumDescriptor* enum_descriptor, bool qualified) {
133  if (enum_descriptor->containing_type() == NULL) {
134    if (qualified) {
135      return DotsToColons(enum_descriptor->full_name());
136    } else {
137      return enum_descriptor->name();
138    }
139  } else {
140    string result = ClassName(enum_descriptor->containing_type(), qualified);
141    result += '_';
142    result += enum_descriptor->name();
143    return result;
144  }
145}
146
147
148string SuperClassName(const Descriptor* descriptor) {
149  return HasDescriptorMethods(descriptor->file()) ?
150      "::google::protobuf::Message" : "::google::protobuf::MessageLite";
151}
152
153string FieldName(const FieldDescriptor* field) {
154  string result = field->name();
155  LowerString(&result);
156  if (kKeywords.count(result) > 0) {
157    result.append("_");
158  }
159  return result;
160}
161
162string FieldConstantName(const FieldDescriptor *field) {
163  string field_name = UnderscoresToCamelCase(field->name(), true);
164  string result = "k" + field_name + "FieldNumber";
165
166  if (!field->is_extension() &&
167      field->containing_type()->FindFieldByCamelcaseName(
168        field->camelcase_name()) != field) {
169    // This field's camelcase name is not unique.  As a hack, add the field
170    // number to the constant name.  This makes the constant rather useless,
171    // but what can we do?
172    result += "_" + SimpleItoa(field->number());
173  }
174
175  return result;
176}
177
178string FieldMessageTypeName(const FieldDescriptor* field) {
179  // Note:  The Google-internal version of Protocol Buffers uses this function
180  //   as a hook point for hacks to support legacy code.
181  return ClassName(field->message_type(), true);
182}
183
184string StripProto(const string& filename) {
185  if (HasSuffixString(filename, ".protodevel")) {
186    return StripSuffixString(filename, ".protodevel");
187  } else {
188    return StripSuffixString(filename, ".proto");
189  }
190}
191
192const char* PrimitiveTypeName(FieldDescriptor::CppType type) {
193  switch (type) {
194    case FieldDescriptor::CPPTYPE_INT32  : return "::google::protobuf::int32";
195    case FieldDescriptor::CPPTYPE_INT64  : return "::google::protobuf::int64";
196    case FieldDescriptor::CPPTYPE_UINT32 : return "::google::protobuf::uint32";
197    case FieldDescriptor::CPPTYPE_UINT64 : return "::google::protobuf::uint64";
198    case FieldDescriptor::CPPTYPE_DOUBLE : return "double";
199    case FieldDescriptor::CPPTYPE_FLOAT  : return "float";
200    case FieldDescriptor::CPPTYPE_BOOL   : return "bool";
201    case FieldDescriptor::CPPTYPE_ENUM   : return "int";
202    case FieldDescriptor::CPPTYPE_STRING : return "::std::string";
203    case FieldDescriptor::CPPTYPE_MESSAGE: return NULL;
204
205    // No default because we want the compiler to complain if any new
206    // CppTypes are added.
207  }
208
209  GOOGLE_LOG(FATAL) << "Can't get here.";
210  return NULL;
211}
212
213const char* DeclaredTypeMethodName(FieldDescriptor::Type type) {
214  switch (type) {
215    case FieldDescriptor::TYPE_INT32   : return "Int32";
216    case FieldDescriptor::TYPE_INT64   : return "Int64";
217    case FieldDescriptor::TYPE_UINT32  : return "UInt32";
218    case FieldDescriptor::TYPE_UINT64  : return "UInt64";
219    case FieldDescriptor::TYPE_SINT32  : return "SInt32";
220    case FieldDescriptor::TYPE_SINT64  : return "SInt64";
221    case FieldDescriptor::TYPE_FIXED32 : return "Fixed32";
222    case FieldDescriptor::TYPE_FIXED64 : return "Fixed64";
223    case FieldDescriptor::TYPE_SFIXED32: return "SFixed32";
224    case FieldDescriptor::TYPE_SFIXED64: return "SFixed64";
225    case FieldDescriptor::TYPE_FLOAT   : return "Float";
226    case FieldDescriptor::TYPE_DOUBLE  : return "Double";
227
228    case FieldDescriptor::TYPE_BOOL    : return "Bool";
229    case FieldDescriptor::TYPE_ENUM    : return "Enum";
230
231    case FieldDescriptor::TYPE_STRING  : return "String";
232    case FieldDescriptor::TYPE_BYTES   : return "Bytes";
233    case FieldDescriptor::TYPE_GROUP   : return "Group";
234    case FieldDescriptor::TYPE_MESSAGE : return "Message";
235
236    // No default because we want the compiler to complain if any new
237    // types are added.
238  }
239  GOOGLE_LOG(FATAL) << "Can't get here.";
240  return "";
241}
242
243string DefaultValue(const FieldDescriptor* field) {
244  switch (field->cpp_type()) {
245    case FieldDescriptor::CPPTYPE_INT32:
246      return SimpleItoa(field->default_value_int32());
247    case FieldDescriptor::CPPTYPE_UINT32:
248      return SimpleItoa(field->default_value_uint32()) + "u";
249    case FieldDescriptor::CPPTYPE_INT64:
250      return "GOOGLE_LONGLONG(" + SimpleItoa(field->default_value_int64()) + ")";
251    case FieldDescriptor::CPPTYPE_UINT64:
252      return "GOOGLE_ULONGLONG(" + SimpleItoa(field->default_value_uint64())+ ")";
253    case FieldDescriptor::CPPTYPE_DOUBLE: {
254      double value = field->default_value_double();
255      if (value == numeric_limits<double>::infinity()) {
256        return "::google::protobuf::internal::Infinity()";
257      } else if (value == -numeric_limits<double>::infinity()) {
258        return "-::google::protobuf::internal::Infinity()";
259      } else if (value != value) {
260        return "::google::protobuf::internal::NaN()";
261      } else {
262        return SimpleDtoa(value);
263      }
264    }
265    case FieldDescriptor::CPPTYPE_FLOAT:
266      {
267        float value = field->default_value_float();
268        if (value == numeric_limits<float>::infinity()) {
269          return "static_cast<float>(::google::protobuf::internal::Infinity())";
270        } else if (value == -numeric_limits<float>::infinity()) {
271          return "static_cast<float>(-::google::protobuf::internal::Infinity())";
272        } else if (value != value) {
273          return "static_cast<float>(::google::protobuf::internal::NaN())";
274        } else {
275          string float_value = SimpleFtoa(value);
276          // If floating point value contains a period (.) or an exponent
277          // (either E or e), then append suffix 'f' to make it a float
278          // literal.
279          if (float_value.find_first_of(".eE") != string::npos) {
280            float_value.push_back('f');
281          }
282          return float_value;
283        }
284      }
285    case FieldDescriptor::CPPTYPE_BOOL:
286      return field->default_value_bool() ? "true" : "false";
287    case FieldDescriptor::CPPTYPE_ENUM:
288      // Lazy:  Generate a static_cast because we don't have a helper function
289      //   that constructs the full name of an enum value.
290      return strings::Substitute(
291          "static_cast< $0 >($1)",
292          ClassName(field->enum_type(), true),
293          field->default_value_enum()->number());
294    case FieldDescriptor::CPPTYPE_STRING:
295      return "\"" + CEscape(field->default_value_string()) + "\"";
296    case FieldDescriptor::CPPTYPE_MESSAGE:
297      return FieldMessageTypeName(field) + "::default_instance()";
298  }
299  // Can't actually get here; make compiler happy.  (We could add a default
300  // case above but then we wouldn't get the nice compiler warning when a
301  // new type is added.)
302  GOOGLE_LOG(FATAL) << "Can't get here.";
303  return "";
304}
305
306// Convert a file name into a valid identifier.
307string FilenameIdentifier(const string& filename) {
308  string result;
309  for (int i = 0; i < filename.size(); i++) {
310    if (ascii_isalnum(filename[i])) {
311      result.push_back(filename[i]);
312    } else {
313      // Not alphanumeric.  To avoid any possibility of name conflicts we
314      // use the hex code for the character.
315      result.push_back('_');
316      char buffer[kFastToBufferSize];
317      result.append(FastHexToBuffer(static_cast<uint8>(filename[i]), buffer));
318    }
319  }
320  return result;
321}
322
323// Return the name of the AddDescriptors() function for a given file.
324string GlobalAddDescriptorsName(const string& filename) {
325  return "protobuf_AddDesc_" + FilenameIdentifier(filename);
326}
327
328// Return the name of the AssignDescriptors() function for a given file.
329string GlobalAssignDescriptorsName(const string& filename) {
330  return "protobuf_AssignDesc_" + FilenameIdentifier(filename);
331}
332
333// Return the name of the ShutdownFile() function for a given file.
334string GlobalShutdownFileName(const string& filename) {
335  return "protobuf_ShutdownFile_" + FilenameIdentifier(filename);
336}
337
338}  // namespace cpp
339}  // namespace compiler
340}  // namespace protobuf
341}  // namespace google
342