| OLD | NEW |
| 1 // Protocol Buffers - Google's data interchange format | 1 // Protocol Buffers - Google's data interchange format |
| 2 // Copyright 2008 Google Inc. All rights reserved. | 2 // Copyright 2008 Google Inc. All rights reserved. |
| 3 // https://developers.google.com/protocol-buffers/ | 3 // https://developers.google.com/protocol-buffers/ |
| 4 // | 4 // |
| 5 // Redistribution and use in source and binary forms, with or without | 5 // Redistribution and use in source and binary forms, with or without |
| 6 // modification, are permitted provided that the following conditions are | 6 // modification, are permitted provided that the following conditions are |
| 7 // met: | 7 // met: |
| 8 // | 8 // |
| 9 // * Redistributions of source code must retain the above copyright | 9 // * Redistributions of source code must retain the above copyright |
| 10 // notice, this list of conditions and the following disclaimer. | 10 // notice, this list of conditions and the following disclaimer. |
| (...skipping 14 matching lines...) Expand all Loading... |
| 25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 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. | 29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 30 | 30 |
| 31 package com.google.protobuf.util; | 31 package com.google.protobuf.util; |
| 32 | 32 |
| 33 import static com.google.common.base.Preconditions.checkArgument; | 33 import static com.google.common.base.Preconditions.checkArgument; |
| 34 | 34 |
| 35 import com.google.common.base.CaseFormat; | |
| 36 import com.google.common.base.Joiner; | |
| 37 import com.google.common.base.Splitter; | |
| 38 import com.google.common.primitives.Ints; | 35 import com.google.common.primitives.Ints; |
| 39 import com.google.protobuf.Descriptors.Descriptor; | 36 import com.google.protobuf.Descriptors.Descriptor; |
| 40 import com.google.protobuf.Descriptors.FieldDescriptor; | 37 import com.google.protobuf.Descriptors.FieldDescriptor; |
| 41 import com.google.protobuf.FieldMask; | 38 import com.google.protobuf.FieldMask; |
| 42 import com.google.protobuf.Internal; | 39 import com.google.protobuf.Internal; |
| 43 import com.google.protobuf.Message; | 40 import com.google.protobuf.Message; |
| 44 | 41 |
| 45 import java.util.ArrayList; | |
| 46 import java.util.Arrays; | 42 import java.util.Arrays; |
| 47 import java.util.List; | |
| 48 | 43 |
| 49 /** | 44 /** |
| 50 * Utility helper functions to work with {@link com.google.protobuf.FieldMask}. | 45 * Utility helper functions to work with {@link com.google.protobuf.FieldMask}. |
| 51 */ | 46 */ |
| 52 public class FieldMaskUtil { | 47 public class FieldMaskUtil { |
| 53 private static final String FIELD_PATH_SEPARATOR = ","; | 48 private static final String FIELD_PATH_SEPARATOR = ","; |
| 54 private static final String FIELD_PATH_SEPARATOR_REGEX = ","; | 49 private static final String FIELD_PATH_SEPARATOR_REGEX = ","; |
| 55 private static final String FIELD_SEPARATOR_REGEX = "\\."; | 50 private static final String FIELD_SEPARATOR_REGEX = "\\."; |
| 56 | 51 |
| 57 private FieldMaskUtil() {} | 52 private FieldMaskUtil() {} |
| 58 | 53 |
| 59 /** | 54 /** |
| 60 * Converts a FieldMask to a string. | 55 * Converts a FieldMask to a string. |
| 61 */ | 56 */ |
| 62 public static String toString(FieldMask fieldMask) { | 57 public static String toString(FieldMask fieldMask) { |
| 63 // TODO(xiaofeng): Consider using com.google.common.base.Joiner here instead
. | 58 // TODO(xiaofeng): Consider using com.google.common.base.Joiner here instead
. |
| 64 StringBuilder result = new StringBuilder(); | 59 StringBuilder result = new StringBuilder(); |
| 65 boolean first = true; | 60 boolean first = true; |
| 66 for (String value : fieldMask.getPathsList()) { | 61 for (String value : fieldMask.getPathsList()) { |
| 67 if (value.isEmpty()) { | 62 if (value.isEmpty()) { |
| 68 // Ignore empty paths. | 63 // Ignore empty paths. |
| 69 continue; | 64 continue; |
| 70 } | 65 } |
| 71 if (first) { | 66 if (first) { |
| 72 first = false; | 67 first = false; |
| 73 } else { | 68 } else { |
| 74 result.append(FIELD_PATH_SEPARATOR); | 69 result.append(FIELD_PATH_SEPARATOR); |
| 75 } | 70 } |
| 76 result.append(value); | 71 result.append(value); |
| 77 } | 72 } |
| 78 return result.toString(); | 73 return result.toString(); |
| 79 } | 74 } |
| 80 | 75 |
| 81 /** | 76 /** |
| 82 * Parses from a string to a FieldMask. | 77 * Parses from a string to a FieldMask. |
| 83 */ | 78 */ |
| 84 public static FieldMask fromString(String value) { | 79 public static FieldMask fromString(String value) { |
| 85 // TODO(xiaofeng): Consider using com.google.common.base.Splitter here inste
ad. | 80 // TODO(xiaofeng): Consider using com.google.common.base.Splitter here inste
ad. |
| 86 return fromStringList(null, Arrays.asList(value.split(FIELD_PATH_SEPARATOR_R
EGEX))); | 81 return fromStringList( |
| 82 null, Arrays.asList(value.split(FIELD_PATH_SEPARATOR_REGEX))); |
| 87 } | 83 } |
| 88 | 84 |
| 89 /** | 85 /** |
| 90 * Parses from a string to a FieldMask and validates all field paths. | 86 * Parses from a string to a FieldMask and validates all field paths. |
| 91 * | 87 * |
| 92 * @throws IllegalArgumentException if any of the field path is invalid. | 88 * @throws IllegalArgumentException if any of the field path is invalid. |
| 93 */ | 89 */ |
| 94 public static FieldMask fromString(Class<? extends Message> type, String value
) { | 90 public static FieldMask fromString(Class<? extends Message> type, String value
) { |
| 95 // TODO(xiaofeng): Consider using com.google.common.base.Splitter here inste
ad. | 91 // TODO(xiaofeng): Consider using com.google.common.base.Splitter here inste
ad. |
| 96 return fromStringList(type, Arrays.asList(value.split(FIELD_PATH_SEPARATOR_R
EGEX))); | 92 return fromStringList( |
| 93 type, Arrays.asList(value.split(FIELD_PATH_SEPARATOR_REGEX))); |
| 97 } | 94 } |
| 98 | 95 |
| 99 /** | 96 /** |
| 100 * Constructs a FieldMask for a list of field paths in a certain type. | 97 * Constructs a FieldMask for a list of field paths in a certain type. |
| 101 * | 98 * |
| 102 * @throws IllegalArgumentException if any of the field path is not valid. | 99 * @throws IllegalArgumentException if any of the field path is not valid. |
| 103 */ | 100 */ |
| 104 // TODO(xiaofeng): Consider renaming fromStrings() | 101 // TODO(xiaofeng): Consider renaming fromStrings() |
| 105 public static FieldMask fromStringList(Class<? extends Message> type, Iterable
<String> paths) { | 102 public static FieldMask fromStringList( |
| 103 Class<? extends Message> type, Iterable<String> paths) { |
| 106 FieldMask.Builder builder = FieldMask.newBuilder(); | 104 FieldMask.Builder builder = FieldMask.newBuilder(); |
| 107 for (String path : paths) { | 105 for (String path : paths) { |
| 108 if (path.isEmpty()) { | 106 if (path.isEmpty()) { |
| 109 // Ignore empty field paths. | 107 // Ignore empty field paths. |
| 110 continue; | 108 continue; |
| 111 } | 109 } |
| 112 if (type != null && !isValid(type, path)) { | 110 if (type != null && !isValid(type, path)) { |
| 113 throw new IllegalArgumentException(path + " is not a valid path for " +
type); | 111 throw new IllegalArgumentException( |
| 112 path + " is not a valid path for " + type); |
| 114 } | 113 } |
| 115 builder.addPaths(path); | 114 builder.addPaths(path); |
| 116 } | 115 } |
| 117 return builder.build(); | 116 return builder.build(); |
| 118 } | 117 } |
| 119 | 118 |
| 120 /** | 119 /** |
| 121 * Constructs a FieldMask from the passed field numbers. | 120 * Constructs a FieldMask from the passed field numbers. |
| 122 * | 121 * |
| 123 * @throws IllegalArgumentException if any of the fields are invalid for the m
essage. | 122 * @throws IllegalArgumentException if any of the fields are invalid for the m
essage. |
| (...skipping 16 matching lines...) Expand all Loading... |
| 140 FieldDescriptor field = descriptor.findFieldByNumber(fieldNumber); | 139 FieldDescriptor field = descriptor.findFieldByNumber(fieldNumber); |
| 141 checkArgument( | 140 checkArgument( |
| 142 field != null, | 141 field != null, |
| 143 String.format("%s is not a valid field number for %s.", fieldNumber, t
ype)); | 142 String.format("%s is not a valid field number for %s.", fieldNumber, t
ype)); |
| 144 builder.addPaths(field.getName()); | 143 builder.addPaths(field.getName()); |
| 145 } | 144 } |
| 146 return builder.build(); | 145 return builder.build(); |
| 147 } | 146 } |
| 148 | 147 |
| 149 /** | 148 /** |
| 150 * Converts a field mask to a Proto3 JSON string, that is converting from snak
e case to camel | |
| 151 * case and joining all paths into one string with commas. | |
| 152 */ | |
| 153 public static String toJsonString(FieldMask fieldMask) { | |
| 154 List<String> paths = new ArrayList<String>(fieldMask.getPathsCount()); | |
| 155 for (String path : fieldMask.getPathsList()) { | |
| 156 if (path.isEmpty()) { | |
| 157 continue; | |
| 158 } | |
| 159 paths.add(CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, path)); | |
| 160 } | |
| 161 return Joiner.on(FIELD_PATH_SEPARATOR).join(paths); | |
| 162 } | |
| 163 | |
| 164 /** | |
| 165 * Converts a field mask from a Proto3 JSON string, that is splitting the path
s along commas and | |
| 166 * converting from camel case to snake case. | |
| 167 */ | |
| 168 public static FieldMask fromJsonString(String value) { | |
| 169 Iterable<String> paths = Splitter.on(FIELD_PATH_SEPARATOR).split(value); | |
| 170 FieldMask.Builder builder = FieldMask.newBuilder(); | |
| 171 for (String path : paths) { | |
| 172 if (path.isEmpty()) { | |
| 173 continue; | |
| 174 } | |
| 175 builder.addPaths(CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, pa
th)); | |
| 176 } | |
| 177 return builder.build(); | |
| 178 } | |
| 179 | |
| 180 /** | |
| 181 * Checks whether paths in a given fields mask are valid. | 149 * Checks whether paths in a given fields mask are valid. |
| 182 */ | 150 */ |
| 183 public static boolean isValid(Class<? extends Message> type, FieldMask fieldMa
sk) { | 151 public static boolean isValid(Class<? extends Message> type, FieldMask fieldMa
sk) { |
| 184 Descriptor descriptor = Internal.getDefaultInstance(type).getDescriptorForTy
pe(); | 152 Descriptor descriptor = |
| 185 | 153 Internal.getDefaultInstance(type).getDescriptorForType(); |
| 154 |
| 186 return isValid(descriptor, fieldMask); | 155 return isValid(descriptor, fieldMask); |
| 187 } | 156 } |
| 188 | 157 |
| 189 /** | 158 /** |
| 190 * Checks whether paths in a given fields mask are valid. | 159 * Checks whether paths in a given fields mask are valid. |
| 191 */ | 160 */ |
| 192 public static boolean isValid(Descriptor descriptor, FieldMask fieldMask) { | 161 public static boolean isValid(Descriptor descriptor, FieldMask fieldMask) { |
| 193 for (String path : fieldMask.getPathsList()) { | 162 for (String path : fieldMask.getPathsList()) { |
| 194 if (!isValid(descriptor, path)) { | 163 if (!isValid(descriptor, path)) { |
| 195 return false; | 164 return false; |
| 196 } | 165 } |
| 197 } | 166 } |
| 198 return true; | 167 return true; |
| 199 } | 168 } |
| 200 | 169 |
| 201 /** | 170 /** |
| 202 * Checks whether a given field path is valid. | 171 * Checks whether a given field path is valid. |
| 203 */ | 172 */ |
| 204 public static boolean isValid(Class<? extends Message> type, String path) { | 173 public static boolean isValid(Class<? extends Message> type, String path) { |
| 205 Descriptor descriptor = Internal.getDefaultInstance(type).getDescriptorForTy
pe(); | 174 Descriptor descriptor = |
| 206 | 175 Internal.getDefaultInstance(type).getDescriptorForType(); |
| 176 |
| 207 return isValid(descriptor, path); | 177 return isValid(descriptor, path); |
| 208 } | 178 } |
| 209 | 179 |
| 210 /** | 180 /** |
| 211 * Checks whether paths in a given fields mask are valid. | 181 * Checks whether paths in a given fields mask are valid. |
| 212 */ | 182 */ |
| 213 public static boolean isValid(Descriptor descriptor, String path) { | 183 public static boolean isValid(Descriptor descriptor, String path) { |
| 214 String[] parts = path.split(FIELD_SEPARATOR_REGEX); | 184 String[] parts = path.split(FIELD_SEPARATOR_REGEX); |
| 215 if (parts.length == 0) { | 185 if (parts.length == 0) { |
| 216 return false; | 186 return false; |
| 217 } | 187 } |
| 218 for (String name : parts) { | 188 for (String name : parts) { |
| 219 if (descriptor == null) { | 189 if (descriptor == null) { |
| 220 return false; | 190 return false; |
| 221 } | 191 } |
| 222 FieldDescriptor field = descriptor.findFieldByName(name); | 192 FieldDescriptor field = descriptor.findFieldByName(name); |
| 223 if (field == null) { | 193 if (field == null) { |
| 224 return false; | 194 return false; |
| 225 } | 195 } |
| 226 if (!field.isRepeated() && field.getJavaType() == FieldDescriptor.JavaType
.MESSAGE) { | 196 if (!field.isRepeated() |
| 197 && field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { |
| 227 descriptor = field.getMessageType(); | 198 descriptor = field.getMessageType(); |
| 228 } else { | 199 } else { |
| 229 descriptor = null; | 200 descriptor = null; |
| 230 } | 201 } |
| 231 } | 202 } |
| 232 return true; | 203 return true; |
| 233 } | 204 } |
| 234 | 205 |
| 235 /** | 206 /** |
| 236 * Converts a FieldMask to its canonical form. In the canonical form of a | 207 * Converts a FieldMask to its canonical form. In the canonical form of a |
| 237 * FieldMask, all field paths are sorted alphabetically and redundant field | 208 * FieldMask, all field paths are sorted alphabetically and redundant field |
| 238 * paths are moved. | 209 * paths are moved. |
| 239 */ | 210 */ |
| 240 public static FieldMask normalize(FieldMask mask) { | 211 public static FieldMask normalize(FieldMask mask) { |
| 241 return new FieldMaskTree(mask).toFieldMask(); | 212 return new FieldMaskTree(mask).toFieldMask(); |
| 242 } | 213 } |
| 243 | 214 |
| 244 /** | 215 /** |
| (...skipping 28 matching lines...) Expand all Loading... |
| 273 private boolean replaceRepeatedFields = false; | 244 private boolean replaceRepeatedFields = false; |
| 274 // TODO(b/28277137): change the default behavior to always replace primitive
fields after | 245 // TODO(b/28277137): change the default behavior to always replace primitive
fields after |
| 275 // fixing all failing TAP tests. | 246 // fixing all failing TAP tests. |
| 276 private boolean replacePrimitiveFields = false; | 247 private boolean replacePrimitiveFields = false; |
| 277 | 248 |
| 278 /** | 249 /** |
| 279 * Whether to replace message fields (i.e., discard existing content in | 250 * Whether to replace message fields (i.e., discard existing content in |
| 280 * destination message fields) when merging. | 251 * destination message fields) when merging. |
| 281 * Default behavior is to merge the source message field into the | 252 * Default behavior is to merge the source message field into the |
| 282 * destination message field. | 253 * destination message field. |
| 283 */ | 254 */ |
| 284 public boolean replaceMessageFields() { | 255 public boolean replaceMessageFields() { |
| 285 return replaceMessageFields; | 256 return replaceMessageFields; |
| 286 } | 257 } |
| 287 | 258 |
| 288 /** | 259 /** |
| 289 * Whether to replace repeated fields (i.e., discard existing content in | 260 * Whether to replace repeated fields (i.e., discard existing content in |
| 290 * destination repeated fields) when merging. | 261 * destination repeated fields) when merging. |
| 291 * Default behavior is to append elements from source repeated field to the | 262 * Default behavior is to append elements from source repeated field to the |
| 292 * destination repeated field. | 263 * destination repeated field. |
| 293 */ | 264 */ |
| (...skipping 27 matching lines...) Expand all Loading... |
| 321 | 292 |
| 322 public void setReplacePrimitiveFields(boolean value) { | 293 public void setReplacePrimitiveFields(boolean value) { |
| 323 replacePrimitiveFields = value; | 294 replacePrimitiveFields = value; |
| 324 } | 295 } |
| 325 } | 296 } |
| 326 | 297 |
| 327 /** | 298 /** |
| 328 * Merges fields specified by a FieldMask from one message to another with the | 299 * Merges fields specified by a FieldMask from one message to another with the |
| 329 * specified merge options. | 300 * specified merge options. |
| 330 */ | 301 */ |
| 331 public static void merge( | 302 public static void merge(FieldMask mask, Message source, |
| 332 FieldMask mask, Message source, Message.Builder destination, MergeOptions
options) { | 303 Message.Builder destination, MergeOptions options) { |
| 333 new FieldMaskTree(mask).merge(source, destination, options); | 304 new FieldMaskTree(mask).merge(source, destination, options); |
| 334 } | 305 } |
| 335 | 306 |
| 336 /** | 307 /** |
| 337 * Merges fields specified by a FieldMask from one message to another. | 308 * Merges fields specified by a FieldMask from one message to another. |
| 338 */ | 309 */ |
| 339 public static void merge(FieldMask mask, Message source, Message.Builder desti
nation) { | 310 public static void merge(FieldMask mask, Message source, |
| 311 Message.Builder destination) { |
| 340 merge(mask, source, destination, new MergeOptions()); | 312 merge(mask, source, destination, new MergeOptions()); |
| 341 } | 313 } |
| 342 } | 314 } |
| OLD | NEW |