diff options
| author | thegeorg <[email protected]> | 2022-02-10 16:45:08 +0300 |
|---|---|---|
| committer | Daniil Cherednik <[email protected]> | 2022-02-10 16:45:08 +0300 |
| commit | 4e839db24a3bbc9f1c610c43d6faaaa99824dcca (patch) | |
| tree | 506dac10f5df94fab310584ee51b24fc5a081c22 /contrib/libs/protoc/src/google/protobuf/compiler/csharp/csharp_helpers.cc | |
| parent | 2d37894b1b037cf24231090eda8589bbb44fb6fc (diff) | |
Restoring authorship annotation for <[email protected]>. Commit 1 of 2.
Diffstat (limited to 'contrib/libs/protoc/src/google/protobuf/compiler/csharp/csharp_helpers.cc')
| -rw-r--r-- | contrib/libs/protoc/src/google/protobuf/compiler/csharp/csharp_helpers.cc | 1184 |
1 files changed, 592 insertions, 592 deletions
diff --git a/contrib/libs/protoc/src/google/protobuf/compiler/csharp/csharp_helpers.cc b/contrib/libs/protoc/src/google/protobuf/compiler/csharp/csharp_helpers.cc index b80131b024d..c77bfb5bd0e 100644 --- a/contrib/libs/protoc/src/google/protobuf/compiler/csharp/csharp_helpers.cc +++ b/contrib/libs/protoc/src/google/protobuf/compiler/csharp/csharp_helpers.cc @@ -1,592 +1,592 @@ -// Protocol Buffers - Google's data interchange format -// Copyright 2008 Google Inc. All rights reserved. -// https://developers.google.com/protocol-buffers/ -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -// Author: [email protected] (Kenton Varda) -// Based on original Protocol Buffers design by -// Sanjay Ghemawat, Jeff Dean, and others. - -#include <algorithm> -#include <limits> -#include <vector> -#include <sstream> - -#include <google/protobuf/compiler/csharp/csharp_helpers.h> -#include <google/protobuf/compiler/csharp/csharp_names.h> -#include <google/protobuf/descriptor.pb.h> -#include <google/protobuf/io/printer.h> -#include <google/protobuf/wire_format.h> -#include <google/protobuf/stubs/strutil.h> - -#include <google/protobuf/compiler/csharp/csharp_field_base.h> -#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> -#include <google/protobuf/compiler/csharp/csharp_repeated_primitive_field.h> -#include <google/protobuf/compiler/csharp/csharp_wrapper_field.h> - -namespace google { -namespace protobuf { -namespace compiler { -namespace csharp { - -CSharpType GetCSharpType(FieldDescriptor::Type type) { - switch (type) { - case FieldDescriptor::TYPE_INT32: - return CSHARPTYPE_INT32; - case FieldDescriptor::TYPE_INT64: - return CSHARPTYPE_INT64; - case FieldDescriptor::TYPE_UINT32: - return CSHARPTYPE_UINT32; - case FieldDescriptor::TYPE_UINT64: - return CSHARPTYPE_UINT32; - case FieldDescriptor::TYPE_SINT32: - return CSHARPTYPE_INT32; - case FieldDescriptor::TYPE_SINT64: - return CSHARPTYPE_INT64; - case FieldDescriptor::TYPE_FIXED32: - return CSHARPTYPE_UINT32; - case FieldDescriptor::TYPE_FIXED64: - return CSHARPTYPE_UINT64; - case FieldDescriptor::TYPE_SFIXED32: - return CSHARPTYPE_INT32; - case FieldDescriptor::TYPE_SFIXED64: - return CSHARPTYPE_INT64; - case FieldDescriptor::TYPE_FLOAT: - return CSHARPTYPE_FLOAT; - case FieldDescriptor::TYPE_DOUBLE: - return CSHARPTYPE_DOUBLE; - case FieldDescriptor::TYPE_BOOL: - return CSHARPTYPE_BOOL; - case FieldDescriptor::TYPE_ENUM: - return CSHARPTYPE_ENUM; - case FieldDescriptor::TYPE_STRING: - return CSHARPTYPE_STRING; - case FieldDescriptor::TYPE_BYTES: - return CSHARPTYPE_BYTESTRING; - case FieldDescriptor::TYPE_GROUP: - return CSHARPTYPE_MESSAGE; - case FieldDescriptor::TYPE_MESSAGE: - return CSHARPTYPE_MESSAGE; - - // No default because we want the compiler to complain if any new - // types are added. - } - GOOGLE_LOG(FATAL)<< "Can't get here."; - return (CSharpType) -1; -} - -TProtoStringType StripDotProto(const TProtoStringType& proto_file) { - int lastindex = proto_file.find_last_of("."); - return proto_file.substr(0, lastindex); -} - -TProtoStringType GetFileNamespace(const FileDescriptor* descriptor) { - if (descriptor->options().has_csharp_namespace()) { - return descriptor->options().csharp_namespace(); - } - return UnderscoresToCamelCase(descriptor->package(), true, true); -} - -// Returns the Pascal-cased last part of the proto file. For example, -// input of "google/protobuf/foo_bar.proto" would result in "FooBar". -TProtoStringType GetFileNameBase(const FileDescriptor* descriptor) { - TProtoStringType proto_file = descriptor->name(); - int lastslash = proto_file.find_last_of("/"); - TProtoStringType base = proto_file.substr(lastslash + 1); - return UnderscoresToPascalCase(StripDotProto(base)); -} - -TProtoStringType GetReflectionClassUnqualifiedName(const FileDescriptor* descriptor) { - // TODO: Detect collisions with existing messages, - // and append an underscore if necessary. - return GetFileNameBase(descriptor) + "Reflection"; -} - -TProtoStringType GetExtensionClassUnqualifiedName(const FileDescriptor* descriptor) { - // TODO: Detect collisions with existing messages, - // and append an underscore if necessary. - return GetFileNameBase(descriptor) + "Extensions"; -} - -// TODO(jtattermusch): can we reuse a utility function? -TProtoStringType UnderscoresToCamelCase(const TProtoStringType& input, - bool cap_next_letter, - bool preserve_period) { - TProtoStringType result; - // Note: I distrust ctype.h due to locales. - for (int i = 0; i < input.size(); i++) { - if ('a' <= input[i] && input[i] <= 'z') { - if (cap_next_letter) { - result += input[i] + ('A' - 'a'); - } else { - result += input[i]; - } - cap_next_letter = false; - } else if ('A' <= input[i] && input[i] <= 'Z') { - if (i == 0 && !cap_next_letter) { - // Force first letter to lower-case unless explicitly told to - // capitalize it. - result += input[i] + ('a' - 'A'); - } else { - // Capital letters after the first are left as-is. - result += input[i]; - } - cap_next_letter = false; - } else if ('0' <= input[i] && input[i] <= '9') { - result += input[i]; - cap_next_letter = true; - } else { - cap_next_letter = true; - if (input[i] == '.' && preserve_period) { - result += '.'; - } - } - } - // Add a trailing "_" if the name should be altered. - if (input.size() > 0 && input[input.size() - 1] == '#') { - result += '_'; - } - return result; -} - -TProtoStringType UnderscoresToPascalCase(const TProtoStringType& 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 -TProtoStringType ShoutyToPascalCase(const TProtoStringType& input) { - TProtoStringType 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 -TProtoStringType TryRemovePrefix(const TProtoStringType& prefix, const TProtoStringType& value) { - // First normalize to a lower-case no-underscores prefix to match against - TProtoStringType 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 -TProtoStringType GetEnumValueName(const TProtoStringType& enum_name, const TProtoStringType& enum_value_name) { - TProtoStringType stripped = TryRemovePrefix(enum_name, enum_value_name); - TProtoStringType 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; -} - -uint GetGroupEndTag(const Descriptor* descriptor) { - const Descriptor* containing_type = descriptor->containing_type(); - if (containing_type != NULL) { - const FieldDescriptor* field; - for (int i = 0; i < containing_type->field_count(); i++) { - field = containing_type->field(i); - if (field->type() == FieldDescriptor::Type::TYPE_GROUP && - field->message_type() == descriptor) { - return internal::WireFormatLite::MakeTag( - field->number(), internal::WireFormatLite::WIRETYPE_END_GROUP); - } - } - for (int i = 0; i < containing_type->extension_count(); i++) { - field = containing_type->extension(i); - if (field->type() == FieldDescriptor::Type::TYPE_GROUP && - field->message_type() == descriptor) { - return internal::WireFormatLite::MakeTag( - field->number(), internal::WireFormatLite::WIRETYPE_END_GROUP); - } - } - } else { - const FileDescriptor* containing_file = descriptor->file(); - if (containing_file != NULL) { - const FieldDescriptor* field; - for (int i = 0; i < containing_file->extension_count(); i++) { - field = containing_file->extension(i); - if (field->type() == FieldDescriptor::Type::TYPE_GROUP && - field->message_type() == descriptor) { - return internal::WireFormatLite::MakeTag( - field->number(), internal::WireFormatLite::WIRETYPE_END_GROUP); - } - } - } - } - - return 0; -} - -TProtoStringType ToCSharpName(const TProtoStringType& name, const FileDescriptor* file) { - TProtoStringType result = GetFileNamespace(file); - if (!result.empty()) { - result += '.'; - } - TProtoStringType classname; - if (file->package().empty()) { - classname = name; - } else { - // Strip the proto package from full_name since we've replaced it with - // the C# namespace. - classname = name.substr(file->package().size() + 1); - } - result += StringReplace(classname, ".", ".Types.", true); - return "global::" + result; -} - -TProtoStringType GetReflectionClassName(const FileDescriptor* descriptor) { - TProtoStringType result = GetFileNamespace(descriptor); - if (!result.empty()) { - result += '.'; - } - result += GetReflectionClassUnqualifiedName(descriptor); - return "global::" + result; -} - -TProtoStringType GetFullExtensionName(const FieldDescriptor* descriptor) { - if (descriptor->extension_scope()) { - return GetClassName(descriptor->extension_scope()) + ".Extensions." + GetPropertyName(descriptor); - } - else { - return GetExtensionClassUnqualifiedName(descriptor->file()) + "." + GetPropertyName(descriptor); - } -} - -TProtoStringType GetClassName(const Descriptor* descriptor) { - return ToCSharpName(descriptor->full_name(), descriptor->file()); -} - -TProtoStringType GetClassName(const EnumDescriptor* descriptor) { - return ToCSharpName(descriptor->full_name(), descriptor->file()); -} - -// Groups are hacky: The name of the field is just the lower-cased name -// of the group type. In C#, though, we would like to retain the original -// capitalization of the type name. -TProtoStringType GetFieldName(const FieldDescriptor* descriptor) { - if (descriptor->type() == FieldDescriptor::TYPE_GROUP) { - return descriptor->message_type()->name(); - } else { - return descriptor->name(); - } -} - -TProtoStringType GetFieldConstantName(const FieldDescriptor* field) { - return GetPropertyName(field) + "FieldNumber"; -} - -TProtoStringType GetPropertyName(const FieldDescriptor* descriptor) { - // TODO(jtattermusch): consider introducing csharp_property_name field option - TProtoStringType property_name = UnderscoresToPascalCase(GetFieldName(descriptor)); - // Avoid either our own type name or reserved names. Note that not all names - // are reserved - a field called to_string, write_to etc would still cause a problem. - // There are various ways of ending up with naming collisions, but we try to avoid obvious - // ones. - if (property_name == descriptor->containing_type()->name() - || property_name == "Types" - || property_name == "Descriptor") { - property_name += "_"; - } - return property_name; -} - -TProtoStringType GetOutputFile(const FileDescriptor* descriptor, - const TProtoStringType file_extension, - const bool generate_directories, - const TProtoStringType base_namespace, - TProtoStringType* error) { - TProtoStringType relative_filename = GetFileNameBase(descriptor) + file_extension; - if (!generate_directories) { - return relative_filename; - } - TProtoStringType ns = GetFileNamespace(descriptor); - TProtoStringType namespace_suffix = ns; - if (!base_namespace.empty()) { - // Check that the base_namespace is either equal to or a leading part of - // the file namespace. This isn't just a simple prefix; "Foo.B" shouldn't - // be regarded as a prefix of "Foo.Bar". The simplest option is to add "." - // to both. - TProtoStringType extended_ns = ns + "."; - if (extended_ns.find(base_namespace + ".") != 0) { - *error = "Namespace " + ns + " is not a prefix namespace of base namespace " + base_namespace; - return ""; // This will be ignored, because we've set an error. - } - namespace_suffix = ns.substr(base_namespace.length()); - if (namespace_suffix.find(".") == 0) { - namespace_suffix = namespace_suffix.substr(1); - } - } - - TProtoStringType namespace_dir = StringReplace(namespace_suffix, ".", "/", true); - if (!namespace_dir.empty()) { - namespace_dir += "/"; - } - return namespace_dir + relative_filename; -} - -// TODO: c&p from Java protoc plugin -// For encodings with fixed sizes, returns that size in bytes. Otherwise -// returns -1. -int GetFixedSize(FieldDescriptor::Type type) { - switch (type) { - case FieldDescriptor::TYPE_INT32 : return -1; - case FieldDescriptor::TYPE_INT64 : return -1; - case FieldDescriptor::TYPE_UINT32 : return -1; - case FieldDescriptor::TYPE_UINT64 : return -1; - case FieldDescriptor::TYPE_SINT32 : return -1; - case FieldDescriptor::TYPE_SINT64 : return -1; - case FieldDescriptor::TYPE_FIXED32 : return internal::WireFormatLite::kFixed32Size; - case FieldDescriptor::TYPE_FIXED64 : return internal::WireFormatLite::kFixed64Size; - case FieldDescriptor::TYPE_SFIXED32: return internal::WireFormatLite::kSFixed32Size; - case FieldDescriptor::TYPE_SFIXED64: return internal::WireFormatLite::kSFixed64Size; - case FieldDescriptor::TYPE_FLOAT : return internal::WireFormatLite::kFloatSize; - case FieldDescriptor::TYPE_DOUBLE : return internal::WireFormatLite::kDoubleSize; - - case FieldDescriptor::TYPE_BOOL : return internal::WireFormatLite::kBoolSize; - case FieldDescriptor::TYPE_ENUM : return -1; - - case FieldDescriptor::TYPE_STRING : return -1; - case FieldDescriptor::TYPE_BYTES : return -1; - case FieldDescriptor::TYPE_GROUP : return -1; - case FieldDescriptor::TYPE_MESSAGE : return -1; - - // No default because we want the compiler to complain if any new - // types are added. - } - GOOGLE_LOG(FATAL) << "Can't get here."; - return -1; -} - -static const char base64_chars[] = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - -TProtoStringType StringToBase64(const TProtoStringType& input) { - TProtoStringType result; - size_t remaining = input.size(); - const unsigned char *src = (const unsigned char*) input.c_str(); - while (remaining > 2) { - result += base64_chars[src[0] >> 2]; - result += base64_chars[((src[0] & 0x3) << 4) | (src[1] >> 4)]; - result += base64_chars[((src[1] & 0xf) << 2) | (src[2] >> 6)]; - result += base64_chars[src[2] & 0x3f]; - remaining -= 3; - src += 3; - } - switch (remaining) { - case 2: - result += base64_chars[src[0] >> 2]; - result += base64_chars[((src[0] & 0x3) << 4) | (src[1] >> 4)]; - result += base64_chars[(src[1] & 0xf) << 2]; - result += '='; - src += 2; - break; - case 1: - result += base64_chars[src[0] >> 2]; - result += base64_chars[((src[0] & 0x3) << 4)]; - result += '='; - result += '='; - src += 1; - break; - } - return result; -} - -TProtoStringType FileDescriptorToBase64(const FileDescriptor* descriptor) { - TProtoStringType fdp_bytes; - FileDescriptorProto fdp; - descriptor->CopyTo(&fdp); - fdp.SerializeToString(&fdp_bytes); - return StringToBase64(fdp_bytes); -} - -FieldGeneratorBase* CreateFieldGenerator(const FieldDescriptor* descriptor, - int presenceIndex, - 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, presenceIndex, options); - } else { - return new RepeatedMessageFieldGenerator(descriptor, presenceIndex, options); - } - } else { - if (IsWrapperType(descriptor)) { - if (descriptor->real_containing_oneof()) { - return new WrapperOneofFieldGenerator(descriptor, presenceIndex, options); - } else { - return new WrapperFieldGenerator(descriptor, presenceIndex, options); - } - } else { - if (descriptor->real_containing_oneof()) { - return new MessageOneofFieldGenerator(descriptor, presenceIndex, options); - } else { - return new MessageFieldGenerator(descriptor, presenceIndex, options); - } - } - } - case FieldDescriptor::TYPE_ENUM: - if (descriptor->is_repeated()) { - return new RepeatedEnumFieldGenerator(descriptor, presenceIndex, options); - } else { - if (descriptor->real_containing_oneof()) { - return new EnumOneofFieldGenerator(descriptor, presenceIndex, options); - } else { - return new EnumFieldGenerator(descriptor, presenceIndex, options); - } - } - default: - if (descriptor->is_repeated()) { - return new RepeatedPrimitiveFieldGenerator(descriptor, presenceIndex, options); - } else { - if (descriptor->real_containing_oneof()) { - return new PrimitiveOneofFieldGenerator(descriptor, presenceIndex, options); - } else { - return new PrimitiveFieldGenerator(descriptor, presenceIndex, options); - } - } - } -} - -bool IsNullable(const FieldDescriptor* descriptor) { - if (descriptor->is_repeated()) { - return true; - } - - switch (descriptor->type()) { - case FieldDescriptor::TYPE_ENUM: - case FieldDescriptor::TYPE_DOUBLE: - case FieldDescriptor::TYPE_FLOAT: - case FieldDescriptor::TYPE_INT64: - case FieldDescriptor::TYPE_UINT64: - case FieldDescriptor::TYPE_INT32: - case FieldDescriptor::TYPE_FIXED64: - case FieldDescriptor::TYPE_FIXED32: - case FieldDescriptor::TYPE_BOOL: - case FieldDescriptor::TYPE_UINT32: - case FieldDescriptor::TYPE_SFIXED32: - case FieldDescriptor::TYPE_SFIXED64: - case FieldDescriptor::TYPE_SINT32: - case FieldDescriptor::TYPE_SINT64: - return false; - - case FieldDescriptor::TYPE_MESSAGE: - case FieldDescriptor::TYPE_GROUP: - case FieldDescriptor::TYPE_STRING: - case FieldDescriptor::TYPE_BYTES: - return true; - - default: - GOOGLE_LOG(FATAL) << "Unknown field type."; - return true; - } -} - -} // namespace csharp -} // namespace compiler -} // namespace protobuf -} // namespace google +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: [email protected] (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include <algorithm> +#include <limits> +#include <vector> +#include <sstream> + +#include <google/protobuf/compiler/csharp/csharp_helpers.h> +#include <google/protobuf/compiler/csharp/csharp_names.h> +#include <google/protobuf/descriptor.pb.h> +#include <google/protobuf/io/printer.h> +#include <google/protobuf/wire_format.h> +#include <google/protobuf/stubs/strutil.h> + +#include <google/protobuf/compiler/csharp/csharp_field_base.h> +#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> +#include <google/protobuf/compiler/csharp/csharp_repeated_primitive_field.h> +#include <google/protobuf/compiler/csharp/csharp_wrapper_field.h> + +namespace google { +namespace protobuf { +namespace compiler { +namespace csharp { + +CSharpType GetCSharpType(FieldDescriptor::Type type) { + switch (type) { + case FieldDescriptor::TYPE_INT32: + return CSHARPTYPE_INT32; + case FieldDescriptor::TYPE_INT64: + return CSHARPTYPE_INT64; + case FieldDescriptor::TYPE_UINT32: + return CSHARPTYPE_UINT32; + case FieldDescriptor::TYPE_UINT64: + return CSHARPTYPE_UINT32; + case FieldDescriptor::TYPE_SINT32: + return CSHARPTYPE_INT32; + case FieldDescriptor::TYPE_SINT64: + return CSHARPTYPE_INT64; + case FieldDescriptor::TYPE_FIXED32: + return CSHARPTYPE_UINT32; + case FieldDescriptor::TYPE_FIXED64: + return CSHARPTYPE_UINT64; + case FieldDescriptor::TYPE_SFIXED32: + return CSHARPTYPE_INT32; + case FieldDescriptor::TYPE_SFIXED64: + return CSHARPTYPE_INT64; + case FieldDescriptor::TYPE_FLOAT: + return CSHARPTYPE_FLOAT; + case FieldDescriptor::TYPE_DOUBLE: + return CSHARPTYPE_DOUBLE; + case FieldDescriptor::TYPE_BOOL: + return CSHARPTYPE_BOOL; + case FieldDescriptor::TYPE_ENUM: + return CSHARPTYPE_ENUM; + case FieldDescriptor::TYPE_STRING: + return CSHARPTYPE_STRING; + case FieldDescriptor::TYPE_BYTES: + return CSHARPTYPE_BYTESTRING; + case FieldDescriptor::TYPE_GROUP: + return CSHARPTYPE_MESSAGE; + case FieldDescriptor::TYPE_MESSAGE: + return CSHARPTYPE_MESSAGE; + + // No default because we want the compiler to complain if any new + // types are added. + } + GOOGLE_LOG(FATAL)<< "Can't get here."; + return (CSharpType) -1; +} + +TProtoStringType StripDotProto(const TProtoStringType& proto_file) { + int lastindex = proto_file.find_last_of("."); + return proto_file.substr(0, lastindex); +} + +TProtoStringType GetFileNamespace(const FileDescriptor* descriptor) { + if (descriptor->options().has_csharp_namespace()) { + return descriptor->options().csharp_namespace(); + } + return UnderscoresToCamelCase(descriptor->package(), true, true); +} + +// Returns the Pascal-cased last part of the proto file. For example, +// input of "google/protobuf/foo_bar.proto" would result in "FooBar". +TProtoStringType GetFileNameBase(const FileDescriptor* descriptor) { + TProtoStringType proto_file = descriptor->name(); + int lastslash = proto_file.find_last_of("/"); + TProtoStringType base = proto_file.substr(lastslash + 1); + return UnderscoresToPascalCase(StripDotProto(base)); +} + +TProtoStringType GetReflectionClassUnqualifiedName(const FileDescriptor* descriptor) { + // TODO: Detect collisions with existing messages, + // and append an underscore if necessary. + return GetFileNameBase(descriptor) + "Reflection"; +} + +TProtoStringType GetExtensionClassUnqualifiedName(const FileDescriptor* descriptor) { + // TODO: Detect collisions with existing messages, + // and append an underscore if necessary. + return GetFileNameBase(descriptor) + "Extensions"; +} + +// TODO(jtattermusch): can we reuse a utility function? +TProtoStringType UnderscoresToCamelCase(const TProtoStringType& input, + bool cap_next_letter, + bool preserve_period) { + TProtoStringType result; + // Note: I distrust ctype.h due to locales. + for (int i = 0; i < input.size(); i++) { + if ('a' <= input[i] && input[i] <= 'z') { + if (cap_next_letter) { + result += input[i] + ('A' - 'a'); + } else { + result += input[i]; + } + cap_next_letter = false; + } else if ('A' <= input[i] && input[i] <= 'Z') { + if (i == 0 && !cap_next_letter) { + // Force first letter to lower-case unless explicitly told to + // capitalize it. + result += input[i] + ('a' - 'A'); + } else { + // Capital letters after the first are left as-is. + result += input[i]; + } + cap_next_letter = false; + } else if ('0' <= input[i] && input[i] <= '9') { + result += input[i]; + cap_next_letter = true; + } else { + cap_next_letter = true; + if (input[i] == '.' && preserve_period) { + result += '.'; + } + } + } + // Add a trailing "_" if the name should be altered. + if (input.size() > 0 && input[input.size() - 1] == '#') { + result += '_'; + } + return result; +} + +TProtoStringType UnderscoresToPascalCase(const TProtoStringType& 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 +TProtoStringType ShoutyToPascalCase(const TProtoStringType& input) { + TProtoStringType 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 +TProtoStringType TryRemovePrefix(const TProtoStringType& prefix, const TProtoStringType& value) { + // First normalize to a lower-case no-underscores prefix to match against + TProtoStringType 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 +TProtoStringType GetEnumValueName(const TProtoStringType& enum_name, const TProtoStringType& enum_value_name) { + TProtoStringType stripped = TryRemovePrefix(enum_name, enum_value_name); + TProtoStringType 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; +} + +uint GetGroupEndTag(const Descriptor* descriptor) { + const Descriptor* containing_type = descriptor->containing_type(); + if (containing_type != NULL) { + const FieldDescriptor* field; + for (int i = 0; i < containing_type->field_count(); i++) { + field = containing_type->field(i); + if (field->type() == FieldDescriptor::Type::TYPE_GROUP && + field->message_type() == descriptor) { + return internal::WireFormatLite::MakeTag( + field->number(), internal::WireFormatLite::WIRETYPE_END_GROUP); + } + } + for (int i = 0; i < containing_type->extension_count(); i++) { + field = containing_type->extension(i); + if (field->type() == FieldDescriptor::Type::TYPE_GROUP && + field->message_type() == descriptor) { + return internal::WireFormatLite::MakeTag( + field->number(), internal::WireFormatLite::WIRETYPE_END_GROUP); + } + } + } else { + const FileDescriptor* containing_file = descriptor->file(); + if (containing_file != NULL) { + const FieldDescriptor* field; + for (int i = 0; i < containing_file->extension_count(); i++) { + field = containing_file->extension(i); + if (field->type() == FieldDescriptor::Type::TYPE_GROUP && + field->message_type() == descriptor) { + return internal::WireFormatLite::MakeTag( + field->number(), internal::WireFormatLite::WIRETYPE_END_GROUP); + } + } + } + } + + return 0; +} + +TProtoStringType ToCSharpName(const TProtoStringType& name, const FileDescriptor* file) { + TProtoStringType result = GetFileNamespace(file); + if (!result.empty()) { + result += '.'; + } + TProtoStringType classname; + if (file->package().empty()) { + classname = name; + } else { + // Strip the proto package from full_name since we've replaced it with + // the C# namespace. + classname = name.substr(file->package().size() + 1); + } + result += StringReplace(classname, ".", ".Types.", true); + return "global::" + result; +} + +TProtoStringType GetReflectionClassName(const FileDescriptor* descriptor) { + TProtoStringType result = GetFileNamespace(descriptor); + if (!result.empty()) { + result += '.'; + } + result += GetReflectionClassUnqualifiedName(descriptor); + return "global::" + result; +} + +TProtoStringType GetFullExtensionName(const FieldDescriptor* descriptor) { + if (descriptor->extension_scope()) { + return GetClassName(descriptor->extension_scope()) + ".Extensions." + GetPropertyName(descriptor); + } + else { + return GetExtensionClassUnqualifiedName(descriptor->file()) + "." + GetPropertyName(descriptor); + } +} + +TProtoStringType GetClassName(const Descriptor* descriptor) { + return ToCSharpName(descriptor->full_name(), descriptor->file()); +} + +TProtoStringType GetClassName(const EnumDescriptor* descriptor) { + return ToCSharpName(descriptor->full_name(), descriptor->file()); +} + +// Groups are hacky: The name of the field is just the lower-cased name +// of the group type. In C#, though, we would like to retain the original +// capitalization of the type name. +TProtoStringType GetFieldName(const FieldDescriptor* descriptor) { + if (descriptor->type() == FieldDescriptor::TYPE_GROUP) { + return descriptor->message_type()->name(); + } else { + return descriptor->name(); + } +} + +TProtoStringType GetFieldConstantName(const FieldDescriptor* field) { + return GetPropertyName(field) + "FieldNumber"; +} + +TProtoStringType GetPropertyName(const FieldDescriptor* descriptor) { + // TODO(jtattermusch): consider introducing csharp_property_name field option + TProtoStringType property_name = UnderscoresToPascalCase(GetFieldName(descriptor)); + // Avoid either our own type name or reserved names. Note that not all names + // are reserved - a field called to_string, write_to etc would still cause a problem. + // There are various ways of ending up with naming collisions, but we try to avoid obvious + // ones. + if (property_name == descriptor->containing_type()->name() + || property_name == "Types" + || property_name == "Descriptor") { + property_name += "_"; + } + return property_name; +} + +TProtoStringType GetOutputFile(const FileDescriptor* descriptor, + const TProtoStringType file_extension, + const bool generate_directories, + const TProtoStringType base_namespace, + TProtoStringType* error) { + TProtoStringType relative_filename = GetFileNameBase(descriptor) + file_extension; + if (!generate_directories) { + return relative_filename; + } + TProtoStringType ns = GetFileNamespace(descriptor); + TProtoStringType namespace_suffix = ns; + if (!base_namespace.empty()) { + // Check that the base_namespace is either equal to or a leading part of + // the file namespace. This isn't just a simple prefix; "Foo.B" shouldn't + // be regarded as a prefix of "Foo.Bar". The simplest option is to add "." + // to both. + TProtoStringType extended_ns = ns + "."; + if (extended_ns.find(base_namespace + ".") != 0) { + *error = "Namespace " + ns + " is not a prefix namespace of base namespace " + base_namespace; + return ""; // This will be ignored, because we've set an error. + } + namespace_suffix = ns.substr(base_namespace.length()); + if (namespace_suffix.find(".") == 0) { + namespace_suffix = namespace_suffix.substr(1); + } + } + + TProtoStringType namespace_dir = StringReplace(namespace_suffix, ".", "/", true); + if (!namespace_dir.empty()) { + namespace_dir += "/"; + } + return namespace_dir + relative_filename; +} + +// TODO: c&p from Java protoc plugin +// For encodings with fixed sizes, returns that size in bytes. Otherwise +// returns -1. +int GetFixedSize(FieldDescriptor::Type type) { + switch (type) { + case FieldDescriptor::TYPE_INT32 : return -1; + case FieldDescriptor::TYPE_INT64 : return -1; + case FieldDescriptor::TYPE_UINT32 : return -1; + case FieldDescriptor::TYPE_UINT64 : return -1; + case FieldDescriptor::TYPE_SINT32 : return -1; + case FieldDescriptor::TYPE_SINT64 : return -1; + case FieldDescriptor::TYPE_FIXED32 : return internal::WireFormatLite::kFixed32Size; + case FieldDescriptor::TYPE_FIXED64 : return internal::WireFormatLite::kFixed64Size; + case FieldDescriptor::TYPE_SFIXED32: return internal::WireFormatLite::kSFixed32Size; + case FieldDescriptor::TYPE_SFIXED64: return internal::WireFormatLite::kSFixed64Size; + case FieldDescriptor::TYPE_FLOAT : return internal::WireFormatLite::kFloatSize; + case FieldDescriptor::TYPE_DOUBLE : return internal::WireFormatLite::kDoubleSize; + + case FieldDescriptor::TYPE_BOOL : return internal::WireFormatLite::kBoolSize; + case FieldDescriptor::TYPE_ENUM : return -1; + + case FieldDescriptor::TYPE_STRING : return -1; + case FieldDescriptor::TYPE_BYTES : return -1; + case FieldDescriptor::TYPE_GROUP : return -1; + case FieldDescriptor::TYPE_MESSAGE : return -1; + + // No default because we want the compiler to complain if any new + // types are added. + } + GOOGLE_LOG(FATAL) << "Can't get here."; + return -1; +} + +static const char base64_chars[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +TProtoStringType StringToBase64(const TProtoStringType& input) { + TProtoStringType result; + size_t remaining = input.size(); + const unsigned char *src = (const unsigned char*) input.c_str(); + while (remaining > 2) { + result += base64_chars[src[0] >> 2]; + result += base64_chars[((src[0] & 0x3) << 4) | (src[1] >> 4)]; + result += base64_chars[((src[1] & 0xf) << 2) | (src[2] >> 6)]; + result += base64_chars[src[2] & 0x3f]; + remaining -= 3; + src += 3; + } + switch (remaining) { + case 2: + result += base64_chars[src[0] >> 2]; + result += base64_chars[((src[0] & 0x3) << 4) | (src[1] >> 4)]; + result += base64_chars[(src[1] & 0xf) << 2]; + result += '='; + src += 2; + break; + case 1: + result += base64_chars[src[0] >> 2]; + result += base64_chars[((src[0] & 0x3) << 4)]; + result += '='; + result += '='; + src += 1; + break; + } + return result; +} + +TProtoStringType FileDescriptorToBase64(const FileDescriptor* descriptor) { + TProtoStringType fdp_bytes; + FileDescriptorProto fdp; + descriptor->CopyTo(&fdp); + fdp.SerializeToString(&fdp_bytes); + return StringToBase64(fdp_bytes); +} + +FieldGeneratorBase* CreateFieldGenerator(const FieldDescriptor* descriptor, + int presenceIndex, + 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, presenceIndex, options); + } else { + return new RepeatedMessageFieldGenerator(descriptor, presenceIndex, options); + } + } else { + if (IsWrapperType(descriptor)) { + if (descriptor->real_containing_oneof()) { + return new WrapperOneofFieldGenerator(descriptor, presenceIndex, options); + } else { + return new WrapperFieldGenerator(descriptor, presenceIndex, options); + } + } else { + if (descriptor->real_containing_oneof()) { + return new MessageOneofFieldGenerator(descriptor, presenceIndex, options); + } else { + return new MessageFieldGenerator(descriptor, presenceIndex, options); + } + } + } + case FieldDescriptor::TYPE_ENUM: + if (descriptor->is_repeated()) { + return new RepeatedEnumFieldGenerator(descriptor, presenceIndex, options); + } else { + if (descriptor->real_containing_oneof()) { + return new EnumOneofFieldGenerator(descriptor, presenceIndex, options); + } else { + return new EnumFieldGenerator(descriptor, presenceIndex, options); + } + } + default: + if (descriptor->is_repeated()) { + return new RepeatedPrimitiveFieldGenerator(descriptor, presenceIndex, options); + } else { + if (descriptor->real_containing_oneof()) { + return new PrimitiveOneofFieldGenerator(descriptor, presenceIndex, options); + } else { + return new PrimitiveFieldGenerator(descriptor, presenceIndex, options); + } + } + } +} + +bool IsNullable(const FieldDescriptor* descriptor) { + if (descriptor->is_repeated()) { + return true; + } + + switch (descriptor->type()) { + case FieldDescriptor::TYPE_ENUM: + case FieldDescriptor::TYPE_DOUBLE: + case FieldDescriptor::TYPE_FLOAT: + case FieldDescriptor::TYPE_INT64: + case FieldDescriptor::TYPE_UINT64: + case FieldDescriptor::TYPE_INT32: + case FieldDescriptor::TYPE_FIXED64: + case FieldDescriptor::TYPE_FIXED32: + case FieldDescriptor::TYPE_BOOL: + case FieldDescriptor::TYPE_UINT32: + case FieldDescriptor::TYPE_SFIXED32: + case FieldDescriptor::TYPE_SFIXED64: + case FieldDescriptor::TYPE_SINT32: + case FieldDescriptor::TYPE_SINT64: + return false; + + case FieldDescriptor::TYPE_MESSAGE: + case FieldDescriptor::TYPE_GROUP: + case FieldDescriptor::TYPE_STRING: + case FieldDescriptor::TYPE_BYTES: + return true; + + default: + GOOGLE_LOG(FATAL) << "Unknown field type."; + return true; + } +} + +} // namespace csharp +} // namespace compiler +} // namespace protobuf +} // namespace google |
