aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/google/protobuf/descriptor.cc
diff options
context:
space:
mode:
authorGravatar Bo Yang <teboring@google.com>2016-09-19 13:45:07 -0700
committerGravatar Bo Yang <teboring@google.com>2016-10-10 11:23:36 -0700
commitcc8ca5b6a5478b40546d4206392eb1471454460d (patch)
treec0b45abfa16d7d373a6ea8f7fe50f1de00ab938e /src/google/protobuf/descriptor.cc
parent337a028bb65ccca4dda768695950b5aba53ae2c9 (diff)
Integrate internal changes
Diffstat (limited to 'src/google/protobuf/descriptor.cc')
-rw-r--r--src/google/protobuf/descriptor.cc198
1 files changed, 187 insertions, 11 deletions
diff --git a/src/google/protobuf/descriptor.cc b/src/google/protobuf/descriptor.cc
index 9b20946c..4dccb585 100644
--- a/src/google/protobuf/descriptor.cc
+++ b/src/google/protobuf/descriptor.cc
@@ -69,6 +69,7 @@
#undef PACKAGE // autoheader #defines this. :(
namespace google {
+
namespace protobuf {
const FieldDescriptor::CppType
@@ -164,6 +165,15 @@ const int FieldDescriptor::kLastReservedNumber;
namespace {
+// Note: I distrust ctype.h due to locales.
+char ToUpper(char ch) {
+ return (ch >= 'a' && ch <= 'z') ? (ch - 'a' + 'A') : ch;
+}
+
+char ToLower(char ch) {
+ return (ch >= 'A' && ch <= 'Z') ? (ch - 'A' + 'a') : ch;
+}
+
string ToCamelCase(const string& input, bool lower_first) {
bool capitalize_next = !lower_first;
string result;
@@ -173,12 +183,7 @@ string ToCamelCase(const string& input, bool lower_first) {
if (input[i] == '_') {
capitalize_next = true;
} else if (capitalize_next) {
- // Note: I distrust ctype.h due to locales.
- if ('a' <= input[i] && input[i] <= 'z') {
- result.push_back(input[i] - 'a' + 'A');
- } else {
- result.push_back(input[i]);
- }
+ result.push_back(ToUpper(input[i]));
capitalize_next = false;
} else {
result.push_back(input[i]);
@@ -186,13 +191,116 @@ string ToCamelCase(const string& input, bool lower_first) {
}
// Lower-case the first letter.
- if (lower_first && !result.empty() && 'A' <= result[0] && result[0] <= 'Z') {
- result[0] = result[0] - 'A' + 'a';
+ if (lower_first && !result.empty()) {
+ result[0] = ToLower(result[0]);
}
return result;
}
+string ToJsonName(const string& input) {
+ bool capitalize_next = false;
+ string result;
+ result.reserve(input.size());
+
+ for (int i = 0; i < input.size(); i++) {
+ if (input[i] == '_') {
+ capitalize_next = true;
+ } else if (capitalize_next) {
+ result.push_back(ToUpper(input[i]));
+ capitalize_next = false;
+ } else {
+ result.push_back(input[i]);
+ }
+ }
+
+ return result;
+}
+
+string EnumValueToPascalCase(const string& input) {
+ bool next_upper = true;
+ string result;
+ result.reserve(input.size());
+
+ for (int i = 0; i < input.size(); i++) {
+ if (input[i] == '_') {
+ next_upper = true;
+ } else {
+ if (next_upper) {
+ result.push_back(ToUpper(input[i]));
+ } else {
+ result.push_back(ToLower(input[i]));
+ }
+ next_upper = false;
+ }
+ }
+
+ return result;
+}
+
+// Class to remove an enum prefix from enum values.
+class PrefixRemover {
+ public:
+ PrefixRemover(StringPiece prefix) {
+ // Strip underscores and lower-case the prefix.
+ for (int i = 0; i < prefix.size(); i++) {
+ if (prefix[i] != '_') {
+ prefix_ += ascii_tolower(prefix[i]);
+ }
+ }
+ }
+
+ // Tries to remove the enum prefix from this enum value.
+ // If this is not possible, returns the input verbatim.
+ string MaybeRemove(StringPiece str) {
+ // We can't just lowercase and strip str and look for a prefix.
+ // We need to properly recognize the difference between:
+ //
+ // enum Foo {
+ // FOO_BAR_BAZ = 0;
+ // FOO_BARBAZ = 1;
+ // }
+ //
+ // This is acceptable (though perhaps not advisable) because even when
+ // we PascalCase, these two will still be distinct (BarBaz vs. Barbaz).
+ size_t i, j;
+
+ // Skip past prefix_ in str if we can.
+ for (i = 0, j = 0; i < str.size() && j < prefix_.size(); i++) {
+ if (str[i] == '_') {
+ continue;
+ }
+
+ if (ascii_tolower(str[i]) != prefix_[j++]) {
+ return str.as_string();
+ }
+ }
+
+ // If we didn't make it through the prefix, we've failed to strip the
+ // prefix.
+ if (j < prefix_.size()) {
+ return str.as_string();
+ }
+
+ // Skip underscores between prefix and further characters.
+ while (i < str.size() && str[i] == '_') {
+ i++;
+ }
+
+ // Enum label can't be the empty string.
+ if (i == str.size()) {
+ return str.as_string();
+ }
+
+ // We successfully stripped the prefix.
+ str.remove_prefix(i);
+ return str.as_string();
+ }
+
+ private:
+ string prefix_;
+};
+
// A DescriptorPool contains a bunch of hash_maps to implement the
// various Find*By*() methods. Since hashtable lookups are O(1), it's
// most efficient to construct a fixed set of large hash_maps used by
@@ -2390,6 +2498,17 @@ void FieldDescriptor::DebugString(int depth,
strings::SubstituteAndAppend(contents, " [default = $0",
DefaultValueAsString(true));
}
+ if (has_json_name_) {
+ if (!bracketed) {
+ bracketed = true;
+ contents->append("[");
+ } else {
+ contents->append(", ");
+ }
+ contents->append("json_name = \"");
+ contents->append(CEscape(json_name()));
+ contents->append("\"");
+ }
string formatted_options;
if (FormatBracketedOptions(depth, options(), &formatted_options)) {
@@ -2980,6 +3099,8 @@ class DescriptorBuilder {
void BuildOneof(const OneofDescriptorProto& proto,
Descriptor* parent,
OneofDescriptor* result);
+ void CheckEnumValueUniqueness(const EnumDescriptorProto& proto,
+ const EnumDescriptor* result);
void BuildEnum(const EnumDescriptorProto& proto,
const Descriptor* parent,
EnumDescriptor* result);
@@ -4232,7 +4353,7 @@ void DescriptorBuilder::BuildFieldOrExtension(const FieldDescriptorProto& proto,
result->json_name_ = tables_->AllocateString(proto.json_name());
} else {
result->has_json_name_ = false;
- result->json_name_ = result->camelcase_name_;
+ result->json_name_ = tables_->AllocateString(ToJsonName(proto.name()));
}
// Some compilers do not allow static_cast directly between two enum types,
@@ -4540,6 +4661,61 @@ void DescriptorBuilder::BuildOneof(const OneofDescriptorProto& proto,
proto, Symbol(result));
}
+void DescriptorBuilder::CheckEnumValueUniqueness(
+ const EnumDescriptorProto& proto, const EnumDescriptor* result) {
+
+ // Check that enum labels are still unique when we remove the enum prefix from
+ // values that have it.
+ //
+ // This will fail for something like:
+ //
+ // enum MyEnum {
+ // MY_ENUM_FOO = 0;
+ // FOO = 1;
+ // }
+ //
+ // By enforcing this reasonable constraint, we allow code generators to strip
+ // the prefix and/or PascalCase it without creating conflicts. This can lead
+ // to much nicer language-specific enums like:
+ //
+ // enum NameType {
+ // FirstName = 1,
+ // LastName = 2,
+ // }
+ //
+ // Instead of:
+ //
+ // enum NameType {
+ // NAME_TYPE_FIRST_NAME = 1,
+ // NAME_TYPE_LAST_NAME = 2,
+ // }
+ PrefixRemover remover(result->name());
+ map<string, const google::protobuf::EnumValueDescriptor*> values;
+ for (int i = 0; i < result->value_count(); i++) {
+ const google::protobuf::EnumValueDescriptor* value = result->value(i);
+ string stripped =
+ EnumValueToPascalCase(remover.MaybeRemove(value->name()));
+ std::pair<map<string, const google::protobuf::EnumValueDescriptor*>::iterator, bool>
+ insert_result = values.insert(std::make_pair(stripped, value));
+ bool inserted = insert_result.second;
+
+ // We don't throw the error if the two conflicting symbols are identical, or
+ // if they map to the same number. In the former case, the normal symbol
+ // duplication error will fire so we don't need to (and its error message
+ // will make more sense). We allow the latter case so users can create
+ // aliases which add or remove the prefix (code generators that do prefix
+ // stripping should de-dup the labels in this case).
+ if (!inserted && insert_result.first->second->name() != value->name() &&
+ insert_result.first->second->number() != value->number()) {
+ AddError(value->full_name(), proto.value(i),
+ DescriptorPool::ErrorCollector::NAME,
+ "When enum name is stripped and label is PascalCased (" +
+ stripped + "), this value label conflicts with " +
+ values[stripped]->name());
+ }
+ }
+}
+
void DescriptorBuilder::BuildEnum(const EnumDescriptorProto& proto,
const Descriptor* parent,
EnumDescriptor* result) {
@@ -4568,6 +4744,8 @@ void DescriptorBuilder::BuildEnum(const EnumDescriptorProto& proto,
BUILD_ARRAY(proto, result, value, BuildEnumValue, result);
+ CheckEnumValueUniqueness(proto, result);
+
// Copy options.
if (!proto.has_options()) {
result->options_ = NULL; // Will set to default_instance later.
@@ -5254,7 +5432,6 @@ void DescriptorBuilder::ValidateProto3Enum(
}
}
-
void DescriptorBuilder::ValidateMessageOptions(Descriptor* message,
const DescriptorProto& proto) {
VALIDATE_OPTIONS_FROM_ARRAY(message, field, Field);
@@ -5275,7 +5452,6 @@ void DescriptorBuilder::ValidateMessageOptions(Descriptor* message,
max_extension_range));
}
}
-
}
void DescriptorBuilder::ValidateFieldOptions(FieldDescriptor* field,