Index: third_party/protobuf/src/google/protobuf/compiler/csharp/csharp_helpers.cc |
diff --git a/third_party/protobuf/src/google/protobuf/compiler/csharp/csharp_helpers.cc b/third_party/protobuf/src/google/protobuf/compiler/csharp/csharp_helpers.cc |
index c51fe44b391ec1cd4f8de654a9bdefb653d8a75c..6c154c5a5862950d110f2d5049f6ca1af0e2b52a 100644 |
--- a/third_party/protobuf/src/google/protobuf/compiler/csharp/csharp_helpers.cc |
+++ b/third_party/protobuf/src/google/protobuf/compiler/csharp/csharp_helpers.cc |
@@ -48,6 +48,7 @@ |
#include <google/protobuf/compiler/csharp/csharp_enum_field.h> |
#include <google/protobuf/compiler/csharp/csharp_map_field.h> |
#include <google/protobuf/compiler/csharp/csharp_message_field.h> |
+#include <google/protobuf/compiler/csharp/csharp_options.h> |
#include <google/protobuf/compiler/csharp/csharp_primitive_field.h> |
#include <google/protobuf/compiler/csharp/csharp_repeated_enum_field.h> |
#include <google/protobuf/compiler/csharp/csharp_repeated_message_field.h> |
@@ -127,7 +128,8 @@ std::string GetFileNameBase(const FileDescriptor* descriptor) { |
} |
std::string GetReflectionClassUnqualifiedName(const FileDescriptor* descriptor) { |
- // TODO: Detect collisions with existing messages, and append an underscore if necessary. |
+ // TODO: Detect collisions with existing messages, |
+ // and append an underscore if necessary. |
return GetFileNameBase(descriptor) + "Reflection"; |
} |
@@ -176,6 +178,104 @@ std::string UnderscoresToPascalCase(const std::string& input) { |
return UnderscoresToCamelCase(input, true); |
} |
+// Convert a string which is expected to be SHOUTY_CASE (but may not be *precisely* shouty) |
+// into a PascalCase string. Precise rules implemented: |
+ |
+// Previous input character Current character Case |
+// Any Non-alphanumeric Skipped |
+// None - first char of input Alphanumeric Upper |
+// Non-letter (e.g. _ or 1) Alphanumeric Upper |
+// Numeric Alphanumeric Upper |
+// Lower letter Alphanumeric Same as current |
+// Upper letter Alphanumeric Lower |
+std::string ShoutyToPascalCase(const std::string& input) { |
+ string result; |
+ // Simple way of implementing "always start with upper" |
+ char previous = '_'; |
+ for (int i = 0; i < input.size(); i++) { |
+ char current = input[i]; |
+ if (!ascii_isalnum(current)) { |
+ previous = current; |
+ continue; |
+ } |
+ if (!ascii_isalnum(previous)) { |
+ result += ascii_toupper(current); |
+ } else if (ascii_isdigit(previous)) { |
+ result += ascii_toupper(current); |
+ } else if (ascii_islower(previous)) { |
+ result += current; |
+ } else { |
+ result += ascii_tolower(current); |
+ } |
+ previous = current; |
+ } |
+ return result; |
+} |
+ |
+// Attempt to remove a prefix from a value, ignoring casing and skipping underscores. |
+// (foo, foo_bar) => bar - underscore after prefix is skipped |
+// (FOO, foo_bar) => bar - casing is ignored |
+// (foo_bar, foobarbaz) => baz - underscore in prefix is ignored |
+// (foobar, foo_barbaz) => baz - underscore in value is ignored |
+// (foo, bar) => bar - prefix isn't matched; return original value |
+std::string TryRemovePrefix(const std::string& prefix, const std::string& value) { |
+ // First normalize to a lower-case no-underscores prefix to match against |
+ std::string prefix_to_match = ""; |
+ for (size_t i = 0; i < prefix.size(); i++) { |
+ if (prefix[i] != '_') { |
+ prefix_to_match += ascii_tolower(prefix[i]); |
+ } |
+ } |
+ |
+ // This keeps track of how much of value we've consumed |
+ size_t prefix_index, value_index; |
+ for (prefix_index = 0, value_index = 0; |
+ prefix_index < prefix_to_match.size() && value_index < value.size(); |
+ value_index++) { |
+ // Skip over underscores in the value |
+ if (value[value_index] == '_') { |
+ continue; |
+ } |
+ if (ascii_tolower(value[value_index]) != prefix_to_match[prefix_index++]) { |
+ // Failed to match the prefix - bail out early. |
+ return value; |
+ } |
+ } |
+ |
+ // If we didn't finish looking through the prefix, we can't strip it. |
+ if (prefix_index < prefix_to_match.size()) { |
+ return value; |
+ } |
+ |
+ // Step over any underscores after the prefix |
+ while (value_index < value.size() && value[value_index] == '_') { |
+ value_index++; |
+ } |
+ |
+ // If there's nothing left (e.g. it was a prefix with only underscores afterwards), don't strip. |
+ if (value_index == value.size()) { |
+ return value; |
+ } |
+ |
+ return value.substr(value_index); |
+} |
+ |
+// Format the enum value name in a pleasant way for C#: |
+// - Strip the enum name as a prefix if possible |
+// - Convert to PascalCase. |
+// For example, an enum called Color with a value of COLOR_BLUE should |
+// result in an enum value in C# called just Blue |
+std::string GetEnumValueName(const std::string& enum_name, const std::string& enum_value_name) { |
+ std::string stripped = TryRemovePrefix(enum_name, enum_value_name); |
+ std::string result = ShoutyToPascalCase(stripped); |
+ // Just in case we have an enum name of FOO and a value of FOO_2... make sure the returned |
+ // string is a valid identifier. |
+ if (ascii_isdigit(result[0])) { |
+ result = "_" + result; |
+ } |
+ return result; |
+} |
+ |
std::string ToCSharpName(const std::string& name, const FileDescriptor* file) { |
std::string result = GetFileNamespace(file); |
if (result != "") { |
@@ -351,49 +451,50 @@ std::string FileDescriptorToBase64(const FileDescriptor* descriptor) { |
} |
FieldGeneratorBase* CreateFieldGenerator(const FieldDescriptor* descriptor, |
- int fieldOrdinal) { |
+ int fieldOrdinal, |
+ const Options* options) { |
switch (descriptor->type()) { |
case FieldDescriptor::TYPE_GROUP: |
case FieldDescriptor::TYPE_MESSAGE: |
if (descriptor->is_repeated()) { |
if (descriptor->is_map()) { |
- return new MapFieldGenerator(descriptor, fieldOrdinal); |
+ return new MapFieldGenerator(descriptor, fieldOrdinal, options); |
} else { |
- return new RepeatedMessageFieldGenerator(descriptor, fieldOrdinal); |
+ return new RepeatedMessageFieldGenerator(descriptor, fieldOrdinal, options); |
} |
} else { |
if (IsWrapperType(descriptor)) { |
if (descriptor->containing_oneof()) { |
- return new WrapperOneofFieldGenerator(descriptor, fieldOrdinal); |
+ return new WrapperOneofFieldGenerator(descriptor, fieldOrdinal, options); |
} else { |
- return new WrapperFieldGenerator(descriptor, fieldOrdinal); |
+ return new WrapperFieldGenerator(descriptor, fieldOrdinal, options); |
} |
} else { |
if (descriptor->containing_oneof()) { |
- return new MessageOneofFieldGenerator(descriptor, fieldOrdinal); |
+ return new MessageOneofFieldGenerator(descriptor, fieldOrdinal, options); |
} else { |
- return new MessageFieldGenerator(descriptor, fieldOrdinal); |
+ return new MessageFieldGenerator(descriptor, fieldOrdinal, options); |
} |
} |
} |
case FieldDescriptor::TYPE_ENUM: |
if (descriptor->is_repeated()) { |
- return new RepeatedEnumFieldGenerator(descriptor, fieldOrdinal); |
+ return new RepeatedEnumFieldGenerator(descriptor, fieldOrdinal, options); |
} else { |
if (descriptor->containing_oneof()) { |
- return new EnumOneofFieldGenerator(descriptor, fieldOrdinal); |
+ return new EnumOneofFieldGenerator(descriptor, fieldOrdinal, options); |
} else { |
- return new EnumFieldGenerator(descriptor, fieldOrdinal); |
+ return new EnumFieldGenerator(descriptor, fieldOrdinal, options); |
} |
} |
default: |
if (descriptor->is_repeated()) { |
- return new RepeatedPrimitiveFieldGenerator(descriptor, fieldOrdinal); |
+ return new RepeatedPrimitiveFieldGenerator(descriptor, fieldOrdinal, options); |
} else { |
if (descriptor->containing_oneof()) { |
- return new PrimitiveOneofFieldGenerator(descriptor, fieldOrdinal); |
+ return new PrimitiveOneofFieldGenerator(descriptor, fieldOrdinal, options); |
} else { |
- return new PrimitiveFieldGenerator(descriptor, fieldOrdinal); |
+ return new PrimitiveFieldGenerator(descriptor, fieldOrdinal, options); |
} |
} |
} |