1 // Protocol Buffers - Google's data interchange format 2 // Copyright 2008 Google Inc. All rights reserved. 3 // 4 // Use of this source code is governed by a BSD-style 5 // license that can be found in the LICENSE file or at 6 // https://developers.google.com/open-source/licenses/bsd 7 8 // Defines utilities for the FieldMask well known type. 9 10 #ifndef GOOGLE_PROTOBUF_UTIL_FIELD_MASK_UTIL_H__ 11 #define GOOGLE_PROTOBUF_UTIL_FIELD_MASK_UTIL_H__ 12 13 #include <cstdint> 14 #include <string> 15 #include <vector> 16 17 #include "google/protobuf/field_mask.pb.h" 18 #include "absl/log/absl_check.h" 19 #include "absl/strings/string_view.h" 20 #include "google/protobuf/descriptor.h" 21 22 // Must be included last. 23 #include "google/protobuf/port_def.inc" 24 25 namespace google { 26 namespace protobuf { 27 namespace util { 28 29 class PROTOBUF_EXPORT FieldMaskUtil { 30 typedef google::protobuf::FieldMask FieldMask; 31 32 public: 33 // Converts FieldMask to/from string, formatted by separating each path 34 // with a comma (e.g., "foo_bar,baz.quz"). 35 static std::string ToString(const FieldMask& mask); 36 static void FromString(absl::string_view str, FieldMask* out); 37 38 // Populates the FieldMask with the paths corresponding to the fields with the 39 // given numbers, after checking that all field numbers are valid. 40 template <typename T> FromFieldNumbers(const std::vector<int64_t> & field_numbers,FieldMask * out)41 static void FromFieldNumbers(const std::vector<int64_t>& field_numbers, 42 FieldMask* out) { 43 for (const auto field_number : field_numbers) { 44 const FieldDescriptor* field_desc = 45 T::descriptor()->FindFieldByNumber(field_number); 46 ABSL_CHECK(field_desc != nullptr) 47 << "Invalid field number for " << T::descriptor()->full_name() << ": " 48 << field_number; 49 AddPathToFieldMask<T>(field_desc->lowercase_name(), out); 50 } 51 } 52 53 // Converts FieldMask to/from string, formatted according to proto3 JSON 54 // spec for FieldMask (e.g., "fooBar,baz.quz"). If the field name is not 55 // style conforming (i.e., not snake_case when converted to string, or not 56 // camelCase when converted from string), the conversion will fail. 57 static bool ToJsonString(const FieldMask& mask, std::string* out); 58 static bool FromJsonString(absl::string_view str, FieldMask* out); 59 60 // Get the descriptors of the fields which the given path from the message 61 // descriptor traverses, if field_descriptors is not null. 62 // Return false if the path is not valid, and the content of field_descriptors 63 // is unspecified. 64 static bool GetFieldDescriptors( 65 const Descriptor* descriptor, absl::string_view path, 66 std::vector<const FieldDescriptor*>* field_descriptors); 67 68 // Checks whether the given path is valid for type T. 69 template <typename T> IsValidPath(absl::string_view path)70 static bool IsValidPath(absl::string_view path) { 71 return GetFieldDescriptors(T::descriptor(), path, nullptr); 72 } 73 74 // Checks whether the given FieldMask is valid for type T. 75 template <typename T> IsValidFieldMask(const FieldMask & mask)76 static bool IsValidFieldMask(const FieldMask& mask) { 77 for (int i = 0; i < mask.paths_size(); ++i) { 78 if (!GetFieldDescriptors(T::descriptor(), mask.paths(i), nullptr)) { 79 return false; 80 } 81 } 82 return true; 83 } 84 85 // Adds a path to FieldMask after checking whether the given path is valid. 86 // This method check-fails if the path is not a valid path for type T. 87 template <typename T> AddPathToFieldMask(absl::string_view path,FieldMask * mask)88 static void AddPathToFieldMask(absl::string_view path, FieldMask* mask) { 89 ABSL_CHECK(IsValidPath<T>(path)) << path; 90 mask->add_paths(std::string(path)); 91 } 92 93 // Creates a FieldMask with all fields of type T. This FieldMask only 94 // contains fields of T but not any sub-message fields. 95 template <typename T> GetFieldMaskForAllFields()96 static FieldMask GetFieldMaskForAllFields() { 97 FieldMask out; 98 GetFieldMaskForAllFields(T::descriptor(), &out); 99 return out; 100 } 101 template <typename T> 102 [[deprecated("Use *out = GetFieldMaskForAllFields() instead")]] static void GetFieldMaskForAllFields(FieldMask * out)103 GetFieldMaskForAllFields(FieldMask* out) { 104 GetFieldMaskForAllFields(T::descriptor(), out); 105 } 106 // This flavor takes the protobuf type descriptor as an argument. 107 // Useful when the type is not known at compile time. 108 static void GetFieldMaskForAllFields(const Descriptor* descriptor, 109 FieldMask* out); 110 111 // Converts a FieldMask to the canonical form. It will: 112 // 1. Remove paths that are covered by another path. For example, 113 // "foo.bar" is covered by "foo" and will be removed if "foo" 114 // is also in the FieldMask. 115 // 2. Sort all paths in alphabetical order. 116 static void ToCanonicalForm(const FieldMask& mask, FieldMask* out); 117 118 // Creates an union of two FieldMasks. 119 static void Union(const FieldMask& mask1, const FieldMask& mask2, 120 FieldMask* out); 121 122 // Creates an intersection of two FieldMasks. 123 static void Intersect(const FieldMask& mask1, const FieldMask& mask2, 124 FieldMask* out); 125 126 // Subtracts mask2 from mask1 base of type T. 127 template <typename T> Subtract(const FieldMask & mask1,const FieldMask & mask2,FieldMask * out)128 static void Subtract(const FieldMask& mask1, const FieldMask& mask2, 129 FieldMask* out) { 130 Subtract(T::descriptor(), mask1, mask2, out); 131 } 132 // This flavor takes the protobuf type descriptor as an argument. 133 // Useful when the type is not known at compile time. 134 static void Subtract(const Descriptor* descriptor, const FieldMask& mask1, 135 const FieldMask& mask2, FieldMask* out); 136 137 // Returns true if path is covered by the given FieldMask. Note that path 138 // "foo.bar" covers all paths like "foo.bar.baz", "foo.bar.quz.x", etc. 139 // Also note that parent paths are not covered by explicit child path, i.e. 140 // "foo.bar" does NOT cover "foo", even if "bar" is the only child. 141 static bool IsPathInFieldMask(absl::string_view path, const FieldMask& mask); 142 143 class MergeOptions; 144 // Merges fields specified in a FieldMask into another message. 145 static void MergeMessageTo(const Message& source, const FieldMask& mask, 146 const MergeOptions& options, Message* destination); 147 148 class TrimOptions; 149 // Removes from 'message' any field that is not represented in the given 150 // FieldMask. If the FieldMask is empty, does nothing. 151 // Returns true if the message is modified. 152 static bool TrimMessage(const FieldMask& mask, Message* message); 153 154 // Removes from 'message' any field that is not represented in the given 155 // FieldMask with customized TrimOptions. 156 // If the FieldMask is empty, does nothing. 157 // Returns true if the message is modified. 158 static bool TrimMessage(const FieldMask& mask, Message* message, 159 const TrimOptions& options); 160 161 private: 162 friend class SnakeCaseCamelCaseTest; 163 // Converts a field name from snake_case to camelCase: 164 // 1. Every character after "_" will be converted to uppercase. 165 // 2. All "_"s are removed. 166 // The conversion will fail if: 167 // 1. The field name contains uppercase letters. 168 // 2. Any character after a "_" is not a lowercase letter. 169 // If the conversion succeeds, it's guaranteed that the resulted 170 // camelCase name will yield the original snake_case name when 171 // converted using CamelCaseToSnakeCase(). 172 // 173 // Note that the input can contain characters not allowed in C identifiers. 174 // For example, "foo_bar,baz_quz" will be converted to "fooBar,bazQuz" 175 // successfully. 176 static bool SnakeCaseToCamelCase(absl::string_view input, 177 std::string* output); 178 // Converts a field name from camelCase to snake_case: 179 // 1. Every uppercase letter is converted to lowercase with an additional 180 // preceding "_". 181 // The conversion will fail if: 182 // 1. The field name contains "_"s. 183 // If the conversion succeeds, it's guaranteed that the resulted 184 // snake_case name will yield the original camelCase name when 185 // converted using SnakeCaseToCamelCase(). 186 // 187 // Note that the input can contain characters not allowed in C identifiers. 188 // For example, "fooBar,bazQuz" will be converted to "foo_bar,baz_quz" 189 // successfully. 190 static bool CamelCaseToSnakeCase(absl::string_view input, 191 std::string* output); 192 }; 193 194 class PROTOBUF_EXPORT FieldMaskUtil::MergeOptions { 195 public: MergeOptions()196 MergeOptions() 197 : replace_message_fields_(false), replace_repeated_fields_(false) {} 198 // When merging message fields, the default behavior is to merge the 199 // content of two message fields together. If you instead want to use 200 // the field from the source message to replace the corresponding field 201 // in the destination message, set this flag to true. When this flag is set, 202 // specified submessage fields that are missing in source will be cleared in 203 // destination. set_replace_message_fields(bool value)204 void set_replace_message_fields(bool value) { 205 replace_message_fields_ = value; 206 } replace_message_fields()207 bool replace_message_fields() const { return replace_message_fields_; } 208 // The default merging behavior will append entries from the source 209 // repeated field to the destination repeated field. If you only want 210 // to keep the entries from the source repeated field, set this flag 211 // to true. set_replace_repeated_fields(bool value)212 void set_replace_repeated_fields(bool value) { 213 replace_repeated_fields_ = value; 214 } replace_repeated_fields()215 bool replace_repeated_fields() const { return replace_repeated_fields_; } 216 217 private: 218 bool replace_message_fields_; 219 bool replace_repeated_fields_; 220 }; 221 222 class PROTOBUF_EXPORT FieldMaskUtil::TrimOptions { 223 public: TrimOptions()224 TrimOptions() : keep_required_fields_(false) {} 225 // When trimming message fields, the default behavior is to trim required 226 // fields of the present message if they are not specified in the field mask. 227 // If you instead want to keep required fields of the present message even 228 // when they are not specified in the field mask, set this flag to true. set_keep_required_fields(bool value)229 void set_keep_required_fields(bool value) { keep_required_fields_ = value; } keep_required_fields()230 bool keep_required_fields() const { return keep_required_fields_; } 231 232 private: 233 bool keep_required_fields_; 234 }; 235 236 } // namespace util 237 } // namespace protobuf 238 } // namespace google 239 240 #include "google/protobuf/port_undef.inc" 241 242 #endif // GOOGLE_PROTOBUF_UTIL_FIELD_MASK_UTIL_H__ 243