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 |