テキストの各行をキーと値に分離し、複数テキストファイルを読み込み、キーを突き合わせ照合し、その結果を表示するGUIユーテリティです。
リビジョン | 78e26970aac374fd5d053d32b57a87879b166a38 (tree) |
---|---|
日時 | 2011-10-14 02:11:10 |
作者 | seraphy <seraphy@192....> |
コミッター | seraphy |
ProtocolBuffersによるデータの出力
@@ -6,6 +6,10 @@ libs.CopyLibs.classpath=\ | ||
6 | 6 | ${base}/CopyLibs/org-netbeans-modules-java-j2seproject-copylibstask.jar |
7 | 7 | libs.JWSAntTasks.classpath=\ |
8 | 8 | ${base}/JWSAntTasks/org-netbeans-modules-javawebstart-anttasks.jar |
9 | +libs.protobuf-2.4.1.classpath=\ | |
10 | + ${base}/protobuf-2.4.1/protobuf-java-2.4.1.jar | |
11 | +libs.protobuf-2.4.1.src=\ | |
12 | + ${base}/protobuf-2.4.1/java/ | |
9 | 13 | libs.swing-app-framework.classpath=\ |
10 | 14 | ${base}/swing-app-framework/appframework-1.0.3.jar:\ |
11 | 15 | ${base}/swing-app-framework/swing-worker-1.1.jar |
@@ -0,0 +1,764 @@ | ||
1 | +// Protocol Buffers - Google's data interchange format | |
2 | +// Copyright 2008 Google Inc. All rights reserved. | |
3 | +// http://code.google.com/p/protobuf/ | |
4 | +// | |
5 | +// Redistribution and use in source and binary forms, with or without | |
6 | +// modification, are permitted provided that the following conditions are | |
7 | +// met: | |
8 | +// | |
9 | +// * Redistributions of source code must retain the above copyright | |
10 | +// notice, this list of conditions and the following disclaimer. | |
11 | +// * Redistributions in binary form must reproduce the above | |
12 | +// copyright notice, this list of conditions and the following disclaimer | |
13 | +// in the documentation and/or other materials provided with the | |
14 | +// distribution. | |
15 | +// * Neither the name of Google Inc. nor the names of its | |
16 | +// contributors may be used to endorse or promote products derived from | |
17 | +// this software without specific prior written permission. | |
18 | +// | |
19 | +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
20 | +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
21 | +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
22 | +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
23 | +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
24 | +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
25 | +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
26 | +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
27 | +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
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. | |
30 | + | |
31 | +package com.google.protobuf; | |
32 | + | |
33 | +import com.google.protobuf.Descriptors.Descriptor; | |
34 | +import com.google.protobuf.Descriptors.FieldDescriptor; | |
35 | +import com.google.protobuf.Internal.EnumLite; | |
36 | + | |
37 | +import java.io.IOException; | |
38 | +import java.io.InputStream; | |
39 | +import java.util.ArrayList; | |
40 | +import java.util.List; | |
41 | +import java.util.Map; | |
42 | + | |
43 | +/** | |
44 | + * A partial implementation of the {@link Message} interface which implements | |
45 | + * as many methods of that interface as possible in terms of other methods. | |
46 | + * | |
47 | + * @author kenton@google.com Kenton Varda | |
48 | + */ | |
49 | +public abstract class AbstractMessage extends AbstractMessageLite | |
50 | + implements Message { | |
51 | + @SuppressWarnings("unchecked") | |
52 | + public boolean isInitialized() { | |
53 | + // Check that all required fields are present. | |
54 | + for (final FieldDescriptor field : getDescriptorForType().getFields()) { | |
55 | + if (field.isRequired()) { | |
56 | + if (!hasField(field)) { | |
57 | + return false; | |
58 | + } | |
59 | + } | |
60 | + } | |
61 | + | |
62 | + // Check that embedded messages are initialized. | |
63 | + for (final Map.Entry<FieldDescriptor, Object> entry : | |
64 | + getAllFields().entrySet()) { | |
65 | + final FieldDescriptor field = entry.getKey(); | |
66 | + if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { | |
67 | + if (field.isRepeated()) { | |
68 | + for (final Message element : (List<Message>) entry.getValue()) { | |
69 | + if (!element.isInitialized()) { | |
70 | + return false; | |
71 | + } | |
72 | + } | |
73 | + } else { | |
74 | + if (!((Message) entry.getValue()).isInitialized()) { | |
75 | + return false; | |
76 | + } | |
77 | + } | |
78 | + } | |
79 | + } | |
80 | + | |
81 | + return true; | |
82 | + } | |
83 | + | |
84 | + @Override | |
85 | + public final String toString() { | |
86 | + return TextFormat.printToString(this); | |
87 | + } | |
88 | + | |
89 | + public void writeTo(final CodedOutputStream output) throws IOException { | |
90 | + final boolean isMessageSet = | |
91 | + getDescriptorForType().getOptions().getMessageSetWireFormat(); | |
92 | + | |
93 | + for (final Map.Entry<FieldDescriptor, Object> entry : | |
94 | + getAllFields().entrySet()) { | |
95 | + final FieldDescriptor field = entry.getKey(); | |
96 | + final Object value = entry.getValue(); | |
97 | + if (isMessageSet && field.isExtension() && | |
98 | + field.getType() == FieldDescriptor.Type.MESSAGE && | |
99 | + !field.isRepeated()) { | |
100 | + output.writeMessageSetExtension(field.getNumber(), (Message) value); | |
101 | + } else { | |
102 | + FieldSet.writeField(field, value, output); | |
103 | + } | |
104 | + } | |
105 | + | |
106 | + final UnknownFieldSet unknownFields = getUnknownFields(); | |
107 | + if (isMessageSet) { | |
108 | + unknownFields.writeAsMessageSetTo(output); | |
109 | + } else { | |
110 | + unknownFields.writeTo(output); | |
111 | + } | |
112 | + } | |
113 | + | |
114 | + private int memoizedSize = -1; | |
115 | + | |
116 | + public int getSerializedSize() { | |
117 | + int size = memoizedSize; | |
118 | + if (size != -1) { | |
119 | + return size; | |
120 | + } | |
121 | + | |
122 | + size = 0; | |
123 | + final boolean isMessageSet = | |
124 | + getDescriptorForType().getOptions().getMessageSetWireFormat(); | |
125 | + | |
126 | + for (final Map.Entry<FieldDescriptor, Object> entry : | |
127 | + getAllFields().entrySet()) { | |
128 | + final FieldDescriptor field = entry.getKey(); | |
129 | + final Object value = entry.getValue(); | |
130 | + if (isMessageSet && field.isExtension() && | |
131 | + field.getType() == FieldDescriptor.Type.MESSAGE && | |
132 | + !field.isRepeated()) { | |
133 | + size += CodedOutputStream.computeMessageSetExtensionSize( | |
134 | + field.getNumber(), (Message) value); | |
135 | + } else { | |
136 | + size += FieldSet.computeFieldSize(field, value); | |
137 | + } | |
138 | + } | |
139 | + | |
140 | + final UnknownFieldSet unknownFields = getUnknownFields(); | |
141 | + if (isMessageSet) { | |
142 | + size += unknownFields.getSerializedSizeAsMessageSet(); | |
143 | + } else { | |
144 | + size += unknownFields.getSerializedSize(); | |
145 | + } | |
146 | + | |
147 | + memoizedSize = size; | |
148 | + return size; | |
149 | + } | |
150 | + | |
151 | + @Override | |
152 | + public boolean equals(final Object other) { | |
153 | + if (other == this) { | |
154 | + return true; | |
155 | + } | |
156 | + if (!(other instanceof Message)) { | |
157 | + return false; | |
158 | + } | |
159 | + final Message otherMessage = (Message) other; | |
160 | + if (getDescriptorForType() != otherMessage.getDescriptorForType()) { | |
161 | + return false; | |
162 | + } | |
163 | + return getAllFields().equals(otherMessage.getAllFields()) && | |
164 | + getUnknownFields().equals(otherMessage.getUnknownFields()); | |
165 | + } | |
166 | + | |
167 | + @Override | |
168 | + public int hashCode() { | |
169 | + int hash = 41; | |
170 | + hash = (19 * hash) + getDescriptorForType().hashCode(); | |
171 | + hash = hashFields(hash, getAllFields()); | |
172 | + hash = (29 * hash) + getUnknownFields().hashCode(); | |
173 | + return hash; | |
174 | + } | |
175 | + | |
176 | + /** Get a hash code for given fields and values, using the given seed. */ | |
177 | + @SuppressWarnings("unchecked") | |
178 | + protected int hashFields(int hash, Map<FieldDescriptor, Object> map) { | |
179 | + for (Map.Entry<FieldDescriptor, Object> entry : map.entrySet()) { | |
180 | + FieldDescriptor field = entry.getKey(); | |
181 | + Object value = entry.getValue(); | |
182 | + hash = (37 * hash) + field.getNumber(); | |
183 | + if (field.getType() != FieldDescriptor.Type.ENUM){ | |
184 | + hash = (53 * hash) + value.hashCode(); | |
185 | + } else if (field.isRepeated()) { | |
186 | + List<? extends EnumLite> list = (List<? extends EnumLite>) value; | |
187 | + hash = (53 * hash) + hashEnumList(list); | |
188 | + } else { | |
189 | + hash = (53 * hash) + hashEnum((EnumLite) value); | |
190 | + } | |
191 | + } | |
192 | + return hash; | |
193 | + } | |
194 | + | |
195 | + /** | |
196 | + * Helper method for implementing {@link Message#hashCode()}. | |
197 | + * @see Boolean#hashCode() | |
198 | + */ | |
199 | + protected static int hashLong(long n) { | |
200 | + return (int) (n ^ (n >>> 32)); | |
201 | + } | |
202 | + | |
203 | + /** | |
204 | + * Helper method for implementing {@link Message#hashCode()}. | |
205 | + * @see Boolean#hashCode() | |
206 | + */ | |
207 | + protected static int hashBoolean(boolean b) { | |
208 | + return b ? 1231 : 1237; | |
209 | + } | |
210 | + | |
211 | + /** | |
212 | + * Helper method for implementing {@link Message#hashCode()}. | |
213 | + * <p> | |
214 | + * This is needed because {@link java.lang.Enum#hashCode()} is final, but we | |
215 | + * need to use the field number as the hash code to ensure compatibility | |
216 | + * between statically and dynamically generated enum objects. | |
217 | + */ | |
218 | + protected static int hashEnum(EnumLite e) { | |
219 | + return e.getNumber(); | |
220 | + } | |
221 | + | |
222 | + /** Helper method for implementing {@link Message#hashCode()}. */ | |
223 | + protected static int hashEnumList(List<? extends EnumLite> list) { | |
224 | + int hash = 1; | |
225 | + for (EnumLite e : list) { | |
226 | + hash = 31 * hash + hashEnum(e); | |
227 | + } | |
228 | + return hash; | |
229 | + } | |
230 | + | |
231 | + // ================================================================= | |
232 | + | |
233 | + /** | |
234 | + * A partial implementation of the {@link Message.Builder} interface which | |
235 | + * implements as many methods of that interface as possible in terms of | |
236 | + * other methods. | |
237 | + */ | |
238 | + @SuppressWarnings("unchecked") | |
239 | + public static abstract class Builder<BuilderType extends Builder> | |
240 | + extends AbstractMessageLite.Builder<BuilderType> | |
241 | + implements Message.Builder { | |
242 | + // The compiler produces an error if this is not declared explicitly. | |
243 | + @Override | |
244 | + public abstract BuilderType clone(); | |
245 | + | |
246 | + public BuilderType clear() { | |
247 | + for (final Map.Entry<FieldDescriptor, Object> entry : | |
248 | + getAllFields().entrySet()) { | |
249 | + clearField(entry.getKey()); | |
250 | + } | |
251 | + return (BuilderType) this; | |
252 | + } | |
253 | + | |
254 | + public BuilderType mergeFrom(final Message other) { | |
255 | + if (other.getDescriptorForType() != getDescriptorForType()) { | |
256 | + throw new IllegalArgumentException( | |
257 | + "mergeFrom(Message) can only merge messages of the same type."); | |
258 | + } | |
259 | + | |
260 | + // Note: We don't attempt to verify that other's fields have valid | |
261 | + // types. Doing so would be a losing battle. We'd have to verify | |
262 | + // all sub-messages as well, and we'd have to make copies of all of | |
263 | + // them to insure that they don't change after verification (since | |
264 | + // the Message interface itself cannot enforce immutability of | |
265 | + // implementations). | |
266 | + // TODO(kenton): Provide a function somewhere called makeDeepCopy() | |
267 | + // which allows people to make secure deep copies of messages. | |
268 | + | |
269 | + for (final Map.Entry<FieldDescriptor, Object> entry : | |
270 | + other.getAllFields().entrySet()) { | |
271 | + final FieldDescriptor field = entry.getKey(); | |
272 | + if (field.isRepeated()) { | |
273 | + for (final Object element : (List)entry.getValue()) { | |
274 | + addRepeatedField(field, element); | |
275 | + } | |
276 | + } else if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { | |
277 | + final Message existingValue = (Message)getField(field); | |
278 | + if (existingValue == existingValue.getDefaultInstanceForType()) { | |
279 | + setField(field, entry.getValue()); | |
280 | + } else { | |
281 | + setField(field, | |
282 | + existingValue.newBuilderForType() | |
283 | + .mergeFrom(existingValue) | |
284 | + .mergeFrom((Message)entry.getValue()) | |
285 | + .build()); | |
286 | + } | |
287 | + } else { | |
288 | + setField(field, entry.getValue()); | |
289 | + } | |
290 | + } | |
291 | + | |
292 | + mergeUnknownFields(other.getUnknownFields()); | |
293 | + | |
294 | + return (BuilderType) this; | |
295 | + } | |
296 | + | |
297 | + @Override | |
298 | + public BuilderType mergeFrom(final CodedInputStream input) | |
299 | + throws IOException { | |
300 | + return mergeFrom(input, ExtensionRegistry.getEmptyRegistry()); | |
301 | + } | |
302 | + | |
303 | + @Override | |
304 | + public BuilderType mergeFrom( | |
305 | + final CodedInputStream input, | |
306 | + final ExtensionRegistryLite extensionRegistry) | |
307 | + throws IOException { | |
308 | + final UnknownFieldSet.Builder unknownFields = | |
309 | + UnknownFieldSet.newBuilder(getUnknownFields()); | |
310 | + while (true) { | |
311 | + final int tag = input.readTag(); | |
312 | + if (tag == 0) { | |
313 | + break; | |
314 | + } | |
315 | + | |
316 | + if (!mergeFieldFrom(input, unknownFields, extensionRegistry, | |
317 | + this, tag)) { | |
318 | + // end group tag | |
319 | + break; | |
320 | + } | |
321 | + } | |
322 | + setUnknownFields(unknownFields.build()); | |
323 | + return (BuilderType) this; | |
324 | + } | |
325 | + | |
326 | + /** | |
327 | + * Like {@link #mergeFrom(CodedInputStream, UnknownFieldSet.Builder, | |
328 | + * ExtensionRegistryLite, Message.Builder)}, but parses a single field. | |
329 | + * Package-private because it is used by GeneratedMessage.ExtendableMessage. | |
330 | + * @param tag The tag, which should have already been read. | |
331 | + * @return {@code true} unless the tag is an end-group tag. | |
332 | + */ | |
333 | + static boolean mergeFieldFrom( | |
334 | + final CodedInputStream input, | |
335 | + final UnknownFieldSet.Builder unknownFields, | |
336 | + final ExtensionRegistryLite extensionRegistry, | |
337 | + final Message.Builder builder, | |
338 | + final int tag) throws IOException { | |
339 | + final Descriptor type = builder.getDescriptorForType(); | |
340 | + | |
341 | + if (type.getOptions().getMessageSetWireFormat() && | |
342 | + tag == WireFormat.MESSAGE_SET_ITEM_TAG) { | |
343 | + mergeMessageSetExtensionFromCodedStream( | |
344 | + input, unknownFields, extensionRegistry, builder); | |
345 | + return true; | |
346 | + } | |
347 | + | |
348 | + final int wireType = WireFormat.getTagWireType(tag); | |
349 | + final int fieldNumber = WireFormat.getTagFieldNumber(tag); | |
350 | + | |
351 | + final FieldDescriptor field; | |
352 | + Message defaultInstance = null; | |
353 | + | |
354 | + if (type.isExtensionNumber(fieldNumber)) { | |
355 | + // extensionRegistry may be either ExtensionRegistry or | |
356 | + // ExtensionRegistryLite. Since the type we are parsing is a full | |
357 | + // message, only a full ExtensionRegistry could possibly contain | |
358 | + // extensions of it. Otherwise we will treat the registry as if it | |
359 | + // were empty. | |
360 | + if (extensionRegistry instanceof ExtensionRegistry) { | |
361 | + final ExtensionRegistry.ExtensionInfo extension = | |
362 | + ((ExtensionRegistry) extensionRegistry) | |
363 | + .findExtensionByNumber(type, fieldNumber); | |
364 | + if (extension == null) { | |
365 | + field = null; | |
366 | + } else { | |
367 | + field = extension.descriptor; | |
368 | + defaultInstance = extension.defaultInstance; | |
369 | + if (defaultInstance == null && | |
370 | + field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { | |
371 | + throw new IllegalStateException( | |
372 | + "Message-typed extension lacked default instance: " + | |
373 | + field.getFullName()); | |
374 | + } | |
375 | + } | |
376 | + } else { | |
377 | + field = null; | |
378 | + } | |
379 | + } else { | |
380 | + field = type.findFieldByNumber(fieldNumber); | |
381 | + } | |
382 | + | |
383 | + boolean unknown = false; | |
384 | + boolean packed = false; | |
385 | + if (field == null) { | |
386 | + unknown = true; // Unknown field. | |
387 | + } else if (wireType == FieldSet.getWireFormatForFieldType( | |
388 | + field.getLiteType(), | |
389 | + false /* isPacked */)) { | |
390 | + packed = false; | |
391 | + } else if (field.isPackable() && | |
392 | + wireType == FieldSet.getWireFormatForFieldType( | |
393 | + field.getLiteType(), | |
394 | + true /* isPacked */)) { | |
395 | + packed = true; | |
396 | + } else { | |
397 | + unknown = true; // Unknown wire type. | |
398 | + } | |
399 | + | |
400 | + if (unknown) { // Unknown field or wrong wire type. Skip. | |
401 | + return unknownFields.mergeFieldFrom(tag, input); | |
402 | + } | |
403 | + | |
404 | + if (packed) { | |
405 | + final int length = input.readRawVarint32(); | |
406 | + final int limit = input.pushLimit(length); | |
407 | + if (field.getLiteType() == WireFormat.FieldType.ENUM) { | |
408 | + while (input.getBytesUntilLimit() > 0) { | |
409 | + final int rawValue = input.readEnum(); | |
410 | + final Object value = field.getEnumType().findValueByNumber(rawValue); | |
411 | + if (value == null) { | |
412 | + // If the number isn't recognized as a valid value for this | |
413 | + // enum, drop it (don't even add it to unknownFields). | |
414 | + return true; | |
415 | + } | |
416 | + builder.addRepeatedField(field, value); | |
417 | + } | |
418 | + } else { | |
419 | + while (input.getBytesUntilLimit() > 0) { | |
420 | + final Object value = | |
421 | + FieldSet.readPrimitiveField(input, field.getLiteType()); | |
422 | + builder.addRepeatedField(field, value); | |
423 | + } | |
424 | + } | |
425 | + input.popLimit(limit); | |
426 | + } else { | |
427 | + final Object value; | |
428 | + switch (field.getType()) { | |
429 | + case GROUP: { | |
430 | + final Message.Builder subBuilder; | |
431 | + if (defaultInstance != null) { | |
432 | + subBuilder = defaultInstance.newBuilderForType(); | |
433 | + } else { | |
434 | + subBuilder = builder.newBuilderForField(field); | |
435 | + } | |
436 | + if (!field.isRepeated()) { | |
437 | + subBuilder.mergeFrom((Message) builder.getField(field)); | |
438 | + } | |
439 | + input.readGroup(field.getNumber(), subBuilder, extensionRegistry); | |
440 | + value = subBuilder.build(); | |
441 | + break; | |
442 | + } | |
443 | + case MESSAGE: { | |
444 | + final Message.Builder subBuilder; | |
445 | + if (defaultInstance != null) { | |
446 | + subBuilder = defaultInstance.newBuilderForType(); | |
447 | + } else { | |
448 | + subBuilder = builder.newBuilderForField(field); | |
449 | + } | |
450 | + if (!field.isRepeated()) { | |
451 | + subBuilder.mergeFrom((Message) builder.getField(field)); | |
452 | + } | |
453 | + input.readMessage(subBuilder, extensionRegistry); | |
454 | + value = subBuilder.build(); | |
455 | + break; | |
456 | + } | |
457 | + case ENUM: | |
458 | + final int rawValue = input.readEnum(); | |
459 | + value = field.getEnumType().findValueByNumber(rawValue); | |
460 | + // If the number isn't recognized as a valid value for this enum, | |
461 | + // drop it. | |
462 | + if (value == null) { | |
463 | + unknownFields.mergeVarintField(fieldNumber, rawValue); | |
464 | + return true; | |
465 | + } | |
466 | + break; | |
467 | + default: | |
468 | + value = FieldSet.readPrimitiveField(input, field.getLiteType()); | |
469 | + break; | |
470 | + } | |
471 | + | |
472 | + if (field.isRepeated()) { | |
473 | + builder.addRepeatedField(field, value); | |
474 | + } else { | |
475 | + builder.setField(field, value); | |
476 | + } | |
477 | + } | |
478 | + | |
479 | + return true; | |
480 | + } | |
481 | + | |
482 | + /** Called by {@code #mergeFieldFrom()} to parse a MessageSet extension. */ | |
483 | + private static void mergeMessageSetExtensionFromCodedStream( | |
484 | + final CodedInputStream input, | |
485 | + final UnknownFieldSet.Builder unknownFields, | |
486 | + final ExtensionRegistryLite extensionRegistry, | |
487 | + final Message.Builder builder) throws IOException { | |
488 | + final Descriptor type = builder.getDescriptorForType(); | |
489 | + | |
490 | + // The wire format for MessageSet is: | |
491 | + // message MessageSet { | |
492 | + // repeated group Item = 1 { | |
493 | + // required int32 typeId = 2; | |
494 | + // required bytes message = 3; | |
495 | + // } | |
496 | + // } | |
497 | + // "typeId" is the extension's field number. The extension can only be | |
498 | + // a message type, where "message" contains the encoded bytes of that | |
499 | + // message. | |
500 | + // | |
501 | + // In practice, we will probably never see a MessageSet item in which | |
502 | + // the message appears before the type ID, or where either field does not | |
503 | + // appear exactly once. However, in theory such cases are valid, so we | |
504 | + // should be prepared to accept them. | |
505 | + | |
506 | + int typeId = 0; | |
507 | + ByteString rawBytes = null; // If we encounter "message" before "typeId" | |
508 | + Message.Builder subBuilder = null; | |
509 | + FieldDescriptor field = null; | |
510 | + | |
511 | + while (true) { | |
512 | + final int tag = input.readTag(); | |
513 | + if (tag == 0) { | |
514 | + break; | |
515 | + } | |
516 | + | |
517 | + if (tag == WireFormat.MESSAGE_SET_TYPE_ID_TAG) { | |
518 | + typeId = input.readUInt32(); | |
519 | + // Zero is not a valid type ID. | |
520 | + if (typeId != 0) { | |
521 | + final ExtensionRegistry.ExtensionInfo extension; | |
522 | + | |
523 | + // extensionRegistry may be either ExtensionRegistry or | |
524 | + // ExtensionRegistryLite. Since the type we are parsing is a full | |
525 | + // message, only a full ExtensionRegistry could possibly contain | |
526 | + // extensions of it. Otherwise we will treat the registry as if it | |
527 | + // were empty. | |
528 | + if (extensionRegistry instanceof ExtensionRegistry) { | |
529 | + extension = ((ExtensionRegistry) extensionRegistry) | |
530 | + .findExtensionByNumber(type, typeId); | |
531 | + } else { | |
532 | + extension = null; | |
533 | + } | |
534 | + | |
535 | + if (extension != null) { | |
536 | + field = extension.descriptor; | |
537 | + subBuilder = extension.defaultInstance.newBuilderForType(); | |
538 | + final Message originalMessage = (Message)builder.getField(field); | |
539 | + if (originalMessage != null) { | |
540 | + subBuilder.mergeFrom(originalMessage); | |
541 | + } | |
542 | + if (rawBytes != null) { | |
543 | + // We already encountered the message. Parse it now. | |
544 | + subBuilder.mergeFrom( | |
545 | + CodedInputStream.newInstance(rawBytes.newInput())); | |
546 | + rawBytes = null; | |
547 | + } | |
548 | + } else { | |
549 | + // Unknown extension number. If we already saw data, put it | |
550 | + // in rawBytes. | |
551 | + if (rawBytes != null) { | |
552 | + unknownFields.mergeField(typeId, | |
553 | + UnknownFieldSet.Field.newBuilder() | |
554 | + .addLengthDelimited(rawBytes) | |
555 | + .build()); | |
556 | + rawBytes = null; | |
557 | + } | |
558 | + } | |
559 | + } | |
560 | + } else if (tag == WireFormat.MESSAGE_SET_MESSAGE_TAG) { | |
561 | + if (typeId == 0) { | |
562 | + // We haven't seen a type ID yet, so we have to store the raw bytes | |
563 | + // for now. | |
564 | + rawBytes = input.readBytes(); | |
565 | + } else if (subBuilder == null) { | |
566 | + // We don't know how to parse this. Ignore it. | |
567 | + unknownFields.mergeField(typeId, | |
568 | + UnknownFieldSet.Field.newBuilder() | |
569 | + .addLengthDelimited(input.readBytes()) | |
570 | + .build()); | |
571 | + } else { | |
572 | + // We already know the type, so we can parse directly from the input | |
573 | + // with no copying. Hooray! | |
574 | + input.readMessage(subBuilder, extensionRegistry); | |
575 | + } | |
576 | + } else { | |
577 | + // Unknown tag. Skip it. | |
578 | + if (!input.skipField(tag)) { | |
579 | + break; // end of group | |
580 | + } | |
581 | + } | |
582 | + } | |
583 | + | |
584 | + input.checkLastTagWas(WireFormat.MESSAGE_SET_ITEM_END_TAG); | |
585 | + | |
586 | + if (subBuilder != null) { | |
587 | + builder.setField(field, subBuilder.build()); | |
588 | + } | |
589 | + } | |
590 | + | |
591 | + public BuilderType mergeUnknownFields(final UnknownFieldSet unknownFields) { | |
592 | + setUnknownFields( | |
593 | + UnknownFieldSet.newBuilder(getUnknownFields()) | |
594 | + .mergeFrom(unknownFields) | |
595 | + .build()); | |
596 | + return (BuilderType) this; | |
597 | + } | |
598 | + | |
599 | + /** | |
600 | + * Construct an UninitializedMessageException reporting missing fields in | |
601 | + * the given message. | |
602 | + */ | |
603 | + protected static UninitializedMessageException | |
604 | + newUninitializedMessageException(Message message) { | |
605 | + return new UninitializedMessageException(findMissingFields(message)); | |
606 | + } | |
607 | + | |
608 | + /** | |
609 | + * Populates {@code this.missingFields} with the full "path" of each | |
610 | + * missing required field in the given message. | |
611 | + */ | |
612 | + private static List<String> findMissingFields(final Message message) { | |
613 | + final List<String> results = new ArrayList<String>(); | |
614 | + findMissingFields(message, "", results); | |
615 | + return results; | |
616 | + } | |
617 | + | |
618 | + /** Recursive helper implementing {@link #findMissingFields(Message)}. */ | |
619 | + private static void findMissingFields(final Message message, | |
620 | + final String prefix, | |
621 | + final List<String> results) { | |
622 | + for (final FieldDescriptor field : | |
623 | + message.getDescriptorForType().getFields()) { | |
624 | + if (field.isRequired() && !message.hasField(field)) { | |
625 | + results.add(prefix + field.getName()); | |
626 | + } | |
627 | + } | |
628 | + | |
629 | + for (final Map.Entry<FieldDescriptor, Object> entry : | |
630 | + message.getAllFields().entrySet()) { | |
631 | + final FieldDescriptor field = entry.getKey(); | |
632 | + final Object value = entry.getValue(); | |
633 | + | |
634 | + if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { | |
635 | + if (field.isRepeated()) { | |
636 | + int i = 0; | |
637 | + for (final Object element : (List) value) { | |
638 | + findMissingFields((Message) element, | |
639 | + subMessagePrefix(prefix, field, i++), | |
640 | + results); | |
641 | + } | |
642 | + } else { | |
643 | + if (message.hasField(field)) { | |
644 | + findMissingFields((Message) value, | |
645 | + subMessagePrefix(prefix, field, -1), | |
646 | + results); | |
647 | + } | |
648 | + } | |
649 | + } | |
650 | + } | |
651 | + } | |
652 | + | |
653 | + private static String subMessagePrefix(final String prefix, | |
654 | + final FieldDescriptor field, | |
655 | + final int index) { | |
656 | + final StringBuilder result = new StringBuilder(prefix); | |
657 | + if (field.isExtension()) { | |
658 | + result.append('(') | |
659 | + .append(field.getFullName()) | |
660 | + .append(')'); | |
661 | + } else { | |
662 | + result.append(field.getName()); | |
663 | + } | |
664 | + if (index != -1) { | |
665 | + result.append('[') | |
666 | + .append(index) | |
667 | + .append(']'); | |
668 | + } | |
669 | + result.append('.'); | |
670 | + return result.toString(); | |
671 | + } | |
672 | + | |
673 | + // =============================================================== | |
674 | + // The following definitions seem to be required in order to make javac | |
675 | + // not produce weird errors like: | |
676 | + // | |
677 | + // java/com/google/protobuf/DynamicMessage.java:203: types | |
678 | + // com.google.protobuf.AbstractMessage.Builder< | |
679 | + // com.google.protobuf.DynamicMessage.Builder> and | |
680 | + // com.google.protobuf.AbstractMessage.Builder< | |
681 | + // com.google.protobuf.DynamicMessage.Builder> are incompatible; both | |
682 | + // define mergeFrom(com.google.protobuf.ByteString), but with unrelated | |
683 | + // return types. | |
684 | + // | |
685 | + // Strangely, these lines are only needed if javac is invoked separately | |
686 | + // on AbstractMessage.java and AbstractMessageLite.java. If javac is | |
687 | + // invoked on both simultaneously, it works. (Or maybe the important | |
688 | + // point is whether or not DynamicMessage.java is compiled together with | |
689 | + // AbstractMessageLite.java -- not sure.) I suspect this is a compiler | |
690 | + // bug. | |
691 | + | |
692 | + @Override | |
693 | + public BuilderType mergeFrom(final ByteString data) | |
694 | + throws InvalidProtocolBufferException { | |
695 | + return super.mergeFrom(data); | |
696 | + } | |
697 | + | |
698 | + @Override | |
699 | + public BuilderType mergeFrom( | |
700 | + final ByteString data, | |
701 | + final ExtensionRegistryLite extensionRegistry) | |
702 | + throws InvalidProtocolBufferException { | |
703 | + return super.mergeFrom(data, extensionRegistry); | |
704 | + } | |
705 | + | |
706 | + @Override | |
707 | + public BuilderType mergeFrom(final byte[] data) | |
708 | + throws InvalidProtocolBufferException { | |
709 | + return super.mergeFrom(data); | |
710 | + } | |
711 | + | |
712 | + @Override | |
713 | + public BuilderType mergeFrom( | |
714 | + final byte[] data, final int off, final int len) | |
715 | + throws InvalidProtocolBufferException { | |
716 | + return super.mergeFrom(data, off, len); | |
717 | + } | |
718 | + | |
719 | + @Override | |
720 | + public BuilderType mergeFrom( | |
721 | + final byte[] data, | |
722 | + final ExtensionRegistryLite extensionRegistry) | |
723 | + throws InvalidProtocolBufferException { | |
724 | + return super.mergeFrom(data, extensionRegistry); | |
725 | + } | |
726 | + | |
727 | + @Override | |
728 | + public BuilderType mergeFrom( | |
729 | + final byte[] data, final int off, final int len, | |
730 | + final ExtensionRegistryLite extensionRegistry) | |
731 | + throws InvalidProtocolBufferException { | |
732 | + return super.mergeFrom(data, off, len, extensionRegistry); | |
733 | + } | |
734 | + | |
735 | + @Override | |
736 | + public BuilderType mergeFrom(final InputStream input) | |
737 | + throws IOException { | |
738 | + return super.mergeFrom(input); | |
739 | + } | |
740 | + | |
741 | + @Override | |
742 | + public BuilderType mergeFrom( | |
743 | + final InputStream input, | |
744 | + final ExtensionRegistryLite extensionRegistry) | |
745 | + throws IOException { | |
746 | + return super.mergeFrom(input, extensionRegistry); | |
747 | + } | |
748 | + | |
749 | + @Override | |
750 | + public boolean mergeDelimitedFrom(final InputStream input) | |
751 | + throws IOException { | |
752 | + return super.mergeDelimitedFrom(input); | |
753 | + } | |
754 | + | |
755 | + @Override | |
756 | + public boolean mergeDelimitedFrom( | |
757 | + final InputStream input, | |
758 | + final ExtensionRegistryLite extensionRegistry) | |
759 | + throws IOException { | |
760 | + return super.mergeDelimitedFrom(input, extensionRegistry); | |
761 | + } | |
762 | + | |
763 | + } | |
764 | +} |
@@ -0,0 +1,325 @@ | ||
1 | +// Protocol Buffers - Google's data interchange format | |
2 | +// Copyright 2008 Google Inc. All rights reserved. | |
3 | +// http://code.google.com/p/protobuf/ | |
4 | +// | |
5 | +// Redistribution and use in source and binary forms, with or without | |
6 | +// modification, are permitted provided that the following conditions are | |
7 | +// met: | |
8 | +// | |
9 | +// * Redistributions of source code must retain the above copyright | |
10 | +// notice, this list of conditions and the following disclaimer. | |
11 | +// * Redistributions in binary form must reproduce the above | |
12 | +// copyright notice, this list of conditions and the following disclaimer | |
13 | +// in the documentation and/or other materials provided with the | |
14 | +// distribution. | |
15 | +// * Neither the name of Google Inc. nor the names of its | |
16 | +// contributors may be used to endorse or promote products derived from | |
17 | +// this software without specific prior written permission. | |
18 | +// | |
19 | +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
20 | +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
21 | +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
22 | +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
23 | +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
24 | +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
25 | +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
26 | +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
27 | +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
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. | |
30 | + | |
31 | +package com.google.protobuf; | |
32 | + | |
33 | +import java.io.FilterInputStream; | |
34 | +import java.io.InputStream; | |
35 | +import java.io.IOException; | |
36 | +import java.io.OutputStream; | |
37 | +import java.util.Collection; | |
38 | + | |
39 | +/** | |
40 | + * A partial implementation of the {@link MessageLite} interface which | |
41 | + * implements as many methods of that interface as possible in terms of other | |
42 | + * methods. | |
43 | + * | |
44 | + * @author kenton@google.com Kenton Varda | |
45 | + */ | |
46 | +public abstract class AbstractMessageLite implements MessageLite { | |
47 | + public ByteString toByteString() { | |
48 | + try { | |
49 | + final ByteString.CodedBuilder out = | |
50 | + ByteString.newCodedBuilder(getSerializedSize()); | |
51 | + writeTo(out.getCodedOutput()); | |
52 | + return out.build(); | |
53 | + } catch (IOException e) { | |
54 | + throw new RuntimeException( | |
55 | + "Serializing to a ByteString threw an IOException (should " + | |
56 | + "never happen).", e); | |
57 | + } | |
58 | + } | |
59 | + | |
60 | + public byte[] toByteArray() { | |
61 | + try { | |
62 | + final byte[] result = new byte[getSerializedSize()]; | |
63 | + final CodedOutputStream output = CodedOutputStream.newInstance(result); | |
64 | + writeTo(output); | |
65 | + output.checkNoSpaceLeft(); | |
66 | + return result; | |
67 | + } catch (IOException e) { | |
68 | + throw new RuntimeException( | |
69 | + "Serializing to a byte array threw an IOException " + | |
70 | + "(should never happen).", e); | |
71 | + } | |
72 | + } | |
73 | + | |
74 | + public void writeTo(final OutputStream output) throws IOException { | |
75 | + final int bufferSize = | |
76 | + CodedOutputStream.computePreferredBufferSize(getSerializedSize()); | |
77 | + final CodedOutputStream codedOutput = | |
78 | + CodedOutputStream.newInstance(output, bufferSize); | |
79 | + writeTo(codedOutput); | |
80 | + codedOutput.flush(); | |
81 | + } | |
82 | + | |
83 | + public void writeDelimitedTo(final OutputStream output) throws IOException { | |
84 | + final int serialized = getSerializedSize(); | |
85 | + final int bufferSize = CodedOutputStream.computePreferredBufferSize( | |
86 | + CodedOutputStream.computeRawVarint32Size(serialized) + serialized); | |
87 | + final CodedOutputStream codedOutput = | |
88 | + CodedOutputStream.newInstance(output, bufferSize); | |
89 | + codedOutput.writeRawVarint32(serialized); | |
90 | + writeTo(codedOutput); | |
91 | + codedOutput.flush(); | |
92 | + } | |
93 | + | |
94 | + /** | |
95 | + * A partial implementation of the {@link Message.Builder} interface which | |
96 | + * implements as many methods of that interface as possible in terms of | |
97 | + * other methods. | |
98 | + */ | |
99 | + @SuppressWarnings("unchecked") | |
100 | + public static abstract class Builder<BuilderType extends Builder> | |
101 | + implements MessageLite.Builder { | |
102 | + // The compiler produces an error if this is not declared explicitly. | |
103 | + @Override | |
104 | + public abstract BuilderType clone(); | |
105 | + | |
106 | + public BuilderType mergeFrom(final CodedInputStream input) | |
107 | + throws IOException { | |
108 | + return mergeFrom(input, ExtensionRegistryLite.getEmptyRegistry()); | |
109 | + } | |
110 | + | |
111 | + // Re-defined here for return type covariance. | |
112 | + public abstract BuilderType mergeFrom( | |
113 | + final CodedInputStream input, | |
114 | + final ExtensionRegistryLite extensionRegistry) | |
115 | + throws IOException; | |
116 | + | |
117 | + public BuilderType mergeFrom(final ByteString data) | |
118 | + throws InvalidProtocolBufferException { | |
119 | + try { | |
120 | + final CodedInputStream input = data.newCodedInput(); | |
121 | + mergeFrom(input); | |
122 | + input.checkLastTagWas(0); | |
123 | + return (BuilderType) this; | |
124 | + } catch (InvalidProtocolBufferException e) { | |
125 | + throw e; | |
126 | + } catch (IOException e) { | |
127 | + throw new RuntimeException( | |
128 | + "Reading from a ByteString threw an IOException (should " + | |
129 | + "never happen).", e); | |
130 | + } | |
131 | + } | |
132 | + | |
133 | + public BuilderType mergeFrom( | |
134 | + final ByteString data, | |
135 | + final ExtensionRegistryLite extensionRegistry) | |
136 | + throws InvalidProtocolBufferException { | |
137 | + try { | |
138 | + final CodedInputStream input = data.newCodedInput(); | |
139 | + mergeFrom(input, extensionRegistry); | |
140 | + input.checkLastTagWas(0); | |
141 | + return (BuilderType) this; | |
142 | + } catch (InvalidProtocolBufferException e) { | |
143 | + throw e; | |
144 | + } catch (IOException e) { | |
145 | + throw new RuntimeException( | |
146 | + "Reading from a ByteString threw an IOException (should " + | |
147 | + "never happen).", e); | |
148 | + } | |
149 | + } | |
150 | + | |
151 | + public BuilderType mergeFrom(final byte[] data) | |
152 | + throws InvalidProtocolBufferException { | |
153 | + return mergeFrom(data, 0, data.length); | |
154 | + } | |
155 | + | |
156 | + public BuilderType mergeFrom(final byte[] data, final int off, | |
157 | + final int len) | |
158 | + throws InvalidProtocolBufferException { | |
159 | + try { | |
160 | + final CodedInputStream input = | |
161 | + CodedInputStream.newInstance(data, off, len); | |
162 | + mergeFrom(input); | |
163 | + input.checkLastTagWas(0); | |
164 | + return (BuilderType) this; | |
165 | + } catch (InvalidProtocolBufferException e) { | |
166 | + throw e; | |
167 | + } catch (IOException e) { | |
168 | + throw new RuntimeException( | |
169 | + "Reading from a byte array threw an IOException (should " + | |
170 | + "never happen).", e); | |
171 | + } | |
172 | + } | |
173 | + | |
174 | + public BuilderType mergeFrom( | |
175 | + final byte[] data, | |
176 | + final ExtensionRegistryLite extensionRegistry) | |
177 | + throws InvalidProtocolBufferException { | |
178 | + return mergeFrom(data, 0, data.length, extensionRegistry); | |
179 | + } | |
180 | + | |
181 | + public BuilderType mergeFrom( | |
182 | + final byte[] data, final int off, final int len, | |
183 | + final ExtensionRegistryLite extensionRegistry) | |
184 | + throws InvalidProtocolBufferException { | |
185 | + try { | |
186 | + final CodedInputStream input = | |
187 | + CodedInputStream.newInstance(data, off, len); | |
188 | + mergeFrom(input, extensionRegistry); | |
189 | + input.checkLastTagWas(0); | |
190 | + return (BuilderType) this; | |
191 | + } catch (InvalidProtocolBufferException e) { | |
192 | + throw e; | |
193 | + } catch (IOException e) { | |
194 | + throw new RuntimeException( | |
195 | + "Reading from a byte array threw an IOException (should " + | |
196 | + "never happen).", e); | |
197 | + } | |
198 | + } | |
199 | + | |
200 | + public BuilderType mergeFrom(final InputStream input) throws IOException { | |
201 | + final CodedInputStream codedInput = CodedInputStream.newInstance(input); | |
202 | + mergeFrom(codedInput); | |
203 | + codedInput.checkLastTagWas(0); | |
204 | + return (BuilderType) this; | |
205 | + } | |
206 | + | |
207 | + public BuilderType mergeFrom( | |
208 | + final InputStream input, | |
209 | + final ExtensionRegistryLite extensionRegistry) | |
210 | + throws IOException { | |
211 | + final CodedInputStream codedInput = CodedInputStream.newInstance(input); | |
212 | + mergeFrom(codedInput, extensionRegistry); | |
213 | + codedInput.checkLastTagWas(0); | |
214 | + return (BuilderType) this; | |
215 | + } | |
216 | + | |
217 | + /** | |
218 | + * An InputStream implementations which reads from some other InputStream | |
219 | + * but is limited to a particular number of bytes. Used by | |
220 | + * mergeDelimitedFrom(). This is intentionally package-private so that | |
221 | + * UnknownFieldSet can share it. | |
222 | + */ | |
223 | + static final class LimitedInputStream extends FilterInputStream { | |
224 | + private int limit; | |
225 | + | |
226 | + LimitedInputStream(InputStream in, int limit) { | |
227 | + super(in); | |
228 | + this.limit = limit; | |
229 | + } | |
230 | + | |
231 | + @Override | |
232 | + public int available() throws IOException { | |
233 | + return Math.min(super.available(), limit); | |
234 | + } | |
235 | + | |
236 | + @Override | |
237 | + public int read() throws IOException { | |
238 | + if (limit <= 0) { | |
239 | + return -1; | |
240 | + } | |
241 | + final int result = super.read(); | |
242 | + if (result >= 0) { | |
243 | + --limit; | |
244 | + } | |
245 | + return result; | |
246 | + } | |
247 | + | |
248 | + @Override | |
249 | + public int read(final byte[] b, final int off, int len) | |
250 | + throws IOException { | |
251 | + if (limit <= 0) { | |
252 | + return -1; | |
253 | + } | |
254 | + len = Math.min(len, limit); | |
255 | + final int result = super.read(b, off, len); | |
256 | + if (result >= 0) { | |
257 | + limit -= result; | |
258 | + } | |
259 | + return result; | |
260 | + } | |
261 | + | |
262 | + @Override | |
263 | + public long skip(final long n) throws IOException { | |
264 | + final long result = super.skip(Math.min(n, limit)); | |
265 | + if (result >= 0) { | |
266 | + limit -= result; | |
267 | + } | |
268 | + return result; | |
269 | + } | |
270 | + } | |
271 | + | |
272 | + public boolean mergeDelimitedFrom( | |
273 | + final InputStream input, | |
274 | + final ExtensionRegistryLite extensionRegistry) | |
275 | + throws IOException { | |
276 | + final int firstByte = input.read(); | |
277 | + if (firstByte == -1) { | |
278 | + return false; | |
279 | + } | |
280 | + final int size = CodedInputStream.readRawVarint32(firstByte, input); | |
281 | + final InputStream limitedInput = new LimitedInputStream(input, size); | |
282 | + mergeFrom(limitedInput, extensionRegistry); | |
283 | + return true; | |
284 | + } | |
285 | + | |
286 | + public boolean mergeDelimitedFrom(final InputStream input) | |
287 | + throws IOException { | |
288 | + return mergeDelimitedFrom(input, | |
289 | + ExtensionRegistryLite.getEmptyRegistry()); | |
290 | + } | |
291 | + | |
292 | + /** | |
293 | + * Construct an UninitializedMessageException reporting missing fields in | |
294 | + * the given message. | |
295 | + */ | |
296 | + protected static UninitializedMessageException | |
297 | + newUninitializedMessageException(MessageLite message) { | |
298 | + return new UninitializedMessageException(message); | |
299 | + } | |
300 | + | |
301 | + /** | |
302 | + * Adds the {@code values} to the {@code list}. This is a helper method | |
303 | + * used by generated code. Users should ignore it. | |
304 | + * | |
305 | + * @throws NullPointerException if any of the elements of {@code values} is | |
306 | + * null. | |
307 | + */ | |
308 | + protected static <T> void addAll(final Iterable<T> values, | |
309 | + final Collection<? super T> list) { | |
310 | + for (final T value : values) { | |
311 | + if (value == null) { | |
312 | + throw new NullPointerException(); | |
313 | + } | |
314 | + } | |
315 | + if (values instanceof Collection) { | |
316 | + final Collection<T> collection = (Collection<T>) values; | |
317 | + list.addAll(collection); | |
318 | + } else { | |
319 | + for (final T value : values) { | |
320 | + list.add(value); | |
321 | + } | |
322 | + } | |
323 | + } | |
324 | + } | |
325 | +} |
@@ -0,0 +1,51 @@ | ||
1 | +// Protocol Buffers - Google's data interchange format | |
2 | +// Copyright 2008 Google Inc. All rights reserved. | |
3 | +// http://code.google.com/p/protobuf/ | |
4 | +// | |
5 | +// Redistribution and use in source and binary forms, with or without | |
6 | +// modification, are permitted provided that the following conditions are | |
7 | +// met: | |
8 | +// | |
9 | +// * Redistributions of source code must retain the above copyright | |
10 | +// notice, this list of conditions and the following disclaimer. | |
11 | +// * Redistributions in binary form must reproduce the above | |
12 | +// copyright notice, this list of conditions and the following disclaimer | |
13 | +// in the documentation and/or other materials provided with the | |
14 | +// distribution. | |
15 | +// * Neither the name of Google Inc. nor the names of its | |
16 | +// contributors may be used to endorse or promote products derived from | |
17 | +// this software without specific prior written permission. | |
18 | +// | |
19 | +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
20 | +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
21 | +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
22 | +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
23 | +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
24 | +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
25 | +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
26 | +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
27 | +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
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. | |
30 | + | |
31 | +package com.google.protobuf; | |
32 | + | |
33 | +/** | |
34 | + * <p>Abstract interface for a blocking RPC channel. {@code BlockingRpcChannel} | |
35 | + * is the blocking equivalent to {@link RpcChannel}. | |
36 | + * | |
37 | + * @author kenton@google.com Kenton Varda | |
38 | + * @author cpovirk@google.com Chris Povirk | |
39 | + */ | |
40 | +public interface BlockingRpcChannel { | |
41 | + /** | |
42 | + * Call the given method of the remote service and blocks until it returns. | |
43 | + * {@code callBlockingMethod()} is the blocking equivalent to | |
44 | + * {@link RpcChannel#callMethod}. | |
45 | + */ | |
46 | + Message callBlockingMethod( | |
47 | + Descriptors.MethodDescriptor method, | |
48 | + RpcController controller, | |
49 | + Message request, | |
50 | + Message responsePrototype) throws ServiceException; | |
51 | +} |
@@ -0,0 +1,64 @@ | ||
1 | +// Protocol Buffers - Google's data interchange format | |
2 | +// Copyright 2008 Google Inc. All rights reserved. | |
3 | +// http://code.google.com/p/protobuf/ | |
4 | +// | |
5 | +// Redistribution and use in source and binary forms, with or without | |
6 | +// modification, are permitted provided that the following conditions are | |
7 | +// met: | |
8 | +// | |
9 | +// * Redistributions of source code must retain the above copyright | |
10 | +// notice, this list of conditions and the following disclaimer. | |
11 | +// * Redistributions in binary form must reproduce the above | |
12 | +// copyright notice, this list of conditions and the following disclaimer | |
13 | +// in the documentation and/or other materials provided with the | |
14 | +// distribution. | |
15 | +// * Neither the name of Google Inc. nor the names of its | |
16 | +// contributors may be used to endorse or promote products derived from | |
17 | +// this software without specific prior written permission. | |
18 | +// | |
19 | +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
20 | +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
21 | +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
22 | +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
23 | +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
24 | +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
25 | +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
26 | +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
27 | +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
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. | |
30 | + | |
31 | +package com.google.protobuf; | |
32 | + | |
33 | +/** | |
34 | + * Blocking equivalent to {@link Service}. | |
35 | + * | |
36 | + * @author kenton@google.com Kenton Varda | |
37 | + * @author cpovirk@google.com Chris Povirk | |
38 | + */ | |
39 | +public interface BlockingService { | |
40 | + /** | |
41 | + * Equivalent to {@link Service#getDescriptorForType}. | |
42 | + */ | |
43 | + Descriptors.ServiceDescriptor getDescriptorForType(); | |
44 | + | |
45 | + /** | |
46 | + * Equivalent to {@link Service#callMethod}, except that | |
47 | + * {@code callBlockingMethod()} returns the result of the RPC or throws a | |
48 | + * {@link ServiceException} if there is a failure, rather than passing the | |
49 | + * information to a callback. | |
50 | + */ | |
51 | + Message callBlockingMethod(Descriptors.MethodDescriptor method, | |
52 | + RpcController controller, | |
53 | + Message request) throws ServiceException; | |
54 | + | |
55 | + /** | |
56 | + * Equivalent to {@link Service#getRequestPrototype}. | |
57 | + */ | |
58 | + Message getRequestPrototype(Descriptors.MethodDescriptor method); | |
59 | + | |
60 | + /** | |
61 | + * Equivalent to {@link Service#getResponsePrototype}. | |
62 | + */ | |
63 | + Message getResponsePrototype(Descriptors.MethodDescriptor method); | |
64 | +} |
@@ -0,0 +1,403 @@ | ||
1 | +// Protocol Buffers - Google's data interchange format | |
2 | +// Copyright 2008 Google Inc. All rights reserved. | |
3 | +// http://code.google.com/p/protobuf/ | |
4 | +// | |
5 | +// Redistribution and use in source and binary forms, with or without | |
6 | +// modification, are permitted provided that the following conditions are | |
7 | +// met: | |
8 | +// | |
9 | +// * Redistributions of source code must retain the above copyright | |
10 | +// notice, this list of conditions and the following disclaimer. | |
11 | +// * Redistributions in binary form must reproduce the above | |
12 | +// copyright notice, this list of conditions and the following disclaimer | |
13 | +// in the documentation and/or other materials provided with the | |
14 | +// distribution. | |
15 | +// * Neither the name of Google Inc. nor the names of its | |
16 | +// contributors may be used to endorse or promote products derived from | |
17 | +// this software without specific prior written permission. | |
18 | +// | |
19 | +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
20 | +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
21 | +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
22 | +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
23 | +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
24 | +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
25 | +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
26 | +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
27 | +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
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. | |
30 | + | |
31 | +package com.google.protobuf; | |
32 | + | |
33 | +import java.io.InputStream; | |
34 | +import java.io.ByteArrayInputStream; | |
35 | +import java.io.ByteArrayOutputStream; | |
36 | +import java.io.FilterOutputStream; | |
37 | +import java.io.UnsupportedEncodingException; | |
38 | +import java.nio.ByteBuffer; | |
39 | +import java.util.List; | |
40 | + | |
41 | +/** | |
42 | + * Immutable array of bytes. | |
43 | + * | |
44 | + * @author crazybob@google.com Bob Lee | |
45 | + * @author kenton@google.com Kenton Varda | |
46 | + */ | |
47 | +public final class ByteString { | |
48 | + private final byte[] bytes; | |
49 | + | |
50 | + private ByteString(final byte[] bytes) { | |
51 | + this.bytes = bytes; | |
52 | + } | |
53 | + | |
54 | + /** | |
55 | + * Gets the byte at the given index. | |
56 | + * | |
57 | + * @throws ArrayIndexOutOfBoundsException {@code index} is < 0 or >= size | |
58 | + */ | |
59 | + public byte byteAt(final int index) { | |
60 | + return bytes[index]; | |
61 | + } | |
62 | + | |
63 | + /** | |
64 | + * Gets the number of bytes. | |
65 | + */ | |
66 | + public int size() { | |
67 | + return bytes.length; | |
68 | + } | |
69 | + | |
70 | + /** | |
71 | + * Returns {@code true} if the size is {@code 0}, {@code false} otherwise. | |
72 | + */ | |
73 | + public boolean isEmpty() { | |
74 | + return bytes.length == 0; | |
75 | + } | |
76 | + | |
77 | + // ================================================================= | |
78 | + // byte[] -> ByteString | |
79 | + | |
80 | + /** | |
81 | + * Empty ByteString. | |
82 | + */ | |
83 | + public static final ByteString EMPTY = new ByteString(new byte[0]); | |
84 | + | |
85 | + /** | |
86 | + * Copies the given bytes into a {@code ByteString}. | |
87 | + */ | |
88 | + public static ByteString copyFrom(final byte[] bytes, final int offset, | |
89 | + final int size) { | |
90 | + final byte[] copy = new byte[size]; | |
91 | + System.arraycopy(bytes, offset, copy, 0, size); | |
92 | + return new ByteString(copy); | |
93 | + } | |
94 | + | |
95 | + /** | |
96 | + * Copies the given bytes into a {@code ByteString}. | |
97 | + */ | |
98 | + public static ByteString copyFrom(final byte[] bytes) { | |
99 | + return copyFrom(bytes, 0, bytes.length); | |
100 | + } | |
101 | + | |
102 | + /** | |
103 | + * Copies {@code size} bytes from a {@code java.nio.ByteBuffer} into | |
104 | + * a {@code ByteString}. | |
105 | + */ | |
106 | + public static ByteString copyFrom(final ByteBuffer bytes, final int size) { | |
107 | + final byte[] copy = new byte[size]; | |
108 | + bytes.get(copy); | |
109 | + return new ByteString(copy); | |
110 | + } | |
111 | + | |
112 | + /** | |
113 | + * Copies the remaining bytes from a {@code java.nio.ByteBuffer} into | |
114 | + * a {@code ByteString}. | |
115 | + */ | |
116 | + public static ByteString copyFrom(final ByteBuffer bytes) { | |
117 | + return copyFrom(bytes, bytes.remaining()); | |
118 | + } | |
119 | + | |
120 | + /** | |
121 | + * Encodes {@code text} into a sequence of bytes using the named charset | |
122 | + * and returns the result as a {@code ByteString}. | |
123 | + */ | |
124 | + public static ByteString copyFrom(final String text, final String charsetName) | |
125 | + throws UnsupportedEncodingException { | |
126 | + return new ByteString(text.getBytes(charsetName)); | |
127 | + } | |
128 | + | |
129 | + /** | |
130 | + * Encodes {@code text} into a sequence of UTF-8 bytes and returns the | |
131 | + * result as a {@code ByteString}. | |
132 | + */ | |
133 | + public static ByteString copyFromUtf8(final String text) { | |
134 | + try { | |
135 | + return new ByteString(text.getBytes("UTF-8")); | |
136 | + } catch (UnsupportedEncodingException e) { | |
137 | + throw new RuntimeException("UTF-8 not supported?", e); | |
138 | + } | |
139 | + } | |
140 | + | |
141 | + /** | |
142 | + * Concatenates all byte strings in the list and returns the result. | |
143 | + * | |
144 | + * <p>The returned {@code ByteString} is not necessarily a unique object. | |
145 | + * If the list is empty, the returned object is the singleton empty | |
146 | + * {@code ByteString}. If the list has only one element, that | |
147 | + * {@code ByteString} will be returned without copying. | |
148 | + */ | |
149 | + public static ByteString copyFrom(List<ByteString> list) { | |
150 | + if (list.size() == 0) { | |
151 | + return EMPTY; | |
152 | + } else if (list.size() == 1) { | |
153 | + return list.get(0); | |
154 | + } | |
155 | + | |
156 | + int size = 0; | |
157 | + for (ByteString str : list) { | |
158 | + size += str.size(); | |
159 | + } | |
160 | + byte[] bytes = new byte[size]; | |
161 | + int pos = 0; | |
162 | + for (ByteString str : list) { | |
163 | + System.arraycopy(str.bytes, 0, bytes, pos, str.size()); | |
164 | + pos += str.size(); | |
165 | + } | |
166 | + return new ByteString(bytes); | |
167 | + } | |
168 | + | |
169 | + // ================================================================= | |
170 | + // ByteString -> byte[] | |
171 | + | |
172 | + /** | |
173 | + * Copies bytes into a buffer at the given offset. | |
174 | + * | |
175 | + * @param target buffer to copy into | |
176 | + * @param offset in the target buffer | |
177 | + */ | |
178 | + public void copyTo(final byte[] target, final int offset) { | |
179 | + System.arraycopy(bytes, 0, target, offset, bytes.length); | |
180 | + } | |
181 | + | |
182 | + /** | |
183 | + * Copies bytes into a buffer. | |
184 | + * | |
185 | + * @param target buffer to copy into | |
186 | + * @param sourceOffset offset within these bytes | |
187 | + * @param targetOffset offset within the target buffer | |
188 | + * @param size number of bytes to copy | |
189 | + */ | |
190 | + public void copyTo(final byte[] target, final int sourceOffset, | |
191 | + final int targetOffset, | |
192 | + final int size) { | |
193 | + System.arraycopy(bytes, sourceOffset, target, targetOffset, size); | |
194 | + } | |
195 | + | |
196 | + /** | |
197 | + * Copies bytes into a ByteBuffer. | |
198 | + * | |
199 | + * @param target ByteBuffer to copy into. | |
200 | + * @throws ReadOnlyBufferException if the {@code target} is read-only | |
201 | + * @throws BufferOverflowException if the {@code target}'s remaining() | |
202 | + * space is not large enough to hold the data. | |
203 | + */ | |
204 | + public void copyTo(ByteBuffer target) { | |
205 | + target.put(bytes, 0, bytes.length); | |
206 | + } | |
207 | + | |
208 | + /** | |
209 | + * Copies bytes to a {@code byte[]}. | |
210 | + */ | |
211 | + public byte[] toByteArray() { | |
212 | + final int size = bytes.length; | |
213 | + final byte[] copy = new byte[size]; | |
214 | + System.arraycopy(bytes, 0, copy, 0, size); | |
215 | + return copy; | |
216 | + } | |
217 | + | |
218 | + /** | |
219 | + * Constructs a new read-only {@code java.nio.ByteBuffer} with the | |
220 | + * same backing byte array. | |
221 | + */ | |
222 | + public ByteBuffer asReadOnlyByteBuffer() { | |
223 | + final ByteBuffer byteBuffer = ByteBuffer.wrap(bytes); | |
224 | + return byteBuffer.asReadOnlyBuffer(); | |
225 | + } | |
226 | + | |
227 | + /** | |
228 | + * Constructs a new {@code String} by decoding the bytes using the | |
229 | + * specified charset. | |
230 | + */ | |
231 | + public String toString(final String charsetName) | |
232 | + throws UnsupportedEncodingException { | |
233 | + return new String(bytes, charsetName); | |
234 | + } | |
235 | + | |
236 | + /** | |
237 | + * Constructs a new {@code String} by decoding the bytes as UTF-8. | |
238 | + */ | |
239 | + public String toStringUtf8() { | |
240 | + try { | |
241 | + return new String(bytes, "UTF-8"); | |
242 | + } catch (UnsupportedEncodingException e) { | |
243 | + throw new RuntimeException("UTF-8 not supported?", e); | |
244 | + } | |
245 | + } | |
246 | + | |
247 | + // ================================================================= | |
248 | + // equals() and hashCode() | |
249 | + | |
250 | + @Override | |
251 | + public boolean equals(final Object o) { | |
252 | + if (o == this) { | |
253 | + return true; | |
254 | + } | |
255 | + | |
256 | + if (!(o instanceof ByteString)) { | |
257 | + return false; | |
258 | + } | |
259 | + | |
260 | + final ByteString other = (ByteString) o; | |
261 | + final int size = bytes.length; | |
262 | + if (size != other.bytes.length) { | |
263 | + return false; | |
264 | + } | |
265 | + | |
266 | + final byte[] thisBytes = bytes; | |
267 | + final byte[] otherBytes = other.bytes; | |
268 | + for (int i = 0; i < size; i++) { | |
269 | + if (thisBytes[i] != otherBytes[i]) { | |
270 | + return false; | |
271 | + } | |
272 | + } | |
273 | + | |
274 | + return true; | |
275 | + } | |
276 | + | |
277 | + private volatile int hash = 0; | |
278 | + | |
279 | + @Override | |
280 | + public int hashCode() { | |
281 | + int h = hash; | |
282 | + | |
283 | + if (h == 0) { | |
284 | + final byte[] thisBytes = bytes; | |
285 | + final int size = bytes.length; | |
286 | + | |
287 | + h = size; | |
288 | + for (int i = 0; i < size; i++) { | |
289 | + h = h * 31 + thisBytes[i]; | |
290 | + } | |
291 | + if (h == 0) { | |
292 | + h = 1; | |
293 | + } | |
294 | + | |
295 | + hash = h; | |
296 | + } | |
297 | + | |
298 | + return h; | |
299 | + } | |
300 | + | |
301 | + // ================================================================= | |
302 | + // Input stream | |
303 | + | |
304 | + /** | |
305 | + * Creates an {@code InputStream} which can be used to read the bytes. | |
306 | + */ | |
307 | + public InputStream newInput() { | |
308 | + return new ByteArrayInputStream(bytes); | |
309 | + } | |
310 | + | |
311 | + /** | |
312 | + * Creates a {@link CodedInputStream} which can be used to read the bytes. | |
313 | + * Using this is more efficient than creating a {@link CodedInputStream} | |
314 | + * wrapping the result of {@link #newInput()}. | |
315 | + */ | |
316 | + public CodedInputStream newCodedInput() { | |
317 | + // We trust CodedInputStream not to modify the bytes, or to give anyone | |
318 | + // else access to them. | |
319 | + return CodedInputStream.newInstance(bytes); | |
320 | + } | |
321 | + | |
322 | + // ================================================================= | |
323 | + // Output stream | |
324 | + | |
325 | + /** | |
326 | + * Creates a new {@link Output} with the given initial capacity. | |
327 | + */ | |
328 | + public static Output newOutput(final int initialCapacity) { | |
329 | + return new Output(new ByteArrayOutputStream(initialCapacity)); | |
330 | + } | |
331 | + | |
332 | + /** | |
333 | + * Creates a new {@link Output}. | |
334 | + */ | |
335 | + public static Output newOutput() { | |
336 | + return newOutput(32); | |
337 | + } | |
338 | + | |
339 | + /** | |
340 | + * Outputs to a {@code ByteString} instance. Call {@link #toByteString()} to | |
341 | + * create the {@code ByteString} instance. | |
342 | + */ | |
343 | + public static final class Output extends FilterOutputStream { | |
344 | + private final ByteArrayOutputStream bout; | |
345 | + | |
346 | + /** | |
347 | + * Constructs a new output with the given initial capacity. | |
348 | + */ | |
349 | + private Output(final ByteArrayOutputStream bout) { | |
350 | + super(bout); | |
351 | + this.bout = bout; | |
352 | + } | |
353 | + | |
354 | + /** | |
355 | + * Creates a {@code ByteString} instance from this {@code Output}. | |
356 | + */ | |
357 | + public ByteString toByteString() { | |
358 | + final byte[] byteArray = bout.toByteArray(); | |
359 | + return new ByteString(byteArray); | |
360 | + } | |
361 | + } | |
362 | + | |
363 | + /** | |
364 | + * Constructs a new ByteString builder, which allows you to efficiently | |
365 | + * construct a {@code ByteString} by writing to a {@link CodedOutputStream}. | |
366 | + * Using this is much more efficient than calling {@code newOutput()} and | |
367 | + * wrapping that in a {@code CodedOutputStream}. | |
368 | + * | |
369 | + * <p>This is package-private because it's a somewhat confusing interface. | |
370 | + * Users can call {@link Message#toByteString()} instead of calling this | |
371 | + * directly. | |
372 | + * | |
373 | + * @param size The target byte size of the {@code ByteString}. You must | |
374 | + * write exactly this many bytes before building the result. | |
375 | + */ | |
376 | + static CodedBuilder newCodedBuilder(final int size) { | |
377 | + return new CodedBuilder(size); | |
378 | + } | |
379 | + | |
380 | + /** See {@link ByteString#newCodedBuilder(int)}. */ | |
381 | + static final class CodedBuilder { | |
382 | + private final CodedOutputStream output; | |
383 | + private final byte[] buffer; | |
384 | + | |
385 | + private CodedBuilder(final int size) { | |
386 | + buffer = new byte[size]; | |
387 | + output = CodedOutputStream.newInstance(buffer); | |
388 | + } | |
389 | + | |
390 | + public ByteString build() { | |
391 | + output.checkNoSpaceLeft(); | |
392 | + | |
393 | + // We can be confident that the CodedOutputStream will not modify the | |
394 | + // underlying bytes anymore because it already wrote all of them. So, | |
395 | + // no need to make a copy. | |
396 | + return new ByteString(buffer); | |
397 | + } | |
398 | + | |
399 | + public CodedOutputStream getCodedOutput() { | |
400 | + return output; | |
401 | + } | |
402 | + } | |
403 | +} |
@@ -0,0 +1,885 @@ | ||
1 | +// Protocol Buffers - Google's data interchange format | |
2 | +// Copyright 2008 Google Inc. All rights reserved. | |
3 | +// http://code.google.com/p/protobuf/ | |
4 | +// | |
5 | +// Redistribution and use in source and binary forms, with or without | |
6 | +// modification, are permitted provided that the following conditions are | |
7 | +// met: | |
8 | +// | |
9 | +// * Redistributions of source code must retain the above copyright | |
10 | +// notice, this list of conditions and the following disclaimer. | |
11 | +// * Redistributions in binary form must reproduce the above | |
12 | +// copyright notice, this list of conditions and the following disclaimer | |
13 | +// in the documentation and/or other materials provided with the | |
14 | +// distribution. | |
15 | +// * Neither the name of Google Inc. nor the names of its | |
16 | +// contributors may be used to endorse or promote products derived from | |
17 | +// this software without specific prior written permission. | |
18 | +// | |
19 | +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
20 | +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
21 | +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
22 | +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
23 | +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
24 | +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
25 | +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
26 | +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
27 | +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
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. | |
30 | + | |
31 | +package com.google.protobuf; | |
32 | + | |
33 | +import java.io.IOException; | |
34 | +import java.io.InputStream; | |
35 | +import java.util.ArrayList; | |
36 | +import java.util.List; | |
37 | + | |
38 | +/** | |
39 | + * Reads and decodes protocol message fields. | |
40 | + * | |
41 | + * This class contains two kinds of methods: methods that read specific | |
42 | + * protocol message constructs and field types (e.g. {@link #readTag()} and | |
43 | + * {@link #readInt32()}) and methods that read low-level values (e.g. | |
44 | + * {@link #readRawVarint32()} and {@link #readRawBytes}). If you are reading | |
45 | + * encoded protocol messages, you should use the former methods, but if you are | |
46 | + * reading some other format of your own design, use the latter. | |
47 | + * | |
48 | + * @author kenton@google.com Kenton Varda | |
49 | + */ | |
50 | +public final class CodedInputStream { | |
51 | + /** | |
52 | + * Create a new CodedInputStream wrapping the given InputStream. | |
53 | + */ | |
54 | + public static CodedInputStream newInstance(final InputStream input) { | |
55 | + return new CodedInputStream(input); | |
56 | + } | |
57 | + | |
58 | + /** | |
59 | + * Create a new CodedInputStream wrapping the given byte array. | |
60 | + */ | |
61 | + public static CodedInputStream newInstance(final byte[] buf) { | |
62 | + return newInstance(buf, 0, buf.length); | |
63 | + } | |
64 | + | |
65 | + /** | |
66 | + * Create a new CodedInputStream wrapping the given byte array slice. | |
67 | + */ | |
68 | + public static CodedInputStream newInstance(final byte[] buf, final int off, | |
69 | + final int len) { | |
70 | + CodedInputStream result = new CodedInputStream(buf, off, len); | |
71 | + try { | |
72 | + // Some uses of CodedInputStream can be more efficient if they know | |
73 | + // exactly how many bytes are available. By pushing the end point of the | |
74 | + // buffer as a limit, we allow them to get this information via | |
75 | + // getBytesUntilLimit(). Pushing a limit that we know is at the end of | |
76 | + // the stream can never hurt, since we can never past that point anyway. | |
77 | + result.pushLimit(len); | |
78 | + } catch (InvalidProtocolBufferException ex) { | |
79 | + // The only reason pushLimit() might throw an exception here is if len | |
80 | + // is negative. Normally pushLimit()'s parameter comes directly off the | |
81 | + // wire, so it's important to catch exceptions in case of corrupt or | |
82 | + // malicious data. However, in this case, we expect that len is not a | |
83 | + // user-supplied value, so we can assume that it being negative indicates | |
84 | + // a programming error. Therefore, throwing an unchecked exception is | |
85 | + // appropriate. | |
86 | + throw new IllegalArgumentException(ex); | |
87 | + } | |
88 | + return result; | |
89 | + } | |
90 | + | |
91 | + // ----------------------------------------------------------------- | |
92 | + | |
93 | + /** | |
94 | + * Attempt to read a field tag, returning zero if we have reached EOF. | |
95 | + * Protocol message parsers use this to read tags, since a protocol message | |
96 | + * may legally end wherever a tag occurs, and zero is not a valid tag number. | |
97 | + */ | |
98 | + public int readTag() throws IOException { | |
99 | + if (isAtEnd()) { | |
100 | + lastTag = 0; | |
101 | + return 0; | |
102 | + } | |
103 | + | |
104 | + lastTag = readRawVarint32(); | |
105 | + if (WireFormat.getTagFieldNumber(lastTag) == 0) { | |
106 | + // If we actually read zero (or any tag number corresponding to field | |
107 | + // number zero), that's not a valid tag. | |
108 | + throw InvalidProtocolBufferException.invalidTag(); | |
109 | + } | |
110 | + return lastTag; | |
111 | + } | |
112 | + | |
113 | + /** | |
114 | + * Verifies that the last call to readTag() returned the given tag value. | |
115 | + * This is used to verify that a nested group ended with the correct | |
116 | + * end tag. | |
117 | + * | |
118 | + * @throws InvalidProtocolBufferException {@code value} does not match the | |
119 | + * last tag. | |
120 | + */ | |
121 | + public void checkLastTagWas(final int value) | |
122 | + throws InvalidProtocolBufferException { | |
123 | + if (lastTag != value) { | |
124 | + throw InvalidProtocolBufferException.invalidEndTag(); | |
125 | + } | |
126 | + } | |
127 | + | |
128 | + /** | |
129 | + * Reads and discards a single field, given its tag value. | |
130 | + * | |
131 | + * @return {@code false} if the tag is an endgroup tag, in which case | |
132 | + * nothing is skipped. Otherwise, returns {@code true}. | |
133 | + */ | |
134 | + public boolean skipField(final int tag) throws IOException { | |
135 | + switch (WireFormat.getTagWireType(tag)) { | |
136 | + case WireFormat.WIRETYPE_VARINT: | |
137 | + readInt32(); | |
138 | + return true; | |
139 | + case WireFormat.WIRETYPE_FIXED64: | |
140 | + readRawLittleEndian64(); | |
141 | + return true; | |
142 | + case WireFormat.WIRETYPE_LENGTH_DELIMITED: | |
143 | + skipRawBytes(readRawVarint32()); | |
144 | + return true; | |
145 | + case WireFormat.WIRETYPE_START_GROUP: | |
146 | + skipMessage(); | |
147 | + checkLastTagWas( | |
148 | + WireFormat.makeTag(WireFormat.getTagFieldNumber(tag), | |
149 | + WireFormat.WIRETYPE_END_GROUP)); | |
150 | + return true; | |
151 | + case WireFormat.WIRETYPE_END_GROUP: | |
152 | + return false; | |
153 | + case WireFormat.WIRETYPE_FIXED32: | |
154 | + readRawLittleEndian32(); | |
155 | + return true; | |
156 | + default: | |
157 | + throw InvalidProtocolBufferException.invalidWireType(); | |
158 | + } | |
159 | + } | |
160 | + | |
161 | + /** | |
162 | + * Reads and discards an entire message. This will read either until EOF | |
163 | + * or until an endgroup tag, whichever comes first. | |
164 | + */ | |
165 | + public void skipMessage() throws IOException { | |
166 | + while (true) { | |
167 | + final int tag = readTag(); | |
168 | + if (tag == 0 || !skipField(tag)) { | |
169 | + return; | |
170 | + } | |
171 | + } | |
172 | + } | |
173 | + | |
174 | + // ----------------------------------------------------------------- | |
175 | + | |
176 | + /** Read a {@code double} field value from the stream. */ | |
177 | + public double readDouble() throws IOException { | |
178 | + return Double.longBitsToDouble(readRawLittleEndian64()); | |
179 | + } | |
180 | + | |
181 | + /** Read a {@code float} field value from the stream. */ | |
182 | + public float readFloat() throws IOException { | |
183 | + return Float.intBitsToFloat(readRawLittleEndian32()); | |
184 | + } | |
185 | + | |
186 | + /** Read a {@code uint64} field value from the stream. */ | |
187 | + public long readUInt64() throws IOException { | |
188 | + return readRawVarint64(); | |
189 | + } | |
190 | + | |
191 | + /** Read an {@code int64} field value from the stream. */ | |
192 | + public long readInt64() throws IOException { | |
193 | + return readRawVarint64(); | |
194 | + } | |
195 | + | |
196 | + /** Read an {@code int32} field value from the stream. */ | |
197 | + public int readInt32() throws IOException { | |
198 | + return readRawVarint32(); | |
199 | + } | |
200 | + | |
201 | + /** Read a {@code fixed64} field value from the stream. */ | |
202 | + public long readFixed64() throws IOException { | |
203 | + return readRawLittleEndian64(); | |
204 | + } | |
205 | + | |
206 | + /** Read a {@code fixed32} field value from the stream. */ | |
207 | + public int readFixed32() throws IOException { | |
208 | + return readRawLittleEndian32(); | |
209 | + } | |
210 | + | |
211 | + /** Read a {@code bool} field value from the stream. */ | |
212 | + public boolean readBool() throws IOException { | |
213 | + return readRawVarint32() != 0; | |
214 | + } | |
215 | + | |
216 | + /** Read a {@code string} field value from the stream. */ | |
217 | + public String readString() throws IOException { | |
218 | + final int size = readRawVarint32(); | |
219 | + if (size <= (bufferSize - bufferPos) && size > 0) { | |
220 | + // Fast path: We already have the bytes in a contiguous buffer, so | |
221 | + // just copy directly from it. | |
222 | + final String result = new String(buffer, bufferPos, size, "UTF-8"); | |
223 | + bufferPos += size; | |
224 | + return result; | |
225 | + } else { | |
226 | + // Slow path: Build a byte array first then copy it. | |
227 | + return new String(readRawBytes(size), "UTF-8"); | |
228 | + } | |
229 | + } | |
230 | + | |
231 | + /** Read a {@code group} field value from the stream. */ | |
232 | + public void readGroup(final int fieldNumber, | |
233 | + final MessageLite.Builder builder, | |
234 | + final ExtensionRegistryLite extensionRegistry) | |
235 | + throws IOException { | |
236 | + if (recursionDepth >= recursionLimit) { | |
237 | + throw InvalidProtocolBufferException.recursionLimitExceeded(); | |
238 | + } | |
239 | + ++recursionDepth; | |
240 | + builder.mergeFrom(this, extensionRegistry); | |
241 | + checkLastTagWas( | |
242 | + WireFormat.makeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP)); | |
243 | + --recursionDepth; | |
244 | + } | |
245 | + | |
246 | + /** | |
247 | + * Reads a {@code group} field value from the stream and merges it into the | |
248 | + * given {@link UnknownFieldSet}. | |
249 | + * | |
250 | + * @deprecated UnknownFieldSet.Builder now implements MessageLite.Builder, so | |
251 | + * you can just call {@link #readGroup}. | |
252 | + */ | |
253 | + @Deprecated | |
254 | + public void readUnknownGroup(final int fieldNumber, | |
255 | + final MessageLite.Builder builder) | |
256 | + throws IOException { | |
257 | + // We know that UnknownFieldSet will ignore any ExtensionRegistry so it | |
258 | + // is safe to pass null here. (We can't call | |
259 | + // ExtensionRegistry.getEmptyRegistry() because that would make this | |
260 | + // class depend on ExtensionRegistry, which is not part of the lite | |
261 | + // library.) | |
262 | + readGroup(fieldNumber, builder, null); | |
263 | + } | |
264 | + | |
265 | + /** Read an embedded message field value from the stream. */ | |
266 | + public void readMessage(final MessageLite.Builder builder, | |
267 | + final ExtensionRegistryLite extensionRegistry) | |
268 | + throws IOException { | |
269 | + final int length = readRawVarint32(); | |
270 | + if (recursionDepth >= recursionLimit) { | |
271 | + throw InvalidProtocolBufferException.recursionLimitExceeded(); | |
272 | + } | |
273 | + final int oldLimit = pushLimit(length); | |
274 | + ++recursionDepth; | |
275 | + builder.mergeFrom(this, extensionRegistry); | |
276 | + checkLastTagWas(0); | |
277 | + --recursionDepth; | |
278 | + popLimit(oldLimit); | |
279 | + } | |
280 | + | |
281 | + /** Read a {@code bytes} field value from the stream. */ | |
282 | + public ByteString readBytes() throws IOException { | |
283 | + final int size = readRawVarint32(); | |
284 | + if (size == 0) { | |
285 | + return ByteString.EMPTY; | |
286 | + } else if (size <= (bufferSize - bufferPos) && size > 0) { | |
287 | + // Fast path: We already have the bytes in a contiguous buffer, so | |
288 | + // just copy directly from it. | |
289 | + final ByteString result = ByteString.copyFrom(buffer, bufferPos, size); | |
290 | + bufferPos += size; | |
291 | + return result; | |
292 | + } else { | |
293 | + // Slow path: Build a byte array first then copy it. | |
294 | + return ByteString.copyFrom(readRawBytes(size)); | |
295 | + } | |
296 | + } | |
297 | + | |
298 | + /** Read a {@code uint32} field value from the stream. */ | |
299 | + public int readUInt32() throws IOException { | |
300 | + return readRawVarint32(); | |
301 | + } | |
302 | + | |
303 | + /** | |
304 | + * Read an enum field value from the stream. Caller is responsible | |
305 | + * for converting the numeric value to an actual enum. | |
306 | + */ | |
307 | + public int readEnum() throws IOException { | |
308 | + return readRawVarint32(); | |
309 | + } | |
310 | + | |
311 | + /** Read an {@code sfixed32} field value from the stream. */ | |
312 | + public int readSFixed32() throws IOException { | |
313 | + return readRawLittleEndian32(); | |
314 | + } | |
315 | + | |
316 | + /** Read an {@code sfixed64} field value from the stream. */ | |
317 | + public long readSFixed64() throws IOException { | |
318 | + return readRawLittleEndian64(); | |
319 | + } | |
320 | + | |
321 | + /** Read an {@code sint32} field value from the stream. */ | |
322 | + public int readSInt32() throws IOException { | |
323 | + return decodeZigZag32(readRawVarint32()); | |
324 | + } | |
325 | + | |
326 | + /** Read an {@code sint64} field value from the stream. */ | |
327 | + public long readSInt64() throws IOException { | |
328 | + return decodeZigZag64(readRawVarint64()); | |
329 | + } | |
330 | + | |
331 | + // ================================================================= | |
332 | + | |
333 | + /** | |
334 | + * Read a raw Varint from the stream. If larger than 32 bits, discard the | |
335 | + * upper bits. | |
336 | + */ | |
337 | + public int readRawVarint32() throws IOException { | |
338 | + byte tmp = readRawByte(); | |
339 | + if (tmp >= 0) { | |
340 | + return tmp; | |
341 | + } | |
342 | + int result = tmp & 0x7f; | |
343 | + if ((tmp = readRawByte()) >= 0) { | |
344 | + result |= tmp << 7; | |
345 | + } else { | |
346 | + result |= (tmp & 0x7f) << 7; | |
347 | + if ((tmp = readRawByte()) >= 0) { | |
348 | + result |= tmp << 14; | |
349 | + } else { | |
350 | + result |= (tmp & 0x7f) << 14; | |
351 | + if ((tmp = readRawByte()) >= 0) { | |
352 | + result |= tmp << 21; | |
353 | + } else { | |
354 | + result |= (tmp & 0x7f) << 21; | |
355 | + result |= (tmp = readRawByte()) << 28; | |
356 | + if (tmp < 0) { | |
357 | + // Discard upper 32 bits. | |
358 | + for (int i = 0; i < 5; i++) { | |
359 | + if (readRawByte() >= 0) { | |
360 | + return result; | |
361 | + } | |
362 | + } | |
363 | + throw InvalidProtocolBufferException.malformedVarint(); | |
364 | + } | |
365 | + } | |
366 | + } | |
367 | + } | |
368 | + return result; | |
369 | + } | |
370 | + | |
371 | + /** | |
372 | + * Reads a varint from the input one byte at a time, so that it does not | |
373 | + * read any bytes after the end of the varint. If you simply wrapped the | |
374 | + * stream in a CodedInputStream and used {@link #readRawVarint32(InputStream)} | |
375 | + * then you would probably end up reading past the end of the varint since | |
376 | + * CodedInputStream buffers its input. | |
377 | + */ | |
378 | + static int readRawVarint32(final InputStream input) throws IOException { | |
379 | + final int firstByte = input.read(); | |
380 | + if (firstByte == -1) { | |
381 | + throw InvalidProtocolBufferException.truncatedMessage(); | |
382 | + } | |
383 | + return readRawVarint32(firstByte, input); | |
384 | + } | |
385 | + | |
386 | + /** | |
387 | + * Like {@link #readRawVarint32(InputStream)}, but expects that the caller | |
388 | + * has already read one byte. This allows the caller to determine if EOF | |
389 | + * has been reached before attempting to read. | |
390 | + */ | |
391 | + public static int readRawVarint32( | |
392 | + final int firstByte, final InputStream input) throws IOException { | |
393 | + if ((firstByte & 0x80) == 0) { | |
394 | + return firstByte; | |
395 | + } | |
396 | + | |
397 | + int result = firstByte & 0x7f; | |
398 | + int offset = 7; | |
399 | + for (; offset < 32; offset += 7) { | |
400 | + final int b = input.read(); | |
401 | + if (b == -1) { | |
402 | + throw InvalidProtocolBufferException.truncatedMessage(); | |
403 | + } | |
404 | + result |= (b & 0x7f) << offset; | |
405 | + if ((b & 0x80) == 0) { | |
406 | + return result; | |
407 | + } | |
408 | + } | |
409 | + // Keep reading up to 64 bits. | |
410 | + for (; offset < 64; offset += 7) { | |
411 | + final int b = input.read(); | |
412 | + if (b == -1) { | |
413 | + throw InvalidProtocolBufferException.truncatedMessage(); | |
414 | + } | |
415 | + if ((b & 0x80) == 0) { | |
416 | + return result; | |
417 | + } | |
418 | + } | |
419 | + throw InvalidProtocolBufferException.malformedVarint(); | |
420 | + } | |
421 | + | |
422 | + /** Read a raw Varint from the stream. */ | |
423 | + public long readRawVarint64() throws IOException { | |
424 | + int shift = 0; | |
425 | + long result = 0; | |
426 | + while (shift < 64) { | |
427 | + final byte b = readRawByte(); | |
428 | + result |= (long)(b & 0x7F) << shift; | |
429 | + if ((b & 0x80) == 0) { | |
430 | + return result; | |
431 | + } | |
432 | + shift += 7; | |
433 | + } | |
434 | + throw InvalidProtocolBufferException.malformedVarint(); | |
435 | + } | |
436 | + | |
437 | + /** Read a 32-bit little-endian integer from the stream. */ | |
438 | + public int readRawLittleEndian32() throws IOException { | |
439 | + final byte b1 = readRawByte(); | |
440 | + final byte b2 = readRawByte(); | |
441 | + final byte b3 = readRawByte(); | |
442 | + final byte b4 = readRawByte(); | |
443 | + return (((int)b1 & 0xff) ) | | |
444 | + (((int)b2 & 0xff) << 8) | | |
445 | + (((int)b3 & 0xff) << 16) | | |
446 | + (((int)b4 & 0xff) << 24); | |
447 | + } | |
448 | + | |
449 | + /** Read a 64-bit little-endian integer from the stream. */ | |
450 | + public long readRawLittleEndian64() throws IOException { | |
451 | + final byte b1 = readRawByte(); | |
452 | + final byte b2 = readRawByte(); | |
453 | + final byte b3 = readRawByte(); | |
454 | + final byte b4 = readRawByte(); | |
455 | + final byte b5 = readRawByte(); | |
456 | + final byte b6 = readRawByte(); | |
457 | + final byte b7 = readRawByte(); | |
458 | + final byte b8 = readRawByte(); | |
459 | + return (((long)b1 & 0xff) ) | | |
460 | + (((long)b2 & 0xff) << 8) | | |
461 | + (((long)b3 & 0xff) << 16) | | |
462 | + (((long)b4 & 0xff) << 24) | | |
463 | + (((long)b5 & 0xff) << 32) | | |
464 | + (((long)b6 & 0xff) << 40) | | |
465 | + (((long)b7 & 0xff) << 48) | | |
466 | + (((long)b8 & 0xff) << 56); | |
467 | + } | |
468 | + | |
469 | + /** | |
470 | + * Decode a ZigZag-encoded 32-bit value. ZigZag encodes signed integers | |
471 | + * into values that can be efficiently encoded with varint. (Otherwise, | |
472 | + * negative values must be sign-extended to 64 bits to be varint encoded, | |
473 | + * thus always taking 10 bytes on the wire.) | |
474 | + * | |
475 | + * @param n An unsigned 32-bit integer, stored in a signed int because | |
476 | + * Java has no explicit unsigned support. | |
477 | + * @return A signed 32-bit integer. | |
478 | + */ | |
479 | + public static int decodeZigZag32(final int n) { | |
480 | + return (n >>> 1) ^ -(n & 1); | |
481 | + } | |
482 | + | |
483 | + /** | |
484 | + * Decode a ZigZag-encoded 64-bit value. ZigZag encodes signed integers | |
485 | + * into values that can be efficiently encoded with varint. (Otherwise, | |
486 | + * negative values must be sign-extended to 64 bits to be varint encoded, | |
487 | + * thus always taking 10 bytes on the wire.) | |
488 | + * | |
489 | + * @param n An unsigned 64-bit integer, stored in a signed int because | |
490 | + * Java has no explicit unsigned support. | |
491 | + * @return A signed 64-bit integer. | |
492 | + */ | |
493 | + public static long decodeZigZag64(final long n) { | |
494 | + return (n >>> 1) ^ -(n & 1); | |
495 | + } | |
496 | + | |
497 | + // ----------------------------------------------------------------- | |
498 | + | |
499 | + private final byte[] buffer; | |
500 | + private int bufferSize; | |
501 | + private int bufferSizeAfterLimit; | |
502 | + private int bufferPos; | |
503 | + private final InputStream input; | |
504 | + private int lastTag; | |
505 | + | |
506 | + /** | |
507 | + * The total number of bytes read before the current buffer. The total | |
508 | + * bytes read up to the current position can be computed as | |
509 | + * {@code totalBytesRetired + bufferPos}. This value may be negative if | |
510 | + * reading started in the middle of the current buffer (e.g. if the | |
511 | + * constructor that takes a byte array and an offset was used). | |
512 | + */ | |
513 | + private int totalBytesRetired; | |
514 | + | |
515 | + /** The absolute position of the end of the current message. */ | |
516 | + private int currentLimit = Integer.MAX_VALUE; | |
517 | + | |
518 | + /** See setRecursionLimit() */ | |
519 | + private int recursionDepth; | |
520 | + private int recursionLimit = DEFAULT_RECURSION_LIMIT; | |
521 | + | |
522 | + /** See setSizeLimit() */ | |
523 | + private int sizeLimit = DEFAULT_SIZE_LIMIT; | |
524 | + | |
525 | + private static final int DEFAULT_RECURSION_LIMIT = 64; | |
526 | + private static final int DEFAULT_SIZE_LIMIT = 64 << 20; // 64MB | |
527 | + private static final int BUFFER_SIZE = 4096; | |
528 | + | |
529 | + private CodedInputStream(final byte[] buffer, final int off, final int len) { | |
530 | + this.buffer = buffer; | |
531 | + bufferSize = off + len; | |
532 | + bufferPos = off; | |
533 | + totalBytesRetired = -off; | |
534 | + input = null; | |
535 | + } | |
536 | + | |
537 | + private CodedInputStream(final InputStream input) { | |
538 | + buffer = new byte[BUFFER_SIZE]; | |
539 | + bufferSize = 0; | |
540 | + bufferPos = 0; | |
541 | + totalBytesRetired = 0; | |
542 | + this.input = input; | |
543 | + } | |
544 | + | |
545 | + /** | |
546 | + * Set the maximum message recursion depth. In order to prevent malicious | |
547 | + * messages from causing stack overflows, {@code CodedInputStream} limits | |
548 | + * how deeply messages may be nested. The default limit is 64. | |
549 | + * | |
550 | + * @return the old limit. | |
551 | + */ | |
552 | + public int setRecursionLimit(final int limit) { | |
553 | + if (limit < 0) { | |
554 | + throw new IllegalArgumentException( | |
555 | + "Recursion limit cannot be negative: " + limit); | |
556 | + } | |
557 | + final int oldLimit = recursionLimit; | |
558 | + recursionLimit = limit; | |
559 | + return oldLimit; | |
560 | + } | |
561 | + | |
562 | + /** | |
563 | + * Set the maximum message size. In order to prevent malicious | |
564 | + * messages from exhausting memory or causing integer overflows, | |
565 | + * {@code CodedInputStream} limits how large a message may be. | |
566 | + * The default limit is 64MB. You should set this limit as small | |
567 | + * as you can without harming your app's functionality. Note that | |
568 | + * size limits only apply when reading from an {@code InputStream}, not | |
569 | + * when constructed around a raw byte array (nor with | |
570 | + * {@link ByteString#newCodedInput}). | |
571 | + * <p> | |
572 | + * If you want to read several messages from a single CodedInputStream, you | |
573 | + * could call {@link #resetSizeCounter()} after each one to avoid hitting the | |
574 | + * size limit. | |
575 | + * | |
576 | + * @return the old limit. | |
577 | + */ | |
578 | + public int setSizeLimit(final int limit) { | |
579 | + if (limit < 0) { | |
580 | + throw new IllegalArgumentException( | |
581 | + "Size limit cannot be negative: " + limit); | |
582 | + } | |
583 | + final int oldLimit = sizeLimit; | |
584 | + sizeLimit = limit; | |
585 | + return oldLimit; | |
586 | + } | |
587 | + | |
588 | + /** | |
589 | + * Resets the current size counter to zero (see {@link #setSizeLimit(int)}). | |
590 | + */ | |
591 | + public void resetSizeCounter() { | |
592 | + totalBytesRetired = -bufferPos; | |
593 | + } | |
594 | + | |
595 | + /** | |
596 | + * Sets {@code currentLimit} to (current position) + {@code byteLimit}. This | |
597 | + * is called when descending into a length-delimited embedded message. | |
598 | + * | |
599 | + * <p>Note that {@code pushLimit()} does NOT affect how many bytes the | |
600 | + * {@code CodedInputStream} reads from an underlying {@code InputStream} when | |
601 | + * refreshing its buffer. If you need to prevent reading past a certain | |
602 | + * point in the underlying {@code InputStream} (e.g. because you expect it to | |
603 | + * contain more data after the end of the message which you need to handle | |
604 | + * differently) then you must place a wrapper around you {@code InputStream} | |
605 | + * which limits the amount of data that can be read from it. | |
606 | + * | |
607 | + * @return the old limit. | |
608 | + */ | |
609 | + public int pushLimit(int byteLimit) throws InvalidProtocolBufferException { | |
610 | + if (byteLimit < 0) { | |
611 | + throw InvalidProtocolBufferException.negativeSize(); | |
612 | + } | |
613 | + byteLimit += totalBytesRetired + bufferPos; | |
614 | + final int oldLimit = currentLimit; | |
615 | + if (byteLimit > oldLimit) { | |
616 | + throw InvalidProtocolBufferException.truncatedMessage(); | |
617 | + } | |
618 | + currentLimit = byteLimit; | |
619 | + | |
620 | + recomputeBufferSizeAfterLimit(); | |
621 | + | |
622 | + return oldLimit; | |
623 | + } | |
624 | + | |
625 | + private void recomputeBufferSizeAfterLimit() { | |
626 | + bufferSize += bufferSizeAfterLimit; | |
627 | + final int bufferEnd = totalBytesRetired + bufferSize; | |
628 | + if (bufferEnd > currentLimit) { | |
629 | + // Limit is in current buffer. | |
630 | + bufferSizeAfterLimit = bufferEnd - currentLimit; | |
631 | + bufferSize -= bufferSizeAfterLimit; | |
632 | + } else { | |
633 | + bufferSizeAfterLimit = 0; | |
634 | + } | |
635 | + } | |
636 | + | |
637 | + /** | |
638 | + * Discards the current limit, returning to the previous limit. | |
639 | + * | |
640 | + * @param oldLimit The old limit, as returned by {@code pushLimit}. | |
641 | + */ | |
642 | + public void popLimit(final int oldLimit) { | |
643 | + currentLimit = oldLimit; | |
644 | + recomputeBufferSizeAfterLimit(); | |
645 | + } | |
646 | + | |
647 | + /** | |
648 | + * Returns the number of bytes to be read before the current limit. | |
649 | + * If no limit is set, returns -1. | |
650 | + */ | |
651 | + public int getBytesUntilLimit() { | |
652 | + if (currentLimit == Integer.MAX_VALUE) { | |
653 | + return -1; | |
654 | + } | |
655 | + | |
656 | + final int currentAbsolutePosition = totalBytesRetired + bufferPos; | |
657 | + return currentLimit - currentAbsolutePosition; | |
658 | + } | |
659 | + | |
660 | + /** | |
661 | + * Returns true if the stream has reached the end of the input. This is the | |
662 | + * case if either the end of the underlying input source has been reached or | |
663 | + * if the stream has reached a limit created using {@link #pushLimit(int)}. | |
664 | + */ | |
665 | + public boolean isAtEnd() throws IOException { | |
666 | + return bufferPos == bufferSize && !refillBuffer(false); | |
667 | + } | |
668 | + | |
669 | + /** | |
670 | + * The total bytes read up to the current position. Calling | |
671 | + * {@link #resetSizeCounter()} resets this value to zero. | |
672 | + */ | |
673 | + public int getTotalBytesRead() { | |
674 | + return totalBytesRetired + bufferPos; | |
675 | + } | |
676 | + | |
677 | + /** | |
678 | + * Called with {@code this.buffer} is empty to read more bytes from the | |
679 | + * input. If {@code mustSucceed} is true, refillBuffer() gurantees that | |
680 | + * either there will be at least one byte in the buffer when it returns | |
681 | + * or it will throw an exception. If {@code mustSucceed} is false, | |
682 | + * refillBuffer() returns false if no more bytes were available. | |
683 | + */ | |
684 | + private boolean refillBuffer(final boolean mustSucceed) throws IOException { | |
685 | + if (bufferPos < bufferSize) { | |
686 | + throw new IllegalStateException( | |
687 | + "refillBuffer() called when buffer wasn't empty."); | |
688 | + } | |
689 | + | |
690 | + if (totalBytesRetired + bufferSize == currentLimit) { | |
691 | + // Oops, we hit a limit. | |
692 | + if (mustSucceed) { | |
693 | + throw InvalidProtocolBufferException.truncatedMessage(); | |
694 | + } else { | |
695 | + return false; | |
696 | + } | |
697 | + } | |
698 | + | |
699 | + totalBytesRetired += bufferSize; | |
700 | + | |
701 | + bufferPos = 0; | |
702 | + bufferSize = (input == null) ? -1 : input.read(buffer); | |
703 | + if (bufferSize == 0 || bufferSize < -1) { | |
704 | + throw new IllegalStateException( | |
705 | + "InputStream#read(byte[]) returned invalid result: " + bufferSize + | |
706 | + "\nThe InputStream implementation is buggy."); | |
707 | + } | |
708 | + if (bufferSize == -1) { | |
709 | + bufferSize = 0; | |
710 | + if (mustSucceed) { | |
711 | + throw InvalidProtocolBufferException.truncatedMessage(); | |
712 | + } else { | |
713 | + return false; | |
714 | + } | |
715 | + } else { | |
716 | + recomputeBufferSizeAfterLimit(); | |
717 | + final int totalBytesRead = | |
718 | + totalBytesRetired + bufferSize + bufferSizeAfterLimit; | |
719 | + if (totalBytesRead > sizeLimit || totalBytesRead < 0) { | |
720 | + throw InvalidProtocolBufferException.sizeLimitExceeded(); | |
721 | + } | |
722 | + return true; | |
723 | + } | |
724 | + } | |
725 | + | |
726 | + /** | |
727 | + * Read one byte from the input. | |
728 | + * | |
729 | + * @throws InvalidProtocolBufferException The end of the stream or the current | |
730 | + * limit was reached. | |
731 | + */ | |
732 | + public byte readRawByte() throws IOException { | |
733 | + if (bufferPos == bufferSize) { | |
734 | + refillBuffer(true); | |
735 | + } | |
736 | + return buffer[bufferPos++]; | |
737 | + } | |
738 | + | |
739 | + /** | |
740 | + * Read a fixed size of bytes from the input. | |
741 | + * | |
742 | + * @throws InvalidProtocolBufferException The end of the stream or the current | |
743 | + * limit was reached. | |
744 | + */ | |
745 | + public byte[] readRawBytes(final int size) throws IOException { | |
746 | + if (size < 0) { | |
747 | + throw InvalidProtocolBufferException.negativeSize(); | |
748 | + } | |
749 | + | |
750 | + if (totalBytesRetired + bufferPos + size > currentLimit) { | |
751 | + // Read to the end of the stream anyway. | |
752 | + skipRawBytes(currentLimit - totalBytesRetired - bufferPos); | |
753 | + // Then fail. | |
754 | + throw InvalidProtocolBufferException.truncatedMessage(); | |
755 | + } | |
756 | + | |
757 | + if (size <= bufferSize - bufferPos) { | |
758 | + // We have all the bytes we need already. | |
759 | + final byte[] bytes = new byte[size]; | |
760 | + System.arraycopy(buffer, bufferPos, bytes, 0, size); | |
761 | + bufferPos += size; | |
762 | + return bytes; | |
763 | + } else if (size < BUFFER_SIZE) { | |
764 | + // Reading more bytes than are in the buffer, but not an excessive number | |
765 | + // of bytes. We can safely allocate the resulting array ahead of time. | |
766 | + | |
767 | + // First copy what we have. | |
768 | + final byte[] bytes = new byte[size]; | |
769 | + int pos = bufferSize - bufferPos; | |
770 | + System.arraycopy(buffer, bufferPos, bytes, 0, pos); | |
771 | + bufferPos = bufferSize; | |
772 | + | |
773 | + // We want to use refillBuffer() and then copy from the buffer into our | |
774 | + // byte array rather than reading directly into our byte array because | |
775 | + // the input may be unbuffered. | |
776 | + refillBuffer(true); | |
777 | + | |
778 | + while (size - pos > bufferSize) { | |
779 | + System.arraycopy(buffer, 0, bytes, pos, bufferSize); | |
780 | + pos += bufferSize; | |
781 | + bufferPos = bufferSize; | |
782 | + refillBuffer(true); | |
783 | + } | |
784 | + | |
785 | + System.arraycopy(buffer, 0, bytes, pos, size - pos); | |
786 | + bufferPos = size - pos; | |
787 | + | |
788 | + return bytes; | |
789 | + } else { | |
790 | + // The size is very large. For security reasons, we can't allocate the | |
791 | + // entire byte array yet. The size comes directly from the input, so a | |
792 | + // maliciously-crafted message could provide a bogus very large size in | |
793 | + // order to trick the app into allocating a lot of memory. We avoid this | |
794 | + // by allocating and reading only a small chunk at a time, so that the | |
795 | + // malicious message must actually *be* extremely large to cause | |
796 | + // problems. Meanwhile, we limit the allowed size of a message elsewhere. | |
797 | + | |
798 | + // Remember the buffer markers since we'll have to copy the bytes out of | |
799 | + // it later. | |
800 | + final int originalBufferPos = bufferPos; | |
801 | + final int originalBufferSize = bufferSize; | |
802 | + | |
803 | + // Mark the current buffer consumed. | |
804 | + totalBytesRetired += bufferSize; | |
805 | + bufferPos = 0; | |
806 | + bufferSize = 0; | |
807 | + | |
808 | + // Read all the rest of the bytes we need. | |
809 | + int sizeLeft = size - (originalBufferSize - originalBufferPos); | |
810 | + final List<byte[]> chunks = new ArrayList<byte[]>(); | |
811 | + | |
812 | + while (sizeLeft > 0) { | |
813 | + final byte[] chunk = new byte[Math.min(sizeLeft, BUFFER_SIZE)]; | |
814 | + int pos = 0; | |
815 | + while (pos < chunk.length) { | |
816 | + final int n = (input == null) ? -1 : | |
817 | + input.read(chunk, pos, chunk.length - pos); | |
818 | + if (n == -1) { | |
819 | + throw InvalidProtocolBufferException.truncatedMessage(); | |
820 | + } | |
821 | + totalBytesRetired += n; | |
822 | + pos += n; | |
823 | + } | |
824 | + sizeLeft -= chunk.length; | |
825 | + chunks.add(chunk); | |
826 | + } | |
827 | + | |
828 | + // OK, got everything. Now concatenate it all into one buffer. | |
829 | + final byte[] bytes = new byte[size]; | |
830 | + | |
831 | + // Start by copying the leftover bytes from this.buffer. | |
832 | + int pos = originalBufferSize - originalBufferPos; | |
833 | + System.arraycopy(buffer, originalBufferPos, bytes, 0, pos); | |
834 | + | |
835 | + // And now all the chunks. | |
836 | + for (final byte[] chunk : chunks) { | |
837 | + System.arraycopy(chunk, 0, bytes, pos, chunk.length); | |
838 | + pos += chunk.length; | |
839 | + } | |
840 | + | |
841 | + // Done. | |
842 | + return bytes; | |
843 | + } | |
844 | + } | |
845 | + | |
846 | + /** | |
847 | + * Reads and discards {@code size} bytes. | |
848 | + * | |
849 | + * @throws InvalidProtocolBufferException The end of the stream or the current | |
850 | + * limit was reached. | |
851 | + */ | |
852 | + public void skipRawBytes(final int size) throws IOException { | |
853 | + if (size < 0) { | |
854 | + throw InvalidProtocolBufferException.negativeSize(); | |
855 | + } | |
856 | + | |
857 | + if (totalBytesRetired + bufferPos + size > currentLimit) { | |
858 | + // Read to the end of the stream anyway. | |
859 | + skipRawBytes(currentLimit - totalBytesRetired - bufferPos); | |
860 | + // Then fail. | |
861 | + throw InvalidProtocolBufferException.truncatedMessage(); | |
862 | + } | |
863 | + | |
864 | + if (size <= bufferSize - bufferPos) { | |
865 | + // We have all the bytes we need already. | |
866 | + bufferPos += size; | |
867 | + } else { | |
868 | + // Skipping more bytes than are in the buffer. First skip what we have. | |
869 | + int pos = bufferSize - bufferPos; | |
870 | + bufferPos = bufferSize; | |
871 | + | |
872 | + // Keep refilling the buffer until we get to the point we wanted to skip | |
873 | + // to. This has the side effect of ensuring the limits are updated | |
874 | + // correctly. | |
875 | + refillBuffer(true); | |
876 | + while (size - pos > bufferSize) { | |
877 | + pos += bufferSize; | |
878 | + bufferPos = bufferSize; | |
879 | + refillBuffer(true); | |
880 | + } | |
881 | + | |
882 | + bufferPos = size - pos; | |
883 | + } | |
884 | + } | |
885 | +} |
@@ -0,0 +1,1081 @@ | ||
1 | +// Protocol Buffers - Google's data interchange format | |
2 | +// Copyright 2008 Google Inc. All rights reserved. | |
3 | +// http://code.google.com/p/protobuf/ | |
4 | +// | |
5 | +// Redistribution and use in source and binary forms, with or without | |
6 | +// modification, are permitted provided that the following conditions are | |
7 | +// met: | |
8 | +// | |
9 | +// * Redistributions of source code must retain the above copyright | |
10 | +// notice, this list of conditions and the following disclaimer. | |
11 | +// * Redistributions in binary form must reproduce the above | |
12 | +// copyright notice, this list of conditions and the following disclaimer | |
13 | +// in the documentation and/or other materials provided with the | |
14 | +// distribution. | |
15 | +// * Neither the name of Google Inc. nor the names of its | |
16 | +// contributors may be used to endorse or promote products derived from | |
17 | +// this software without specific prior written permission. | |
18 | +// | |
19 | +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
20 | +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
21 | +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
22 | +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
23 | +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
24 | +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
25 | +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
26 | +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
27 | +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
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. | |
30 | + | |
31 | +package com.google.protobuf; | |
32 | + | |
33 | +import java.io.OutputStream; | |
34 | +import java.io.IOException; | |
35 | +import java.io.UnsupportedEncodingException; | |
36 | +import java.io.InputStream; | |
37 | + | |
38 | +/** | |
39 | + * Encodes and writes protocol message fields. | |
40 | + * | |
41 | + * <p>This class contains two kinds of methods: methods that write specific | |
42 | + * protocol message constructs and field types (e.g. {@link #writeTag} and | |
43 | + * {@link #writeInt32}) and methods that write low-level values (e.g. | |
44 | + * {@link #writeRawVarint32} and {@link #writeRawBytes}). If you are | |
45 | + * writing encoded protocol messages, you should use the former methods, but if | |
46 | + * you are writing some other format of your own design, use the latter. | |
47 | + * | |
48 | + * <p>This class is totally unsynchronized. | |
49 | + * | |
50 | + * @author kneton@google.com Kenton Varda | |
51 | + */ | |
52 | +public final class CodedOutputStream { | |
53 | + private final byte[] buffer; | |
54 | + private final int limit; | |
55 | + private int position; | |
56 | + | |
57 | + private final OutputStream output; | |
58 | + | |
59 | + /** | |
60 | + * The buffer size used in {@link #newInstance(OutputStream)}. | |
61 | + */ | |
62 | + public static final int DEFAULT_BUFFER_SIZE = 4096; | |
63 | + | |
64 | + /** | |
65 | + * Returns the buffer size to efficiently write dataLength bytes to this | |
66 | + * CodedOutputStream. Used by AbstractMessageLite. | |
67 | + * | |
68 | + * @return the buffer size to efficiently write dataLength bytes to this | |
69 | + * CodedOutputStream. | |
70 | + */ | |
71 | + static int computePreferredBufferSize(int dataLength) { | |
72 | + if (dataLength > DEFAULT_BUFFER_SIZE) return DEFAULT_BUFFER_SIZE; | |
73 | + return dataLength; | |
74 | + } | |
75 | + | |
76 | + private CodedOutputStream(final byte[] buffer, final int offset, | |
77 | + final int length) { | |
78 | + output = null; | |
79 | + this.buffer = buffer; | |
80 | + position = offset; | |
81 | + limit = offset + length; | |
82 | + } | |
83 | + | |
84 | + private CodedOutputStream(final OutputStream output, final byte[] buffer) { | |
85 | + this.output = output; | |
86 | + this.buffer = buffer; | |
87 | + position = 0; | |
88 | + limit = buffer.length; | |
89 | + } | |
90 | + | |
91 | + /** | |
92 | + * Create a new {@code CodedOutputStream} wrapping the given | |
93 | + * {@code OutputStream}. | |
94 | + */ | |
95 | + public static CodedOutputStream newInstance(final OutputStream output) { | |
96 | + return newInstance(output, DEFAULT_BUFFER_SIZE); | |
97 | + } | |
98 | + | |
99 | + /** | |
100 | + * Create a new {@code CodedOutputStream} wrapping the given | |
101 | + * {@code OutputStream} with a given buffer size. | |
102 | + */ | |
103 | + public static CodedOutputStream newInstance(final OutputStream output, | |
104 | + final int bufferSize) { | |
105 | + return new CodedOutputStream(output, new byte[bufferSize]); | |
106 | + } | |
107 | + | |
108 | + /** | |
109 | + * Create a new {@code CodedOutputStream} that writes directly to the given | |
110 | + * byte array. If more bytes are written than fit in the array, | |
111 | + * {@link OutOfSpaceException} will be thrown. Writing directly to a flat | |
112 | + * array is faster than writing to an {@code OutputStream}. See also | |
113 | + * {@link ByteString#newCodedBuilder}. | |
114 | + */ | |
115 | + public static CodedOutputStream newInstance(final byte[] flatArray) { | |
116 | + return newInstance(flatArray, 0, flatArray.length); | |
117 | + } | |
118 | + | |
119 | + /** | |
120 | + * Create a new {@code CodedOutputStream} that writes directly to the given | |
121 | + * byte array slice. If more bytes are written than fit in the slice, | |
122 | + * {@link OutOfSpaceException} will be thrown. Writing directly to a flat | |
123 | + * array is faster than writing to an {@code OutputStream}. See also | |
124 | + * {@link ByteString#newCodedBuilder}. | |
125 | + */ | |
126 | + public static CodedOutputStream newInstance(final byte[] flatArray, | |
127 | + final int offset, | |
128 | + final int length) { | |
129 | + return new CodedOutputStream(flatArray, offset, length); | |
130 | + } | |
131 | + | |
132 | + // ----------------------------------------------------------------- | |
133 | + | |
134 | + /** Write a {@code double} field, including tag, to the stream. */ | |
135 | + public void writeDouble(final int fieldNumber, final double value) | |
136 | + throws IOException { | |
137 | + writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED64); | |
138 | + writeDoubleNoTag(value); | |
139 | + } | |
140 | + | |
141 | + /** Write a {@code float} field, including tag, to the stream. */ | |
142 | + public void writeFloat(final int fieldNumber, final float value) | |
143 | + throws IOException { | |
144 | + writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED32); | |
145 | + writeFloatNoTag(value); | |
146 | + } | |
147 | + | |
148 | + /** Write a {@code uint64} field, including tag, to the stream. */ | |
149 | + public void writeUInt64(final int fieldNumber, final long value) | |
150 | + throws IOException { | |
151 | + writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); | |
152 | + writeUInt64NoTag(value); | |
153 | + } | |
154 | + | |
155 | + /** Write an {@code int64} field, including tag, to the stream. */ | |
156 | + public void writeInt64(final int fieldNumber, final long value) | |
157 | + throws IOException { | |
158 | + writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); | |
159 | + writeInt64NoTag(value); | |
160 | + } | |
161 | + | |
162 | + /** Write an {@code int32} field, including tag, to the stream. */ | |
163 | + public void writeInt32(final int fieldNumber, final int value) | |
164 | + throws IOException { | |
165 | + writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); | |
166 | + writeInt32NoTag(value); | |
167 | + } | |
168 | + | |
169 | + /** Write a {@code fixed64} field, including tag, to the stream. */ | |
170 | + public void writeFixed64(final int fieldNumber, final long value) | |
171 | + throws IOException { | |
172 | + writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED64); | |
173 | + writeFixed64NoTag(value); | |
174 | + } | |
175 | + | |
176 | + /** Write a {@code fixed32} field, including tag, to the stream. */ | |
177 | + public void writeFixed32(final int fieldNumber, final int value) | |
178 | + throws IOException { | |
179 | + writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED32); | |
180 | + writeFixed32NoTag(value); | |
181 | + } | |
182 | + | |
183 | + /** Write a {@code bool} field, including tag, to the stream. */ | |
184 | + public void writeBool(final int fieldNumber, final boolean value) | |
185 | + throws IOException { | |
186 | + writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); | |
187 | + writeBoolNoTag(value); | |
188 | + } | |
189 | + | |
190 | + /** Write a {@code string} field, including tag, to the stream. */ | |
191 | + public void writeString(final int fieldNumber, final String value) | |
192 | + throws IOException { | |
193 | + writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); | |
194 | + writeStringNoTag(value); | |
195 | + } | |
196 | + | |
197 | + /** Write a {@code group} field, including tag, to the stream. */ | |
198 | + public void writeGroup(final int fieldNumber, final MessageLite value) | |
199 | + throws IOException { | |
200 | + writeTag(fieldNumber, WireFormat.WIRETYPE_START_GROUP); | |
201 | + writeGroupNoTag(value); | |
202 | + writeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP); | |
203 | + } | |
204 | + | |
205 | + /** | |
206 | + * Write a group represented by an {@link UnknownFieldSet}. | |
207 | + * | |
208 | + * @deprecated UnknownFieldSet now implements MessageLite, so you can just | |
209 | + * call {@link #writeGroup}. | |
210 | + */ | |
211 | + @Deprecated | |
212 | + public void writeUnknownGroup(final int fieldNumber, | |
213 | + final MessageLite value) | |
214 | + throws IOException { | |
215 | + writeGroup(fieldNumber, value); | |
216 | + } | |
217 | + | |
218 | + /** Write an embedded message field, including tag, to the stream. */ | |
219 | + public void writeMessage(final int fieldNumber, final MessageLite value) | |
220 | + throws IOException { | |
221 | + writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); | |
222 | + writeMessageNoTag(value); | |
223 | + } | |
224 | + | |
225 | + /** Write a {@code bytes} field, including tag, to the stream. */ | |
226 | + public void writeBytes(final int fieldNumber, final ByteString value) | |
227 | + throws IOException { | |
228 | + writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); | |
229 | + writeBytesNoTag(value); | |
230 | + } | |
231 | + | |
232 | + /** Write a {@code uint32} field, including tag, to the stream. */ | |
233 | + public void writeUInt32(final int fieldNumber, final int value) | |
234 | + throws IOException { | |
235 | + writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); | |
236 | + writeUInt32NoTag(value); | |
237 | + } | |
238 | + | |
239 | + /** | |
240 | + * Write an enum field, including tag, to the stream. Caller is responsible | |
241 | + * for converting the enum value to its numeric value. | |
242 | + */ | |
243 | + public void writeEnum(final int fieldNumber, final int value) | |
244 | + throws IOException { | |
245 | + writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); | |
246 | + writeEnumNoTag(value); | |
247 | + } | |
248 | + | |
249 | + /** Write an {@code sfixed32} field, including tag, to the stream. */ | |
250 | + public void writeSFixed32(final int fieldNumber, final int value) | |
251 | + throws IOException { | |
252 | + writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED32); | |
253 | + writeSFixed32NoTag(value); | |
254 | + } | |
255 | + | |
256 | + /** Write an {@code sfixed64} field, including tag, to the stream. */ | |
257 | + public void writeSFixed64(final int fieldNumber, final long value) | |
258 | + throws IOException { | |
259 | + writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED64); | |
260 | + writeSFixed64NoTag(value); | |
261 | + } | |
262 | + | |
263 | + /** Write an {@code sint32} field, including tag, to the stream. */ | |
264 | + public void writeSInt32(final int fieldNumber, final int value) | |
265 | + throws IOException { | |
266 | + writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); | |
267 | + writeSInt32NoTag(value); | |
268 | + } | |
269 | + | |
270 | + /** Write an {@code sint64} field, including tag, to the stream. */ | |
271 | + public void writeSInt64(final int fieldNumber, final long value) | |
272 | + throws IOException { | |
273 | + writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); | |
274 | + writeSInt64NoTag(value); | |
275 | + } | |
276 | + | |
277 | + /** | |
278 | + * Write a MessageSet extension field to the stream. For historical reasons, | |
279 | + * the wire format differs from normal fields. | |
280 | + */ | |
281 | + public void writeMessageSetExtension(final int fieldNumber, | |
282 | + final MessageLite value) | |
283 | + throws IOException { | |
284 | + writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_START_GROUP); | |
285 | + writeUInt32(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber); | |
286 | + writeMessage(WireFormat.MESSAGE_SET_MESSAGE, value); | |
287 | + writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_END_GROUP); | |
288 | + } | |
289 | + | |
290 | + /** | |
291 | + * Write an unparsed MessageSet extension field to the stream. For | |
292 | + * historical reasons, the wire format differs from normal fields. | |
293 | + */ | |
294 | + public void writeRawMessageSetExtension(final int fieldNumber, | |
295 | + final ByteString value) | |
296 | + throws IOException { | |
297 | + writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_START_GROUP); | |
298 | + writeUInt32(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber); | |
299 | + writeBytes(WireFormat.MESSAGE_SET_MESSAGE, value); | |
300 | + writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_END_GROUP); | |
301 | + } | |
302 | + | |
303 | + // ----------------------------------------------------------------- | |
304 | + | |
305 | + /** Write a {@code double} field to the stream. */ | |
306 | + public void writeDoubleNoTag(final double value) throws IOException { | |
307 | + writeRawLittleEndian64(Double.doubleToRawLongBits(value)); | |
308 | + } | |
309 | + | |
310 | + /** Write a {@code float} field to the stream. */ | |
311 | + public void writeFloatNoTag(final float value) throws IOException { | |
312 | + writeRawLittleEndian32(Float.floatToRawIntBits(value)); | |
313 | + } | |
314 | + | |
315 | + /** Write a {@code uint64} field to the stream. */ | |
316 | + public void writeUInt64NoTag(final long value) throws IOException { | |
317 | + writeRawVarint64(value); | |
318 | + } | |
319 | + | |
320 | + /** Write an {@code int64} field to the stream. */ | |
321 | + public void writeInt64NoTag(final long value) throws IOException { | |
322 | + writeRawVarint64(value); | |
323 | + } | |
324 | + | |
325 | + /** Write an {@code int32} field to the stream. */ | |
326 | + public void writeInt32NoTag(final int value) throws IOException { | |
327 | + if (value >= 0) { | |
328 | + writeRawVarint32(value); | |
329 | + } else { | |
330 | + // Must sign-extend. | |
331 | + writeRawVarint64(value); | |
332 | + } | |
333 | + } | |
334 | + | |
335 | + /** Write a {@code fixed64} field to the stream. */ | |
336 | + public void writeFixed64NoTag(final long value) throws IOException { | |
337 | + writeRawLittleEndian64(value); | |
338 | + } | |
339 | + | |
340 | + /** Write a {@code fixed32} field to the stream. */ | |
341 | + public void writeFixed32NoTag(final int value) throws IOException { | |
342 | + writeRawLittleEndian32(value); | |
343 | + } | |
344 | + | |
345 | + /** Write a {@code bool} field to the stream. */ | |
346 | + public void writeBoolNoTag(final boolean value) throws IOException { | |
347 | + writeRawByte(value ? 1 : 0); | |
348 | + } | |
349 | + | |
350 | + /** Write a {@code string} field to the stream. */ | |
351 | + public void writeStringNoTag(final String value) throws IOException { | |
352 | + // Unfortunately there does not appear to be any way to tell Java to encode | |
353 | + // UTF-8 directly into our buffer, so we have to let it create its own byte | |
354 | + // array and then copy. | |
355 | + final byte[] bytes = value.getBytes("UTF-8"); | |
356 | + writeRawVarint32(bytes.length); | |
357 | + writeRawBytes(bytes); | |
358 | + } | |
359 | + | |
360 | + /** Write a {@code group} field to the stream. */ | |
361 | + public void writeGroupNoTag(final MessageLite value) throws IOException { | |
362 | + value.writeTo(this); | |
363 | + } | |
364 | + | |
365 | + /** | |
366 | + * Write a group represented by an {@link UnknownFieldSet}. | |
367 | + * | |
368 | + * @deprecated UnknownFieldSet now implements MessageLite, so you can just | |
369 | + * call {@link #writeGroupNoTag}. | |
370 | + */ | |
371 | + @Deprecated | |
372 | + public void writeUnknownGroupNoTag(final MessageLite value) | |
373 | + throws IOException { | |
374 | + writeGroupNoTag(value); | |
375 | + } | |
376 | + | |
377 | + /** Write an embedded message field to the stream. */ | |
378 | + public void writeMessageNoTag(final MessageLite value) throws IOException { | |
379 | + writeRawVarint32(value.getSerializedSize()); | |
380 | + value.writeTo(this); | |
381 | + } | |
382 | + | |
383 | + /** Write a {@code bytes} field to the stream. */ | |
384 | + public void writeBytesNoTag(final ByteString value) throws IOException { | |
385 | + writeRawVarint32(value.size()); | |
386 | + writeRawBytes(value); | |
387 | + } | |
388 | + | |
389 | + /** Write a {@code uint32} field to the stream. */ | |
390 | + public void writeUInt32NoTag(final int value) throws IOException { | |
391 | + writeRawVarint32(value); | |
392 | + } | |
393 | + | |
394 | + /** | |
395 | + * Write an enum field to the stream. Caller is responsible | |
396 | + * for converting the enum value to its numeric value. | |
397 | + */ | |
398 | + public void writeEnumNoTag(final int value) throws IOException { | |
399 | + writeInt32NoTag(value); | |
400 | + } | |
401 | + | |
402 | + /** Write an {@code sfixed32} field to the stream. */ | |
403 | + public void writeSFixed32NoTag(final int value) throws IOException { | |
404 | + writeRawLittleEndian32(value); | |
405 | + } | |
406 | + | |
407 | + /** Write an {@code sfixed64} field to the stream. */ | |
408 | + public void writeSFixed64NoTag(final long value) throws IOException { | |
409 | + writeRawLittleEndian64(value); | |
410 | + } | |
411 | + | |
412 | + /** Write an {@code sint32} field to the stream. */ | |
413 | + public void writeSInt32NoTag(final int value) throws IOException { | |
414 | + writeRawVarint32(encodeZigZag32(value)); | |
415 | + } | |
416 | + | |
417 | + /** Write an {@code sint64} field to the stream. */ | |
418 | + public void writeSInt64NoTag(final long value) throws IOException { | |
419 | + writeRawVarint64(encodeZigZag64(value)); | |
420 | + } | |
421 | + | |
422 | + // ================================================================= | |
423 | + | |
424 | + /** | |
425 | + * Compute the number of bytes that would be needed to encode a | |
426 | + * {@code double} field, including tag. | |
427 | + */ | |
428 | + public static int computeDoubleSize(final int fieldNumber, | |
429 | + final double value) { | |
430 | + return computeTagSize(fieldNumber) + computeDoubleSizeNoTag(value); | |
431 | + } | |
432 | + | |
433 | + /** | |
434 | + * Compute the number of bytes that would be needed to encode a | |
435 | + * {@code float} field, including tag. | |
436 | + */ | |
437 | + public static int computeFloatSize(final int fieldNumber, final float value) { | |
438 | + return computeTagSize(fieldNumber) + computeFloatSizeNoTag(value); | |
439 | + } | |
440 | + | |
441 | + /** | |
442 | + * Compute the number of bytes that would be needed to encode a | |
443 | + * {@code uint64} field, including tag. | |
444 | + */ | |
445 | + public static int computeUInt64Size(final int fieldNumber, final long value) { | |
446 | + return computeTagSize(fieldNumber) + computeUInt64SizeNoTag(value); | |
447 | + } | |
448 | + | |
449 | + /** | |
450 | + * Compute the number of bytes that would be needed to encode an | |
451 | + * {@code int64} field, including tag. | |
452 | + */ | |
453 | + public static int computeInt64Size(final int fieldNumber, final long value) { | |
454 | + return computeTagSize(fieldNumber) + computeInt64SizeNoTag(value); | |
455 | + } | |
456 | + | |
457 | + /** | |
458 | + * Compute the number of bytes that would be needed to encode an | |
459 | + * {@code int32} field, including tag. | |
460 | + */ | |
461 | + public static int computeInt32Size(final int fieldNumber, final int value) { | |
462 | + return computeTagSize(fieldNumber) + computeInt32SizeNoTag(value); | |
463 | + } | |
464 | + | |
465 | + /** | |
466 | + * Compute the number of bytes that would be needed to encode a | |
467 | + * {@code fixed64} field, including tag. | |
468 | + */ | |
469 | + public static int computeFixed64Size(final int fieldNumber, | |
470 | + final long value) { | |
471 | + return computeTagSize(fieldNumber) + computeFixed64SizeNoTag(value); | |
472 | + } | |
473 | + | |
474 | + /** | |
475 | + * Compute the number of bytes that would be needed to encode a | |
476 | + * {@code fixed32} field, including tag. | |
477 | + */ | |
478 | + public static int computeFixed32Size(final int fieldNumber, | |
479 | + final int value) { | |
480 | + return computeTagSize(fieldNumber) + computeFixed32SizeNoTag(value); | |
481 | + } | |
482 | + | |
483 | + /** | |
484 | + * Compute the number of bytes that would be needed to encode a | |
485 | + * {@code bool} field, including tag. | |
486 | + */ | |
487 | + public static int computeBoolSize(final int fieldNumber, | |
488 | + final boolean value) { | |
489 | + return computeTagSize(fieldNumber) + computeBoolSizeNoTag(value); | |
490 | + } | |
491 | + | |
492 | + /** | |
493 | + * Compute the number of bytes that would be needed to encode a | |
494 | + * {@code string} field, including tag. | |
495 | + */ | |
496 | + public static int computeStringSize(final int fieldNumber, | |
497 | + final String value) { | |
498 | + return computeTagSize(fieldNumber) + computeStringSizeNoTag(value); | |
499 | + } | |
500 | + | |
501 | + /** | |
502 | + * Compute the number of bytes that would be needed to encode a | |
503 | + * {@code group} field, including tag. | |
504 | + */ | |
505 | + public static int computeGroupSize(final int fieldNumber, | |
506 | + final MessageLite value) { | |
507 | + return computeTagSize(fieldNumber) * 2 + computeGroupSizeNoTag(value); | |
508 | + } | |
509 | + | |
510 | + /** | |
511 | + * Compute the number of bytes that would be needed to encode a | |
512 | + * {@code group} field represented by an {@code UnknownFieldSet}, including | |
513 | + * tag. | |
514 | + * | |
515 | + * @deprecated UnknownFieldSet now implements MessageLite, so you can just | |
516 | + * call {@link #computeGroupSize}. | |
517 | + */ | |
518 | + @Deprecated | |
519 | + public static int computeUnknownGroupSize(final int fieldNumber, | |
520 | + final MessageLite value) { | |
521 | + return computeGroupSize(fieldNumber, value); | |
522 | + } | |
523 | + | |
524 | + /** | |
525 | + * Compute the number of bytes that would be needed to encode an | |
526 | + * embedded message field, including tag. | |
527 | + */ | |
528 | + public static int computeMessageSize(final int fieldNumber, | |
529 | + final MessageLite value) { | |
530 | + return computeTagSize(fieldNumber) + computeMessageSizeNoTag(value); | |
531 | + } | |
532 | + | |
533 | + /** | |
534 | + * Compute the number of bytes that would be needed to encode a | |
535 | + * {@code bytes} field, including tag. | |
536 | + */ | |
537 | + public static int computeBytesSize(final int fieldNumber, | |
538 | + final ByteString value) { | |
539 | + return computeTagSize(fieldNumber) + computeBytesSizeNoTag(value); | |
540 | + } | |
541 | + | |
542 | + /** | |
543 | + * Compute the number of bytes that would be needed to encode a | |
544 | + * {@code uint32} field, including tag. | |
545 | + */ | |
546 | + public static int computeUInt32Size(final int fieldNumber, final int value) { | |
547 | + return computeTagSize(fieldNumber) + computeUInt32SizeNoTag(value); | |
548 | + } | |
549 | + | |
550 | + /** | |
551 | + * Compute the number of bytes that would be needed to encode an | |
552 | + * enum field, including tag. Caller is responsible for converting the | |
553 | + * enum value to its numeric value. | |
554 | + */ | |
555 | + public static int computeEnumSize(final int fieldNumber, final int value) { | |
556 | + return computeTagSize(fieldNumber) + computeEnumSizeNoTag(value); | |
557 | + } | |
558 | + | |
559 | + /** | |
560 | + * Compute the number of bytes that would be needed to encode an | |
561 | + * {@code sfixed32} field, including tag. | |
562 | + */ | |
563 | + public static int computeSFixed32Size(final int fieldNumber, | |
564 | + final int value) { | |
565 | + return computeTagSize(fieldNumber) + computeSFixed32SizeNoTag(value); | |
566 | + } | |
567 | + | |
568 | + /** | |
569 | + * Compute the number of bytes that would be needed to encode an | |
570 | + * {@code sfixed64} field, including tag. | |
571 | + */ | |
572 | + public static int computeSFixed64Size(final int fieldNumber, | |
573 | + final long value) { | |
574 | + return computeTagSize(fieldNumber) + computeSFixed64SizeNoTag(value); | |
575 | + } | |
576 | + | |
577 | + /** | |
578 | + * Compute the number of bytes that would be needed to encode an | |
579 | + * {@code sint32} field, including tag. | |
580 | + */ | |
581 | + public static int computeSInt32Size(final int fieldNumber, final int value) { | |
582 | + return computeTagSize(fieldNumber) + computeSInt32SizeNoTag(value); | |
583 | + } | |
584 | + | |
585 | + /** | |
586 | + * Compute the number of bytes that would be needed to encode an | |
587 | + * {@code sint64} field, including tag. | |
588 | + */ | |
589 | + public static int computeSInt64Size(final int fieldNumber, final long value) { | |
590 | + return computeTagSize(fieldNumber) + computeSInt64SizeNoTag(value); | |
591 | + } | |
592 | + | |
593 | + /** | |
594 | + * Compute the number of bytes that would be needed to encode a | |
595 | + * MessageSet extension to the stream. For historical reasons, | |
596 | + * the wire format differs from normal fields. | |
597 | + */ | |
598 | + public static int computeMessageSetExtensionSize( | |
599 | + final int fieldNumber, final MessageLite value) { | |
600 | + return computeTagSize(WireFormat.MESSAGE_SET_ITEM) * 2 + | |
601 | + computeUInt32Size(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber) + | |
602 | + computeMessageSize(WireFormat.MESSAGE_SET_MESSAGE, value); | |
603 | + } | |
604 | + | |
605 | + /** | |
606 | + * Compute the number of bytes that would be needed to encode an | |
607 | + * unparsed MessageSet extension field to the stream. For | |
608 | + * historical reasons, the wire format differs from normal fields. | |
609 | + */ | |
610 | + public static int computeRawMessageSetExtensionSize( | |
611 | + final int fieldNumber, final ByteString value) { | |
612 | + return computeTagSize(WireFormat.MESSAGE_SET_ITEM) * 2 + | |
613 | + computeUInt32Size(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber) + | |
614 | + computeBytesSize(WireFormat.MESSAGE_SET_MESSAGE, value); | |
615 | + } | |
616 | + | |
617 | + // ----------------------------------------------------------------- | |
618 | + | |
619 | + /** | |
620 | + * Compute the number of bytes that would be needed to encode a | |
621 | + * {@code double} field, including tag. | |
622 | + */ | |
623 | + public static int computeDoubleSizeNoTag(final double value) { | |
624 | + return LITTLE_ENDIAN_64_SIZE; | |
625 | + } | |
626 | + | |
627 | + /** | |
628 | + * Compute the number of bytes that would be needed to encode a | |
629 | + * {@code float} field, including tag. | |
630 | + */ | |
631 | + public static int computeFloatSizeNoTag(final float value) { | |
632 | + return LITTLE_ENDIAN_32_SIZE; | |
633 | + } | |
634 | + | |
635 | + /** | |
636 | + * Compute the number of bytes that would be needed to encode a | |
637 | + * {@code uint64} field, including tag. | |
638 | + */ | |
639 | + public static int computeUInt64SizeNoTag(final long value) { | |
640 | + return computeRawVarint64Size(value); | |
641 | + } | |
642 | + | |
643 | + /** | |
644 | + * Compute the number of bytes that would be needed to encode an | |
645 | + * {@code int64} field, including tag. | |
646 | + */ | |
647 | + public static int computeInt64SizeNoTag(final long value) { | |
648 | + return computeRawVarint64Size(value); | |
649 | + } | |
650 | + | |
651 | + /** | |
652 | + * Compute the number of bytes that would be needed to encode an | |
653 | + * {@code int32} field, including tag. | |
654 | + */ | |
655 | + public static int computeInt32SizeNoTag(final int value) { | |
656 | + if (value >= 0) { | |
657 | + return computeRawVarint32Size(value); | |
658 | + } else { | |
659 | + // Must sign-extend. | |
660 | + return 10; | |
661 | + } | |
662 | + } | |
663 | + | |
664 | + /** | |
665 | + * Compute the number of bytes that would be needed to encode a | |
666 | + * {@code fixed64} field. | |
667 | + */ | |
668 | + public static int computeFixed64SizeNoTag(final long value) { | |
669 | + return LITTLE_ENDIAN_64_SIZE; | |
670 | + } | |
671 | + | |
672 | + /** | |
673 | + * Compute the number of bytes that would be needed to encode a | |
674 | + * {@code fixed32} field. | |
675 | + */ | |
676 | + public static int computeFixed32SizeNoTag(final int value) { | |
677 | + return LITTLE_ENDIAN_32_SIZE; | |
678 | + } | |
679 | + | |
680 | + /** | |
681 | + * Compute the number of bytes that would be needed to encode a | |
682 | + * {@code bool} field. | |
683 | + */ | |
684 | + public static int computeBoolSizeNoTag(final boolean value) { | |
685 | + return 1; | |
686 | + } | |
687 | + | |
688 | + /** | |
689 | + * Compute the number of bytes that would be needed to encode a | |
690 | + * {@code string} field. | |
691 | + */ | |
692 | + public static int computeStringSizeNoTag(final String value) { | |
693 | + try { | |
694 | + final byte[] bytes = value.getBytes("UTF-8"); | |
695 | + return computeRawVarint32Size(bytes.length) + | |
696 | + bytes.length; | |
697 | + } catch (UnsupportedEncodingException e) { | |
698 | + throw new RuntimeException("UTF-8 not supported.", e); | |
699 | + } | |
700 | + } | |
701 | + | |
702 | + /** | |
703 | + * Compute the number of bytes that would be needed to encode a | |
704 | + * {@code group} field. | |
705 | + */ | |
706 | + public static int computeGroupSizeNoTag(final MessageLite value) { | |
707 | + return value.getSerializedSize(); | |
708 | + } | |
709 | + | |
710 | + /** | |
711 | + * Compute the number of bytes that would be needed to encode a | |
712 | + * {@code group} field represented by an {@code UnknownFieldSet}, including | |
713 | + * tag. | |
714 | + * | |
715 | + * @deprecated UnknownFieldSet now implements MessageLite, so you can just | |
716 | + * call {@link #computeUnknownGroupSizeNoTag}. | |
717 | + */ | |
718 | + @Deprecated | |
719 | + public static int computeUnknownGroupSizeNoTag(final MessageLite value) { | |
720 | + return computeGroupSizeNoTag(value); | |
721 | + } | |
722 | + | |
723 | + /** | |
724 | + * Compute the number of bytes that would be needed to encode an embedded | |
725 | + * message field. | |
726 | + */ | |
727 | + public static int computeMessageSizeNoTag(final MessageLite value) { | |
728 | + final int size = value.getSerializedSize(); | |
729 | + return computeRawVarint32Size(size) + size; | |
730 | + } | |
731 | + | |
732 | + /** | |
733 | + * Compute the number of bytes that would be needed to encode a | |
734 | + * {@code bytes} field. | |
735 | + */ | |
736 | + public static int computeBytesSizeNoTag(final ByteString value) { | |
737 | + return computeRawVarint32Size(value.size()) + | |
738 | + value.size(); | |
739 | + } | |
740 | + | |
741 | + /** | |
742 | + * Compute the number of bytes that would be needed to encode a | |
743 | + * {@code uint32} field. | |
744 | + */ | |
745 | + public static int computeUInt32SizeNoTag(final int value) { | |
746 | + return computeRawVarint32Size(value); | |
747 | + } | |
748 | + | |
749 | + /** | |
750 | + * Compute the number of bytes that would be needed to encode an enum field. | |
751 | + * Caller is responsible for converting the enum value to its numeric value. | |
752 | + */ | |
753 | + public static int computeEnumSizeNoTag(final int value) { | |
754 | + return computeInt32SizeNoTag(value); | |
755 | + } | |
756 | + | |
757 | + /** | |
758 | + * Compute the number of bytes that would be needed to encode an | |
759 | + * {@code sfixed32} field. | |
760 | + */ | |
761 | + public static int computeSFixed32SizeNoTag(final int value) { | |
762 | + return LITTLE_ENDIAN_32_SIZE; | |
763 | + } | |
764 | + | |
765 | + /** | |
766 | + * Compute the number of bytes that would be needed to encode an | |
767 | + * {@code sfixed64} field. | |
768 | + */ | |
769 | + public static int computeSFixed64SizeNoTag(final long value) { | |
770 | + return LITTLE_ENDIAN_64_SIZE; | |
771 | + } | |
772 | + | |
773 | + /** | |
774 | + * Compute the number of bytes that would be needed to encode an | |
775 | + * {@code sint32} field. | |
776 | + */ | |
777 | + public static int computeSInt32SizeNoTag(final int value) { | |
778 | + return computeRawVarint32Size(encodeZigZag32(value)); | |
779 | + } | |
780 | + | |
781 | + /** | |
782 | + * Compute the number of bytes that would be needed to encode an | |
783 | + * {@code sint64} field. | |
784 | + */ | |
785 | + public static int computeSInt64SizeNoTag(final long value) { | |
786 | + return computeRawVarint64Size(encodeZigZag64(value)); | |
787 | + } | |
788 | + | |
789 | + // ================================================================= | |
790 | + | |
791 | + /** | |
792 | + * Internal helper that writes the current buffer to the output. The | |
793 | + * buffer position is reset to its initial value when this returns. | |
794 | + */ | |
795 | + private void refreshBuffer() throws IOException { | |
796 | + if (output == null) { | |
797 | + // We're writing to a single buffer. | |
798 | + throw new OutOfSpaceException(); | |
799 | + } | |
800 | + | |
801 | + // Since we have an output stream, this is our buffer | |
802 | + // and buffer offset == 0 | |
803 | + output.write(buffer, 0, position); | |
804 | + position = 0; | |
805 | + } | |
806 | + | |
807 | + /** | |
808 | + * Flushes the stream and forces any buffered bytes to be written. This | |
809 | + * does not flush the underlying OutputStream. | |
810 | + */ | |
811 | + public void flush() throws IOException { | |
812 | + if (output != null) { | |
813 | + refreshBuffer(); | |
814 | + } | |
815 | + } | |
816 | + | |
817 | + /** | |
818 | + * If writing to a flat array, return the space left in the array. | |
819 | + * Otherwise, throws {@code UnsupportedOperationException}. | |
820 | + */ | |
821 | + public int spaceLeft() { | |
822 | + if (output == null) { | |
823 | + return limit - position; | |
824 | + } else { | |
825 | + throw new UnsupportedOperationException( | |
826 | + "spaceLeft() can only be called on CodedOutputStreams that are " + | |
827 | + "writing to a flat array."); | |
828 | + } | |
829 | + } | |
830 | + | |
831 | + /** | |
832 | + * Verifies that {@link #spaceLeft()} returns zero. It's common to create | |
833 | + * a byte array that is exactly big enough to hold a message, then write to | |
834 | + * it with a {@code CodedOutputStream}. Calling {@code checkNoSpaceLeft()} | |
835 | + * after writing verifies that the message was actually as big as expected, | |
836 | + * which can help catch bugs. | |
837 | + */ | |
838 | + public void checkNoSpaceLeft() { | |
839 | + if (spaceLeft() != 0) { | |
840 | + throw new IllegalStateException( | |
841 | + "Did not write as much data as expected."); | |
842 | + } | |
843 | + } | |
844 | + | |
845 | + /** | |
846 | + * If you create a CodedOutputStream around a simple flat array, you must | |
847 | + * not attempt to write more bytes than the array has space. Otherwise, | |
848 | + * this exception will be thrown. | |
849 | + */ | |
850 | + public static class OutOfSpaceException extends IOException { | |
851 | + private static final long serialVersionUID = -6947486886997889499L; | |
852 | + | |
853 | + OutOfSpaceException() { | |
854 | + super("CodedOutputStream was writing to a flat byte array and ran " + | |
855 | + "out of space."); | |
856 | + } | |
857 | + } | |
858 | + | |
859 | + /** Write a single byte. */ | |
860 | + public void writeRawByte(final byte value) throws IOException { | |
861 | + if (position == limit) { | |
862 | + refreshBuffer(); | |
863 | + } | |
864 | + | |
865 | + buffer[position++] = value; | |
866 | + } | |
867 | + | |
868 | + /** Write a single byte, represented by an integer value. */ | |
869 | + public void writeRawByte(final int value) throws IOException { | |
870 | + writeRawByte((byte) value); | |
871 | + } | |
872 | + | |
873 | + /** Write a byte string. */ | |
874 | + public void writeRawBytes(final ByteString value) throws IOException { | |
875 | + writeRawBytes(value, 0, value.size()); | |
876 | + } | |
877 | + | |
878 | + /** Write an array of bytes. */ | |
879 | + public void writeRawBytes(final byte[] value) throws IOException { | |
880 | + writeRawBytes(value, 0, value.length); | |
881 | + } | |
882 | + | |
883 | + /** Write part of an array of bytes. */ | |
884 | + public void writeRawBytes(final byte[] value, int offset, int length) | |
885 | + throws IOException { | |
886 | + if (limit - position >= length) { | |
887 | + // We have room in the current buffer. | |
888 | + System.arraycopy(value, offset, buffer, position, length); | |
889 | + position += length; | |
890 | + } else { | |
891 | + // Write extends past current buffer. Fill the rest of this buffer and | |
892 | + // flush. | |
893 | + final int bytesWritten = limit - position; | |
894 | + System.arraycopy(value, offset, buffer, position, bytesWritten); | |
895 | + offset += bytesWritten; | |
896 | + length -= bytesWritten; | |
897 | + position = limit; | |
898 | + refreshBuffer(); | |
899 | + | |
900 | + // Now deal with the rest. | |
901 | + // Since we have an output stream, this is our buffer | |
902 | + // and buffer offset == 0 | |
903 | + if (length <= limit) { | |
904 | + // Fits in new buffer. | |
905 | + System.arraycopy(value, offset, buffer, 0, length); | |
906 | + position = length; | |
907 | + } else { | |
908 | + // Write is very big. Let's do it all at once. | |
909 | + output.write(value, offset, length); | |
910 | + } | |
911 | + } | |
912 | + } | |
913 | + | |
914 | + /** Write part of a byte string. */ | |
915 | + public void writeRawBytes(final ByteString value, int offset, int length) | |
916 | + throws IOException { | |
917 | + if (limit - position >= length) { | |
918 | + // We have room in the current buffer. | |
919 | + value.copyTo(buffer, offset, position, length); | |
920 | + position += length; | |
921 | + } else { | |
922 | + // Write extends past current buffer. Fill the rest of this buffer and | |
923 | + // flush. | |
924 | + final int bytesWritten = limit - position; | |
925 | + value.copyTo(buffer, offset, position, bytesWritten); | |
926 | + offset += bytesWritten; | |
927 | + length -= bytesWritten; | |
928 | + position = limit; | |
929 | + refreshBuffer(); | |
930 | + | |
931 | + // Now deal with the rest. | |
932 | + // Since we have an output stream, this is our buffer | |
933 | + // and buffer offset == 0 | |
934 | + if (length <= limit) { | |
935 | + // Fits in new buffer. | |
936 | + value.copyTo(buffer, offset, 0, length); | |
937 | + position = length; | |
938 | + } else { | |
939 | + // Write is very big, but we can't do it all at once without allocating | |
940 | + // an a copy of the byte array since ByteString does not give us access | |
941 | + // to the underlying bytes. Use the InputStream interface on the | |
942 | + // ByteString and our buffer to copy between the two. | |
943 | + InputStream inputStreamFrom = value.newInput(); | |
944 | + if (offset != inputStreamFrom.skip(offset)) { | |
945 | + throw new IllegalStateException("Skip failed? Should never happen."); | |
946 | + } | |
947 | + // Use the buffer as the temporary buffer to avoid allocating memory. | |
948 | + while (length > 0) { | |
949 | + int bytesToRead = Math.min(length, limit); | |
950 | + int bytesRead = inputStreamFrom.read(buffer, 0, bytesToRead); | |
951 | + if (bytesRead != bytesToRead) { | |
952 | + throw new IllegalStateException("Read failed? Should never happen"); | |
953 | + } | |
954 | + output.write(buffer, 0, bytesRead); | |
955 | + length -= bytesRead; | |
956 | + } | |
957 | + } | |
958 | + } | |
959 | + } | |
960 | + | |
961 | + /** Encode and write a tag. */ | |
962 | + public void writeTag(final int fieldNumber, final int wireType) | |
963 | + throws IOException { | |
964 | + writeRawVarint32(WireFormat.makeTag(fieldNumber, wireType)); | |
965 | + } | |
966 | + | |
967 | + /** Compute the number of bytes that would be needed to encode a tag. */ | |
968 | + public static int computeTagSize(final int fieldNumber) { | |
969 | + return computeRawVarint32Size(WireFormat.makeTag(fieldNumber, 0)); | |
970 | + } | |
971 | + | |
972 | + /** | |
973 | + * Encode and write a varint. {@code value} is treated as | |
974 | + * unsigned, so it won't be sign-extended if negative. | |
975 | + */ | |
976 | + public void writeRawVarint32(int value) throws IOException { | |
977 | + while (true) { | |
978 | + if ((value & ~0x7F) == 0) { | |
979 | + writeRawByte(value); | |
980 | + return; | |
981 | + } else { | |
982 | + writeRawByte((value & 0x7F) | 0x80); | |
983 | + value >>>= 7; | |
984 | + } | |
985 | + } | |
986 | + } | |
987 | + | |
988 | + /** | |
989 | + * Compute the number of bytes that would be needed to encode a varint. | |
990 | + * {@code value} is treated as unsigned, so it won't be sign-extended if | |
991 | + * negative. | |
992 | + */ | |
993 | + public static int computeRawVarint32Size(final int value) { | |
994 | + if ((value & (0xffffffff << 7)) == 0) return 1; | |
995 | + if ((value & (0xffffffff << 14)) == 0) return 2; | |
996 | + if ((value & (0xffffffff << 21)) == 0) return 3; | |
997 | + if ((value & (0xffffffff << 28)) == 0) return 4; | |
998 | + return 5; | |
999 | + } | |
1000 | + | |
1001 | + /** Encode and write a varint. */ | |
1002 | + public void writeRawVarint64(long value) throws IOException { | |
1003 | + while (true) { | |
1004 | + if ((value & ~0x7FL) == 0) { | |
1005 | + writeRawByte((int)value); | |
1006 | + return; | |
1007 | + } else { | |
1008 | + writeRawByte(((int)value & 0x7F) | 0x80); | |
1009 | + value >>>= 7; | |
1010 | + } | |
1011 | + } | |
1012 | + } | |
1013 | + | |
1014 | + /** Compute the number of bytes that would be needed to encode a varint. */ | |
1015 | + public static int computeRawVarint64Size(final long value) { | |
1016 | + if ((value & (0xffffffffffffffffL << 7)) == 0) return 1; | |
1017 | + if ((value & (0xffffffffffffffffL << 14)) == 0) return 2; | |
1018 | + if ((value & (0xffffffffffffffffL << 21)) == 0) return 3; | |
1019 | + if ((value & (0xffffffffffffffffL << 28)) == 0) return 4; | |
1020 | + if ((value & (0xffffffffffffffffL << 35)) == 0) return 5; | |
1021 | + if ((value & (0xffffffffffffffffL << 42)) == 0) return 6; | |
1022 | + if ((value & (0xffffffffffffffffL << 49)) == 0) return 7; | |
1023 | + if ((value & (0xffffffffffffffffL << 56)) == 0) return 8; | |
1024 | + if ((value & (0xffffffffffffffffL << 63)) == 0) return 9; | |
1025 | + return 10; | |
1026 | + } | |
1027 | + | |
1028 | + /** Write a little-endian 32-bit integer. */ | |
1029 | + public void writeRawLittleEndian32(final int value) throws IOException { | |
1030 | + writeRawByte((value ) & 0xFF); | |
1031 | + writeRawByte((value >> 8) & 0xFF); | |
1032 | + writeRawByte((value >> 16) & 0xFF); | |
1033 | + writeRawByte((value >> 24) & 0xFF); | |
1034 | + } | |
1035 | + | |
1036 | + public static final int LITTLE_ENDIAN_32_SIZE = 4; | |
1037 | + | |
1038 | + /** Write a little-endian 64-bit integer. */ | |
1039 | + public void writeRawLittleEndian64(final long value) throws IOException { | |
1040 | + writeRawByte((int)(value ) & 0xFF); | |
1041 | + writeRawByte((int)(value >> 8) & 0xFF); | |
1042 | + writeRawByte((int)(value >> 16) & 0xFF); | |
1043 | + writeRawByte((int)(value >> 24) & 0xFF); | |
1044 | + writeRawByte((int)(value >> 32) & 0xFF); | |
1045 | + writeRawByte((int)(value >> 40) & 0xFF); | |
1046 | + writeRawByte((int)(value >> 48) & 0xFF); | |
1047 | + writeRawByte((int)(value >> 56) & 0xFF); | |
1048 | + } | |
1049 | + | |
1050 | + public static final int LITTLE_ENDIAN_64_SIZE = 8; | |
1051 | + | |
1052 | + /** | |
1053 | + * Encode a ZigZag-encoded 32-bit value. ZigZag encodes signed integers | |
1054 | + * into values that can be efficiently encoded with varint. (Otherwise, | |
1055 | + * negative values must be sign-extended to 64 bits to be varint encoded, | |
1056 | + * thus always taking 10 bytes on the wire.) | |
1057 | + * | |
1058 | + * @param n A signed 32-bit integer. | |
1059 | + * @return An unsigned 32-bit integer, stored in a signed int because | |
1060 | + * Java has no explicit unsigned support. | |
1061 | + */ | |
1062 | + public static int encodeZigZag32(final int n) { | |
1063 | + // Note: the right-shift must be arithmetic | |
1064 | + return (n << 1) ^ (n >> 31); | |
1065 | + } | |
1066 | + | |
1067 | + /** | |
1068 | + * Encode a ZigZag-encoded 64-bit value. ZigZag encodes signed integers | |
1069 | + * into values that can be efficiently encoded with varint. (Otherwise, | |
1070 | + * negative values must be sign-extended to 64 bits to be varint encoded, | |
1071 | + * thus always taking 10 bytes on the wire.) | |
1072 | + * | |
1073 | + * @param n A signed 64-bit integer. | |
1074 | + * @return An unsigned 64-bit integer, stored in a signed int because | |
1075 | + * Java has no explicit unsigned support. | |
1076 | + */ | |
1077 | + public static long encodeZigZag64(final long n) { | |
1078 | + // Note: the right-shift must be arithmetic | |
1079 | + return (n << 1) ^ (n >> 63); | |
1080 | + } | |
1081 | +} |
@@ -0,0 +1,1887 @@ | ||
1 | +// Protocol Buffers - Google's data interchange format | |
2 | +// Copyright 2008 Google Inc. All rights reserved. | |
3 | +// http://code.google.com/p/protobuf/ | |
4 | +// | |
5 | +// Redistribution and use in source and binary forms, with or without | |
6 | +// modification, are permitted provided that the following conditions are | |
7 | +// met: | |
8 | +// | |
9 | +// * Redistributions of source code must retain the above copyright | |
10 | +// notice, this list of conditions and the following disclaimer. | |
11 | +// * Redistributions in binary form must reproduce the above | |
12 | +// copyright notice, this list of conditions and the following disclaimer | |
13 | +// in the documentation and/or other materials provided with the | |
14 | +// distribution. | |
15 | +// * Neither the name of Google Inc. nor the names of its | |
16 | +// contributors may be used to endorse or promote products derived from | |
17 | +// this software without specific prior written permission. | |
18 | +// | |
19 | +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
20 | +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
21 | +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
22 | +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
23 | +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
24 | +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
25 | +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
26 | +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
27 | +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
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. | |
30 | + | |
31 | +package com.google.protobuf; | |
32 | + | |
33 | +import com.google.protobuf.DescriptorProtos.*; | |
34 | + | |
35 | +import java.util.Arrays; | |
36 | +import java.util.Collections; | |
37 | +import java.util.HashMap; | |
38 | +import java.util.List; | |
39 | +import java.util.Map; | |
40 | +import java.io.UnsupportedEncodingException; | |
41 | + | |
42 | +/** | |
43 | + * Contains a collection of classes which describe protocol message types. | |
44 | + * | |
45 | + * Every message type has a {@link Descriptor}, which lists all | |
46 | + * its fields and other information about a type. You can get a message | |
47 | + * type's descriptor by calling {@code MessageType.getDescriptor()}, or | |
48 | + * (given a message object of the type) {@code message.getDescriptorForType()}. | |
49 | + * Furthermore, each message is associated with a {@link FileDescriptor} for | |
50 | + * a relevant {@code .proto} file. You can obtain it by calling | |
51 | + * {@code Descriptor.getFile()}. A {@link FileDescriptor} contains descriptors | |
52 | + * for all the messages defined in that file, and file descriptors for all the | |
53 | + * imported {@code .proto} files. | |
54 | + * | |
55 | + * Descriptors are built from DescriptorProtos, as defined in | |
56 | + * {@code google/protobuf/descriptor.proto}. | |
57 | + * | |
58 | + * @author kenton@google.com Kenton Varda | |
59 | + */ | |
60 | +public final class Descriptors { | |
61 | + /** | |
62 | + * Describes a {@code .proto} file, including everything defined within. | |
63 | + * That includes, in particular, descriptors for all the messages and | |
64 | + * file descriptors for all other imported {@code .proto} files | |
65 | + * (dependencies). | |
66 | + */ | |
67 | + public static final class FileDescriptor { | |
68 | + /** Convert the descriptor to its protocol message representation. */ | |
69 | + public FileDescriptorProto toProto() { return proto; } | |
70 | + | |
71 | + /** Get the file name. */ | |
72 | + public String getName() { return proto.getName(); } | |
73 | + | |
74 | + /** | |
75 | + * Get the proto package name. This is the package name given by the | |
76 | + * {@code package} statement in the {@code .proto} file, which differs | |
77 | + * from the Java package. | |
78 | + */ | |
79 | + public String getPackage() { return proto.getPackage(); } | |
80 | + | |
81 | + /** Get the {@code FileOptions}, defined in {@code descriptor.proto}. */ | |
82 | + public FileOptions getOptions() { return proto.getOptions(); } | |
83 | + | |
84 | + /** Get a list of top-level message types declared in this file. */ | |
85 | + public List<Descriptor> getMessageTypes() { | |
86 | + return Collections.unmodifiableList(Arrays.asList(messageTypes)); | |
87 | + } | |
88 | + | |
89 | + /** Get a list of top-level enum types declared in this file. */ | |
90 | + public List<EnumDescriptor> getEnumTypes() { | |
91 | + return Collections.unmodifiableList(Arrays.asList(enumTypes)); | |
92 | + } | |
93 | + | |
94 | + /** Get a list of top-level services declared in this file. */ | |
95 | + public List<ServiceDescriptor> getServices() { | |
96 | + return Collections.unmodifiableList(Arrays.asList(services)); | |
97 | + } | |
98 | + | |
99 | + /** Get a list of top-level extensions declared in this file. */ | |
100 | + public List<FieldDescriptor> getExtensions() { | |
101 | + return Collections.unmodifiableList(Arrays.asList(extensions)); | |
102 | + } | |
103 | + | |
104 | + /** Get a list of this file's dependencies (imports). */ | |
105 | + public List<FileDescriptor> getDependencies() { | |
106 | + return Collections.unmodifiableList(Arrays.asList(dependencies)); | |
107 | + } | |
108 | + | |
109 | + /** | |
110 | + * Find a message type in the file by name. Does not find nested types. | |
111 | + * | |
112 | + * @param name The unqualified type name to look for. | |
113 | + * @return The message type's descriptor, or {@code null} if not found. | |
114 | + */ | |
115 | + public Descriptor findMessageTypeByName(String name) { | |
116 | + // Don't allow looking up nested types. This will make optimization | |
117 | + // easier later. | |
118 | + if (name.indexOf('.') != -1) { | |
119 | + return null; | |
120 | + } | |
121 | + if (getPackage().length() > 0) { | |
122 | + name = getPackage() + '.' + name; | |
123 | + } | |
124 | + final GenericDescriptor result = pool.findSymbol(name); | |
125 | + if (result != null && result instanceof Descriptor && | |
126 | + result.getFile() == this) { | |
127 | + return (Descriptor)result; | |
128 | + } else { | |
129 | + return null; | |
130 | + } | |
131 | + } | |
132 | + | |
133 | + /** | |
134 | + * Find an enum type in the file by name. Does not find nested types. | |
135 | + * | |
136 | + * @param name The unqualified type name to look for. | |
137 | + * @return The enum type's descriptor, or {@code null} if not found. | |
138 | + */ | |
139 | + public EnumDescriptor findEnumTypeByName(String name) { | |
140 | + // Don't allow looking up nested types. This will make optimization | |
141 | + // easier later. | |
142 | + if (name.indexOf('.') != -1) { | |
143 | + return null; | |
144 | + } | |
145 | + if (getPackage().length() > 0) { | |
146 | + name = getPackage() + '.' + name; | |
147 | + } | |
148 | + final GenericDescriptor result = pool.findSymbol(name); | |
149 | + if (result != null && result instanceof EnumDescriptor && | |
150 | + result.getFile() == this) { | |
151 | + return (EnumDescriptor)result; | |
152 | + } else { | |
153 | + return null; | |
154 | + } | |
155 | + } | |
156 | + | |
157 | + /** | |
158 | + * Find a service type in the file by name. | |
159 | + * | |
160 | + * @param name The unqualified type name to look for. | |
161 | + * @return The service type's descriptor, or {@code null} if not found. | |
162 | + */ | |
163 | + public ServiceDescriptor findServiceByName(String name) { | |
164 | + // Don't allow looking up nested types. This will make optimization | |
165 | + // easier later. | |
166 | + if (name.indexOf('.') != -1) { | |
167 | + return null; | |
168 | + } | |
169 | + if (getPackage().length() > 0) { | |
170 | + name = getPackage() + '.' + name; | |
171 | + } | |
172 | + final GenericDescriptor result = pool.findSymbol(name); | |
173 | + if (result != null && result instanceof ServiceDescriptor && | |
174 | + result.getFile() == this) { | |
175 | + return (ServiceDescriptor)result; | |
176 | + } else { | |
177 | + return null; | |
178 | + } | |
179 | + } | |
180 | + | |
181 | + /** | |
182 | + * Find an extension in the file by name. Does not find extensions nested | |
183 | + * inside message types. | |
184 | + * | |
185 | + * @param name The unqualified extension name to look for. | |
186 | + * @return The extension's descriptor, or {@code null} if not found. | |
187 | + */ | |
188 | + public FieldDescriptor findExtensionByName(String name) { | |
189 | + if (name.indexOf('.') != -1) { | |
190 | + return null; | |
191 | + } | |
192 | + if (getPackage().length() > 0) { | |
193 | + name = getPackage() + '.' + name; | |
194 | + } | |
195 | + final GenericDescriptor result = pool.findSymbol(name); | |
196 | + if (result != null && result instanceof FieldDescriptor && | |
197 | + result.getFile() == this) { | |
198 | + return (FieldDescriptor)result; | |
199 | + } else { | |
200 | + return null; | |
201 | + } | |
202 | + } | |
203 | + | |
204 | + /** | |
205 | + * Construct a {@code FileDescriptor}. | |
206 | + * | |
207 | + * @param proto The protocol message form of the FileDescriptor. | |
208 | + * @param dependencies {@code FileDescriptor}s corresponding to all of | |
209 | + * the file's dependencies, in the exact order listed | |
210 | + * in {@code proto}. | |
211 | + * @throws DescriptorValidationException {@code proto} is not a valid | |
212 | + * descriptor. This can occur for a number of reasons, e.g. | |
213 | + * because a field has an undefined type or because two messages | |
214 | + * were defined with the same name. | |
215 | + */ | |
216 | + public static FileDescriptor buildFrom(final FileDescriptorProto proto, | |
217 | + final FileDescriptor[] dependencies) | |
218 | + throws DescriptorValidationException { | |
219 | + // Building decsriptors involves two steps: translating and linking. | |
220 | + // In the translation step (implemented by FileDescriptor's | |
221 | + // constructor), we build an object tree mirroring the | |
222 | + // FileDescriptorProto's tree and put all of the descriptors into the | |
223 | + // DescriptorPool's lookup tables. In the linking step, we look up all | |
224 | + // type references in the DescriptorPool, so that, for example, a | |
225 | + // FieldDescriptor for an embedded message contains a pointer directly | |
226 | + // to the Descriptor for that message's type. We also detect undefined | |
227 | + // types in the linking step. | |
228 | + final DescriptorPool pool = new DescriptorPool(dependencies); | |
229 | + final FileDescriptor result = | |
230 | + new FileDescriptor(proto, dependencies, pool); | |
231 | + | |
232 | + if (dependencies.length != proto.getDependencyCount()) { | |
233 | + throw new DescriptorValidationException(result, | |
234 | + "Dependencies passed to FileDescriptor.buildFrom() don't match " + | |
235 | + "those listed in the FileDescriptorProto."); | |
236 | + } | |
237 | + for (int i = 0; i < proto.getDependencyCount(); i++) { | |
238 | + if (!dependencies[i].getName().equals(proto.getDependency(i))) { | |
239 | + throw new DescriptorValidationException(result, | |
240 | + "Dependencies passed to FileDescriptor.buildFrom() don't match " + | |
241 | + "those listed in the FileDescriptorProto."); | |
242 | + } | |
243 | + } | |
244 | + | |
245 | + result.crossLink(); | |
246 | + return result; | |
247 | + } | |
248 | + | |
249 | + /** | |
250 | + * This method is to be called by generated code only. It is equivalent | |
251 | + * to {@code buildFrom} except that the {@code FileDescriptorProto} is | |
252 | + * encoded in protocol buffer wire format. | |
253 | + */ | |
254 | + public static void internalBuildGeneratedFileFrom( | |
255 | + final String[] descriptorDataParts, | |
256 | + final FileDescriptor[] dependencies, | |
257 | + final InternalDescriptorAssigner descriptorAssigner) { | |
258 | + // Hack: We can't embed a raw byte array inside generated Java code | |
259 | + // (at least, not efficiently), but we can embed Strings. So, the | |
260 | + // protocol compiler embeds the FileDescriptorProto as a giant | |
261 | + // string literal which is passed to this function to construct the | |
262 | + // file's FileDescriptor. The string literal contains only 8-bit | |
263 | + // characters, each one representing a byte of the FileDescriptorProto's | |
264 | + // serialized form. So, if we convert it to bytes in ISO-8859-1, we | |
265 | + // should get the original bytes that we want. | |
266 | + | |
267 | + // descriptorData may contain multiple strings in order to get around the | |
268 | + // Java 64k string literal limit. | |
269 | + StringBuilder descriptorData = new StringBuilder(); | |
270 | + for (String part : descriptorDataParts) { | |
271 | + descriptorData.append(part); | |
272 | + } | |
273 | + | |
274 | + final byte[] descriptorBytes; | |
275 | + try { | |
276 | + descriptorBytes = descriptorData.toString().getBytes("ISO-8859-1"); | |
277 | + } catch (UnsupportedEncodingException e) { | |
278 | + throw new RuntimeException( | |
279 | + "Standard encoding ISO-8859-1 not supported by JVM.", e); | |
280 | + } | |
281 | + | |
282 | + FileDescriptorProto proto; | |
283 | + try { | |
284 | + proto = FileDescriptorProto.parseFrom(descriptorBytes); | |
285 | + } catch (InvalidProtocolBufferException e) { | |
286 | + throw new IllegalArgumentException( | |
287 | + "Failed to parse protocol buffer descriptor for generated code.", e); | |
288 | + } | |
289 | + | |
290 | + final FileDescriptor result; | |
291 | + try { | |
292 | + result = buildFrom(proto, dependencies); | |
293 | + } catch (DescriptorValidationException e) { | |
294 | + throw new IllegalArgumentException( | |
295 | + "Invalid embedded descriptor for \"" + proto.getName() + "\".", e); | |
296 | + } | |
297 | + | |
298 | + final ExtensionRegistry registry = | |
299 | + descriptorAssigner.assignDescriptors(result); | |
300 | + | |
301 | + if (registry != null) { | |
302 | + // We must re-parse the proto using the registry. | |
303 | + try { | |
304 | + proto = FileDescriptorProto.parseFrom(descriptorBytes, registry); | |
305 | + } catch (InvalidProtocolBufferException e) { | |
306 | + throw new IllegalArgumentException( | |
307 | + "Failed to parse protocol buffer descriptor for generated code.", | |
308 | + e); | |
309 | + } | |
310 | + | |
311 | + result.setProto(proto); | |
312 | + } | |
313 | + } | |
314 | + | |
315 | + /** | |
316 | + * This class should be used by generated code only. When calling | |
317 | + * {@link FileDescriptor#internalBuildGeneratedFileFrom}, the caller | |
318 | + * provides a callback implementing this interface. The callback is called | |
319 | + * after the FileDescriptor has been constructed, in order to assign all | |
320 | + * the global variales defined in the generated code which point at parts | |
321 | + * of the FileDescriptor. The callback returns an ExtensionRegistry which | |
322 | + * contains any extensions which might be used in the descriptor -- that | |
323 | + * is, extensions of the various "Options" messages defined in | |
324 | + * descriptor.proto. The callback may also return null to indicate that | |
325 | + * no extensions are used in the decsriptor. | |
326 | + */ | |
327 | + public interface InternalDescriptorAssigner { | |
328 | + ExtensionRegistry assignDescriptors(FileDescriptor root); | |
329 | + } | |
330 | + | |
331 | + private FileDescriptorProto proto; | |
332 | + private final Descriptor[] messageTypes; | |
333 | + private final EnumDescriptor[] enumTypes; | |
334 | + private final ServiceDescriptor[] services; | |
335 | + private final FieldDescriptor[] extensions; | |
336 | + private final FileDescriptor[] dependencies; | |
337 | + private final DescriptorPool pool; | |
338 | + | |
339 | + private FileDescriptor(final FileDescriptorProto proto, | |
340 | + final FileDescriptor[] dependencies, | |
341 | + final DescriptorPool pool) | |
342 | + throws DescriptorValidationException { | |
343 | + this.pool = pool; | |
344 | + this.proto = proto; | |
345 | + this.dependencies = dependencies.clone(); | |
346 | + | |
347 | + pool.addPackage(getPackage(), this); | |
348 | + | |
349 | + messageTypes = new Descriptor[proto.getMessageTypeCount()]; | |
350 | + for (int i = 0; i < proto.getMessageTypeCount(); i++) { | |
351 | + messageTypes[i] = | |
352 | + new Descriptor(proto.getMessageType(i), this, null, i); | |
353 | + } | |
354 | + | |
355 | + enumTypes = new EnumDescriptor[proto.getEnumTypeCount()]; | |
356 | + for (int i = 0; i < proto.getEnumTypeCount(); i++) { | |
357 | + enumTypes[i] = new EnumDescriptor(proto.getEnumType(i), this, null, i); | |
358 | + } | |
359 | + | |
360 | + services = new ServiceDescriptor[proto.getServiceCount()]; | |
361 | + for (int i = 0; i < proto.getServiceCount(); i++) { | |
362 | + services[i] = new ServiceDescriptor(proto.getService(i), this, i); | |
363 | + } | |
364 | + | |
365 | + extensions = new FieldDescriptor[proto.getExtensionCount()]; | |
366 | + for (int i = 0; i < proto.getExtensionCount(); i++) { | |
367 | + extensions[i] = new FieldDescriptor( | |
368 | + proto.getExtension(i), this, null, i, true); | |
369 | + } | |
370 | + } | |
371 | + | |
372 | + /** Look up and cross-link all field types, etc. */ | |
373 | + private void crossLink() throws DescriptorValidationException { | |
374 | + for (final Descriptor messageType : messageTypes) { | |
375 | + messageType.crossLink(); | |
376 | + } | |
377 | + | |
378 | + for (final ServiceDescriptor service : services) { | |
379 | + service.crossLink(); | |
380 | + } | |
381 | + | |
382 | + for (final FieldDescriptor extension : extensions) { | |
383 | + extension.crossLink(); | |
384 | + } | |
385 | + } | |
386 | + | |
387 | + /** | |
388 | + * Replace our {@link FileDescriptorProto} with the given one, which is | |
389 | + * identical except that it might contain extensions that weren't present | |
390 | + * in the original. This method is needed for bootstrapping when a file | |
391 | + * defines custom options. The options may be defined in the file itself, | |
392 | + * so we can't actually parse them until we've constructed the descriptors, | |
393 | + * but to construct the decsriptors we have to have parsed the descriptor | |
394 | + * protos. So, we have to parse the descriptor protos a second time after | |
395 | + * constructing the descriptors. | |
396 | + */ | |
397 | + private void setProto(final FileDescriptorProto proto) { | |
398 | + this.proto = proto; | |
399 | + | |
400 | + for (int i = 0; i < messageTypes.length; i++) { | |
401 | + messageTypes[i].setProto(proto.getMessageType(i)); | |
402 | + } | |
403 | + | |
404 | + for (int i = 0; i < enumTypes.length; i++) { | |
405 | + enumTypes[i].setProto(proto.getEnumType(i)); | |
406 | + } | |
407 | + | |
408 | + for (int i = 0; i < services.length; i++) { | |
409 | + services[i].setProto(proto.getService(i)); | |
410 | + } | |
411 | + | |
412 | + for (int i = 0; i < extensions.length; i++) { | |
413 | + extensions[i].setProto(proto.getExtension(i)); | |
414 | + } | |
415 | + } | |
416 | + } | |
417 | + | |
418 | + // ================================================================= | |
419 | + | |
420 | + /** Describes a message type. */ | |
421 | + public static final class Descriptor implements GenericDescriptor { | |
422 | + /** | |
423 | + * Get the index of this descriptor within its parent. In other words, | |
424 | + * given a {@link FileDescriptor} {@code file}, the following is true: | |
425 | + * <pre> | |
426 | + * for all i in [0, file.getMessageTypeCount()): | |
427 | + * file.getMessageType(i).getIndex() == i | |
428 | + * </pre> | |
429 | + * Similarly, for a {@link Descriptor} {@code messageType}: | |
430 | + * <pre> | |
431 | + * for all i in [0, messageType.getNestedTypeCount()): | |
432 | + * messageType.getNestedType(i).getIndex() == i | |
433 | + * </pre> | |
434 | + */ | |
435 | + public int getIndex() { return index; } | |
436 | + | |
437 | + /** Convert the descriptor to its protocol message representation. */ | |
438 | + public DescriptorProto toProto() { return proto; } | |
439 | + | |
440 | + /** Get the type's unqualified name. */ | |
441 | + public String getName() { return proto.getName(); } | |
442 | + | |
443 | + /** | |
444 | + * Get the type's fully-qualified name, within the proto language's | |
445 | + * namespace. This differs from the Java name. For example, given this | |
446 | + * {@code .proto}: | |
447 | + * <pre> | |
448 | + * package foo.bar; | |
449 | + * option java_package = "com.example.protos" | |
450 | + * message Baz {} | |
451 | + * </pre> | |
452 | + * {@code Baz}'s full name is "foo.bar.Baz". | |
453 | + */ | |
454 | + public String getFullName() { return fullName; } | |
455 | + | |
456 | + /** Get the {@link FileDescriptor} containing this descriptor. */ | |
457 | + public FileDescriptor getFile() { return file; } | |
458 | + | |
459 | + /** If this is a nested type, get the outer descriptor, otherwise null. */ | |
460 | + public Descriptor getContainingType() { return containingType; } | |
461 | + | |
462 | + /** Get the {@code MessageOptions}, defined in {@code descriptor.proto}. */ | |
463 | + public MessageOptions getOptions() { return proto.getOptions(); } | |
464 | + | |
465 | + /** Get a list of this message type's fields. */ | |
466 | + public List<FieldDescriptor> getFields() { | |
467 | + return Collections.unmodifiableList(Arrays.asList(fields)); | |
468 | + } | |
469 | + | |
470 | + /** Get a list of this message type's extensions. */ | |
471 | + public List<FieldDescriptor> getExtensions() { | |
472 | + return Collections.unmodifiableList(Arrays.asList(extensions)); | |
473 | + } | |
474 | + | |
475 | + /** Get a list of message types nested within this one. */ | |
476 | + public List<Descriptor> getNestedTypes() { | |
477 | + return Collections.unmodifiableList(Arrays.asList(nestedTypes)); | |
478 | + } | |
479 | + | |
480 | + /** Get a list of enum types nested within this one. */ | |
481 | + public List<EnumDescriptor> getEnumTypes() { | |
482 | + return Collections.unmodifiableList(Arrays.asList(enumTypes)); | |
483 | + } | |
484 | + | |
485 | + /** Determines if the given field number is an extension. */ | |
486 | + public boolean isExtensionNumber(final int number) { | |
487 | + for (final DescriptorProto.ExtensionRange range : | |
488 | + proto.getExtensionRangeList()) { | |
489 | + if (range.getStart() <= number && number < range.getEnd()) { | |
490 | + return true; | |
491 | + } | |
492 | + } | |
493 | + return false; | |
494 | + } | |
495 | + | |
496 | + /** | |
497 | + * Finds a field by name. | |
498 | + * @param name The unqualified name of the field (e.g. "foo"). | |
499 | + * @return The field's descriptor, or {@code null} if not found. | |
500 | + */ | |
501 | + public FieldDescriptor findFieldByName(final String name) { | |
502 | + final GenericDescriptor result = | |
503 | + file.pool.findSymbol(fullName + '.' + name); | |
504 | + if (result != null && result instanceof FieldDescriptor) { | |
505 | + return (FieldDescriptor)result; | |
506 | + } else { | |
507 | + return null; | |
508 | + } | |
509 | + } | |
510 | + | |
511 | + /** | |
512 | + * Finds a field by field number. | |
513 | + * @param number The field number within this message type. | |
514 | + * @return The field's descriptor, or {@code null} if not found. | |
515 | + */ | |
516 | + public FieldDescriptor findFieldByNumber(final int number) { | |
517 | + return file.pool.fieldsByNumber.get( | |
518 | + new DescriptorPool.DescriptorIntPair(this, number)); | |
519 | + } | |
520 | + | |
521 | + /** | |
522 | + * Finds a nested message type by name. | |
523 | + * @param name The unqualified name of the nested type (e.g. "Foo"). | |
524 | + * @return The types's descriptor, or {@code null} if not found. | |
525 | + */ | |
526 | + public Descriptor findNestedTypeByName(final String name) { | |
527 | + final GenericDescriptor result = | |
528 | + file.pool.findSymbol(fullName + '.' + name); | |
529 | + if (result != null && result instanceof Descriptor) { | |
530 | + return (Descriptor)result; | |
531 | + } else { | |
532 | + return null; | |
533 | + } | |
534 | + } | |
535 | + | |
536 | + /** | |
537 | + * Finds a nested enum type by name. | |
538 | + * @param name The unqualified name of the nested type (e.g. "Foo"). | |
539 | + * @return The types's descriptor, or {@code null} if not found. | |
540 | + */ | |
541 | + public EnumDescriptor findEnumTypeByName(final String name) { | |
542 | + final GenericDescriptor result = | |
543 | + file.pool.findSymbol(fullName + '.' + name); | |
544 | + if (result != null && result instanceof EnumDescriptor) { | |
545 | + return (EnumDescriptor)result; | |
546 | + } else { | |
547 | + return null; | |
548 | + } | |
549 | + } | |
550 | + | |
551 | + private final int index; | |
552 | + private DescriptorProto proto; | |
553 | + private final String fullName; | |
554 | + private final FileDescriptor file; | |
555 | + private final Descriptor containingType; | |
556 | + private final Descriptor[] nestedTypes; | |
557 | + private final EnumDescriptor[] enumTypes; | |
558 | + private final FieldDescriptor[] fields; | |
559 | + private final FieldDescriptor[] extensions; | |
560 | + | |
561 | + private Descriptor(final DescriptorProto proto, | |
562 | + final FileDescriptor file, | |
563 | + final Descriptor parent, | |
564 | + final int index) | |
565 | + throws DescriptorValidationException { | |
566 | + this.index = index; | |
567 | + this.proto = proto; | |
568 | + fullName = computeFullName(file, parent, proto.getName()); | |
569 | + this.file = file; | |
570 | + containingType = parent; | |
571 | + | |
572 | + nestedTypes = new Descriptor[proto.getNestedTypeCount()]; | |
573 | + for (int i = 0; i < proto.getNestedTypeCount(); i++) { | |
574 | + nestedTypes[i] = new Descriptor( | |
575 | + proto.getNestedType(i), file, this, i); | |
576 | + } | |
577 | + | |
578 | + enumTypes = new EnumDescriptor[proto.getEnumTypeCount()]; | |
579 | + for (int i = 0; i < proto.getEnumTypeCount(); i++) { | |
580 | + enumTypes[i] = new EnumDescriptor( | |
581 | + proto.getEnumType(i), file, this, i); | |
582 | + } | |
583 | + | |
584 | + fields = new FieldDescriptor[proto.getFieldCount()]; | |
585 | + for (int i = 0; i < proto.getFieldCount(); i++) { | |
586 | + fields[i] = new FieldDescriptor( | |
587 | + proto.getField(i), file, this, i, false); | |
588 | + } | |
589 | + | |
590 | + extensions = new FieldDescriptor[proto.getExtensionCount()]; | |
591 | + for (int i = 0; i < proto.getExtensionCount(); i++) { | |
592 | + extensions[i] = new FieldDescriptor( | |
593 | + proto.getExtension(i), file, this, i, true); | |
594 | + } | |
595 | + | |
596 | + file.pool.addSymbol(this); | |
597 | + } | |
598 | + | |
599 | + /** Look up and cross-link all field types, etc. */ | |
600 | + private void crossLink() throws DescriptorValidationException { | |
601 | + for (final Descriptor nestedType : nestedTypes) { | |
602 | + nestedType.crossLink(); | |
603 | + } | |
604 | + | |
605 | + for (final FieldDescriptor field : fields) { | |
606 | + field.crossLink(); | |
607 | + } | |
608 | + | |
609 | + for (final FieldDescriptor extension : extensions) { | |
610 | + extension.crossLink(); | |
611 | + } | |
612 | + } | |
613 | + | |
614 | + /** See {@link FileDescriptor#setProto}. */ | |
615 | + private void setProto(final DescriptorProto proto) { | |
616 | + this.proto = proto; | |
617 | + | |
618 | + for (int i = 0; i < nestedTypes.length; i++) { | |
619 | + nestedTypes[i].setProto(proto.getNestedType(i)); | |
620 | + } | |
621 | + | |
622 | + for (int i = 0; i < enumTypes.length; i++) { | |
623 | + enumTypes[i].setProto(proto.getEnumType(i)); | |
624 | + } | |
625 | + | |
626 | + for (int i = 0; i < fields.length; i++) { | |
627 | + fields[i].setProto(proto.getField(i)); | |
628 | + } | |
629 | + | |
630 | + for (int i = 0; i < extensions.length; i++) { | |
631 | + extensions[i].setProto(proto.getExtension(i)); | |
632 | + } | |
633 | + } | |
634 | + } | |
635 | + | |
636 | + // ================================================================= | |
637 | + | |
638 | + /** Describes a field of a message type. */ | |
639 | + public static final class FieldDescriptor | |
640 | + implements GenericDescriptor, Comparable<FieldDescriptor>, | |
641 | + FieldSet.FieldDescriptorLite<FieldDescriptor> { | |
642 | + /** | |
643 | + * Get the index of this descriptor within its parent. | |
644 | + * @see Descriptor#getIndex() | |
645 | + */ | |
646 | + public int getIndex() { return index; } | |
647 | + | |
648 | + /** Convert the descriptor to its protocol message representation. */ | |
649 | + public FieldDescriptorProto toProto() { return proto; } | |
650 | + | |
651 | + /** Get the field's unqualified name. */ | |
652 | + public String getName() { return proto.getName(); } | |
653 | + | |
654 | + /** Get the field's number. */ | |
655 | + public int getNumber() { return proto.getNumber(); } | |
656 | + | |
657 | + /** | |
658 | + * Get the field's fully-qualified name. | |
659 | + * @see Descriptor#getFullName() | |
660 | + */ | |
661 | + public String getFullName() { return fullName; } | |
662 | + | |
663 | + /** | |
664 | + * Get the field's java type. This is just for convenience. Every | |
665 | + * {@code FieldDescriptorProto.Type} maps to exactly one Java type. | |
666 | + */ | |
667 | + public JavaType getJavaType() { return type.getJavaType(); } | |
668 | + | |
669 | + /** For internal use only. */ | |
670 | + public WireFormat.JavaType getLiteJavaType() { | |
671 | + return getLiteType().getJavaType(); | |
672 | + } | |
673 | + | |
674 | + /** Get the {@code FileDescriptor} containing this descriptor. */ | |
675 | + public FileDescriptor getFile() { return file; } | |
676 | + | |
677 | + /** Get the field's declared type. */ | |
678 | + public Type getType() { return type; } | |
679 | + | |
680 | + /** For internal use only. */ | |
681 | + public WireFormat.FieldType getLiteType() { | |
682 | + return table[type.ordinal()]; | |
683 | + } | |
684 | + // I'm pretty sure values() constructs a new array every time, since there | |
685 | + // is nothing stopping the caller from mutating the array. Therefore we | |
686 | + // make a static copy here. | |
687 | + private static final WireFormat.FieldType[] table = | |
688 | + WireFormat.FieldType.values(); | |
689 | + | |
690 | + /** Is this field declared required? */ | |
691 | + public boolean isRequired() { | |
692 | + return proto.getLabel() == FieldDescriptorProto.Label.LABEL_REQUIRED; | |
693 | + } | |
694 | + | |
695 | + /** Is this field declared optional? */ | |
696 | + public boolean isOptional() { | |
697 | + return proto.getLabel() == FieldDescriptorProto.Label.LABEL_OPTIONAL; | |
698 | + } | |
699 | + | |
700 | + /** Is this field declared repeated? */ | |
701 | + public boolean isRepeated() { | |
702 | + return proto.getLabel() == FieldDescriptorProto.Label.LABEL_REPEATED; | |
703 | + } | |
704 | + | |
705 | + /** Does this field have the {@code [packed = true]} option? */ | |
706 | + public boolean isPacked() { | |
707 | + return getOptions().getPacked(); | |
708 | + } | |
709 | + | |
710 | + /** Can this field be packed? i.e. is it a repeated primitive field? */ | |
711 | + public boolean isPackable() { | |
712 | + return isRepeated() && getLiteType().isPackable(); | |
713 | + } | |
714 | + | |
715 | + /** Returns true if the field had an explicitly-defined default value. */ | |
716 | + public boolean hasDefaultValue() { return proto.hasDefaultValue(); } | |
717 | + | |
718 | + /** | |
719 | + * Returns the field's default value. Valid for all types except for | |
720 | + * messages and groups. For all other types, the object returned is of | |
721 | + * the same class that would returned by Message.getField(this). | |
722 | + */ | |
723 | + public Object getDefaultValue() { | |
724 | + if (getJavaType() == JavaType.MESSAGE) { | |
725 | + throw new UnsupportedOperationException( | |
726 | + "FieldDescriptor.getDefaultValue() called on an embedded message " + | |
727 | + "field."); | |
728 | + } | |
729 | + return defaultValue; | |
730 | + } | |
731 | + | |
732 | + /** Get the {@code FieldOptions}, defined in {@code descriptor.proto}. */ | |
733 | + public FieldOptions getOptions() { return proto.getOptions(); } | |
734 | + | |
735 | + /** Is this field an extension? */ | |
736 | + public boolean isExtension() { return proto.hasExtendee(); } | |
737 | + | |
738 | + /** | |
739 | + * Get the field's containing type. For extensions, this is the type being | |
740 | + * extended, not the location where the extension was defined. See | |
741 | + * {@link #getExtensionScope()}. | |
742 | + */ | |
743 | + public Descriptor getContainingType() { return containingType; } | |
744 | + | |
745 | + /** | |
746 | + * For extensions defined nested within message types, gets the outer | |
747 | + * type. Not valid for non-extension fields. For example, consider | |
748 | + * this {@code .proto} file: | |
749 | + * <pre> | |
750 | + * message Foo { | |
751 | + * extensions 1000 to max; | |
752 | + * } | |
753 | + * extend Foo { | |
754 | + * optional int32 baz = 1234; | |
755 | + * } | |
756 | + * message Bar { | |
757 | + * extend Foo { | |
758 | + * optional int32 qux = 4321; | |
759 | + * } | |
760 | + * } | |
761 | + * </pre> | |
762 | + * Both {@code baz}'s and {@code qux}'s containing type is {@code Foo}. | |
763 | + * However, {@code baz}'s extension scope is {@code null} while | |
764 | + * {@code qux}'s extension scope is {@code Bar}. | |
765 | + */ | |
766 | + public Descriptor getExtensionScope() { | |
767 | + if (!isExtension()) { | |
768 | + throw new UnsupportedOperationException( | |
769 | + "This field is not an extension."); | |
770 | + } | |
771 | + return extensionScope; | |
772 | + } | |
773 | + | |
774 | + /** For embedded message and group fields, gets the field's type. */ | |
775 | + public Descriptor getMessageType() { | |
776 | + if (getJavaType() != JavaType.MESSAGE) { | |
777 | + throw new UnsupportedOperationException( | |
778 | + "This field is not of message type."); | |
779 | + } | |
780 | + return messageType; | |
781 | + } | |
782 | + | |
783 | + /** For enum fields, gets the field's type. */ | |
784 | + public EnumDescriptor getEnumType() { | |
785 | + if (getJavaType() != JavaType.ENUM) { | |
786 | + throw new UnsupportedOperationException( | |
787 | + "This field is not of enum type."); | |
788 | + } | |
789 | + return enumType; | |
790 | + } | |
791 | + | |
792 | + /** | |
793 | + * Compare with another {@code FieldDescriptor}. This orders fields in | |
794 | + * "canonical" order, which simply means ascending order by field number. | |
795 | + * {@code other} must be a field of the same type -- i.e. | |
796 | + * {@code getContainingType()} must return the same {@code Descriptor} for | |
797 | + * both fields. | |
798 | + * | |
799 | + * @return negative, zero, or positive if {@code this} is less than, | |
800 | + * equal to, or greater than {@code other}, respectively. | |
801 | + */ | |
802 | + public int compareTo(final FieldDescriptor other) { | |
803 | + if (other.containingType != containingType) { | |
804 | + throw new IllegalArgumentException( | |
805 | + "FieldDescriptors can only be compared to other FieldDescriptors " + | |
806 | + "for fields of the same message type."); | |
807 | + } | |
808 | + return getNumber() - other.getNumber(); | |
809 | + } | |
810 | + | |
811 | + private final int index; | |
812 | + | |
813 | + private FieldDescriptorProto proto; | |
814 | + private final String fullName; | |
815 | + private final FileDescriptor file; | |
816 | + private final Descriptor extensionScope; | |
817 | + | |
818 | + // Possibly initialized during cross-linking. | |
819 | + private Type type; | |
820 | + private Descriptor containingType; | |
821 | + private Descriptor messageType; | |
822 | + private EnumDescriptor enumType; | |
823 | + private Object defaultValue; | |
824 | + | |
825 | + public enum Type { | |
826 | + DOUBLE (JavaType.DOUBLE ), | |
827 | + FLOAT (JavaType.FLOAT ), | |
828 | + INT64 (JavaType.LONG ), | |
829 | + UINT64 (JavaType.LONG ), | |
830 | + INT32 (JavaType.INT ), | |
831 | + FIXED64 (JavaType.LONG ), | |
832 | + FIXED32 (JavaType.INT ), | |
833 | + BOOL (JavaType.BOOLEAN ), | |
834 | + STRING (JavaType.STRING ), | |
835 | + GROUP (JavaType.MESSAGE ), | |
836 | + MESSAGE (JavaType.MESSAGE ), | |
837 | + BYTES (JavaType.BYTE_STRING), | |
838 | + UINT32 (JavaType.INT ), | |
839 | + ENUM (JavaType.ENUM ), | |
840 | + SFIXED32(JavaType.INT ), | |
841 | + SFIXED64(JavaType.LONG ), | |
842 | + SINT32 (JavaType.INT ), | |
843 | + SINT64 (JavaType.LONG ); | |
844 | + | |
845 | + Type(final JavaType javaType) { | |
846 | + this.javaType = javaType; | |
847 | + } | |
848 | + | |
849 | + private JavaType javaType; | |
850 | + | |
851 | + public FieldDescriptorProto.Type toProto() { | |
852 | + return FieldDescriptorProto.Type.valueOf(ordinal() + 1); | |
853 | + } | |
854 | + public JavaType getJavaType() { return javaType; } | |
855 | + | |
856 | + public static Type valueOf(final FieldDescriptorProto.Type type) { | |
857 | + return values()[type.getNumber() - 1]; | |
858 | + } | |
859 | + } | |
860 | + | |
861 | + static { | |
862 | + // Refuse to init if someone added a new declared type. | |
863 | + if (Type.values().length != FieldDescriptorProto.Type.values().length) { | |
864 | + throw new RuntimeException( | |
865 | + "descriptor.proto has a new declared type but Desrciptors.java " + | |
866 | + "wasn't updated."); | |
867 | + } | |
868 | + } | |
869 | + | |
870 | + public enum JavaType { | |
871 | + INT(0), | |
872 | + LONG(0L), | |
873 | + FLOAT(0F), | |
874 | + DOUBLE(0D), | |
875 | + BOOLEAN(false), | |
876 | + STRING(""), | |
877 | + BYTE_STRING(ByteString.EMPTY), | |
878 | + ENUM(null), | |
879 | + MESSAGE(null); | |
880 | + | |
881 | + JavaType(final Object defaultDefault) { | |
882 | + this.defaultDefault = defaultDefault; | |
883 | + } | |
884 | + | |
885 | + /** | |
886 | + * The default default value for fields of this type, if it's a primitive | |
887 | + * type. This is meant for use inside this file only, hence is private. | |
888 | + */ | |
889 | + private final Object defaultDefault; | |
890 | + } | |
891 | + | |
892 | + private FieldDescriptor(final FieldDescriptorProto proto, | |
893 | + final FileDescriptor file, | |
894 | + final Descriptor parent, | |
895 | + final int index, | |
896 | + final boolean isExtension) | |
897 | + throws DescriptorValidationException { | |
898 | + this.index = index; | |
899 | + this.proto = proto; | |
900 | + fullName = computeFullName(file, parent, proto.getName()); | |
901 | + this.file = file; | |
902 | + | |
903 | + if (proto.hasType()) { | |
904 | + type = Type.valueOf(proto.getType()); | |
905 | + } | |
906 | + | |
907 | + if (getNumber() <= 0) { | |
908 | + throw new DescriptorValidationException(this, | |
909 | + "Field numbers must be positive integers."); | |
910 | + } | |
911 | + | |
912 | + // Only repeated primitive fields may be packed. | |
913 | + if (proto.getOptions().getPacked() && !isPackable()) { | |
914 | + throw new DescriptorValidationException(this, | |
915 | + "[packed = true] can only be specified for repeated primitive " + | |
916 | + "fields."); | |
917 | + } | |
918 | + | |
919 | + if (isExtension) { | |
920 | + if (!proto.hasExtendee()) { | |
921 | + throw new DescriptorValidationException(this, | |
922 | + "FieldDescriptorProto.extendee not set for extension field."); | |
923 | + } | |
924 | + containingType = null; // Will be filled in when cross-linking | |
925 | + if (parent != null) { | |
926 | + extensionScope = parent; | |
927 | + } else { | |
928 | + extensionScope = null; | |
929 | + } | |
930 | + } else { | |
931 | + if (proto.hasExtendee()) { | |
932 | + throw new DescriptorValidationException(this, | |
933 | + "FieldDescriptorProto.extendee set for non-extension field."); | |
934 | + } | |
935 | + containingType = parent; | |
936 | + extensionScope = null; | |
937 | + } | |
938 | + | |
939 | + file.pool.addSymbol(this); | |
940 | + } | |
941 | + | |
942 | + /** Look up and cross-link all field types, etc. */ | |
943 | + private void crossLink() throws DescriptorValidationException { | |
944 | + if (proto.hasExtendee()) { | |
945 | + final GenericDescriptor extendee = | |
946 | + file.pool.lookupSymbol(proto.getExtendee(), this); | |
947 | + if (!(extendee instanceof Descriptor)) { | |
948 | + throw new DescriptorValidationException(this, | |
949 | + '\"' + proto.getExtendee() + "\" is not a message type."); | |
950 | + } | |
951 | + containingType = (Descriptor)extendee; | |
952 | + | |
953 | + if (!getContainingType().isExtensionNumber(getNumber())) { | |
954 | + throw new DescriptorValidationException(this, | |
955 | + '\"' + getContainingType().getFullName() + | |
956 | + "\" does not declare " + getNumber() + | |
957 | + " as an extension number."); | |
958 | + } | |
959 | + } | |
960 | + | |
961 | + if (proto.hasTypeName()) { | |
962 | + final GenericDescriptor typeDescriptor = | |
963 | + file.pool.lookupSymbol(proto.getTypeName(), this); | |
964 | + | |
965 | + if (!proto.hasType()) { | |
966 | + // Choose field type based on symbol. | |
967 | + if (typeDescriptor instanceof Descriptor) { | |
968 | + type = Type.MESSAGE; | |
969 | + } else if (typeDescriptor instanceof EnumDescriptor) { | |
970 | + type = Type.ENUM; | |
971 | + } else { | |
972 | + throw new DescriptorValidationException(this, | |
973 | + '\"' + proto.getTypeName() + "\" is not a type."); | |
974 | + } | |
975 | + } | |
976 | + | |
977 | + if (getJavaType() == JavaType.MESSAGE) { | |
978 | + if (!(typeDescriptor instanceof Descriptor)) { | |
979 | + throw new DescriptorValidationException(this, | |
980 | + '\"' + proto.getTypeName() + "\" is not a message type."); | |
981 | + } | |
982 | + messageType = (Descriptor)typeDescriptor; | |
983 | + | |
984 | + if (proto.hasDefaultValue()) { | |
985 | + throw new DescriptorValidationException(this, | |
986 | + "Messages can't have default values."); | |
987 | + } | |
988 | + } else if (getJavaType() == JavaType.ENUM) { | |
989 | + if (!(typeDescriptor instanceof EnumDescriptor)) { | |
990 | + throw new DescriptorValidationException(this, | |
991 | + '\"' + proto.getTypeName() + "\" is not an enum type."); | |
992 | + } | |
993 | + enumType = (EnumDescriptor)typeDescriptor; | |
994 | + } else { | |
995 | + throw new DescriptorValidationException(this, | |
996 | + "Field with primitive type has type_name."); | |
997 | + } | |
998 | + } else { | |
999 | + if (getJavaType() == JavaType.MESSAGE || | |
1000 | + getJavaType() == JavaType.ENUM) { | |
1001 | + throw new DescriptorValidationException(this, | |
1002 | + "Field with message or enum type missing type_name."); | |
1003 | + } | |
1004 | + } | |
1005 | + | |
1006 | + // We don't attempt to parse the default value until here because for | |
1007 | + // enums we need the enum type's descriptor. | |
1008 | + if (proto.hasDefaultValue()) { | |
1009 | + if (isRepeated()) { | |
1010 | + throw new DescriptorValidationException(this, | |
1011 | + "Repeated fields cannot have default values."); | |
1012 | + } | |
1013 | + | |
1014 | + try { | |
1015 | + switch (getType()) { | |
1016 | + case INT32: | |
1017 | + case SINT32: | |
1018 | + case SFIXED32: | |
1019 | + defaultValue = TextFormat.parseInt32(proto.getDefaultValue()); | |
1020 | + break; | |
1021 | + case UINT32: | |
1022 | + case FIXED32: | |
1023 | + defaultValue = TextFormat.parseUInt32(proto.getDefaultValue()); | |
1024 | + break; | |
1025 | + case INT64: | |
1026 | + case SINT64: | |
1027 | + case SFIXED64: | |
1028 | + defaultValue = TextFormat.parseInt64(proto.getDefaultValue()); | |
1029 | + break; | |
1030 | + case UINT64: | |
1031 | + case FIXED64: | |
1032 | + defaultValue = TextFormat.parseUInt64(proto.getDefaultValue()); | |
1033 | + break; | |
1034 | + case FLOAT: | |
1035 | + if (proto.getDefaultValue().equals("inf")) { | |
1036 | + defaultValue = Float.POSITIVE_INFINITY; | |
1037 | + } else if (proto.getDefaultValue().equals("-inf")) { | |
1038 | + defaultValue = Float.NEGATIVE_INFINITY; | |
1039 | + } else if (proto.getDefaultValue().equals("nan")) { | |
1040 | + defaultValue = Float.NaN; | |
1041 | + } else { | |
1042 | + defaultValue = Float.valueOf(proto.getDefaultValue()); | |
1043 | + } | |
1044 | + break; | |
1045 | + case DOUBLE: | |
1046 | + if (proto.getDefaultValue().equals("inf")) { | |
1047 | + defaultValue = Double.POSITIVE_INFINITY; | |
1048 | + } else if (proto.getDefaultValue().equals("-inf")) { | |
1049 | + defaultValue = Double.NEGATIVE_INFINITY; | |
1050 | + } else if (proto.getDefaultValue().equals("nan")) { | |
1051 | + defaultValue = Double.NaN; | |
1052 | + } else { | |
1053 | + defaultValue = Double.valueOf(proto.getDefaultValue()); | |
1054 | + } | |
1055 | + break; | |
1056 | + case BOOL: | |
1057 | + defaultValue = Boolean.valueOf(proto.getDefaultValue()); | |
1058 | + break; | |
1059 | + case STRING: | |
1060 | + defaultValue = proto.getDefaultValue(); | |
1061 | + break; | |
1062 | + case BYTES: | |
1063 | + try { | |
1064 | + defaultValue = | |
1065 | + TextFormat.unescapeBytes(proto.getDefaultValue()); | |
1066 | + } catch (TextFormat.InvalidEscapeSequenceException e) { | |
1067 | + throw new DescriptorValidationException(this, | |
1068 | + "Couldn't parse default value: " + e.getMessage(), e); | |
1069 | + } | |
1070 | + break; | |
1071 | + case ENUM: | |
1072 | + defaultValue = enumType.findValueByName(proto.getDefaultValue()); | |
1073 | + if (defaultValue == null) { | |
1074 | + throw new DescriptorValidationException(this, | |
1075 | + "Unknown enum default value: \"" + | |
1076 | + proto.getDefaultValue() + '\"'); | |
1077 | + } | |
1078 | + break; | |
1079 | + case MESSAGE: | |
1080 | + case GROUP: | |
1081 | + throw new DescriptorValidationException(this, | |
1082 | + "Message type had default value."); | |
1083 | + } | |
1084 | + } catch (NumberFormatException e) { | |
1085 | + throw new DescriptorValidationException(this, | |
1086 | + "Could not parse default value: \"" + | |
1087 | + proto.getDefaultValue() + '\"', e); | |
1088 | + } | |
1089 | + } else { | |
1090 | + // Determine the default default for this field. | |
1091 | + if (isRepeated()) { | |
1092 | + defaultValue = Collections.emptyList(); | |
1093 | + } else { | |
1094 | + switch (getJavaType()) { | |
1095 | + case ENUM: | |
1096 | + // We guarantee elsewhere that an enum type always has at least | |
1097 | + // one possible value. | |
1098 | + defaultValue = enumType.getValues().get(0); | |
1099 | + break; | |
1100 | + case MESSAGE: | |
1101 | + defaultValue = null; | |
1102 | + break; | |
1103 | + default: | |
1104 | + defaultValue = getJavaType().defaultDefault; | |
1105 | + break; | |
1106 | + } | |
1107 | + } | |
1108 | + } | |
1109 | + | |
1110 | + if (!isExtension()) { | |
1111 | + file.pool.addFieldByNumber(this); | |
1112 | + } | |
1113 | + | |
1114 | + if (containingType != null && | |
1115 | + containingType.getOptions().getMessageSetWireFormat()) { | |
1116 | + if (isExtension()) { | |
1117 | + if (!isOptional() || getType() != Type.MESSAGE) { | |
1118 | + throw new DescriptorValidationException(this, | |
1119 | + "Extensions of MessageSets must be optional messages."); | |
1120 | + } | |
1121 | + } else { | |
1122 | + throw new DescriptorValidationException(this, | |
1123 | + "MessageSets cannot have fields, only extensions."); | |
1124 | + } | |
1125 | + } | |
1126 | + } | |
1127 | + | |
1128 | + /** See {@link FileDescriptor#setProto}. */ | |
1129 | + private void setProto(final FieldDescriptorProto proto) { | |
1130 | + this.proto = proto; | |
1131 | + } | |
1132 | + | |
1133 | + /** | |
1134 | + * For internal use only. This is to satisfy the FieldDescriptorLite | |
1135 | + * interface. | |
1136 | + */ | |
1137 | + public MessageLite.Builder internalMergeFrom( | |
1138 | + MessageLite.Builder to, MessageLite from) { | |
1139 | + // FieldDescriptors are only used with non-lite messages so we can just | |
1140 | + // down-cast and call mergeFrom directly. | |
1141 | + return ((Message.Builder) to).mergeFrom((Message) from); | |
1142 | + } | |
1143 | + } | |
1144 | + | |
1145 | + // ================================================================= | |
1146 | + | |
1147 | + /** Describes an enum type. */ | |
1148 | + public static final class EnumDescriptor | |
1149 | + implements GenericDescriptor, Internal.EnumLiteMap<EnumValueDescriptor> { | |
1150 | + /** | |
1151 | + * Get the index of this descriptor within its parent. | |
1152 | + * @see Descriptor#getIndex() | |
1153 | + */ | |
1154 | + public int getIndex() { return index; } | |
1155 | + | |
1156 | + /** Convert the descriptor to its protocol message representation. */ | |
1157 | + public EnumDescriptorProto toProto() { return proto; } | |
1158 | + | |
1159 | + /** Get the type's unqualified name. */ | |
1160 | + public String getName() { return proto.getName(); } | |
1161 | + | |
1162 | + /** | |
1163 | + * Get the type's fully-qualified name. | |
1164 | + * @see Descriptor#getFullName() | |
1165 | + */ | |
1166 | + public String getFullName() { return fullName; } | |
1167 | + | |
1168 | + /** Get the {@link FileDescriptor} containing this descriptor. */ | |
1169 | + public FileDescriptor getFile() { return file; } | |
1170 | + | |
1171 | + /** If this is a nested type, get the outer descriptor, otherwise null. */ | |
1172 | + public Descriptor getContainingType() { return containingType; } | |
1173 | + | |
1174 | + /** Get the {@code EnumOptions}, defined in {@code descriptor.proto}. */ | |
1175 | + public EnumOptions getOptions() { return proto.getOptions(); } | |
1176 | + | |
1177 | + /** Get a list of defined values for this enum. */ | |
1178 | + public List<EnumValueDescriptor> getValues() { | |
1179 | + return Collections.unmodifiableList(Arrays.asList(values)); | |
1180 | + } | |
1181 | + | |
1182 | + /** | |
1183 | + * Find an enum value by name. | |
1184 | + * @param name The unqualified name of the value (e.g. "FOO"). | |
1185 | + * @return the value's decsriptor, or {@code null} if not found. | |
1186 | + */ | |
1187 | + public EnumValueDescriptor findValueByName(final String name) { | |
1188 | + final GenericDescriptor result = | |
1189 | + file.pool.findSymbol(fullName + '.' + name); | |
1190 | + if (result != null && result instanceof EnumValueDescriptor) { | |
1191 | + return (EnumValueDescriptor)result; | |
1192 | + } else { | |
1193 | + return null; | |
1194 | + } | |
1195 | + } | |
1196 | + | |
1197 | + /** | |
1198 | + * Find an enum value by number. If multiple enum values have the same | |
1199 | + * number, this returns the first defined value with that number. | |
1200 | + * @param number The value's number. | |
1201 | + * @return the value's decsriptor, or {@code null} if not found. | |
1202 | + */ | |
1203 | + public EnumValueDescriptor findValueByNumber(final int number) { | |
1204 | + return file.pool.enumValuesByNumber.get( | |
1205 | + new DescriptorPool.DescriptorIntPair(this, number)); | |
1206 | + } | |
1207 | + | |
1208 | + private final int index; | |
1209 | + private EnumDescriptorProto proto; | |
1210 | + private final String fullName; | |
1211 | + private final FileDescriptor file; | |
1212 | + private final Descriptor containingType; | |
1213 | + private EnumValueDescriptor[] values; | |
1214 | + | |
1215 | + private EnumDescriptor(final EnumDescriptorProto proto, | |
1216 | + final FileDescriptor file, | |
1217 | + final Descriptor parent, | |
1218 | + final int index) | |
1219 | + throws DescriptorValidationException { | |
1220 | + this.index = index; | |
1221 | + this.proto = proto; | |
1222 | + fullName = computeFullName(file, parent, proto.getName()); | |
1223 | + this.file = file; | |
1224 | + containingType = parent; | |
1225 | + | |
1226 | + if (proto.getValueCount() == 0) { | |
1227 | + // We cannot allow enums with no values because this would mean there | |
1228 | + // would be no valid default value for fields of this type. | |
1229 | + throw new DescriptorValidationException(this, | |
1230 | + "Enums must contain at least one value."); | |
1231 | + } | |
1232 | + | |
1233 | + values = new EnumValueDescriptor[proto.getValueCount()]; | |
1234 | + for (int i = 0; i < proto.getValueCount(); i++) { | |
1235 | + values[i] = new EnumValueDescriptor( | |
1236 | + proto.getValue(i), file, this, i); | |
1237 | + } | |
1238 | + | |
1239 | + file.pool.addSymbol(this); | |
1240 | + } | |
1241 | + | |
1242 | + /** See {@link FileDescriptor#setProto}. */ | |
1243 | + private void setProto(final EnumDescriptorProto proto) { | |
1244 | + this.proto = proto; | |
1245 | + | |
1246 | + for (int i = 0; i < values.length; i++) { | |
1247 | + values[i].setProto(proto.getValue(i)); | |
1248 | + } | |
1249 | + } | |
1250 | + } | |
1251 | + | |
1252 | + // ================================================================= | |
1253 | + | |
1254 | + /** | |
1255 | + * Describes one value within an enum type. Note that multiple defined | |
1256 | + * values may have the same number. In generated Java code, all values | |
1257 | + * with the same number after the first become aliases of the first. | |
1258 | + * However, they still have independent EnumValueDescriptors. | |
1259 | + */ | |
1260 | + public static final class EnumValueDescriptor | |
1261 | + implements GenericDescriptor, Internal.EnumLite { | |
1262 | + /** | |
1263 | + * Get the index of this descriptor within its parent. | |
1264 | + * @see Descriptor#getIndex() | |
1265 | + */ | |
1266 | + public int getIndex() { return index; } | |
1267 | + | |
1268 | + /** Convert the descriptor to its protocol message representation. */ | |
1269 | + public EnumValueDescriptorProto toProto() { return proto; } | |
1270 | + | |
1271 | + /** Get the value's unqualified name. */ | |
1272 | + public String getName() { return proto.getName(); } | |
1273 | + | |
1274 | + /** Get the value's number. */ | |
1275 | + public int getNumber() { return proto.getNumber(); } | |
1276 | + | |
1277 | + /** | |
1278 | + * Get the value's fully-qualified name. | |
1279 | + * @see Descriptor#getFullName() | |
1280 | + */ | |
1281 | + public String getFullName() { return fullName; } | |
1282 | + | |
1283 | + /** Get the {@link FileDescriptor} containing this descriptor. */ | |
1284 | + public FileDescriptor getFile() { return file; } | |
1285 | + | |
1286 | + /** Get the value's enum type. */ | |
1287 | + public EnumDescriptor getType() { return type; } | |
1288 | + | |
1289 | + /** | |
1290 | + * Get the {@code EnumValueOptions}, defined in {@code descriptor.proto}. | |
1291 | + */ | |
1292 | + public EnumValueOptions getOptions() { return proto.getOptions(); } | |
1293 | + | |
1294 | + private final int index; | |
1295 | + private EnumValueDescriptorProto proto; | |
1296 | + private final String fullName; | |
1297 | + private final FileDescriptor file; | |
1298 | + private final EnumDescriptor type; | |
1299 | + | |
1300 | + private EnumValueDescriptor(final EnumValueDescriptorProto proto, | |
1301 | + final FileDescriptor file, | |
1302 | + final EnumDescriptor parent, | |
1303 | + final int index) | |
1304 | + throws DescriptorValidationException { | |
1305 | + this.index = index; | |
1306 | + this.proto = proto; | |
1307 | + this.file = file; | |
1308 | + type = parent; | |
1309 | + | |
1310 | + fullName = parent.getFullName() + '.' + proto.getName(); | |
1311 | + | |
1312 | + file.pool.addSymbol(this); | |
1313 | + file.pool.addEnumValueByNumber(this); | |
1314 | + } | |
1315 | + | |
1316 | + /** See {@link FileDescriptor#setProto}. */ | |
1317 | + private void setProto(final EnumValueDescriptorProto proto) { | |
1318 | + this.proto = proto; | |
1319 | + } | |
1320 | + } | |
1321 | + | |
1322 | + // ================================================================= | |
1323 | + | |
1324 | + /** Describes a service type. */ | |
1325 | + public static final class ServiceDescriptor implements GenericDescriptor { | |
1326 | + /** | |
1327 | + * Get the index of this descriptor within its parent. | |
1328 | + * * @see Descriptors.Descriptor#getIndex() | |
1329 | + */ | |
1330 | + public int getIndex() { return index; } | |
1331 | + | |
1332 | + /** Convert the descriptor to its protocol message representation. */ | |
1333 | + public ServiceDescriptorProto toProto() { return proto; } | |
1334 | + | |
1335 | + /** Get the type's unqualified name. */ | |
1336 | + public String getName() { return proto.getName(); } | |
1337 | + | |
1338 | + /** | |
1339 | + * Get the type's fully-qualified name. | |
1340 | + * @see Descriptor#getFullName() | |
1341 | + */ | |
1342 | + public String getFullName() { return fullName; } | |
1343 | + | |
1344 | + /** Get the {@link FileDescriptor} containing this descriptor. */ | |
1345 | + public FileDescriptor getFile() { return file; } | |
1346 | + | |
1347 | + /** Get the {@code ServiceOptions}, defined in {@code descriptor.proto}. */ | |
1348 | + public ServiceOptions getOptions() { return proto.getOptions(); } | |
1349 | + | |
1350 | + /** Get a list of methods for this service. */ | |
1351 | + public List<MethodDescriptor> getMethods() { | |
1352 | + return Collections.unmodifiableList(Arrays.asList(methods)); | |
1353 | + } | |
1354 | + | |
1355 | + /** | |
1356 | + * Find a method by name. | |
1357 | + * @param name The unqualified name of the method (e.g. "Foo"). | |
1358 | + * @return the method's decsriptor, or {@code null} if not found. | |
1359 | + */ | |
1360 | + public MethodDescriptor findMethodByName(final String name) { | |
1361 | + final GenericDescriptor result = | |
1362 | + file.pool.findSymbol(fullName + '.' + name); | |
1363 | + if (result != null && result instanceof MethodDescriptor) { | |
1364 | + return (MethodDescriptor)result; | |
1365 | + } else { | |
1366 | + return null; | |
1367 | + } | |
1368 | + } | |
1369 | + | |
1370 | + private final int index; | |
1371 | + private ServiceDescriptorProto proto; | |
1372 | + private final String fullName; | |
1373 | + private final FileDescriptor file; | |
1374 | + private MethodDescriptor[] methods; | |
1375 | + | |
1376 | + private ServiceDescriptor(final ServiceDescriptorProto proto, | |
1377 | + final FileDescriptor file, | |
1378 | + final int index) | |
1379 | + throws DescriptorValidationException { | |
1380 | + this.index = index; | |
1381 | + this.proto = proto; | |
1382 | + fullName = computeFullName(file, null, proto.getName()); | |
1383 | + this.file = file; | |
1384 | + | |
1385 | + methods = new MethodDescriptor[proto.getMethodCount()]; | |
1386 | + for (int i = 0; i < proto.getMethodCount(); i++) { | |
1387 | + methods[i] = new MethodDescriptor( | |
1388 | + proto.getMethod(i), file, this, i); | |
1389 | + } | |
1390 | + | |
1391 | + file.pool.addSymbol(this); | |
1392 | + } | |
1393 | + | |
1394 | + private void crossLink() throws DescriptorValidationException { | |
1395 | + for (final MethodDescriptor method : methods) { | |
1396 | + method.crossLink(); | |
1397 | + } | |
1398 | + } | |
1399 | + | |
1400 | + /** See {@link FileDescriptor#setProto}. */ | |
1401 | + private void setProto(final ServiceDescriptorProto proto) { | |
1402 | + this.proto = proto; | |
1403 | + | |
1404 | + for (int i = 0; i < methods.length; i++) { | |
1405 | + methods[i].setProto(proto.getMethod(i)); | |
1406 | + } | |
1407 | + } | |
1408 | + } | |
1409 | + | |
1410 | + // ================================================================= | |
1411 | + | |
1412 | + /** | |
1413 | + * Describes one method within a service type. | |
1414 | + */ | |
1415 | + public static final class MethodDescriptor implements GenericDescriptor { | |
1416 | + /** | |
1417 | + * Get the index of this descriptor within its parent. | |
1418 | + * * @see Descriptors.Descriptor#getIndex() | |
1419 | + */ | |
1420 | + public int getIndex() { return index; } | |
1421 | + | |
1422 | + /** Convert the descriptor to its protocol message representation. */ | |
1423 | + public MethodDescriptorProto toProto() { return proto; } | |
1424 | + | |
1425 | + /** Get the method's unqualified name. */ | |
1426 | + public String getName() { return proto.getName(); } | |
1427 | + | |
1428 | + /** | |
1429 | + * Get the method's fully-qualified name. | |
1430 | + * @see Descriptor#getFullName() | |
1431 | + */ | |
1432 | + public String getFullName() { return fullName; } | |
1433 | + | |
1434 | + /** Get the {@link FileDescriptor} containing this descriptor. */ | |
1435 | + public FileDescriptor getFile() { return file; } | |
1436 | + | |
1437 | + /** Get the method's service type. */ | |
1438 | + public ServiceDescriptor getService() { return service; } | |
1439 | + | |
1440 | + /** Get the method's input type. */ | |
1441 | + public Descriptor getInputType() { return inputType; } | |
1442 | + | |
1443 | + /** Get the method's output type. */ | |
1444 | + public Descriptor getOutputType() { return outputType; } | |
1445 | + | |
1446 | + /** | |
1447 | + * Get the {@code MethodOptions}, defined in {@code descriptor.proto}. | |
1448 | + */ | |
1449 | + public MethodOptions getOptions() { return proto.getOptions(); } | |
1450 | + | |
1451 | + private final int index; | |
1452 | + private MethodDescriptorProto proto; | |
1453 | + private final String fullName; | |
1454 | + private final FileDescriptor file; | |
1455 | + private final ServiceDescriptor service; | |
1456 | + | |
1457 | + // Initialized during cross-linking. | |
1458 | + private Descriptor inputType; | |
1459 | + private Descriptor outputType; | |
1460 | + | |
1461 | + private MethodDescriptor(final MethodDescriptorProto proto, | |
1462 | + final FileDescriptor file, | |
1463 | + final ServiceDescriptor parent, | |
1464 | + final int index) | |
1465 | + throws DescriptorValidationException { | |
1466 | + this.index = index; | |
1467 | + this.proto = proto; | |
1468 | + this.file = file; | |
1469 | + service = parent; | |
1470 | + | |
1471 | + fullName = parent.getFullName() + '.' + proto.getName(); | |
1472 | + | |
1473 | + file.pool.addSymbol(this); | |
1474 | + } | |
1475 | + | |
1476 | + private void crossLink() throws DescriptorValidationException { | |
1477 | + final GenericDescriptor input = | |
1478 | + file.pool.lookupSymbol(proto.getInputType(), this); | |
1479 | + if (!(input instanceof Descriptor)) { | |
1480 | + throw new DescriptorValidationException(this, | |
1481 | + '\"' + proto.getInputType() + "\" is not a message type."); | |
1482 | + } | |
1483 | + inputType = (Descriptor)input; | |
1484 | + | |
1485 | + final GenericDescriptor output = | |
1486 | + file.pool.lookupSymbol(proto.getOutputType(), this); | |
1487 | + if (!(output instanceof Descriptor)) { | |
1488 | + throw new DescriptorValidationException(this, | |
1489 | + '\"' + proto.getOutputType() + "\" is not a message type."); | |
1490 | + } | |
1491 | + outputType = (Descriptor)output; | |
1492 | + } | |
1493 | + | |
1494 | + /** See {@link FileDescriptor#setProto}. */ | |
1495 | + private void setProto(final MethodDescriptorProto proto) { | |
1496 | + this.proto = proto; | |
1497 | + } | |
1498 | + } | |
1499 | + | |
1500 | + // ================================================================= | |
1501 | + | |
1502 | + private static String computeFullName(final FileDescriptor file, | |
1503 | + final Descriptor parent, | |
1504 | + final String name) { | |
1505 | + if (parent != null) { | |
1506 | + return parent.getFullName() + '.' + name; | |
1507 | + } else if (file.getPackage().length() > 0) { | |
1508 | + return file.getPackage() + '.' + name; | |
1509 | + } else { | |
1510 | + return name; | |
1511 | + } | |
1512 | + } | |
1513 | + | |
1514 | + // ================================================================= | |
1515 | + | |
1516 | + /** | |
1517 | + * All descriptors except {@code FileDescriptor} implement this to make | |
1518 | + * {@code DescriptorPool}'s life easier. | |
1519 | + */ | |
1520 | + private interface GenericDescriptor { | |
1521 | + Message toProto(); | |
1522 | + String getName(); | |
1523 | + String getFullName(); | |
1524 | + FileDescriptor getFile(); | |
1525 | + } | |
1526 | + | |
1527 | + /** | |
1528 | + * Thrown when building descriptors fails because the source DescriptorProtos | |
1529 | + * are not valid. | |
1530 | + */ | |
1531 | + public static class DescriptorValidationException extends Exception { | |
1532 | + private static final long serialVersionUID = 5750205775490483148L; | |
1533 | + | |
1534 | + /** Gets the full name of the descriptor where the error occurred. */ | |
1535 | + public String getProblemSymbolName() { return name; } | |
1536 | + | |
1537 | + /** | |
1538 | + * Gets the the protocol message representation of the invalid descriptor. | |
1539 | + */ | |
1540 | + public Message getProblemProto() { return proto; } | |
1541 | + | |
1542 | + /** | |
1543 | + * Gets a human-readable description of the error. | |
1544 | + */ | |
1545 | + public String getDescription() { return description; } | |
1546 | + | |
1547 | + private final String name; | |
1548 | + private final Message proto; | |
1549 | + private final String description; | |
1550 | + | |
1551 | + private DescriptorValidationException( | |
1552 | + final GenericDescriptor problemDescriptor, | |
1553 | + final String description) { | |
1554 | + super(problemDescriptor.getFullName() + ": " + description); | |
1555 | + | |
1556 | + // Note that problemDescriptor may be partially uninitialized, so we | |
1557 | + // don't want to expose it directly to the user. So, we only provide | |
1558 | + // the name and the original proto. | |
1559 | + name = problemDescriptor.getFullName(); | |
1560 | + proto = problemDescriptor.toProto(); | |
1561 | + this.description = description; | |
1562 | + } | |
1563 | + | |
1564 | + private DescriptorValidationException( | |
1565 | + final GenericDescriptor problemDescriptor, | |
1566 | + final String description, | |
1567 | + final Throwable cause) { | |
1568 | + this(problemDescriptor, description); | |
1569 | + initCause(cause); | |
1570 | + } | |
1571 | + | |
1572 | + private DescriptorValidationException( | |
1573 | + final FileDescriptor problemDescriptor, | |
1574 | + final String description) { | |
1575 | + super(problemDescriptor.getName() + ": " + description); | |
1576 | + | |
1577 | + // Note that problemDescriptor may be partially uninitialized, so we | |
1578 | + // don't want to expose it directly to the user. So, we only provide | |
1579 | + // the name and the original proto. | |
1580 | + name = problemDescriptor.getName(); | |
1581 | + proto = problemDescriptor.toProto(); | |
1582 | + this.description = description; | |
1583 | + } | |
1584 | + } | |
1585 | + | |
1586 | + // ================================================================= | |
1587 | + | |
1588 | + /** | |
1589 | + * A private helper class which contains lookup tables containing all the | |
1590 | + * descriptors defined in a particular file. | |
1591 | + */ | |
1592 | + private static final class DescriptorPool { | |
1593 | + DescriptorPool(final FileDescriptor[] dependencies) { | |
1594 | + this.dependencies = new DescriptorPool[dependencies.length]; | |
1595 | + | |
1596 | + for (int i = 0; i < dependencies.length; i++) { | |
1597 | + this.dependencies[i] = dependencies[i].pool; | |
1598 | + } | |
1599 | + | |
1600 | + for (final FileDescriptor dependency : dependencies) { | |
1601 | + try { | |
1602 | + addPackage(dependency.getPackage(), dependency); | |
1603 | + } catch (DescriptorValidationException e) { | |
1604 | + // Can't happen, because addPackage() only fails when the name | |
1605 | + // conflicts with a non-package, but we have not yet added any | |
1606 | + // non-packages at this point. | |
1607 | + assert false; | |
1608 | + } | |
1609 | + } | |
1610 | + } | |
1611 | + | |
1612 | + private final DescriptorPool[] dependencies; | |
1613 | + | |
1614 | + private final Map<String, GenericDescriptor> descriptorsByName = | |
1615 | + new HashMap<String, GenericDescriptor>(); | |
1616 | + private final Map<DescriptorIntPair, FieldDescriptor> fieldsByNumber = | |
1617 | + new HashMap<DescriptorIntPair, FieldDescriptor>(); | |
1618 | + private final Map<DescriptorIntPair, EnumValueDescriptor> enumValuesByNumber | |
1619 | + = new HashMap<DescriptorIntPair, EnumValueDescriptor>(); | |
1620 | + | |
1621 | + /** Find a generic descriptor by fully-qualified name. */ | |
1622 | + GenericDescriptor findSymbol(final String fullName) { | |
1623 | + GenericDescriptor result = descriptorsByName.get(fullName); | |
1624 | + if (result != null) { | |
1625 | + return result; | |
1626 | + } | |
1627 | + | |
1628 | + for (final DescriptorPool dependency : dependencies) { | |
1629 | + result = dependency.descriptorsByName.get(fullName); | |
1630 | + if (result != null) { | |
1631 | + return result; | |
1632 | + } | |
1633 | + } | |
1634 | + | |
1635 | + return null; | |
1636 | + } | |
1637 | + | |
1638 | + /** | |
1639 | + * Look up a descriptor by name, relative to some other descriptor. | |
1640 | + * The name may be fully-qualified (with a leading '.'), | |
1641 | + * partially-qualified, or unqualified. C++-like name lookup semantics | |
1642 | + * are used to search for the matching descriptor. | |
1643 | + */ | |
1644 | + GenericDescriptor lookupSymbol(final String name, | |
1645 | + final GenericDescriptor relativeTo) | |
1646 | + throws DescriptorValidationException { | |
1647 | + // TODO(kenton): This could be optimized in a number of ways. | |
1648 | + | |
1649 | + GenericDescriptor result; | |
1650 | + if (name.startsWith(".")) { | |
1651 | + // Fully-qualified name. | |
1652 | + result = findSymbol(name.substring(1)); | |
1653 | + } else { | |
1654 | + // If "name" is a compound identifier, we want to search for the | |
1655 | + // first component of it, then search within it for the rest. | |
1656 | + final int firstPartLength = name.indexOf('.'); | |
1657 | + final String firstPart; | |
1658 | + if (firstPartLength == -1) { | |
1659 | + firstPart = name; | |
1660 | + } else { | |
1661 | + firstPart = name.substring(0, firstPartLength); | |
1662 | + } | |
1663 | + | |
1664 | + // We will search each parent scope of "relativeTo" looking for the | |
1665 | + // symbol. | |
1666 | + final StringBuilder scopeToTry = | |
1667 | + new StringBuilder(relativeTo.getFullName()); | |
1668 | + | |
1669 | + while (true) { | |
1670 | + // Chop off the last component of the scope. | |
1671 | + final int dotpos = scopeToTry.lastIndexOf("."); | |
1672 | + if (dotpos == -1) { | |
1673 | + result = findSymbol(name); | |
1674 | + break; | |
1675 | + } else { | |
1676 | + scopeToTry.setLength(dotpos + 1); | |
1677 | + | |
1678 | + // Append firstPart and try to find. | |
1679 | + scopeToTry.append(firstPart); | |
1680 | + result = findSymbol(scopeToTry.toString()); | |
1681 | + | |
1682 | + if (result != null) { | |
1683 | + if (firstPartLength != -1) { | |
1684 | + // We only found the first part of the symbol. Now look for | |
1685 | + // the whole thing. If this fails, we *don't* want to keep | |
1686 | + // searching parent scopes. | |
1687 | + scopeToTry.setLength(dotpos + 1); | |
1688 | + scopeToTry.append(name); | |
1689 | + result = findSymbol(scopeToTry.toString()); | |
1690 | + } | |
1691 | + break; | |
1692 | + } | |
1693 | + | |
1694 | + // Not found. Remove the name so we can try again. | |
1695 | + scopeToTry.setLength(dotpos); | |
1696 | + } | |
1697 | + } | |
1698 | + } | |
1699 | + | |
1700 | + if (result == null) { | |
1701 | + throw new DescriptorValidationException(relativeTo, | |
1702 | + '\"' + name + "\" is not defined."); | |
1703 | + } else { | |
1704 | + return result; | |
1705 | + } | |
1706 | + } | |
1707 | + | |
1708 | + /** | |
1709 | + * Adds a symbol to the symbol table. If a symbol with the same name | |
1710 | + * already exists, throws an error. | |
1711 | + */ | |
1712 | + void addSymbol(final GenericDescriptor descriptor) | |
1713 | + throws DescriptorValidationException { | |
1714 | + validateSymbolName(descriptor); | |
1715 | + | |
1716 | + final String fullName = descriptor.getFullName(); | |
1717 | + final int dotpos = fullName.lastIndexOf('.'); | |
1718 | + | |
1719 | + final GenericDescriptor old = descriptorsByName.put(fullName, descriptor); | |
1720 | + if (old != null) { | |
1721 | + descriptorsByName.put(fullName, old); | |
1722 | + | |
1723 | + if (descriptor.getFile() == old.getFile()) { | |
1724 | + if (dotpos == -1) { | |
1725 | + throw new DescriptorValidationException(descriptor, | |
1726 | + '\"' + fullName + "\" is already defined."); | |
1727 | + } else { | |
1728 | + throw new DescriptorValidationException(descriptor, | |
1729 | + '\"' + fullName.substring(dotpos + 1) + | |
1730 | + "\" is already defined in \"" + | |
1731 | + fullName.substring(0, dotpos) + "\"."); | |
1732 | + } | |
1733 | + } else { | |
1734 | + throw new DescriptorValidationException(descriptor, | |
1735 | + '\"' + fullName + "\" is already defined in file \"" + | |
1736 | + old.getFile().getName() + "\"."); | |
1737 | + } | |
1738 | + } | |
1739 | + } | |
1740 | + | |
1741 | + /** | |
1742 | + * Represents a package in the symbol table. We use PackageDescriptors | |
1743 | + * just as placeholders so that someone cannot define, say, a message type | |
1744 | + * that has the same name as an existing package. | |
1745 | + */ | |
1746 | + private static final class PackageDescriptor implements GenericDescriptor { | |
1747 | + public Message toProto() { return file.toProto(); } | |
1748 | + public String getName() { return name; } | |
1749 | + public String getFullName() { return fullName; } | |
1750 | + public FileDescriptor getFile() { return file; } | |
1751 | + | |
1752 | + PackageDescriptor(final String name, final String fullName, | |
1753 | + final FileDescriptor file) { | |
1754 | + this.file = file; | |
1755 | + this.fullName = fullName; | |
1756 | + this.name = name; | |
1757 | + } | |
1758 | + | |
1759 | + private final String name; | |
1760 | + private final String fullName; | |
1761 | + private final FileDescriptor file; | |
1762 | + } | |
1763 | + | |
1764 | + /** | |
1765 | + * Adds a package to the symbol tables. If a package by the same name | |
1766 | + * already exists, that is fine, but if some other kind of symbol exists | |
1767 | + * under the same name, an exception is thrown. If the package has | |
1768 | + * multiple components, this also adds the parent package(s). | |
1769 | + */ | |
1770 | + void addPackage(final String fullName, final FileDescriptor file) | |
1771 | + throws DescriptorValidationException { | |
1772 | + final int dotpos = fullName.lastIndexOf('.'); | |
1773 | + final String name; | |
1774 | + if (dotpos == -1) { | |
1775 | + name = fullName; | |
1776 | + } else { | |
1777 | + addPackage(fullName.substring(0, dotpos), file); | |
1778 | + name = fullName.substring(dotpos + 1); | |
1779 | + } | |
1780 | + | |
1781 | + final GenericDescriptor old = | |
1782 | + descriptorsByName.put(fullName, | |
1783 | + new PackageDescriptor(name, fullName, file)); | |
1784 | + if (old != null) { | |
1785 | + descriptorsByName.put(fullName, old); | |
1786 | + if (!(old instanceof PackageDescriptor)) { | |
1787 | + throw new DescriptorValidationException(file, | |
1788 | + '\"' + name + "\" is already defined (as something other than a " | |
1789 | + + "package) in file \"" + old.getFile().getName() + "\"."); | |
1790 | + } | |
1791 | + } | |
1792 | + } | |
1793 | + | |
1794 | + /** A (GenericDescriptor, int) pair, used as a map key. */ | |
1795 | + private static final class DescriptorIntPair { | |
1796 | + private final GenericDescriptor descriptor; | |
1797 | + private final int number; | |
1798 | + | |
1799 | + DescriptorIntPair(final GenericDescriptor descriptor, final int number) { | |
1800 | + this.descriptor = descriptor; | |
1801 | + this.number = number; | |
1802 | + } | |
1803 | + | |
1804 | + @Override | |
1805 | + public int hashCode() { | |
1806 | + return descriptor.hashCode() * ((1 << 16) - 1) + number; | |
1807 | + } | |
1808 | + @Override | |
1809 | + public boolean equals(final Object obj) { | |
1810 | + if (!(obj instanceof DescriptorIntPair)) { | |
1811 | + return false; | |
1812 | + } | |
1813 | + final DescriptorIntPair other = (DescriptorIntPair)obj; | |
1814 | + return descriptor == other.descriptor && number == other.number; | |
1815 | + } | |
1816 | + } | |
1817 | + | |
1818 | + /** | |
1819 | + * Adds a field to the fieldsByNumber table. Throws an exception if a | |
1820 | + * field with hte same containing type and number already exists. | |
1821 | + */ | |
1822 | + void addFieldByNumber(final FieldDescriptor field) | |
1823 | + throws DescriptorValidationException { | |
1824 | + final DescriptorIntPair key = | |
1825 | + new DescriptorIntPair(field.getContainingType(), field.getNumber()); | |
1826 | + final FieldDescriptor old = fieldsByNumber.put(key, field); | |
1827 | + if (old != null) { | |
1828 | + fieldsByNumber.put(key, old); | |
1829 | + throw new DescriptorValidationException(field, | |
1830 | + "Field number " + field.getNumber() + | |
1831 | + "has already been used in \"" + | |
1832 | + field.getContainingType().getFullName() + | |
1833 | + "\" by field \"" + old.getName() + "\"."); | |
1834 | + } | |
1835 | + } | |
1836 | + | |
1837 | + /** | |
1838 | + * Adds an enum value to the enumValuesByNumber table. If an enum value | |
1839 | + * with the same type and number already exists, does nothing. (This is | |
1840 | + * allowed; the first value define with the number takes precedence.) | |
1841 | + */ | |
1842 | + void addEnumValueByNumber(final EnumValueDescriptor value) { | |
1843 | + final DescriptorIntPair key = | |
1844 | + new DescriptorIntPair(value.getType(), value.getNumber()); | |
1845 | + final EnumValueDescriptor old = enumValuesByNumber.put(key, value); | |
1846 | + if (old != null) { | |
1847 | + enumValuesByNumber.put(key, old); | |
1848 | + // Not an error: Multiple enum values may have the same number, but | |
1849 | + // we only want the first one in the map. | |
1850 | + } | |
1851 | + } | |
1852 | + | |
1853 | + /** | |
1854 | + * Verifies that the descriptor's name is valid (i.e. it contains only | |
1855 | + * letters, digits, and underscores, and does not start with a digit). | |
1856 | + */ | |
1857 | + static void validateSymbolName(final GenericDescriptor descriptor) | |
1858 | + throws DescriptorValidationException { | |
1859 | + final String name = descriptor.getName(); | |
1860 | + if (name.length() == 0) { | |
1861 | + throw new DescriptorValidationException(descriptor, "Missing name."); | |
1862 | + } else { | |
1863 | + boolean valid = true; | |
1864 | + for (int i = 0; i < name.length(); i++) { | |
1865 | + final char c = name.charAt(i); | |
1866 | + // Non-ASCII characters are not valid in protobuf identifiers, even | |
1867 | + // if they are letters or digits. | |
1868 | + if (c >= 128) { | |
1869 | + valid = false; | |
1870 | + } | |
1871 | + // First character must be letter or _. Subsequent characters may | |
1872 | + // be letters, numbers, or digits. | |
1873 | + if (Character.isLetter(c) || c == '_' || | |
1874 | + (Character.isDigit(c) && i > 0)) { | |
1875 | + // Valid | |
1876 | + } else { | |
1877 | + valid = false; | |
1878 | + } | |
1879 | + } | |
1880 | + if (!valid) { | |
1881 | + throw new DescriptorValidationException(descriptor, | |
1882 | + '\"' + name + "\" is not a valid identifier."); | |
1883 | + } | |
1884 | + } | |
1885 | + } | |
1886 | + } | |
1887 | +} |
@@ -0,0 +1,438 @@ | ||
1 | +// Protocol Buffers - Google's data interchange format | |
2 | +// Copyright 2008 Google Inc. All rights reserved. | |
3 | +// http://code.google.com/p/protobuf/ | |
4 | +// | |
5 | +// Redistribution and use in source and binary forms, with or without | |
6 | +// modification, are permitted provided that the following conditions are | |
7 | +// met: | |
8 | +// | |
9 | +// * Redistributions of source code must retain the above copyright | |
10 | +// notice, this list of conditions and the following disclaimer. | |
11 | +// * Redistributions in binary form must reproduce the above | |
12 | +// copyright notice, this list of conditions and the following disclaimer | |
13 | +// in the documentation and/or other materials provided with the | |
14 | +// distribution. | |
15 | +// * Neither the name of Google Inc. nor the names of its | |
16 | +// contributors may be used to endorse or promote products derived from | |
17 | +// this software without specific prior written permission. | |
18 | +// | |
19 | +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
20 | +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
21 | +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
22 | +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
23 | +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
24 | +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
25 | +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
26 | +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
27 | +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
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. | |
30 | + | |
31 | +package com.google.protobuf; | |
32 | + | |
33 | +import com.google.protobuf.Descriptors.Descriptor; | |
34 | +import com.google.protobuf.Descriptors.FieldDescriptor; | |
35 | + | |
36 | +import java.io.InputStream; | |
37 | +import java.io.IOException; | |
38 | +import java.util.Map; | |
39 | + | |
40 | +/** | |
41 | + * An implementation of {@link Message} that can represent arbitrary types, | |
42 | + * given a {@link Descriptors.Descriptor}. | |
43 | + * | |
44 | + * @author kenton@google.com Kenton Varda | |
45 | + */ | |
46 | +public final class DynamicMessage extends AbstractMessage { | |
47 | + private final Descriptor type; | |
48 | + private final FieldSet<FieldDescriptor> fields; | |
49 | + private final UnknownFieldSet unknownFields; | |
50 | + private int memoizedSize = -1; | |
51 | + | |
52 | + /** | |
53 | + * Construct a {@code DynamicMessage} using the given {@code FieldSet}. | |
54 | + */ | |
55 | + private DynamicMessage(Descriptor type, FieldSet<FieldDescriptor> fields, | |
56 | + UnknownFieldSet unknownFields) { | |
57 | + this.type = type; | |
58 | + this.fields = fields; | |
59 | + this.unknownFields = unknownFields; | |
60 | + } | |
61 | + | |
62 | + /** | |
63 | + * Get a {@code DynamicMessage} representing the default instance of the | |
64 | + * given type. | |
65 | + */ | |
66 | + public static DynamicMessage getDefaultInstance(Descriptor type) { | |
67 | + return new DynamicMessage(type, FieldSet.<FieldDescriptor>emptySet(), | |
68 | + UnknownFieldSet.getDefaultInstance()); | |
69 | + } | |
70 | + | |
71 | + /** Parse a message of the given type from the given input stream. */ | |
72 | + public static DynamicMessage parseFrom(Descriptor type, | |
73 | + CodedInputStream input) | |
74 | + throws IOException { | |
75 | + return newBuilder(type).mergeFrom(input).buildParsed(); | |
76 | + } | |
77 | + | |
78 | + /** Parse a message of the given type from the given input stream. */ | |
79 | + public static DynamicMessage parseFrom( | |
80 | + Descriptor type, | |
81 | + CodedInputStream input, | |
82 | + ExtensionRegistry extensionRegistry) | |
83 | + throws IOException { | |
84 | + return newBuilder(type).mergeFrom(input, extensionRegistry).buildParsed(); | |
85 | + } | |
86 | + | |
87 | + /** Parse {@code data} as a message of the given type and return it. */ | |
88 | + public static DynamicMessage parseFrom(Descriptor type, ByteString data) | |
89 | + throws InvalidProtocolBufferException { | |
90 | + return newBuilder(type).mergeFrom(data).buildParsed(); | |
91 | + } | |
92 | + | |
93 | + /** Parse {@code data} as a message of the given type and return it. */ | |
94 | + public static DynamicMessage parseFrom(Descriptor type, ByteString data, | |
95 | + ExtensionRegistry extensionRegistry) | |
96 | + throws InvalidProtocolBufferException { | |
97 | + return newBuilder(type).mergeFrom(data, extensionRegistry).buildParsed(); | |
98 | + } | |
99 | + | |
100 | + /** Parse {@code data} as a message of the given type and return it. */ | |
101 | + public static DynamicMessage parseFrom(Descriptor type, byte[] data) | |
102 | + throws InvalidProtocolBufferException { | |
103 | + return newBuilder(type).mergeFrom(data).buildParsed(); | |
104 | + } | |
105 | + | |
106 | + /** Parse {@code data} as a message of the given type and return it. */ | |
107 | + public static DynamicMessage parseFrom(Descriptor type, byte[] data, | |
108 | + ExtensionRegistry extensionRegistry) | |
109 | + throws InvalidProtocolBufferException { | |
110 | + return newBuilder(type).mergeFrom(data, extensionRegistry).buildParsed(); | |
111 | + } | |
112 | + | |
113 | + /** Parse a message of the given type from {@code input} and return it. */ | |
114 | + public static DynamicMessage parseFrom(Descriptor type, InputStream input) | |
115 | + throws IOException { | |
116 | + return newBuilder(type).mergeFrom(input).buildParsed(); | |
117 | + } | |
118 | + | |
119 | + /** Parse a message of the given type from {@code input} and return it. */ | |
120 | + public static DynamicMessage parseFrom(Descriptor type, InputStream input, | |
121 | + ExtensionRegistry extensionRegistry) | |
122 | + throws IOException { | |
123 | + return newBuilder(type).mergeFrom(input, extensionRegistry).buildParsed(); | |
124 | + } | |
125 | + | |
126 | + /** Construct a {@link Message.Builder} for the given type. */ | |
127 | + public static Builder newBuilder(Descriptor type) { | |
128 | + return new Builder(type); | |
129 | + } | |
130 | + | |
131 | + /** | |
132 | + * Construct a {@link Message.Builder} for a message of the same type as | |
133 | + * {@code prototype}, and initialize it with {@code prototype}'s contents. | |
134 | + */ | |
135 | + public static Builder newBuilder(Message prototype) { | |
136 | + return new Builder(prototype.getDescriptorForType()).mergeFrom(prototype); | |
137 | + } | |
138 | + | |
139 | + // ----------------------------------------------------------------- | |
140 | + // Implementation of Message interface. | |
141 | + | |
142 | + public Descriptor getDescriptorForType() { | |
143 | + return type; | |
144 | + } | |
145 | + | |
146 | + public DynamicMessage getDefaultInstanceForType() { | |
147 | + return getDefaultInstance(type); | |
148 | + } | |
149 | + | |
150 | + public Map<FieldDescriptor, Object> getAllFields() { | |
151 | + return fields.getAllFields(); | |
152 | + } | |
153 | + | |
154 | + public boolean hasField(FieldDescriptor field) { | |
155 | + verifyContainingType(field); | |
156 | + return fields.hasField(field); | |
157 | + } | |
158 | + | |
159 | + public Object getField(FieldDescriptor field) { | |
160 | + verifyContainingType(field); | |
161 | + Object result = fields.getField(field); | |
162 | + if (result == null) { | |
163 | + if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { | |
164 | + result = getDefaultInstance(field.getMessageType()); | |
165 | + } else { | |
166 | + result = field.getDefaultValue(); | |
167 | + } | |
168 | + } | |
169 | + return result; | |
170 | + } | |
171 | + | |
172 | + public int getRepeatedFieldCount(FieldDescriptor field) { | |
173 | + verifyContainingType(field); | |
174 | + return fields.getRepeatedFieldCount(field); | |
175 | + } | |
176 | + | |
177 | + public Object getRepeatedField(FieldDescriptor field, int index) { | |
178 | + verifyContainingType(field); | |
179 | + return fields.getRepeatedField(field, index); | |
180 | + } | |
181 | + | |
182 | + public UnknownFieldSet getUnknownFields() { | |
183 | + return unknownFields; | |
184 | + } | |
185 | + | |
186 | + private static boolean isInitialized(Descriptor type, | |
187 | + FieldSet<FieldDescriptor> fields) { | |
188 | + // Check that all required fields are present. | |
189 | + for (final FieldDescriptor field : type.getFields()) { | |
190 | + if (field.isRequired()) { | |
191 | + if (!fields.hasField(field)) { | |
192 | + return false; | |
193 | + } | |
194 | + } | |
195 | + } | |
196 | + | |
197 | + // Check that embedded messages are initialized. | |
198 | + return fields.isInitialized(); | |
199 | + } | |
200 | + | |
201 | + public boolean isInitialized() { | |
202 | + return isInitialized(type, fields); | |
203 | + } | |
204 | + | |
205 | + public void writeTo(CodedOutputStream output) throws IOException { | |
206 | + if (type.getOptions().getMessageSetWireFormat()) { | |
207 | + fields.writeMessageSetTo(output); | |
208 | + unknownFields.writeAsMessageSetTo(output); | |
209 | + } else { | |
210 | + fields.writeTo(output); | |
211 | + unknownFields.writeTo(output); | |
212 | + } | |
213 | + } | |
214 | + | |
215 | + public int getSerializedSize() { | |
216 | + int size = memoizedSize; | |
217 | + if (size != -1) return size; | |
218 | + | |
219 | + if (type.getOptions().getMessageSetWireFormat()) { | |
220 | + size = fields.getMessageSetSerializedSize(); | |
221 | + size += unknownFields.getSerializedSizeAsMessageSet(); | |
222 | + } else { | |
223 | + size = fields.getSerializedSize(); | |
224 | + size += unknownFields.getSerializedSize(); | |
225 | + } | |
226 | + | |
227 | + memoizedSize = size; | |
228 | + return size; | |
229 | + } | |
230 | + | |
231 | + public Builder newBuilderForType() { | |
232 | + return new Builder(type); | |
233 | + } | |
234 | + | |
235 | + public Builder toBuilder() { | |
236 | + return newBuilderForType().mergeFrom(this); | |
237 | + } | |
238 | + | |
239 | + /** Verifies that the field is a field of this message. */ | |
240 | + private void verifyContainingType(FieldDescriptor field) { | |
241 | + if (field.getContainingType() != type) { | |
242 | + throw new IllegalArgumentException( | |
243 | + "FieldDescriptor does not match message type."); | |
244 | + } | |
245 | + } | |
246 | + | |
247 | + // ================================================================= | |
248 | + | |
249 | + /** | |
250 | + * Builder for {@link DynamicMessage}s. | |
251 | + */ | |
252 | + public static final class Builder extends AbstractMessage.Builder<Builder> { | |
253 | + private final Descriptor type; | |
254 | + private FieldSet<FieldDescriptor> fields; | |
255 | + private UnknownFieldSet unknownFields; | |
256 | + | |
257 | + /** Construct a {@code Builder} for the given type. */ | |
258 | + private Builder(Descriptor type) { | |
259 | + this.type = type; | |
260 | + this.fields = FieldSet.newFieldSet(); | |
261 | + this.unknownFields = UnknownFieldSet.getDefaultInstance(); | |
262 | + } | |
263 | + | |
264 | + // --------------------------------------------------------------- | |
265 | + // Implementation of Message.Builder interface. | |
266 | + | |
267 | + public Builder clear() { | |
268 | + if (fields == null) { | |
269 | + throw new IllegalStateException("Cannot call clear() after build()."); | |
270 | + } | |
271 | + fields.clear(); | |
272 | + return this; | |
273 | + } | |
274 | + | |
275 | + public Builder mergeFrom(Message other) { | |
276 | + if (other instanceof DynamicMessage) { | |
277 | + // This should be somewhat faster than calling super.mergeFrom(). | |
278 | + DynamicMessage otherDynamicMessage = (DynamicMessage) other; | |
279 | + if (otherDynamicMessage.type != type) { | |
280 | + throw new IllegalArgumentException( | |
281 | + "mergeFrom(Message) can only merge messages of the same type."); | |
282 | + } | |
283 | + fields.mergeFrom(otherDynamicMessage.fields); | |
284 | + mergeUnknownFields(otherDynamicMessage.unknownFields); | |
285 | + return this; | |
286 | + } else { | |
287 | + return super.mergeFrom(other); | |
288 | + } | |
289 | + } | |
290 | + | |
291 | + public DynamicMessage build() { | |
292 | + // If fields == null, we'll throw an appropriate exception later. | |
293 | + if (fields != null && !isInitialized()) { | |
294 | + throw newUninitializedMessageException( | |
295 | + new DynamicMessage(type, fields, unknownFields)); | |
296 | + } | |
297 | + return buildPartial(); | |
298 | + } | |
299 | + | |
300 | + /** | |
301 | + * Helper for DynamicMessage.parseFrom() methods to call. Throws | |
302 | + * {@link InvalidProtocolBufferException} instead of | |
303 | + * {@link UninitializedMessageException}. | |
304 | + */ | |
305 | + private DynamicMessage buildParsed() throws InvalidProtocolBufferException { | |
306 | + if (!isInitialized()) { | |
307 | + throw newUninitializedMessageException( | |
308 | + new DynamicMessage(type, fields, unknownFields)) | |
309 | + .asInvalidProtocolBufferException(); | |
310 | + } | |
311 | + return buildPartial(); | |
312 | + } | |
313 | + | |
314 | + public DynamicMessage buildPartial() { | |
315 | + if (fields == null) { | |
316 | + throw new IllegalStateException( | |
317 | + "build() has already been called on this Builder."); | |
318 | + } | |
319 | + fields.makeImmutable(); | |
320 | + DynamicMessage result = | |
321 | + new DynamicMessage(type, fields, unknownFields); | |
322 | + fields = null; | |
323 | + unknownFields = null; | |
324 | + return result; | |
325 | + } | |
326 | + | |
327 | + public Builder clone() { | |
328 | + Builder result = new Builder(type); | |
329 | + result.fields.mergeFrom(fields); | |
330 | + return result; | |
331 | + } | |
332 | + | |
333 | + public boolean isInitialized() { | |
334 | + return DynamicMessage.isInitialized(type, fields); | |
335 | + } | |
336 | + | |
337 | + public Descriptor getDescriptorForType() { | |
338 | + return type; | |
339 | + } | |
340 | + | |
341 | + public DynamicMessage getDefaultInstanceForType() { | |
342 | + return getDefaultInstance(type); | |
343 | + } | |
344 | + | |
345 | + public Map<FieldDescriptor, Object> getAllFields() { | |
346 | + return fields.getAllFields(); | |
347 | + } | |
348 | + | |
349 | + public Builder newBuilderForField(FieldDescriptor field) { | |
350 | + verifyContainingType(field); | |
351 | + | |
352 | + if (field.getJavaType() != FieldDescriptor.JavaType.MESSAGE) { | |
353 | + throw new IllegalArgumentException( | |
354 | + "newBuilderForField is only valid for fields with message type."); | |
355 | + } | |
356 | + | |
357 | + return new Builder(field.getMessageType()); | |
358 | + } | |
359 | + | |
360 | + public boolean hasField(FieldDescriptor field) { | |
361 | + verifyContainingType(field); | |
362 | + return fields.hasField(field); | |
363 | + } | |
364 | + | |
365 | + public Object getField(FieldDescriptor field) { | |
366 | + verifyContainingType(field); | |
367 | + Object result = fields.getField(field); | |
368 | + if (result == null) { | |
369 | + if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { | |
370 | + result = getDefaultInstance(field.getMessageType()); | |
371 | + } else { | |
372 | + result = field.getDefaultValue(); | |
373 | + } | |
374 | + } | |
375 | + return result; | |
376 | + } | |
377 | + | |
378 | + public Builder setField(FieldDescriptor field, Object value) { | |
379 | + verifyContainingType(field); | |
380 | + fields.setField(field, value); | |
381 | + return this; | |
382 | + } | |
383 | + | |
384 | + public Builder clearField(FieldDescriptor field) { | |
385 | + verifyContainingType(field); | |
386 | + fields.clearField(field); | |
387 | + return this; | |
388 | + } | |
389 | + | |
390 | + public int getRepeatedFieldCount(FieldDescriptor field) { | |
391 | + verifyContainingType(field); | |
392 | + return fields.getRepeatedFieldCount(field); | |
393 | + } | |
394 | + | |
395 | + public Object getRepeatedField(FieldDescriptor field, int index) { | |
396 | + verifyContainingType(field); | |
397 | + return fields.getRepeatedField(field, index); | |
398 | + } | |
399 | + | |
400 | + public Builder setRepeatedField(FieldDescriptor field, | |
401 | + int index, Object value) { | |
402 | + verifyContainingType(field); | |
403 | + fields.setRepeatedField(field, index, value); | |
404 | + return this; | |
405 | + } | |
406 | + | |
407 | + public Builder addRepeatedField(FieldDescriptor field, Object value) { | |
408 | + verifyContainingType(field); | |
409 | + fields.addRepeatedField(field, value); | |
410 | + return this; | |
411 | + } | |
412 | + | |
413 | + public UnknownFieldSet getUnknownFields() { | |
414 | + return unknownFields; | |
415 | + } | |
416 | + | |
417 | + public Builder setUnknownFields(UnknownFieldSet unknownFields) { | |
418 | + this.unknownFields = unknownFields; | |
419 | + return this; | |
420 | + } | |
421 | + | |
422 | + public Builder mergeUnknownFields(UnknownFieldSet unknownFields) { | |
423 | + this.unknownFields = | |
424 | + UnknownFieldSet.newBuilder(this.unknownFields) | |
425 | + .mergeFrom(unknownFields) | |
426 | + .build(); | |
427 | + return this; | |
428 | + } | |
429 | + | |
430 | + /** Verifies that the field is a field of this message. */ | |
431 | + private void verifyContainingType(FieldDescriptor field) { | |
432 | + if (field.getContainingType() != type) { | |
433 | + throw new IllegalArgumentException( | |
434 | + "FieldDescriptor does not match message type."); | |
435 | + } | |
436 | + } | |
437 | + } | |
438 | +} |
@@ -0,0 +1,266 @@ | ||
1 | +// Protocol Buffers - Google's data interchange format | |
2 | +// Copyright 2008 Google Inc. All rights reserved. | |
3 | +// http://code.google.com/p/protobuf/ | |
4 | +// | |
5 | +// Redistribution and use in source and binary forms, with or without | |
6 | +// modification, are permitted provided that the following conditions are | |
7 | +// met: | |
8 | +// | |
9 | +// * Redistributions of source code must retain the above copyright | |
10 | +// notice, this list of conditions and the following disclaimer. | |
11 | +// * Redistributions in binary form must reproduce the above | |
12 | +// copyright notice, this list of conditions and the following disclaimer | |
13 | +// in the documentation and/or other materials provided with the | |
14 | +// distribution. | |
15 | +// * Neither the name of Google Inc. nor the names of its | |
16 | +// contributors may be used to endorse or promote products derived from | |
17 | +// this software without specific prior written permission. | |
18 | +// | |
19 | +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
20 | +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
21 | +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
22 | +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
23 | +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
24 | +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
25 | +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
26 | +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
27 | +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
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. | |
30 | + | |
31 | +package com.google.protobuf; | |
32 | + | |
33 | +import com.google.protobuf.Descriptors.Descriptor; | |
34 | +import com.google.protobuf.Descriptors.FieldDescriptor; | |
35 | + | |
36 | +import java.util.Collections; | |
37 | +import java.util.HashMap; | |
38 | +import java.util.Map; | |
39 | + | |
40 | +/** | |
41 | + * A table of known extensions, searchable by name or field number. When | |
42 | + * parsing a protocol message that might have extensions, you must provide | |
43 | + * an {@code ExtensionRegistry} in which you have registered any extensions | |
44 | + * that you want to be able to parse. Otherwise, those extensions will just | |
45 | + * be treated like unknown fields. | |
46 | + * | |
47 | + * <p>For example, if you had the {@code .proto} file: | |
48 | + * | |
49 | + * <pre> | |
50 | + * option java_class = "MyProto"; | |
51 | + * | |
52 | + * message Foo { | |
53 | + * extensions 1000 to max; | |
54 | + * } | |
55 | + * | |
56 | + * extend Foo { | |
57 | + * optional int32 bar; | |
58 | + * } | |
59 | + * </pre> | |
60 | + * | |
61 | + * Then you might write code like: | |
62 | + * | |
63 | + * <pre> | |
64 | + * ExtensionRegistry registry = ExtensionRegistry.newInstance(); | |
65 | + * registry.add(MyProto.bar); | |
66 | + * MyProto.Foo message = MyProto.Foo.parseFrom(input, registry); | |
67 | + * </pre> | |
68 | + * | |
69 | + * <p>Background: | |
70 | + * | |
71 | + * <p>You might wonder why this is necessary. Two alternatives might come to | |
72 | + * mind. First, you might imagine a system where generated extensions are | |
73 | + * automatically registered when their containing classes are loaded. This | |
74 | + * is a popular technique, but is bad design; among other things, it creates a | |
75 | + * situation where behavior can change depending on what classes happen to be | |
76 | + * loaded. It also introduces a security vulnerability, because an | |
77 | + * unprivileged class could cause its code to be called unexpectedly from a | |
78 | + * privileged class by registering itself as an extension of the right type. | |
79 | + * | |
80 | + * <p>Another option you might consider is lazy parsing: do not parse an | |
81 | + * extension until it is first requested, at which point the caller must | |
82 | + * provide a type to use. This introduces a different set of problems. First, | |
83 | + * it would require a mutex lock any time an extension was accessed, which | |
84 | + * would be slow. Second, corrupt data would not be detected until first | |
85 | + * access, at which point it would be much harder to deal with it. Third, it | |
86 | + * could violate the expectation that message objects are immutable, since the | |
87 | + * type provided could be any arbitrary message class. An unprivileged user | |
88 | + * could take advantage of this to inject a mutable object into a message | |
89 | + * belonging to privileged code and create mischief. | |
90 | + * | |
91 | + * @author kenton@google.com Kenton Varda | |
92 | + */ | |
93 | +public final class ExtensionRegistry extends ExtensionRegistryLite { | |
94 | + /** Construct a new, empty instance. */ | |
95 | + public static ExtensionRegistry newInstance() { | |
96 | + return new ExtensionRegistry(); | |
97 | + } | |
98 | + | |
99 | + /** Get the unmodifiable singleton empty instance. */ | |
100 | + public static ExtensionRegistry getEmptyRegistry() { | |
101 | + return EMPTY; | |
102 | + } | |
103 | + | |
104 | + /** Returns an unmodifiable view of the registry. */ | |
105 | + @Override | |
106 | + public ExtensionRegistry getUnmodifiable() { | |
107 | + return new ExtensionRegistry(this); | |
108 | + } | |
109 | + | |
110 | + /** A (Descriptor, Message) pair, returned by lookup methods. */ | |
111 | + public static final class ExtensionInfo { | |
112 | + /** The extension's descriptor. */ | |
113 | + public final FieldDescriptor descriptor; | |
114 | + | |
115 | + /** | |
116 | + * A default instance of the extension's type, if it has a message type. | |
117 | + * Otherwise, {@code null}. | |
118 | + */ | |
119 | + public final Message defaultInstance; | |
120 | + | |
121 | + private ExtensionInfo(final FieldDescriptor descriptor) { | |
122 | + this.descriptor = descriptor; | |
123 | + defaultInstance = null; | |
124 | + } | |
125 | + private ExtensionInfo(final FieldDescriptor descriptor, | |
126 | + final Message defaultInstance) { | |
127 | + this.descriptor = descriptor; | |
128 | + this.defaultInstance = defaultInstance; | |
129 | + } | |
130 | + } | |
131 | + | |
132 | + /** | |
133 | + * Find an extension by fully-qualified field name, in the proto namespace. | |
134 | + * I.e. {@code result.descriptor.fullName()} will match {@code fullName} if | |
135 | + * a match is found. | |
136 | + * | |
137 | + * @return Information about the extension if found, or {@code null} | |
138 | + * otherwise. | |
139 | + */ | |
140 | + public ExtensionInfo findExtensionByName(final String fullName) { | |
141 | + return extensionsByName.get(fullName); | |
142 | + } | |
143 | + | |
144 | + /** | |
145 | + * Find an extension by containing type and field number. | |
146 | + * | |
147 | + * @return Information about the extension if found, or {@code null} | |
148 | + * otherwise. | |
149 | + */ | |
150 | + public ExtensionInfo findExtensionByNumber(final Descriptor containingType, | |
151 | + final int fieldNumber) { | |
152 | + return extensionsByNumber.get( | |
153 | + new DescriptorIntPair(containingType, fieldNumber)); | |
154 | + } | |
155 | + | |
156 | + /** Add an extension from a generated file to the registry. */ | |
157 | + public void add(final GeneratedMessage.GeneratedExtension<?, ?> extension) { | |
158 | + if (extension.getDescriptor().getJavaType() == | |
159 | + FieldDescriptor.JavaType.MESSAGE) { | |
160 | + if (extension.getMessageDefaultInstance() == null) { | |
161 | + throw new IllegalStateException( | |
162 | + "Registered message-type extension had null default instance: " + | |
163 | + extension.getDescriptor().getFullName()); | |
164 | + } | |
165 | + add(new ExtensionInfo(extension.getDescriptor(), | |
166 | + extension.getMessageDefaultInstance())); | |
167 | + } else { | |
168 | + add(new ExtensionInfo(extension.getDescriptor(), null)); | |
169 | + } | |
170 | + } | |
171 | + | |
172 | + /** Add a non-message-type extension to the registry by descriptor. */ | |
173 | + public void add(final FieldDescriptor type) { | |
174 | + if (type.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { | |
175 | + throw new IllegalArgumentException( | |
176 | + "ExtensionRegistry.add() must be provided a default instance when " + | |
177 | + "adding an embedded message extension."); | |
178 | + } | |
179 | + add(new ExtensionInfo(type, null)); | |
180 | + } | |
181 | + | |
182 | + /** Add a message-type extension to the registry by descriptor. */ | |
183 | + public void add(final FieldDescriptor type, final Message defaultInstance) { | |
184 | + if (type.getJavaType() != FieldDescriptor.JavaType.MESSAGE) { | |
185 | + throw new IllegalArgumentException( | |
186 | + "ExtensionRegistry.add() provided a default instance for a " + | |
187 | + "non-message extension."); | |
188 | + } | |
189 | + add(new ExtensionInfo(type, defaultInstance)); | |
190 | + } | |
191 | + | |
192 | + // ================================================================= | |
193 | + // Private stuff. | |
194 | + | |
195 | + private ExtensionRegistry() { | |
196 | + this.extensionsByName = new HashMap<String, ExtensionInfo>(); | |
197 | + this.extensionsByNumber = new HashMap<DescriptorIntPair, ExtensionInfo>(); | |
198 | + } | |
199 | + | |
200 | + private ExtensionRegistry(ExtensionRegistry other) { | |
201 | + super(other); | |
202 | + this.extensionsByName = Collections.unmodifiableMap(other.extensionsByName); | |
203 | + this.extensionsByNumber = | |
204 | + Collections.unmodifiableMap(other.extensionsByNumber); | |
205 | + } | |
206 | + | |
207 | + private final Map<String, ExtensionInfo> extensionsByName; | |
208 | + private final Map<DescriptorIntPair, ExtensionInfo> extensionsByNumber; | |
209 | + | |
210 | + private ExtensionRegistry(boolean empty) { | |
211 | + super(ExtensionRegistryLite.getEmptyRegistry()); | |
212 | + this.extensionsByName = Collections.<String, ExtensionInfo>emptyMap(); | |
213 | + this.extensionsByNumber = | |
214 | + Collections.<DescriptorIntPair, ExtensionInfo>emptyMap(); | |
215 | + } | |
216 | + private static final ExtensionRegistry EMPTY = new ExtensionRegistry(true); | |
217 | + | |
218 | + private void add(final ExtensionInfo extension) { | |
219 | + if (!extension.descriptor.isExtension()) { | |
220 | + throw new IllegalArgumentException( | |
221 | + "ExtensionRegistry.add() was given a FieldDescriptor for a regular " + | |
222 | + "(non-extension) field."); | |
223 | + } | |
224 | + | |
225 | + extensionsByName.put(extension.descriptor.getFullName(), extension); | |
226 | + extensionsByNumber.put( | |
227 | + new DescriptorIntPair(extension.descriptor.getContainingType(), | |
228 | + extension.descriptor.getNumber()), | |
229 | + extension); | |
230 | + | |
231 | + final FieldDescriptor field = extension.descriptor; | |
232 | + if (field.getContainingType().getOptions().getMessageSetWireFormat() && | |
233 | + field.getType() == FieldDescriptor.Type.MESSAGE && | |
234 | + field.isOptional() && | |
235 | + field.getExtensionScope() == field.getMessageType()) { | |
236 | + // This is an extension of a MessageSet type defined within the extension | |
237 | + // type's own scope. For backwards-compatibility, allow it to be looked | |
238 | + // up by type name. | |
239 | + extensionsByName.put(field.getMessageType().getFullName(), extension); | |
240 | + } | |
241 | + } | |
242 | + | |
243 | + /** A (GenericDescriptor, int) pair, used as a map key. */ | |
244 | + private static final class DescriptorIntPair { | |
245 | + private final Descriptor descriptor; | |
246 | + private final int number; | |
247 | + | |
248 | + DescriptorIntPair(final Descriptor descriptor, final int number) { | |
249 | + this.descriptor = descriptor; | |
250 | + this.number = number; | |
251 | + } | |
252 | + | |
253 | + @Override | |
254 | + public int hashCode() { | |
255 | + return descriptor.hashCode() * ((1 << 16) - 1) + number; | |
256 | + } | |
257 | + @Override | |
258 | + public boolean equals(final Object obj) { | |
259 | + if (!(obj instanceof DescriptorIntPair)) { | |
260 | + return false; | |
261 | + } | |
262 | + final DescriptorIntPair other = (DescriptorIntPair)obj; | |
263 | + return descriptor == other.descriptor && number == other.number; | |
264 | + } | |
265 | + } | |
266 | +} |
@@ -0,0 +1,169 @@ | ||
1 | +// Protocol Buffers - Google's data interchange format | |
2 | +// Copyright 2008 Google Inc. All rights reserved. | |
3 | +// http://code.google.com/p/protobuf/ | |
4 | +// | |
5 | +// Redistribution and use in source and binary forms, with or without | |
6 | +// modification, are permitted provided that the following conditions are | |
7 | +// met: | |
8 | +// | |
9 | +// * Redistributions of source code must retain the above copyright | |
10 | +// notice, this list of conditions and the following disclaimer. | |
11 | +// * Redistributions in binary form must reproduce the above | |
12 | +// copyright notice, this list of conditions and the following disclaimer | |
13 | +// in the documentation and/or other materials provided with the | |
14 | +// distribution. | |
15 | +// * Neither the name of Google Inc. nor the names of its | |
16 | +// contributors may be used to endorse or promote products derived from | |
17 | +// this software without specific prior written permission. | |
18 | +// | |
19 | +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
20 | +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
21 | +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
22 | +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
23 | +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
24 | +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
25 | +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
26 | +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
27 | +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
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. | |
30 | + | |
31 | +package com.google.protobuf; | |
32 | + | |
33 | +import java.util.Collections; | |
34 | +import java.util.HashMap; | |
35 | +import java.util.Map; | |
36 | + | |
37 | +/** | |
38 | + * Equivalent to {@link ExtensionRegistry} but supports only "lite" types. | |
39 | + * <p> | |
40 | + * If all of your types are lite types, then you only need to use | |
41 | + * {@code ExtensionRegistryLite}. Similarly, if all your types are regular | |
42 | + * types, then you only need {@link ExtensionRegistry}. Typically it does not | |
43 | + * make sense to mix the two, since if you have any regular types in your | |
44 | + * program, you then require the full runtime and lose all the benefits of | |
45 | + * the lite runtime, so you might as well make all your types be regular types. | |
46 | + * However, in some cases (e.g. when depending on multiple third-patry libraries | |
47 | + * where one uses lite types and one uses regular), you may find yourself | |
48 | + * wanting to mix the two. In this case things get more complicated. | |
49 | + * <p> | |
50 | + * There are three factors to consider: Whether the type being extended is | |
51 | + * lite, whether the embedded type (in the case of a message-typed extension) | |
52 | + * is lite, and whether the extension itself is lite. Since all three are | |
53 | + * declared in different files, they could all be different. Here are all | |
54 | + * the combinations and which type of registry to use: | |
55 | + * <pre> | |
56 | + * Extended type Inner type Extension Use registry | |
57 | + * ======================================================================= | |
58 | + * lite lite lite ExtensionRegistryLite | |
59 | + * lite regular lite ExtensionRegistry | |
60 | + * regular regular regular ExtensionRegistry | |
61 | + * all other combinations not supported | |
62 | + * </pre> | |
63 | + * <p> | |
64 | + * Note that just as regular types are not allowed to contain lite-type fields, | |
65 | + * they are also not allowed to contain lite-type extensions. This is because | |
66 | + * regular types must be fully accessible via reflection, which in turn means | |
67 | + * that all the inner messages must also support reflection. On the other hand, | |
68 | + * since regular types implement the entire lite interface, there is no problem | |
69 | + * with embedding regular types inside lite types. | |
70 | + * | |
71 | + * @author kenton@google.com Kenton Varda | |
72 | + */ | |
73 | +public class ExtensionRegistryLite { | |
74 | + /** Construct a new, empty instance. */ | |
75 | + public static ExtensionRegistryLite newInstance() { | |
76 | + return new ExtensionRegistryLite(); | |
77 | + } | |
78 | + | |
79 | + /** Get the unmodifiable singleton empty instance. */ | |
80 | + public static ExtensionRegistryLite getEmptyRegistry() { | |
81 | + return EMPTY; | |
82 | + } | |
83 | + | |
84 | + /** Returns an unmodifiable view of the registry. */ | |
85 | + public ExtensionRegistryLite getUnmodifiable() { | |
86 | + return new ExtensionRegistryLite(this); | |
87 | + } | |
88 | + | |
89 | + /** | |
90 | + * Find an extension by containing type and field number. | |
91 | + * | |
92 | + * @return Information about the extension if found, or {@code null} | |
93 | + * otherwise. | |
94 | + */ | |
95 | + @SuppressWarnings("unchecked") | |
96 | + public <ContainingType extends MessageLite> | |
97 | + GeneratedMessageLite.GeneratedExtension<ContainingType, ?> | |
98 | + findLiteExtensionByNumber( | |
99 | + final ContainingType containingTypeDefaultInstance, | |
100 | + final int fieldNumber) { | |
101 | + return (GeneratedMessageLite.GeneratedExtension<ContainingType, ?>) | |
102 | + extensionsByNumber.get( | |
103 | + new ObjectIntPair(containingTypeDefaultInstance, fieldNumber)); | |
104 | + } | |
105 | + | |
106 | + /** Add an extension from a lite generated file to the registry. */ | |
107 | + public final void add( | |
108 | + final GeneratedMessageLite.GeneratedExtension<?, ?> extension) { | |
109 | + extensionsByNumber.put( | |
110 | + new ObjectIntPair(extension.getContainingTypeDefaultInstance(), | |
111 | + extension.getNumber()), | |
112 | + extension); | |
113 | + } | |
114 | + | |
115 | + // ================================================================= | |
116 | + // Private stuff. | |
117 | + | |
118 | + // Constructors are package-private so that ExtensionRegistry can subclass | |
119 | + // this. | |
120 | + | |
121 | + ExtensionRegistryLite() { | |
122 | + this.extensionsByNumber = | |
123 | + new HashMap<ObjectIntPair, | |
124 | + GeneratedMessageLite.GeneratedExtension<?, ?>>(); | |
125 | + } | |
126 | + | |
127 | + ExtensionRegistryLite(ExtensionRegistryLite other) { | |
128 | + if (other == EMPTY) { | |
129 | + this.extensionsByNumber = Collections.emptyMap(); | |
130 | + } else { | |
131 | + this.extensionsByNumber = | |
132 | + Collections.unmodifiableMap(other.extensionsByNumber); | |
133 | + } | |
134 | + } | |
135 | + | |
136 | + private final Map<ObjectIntPair, | |
137 | + GeneratedMessageLite.GeneratedExtension<?, ?>> | |
138 | + extensionsByNumber; | |
139 | + | |
140 | + private ExtensionRegistryLite(boolean empty) { | |
141 | + this.extensionsByNumber = Collections.emptyMap(); | |
142 | + } | |
143 | + private static final ExtensionRegistryLite EMPTY = | |
144 | + new ExtensionRegistryLite(true); | |
145 | + | |
146 | + /** A (Object, int) pair, used as a map key. */ | |
147 | + private static final class ObjectIntPair { | |
148 | + private final Object object; | |
149 | + private final int number; | |
150 | + | |
151 | + ObjectIntPair(final Object object, final int number) { | |
152 | + this.object = object; | |
153 | + this.number = number; | |
154 | + } | |
155 | + | |
156 | + @Override | |
157 | + public int hashCode() { | |
158 | + return System.identityHashCode(object) * ((1 << 16) - 1) + number; | |
159 | + } | |
160 | + @Override | |
161 | + public boolean equals(final Object obj) { | |
162 | + if (!(obj instanceof ObjectIntPair)) { | |
163 | + return false; | |
164 | + } | |
165 | + final ObjectIntPair other = (ObjectIntPair)obj; | |
166 | + return object == other.object && number == other.number; | |
167 | + } | |
168 | + } | |
169 | +} |
@@ -0,0 +1,788 @@ | ||
1 | +// Protocol Buffers - Google's data interchange format | |
2 | +// Copyright 2008 Google Inc. All rights reserved. | |
3 | +// http://code.google.com/p/protobuf/ | |
4 | +// | |
5 | +// Redistribution and use in source and binary forms, with or without | |
6 | +// modification, are permitted provided that the following conditions are | |
7 | +// met: | |
8 | +// | |
9 | +// * Redistributions of source code must retain the above copyright | |
10 | +// notice, this list of conditions and the following disclaimer. | |
11 | +// * Redistributions in binary form must reproduce the above | |
12 | +// copyright notice, this list of conditions and the following disclaimer | |
13 | +// in the documentation and/or other materials provided with the | |
14 | +// distribution. | |
15 | +// * Neither the name of Google Inc. nor the names of its | |
16 | +// contributors may be used to endorse or promote products derived from | |
17 | +// this software without specific prior written permission. | |
18 | +// | |
19 | +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
20 | +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
21 | +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
22 | +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
23 | +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
24 | +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
25 | +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
26 | +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
27 | +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
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. | |
30 | + | |
31 | +package com.google.protobuf; | |
32 | + | |
33 | +import java.util.ArrayList; | |
34 | +import java.util.Collections; | |
35 | +import java.util.Iterator; | |
36 | +import java.util.List; | |
37 | +import java.util.Map; | |
38 | +import java.io.IOException; | |
39 | + | |
40 | +/** | |
41 | + * A class which represents an arbitrary set of fields of some message type. | |
42 | + * This is used to implement {@link DynamicMessage}, and also to represent | |
43 | + * extensions in {@link GeneratedMessage}. This class is package-private, | |
44 | + * since outside users should probably be using {@link DynamicMessage}. | |
45 | + * | |
46 | + * @author kenton@google.com Kenton Varda | |
47 | + */ | |
48 | +final class FieldSet<FieldDescriptorType extends | |
49 | + FieldSet.FieldDescriptorLite<FieldDescriptorType>> { | |
50 | + /** | |
51 | + * Interface for a FieldDescriptor or lite extension descriptor. This | |
52 | + * prevents FieldSet from depending on {@link Descriptors.FieldDescriptor}. | |
53 | + */ | |
54 | + public interface FieldDescriptorLite<T extends FieldDescriptorLite<T>> | |
55 | + extends Comparable<T> { | |
56 | + int getNumber(); | |
57 | + WireFormat.FieldType getLiteType(); | |
58 | + WireFormat.JavaType getLiteJavaType(); | |
59 | + boolean isRepeated(); | |
60 | + boolean isPacked(); | |
61 | + Internal.EnumLiteMap<?> getEnumType(); | |
62 | + | |
63 | + // If getLiteJavaType() == MESSAGE, this merges a message object of the | |
64 | + // type into a builder of the type. Returns {@code to}. | |
65 | + MessageLite.Builder internalMergeFrom( | |
66 | + MessageLite.Builder to, MessageLite from); | |
67 | + } | |
68 | + | |
69 | + private final SmallSortedMap<FieldDescriptorType, Object> fields; | |
70 | + private boolean isImmutable; | |
71 | + | |
72 | + /** Construct a new FieldSet. */ | |
73 | + private FieldSet() { | |
74 | + this.fields = SmallSortedMap.newFieldMap(16); | |
75 | + } | |
76 | + | |
77 | + /** | |
78 | + * Construct an empty FieldSet. This is only used to initialize | |
79 | + * DEFAULT_INSTANCE. | |
80 | + */ | |
81 | + private FieldSet(final boolean dummy) { | |
82 | + this.fields = SmallSortedMap.newFieldMap(0); | |
83 | + makeImmutable(); | |
84 | + } | |
85 | + | |
86 | + /** Construct a new FieldSet. */ | |
87 | + public static <T extends FieldSet.FieldDescriptorLite<T>> | |
88 | + FieldSet<T> newFieldSet() { | |
89 | + return new FieldSet<T>(); | |
90 | + } | |
91 | + | |
92 | + /** Get an immutable empty FieldSet. */ | |
93 | + @SuppressWarnings("unchecked") | |
94 | + public static <T extends FieldSet.FieldDescriptorLite<T>> | |
95 | + FieldSet<T> emptySet() { | |
96 | + return DEFAULT_INSTANCE; | |
97 | + } | |
98 | + @SuppressWarnings("unchecked") | |
99 | + private static final FieldSet DEFAULT_INSTANCE = new FieldSet(true); | |
100 | + | |
101 | + /** Make this FieldSet immutable from this point forward. */ | |
102 | + @SuppressWarnings("unchecked") | |
103 | + public void makeImmutable() { | |
104 | + if (isImmutable) { | |
105 | + return; | |
106 | + } | |
107 | + fields.makeImmutable(); | |
108 | + isImmutable = true; | |
109 | + } | |
110 | + | |
111 | + /** | |
112 | + * Retuns whether the FieldSet is immutable. This is true if it is the | |
113 | + * {@link #emptySet} or if {@link #makeImmutable} were called. | |
114 | + * | |
115 | + * @return whether the FieldSet is immutable. | |
116 | + */ | |
117 | + public boolean isImmutable() { | |
118 | + return isImmutable; | |
119 | + } | |
120 | + | |
121 | + /** | |
122 | + * Clones the FieldSet. The returned FieldSet will be mutable even if the | |
123 | + * original FieldSet was immutable. | |
124 | + * | |
125 | + * @return the newly cloned FieldSet | |
126 | + */ | |
127 | + @Override | |
128 | + public FieldSet<FieldDescriptorType> clone() { | |
129 | + // We can't just call fields.clone because List objects in the map | |
130 | + // should not be shared. | |
131 | + FieldSet<FieldDescriptorType> clone = FieldSet.newFieldSet(); | |
132 | + for (int i = 0; i < fields.getNumArrayEntries(); i++) { | |
133 | + Map.Entry<FieldDescriptorType, Object> entry = fields.getArrayEntryAt(i); | |
134 | + FieldDescriptorType descriptor = entry.getKey(); | |
135 | + clone.setField(descriptor, entry.getValue()); | |
136 | + } | |
137 | + for (Map.Entry<FieldDescriptorType, Object> entry : | |
138 | + fields.getOverflowEntries()) { | |
139 | + FieldDescriptorType descriptor = entry.getKey(); | |
140 | + clone.setField(descriptor, entry.getValue()); | |
141 | + } | |
142 | + return clone; | |
143 | + } | |
144 | + | |
145 | + // ================================================================= | |
146 | + | |
147 | + /** See {@link Message.Builder#clear()}. */ | |
148 | + public void clear() { | |
149 | + fields.clear(); | |
150 | + } | |
151 | + | |
152 | + /** | |
153 | + * Get a simple map containing all the fields. | |
154 | + */ | |
155 | + public Map<FieldDescriptorType, Object> getAllFields() { | |
156 | + return fields.isImmutable() ? fields : Collections.unmodifiableMap(fields); | |
157 | + } | |
158 | + | |
159 | + /** | |
160 | + * Get an iterator to the field map. This iterator should not be leaked out | |
161 | + * of the protobuf library as it is not protected from mutation when | |
162 | + * fields is not immutable. | |
163 | + */ | |
164 | + public Iterator<Map.Entry<FieldDescriptorType, Object>> iterator() { | |
165 | + return fields.entrySet().iterator(); | |
166 | + } | |
167 | + | |
168 | + /** | |
169 | + * Useful for implementing | |
170 | + * {@link Message#hasField(Descriptors.FieldDescriptor)}. | |
171 | + */ | |
172 | + public boolean hasField(final FieldDescriptorType descriptor) { | |
173 | + if (descriptor.isRepeated()) { | |
174 | + throw new IllegalArgumentException( | |
175 | + "hasField() can only be called on non-repeated fields."); | |
176 | + } | |
177 | + | |
178 | + return fields.get(descriptor) != null; | |
179 | + } | |
180 | + | |
181 | + /** | |
182 | + * Useful for implementing | |
183 | + * {@link Message#getField(Descriptors.FieldDescriptor)}. This method | |
184 | + * returns {@code null} if the field is not set; in this case it is up | |
185 | + * to the caller to fetch the field's default value. | |
186 | + */ | |
187 | + public Object getField(final FieldDescriptorType descriptor) { | |
188 | + return fields.get(descriptor); | |
189 | + } | |
190 | + | |
191 | + /** | |
192 | + * Useful for implementing | |
193 | + * {@link Message.Builder#setField(Descriptors.FieldDescriptor,Object)}. | |
194 | + */ | |
195 | + @SuppressWarnings("unchecked") | |
196 | + public void setField(final FieldDescriptorType descriptor, | |
197 | + Object value) { | |
198 | + if (descriptor.isRepeated()) { | |
199 | + if (!(value instanceof List)) { | |
200 | + throw new IllegalArgumentException( | |
201 | + "Wrong object type used with protocol message reflection."); | |
202 | + } | |
203 | + | |
204 | + // Wrap the contents in a new list so that the caller cannot change | |
205 | + // the list's contents after setting it. | |
206 | + final List newList = new ArrayList(); | |
207 | + newList.addAll((List)value); | |
208 | + for (final Object element : newList) { | |
209 | + verifyType(descriptor.getLiteType(), element); | |
210 | + } | |
211 | + value = newList; | |
212 | + } else { | |
213 | + verifyType(descriptor.getLiteType(), value); | |
214 | + } | |
215 | + | |
216 | + fields.put(descriptor, value); | |
217 | + } | |
218 | + | |
219 | + /** | |
220 | + * Useful for implementing | |
221 | + * {@link Message.Builder#clearField(Descriptors.FieldDescriptor)}. | |
222 | + */ | |
223 | + public void clearField(final FieldDescriptorType descriptor) { | |
224 | + fields.remove(descriptor); | |
225 | + } | |
226 | + | |
227 | + /** | |
228 | + * Useful for implementing | |
229 | + * {@link Message#getRepeatedFieldCount(Descriptors.FieldDescriptor)}. | |
230 | + */ | |
231 | + public int getRepeatedFieldCount(final FieldDescriptorType descriptor) { | |
232 | + if (!descriptor.isRepeated()) { | |
233 | + throw new IllegalArgumentException( | |
234 | + "getRepeatedField() can only be called on repeated fields."); | |
235 | + } | |
236 | + | |
237 | + final Object value = fields.get(descriptor); | |
238 | + if (value == null) { | |
239 | + return 0; | |
240 | + } else { | |
241 | + return ((List<?>) value).size(); | |
242 | + } | |
243 | + } | |
244 | + | |
245 | + /** | |
246 | + * Useful for implementing | |
247 | + * {@link Message#getRepeatedField(Descriptors.FieldDescriptor,int)}. | |
248 | + */ | |
249 | + public Object getRepeatedField(final FieldDescriptorType descriptor, | |
250 | + final int index) { | |
251 | + if (!descriptor.isRepeated()) { | |
252 | + throw new IllegalArgumentException( | |
253 | + "getRepeatedField() can only be called on repeated fields."); | |
254 | + } | |
255 | + | |
256 | + final Object value = fields.get(descriptor); | |
257 | + | |
258 | + if (value == null) { | |
259 | + throw new IndexOutOfBoundsException(); | |
260 | + } else { | |
261 | + return ((List<?>) value).get(index); | |
262 | + } | |
263 | + } | |
264 | + | |
265 | + /** | |
266 | + * Useful for implementing | |
267 | + * {@link Message.Builder#setRepeatedField(Descriptors.FieldDescriptor,int,Object)}. | |
268 | + */ | |
269 | + @SuppressWarnings("unchecked") | |
270 | + public void setRepeatedField(final FieldDescriptorType descriptor, | |
271 | + final int index, | |
272 | + final Object value) { | |
273 | + if (!descriptor.isRepeated()) { | |
274 | + throw new IllegalArgumentException( | |
275 | + "getRepeatedField() can only be called on repeated fields."); | |
276 | + } | |
277 | + | |
278 | + final Object list = fields.get(descriptor); | |
279 | + if (list == null) { | |
280 | + throw new IndexOutOfBoundsException(); | |
281 | + } | |
282 | + | |
283 | + verifyType(descriptor.getLiteType(), value); | |
284 | + ((List) list).set(index, value); | |
285 | + } | |
286 | + | |
287 | + /** | |
288 | + * Useful for implementing | |
289 | + * {@link Message.Builder#addRepeatedField(Descriptors.FieldDescriptor,Object)}. | |
290 | + */ | |
291 | + @SuppressWarnings("unchecked") | |
292 | + public void addRepeatedField(final FieldDescriptorType descriptor, | |
293 | + final Object value) { | |
294 | + if (!descriptor.isRepeated()) { | |
295 | + throw new IllegalArgumentException( | |
296 | + "addRepeatedField() can only be called on repeated fields."); | |
297 | + } | |
298 | + | |
299 | + verifyType(descriptor.getLiteType(), value); | |
300 | + | |
301 | + final Object existingValue = fields.get(descriptor); | |
302 | + List list; | |
303 | + if (existingValue == null) { | |
304 | + list = new ArrayList(); | |
305 | + fields.put(descriptor, list); | |
306 | + } else { | |
307 | + list = (List) existingValue; | |
308 | + } | |
309 | + | |
310 | + list.add(value); | |
311 | + } | |
312 | + | |
313 | + /** | |
314 | + * Verifies that the given object is of the correct type to be a valid | |
315 | + * value for the given field. (For repeated fields, this checks if the | |
316 | + * object is the right type to be one element of the field.) | |
317 | + * | |
318 | + * @throws IllegalArgumentException The value is not of the right type. | |
319 | + */ | |
320 | + private static void verifyType(final WireFormat.FieldType type, | |
321 | + final Object value) { | |
322 | + if (value == null) { | |
323 | + throw new NullPointerException(); | |
324 | + } | |
325 | + | |
326 | + boolean isValid = false; | |
327 | + switch (type.getJavaType()) { | |
328 | + case INT: isValid = value instanceof Integer ; break; | |
329 | + case LONG: isValid = value instanceof Long ; break; | |
330 | + case FLOAT: isValid = value instanceof Float ; break; | |
331 | + case DOUBLE: isValid = value instanceof Double ; break; | |
332 | + case BOOLEAN: isValid = value instanceof Boolean ; break; | |
333 | + case STRING: isValid = value instanceof String ; break; | |
334 | + case BYTE_STRING: isValid = value instanceof ByteString; break; | |
335 | + case ENUM: | |
336 | + // TODO(kenton): Caller must do type checking here, I guess. | |
337 | + isValid = value instanceof Internal.EnumLite; | |
338 | + break; | |
339 | + case MESSAGE: | |
340 | + // TODO(kenton): Caller must do type checking here, I guess. | |
341 | + isValid = value instanceof MessageLite; | |
342 | + break; | |
343 | + } | |
344 | + | |
345 | + if (!isValid) { | |
346 | + // TODO(kenton): When chaining calls to setField(), it can be hard to | |
347 | + // tell from the stack trace which exact call failed, since the whole | |
348 | + // chain is considered one line of code. It would be nice to print | |
349 | + // more information here, e.g. naming the field. We used to do that. | |
350 | + // But we can't now that FieldSet doesn't use descriptors. Maybe this | |
351 | + // isn't a big deal, though, since it would only really apply when using | |
352 | + // reflection and generally people don't chain reflection setters. | |
353 | + throw new IllegalArgumentException( | |
354 | + "Wrong object type used with protocol message reflection."); | |
355 | + } | |
356 | + } | |
357 | + | |
358 | + // ================================================================= | |
359 | + // Parsing and serialization | |
360 | + | |
361 | + /** | |
362 | + * See {@link Message#isInitialized()}. Note: Since {@code FieldSet} | |
363 | + * itself does not have any way of knowing about required fields that | |
364 | + * aren't actually present in the set, it is up to the caller to check | |
365 | + * that all required fields are present. | |
366 | + */ | |
367 | + public boolean isInitialized() { | |
368 | + for (int i = 0; i < fields.getNumArrayEntries(); i++) { | |
369 | + if (!isInitialized(fields.getArrayEntryAt(i))) { | |
370 | + return false; | |
371 | + } | |
372 | + } | |
373 | + for (final Map.Entry<FieldDescriptorType, Object> entry : | |
374 | + fields.getOverflowEntries()) { | |
375 | + if (!isInitialized(entry)) { | |
376 | + return false; | |
377 | + } | |
378 | + } | |
379 | + return true; | |
380 | + } | |
381 | + | |
382 | + @SuppressWarnings("unchecked") | |
383 | + private boolean isInitialized( | |
384 | + final Map.Entry<FieldDescriptorType, Object> entry) { | |
385 | + final FieldDescriptorType descriptor = entry.getKey(); | |
386 | + if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE) { | |
387 | + if (descriptor.isRepeated()) { | |
388 | + for (final MessageLite element: | |
389 | + (List<MessageLite>) entry.getValue()) { | |
390 | + if (!element.isInitialized()) { | |
391 | + return false; | |
392 | + } | |
393 | + } | |
394 | + } else { | |
395 | + if (!((MessageLite) entry.getValue()).isInitialized()) { | |
396 | + return false; | |
397 | + } | |
398 | + } | |
399 | + } | |
400 | + return true; | |
401 | + } | |
402 | + | |
403 | + /** | |
404 | + * Given a field type, return the wire type. | |
405 | + * | |
406 | + * @returns One of the {@code WIRETYPE_} constants defined in | |
407 | + * {@link WireFormat}. | |
408 | + */ | |
409 | + static int getWireFormatForFieldType(final WireFormat.FieldType type, | |
410 | + boolean isPacked) { | |
411 | + if (isPacked) { | |
412 | + return WireFormat.WIRETYPE_LENGTH_DELIMITED; | |
413 | + } else { | |
414 | + return type.getWireType(); | |
415 | + } | |
416 | + } | |
417 | + | |
418 | + /** | |
419 | + * Like {@link #mergeFrom(Message)}, but merges from another {@link FieldSet}. | |
420 | + */ | |
421 | + public void mergeFrom(final FieldSet<FieldDescriptorType> other) { | |
422 | + for (int i = 0; i < other.fields.getNumArrayEntries(); i++) { | |
423 | + mergeFromField(other.fields.getArrayEntryAt(i)); | |
424 | + } | |
425 | + for (final Map.Entry<FieldDescriptorType, Object> entry : | |
426 | + other.fields.getOverflowEntries()) { | |
427 | + mergeFromField(entry); | |
428 | + } | |
429 | + } | |
430 | + | |
431 | + @SuppressWarnings("unchecked") | |
432 | + private void mergeFromField( | |
433 | + final Map.Entry<FieldDescriptorType, Object> entry) { | |
434 | + final FieldDescriptorType descriptor = entry.getKey(); | |
435 | + final Object otherValue = entry.getValue(); | |
436 | + | |
437 | + if (descriptor.isRepeated()) { | |
438 | + Object value = fields.get(descriptor); | |
439 | + if (value == null) { | |
440 | + // Our list is empty, but we still need to make a defensive copy of | |
441 | + // the other list since we don't know if the other FieldSet is still | |
442 | + // mutable. | |
443 | + fields.put(descriptor, new ArrayList((List) otherValue)); | |
444 | + } else { | |
445 | + // Concatenate the lists. | |
446 | + ((List) value).addAll((List) otherValue); | |
447 | + } | |
448 | + } else if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE) { | |
449 | + Object value = fields.get(descriptor); | |
450 | + if (value == null) { | |
451 | + fields.put(descriptor, otherValue); | |
452 | + } else { | |
453 | + // Merge the messages. | |
454 | + fields.put( | |
455 | + descriptor, | |
456 | + descriptor.internalMergeFrom( | |
457 | + ((MessageLite) value).toBuilder(), (MessageLite) otherValue) | |
458 | + .build()); | |
459 | + } | |
460 | + | |
461 | + } else { | |
462 | + fields.put(descriptor, otherValue); | |
463 | + } | |
464 | + } | |
465 | + | |
466 | + // TODO(kenton): Move static parsing and serialization methods into some | |
467 | + // other class. Probably WireFormat. | |
468 | + | |
469 | + /** | |
470 | + * Read a field of any primitive type from a CodedInputStream. Enums, | |
471 | + * groups, and embedded messages are not handled by this method. | |
472 | + * | |
473 | + * @param input The stream from which to read. | |
474 | + * @param type Declared type of the field. | |
475 | + * @return An object representing the field's value, of the exact | |
476 | + * type which would be returned by | |
477 | + * {@link Message#getField(Descriptors.FieldDescriptor)} for | |
478 | + * this field. | |
479 | + */ | |
480 | + public static Object readPrimitiveField( | |
481 | + CodedInputStream input, | |
482 | + final WireFormat.FieldType type) throws IOException { | |
483 | + switch (type) { | |
484 | + case DOUBLE : return input.readDouble (); | |
485 | + case FLOAT : return input.readFloat (); | |
486 | + case INT64 : return input.readInt64 (); | |
487 | + case UINT64 : return input.readUInt64 (); | |
488 | + case INT32 : return input.readInt32 (); | |
489 | + case FIXED64 : return input.readFixed64 (); | |
490 | + case FIXED32 : return input.readFixed32 (); | |
491 | + case BOOL : return input.readBool (); | |
492 | + case STRING : return input.readString (); | |
493 | + case BYTES : return input.readBytes (); | |
494 | + case UINT32 : return input.readUInt32 (); | |
495 | + case SFIXED32: return input.readSFixed32(); | |
496 | + case SFIXED64: return input.readSFixed64(); | |
497 | + case SINT32 : return input.readSInt32 (); | |
498 | + case SINT64 : return input.readSInt64 (); | |
499 | + | |
500 | + case GROUP: | |
501 | + throw new IllegalArgumentException( | |
502 | + "readPrimitiveField() cannot handle nested groups."); | |
503 | + case MESSAGE: | |
504 | + throw new IllegalArgumentException( | |
505 | + "readPrimitiveField() cannot handle embedded messages."); | |
506 | + case ENUM: | |
507 | + // We don't handle enums because we don't know what to do if the | |
508 | + // value is not recognized. | |
509 | + throw new IllegalArgumentException( | |
510 | + "readPrimitiveField() cannot handle enums."); | |
511 | + } | |
512 | + | |
513 | + throw new RuntimeException( | |
514 | + "There is no way to get here, but the compiler thinks otherwise."); | |
515 | + } | |
516 | + | |
517 | + /** See {@link Message#writeTo(CodedOutputStream)}. */ | |
518 | + public void writeTo(final CodedOutputStream output) | |
519 | + throws IOException { | |
520 | + for (int i = 0; i < fields.getNumArrayEntries(); i++) { | |
521 | + final Map.Entry<FieldDescriptorType, Object> entry = | |
522 | + fields.getArrayEntryAt(i); | |
523 | + writeField(entry.getKey(), entry.getValue(), output); | |
524 | + } | |
525 | + for (final Map.Entry<FieldDescriptorType, Object> entry : | |
526 | + fields.getOverflowEntries()) { | |
527 | + writeField(entry.getKey(), entry.getValue(), output); | |
528 | + } | |
529 | + } | |
530 | + | |
531 | + /** | |
532 | + * Like {@link #writeTo} but uses MessageSet wire format. | |
533 | + */ | |
534 | + public void writeMessageSetTo(final CodedOutputStream output) | |
535 | + throws IOException { | |
536 | + for (int i = 0; i < fields.getNumArrayEntries(); i++) { | |
537 | + writeMessageSetTo(fields.getArrayEntryAt(i), output); | |
538 | + } | |
539 | + for (final Map.Entry<FieldDescriptorType, Object> entry : | |
540 | + fields.getOverflowEntries()) { | |
541 | + writeMessageSetTo(entry, output); | |
542 | + } | |
543 | + } | |
544 | + | |
545 | + private void writeMessageSetTo( | |
546 | + final Map.Entry<FieldDescriptorType, Object> entry, | |
547 | + final CodedOutputStream output) throws IOException { | |
548 | + final FieldDescriptorType descriptor = entry.getKey(); | |
549 | + if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE && | |
550 | + !descriptor.isRepeated() && !descriptor.isPacked()) { | |
551 | + output.writeMessageSetExtension(entry.getKey().getNumber(), | |
552 | + (MessageLite) entry.getValue()); | |
553 | + } else { | |
554 | + writeField(descriptor, entry.getValue(), output); | |
555 | + } | |
556 | + } | |
557 | + | |
558 | + /** | |
559 | + * Write a single tag-value pair to the stream. | |
560 | + * | |
561 | + * @param output The output stream. | |
562 | + * @param type The field's type. | |
563 | + * @param number The field's number. | |
564 | + * @param value Object representing the field's value. Must be of the exact | |
565 | + * type which would be returned by | |
566 | + * {@link Message#getField(Descriptors.FieldDescriptor)} for | |
567 | + * this field. | |
568 | + */ | |
569 | + private static void writeElement(final CodedOutputStream output, | |
570 | + final WireFormat.FieldType type, | |
571 | + final int number, | |
572 | + final Object value) throws IOException { | |
573 | + // Special case for groups, which need a start and end tag; other fields | |
574 | + // can just use writeTag() and writeFieldNoTag(). | |
575 | + if (type == WireFormat.FieldType.GROUP) { | |
576 | + output.writeGroup(number, (MessageLite) value); | |
577 | + } else { | |
578 | + output.writeTag(number, getWireFormatForFieldType(type, false)); | |
579 | + writeElementNoTag(output, type, value); | |
580 | + } | |
581 | + } | |
582 | + | |
583 | + /** | |
584 | + * Write a field of arbitrary type, without its tag, to the stream. | |
585 | + * | |
586 | + * @param output The output stream. | |
587 | + * @param type The field's type. | |
588 | + * @param value Object representing the field's value. Must be of the exact | |
589 | + * type which would be returned by | |
590 | + * {@link Message#getField(Descriptors.FieldDescriptor)} for | |
591 | + * this field. | |
592 | + */ | |
593 | + private static void writeElementNoTag( | |
594 | + final CodedOutputStream output, | |
595 | + final WireFormat.FieldType type, | |
596 | + final Object value) throws IOException { | |
597 | + switch (type) { | |
598 | + case DOUBLE : output.writeDoubleNoTag ((Double ) value); break; | |
599 | + case FLOAT : output.writeFloatNoTag ((Float ) value); break; | |
600 | + case INT64 : output.writeInt64NoTag ((Long ) value); break; | |
601 | + case UINT64 : output.writeUInt64NoTag ((Long ) value); break; | |
602 | + case INT32 : output.writeInt32NoTag ((Integer ) value); break; | |
603 | + case FIXED64 : output.writeFixed64NoTag ((Long ) value); break; | |
604 | + case FIXED32 : output.writeFixed32NoTag ((Integer ) value); break; | |
605 | + case BOOL : output.writeBoolNoTag ((Boolean ) value); break; | |
606 | + case STRING : output.writeStringNoTag ((String ) value); break; | |
607 | + case GROUP : output.writeGroupNoTag ((MessageLite) value); break; | |
608 | + case MESSAGE : output.writeMessageNoTag ((MessageLite) value); break; | |
609 | + case BYTES : output.writeBytesNoTag ((ByteString ) value); break; | |
610 | + case UINT32 : output.writeUInt32NoTag ((Integer ) value); break; | |
611 | + case SFIXED32: output.writeSFixed32NoTag((Integer ) value); break; | |
612 | + case SFIXED64: output.writeSFixed64NoTag((Long ) value); break; | |
613 | + case SINT32 : output.writeSInt32NoTag ((Integer ) value); break; | |
614 | + case SINT64 : output.writeSInt64NoTag ((Long ) value); break; | |
615 | + | |
616 | + case ENUM: | |
617 | + output.writeEnumNoTag(((Internal.EnumLite) value).getNumber()); | |
618 | + break; | |
619 | + } | |
620 | + } | |
621 | + | |
622 | + /** Write a single field. */ | |
623 | + public static void writeField(final FieldDescriptorLite<?> descriptor, | |
624 | + final Object value, | |
625 | + final CodedOutputStream output) | |
626 | + throws IOException { | |
627 | + WireFormat.FieldType type = descriptor.getLiteType(); | |
628 | + int number = descriptor.getNumber(); | |
629 | + if (descriptor.isRepeated()) { | |
630 | + final List<?> valueList = (List<?>)value; | |
631 | + if (descriptor.isPacked()) { | |
632 | + output.writeTag(number, WireFormat.WIRETYPE_LENGTH_DELIMITED); | |
633 | + // Compute the total data size so the length can be written. | |
634 | + int dataSize = 0; | |
635 | + for (final Object element : valueList) { | |
636 | + dataSize += computeElementSizeNoTag(type, element); | |
637 | + } | |
638 | + output.writeRawVarint32(dataSize); | |
639 | + // Write the data itself, without any tags. | |
640 | + for (final Object element : valueList) { | |
641 | + writeElementNoTag(output, type, element); | |
642 | + } | |
643 | + } else { | |
644 | + for (final Object element : valueList) { | |
645 | + writeElement(output, type, number, element); | |
646 | + } | |
647 | + } | |
648 | + } else { | |
649 | + writeElement(output, type, number, value); | |
650 | + } | |
651 | + } | |
652 | + | |
653 | + /** | |
654 | + * See {@link Message#getSerializedSize()}. It's up to the caller to cache | |
655 | + * the resulting size if desired. | |
656 | + */ | |
657 | + public int getSerializedSize() { | |
658 | + int size = 0; | |
659 | + for (int i = 0; i < fields.getNumArrayEntries(); i++) { | |
660 | + final Map.Entry<FieldDescriptorType, Object> entry = | |
661 | + fields.getArrayEntryAt(i); | |
662 | + size += computeFieldSize(entry.getKey(), entry.getValue()); | |
663 | + } | |
664 | + for (final Map.Entry<FieldDescriptorType, Object> entry : | |
665 | + fields.getOverflowEntries()) { | |
666 | + size += computeFieldSize(entry.getKey(), entry.getValue()); | |
667 | + } | |
668 | + return size; | |
669 | + } | |
670 | + | |
671 | + /** | |
672 | + * Like {@link #getSerializedSize} but uses MessageSet wire format. | |
673 | + */ | |
674 | + public int getMessageSetSerializedSize() { | |
675 | + int size = 0; | |
676 | + for (int i = 0; i < fields.getNumArrayEntries(); i++) { | |
677 | + size += getMessageSetSerializedSize(fields.getArrayEntryAt(i)); | |
678 | + } | |
679 | + for (final Map.Entry<FieldDescriptorType, Object> entry : | |
680 | + fields.getOverflowEntries()) { | |
681 | + size += getMessageSetSerializedSize(entry); | |
682 | + } | |
683 | + return size; | |
684 | + } | |
685 | + | |
686 | + private int getMessageSetSerializedSize( | |
687 | + final Map.Entry<FieldDescriptorType, Object> entry) { | |
688 | + final FieldDescriptorType descriptor = entry.getKey(); | |
689 | + if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE && | |
690 | + !descriptor.isRepeated() && !descriptor.isPacked()) { | |
691 | + return CodedOutputStream.computeMessageSetExtensionSize( | |
692 | + entry.getKey().getNumber(), (MessageLite) entry.getValue()); | |
693 | + } else { | |
694 | + return computeFieldSize(descriptor, entry.getValue()); | |
695 | + } | |
696 | + } | |
697 | + | |
698 | + /** | |
699 | + * Compute the number of bytes that would be needed to encode a | |
700 | + * single tag/value pair of arbitrary type. | |
701 | + * | |
702 | + * @param type The field's type. | |
703 | + * @param number The field's number. | |
704 | + * @param value Object representing the field's value. Must be of the exact | |
705 | + * type which would be returned by | |
706 | + * {@link Message#getField(Descriptors.FieldDescriptor)} for | |
707 | + * this field. | |
708 | + */ | |
709 | + private static int computeElementSize( | |
710 | + final WireFormat.FieldType type, | |
711 | + final int number, final Object value) { | |
712 | + int tagSize = CodedOutputStream.computeTagSize(number); | |
713 | + if (type == WireFormat.FieldType.GROUP) { | |
714 | + tagSize *= 2; | |
715 | + } | |
716 | + return tagSize + computeElementSizeNoTag(type, value); | |
717 | + } | |
718 | + | |
719 | + /** | |
720 | + * Compute the number of bytes that would be needed to encode a | |
721 | + * particular value of arbitrary type, excluding tag. | |
722 | + * | |
723 | + * @param type The field's type. | |
724 | + * @param value Object representing the field's value. Must be of the exact | |
725 | + * type which would be returned by | |
726 | + * {@link Message#getField(Descriptors.FieldDescriptor)} for | |
727 | + * this field. | |
728 | + */ | |
729 | + private static int computeElementSizeNoTag( | |
730 | + final WireFormat.FieldType type, final Object value) { | |
731 | + switch (type) { | |
732 | + // Note: Minor violation of 80-char limit rule here because this would | |
733 | + // actually be harder to read if we wrapped the lines. | |
734 | + case DOUBLE : return CodedOutputStream.computeDoubleSizeNoTag ((Double )value); | |
735 | + case FLOAT : return CodedOutputStream.computeFloatSizeNoTag ((Float )value); | |
736 | + case INT64 : return CodedOutputStream.computeInt64SizeNoTag ((Long )value); | |
737 | + case UINT64 : return CodedOutputStream.computeUInt64SizeNoTag ((Long )value); | |
738 | + case INT32 : return CodedOutputStream.computeInt32SizeNoTag ((Integer )value); | |
739 | + case FIXED64 : return CodedOutputStream.computeFixed64SizeNoTag ((Long )value); | |
740 | + case FIXED32 : return CodedOutputStream.computeFixed32SizeNoTag ((Integer )value); | |
741 | + case BOOL : return CodedOutputStream.computeBoolSizeNoTag ((Boolean )value); | |
742 | + case STRING : return CodedOutputStream.computeStringSizeNoTag ((String )value); | |
743 | + case GROUP : return CodedOutputStream.computeGroupSizeNoTag ((MessageLite)value); | |
744 | + case MESSAGE : return CodedOutputStream.computeMessageSizeNoTag ((MessageLite)value); | |
745 | + case BYTES : return CodedOutputStream.computeBytesSizeNoTag ((ByteString )value); | |
746 | + case UINT32 : return CodedOutputStream.computeUInt32SizeNoTag ((Integer )value); | |
747 | + case SFIXED32: return CodedOutputStream.computeSFixed32SizeNoTag((Integer )value); | |
748 | + case SFIXED64: return CodedOutputStream.computeSFixed64SizeNoTag((Long )value); | |
749 | + case SINT32 : return CodedOutputStream.computeSInt32SizeNoTag ((Integer )value); | |
750 | + case SINT64 : return CodedOutputStream.computeSInt64SizeNoTag ((Long )value); | |
751 | + | |
752 | + case ENUM: | |
753 | + return CodedOutputStream.computeEnumSizeNoTag( | |
754 | + ((Internal.EnumLite) value).getNumber()); | |
755 | + } | |
756 | + | |
757 | + throw new RuntimeException( | |
758 | + "There is no way to get here, but the compiler thinks otherwise."); | |
759 | + } | |
760 | + | |
761 | + /** | |
762 | + * Compute the number of bytes needed to encode a particular field. | |
763 | + */ | |
764 | + public static int computeFieldSize(final FieldDescriptorLite<?> descriptor, | |
765 | + final Object value) { | |
766 | + WireFormat.FieldType type = descriptor.getLiteType(); | |
767 | + int number = descriptor.getNumber(); | |
768 | + if (descriptor.isRepeated()) { | |
769 | + if (descriptor.isPacked()) { | |
770 | + int dataSize = 0; | |
771 | + for (final Object element : (List<?>)value) { | |
772 | + dataSize += computeElementSizeNoTag(type, element); | |
773 | + } | |
774 | + return dataSize + | |
775 | + CodedOutputStream.computeTagSize(number) + | |
776 | + CodedOutputStream.computeRawVarint32Size(dataSize); | |
777 | + } else { | |
778 | + int size = 0; | |
779 | + for (final Object element : (List<?>)value) { | |
780 | + size += computeElementSize(type, number, element); | |
781 | + } | |
782 | + return size; | |
783 | + } | |
784 | + } else { | |
785 | + return computeElementSize(type, number, value); | |
786 | + } | |
787 | + } | |
788 | +} |
@@ -0,0 +1,1834 @@ | ||
1 | +// Protocol Buffers - Google's data interchange format | |
2 | +// Copyright 2008 Google Inc. All rights reserved. | |
3 | +// http://code.google.com/p/protobuf/ | |
4 | +// | |
5 | +// Redistribution and use in source and binary forms, with or without | |
6 | +// modification, are permitted provided that the following conditions are | |
7 | +// met: | |
8 | +// | |
9 | +// * Redistributions of source code must retain the above copyright | |
10 | +// notice, this list of conditions and the following disclaimer. | |
11 | +// * Redistributions in binary form must reproduce the above | |
12 | +// copyright notice, this list of conditions and the following disclaimer | |
13 | +// in the documentation and/or other materials provided with the | |
14 | +// distribution. | |
15 | +// * Neither the name of Google Inc. nor the names of its | |
16 | +// contributors may be used to endorse or promote products derived from | |
17 | +// this software without specific prior written permission. | |
18 | +// | |
19 | +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
20 | +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
21 | +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
22 | +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
23 | +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
24 | +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
25 | +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
26 | +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
27 | +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
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. | |
30 | + | |
31 | +package com.google.protobuf; | |
32 | + | |
33 | +import com.google.protobuf.Descriptors.Descriptor; | |
34 | +import com.google.protobuf.Descriptors.EnumValueDescriptor; | |
35 | +import com.google.protobuf.Descriptors.FieldDescriptor; | |
36 | + | |
37 | +import java.io.IOException; | |
38 | +import java.io.ObjectStreamException; | |
39 | +import java.io.Serializable; | |
40 | +import java.lang.reflect.InvocationTargetException; | |
41 | +import java.lang.reflect.Method; | |
42 | +import java.util.ArrayList; | |
43 | +import java.util.Collections; | |
44 | +import java.util.Iterator; | |
45 | +import java.util.List; | |
46 | +import java.util.Map; | |
47 | +import java.util.TreeMap; | |
48 | + | |
49 | +/** | |
50 | + * All generated protocol message classes extend this class. This class | |
51 | + * implements most of the Message and Builder interfaces using Java reflection. | |
52 | + * Users can ignore this class and pretend that generated messages implement | |
53 | + * the Message interface directly. | |
54 | + * | |
55 | + * @author kenton@google.com Kenton Varda | |
56 | + */ | |
57 | +public abstract class GeneratedMessage extends AbstractMessage | |
58 | + implements Serializable { | |
59 | + private static final long serialVersionUID = 1L; | |
60 | + | |
61 | + private final UnknownFieldSet unknownFields; | |
62 | + | |
63 | + /** | |
64 | + * For testing. Allows a test to disable the optimization that avoids using | |
65 | + * field builders for nested messages until they are requested. By disabling | |
66 | + * this optimization, existing tests can be reused to test the field builders. | |
67 | + */ | |
68 | + protected static boolean alwaysUseFieldBuilders = false; | |
69 | + | |
70 | + protected GeneratedMessage() { | |
71 | + this.unknownFields = UnknownFieldSet.getDefaultInstance(); | |
72 | + } | |
73 | + | |
74 | + protected GeneratedMessage(Builder<?> builder) { | |
75 | + this.unknownFields = builder.getUnknownFields(); | |
76 | + } | |
77 | + | |
78 | + /** | |
79 | + * For testing. Allows a test to disable the optimization that avoids using | |
80 | + * field builders for nested messages until they are requested. By disabling | |
81 | + * this optimization, existing tests can be reused to test the field builders. | |
82 | + * See {@link RepeatedFieldBuilder} and {@link SingleFieldBuilder}. | |
83 | + */ | |
84 | + static void enableAlwaysUseFieldBuildersForTesting() { | |
85 | + alwaysUseFieldBuilders = true; | |
86 | + } | |
87 | + | |
88 | + /** | |
89 | + * Get the FieldAccessorTable for this type. We can't have the message | |
90 | + * class pass this in to the constructor because of bootstrapping trouble | |
91 | + * with DescriptorProtos. | |
92 | + */ | |
93 | + protected abstract FieldAccessorTable internalGetFieldAccessorTable(); | |
94 | + | |
95 | + //@Override (Java 1.6 override semantics, but we must support 1.5) | |
96 | + public Descriptor getDescriptorForType() { | |
97 | + return internalGetFieldAccessorTable().descriptor; | |
98 | + } | |
99 | + | |
100 | + /** Internal helper which returns a mutable map. */ | |
101 | + private Map<FieldDescriptor, Object> getAllFieldsMutable() { | |
102 | + final TreeMap<FieldDescriptor, Object> result = | |
103 | + new TreeMap<FieldDescriptor, Object>(); | |
104 | + final Descriptor descriptor = internalGetFieldAccessorTable().descriptor; | |
105 | + for (final FieldDescriptor field : descriptor.getFields()) { | |
106 | + if (field.isRepeated()) { | |
107 | + final List<?> value = (List<?>) getField(field); | |
108 | + if (!value.isEmpty()) { | |
109 | + result.put(field, value); | |
110 | + } | |
111 | + } else { | |
112 | + if (hasField(field)) { | |
113 | + result.put(field, getField(field)); | |
114 | + } | |
115 | + } | |
116 | + } | |
117 | + return result; | |
118 | + } | |
119 | + | |
120 | + @Override | |
121 | + public boolean isInitialized() { | |
122 | + for (final FieldDescriptor field : getDescriptorForType().getFields()) { | |
123 | + // Check that all required fields are present. | |
124 | + if (field.isRequired()) { | |
125 | + if (!hasField(field)) { | |
126 | + return false; | |
127 | + } | |
128 | + } | |
129 | + // Check that embedded messages are initialized. | |
130 | + if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { | |
131 | + if (field.isRepeated()) { | |
132 | + @SuppressWarnings("unchecked") final | |
133 | + List<Message> messageList = (List<Message>) getField(field); | |
134 | + for (final Message element : messageList) { | |
135 | + if (!element.isInitialized()) { | |
136 | + return false; | |
137 | + } | |
138 | + } | |
139 | + } else { | |
140 | + if (hasField(field) && !((Message) getField(field)).isInitialized()) { | |
141 | + return false; | |
142 | + } | |
143 | + } | |
144 | + } | |
145 | + } | |
146 | + | |
147 | + return true; | |
148 | + } | |
149 | + | |
150 | + //@Override (Java 1.6 override semantics, but we must support 1.5) | |
151 | + public Map<FieldDescriptor, Object> getAllFields() { | |
152 | + return Collections.unmodifiableMap(getAllFieldsMutable()); | |
153 | + } | |
154 | + | |
155 | + //@Override (Java 1.6 override semantics, but we must support 1.5) | |
156 | + public boolean hasField(final FieldDescriptor field) { | |
157 | + return internalGetFieldAccessorTable().getField(field).has(this); | |
158 | + } | |
159 | + | |
160 | + //@Override (Java 1.6 override semantics, but we must support 1.5) | |
161 | + public Object getField(final FieldDescriptor field) { | |
162 | + return internalGetFieldAccessorTable().getField(field).get(this); | |
163 | + } | |
164 | + | |
165 | + //@Override (Java 1.6 override semantics, but we must support 1.5) | |
166 | + public int getRepeatedFieldCount(final FieldDescriptor field) { | |
167 | + return internalGetFieldAccessorTable().getField(field) | |
168 | + .getRepeatedCount(this); | |
169 | + } | |
170 | + | |
171 | + //@Override (Java 1.6 override semantics, but we must support 1.5) | |
172 | + public Object getRepeatedField(final FieldDescriptor field, final int index) { | |
173 | + return internalGetFieldAccessorTable().getField(field) | |
174 | + .getRepeated(this, index); | |
175 | + } | |
176 | + | |
177 | + //@Override (Java 1.6 override semantics, but we must support 1.5) | |
178 | + public final UnknownFieldSet getUnknownFields() { | |
179 | + return unknownFields; | |
180 | + } | |
181 | + | |
182 | + protected abstract Message.Builder newBuilderForType(BuilderParent parent); | |
183 | + | |
184 | + /** | |
185 | + * Interface for the parent of a Builder that allows the builder to | |
186 | + * communicate invalidations back to the parent for use when using nested | |
187 | + * builders. | |
188 | + */ | |
189 | + protected interface BuilderParent { | |
190 | + | |
191 | + /** | |
192 | + * A builder becomes dirty whenever a field is modified -- including fields | |
193 | + * in nested builders -- and becomes clean when build() is called. Thus, | |
194 | + * when a builder becomes dirty, all its parents become dirty as well, and | |
195 | + * when it becomes clean, all its children become clean. The dirtiness | |
196 | + * state is used to invalidate certain cached values. | |
197 | + * <br> | |
198 | + * To this end, a builder calls markAsDirty() on its parent whenever it | |
199 | + * transitions from clean to dirty. The parent must propagate this call to | |
200 | + * its own parent, unless it was already dirty, in which case the | |
201 | + * grandparent must necessarily already be dirty as well. The parent can | |
202 | + * only transition back to "clean" after calling build() on all children. | |
203 | + */ | |
204 | + void markDirty(); | |
205 | + } | |
206 | + | |
207 | + @SuppressWarnings("unchecked") | |
208 | + public abstract static class Builder <BuilderType extends Builder> | |
209 | + extends AbstractMessage.Builder<BuilderType> { | |
210 | + | |
211 | + private BuilderParent builderParent; | |
212 | + | |
213 | + private BuilderParentImpl meAsParent; | |
214 | + | |
215 | + // Indicates that we've built a message and so we are now obligated | |
216 | + // to dispatch dirty invalidations. See GeneratedMessage.BuilderListener. | |
217 | + private boolean isClean; | |
218 | + | |
219 | + private UnknownFieldSet unknownFields = | |
220 | + UnknownFieldSet.getDefaultInstance(); | |
221 | + | |
222 | + protected Builder() { | |
223 | + this(null); | |
224 | + } | |
225 | + | |
226 | + protected Builder(BuilderParent builderParent) { | |
227 | + this.builderParent = builderParent; | |
228 | + } | |
229 | + | |
230 | + void dispose() { | |
231 | + builderParent = null; | |
232 | + } | |
233 | + | |
234 | + /** | |
235 | + * Called by the subclass when a message is built. | |
236 | + */ | |
237 | + protected void onBuilt() { | |
238 | + if (builderParent != null) { | |
239 | + markClean(); | |
240 | + } | |
241 | + } | |
242 | + | |
243 | + /** | |
244 | + * Called by the subclass or a builder to notify us that a message was | |
245 | + * built and may be cached and therefore invalidations are needed. | |
246 | + */ | |
247 | + protected void markClean() { | |
248 | + this.isClean = true; | |
249 | + } | |
250 | + | |
251 | + /** | |
252 | + * Gets whether invalidations are needed | |
253 | + * | |
254 | + * @return whether invalidations are needed | |
255 | + */ | |
256 | + protected boolean isClean() { | |
257 | + return isClean; | |
258 | + } | |
259 | + | |
260 | + // This is implemented here only to work around an apparent bug in the | |
261 | + // Java compiler and/or build system. See bug #1898463. The mere presence | |
262 | + // of this dummy clone() implementation makes it go away. | |
263 | + @Override | |
264 | + public BuilderType clone() { | |
265 | + throw new UnsupportedOperationException( | |
266 | + "This is supposed to be overridden by subclasses."); | |
267 | + } | |
268 | + | |
269 | + /** | |
270 | + * Called by the initialization and clear code paths to allow subclasses to | |
271 | + * reset any of their builtin fields back to the initial values. | |
272 | + */ | |
273 | + public BuilderType clear() { | |
274 | + unknownFields = UnknownFieldSet.getDefaultInstance(); | |
275 | + onChanged(); | |
276 | + return (BuilderType) this; | |
277 | + } | |
278 | + | |
279 | + /** | |
280 | + * Get the FieldAccessorTable for this type. We can't have the message | |
281 | + * class pass this in to the constructor because of bootstrapping trouble | |
282 | + * with DescriptorProtos. | |
283 | + */ | |
284 | + protected abstract FieldAccessorTable internalGetFieldAccessorTable(); | |
285 | + | |
286 | + //@Override (Java 1.6 override semantics, but we must support 1.5) | |
287 | + public Descriptor getDescriptorForType() { | |
288 | + return internalGetFieldAccessorTable().descriptor; | |
289 | + } | |
290 | + | |
291 | + //@Override (Java 1.6 override semantics, but we must support 1.5) | |
292 | + public Map<FieldDescriptor, Object> getAllFields() { | |
293 | + return Collections.unmodifiableMap(getAllFieldsMutable()); | |
294 | + } | |
295 | + | |
296 | + /** Internal helper which returns a mutable map. */ | |
297 | + private Map<FieldDescriptor, Object> getAllFieldsMutable() { | |
298 | + final TreeMap<FieldDescriptor, Object> result = | |
299 | + new TreeMap<FieldDescriptor, Object>(); | |
300 | + final Descriptor descriptor = internalGetFieldAccessorTable().descriptor; | |
301 | + for (final FieldDescriptor field : descriptor.getFields()) { | |
302 | + if (field.isRepeated()) { | |
303 | + final List value = (List) getField(field); | |
304 | + if (!value.isEmpty()) { | |
305 | + result.put(field, value); | |
306 | + } | |
307 | + } else { | |
308 | + if (hasField(field)) { | |
309 | + result.put(field, getField(field)); | |
310 | + } | |
311 | + } | |
312 | + } | |
313 | + return result; | |
314 | + } | |
315 | + | |
316 | + public Message.Builder newBuilderForField( | |
317 | + final FieldDescriptor field) { | |
318 | + return internalGetFieldAccessorTable().getField(field).newBuilder(); | |
319 | + } | |
320 | + | |
321 | + //@Override (Java 1.6 override semantics, but we must support 1.5) | |
322 | + public boolean hasField(final FieldDescriptor field) { | |
323 | + return internalGetFieldAccessorTable().getField(field).has(this); | |
324 | + } | |
325 | + | |
326 | + //@Override (Java 1.6 override semantics, but we must support 1.5) | |
327 | + public Object getField(final FieldDescriptor field) { | |
328 | + Object object = internalGetFieldAccessorTable().getField(field).get(this); | |
329 | + if (field.isRepeated()) { | |
330 | + // The underlying list object is still modifiable at this point. | |
331 | + // Make sure not to expose the modifiable list to the caller. | |
332 | + return Collections.unmodifiableList((List) object); | |
333 | + } else { | |
334 | + return object; | |
335 | + } | |
336 | + } | |
337 | + | |
338 | + public BuilderType setField(final FieldDescriptor field, | |
339 | + final Object value) { | |
340 | + internalGetFieldAccessorTable().getField(field).set(this, value); | |
341 | + return (BuilderType) this; | |
342 | + } | |
343 | + | |
344 | + //@Override (Java 1.6 override semantics, but we must support 1.5) | |
345 | + public BuilderType clearField(final FieldDescriptor field) { | |
346 | + internalGetFieldAccessorTable().getField(field).clear(this); | |
347 | + return (BuilderType) this; | |
348 | + } | |
349 | + | |
350 | + //@Override (Java 1.6 override semantics, but we must support 1.5) | |
351 | + public int getRepeatedFieldCount(final FieldDescriptor field) { | |
352 | + return internalGetFieldAccessorTable().getField(field) | |
353 | + .getRepeatedCount(this); | |
354 | + } | |
355 | + | |
356 | + //@Override (Java 1.6 override semantics, but we must support 1.5) | |
357 | + public Object getRepeatedField(final FieldDescriptor field, | |
358 | + final int index) { | |
359 | + return internalGetFieldAccessorTable().getField(field) | |
360 | + .getRepeated(this, index); | |
361 | + } | |
362 | + | |
363 | + public BuilderType setRepeatedField(final FieldDescriptor field, | |
364 | + final int index, final Object value) { | |
365 | + internalGetFieldAccessorTable().getField(field) | |
366 | + .setRepeated(this, index, value); | |
367 | + return (BuilderType) this; | |
368 | + } | |
369 | + | |
370 | + public BuilderType addRepeatedField(final FieldDescriptor field, | |
371 | + final Object value) { | |
372 | + internalGetFieldAccessorTable().getField(field).addRepeated(this, value); | |
373 | + return (BuilderType) this; | |
374 | + } | |
375 | + | |
376 | + public final BuilderType setUnknownFields( | |
377 | + final UnknownFieldSet unknownFields) { | |
378 | + this.unknownFields = unknownFields; | |
379 | + onChanged(); | |
380 | + return (BuilderType) this; | |
381 | + } | |
382 | + | |
383 | + @Override | |
384 | + public final BuilderType mergeUnknownFields( | |
385 | + final UnknownFieldSet unknownFields) { | |
386 | + this.unknownFields = | |
387 | + UnknownFieldSet.newBuilder(this.unknownFields) | |
388 | + .mergeFrom(unknownFields) | |
389 | + .build(); | |
390 | + onChanged(); | |
391 | + return (BuilderType) this; | |
392 | + } | |
393 | + | |
394 | + //@Override (Java 1.6 override semantics, but we must support 1.5) | |
395 | + public boolean isInitialized() { | |
396 | + for (final FieldDescriptor field : getDescriptorForType().getFields()) { | |
397 | + // Check that all required fields are present. | |
398 | + if (field.isRequired()) { | |
399 | + if (!hasField(field)) { | |
400 | + return false; | |
401 | + } | |
402 | + } | |
403 | + // Check that embedded messages are initialized. | |
404 | + if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { | |
405 | + if (field.isRepeated()) { | |
406 | + @SuppressWarnings("unchecked") final | |
407 | + List<Message> messageList = (List<Message>) getField(field); | |
408 | + for (final Message element : messageList) { | |
409 | + if (!element.isInitialized()) { | |
410 | + return false; | |
411 | + } | |
412 | + } | |
413 | + } else { | |
414 | + if (hasField(field) && | |
415 | + !((Message) getField(field)).isInitialized()) { | |
416 | + return false; | |
417 | + } | |
418 | + } | |
419 | + } | |
420 | + } | |
421 | + return true; | |
422 | + } | |
423 | + | |
424 | + //@Override (Java 1.6 override semantics, but we must support 1.5) | |
425 | + public final UnknownFieldSet getUnknownFields() { | |
426 | + return unknownFields; | |
427 | + } | |
428 | + | |
429 | + /** | |
430 | + * Called by subclasses to parse an unknown field. | |
431 | + * @return {@code true} unless the tag is an end-group tag. | |
432 | + */ | |
433 | + protected boolean parseUnknownField( | |
434 | + final CodedInputStream input, | |
435 | + final UnknownFieldSet.Builder unknownFields, | |
436 | + final ExtensionRegistryLite extensionRegistry, | |
437 | + final int tag) throws IOException { | |
438 | + return unknownFields.mergeFieldFrom(tag, input); | |
439 | + } | |
440 | + | |
441 | + /** | |
442 | + * Implementation of {@link BuilderParent} for giving to our children. This | |
443 | + * small inner class makes it so we don't publicly expose the BuilderParent | |
444 | + * methods. | |
445 | + */ | |
446 | + private class BuilderParentImpl implements BuilderParent { | |
447 | + | |
448 | + //@Override (Java 1.6 override semantics, but we must support 1.5) | |
449 | + public void markDirty() { | |
450 | + onChanged(); | |
451 | + } | |
452 | + } | |
453 | + | |
454 | + /** | |
455 | + * Gets the {@link BuilderParent} for giving to our children. | |
456 | + * @return The builder parent for our children. | |
457 | + */ | |
458 | + protected BuilderParent getParentForChildren() { | |
459 | + if (meAsParent == null) { | |
460 | + meAsParent = new BuilderParentImpl(); | |
461 | + } | |
462 | + return meAsParent; | |
463 | + } | |
464 | + | |
465 | + /** | |
466 | + * Called when a the builder or one of its nested children has changed | |
467 | + * and any parent should be notified of its invalidation. | |
468 | + */ | |
469 | + protected final void onChanged() { | |
470 | + if (isClean && builderParent != null) { | |
471 | + builderParent.markDirty(); | |
472 | + | |
473 | + // Don't keep dispatching invalidations until build is called again. | |
474 | + isClean = false; | |
475 | + } | |
476 | + } | |
477 | + } | |
478 | + | |
479 | + // ================================================================= | |
480 | + // Extensions-related stuff | |
481 | + | |
482 | + public interface ExtendableMessageOrBuilder< | |
483 | + MessageType extends ExtendableMessage> extends MessageOrBuilder { | |
484 | + | |
485 | + /** Check if a singular extension is present. */ | |
486 | + <Type> boolean hasExtension( | |
487 | + GeneratedExtension<MessageType, Type> extension); | |
488 | + | |
489 | + /** Get the number of elements in a repeated extension. */ | |
490 | + <Type> int getExtensionCount( | |
491 | + GeneratedExtension<MessageType, List<Type>> extension); | |
492 | + | |
493 | + /** Get the value of an extension. */ | |
494 | + <Type> Type getExtension(GeneratedExtension<MessageType, Type> extension); | |
495 | + | |
496 | + /** Get one element of a repeated extension. */ | |
497 | + <Type> Type getExtension( | |
498 | + GeneratedExtension<MessageType, List<Type>> extension, | |
499 | + int index); | |
500 | + } | |
501 | + | |
502 | + /** | |
503 | + * Generated message classes for message types that contain extension ranges | |
504 | + * subclass this. | |
505 | + * | |
506 | + * <p>This class implements type-safe accessors for extensions. They | |
507 | + * implement all the same operations that you can do with normal fields -- | |
508 | + * e.g. "has", "get", and "getCount" -- but for extensions. The extensions | |
509 | + * are identified using instances of the class {@link GeneratedExtension}; | |
510 | + * the protocol compiler generates a static instance of this class for every | |
511 | + * extension in its input. Through the magic of generics, all is made | |
512 | + * type-safe. | |
513 | + * | |
514 | + * <p>For example, imagine you have the {@code .proto} file: | |
515 | + * | |
516 | + * <pre> | |
517 | + * option java_class = "MyProto"; | |
518 | + * | |
519 | + * message Foo { | |
520 | + * extensions 1000 to max; | |
521 | + * } | |
522 | + * | |
523 | + * extend Foo { | |
524 | + * optional int32 bar; | |
525 | + * } | |
526 | + * </pre> | |
527 | + * | |
528 | + * <p>Then you might write code like: | |
529 | + * | |
530 | + * <pre> | |
531 | + * MyProto.Foo foo = getFoo(); | |
532 | + * int i = foo.getExtension(MyProto.bar); | |
533 | + * </pre> | |
534 | + * | |
535 | + * <p>See also {@link ExtendableBuilder}. | |
536 | + */ | |
537 | + public abstract static class ExtendableMessage< | |
538 | + MessageType extends ExtendableMessage> | |
539 | + extends GeneratedMessage | |
540 | + implements ExtendableMessageOrBuilder<MessageType> { | |
541 | + | |
542 | + private final FieldSet<FieldDescriptor> extensions; | |
543 | + | |
544 | + protected ExtendableMessage() { | |
545 | + this.extensions = FieldSet.newFieldSet(); | |
546 | + } | |
547 | + | |
548 | + protected ExtendableMessage( | |
549 | + ExtendableBuilder<MessageType, ?> builder) { | |
550 | + super(builder); | |
551 | + this.extensions = builder.buildExtensions(); | |
552 | + } | |
553 | + | |
554 | + private void verifyExtensionContainingType( | |
555 | + final GeneratedExtension<MessageType, ?> extension) { | |
556 | + if (extension.getDescriptor().getContainingType() != | |
557 | + getDescriptorForType()) { | |
558 | + // This can only happen if someone uses unchecked operations. | |
559 | + throw new IllegalArgumentException( | |
560 | + "Extension is for type \"" + | |
561 | + extension.getDescriptor().getContainingType().getFullName() + | |
562 | + "\" which does not match message type \"" + | |
563 | + getDescriptorForType().getFullName() + "\"."); | |
564 | + } | |
565 | + } | |
566 | + | |
567 | + /** Check if a singular extension is present. */ | |
568 | + //@Override (Java 1.6 override semantics, but we must support 1.5) | |
569 | + public final <Type> boolean hasExtension( | |
570 | + final GeneratedExtension<MessageType, Type> extension) { | |
571 | + verifyExtensionContainingType(extension); | |
572 | + return extensions.hasField(extension.getDescriptor()); | |
573 | + } | |
574 | + | |
575 | + /** Get the number of elements in a repeated extension. */ | |
576 | + //@Override (Java 1.6 override semantics, but we must support 1.5) | |
577 | + public final <Type> int getExtensionCount( | |
578 | + final GeneratedExtension<MessageType, List<Type>> extension) { | |
579 | + verifyExtensionContainingType(extension); | |
580 | + final FieldDescriptor descriptor = extension.getDescriptor(); | |
581 | + return extensions.getRepeatedFieldCount(descriptor); | |
582 | + } | |
583 | + | |
584 | + /** Get the value of an extension. */ | |
585 | + //@Override (Java 1.6 override semantics, but we must support 1.5) | |
586 | + @SuppressWarnings("unchecked") | |
587 | + public final <Type> Type getExtension( | |
588 | + final GeneratedExtension<MessageType, Type> extension) { | |
589 | + verifyExtensionContainingType(extension); | |
590 | + FieldDescriptor descriptor = extension.getDescriptor(); | |
591 | + final Object value = extensions.getField(descriptor); | |
592 | + if (value == null) { | |
593 | + if (descriptor.isRepeated()) { | |
594 | + return (Type) Collections.emptyList(); | |
595 | + } else if (descriptor.getJavaType() == | |
596 | + FieldDescriptor.JavaType.MESSAGE) { | |
597 | + return (Type) extension.getMessageDefaultInstance(); | |
598 | + } else { | |
599 | + return (Type) extension.fromReflectionType( | |
600 | + descriptor.getDefaultValue()); | |
601 | + } | |
602 | + } else { | |
603 | + return (Type) extension.fromReflectionType(value); | |
604 | + } | |
605 | + } | |
606 | + | |
607 | + /** Get one element of a repeated extension. */ | |
608 | + //@Override (Java 1.6 override semantics, but we must support 1.5) | |
609 | + @SuppressWarnings("unchecked") | |
610 | + public final <Type> Type getExtension( | |
611 | + final GeneratedExtension<MessageType, List<Type>> extension, | |
612 | + final int index) { | |
613 | + verifyExtensionContainingType(extension); | |
614 | + FieldDescriptor descriptor = extension.getDescriptor(); | |
615 | + return (Type) extension.singularFromReflectionType( | |
616 | + extensions.getRepeatedField(descriptor, index)); | |
617 | + } | |
618 | + | |
619 | + /** Called by subclasses to check if all extensions are initialized. */ | |
620 | + protected boolean extensionsAreInitialized() { | |
621 | + return extensions.isInitialized(); | |
622 | + } | |
623 | + | |
624 | + @Override | |
625 | + public boolean isInitialized() { | |
626 | + return super.isInitialized() && extensionsAreInitialized(); | |
627 | + } | |
628 | + | |
629 | + /** | |
630 | + * Used by subclasses to serialize extensions. Extension ranges may be | |
631 | + * interleaved with field numbers, but we must write them in canonical | |
632 | + * (sorted by field number) order. ExtensionWriter helps us write | |
633 | + * individual ranges of extensions at once. | |
634 | + */ | |
635 | + protected class ExtensionWriter { | |
636 | + // Imagine how much simpler this code would be if Java iterators had | |
637 | + // a way to get the next element without advancing the iterator. | |
638 | + | |
639 | + private final Iterator<Map.Entry<FieldDescriptor, Object>> iter = | |
640 | + extensions.iterator(); | |
641 | + private Map.Entry<FieldDescriptor, Object> next; | |
642 | + private final boolean messageSetWireFormat; | |
643 | + | |
644 | + private ExtensionWriter(final boolean messageSetWireFormat) { | |
645 | + if (iter.hasNext()) { | |
646 | + next = iter.next(); | |
647 | + } | |
648 | + this.messageSetWireFormat = messageSetWireFormat; | |
649 | + } | |
650 | + | |
651 | + public void writeUntil(final int end, final CodedOutputStream output) | |
652 | + throws IOException { | |
653 | + while (next != null && next.getKey().getNumber() < end) { | |
654 | + FieldDescriptor descriptor = next.getKey(); | |
655 | + if (messageSetWireFormat && descriptor.getLiteJavaType() == | |
656 | + WireFormat.JavaType.MESSAGE && | |
657 | + !descriptor.isRepeated()) { | |
658 | + output.writeMessageSetExtension(descriptor.getNumber(), | |
659 | + (Message) next.getValue()); | |
660 | + } else { | |
661 | + FieldSet.writeField(descriptor, next.getValue(), output); | |
662 | + } | |
663 | + if (iter.hasNext()) { | |
664 | + next = iter.next(); | |
665 | + } else { | |
666 | + next = null; | |
667 | + } | |
668 | + } | |
669 | + } | |
670 | + } | |
671 | + | |
672 | + protected ExtensionWriter newExtensionWriter() { | |
673 | + return new ExtensionWriter(false); | |
674 | + } | |
675 | + protected ExtensionWriter newMessageSetExtensionWriter() { | |
676 | + return new ExtensionWriter(true); | |
677 | + } | |
678 | + | |
679 | + /** Called by subclasses to compute the size of extensions. */ | |
680 | + protected int extensionsSerializedSize() { | |
681 | + return extensions.getSerializedSize(); | |
682 | + } | |
683 | + protected int extensionsSerializedSizeAsMessageSet() { | |
684 | + return extensions.getMessageSetSerializedSize(); | |
685 | + } | |
686 | + | |
687 | + // --------------------------------------------------------------- | |
688 | + // Reflection | |
689 | + | |
690 | + protected Map<FieldDescriptor, Object> getExtensionFields() { | |
691 | + return extensions.getAllFields(); | |
692 | + } | |
693 | + | |
694 | + @Override | |
695 | + public Map<FieldDescriptor, Object> getAllFields() { | |
696 | + final Map<FieldDescriptor, Object> result = super.getAllFieldsMutable(); | |
697 | + result.putAll(getExtensionFields()); | |
698 | + return Collections.unmodifiableMap(result); | |
699 | + } | |
700 | + | |
701 | + @Override | |
702 | + public boolean hasField(final FieldDescriptor field) { | |
703 | + if (field.isExtension()) { | |
704 | + verifyContainingType(field); | |
705 | + return extensions.hasField(field); | |
706 | + } else { | |
707 | + return super.hasField(field); | |
708 | + } | |
709 | + } | |
710 | + | |
711 | + @Override | |
712 | + public Object getField(final FieldDescriptor field) { | |
713 | + if (field.isExtension()) { | |
714 | + verifyContainingType(field); | |
715 | + final Object value = extensions.getField(field); | |
716 | + if (value == null) { | |
717 | + if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { | |
718 | + // Lacking an ExtensionRegistry, we have no way to determine the | |
719 | + // extension's real type, so we return a DynamicMessage. | |
720 | + return DynamicMessage.getDefaultInstance(field.getMessageType()); | |
721 | + } else { | |
722 | + return field.getDefaultValue(); | |
723 | + } | |
724 | + } else { | |
725 | + return value; | |
726 | + } | |
727 | + } else { | |
728 | + return super.getField(field); | |
729 | + } | |
730 | + } | |
731 | + | |
732 | + @Override | |
733 | + public int getRepeatedFieldCount(final FieldDescriptor field) { | |
734 | + if (field.isExtension()) { | |
735 | + verifyContainingType(field); | |
736 | + return extensions.getRepeatedFieldCount(field); | |
737 | + } else { | |
738 | + return super.getRepeatedFieldCount(field); | |
739 | + } | |
740 | + } | |
741 | + | |
742 | + @Override | |
743 | + public Object getRepeatedField(final FieldDescriptor field, | |
744 | + final int index) { | |
745 | + if (field.isExtension()) { | |
746 | + verifyContainingType(field); | |
747 | + return extensions.getRepeatedField(field, index); | |
748 | + } else { | |
749 | + return super.getRepeatedField(field, index); | |
750 | + } | |
751 | + } | |
752 | + | |
753 | + private void verifyContainingType(final FieldDescriptor field) { | |
754 | + if (field.getContainingType() != getDescriptorForType()) { | |
755 | + throw new IllegalArgumentException( | |
756 | + "FieldDescriptor does not match message type."); | |
757 | + } | |
758 | + } | |
759 | + } | |
760 | + | |
761 | + /** | |
762 | + * Generated message builders for message types that contain extension ranges | |
763 | + * subclass this. | |
764 | + * | |
765 | + * <p>This class implements type-safe accessors for extensions. They | |
766 | + * implement all the same operations that you can do with normal fields -- | |
767 | + * e.g. "get", "set", and "add" -- but for extensions. The extensions are | |
768 | + * identified using instances of the class {@link GeneratedExtension}; the | |
769 | + * protocol compiler generates a static instance of this class for every | |
770 | + * extension in its input. Through the magic of generics, all is made | |
771 | + * type-safe. | |
772 | + * | |
773 | + * <p>For example, imagine you have the {@code .proto} file: | |
774 | + * | |
775 | + * <pre> | |
776 | + * option java_class = "MyProto"; | |
777 | + * | |
778 | + * message Foo { | |
779 | + * extensions 1000 to max; | |
780 | + * } | |
781 | + * | |
782 | + * extend Foo { | |
783 | + * optional int32 bar; | |
784 | + * } | |
785 | + * </pre> | |
786 | + * | |
787 | + * <p>Then you might write code like: | |
788 | + * | |
789 | + * <pre> | |
790 | + * MyProto.Foo foo = | |
791 | + * MyProto.Foo.newBuilder() | |
792 | + * .setExtension(MyProto.bar, 123) | |
793 | + * .build(); | |
794 | + * </pre> | |
795 | + * | |
796 | + * <p>See also {@link ExtendableMessage}. | |
797 | + */ | |
798 | + @SuppressWarnings("unchecked") | |
799 | + public abstract static class ExtendableBuilder< | |
800 | + MessageType extends ExtendableMessage, | |
801 | + BuilderType extends ExtendableBuilder> | |
802 | + extends Builder<BuilderType> | |
803 | + implements ExtendableMessageOrBuilder<MessageType> { | |
804 | + | |
805 | + private FieldSet<FieldDescriptor> extensions = FieldSet.emptySet(); | |
806 | + | |
807 | + protected ExtendableBuilder() {} | |
808 | + | |
809 | + protected ExtendableBuilder( | |
810 | + BuilderParent parent) { | |
811 | + super(parent); | |
812 | + } | |
813 | + | |
814 | + @Override | |
815 | + public BuilderType clear() { | |
816 | + extensions = FieldSet.emptySet(); | |
817 | + return super.clear(); | |
818 | + } | |
819 | + | |
820 | + // This is implemented here only to work around an apparent bug in the | |
821 | + // Java compiler and/or build system. See bug #1898463. The mere presence | |
822 | + // of this dummy clone() implementation makes it go away. | |
823 | + @Override | |
824 | + public BuilderType clone() { | |
825 | + throw new UnsupportedOperationException( | |
826 | + "This is supposed to be overridden by subclasses."); | |
827 | + } | |
828 | + | |
829 | + private void ensureExtensionsIsMutable() { | |
830 | + if (extensions.isImmutable()) { | |
831 | + extensions = extensions.clone(); | |
832 | + } | |
833 | + } | |
834 | + | |
835 | + private void verifyExtensionContainingType( | |
836 | + final GeneratedExtension<MessageType, ?> extension) { | |
837 | + if (extension.getDescriptor().getContainingType() != | |
838 | + getDescriptorForType()) { | |
839 | + // This can only happen if someone uses unchecked operations. | |
840 | + throw new IllegalArgumentException( | |
841 | + "Extension is for type \"" + | |
842 | + extension.getDescriptor().getContainingType().getFullName() + | |
843 | + "\" which does not match message type \"" + | |
844 | + getDescriptorForType().getFullName() + "\"."); | |
845 | + } | |
846 | + } | |
847 | + | |
848 | + /** Check if a singular extension is present. */ | |
849 | + //@Override (Java 1.6 override semantics, but we must support 1.5) | |
850 | + public final <Type> boolean hasExtension( | |
851 | + final GeneratedExtension<MessageType, Type> extension) { | |
852 | + verifyExtensionContainingType(extension); | |
853 | + return extensions.hasField(extension.getDescriptor()); | |
854 | + } | |
855 | + | |
856 | + /** Get the number of elements in a repeated extension. */ | |
857 | + //@Override (Java 1.6 override semantics, but we must support 1.5) | |
858 | + public final <Type> int getExtensionCount( | |
859 | + final GeneratedExtension<MessageType, List<Type>> extension) { | |
860 | + verifyExtensionContainingType(extension); | |
861 | + final FieldDescriptor descriptor = extension.getDescriptor(); | |
862 | + return extensions.getRepeatedFieldCount(descriptor); | |
863 | + } | |
864 | + | |
865 | + /** Get the value of an extension. */ | |
866 | + //@Override (Java 1.6 override semantics, but we must support 1.5) | |
867 | + public final <Type> Type getExtension( | |
868 | + final GeneratedExtension<MessageType, Type> extension) { | |
869 | + verifyExtensionContainingType(extension); | |
870 | + FieldDescriptor descriptor = extension.getDescriptor(); | |
871 | + final Object value = extensions.getField(descriptor); | |
872 | + if (value == null) { | |
873 | + if (descriptor.isRepeated()) { | |
874 | + return (Type) Collections.emptyList(); | |
875 | + } else if (descriptor.getJavaType() == | |
876 | + FieldDescriptor.JavaType.MESSAGE) { | |
877 | + return (Type) extension.getMessageDefaultInstance(); | |
878 | + } else { | |
879 | + return (Type) extension.fromReflectionType( | |
880 | + descriptor.getDefaultValue()); | |
881 | + } | |
882 | + } else { | |
883 | + return (Type) extension.fromReflectionType(value); | |
884 | + } | |
885 | + } | |
886 | + | |
887 | + /** Get one element of a repeated extension. */ | |
888 | + //@Override (Java 1.6 override semantics, but we must support 1.5) | |
889 | + public final <Type> Type getExtension( | |
890 | + final GeneratedExtension<MessageType, List<Type>> extension, | |
891 | + final int index) { | |
892 | + verifyExtensionContainingType(extension); | |
893 | + FieldDescriptor descriptor = extension.getDescriptor(); | |
894 | + return (Type) extension.singularFromReflectionType( | |
895 | + extensions.getRepeatedField(descriptor, index)); | |
896 | + } | |
897 | + | |
898 | + /** Set the value of an extension. */ | |
899 | + public final <Type> BuilderType setExtension( | |
900 | + final GeneratedExtension<MessageType, Type> extension, | |
901 | + final Type value) { | |
902 | + verifyExtensionContainingType(extension); | |
903 | + ensureExtensionsIsMutable(); | |
904 | + final FieldDescriptor descriptor = extension.getDescriptor(); | |
905 | + extensions.setField(descriptor, extension.toReflectionType(value)); | |
906 | + onChanged(); | |
907 | + return (BuilderType) this; | |
908 | + } | |
909 | + | |
910 | + /** Set the value of one element of a repeated extension. */ | |
911 | + public final <Type> BuilderType setExtension( | |
912 | + final GeneratedExtension<MessageType, List<Type>> extension, | |
913 | + final int index, final Type value) { | |
914 | + verifyExtensionContainingType(extension); | |
915 | + ensureExtensionsIsMutable(); | |
916 | + final FieldDescriptor descriptor = extension.getDescriptor(); | |
917 | + extensions.setRepeatedField( | |
918 | + descriptor, index, | |
919 | + extension.singularToReflectionType(value)); | |
920 | + onChanged(); | |
921 | + return (BuilderType) this; | |
922 | + } | |
923 | + | |
924 | + /** Append a value to a repeated extension. */ | |
925 | + public final <Type> BuilderType addExtension( | |
926 | + final GeneratedExtension<MessageType, List<Type>> extension, | |
927 | + final Type value) { | |
928 | + verifyExtensionContainingType(extension); | |
929 | + ensureExtensionsIsMutable(); | |
930 | + final FieldDescriptor descriptor = extension.getDescriptor(); | |
931 | + extensions.addRepeatedField( | |
932 | + descriptor, extension.singularToReflectionType(value)); | |
933 | + onChanged(); | |
934 | + return (BuilderType) this; | |
935 | + } | |
936 | + | |
937 | + /** Clear an extension. */ | |
938 | + public final <Type> BuilderType clearExtension( | |
939 | + final GeneratedExtension<MessageType, ?> extension) { | |
940 | + verifyExtensionContainingType(extension); | |
941 | + ensureExtensionsIsMutable(); | |
942 | + extensions.clearField(extension.getDescriptor()); | |
943 | + onChanged(); | |
944 | + return (BuilderType) this; | |
945 | + } | |
946 | + | |
947 | + /** Called by subclasses to check if all extensions are initialized. */ | |
948 | + protected boolean extensionsAreInitialized() { | |
949 | + return extensions.isInitialized(); | |
950 | + } | |
951 | + | |
952 | + /** | |
953 | + * Called by the build code path to create a copy of the extensions for | |
954 | + * building the message. | |
955 | + */ | |
956 | + private FieldSet<FieldDescriptor> buildExtensions() { | |
957 | + extensions.makeImmutable(); | |
958 | + return extensions; | |
959 | + } | |
960 | + | |
961 | + @Override | |
962 | + public boolean isInitialized() { | |
963 | + return super.isInitialized() && extensionsAreInitialized(); | |
964 | + } | |
965 | + | |
966 | + /** | |
967 | + * Called by subclasses to parse an unknown field or an extension. | |
968 | + * @return {@code true} unless the tag is an end-group tag. | |
969 | + */ | |
970 | + @Override | |
971 | + protected boolean parseUnknownField( | |
972 | + final CodedInputStream input, | |
973 | + final UnknownFieldSet.Builder unknownFields, | |
974 | + final ExtensionRegistryLite extensionRegistry, | |
975 | + final int tag) throws IOException { | |
976 | + return AbstractMessage.Builder.mergeFieldFrom( | |
977 | + input, unknownFields, extensionRegistry, this, tag); | |
978 | + } | |
979 | + | |
980 | + // --------------------------------------------------------------- | |
981 | + // Reflection | |
982 | + | |
983 | + @Override | |
984 | + public Map<FieldDescriptor, Object> getAllFields() { | |
985 | + final Map<FieldDescriptor, Object> result = super.getAllFieldsMutable(); | |
986 | + result.putAll(extensions.getAllFields()); | |
987 | + return Collections.unmodifiableMap(result); | |
988 | + } | |
989 | + | |
990 | + @Override | |
991 | + public Object getField(final FieldDescriptor field) { | |
992 | + if (field.isExtension()) { | |
993 | + verifyContainingType(field); | |
994 | + final Object value = extensions.getField(field); | |
995 | + if (value == null) { | |
996 | + if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { | |
997 | + // Lacking an ExtensionRegistry, we have no way to determine the | |
998 | + // extension's real type, so we return a DynamicMessage. | |
999 | + return DynamicMessage.getDefaultInstance(field.getMessageType()); | |
1000 | + } else { | |
1001 | + return field.getDefaultValue(); | |
1002 | + } | |
1003 | + } else { | |
1004 | + return value; | |
1005 | + } | |
1006 | + } else { | |
1007 | + return super.getField(field); | |
1008 | + } | |
1009 | + } | |
1010 | + | |
1011 | + @Override | |
1012 | + public int getRepeatedFieldCount(final FieldDescriptor field) { | |
1013 | + if (field.isExtension()) { | |
1014 | + verifyContainingType(field); | |
1015 | + return extensions.getRepeatedFieldCount(field); | |
1016 | + } else { | |
1017 | + return super.getRepeatedFieldCount(field); | |
1018 | + } | |
1019 | + } | |
1020 | + | |
1021 | + @Override | |
1022 | + public Object getRepeatedField(final FieldDescriptor field, | |
1023 | + final int index) { | |
1024 | + if (field.isExtension()) { | |
1025 | + verifyContainingType(field); | |
1026 | + return extensions.getRepeatedField(field, index); | |
1027 | + } else { | |
1028 | + return super.getRepeatedField(field, index); | |
1029 | + } | |
1030 | + } | |
1031 | + | |
1032 | + @Override | |
1033 | + public boolean hasField(final FieldDescriptor field) { | |
1034 | + if (field.isExtension()) { | |
1035 | + verifyContainingType(field); | |
1036 | + return extensions.hasField(field); | |
1037 | + } else { | |
1038 | + return super.hasField(field); | |
1039 | + } | |
1040 | + } | |
1041 | + | |
1042 | + @Override | |
1043 | + public BuilderType setField(final FieldDescriptor field, | |
1044 | + final Object value) { | |
1045 | + if (field.isExtension()) { | |
1046 | + verifyContainingType(field); | |
1047 | + ensureExtensionsIsMutable(); | |
1048 | + extensions.setField(field, value); | |
1049 | + onChanged(); | |
1050 | + return (BuilderType) this; | |
1051 | + } else { | |
1052 | + return super.setField(field, value); | |
1053 | + } | |
1054 | + } | |
1055 | + | |
1056 | + @Override | |
1057 | + public BuilderType clearField(final FieldDescriptor field) { | |
1058 | + if (field.isExtension()) { | |
1059 | + verifyContainingType(field); | |
1060 | + ensureExtensionsIsMutable(); | |
1061 | + extensions.clearField(field); | |
1062 | + onChanged(); | |
1063 | + return (BuilderType) this; | |
1064 | + } else { | |
1065 | + return super.clearField(field); | |
1066 | + } | |
1067 | + } | |
1068 | + | |
1069 | + @Override | |
1070 | + public BuilderType setRepeatedField(final FieldDescriptor field, | |
1071 | + final int index, final Object value) { | |
1072 | + if (field.isExtension()) { | |
1073 | + verifyContainingType(field); | |
1074 | + ensureExtensionsIsMutable(); | |
1075 | + extensions.setRepeatedField(field, index, value); | |
1076 | + onChanged(); | |
1077 | + return (BuilderType) this; | |
1078 | + } else { | |
1079 | + return super.setRepeatedField(field, index, value); | |
1080 | + } | |
1081 | + } | |
1082 | + | |
1083 | + @Override | |
1084 | + public BuilderType addRepeatedField(final FieldDescriptor field, | |
1085 | + final Object value) { | |
1086 | + if (field.isExtension()) { | |
1087 | + verifyContainingType(field); | |
1088 | + ensureExtensionsIsMutable(); | |
1089 | + extensions.addRepeatedField(field, value); | |
1090 | + onChanged(); | |
1091 | + return (BuilderType) this; | |
1092 | + } else { | |
1093 | + return super.addRepeatedField(field, value); | |
1094 | + } | |
1095 | + } | |
1096 | + | |
1097 | + protected final void mergeExtensionFields(final ExtendableMessage other) { | |
1098 | + ensureExtensionsIsMutable(); | |
1099 | + extensions.mergeFrom(other.extensions); | |
1100 | + onChanged(); | |
1101 | + } | |
1102 | + | |
1103 | + private void verifyContainingType(final FieldDescriptor field) { | |
1104 | + if (field.getContainingType() != getDescriptorForType()) { | |
1105 | + throw new IllegalArgumentException( | |
1106 | + "FieldDescriptor does not match message type."); | |
1107 | + } | |
1108 | + } | |
1109 | + } | |
1110 | + | |
1111 | + // ----------------------------------------------------------------- | |
1112 | + | |
1113 | + /** | |
1114 | + * Gets the descriptor for an extension. The implementation depends on whether | |
1115 | + * the extension is scoped in the top level of a file or scoped in a Message. | |
1116 | + */ | |
1117 | + private static interface ExtensionDescriptorRetriever { | |
1118 | + FieldDescriptor getDescriptor(); | |
1119 | + } | |
1120 | + | |
1121 | + /** For use by generated code only. */ | |
1122 | + public static <ContainingType extends Message, Type> | |
1123 | + GeneratedExtension<ContainingType, Type> | |
1124 | + newMessageScopedGeneratedExtension(final Message scope, | |
1125 | + final int descriptorIndex, | |
1126 | + final Class singularType, | |
1127 | + final Message defaultInstance) { | |
1128 | + // For extensions scoped within a Message, we use the Message to resolve | |
1129 | + // the outer class's descriptor, from which the extension descriptor is | |
1130 | + // obtained. | |
1131 | + return new GeneratedExtension<ContainingType, Type>( | |
1132 | + new ExtensionDescriptorRetriever() { | |
1133 | + //@Override (Java 1.6 override semantics, but we must support 1.5) | |
1134 | + public FieldDescriptor getDescriptor() { | |
1135 | + return scope.getDescriptorForType().getExtensions() | |
1136 | + .get(descriptorIndex); | |
1137 | + } | |
1138 | + }, | |
1139 | + singularType, | |
1140 | + defaultInstance); | |
1141 | + } | |
1142 | + | |
1143 | + /** For use by generated code only. */ | |
1144 | + public static <ContainingType extends Message, Type> | |
1145 | + GeneratedExtension<ContainingType, Type> | |
1146 | + newFileScopedGeneratedExtension(final Class singularType, | |
1147 | + final Message defaultInstance) { | |
1148 | + // For extensions scoped within a file, we rely on the outer class's | |
1149 | + // static initializer to call internalInit() on the extension when the | |
1150 | + // descriptor is available. | |
1151 | + return new GeneratedExtension<ContainingType, Type>( | |
1152 | + null, // ExtensionDescriptorRetriever is initialized in internalInit(); | |
1153 | + singularType, | |
1154 | + defaultInstance); | |
1155 | + } | |
1156 | + | |
1157 | + /** | |
1158 | + * Type used to represent generated extensions. The protocol compiler | |
1159 | + * generates a static singleton instance of this class for each extension. | |
1160 | + * | |
1161 | + * <p>For example, imagine you have the {@code .proto} file: | |
1162 | + * | |
1163 | + * <pre> | |
1164 | + * option java_class = "MyProto"; | |
1165 | + * | |
1166 | + * message Foo { | |
1167 | + * extensions 1000 to max; | |
1168 | + * } | |
1169 | + * | |
1170 | + * extend Foo { | |
1171 | + * optional int32 bar; | |
1172 | + * } | |
1173 | + * </pre> | |
1174 | + * | |
1175 | + * <p>Then, {@code MyProto.Foo.bar} has type | |
1176 | + * {@code GeneratedExtension<MyProto.Foo, Integer>}. | |
1177 | + * | |
1178 | + * <p>In general, users should ignore the details of this type, and simply use | |
1179 | + * these static singletons as parameters to the extension accessors defined | |
1180 | + * in {@link ExtendableMessage} and {@link ExtendableBuilder}. | |
1181 | + */ | |
1182 | + public static final class GeneratedExtension< | |
1183 | + ContainingType extends Message, Type> { | |
1184 | + // TODO(kenton): Find ways to avoid using Java reflection within this | |
1185 | + // class. Also try to avoid suppressing unchecked warnings. | |
1186 | + | |
1187 | + // We can't always initialize the descriptor of a GeneratedExtension when | |
1188 | + // we first construct it due to initialization order difficulties (namely, | |
1189 | + // the descriptor may not have been constructed yet, since it is often | |
1190 | + // constructed by the initializer of a separate module). | |
1191 | + // | |
1192 | + // In the case of nested extensions, we initialize the | |
1193 | + // ExtensionDescriptorRetriever with an instance that uses the scoping | |
1194 | + // Message's default instance to retrieve the extension's descriptor. | |
1195 | + // | |
1196 | + // In the case of non-nested extensions, we initialize the | |
1197 | + // ExtensionDescriptorRetriever to null and rely on the outer class's static | |
1198 | + // initializer to call internalInit() after the descriptor has been parsed. | |
1199 | + private GeneratedExtension(ExtensionDescriptorRetriever descriptorRetriever, | |
1200 | + Class singularType, | |
1201 | + Message messageDefaultInstance) { | |
1202 | + if (Message.class.isAssignableFrom(singularType) && | |
1203 | + !singularType.isInstance(messageDefaultInstance)) { | |
1204 | + throw new IllegalArgumentException( | |
1205 | + "Bad messageDefaultInstance for " + singularType.getName()); | |
1206 | + } | |
1207 | + this.descriptorRetriever = descriptorRetriever; | |
1208 | + this.singularType = singularType; | |
1209 | + this.messageDefaultInstance = messageDefaultInstance; | |
1210 | + | |
1211 | + if (ProtocolMessageEnum.class.isAssignableFrom(singularType)) { | |
1212 | + this.enumValueOf = getMethodOrDie(singularType, "valueOf", | |
1213 | + EnumValueDescriptor.class); | |
1214 | + this.enumGetValueDescriptor = | |
1215 | + getMethodOrDie(singularType, "getValueDescriptor"); | |
1216 | + } else { | |
1217 | + this.enumValueOf = null; | |
1218 | + this.enumGetValueDescriptor = null; | |
1219 | + } | |
1220 | + } | |
1221 | + | |
1222 | + /** For use by generated code only. */ | |
1223 | + public void internalInit(final FieldDescriptor descriptor) { | |
1224 | + if (descriptorRetriever != null) { | |
1225 | + throw new IllegalStateException("Already initialized."); | |
1226 | + } | |
1227 | + descriptorRetriever = new ExtensionDescriptorRetriever() { | |
1228 | + //@Override (Java 1.6 override semantics, but we must support 1.5) | |
1229 | + public FieldDescriptor getDescriptor() { | |
1230 | + return descriptor; | |
1231 | + } | |
1232 | + }; | |
1233 | + } | |
1234 | + | |
1235 | + private ExtensionDescriptorRetriever descriptorRetriever; | |
1236 | + private final Class singularType; | |
1237 | + private final Message messageDefaultInstance; | |
1238 | + private final Method enumValueOf; | |
1239 | + private final Method enumGetValueDescriptor; | |
1240 | + | |
1241 | + public FieldDescriptor getDescriptor() { | |
1242 | + if (descriptorRetriever == null) { | |
1243 | + throw new IllegalStateException( | |
1244 | + "getDescriptor() called before internalInit()"); | |
1245 | + } | |
1246 | + return descriptorRetriever.getDescriptor(); | |
1247 | + } | |
1248 | + | |
1249 | + /** | |
1250 | + * If the extension is an embedded message or group, returns the default | |
1251 | + * instance of the message. | |
1252 | + */ | |
1253 | + public Message getMessageDefaultInstance() { | |
1254 | + return messageDefaultInstance; | |
1255 | + } | |
1256 | + | |
1257 | + /** | |
1258 | + * Convert from the type used by the reflection accessors to the type used | |
1259 | + * by native accessors. E.g., for enums, the reflection accessors use | |
1260 | + * EnumValueDescriptors but the native accessors use the generated enum | |
1261 | + * type. | |
1262 | + */ | |
1263 | + @SuppressWarnings("unchecked") | |
1264 | + private Object fromReflectionType(final Object value) { | |
1265 | + FieldDescriptor descriptor = getDescriptor(); | |
1266 | + if (descriptor.isRepeated()) { | |
1267 | + if (descriptor.getJavaType() == FieldDescriptor.JavaType.MESSAGE || | |
1268 | + descriptor.getJavaType() == FieldDescriptor.JavaType.ENUM) { | |
1269 | + // Must convert the whole list. | |
1270 | + final List result = new ArrayList(); | |
1271 | + for (final Object element : (List) value) { | |
1272 | + result.add(singularFromReflectionType(element)); | |
1273 | + } | |
1274 | + return result; | |
1275 | + } else { | |
1276 | + return value; | |
1277 | + } | |
1278 | + } else { | |
1279 | + return singularFromReflectionType(value); | |
1280 | + } | |
1281 | + } | |
1282 | + | |
1283 | + /** | |
1284 | + * Like {@link #fromReflectionType(Object)}, but if the type is a repeated | |
1285 | + * type, this converts a single element. | |
1286 | + */ | |
1287 | + private Object singularFromReflectionType(final Object value) { | |
1288 | + FieldDescriptor descriptor = getDescriptor(); | |
1289 | + switch (descriptor.getJavaType()) { | |
1290 | + case MESSAGE: | |
1291 | + if (singularType.isInstance(value)) { | |
1292 | + return value; | |
1293 | + } else { | |
1294 | + // It seems the copy of the embedded message stored inside the | |
1295 | + // extended message is not of the exact type the user was | |
1296 | + // expecting. This can happen if a user defines a | |
1297 | + // GeneratedExtension manually and gives it a different type. | |
1298 | + // This should not happen in normal use. But, to be nice, we'll | |
1299 | + // copy the message to whatever type the caller was expecting. | |
1300 | + return messageDefaultInstance.newBuilderForType() | |
1301 | + .mergeFrom((Message) value).build(); | |
1302 | + } | |
1303 | + case ENUM: | |
1304 | + return invokeOrDie(enumValueOf, null, (EnumValueDescriptor) value); | |
1305 | + default: | |
1306 | + return value; | |
1307 | + } | |
1308 | + } | |
1309 | + | |
1310 | + /** | |
1311 | + * Convert from the type used by the native accessors to the type used | |
1312 | + * by reflection accessors. E.g., for enums, the reflection accessors use | |
1313 | + * EnumValueDescriptors but the native accessors use the generated enum | |
1314 | + * type. | |
1315 | + */ | |
1316 | + @SuppressWarnings("unchecked") | |
1317 | + private Object toReflectionType(final Object value) { | |
1318 | + FieldDescriptor descriptor = getDescriptor(); | |
1319 | + if (descriptor.isRepeated()) { | |
1320 | + if (descriptor.getJavaType() == FieldDescriptor.JavaType.ENUM) { | |
1321 | + // Must convert the whole list. | |
1322 | + final List result = new ArrayList(); | |
1323 | + for (final Object element : (List) value) { | |
1324 | + result.add(singularToReflectionType(element)); | |
1325 | + } | |
1326 | + return result; | |
1327 | + } else { | |
1328 | + return value; | |
1329 | + } | |
1330 | + } else { | |
1331 | + return singularToReflectionType(value); | |
1332 | + } | |
1333 | + } | |
1334 | + | |
1335 | + /** | |
1336 | + * Like {@link #toReflectionType(Object)}, but if the type is a repeated | |
1337 | + * type, this converts a single element. | |
1338 | + */ | |
1339 | + private Object singularToReflectionType(final Object value) { | |
1340 | + FieldDescriptor descriptor = getDescriptor(); | |
1341 | + switch (descriptor.getJavaType()) { | |
1342 | + case ENUM: | |
1343 | + return invokeOrDie(enumGetValueDescriptor, value); | |
1344 | + default: | |
1345 | + return value; | |
1346 | + } | |
1347 | + } | |
1348 | + } | |
1349 | + | |
1350 | + // ================================================================= | |
1351 | + | |
1352 | + /** Calls Class.getMethod and throws a RuntimeException if it fails. */ | |
1353 | + @SuppressWarnings("unchecked") | |
1354 | + private static Method getMethodOrDie( | |
1355 | + final Class clazz, final String name, final Class... params) { | |
1356 | + try { | |
1357 | + return clazz.getMethod(name, params); | |
1358 | + } catch (NoSuchMethodException e) { | |
1359 | + throw new RuntimeException( | |
1360 | + "Generated message class \"" + clazz.getName() + | |
1361 | + "\" missing method \"" + name + "\".", e); | |
1362 | + } | |
1363 | + } | |
1364 | + | |
1365 | + /** Calls invoke and throws a RuntimeException if it fails. */ | |
1366 | + private static Object invokeOrDie( | |
1367 | + final Method method, final Object object, final Object... params) { | |
1368 | + try { | |
1369 | + return method.invoke(object, params); | |
1370 | + } catch (IllegalAccessException e) { | |
1371 | + throw new RuntimeException( | |
1372 | + "Couldn't use Java reflection to implement protocol message " + | |
1373 | + "reflection.", e); | |
1374 | + } catch (InvocationTargetException e) { | |
1375 | + final Throwable cause = e.getCause(); | |
1376 | + if (cause instanceof RuntimeException) { | |
1377 | + throw (RuntimeException) cause; | |
1378 | + } else if (cause instanceof Error) { | |
1379 | + throw (Error) cause; | |
1380 | + } else { | |
1381 | + throw new RuntimeException( | |
1382 | + "Unexpected exception thrown by generated accessor method.", cause); | |
1383 | + } | |
1384 | + } | |
1385 | + } | |
1386 | + | |
1387 | + /** | |
1388 | + * Users should ignore this class. This class provides the implementation | |
1389 | + * with access to the fields of a message object using Java reflection. | |
1390 | + */ | |
1391 | + public static final class FieldAccessorTable { | |
1392 | + | |
1393 | + /** | |
1394 | + * Construct a FieldAccessorTable for a particular message class. Only | |
1395 | + * one FieldAccessorTable should ever be constructed per class. | |
1396 | + * | |
1397 | + * @param descriptor The type's descriptor. | |
1398 | + * @param camelCaseNames The camelcase names of all fields in the message. | |
1399 | + * These are used to derive the accessor method names. | |
1400 | + * @param messageClass The message type. | |
1401 | + * @param builderClass The builder type. | |
1402 | + */ | |
1403 | + public FieldAccessorTable( | |
1404 | + final Descriptor descriptor, | |
1405 | + final String[] camelCaseNames, | |
1406 | + final Class<? extends GeneratedMessage> messageClass, | |
1407 | + final Class<? extends Builder> builderClass) { | |
1408 | + this.descriptor = descriptor; | |
1409 | + fields = new FieldAccessor[descriptor.getFields().size()]; | |
1410 | + | |
1411 | + for (int i = 0; i < fields.length; i++) { | |
1412 | + final FieldDescriptor field = descriptor.getFields().get(i); | |
1413 | + if (field.isRepeated()) { | |
1414 | + if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { | |
1415 | + fields[i] = new RepeatedMessageFieldAccessor( | |
1416 | + field, camelCaseNames[i], messageClass, builderClass); | |
1417 | + } else if (field.getJavaType() == FieldDescriptor.JavaType.ENUM) { | |
1418 | + fields[i] = new RepeatedEnumFieldAccessor( | |
1419 | + field, camelCaseNames[i], messageClass, builderClass); | |
1420 | + } else { | |
1421 | + fields[i] = new RepeatedFieldAccessor( | |
1422 | + field, camelCaseNames[i], messageClass, builderClass); | |
1423 | + } | |
1424 | + } else { | |
1425 | + if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { | |
1426 | + fields[i] = new SingularMessageFieldAccessor( | |
1427 | + field, camelCaseNames[i], messageClass, builderClass); | |
1428 | + } else if (field.getJavaType() == FieldDescriptor.JavaType.ENUM) { | |
1429 | + fields[i] = new SingularEnumFieldAccessor( | |
1430 | + field, camelCaseNames[i], messageClass, builderClass); | |
1431 | + } else { | |
1432 | + fields[i] = new SingularFieldAccessor( | |
1433 | + field, camelCaseNames[i], messageClass, builderClass); | |
1434 | + } | |
1435 | + } | |
1436 | + } | |
1437 | + } | |
1438 | + | |
1439 | + private final Descriptor descriptor; | |
1440 | + private final FieldAccessor[] fields; | |
1441 | + | |
1442 | + /** Get the FieldAccessor for a particular field. */ | |
1443 | + private FieldAccessor getField(final FieldDescriptor field) { | |
1444 | + if (field.getContainingType() != descriptor) { | |
1445 | + throw new IllegalArgumentException( | |
1446 | + "FieldDescriptor does not match message type."); | |
1447 | + } else if (field.isExtension()) { | |
1448 | + // If this type had extensions, it would subclass ExtendableMessage, | |
1449 | + // which overrides the reflection interface to handle extensions. | |
1450 | + throw new IllegalArgumentException( | |
1451 | + "This type does not have extensions."); | |
1452 | + } | |
1453 | + return fields[field.getIndex()]; | |
1454 | + } | |
1455 | + | |
1456 | + /** | |
1457 | + * Abstract interface that provides access to a single field. This is | |
1458 | + * implemented differently depending on the field type and cardinality. | |
1459 | + */ | |
1460 | + private interface FieldAccessor { | |
1461 | + Object get(GeneratedMessage message); | |
1462 | + Object get(GeneratedMessage.Builder builder); | |
1463 | + void set(Builder builder, Object value); | |
1464 | + Object getRepeated(GeneratedMessage message, int index); | |
1465 | + Object getRepeated(GeneratedMessage.Builder builder, int index); | |
1466 | + void setRepeated(Builder builder, | |
1467 | + int index, Object value); | |
1468 | + void addRepeated(Builder builder, Object value); | |
1469 | + boolean has(GeneratedMessage message); | |
1470 | + boolean has(GeneratedMessage.Builder builder); | |
1471 | + int getRepeatedCount(GeneratedMessage message); | |
1472 | + int getRepeatedCount(GeneratedMessage.Builder builder); | |
1473 | + void clear(Builder builder); | |
1474 | + Message.Builder newBuilder(); | |
1475 | + } | |
1476 | + | |
1477 | + // --------------------------------------------------------------- | |
1478 | + | |
1479 | + private static class SingularFieldAccessor implements FieldAccessor { | |
1480 | + SingularFieldAccessor( | |
1481 | + final FieldDescriptor descriptor, final String camelCaseName, | |
1482 | + final Class<? extends GeneratedMessage> messageClass, | |
1483 | + final Class<? extends Builder> builderClass) { | |
1484 | + getMethod = getMethodOrDie(messageClass, "get" + camelCaseName); | |
1485 | + getMethodBuilder = getMethodOrDie(builderClass, "get" + camelCaseName); | |
1486 | + type = getMethod.getReturnType(); | |
1487 | + setMethod = getMethodOrDie(builderClass, "set" + camelCaseName, type); | |
1488 | + hasMethod = | |
1489 | + getMethodOrDie(messageClass, "has" + camelCaseName); | |
1490 | + hasMethodBuilder = | |
1491 | + getMethodOrDie(builderClass, "has" + camelCaseName); | |
1492 | + clearMethod = getMethodOrDie(builderClass, "clear" + camelCaseName); | |
1493 | + } | |
1494 | + | |
1495 | + // Note: We use Java reflection to call public methods rather than | |
1496 | + // access private fields directly as this avoids runtime security | |
1497 | + // checks. | |
1498 | + protected final Class<?> type; | |
1499 | + protected final Method getMethod; | |
1500 | + protected final Method getMethodBuilder; | |
1501 | + protected final Method setMethod; | |
1502 | + protected final Method hasMethod; | |
1503 | + protected final Method hasMethodBuilder; | |
1504 | + protected final Method clearMethod; | |
1505 | + | |
1506 | + public Object get(final GeneratedMessage message) { | |
1507 | + return invokeOrDie(getMethod, message); | |
1508 | + } | |
1509 | + public Object get(GeneratedMessage.Builder builder) { | |
1510 | + return invokeOrDie(getMethodBuilder, builder); | |
1511 | + } | |
1512 | + public void set(final Builder builder, final Object value) { | |
1513 | + invokeOrDie(setMethod, builder, value); | |
1514 | + } | |
1515 | + public Object getRepeated(final GeneratedMessage message, | |
1516 | + final int index) { | |
1517 | + throw new UnsupportedOperationException( | |
1518 | + "getRepeatedField() called on a singular field."); | |
1519 | + } | |
1520 | + public Object getRepeated(GeneratedMessage.Builder builder, int index) { | |
1521 | + throw new UnsupportedOperationException( | |
1522 | + "getRepeatedField() called on a singular field."); | |
1523 | + } | |
1524 | + public void setRepeated(final Builder builder, | |
1525 | + final int index, final Object value) { | |
1526 | + throw new UnsupportedOperationException( | |
1527 | + "setRepeatedField() called on a singular field."); | |
1528 | + } | |
1529 | + public void addRepeated(final Builder builder, final Object value) { | |
1530 | + throw new UnsupportedOperationException( | |
1531 | + "addRepeatedField() called on a singular field."); | |
1532 | + } | |
1533 | + public boolean has(final GeneratedMessage message) { | |
1534 | + return (Boolean) invokeOrDie(hasMethod, message); | |
1535 | + } | |
1536 | + public boolean has(GeneratedMessage.Builder builder) { | |
1537 | + return (Boolean) invokeOrDie(hasMethodBuilder, builder); | |
1538 | + } | |
1539 | + public int getRepeatedCount(final GeneratedMessage message) { | |
1540 | + throw new UnsupportedOperationException( | |
1541 | + "getRepeatedFieldSize() called on a singular field."); | |
1542 | + } | |
1543 | + public int getRepeatedCount(GeneratedMessage.Builder builder) { | |
1544 | + throw new UnsupportedOperationException( | |
1545 | + "getRepeatedFieldSize() called on a singular field."); | |
1546 | + } | |
1547 | + public void clear(final Builder builder) { | |
1548 | + invokeOrDie(clearMethod, builder); | |
1549 | + } | |
1550 | + public Message.Builder newBuilder() { | |
1551 | + throw new UnsupportedOperationException( | |
1552 | + "newBuilderForField() called on a non-Message type."); | |
1553 | + } | |
1554 | + } | |
1555 | + | |
1556 | + private static class RepeatedFieldAccessor implements FieldAccessor { | |
1557 | + protected final Class type; | |
1558 | + protected final Method getMethod; | |
1559 | + protected final Method getMethodBuilder; | |
1560 | + protected final Method getRepeatedMethod; | |
1561 | + protected final Method getRepeatedMethodBuilder; | |
1562 | + protected final Method setRepeatedMethod; | |
1563 | + protected final Method addRepeatedMethod; | |
1564 | + protected final Method getCountMethod; | |
1565 | + protected final Method getCountMethodBuilder; | |
1566 | + protected final Method clearMethod; | |
1567 | + | |
1568 | + RepeatedFieldAccessor( | |
1569 | + final FieldDescriptor descriptor, final String camelCaseName, | |
1570 | + final Class<? extends GeneratedMessage> messageClass, | |
1571 | + final Class<? extends Builder> builderClass) { | |
1572 | + getMethod = getMethodOrDie(messageClass, | |
1573 | + "get" + camelCaseName + "List"); | |
1574 | + getMethodBuilder = getMethodOrDie(builderClass, | |
1575 | + "get" + camelCaseName + "List"); | |
1576 | + | |
1577 | + | |
1578 | + getRepeatedMethod = | |
1579 | + getMethodOrDie(messageClass, "get" + camelCaseName, Integer.TYPE); | |
1580 | + getRepeatedMethodBuilder = | |
1581 | + getMethodOrDie(builderClass, "get" + camelCaseName, Integer.TYPE); | |
1582 | + type = getRepeatedMethod.getReturnType(); | |
1583 | + setRepeatedMethod = | |
1584 | + getMethodOrDie(builderClass, "set" + camelCaseName, | |
1585 | + Integer.TYPE, type); | |
1586 | + addRepeatedMethod = | |
1587 | + getMethodOrDie(builderClass, "add" + camelCaseName, type); | |
1588 | + getCountMethod = | |
1589 | + getMethodOrDie(messageClass, "get" + camelCaseName + "Count"); | |
1590 | + getCountMethodBuilder = | |
1591 | + getMethodOrDie(builderClass, "get" + camelCaseName + "Count"); | |
1592 | + | |
1593 | + clearMethod = getMethodOrDie(builderClass, "clear" + camelCaseName); | |
1594 | + } | |
1595 | + | |
1596 | + public Object get(final GeneratedMessage message) { | |
1597 | + return invokeOrDie(getMethod, message); | |
1598 | + } | |
1599 | + public Object get(GeneratedMessage.Builder builder) { | |
1600 | + return invokeOrDie(getMethodBuilder, builder); | |
1601 | + } | |
1602 | + public void set(final Builder builder, final Object value) { | |
1603 | + // Add all the elements individually. This serves two purposes: | |
1604 | + // 1) Verifies that each element has the correct type. | |
1605 | + // 2) Insures that the caller cannot modify the list later on and | |
1606 | + // have the modifications be reflected in the message. | |
1607 | + clear(builder); | |
1608 | + for (final Object element : (List<?>) value) { | |
1609 | + addRepeated(builder, element); | |
1610 | + } | |
1611 | + } | |
1612 | + public Object getRepeated(final GeneratedMessage message, | |
1613 | + final int index) { | |
1614 | + return invokeOrDie(getRepeatedMethod, message, index); | |
1615 | + } | |
1616 | + public Object getRepeated(GeneratedMessage.Builder builder, int index) { | |
1617 | + return invokeOrDie(getRepeatedMethodBuilder, builder, index); | |
1618 | + } | |
1619 | + public void setRepeated(final Builder builder, | |
1620 | + final int index, final Object value) { | |
1621 | + invokeOrDie(setRepeatedMethod, builder, index, value); | |
1622 | + } | |
1623 | + public void addRepeated(final Builder builder, final Object value) { | |
1624 | + invokeOrDie(addRepeatedMethod, builder, value); | |
1625 | + } | |
1626 | + public boolean has(final GeneratedMessage message) { | |
1627 | + throw new UnsupportedOperationException( | |
1628 | + "hasField() called on a singular field."); | |
1629 | + } | |
1630 | + public boolean has(GeneratedMessage.Builder builder) { | |
1631 | + throw new UnsupportedOperationException( | |
1632 | + "hasField() called on a singular field."); | |
1633 | + } | |
1634 | + public int getRepeatedCount(final GeneratedMessage message) { | |
1635 | + return (Integer) invokeOrDie(getCountMethod, message); | |
1636 | + } | |
1637 | + public int getRepeatedCount(GeneratedMessage.Builder builder) { | |
1638 | + return (Integer) invokeOrDie(getCountMethodBuilder, builder); | |
1639 | + } | |
1640 | + public void clear(final Builder builder) { | |
1641 | + invokeOrDie(clearMethod, builder); | |
1642 | + } | |
1643 | + public Message.Builder newBuilder() { | |
1644 | + throw new UnsupportedOperationException( | |
1645 | + "newBuilderForField() called on a non-Message type."); | |
1646 | + } | |
1647 | + } | |
1648 | + | |
1649 | + // --------------------------------------------------------------- | |
1650 | + | |
1651 | + private static final class SingularEnumFieldAccessor | |
1652 | + extends SingularFieldAccessor { | |
1653 | + SingularEnumFieldAccessor( | |
1654 | + final FieldDescriptor descriptor, final String camelCaseName, | |
1655 | + final Class<? extends GeneratedMessage> messageClass, | |
1656 | + final Class<? extends Builder> builderClass) { | |
1657 | + super(descriptor, camelCaseName, messageClass, builderClass); | |
1658 | + | |
1659 | + valueOfMethod = getMethodOrDie(type, "valueOf", | |
1660 | + EnumValueDescriptor.class); | |
1661 | + getValueDescriptorMethod = | |
1662 | + getMethodOrDie(type, "getValueDescriptor"); | |
1663 | + } | |
1664 | + | |
1665 | + private Method valueOfMethod; | |
1666 | + private Method getValueDescriptorMethod; | |
1667 | + | |
1668 | + @Override | |
1669 | + public Object get(final GeneratedMessage message) { | |
1670 | + return invokeOrDie(getValueDescriptorMethod, super.get(message)); | |
1671 | + } | |
1672 | + | |
1673 | + @Override | |
1674 | + public Object get(final GeneratedMessage.Builder builder) { | |
1675 | + return invokeOrDie(getValueDescriptorMethod, super.get(builder)); | |
1676 | + } | |
1677 | + | |
1678 | + @Override | |
1679 | + public void set(final Builder builder, final Object value) { | |
1680 | + super.set(builder, invokeOrDie(valueOfMethod, null, value)); | |
1681 | + } | |
1682 | + } | |
1683 | + | |
1684 | + private static final class RepeatedEnumFieldAccessor | |
1685 | + extends RepeatedFieldAccessor { | |
1686 | + RepeatedEnumFieldAccessor( | |
1687 | + final FieldDescriptor descriptor, final String camelCaseName, | |
1688 | + final Class<? extends GeneratedMessage> messageClass, | |
1689 | + final Class<? extends Builder> builderClass) { | |
1690 | + super(descriptor, camelCaseName, messageClass, builderClass); | |
1691 | + | |
1692 | + valueOfMethod = getMethodOrDie(type, "valueOf", | |
1693 | + EnumValueDescriptor.class); | |
1694 | + getValueDescriptorMethod = | |
1695 | + getMethodOrDie(type, "getValueDescriptor"); | |
1696 | + } | |
1697 | + | |
1698 | + private final Method valueOfMethod; | |
1699 | + private final Method getValueDescriptorMethod; | |
1700 | + | |
1701 | + @Override | |
1702 | + @SuppressWarnings("unchecked") | |
1703 | + public Object get(final GeneratedMessage message) { | |
1704 | + final List newList = new ArrayList(); | |
1705 | + for (final Object element : (List) super.get(message)) { | |
1706 | + newList.add(invokeOrDie(getValueDescriptorMethod, element)); | |
1707 | + } | |
1708 | + return Collections.unmodifiableList(newList); | |
1709 | + } | |
1710 | + | |
1711 | + @Override | |
1712 | + @SuppressWarnings("unchecked") | |
1713 | + public Object get(final GeneratedMessage.Builder builder) { | |
1714 | + final List newList = new ArrayList(); | |
1715 | + for (final Object element : (List) super.get(builder)) { | |
1716 | + newList.add(invokeOrDie(getValueDescriptorMethod, element)); | |
1717 | + } | |
1718 | + return Collections.unmodifiableList(newList); | |
1719 | + } | |
1720 | + | |
1721 | + @Override | |
1722 | + public Object getRepeated(final GeneratedMessage message, | |
1723 | + final int index) { | |
1724 | + return invokeOrDie(getValueDescriptorMethod, | |
1725 | + super.getRepeated(message, index)); | |
1726 | + } | |
1727 | + @Override | |
1728 | + public Object getRepeated(final GeneratedMessage.Builder builder, | |
1729 | + final int index) { | |
1730 | + return invokeOrDie(getValueDescriptorMethod, | |
1731 | + super.getRepeated(builder, index)); | |
1732 | + } | |
1733 | + @Override | |
1734 | + public void setRepeated(final Builder builder, | |
1735 | + final int index, final Object value) { | |
1736 | + super.setRepeated(builder, index, invokeOrDie(valueOfMethod, null, | |
1737 | + value)); | |
1738 | + } | |
1739 | + @Override | |
1740 | + public void addRepeated(final Builder builder, final Object value) { | |
1741 | + super.addRepeated(builder, invokeOrDie(valueOfMethod, null, value)); | |
1742 | + } | |
1743 | + } | |
1744 | + | |
1745 | + // --------------------------------------------------------------- | |
1746 | + | |
1747 | + private static final class SingularMessageFieldAccessor | |
1748 | + extends SingularFieldAccessor { | |
1749 | + SingularMessageFieldAccessor( | |
1750 | + final FieldDescriptor descriptor, final String camelCaseName, | |
1751 | + final Class<? extends GeneratedMessage> messageClass, | |
1752 | + final Class<? extends Builder> builderClass) { | |
1753 | + super(descriptor, camelCaseName, messageClass, builderClass); | |
1754 | + | |
1755 | + newBuilderMethod = getMethodOrDie(type, "newBuilder"); | |
1756 | + } | |
1757 | + | |
1758 | + private final Method newBuilderMethod; | |
1759 | + | |
1760 | + private Object coerceType(final Object value) { | |
1761 | + if (type.isInstance(value)) { | |
1762 | + return value; | |
1763 | + } else { | |
1764 | + // The value is not the exact right message type. However, if it | |
1765 | + // is an alternative implementation of the same type -- e.g. a | |
1766 | + // DynamicMessage -- we should accept it. In this case we can make | |
1767 | + // a copy of the message. | |
1768 | + return ((Message.Builder) invokeOrDie(newBuilderMethod, null)) | |
1769 | + .mergeFrom((Message) value).build(); | |
1770 | + } | |
1771 | + } | |
1772 | + | |
1773 | + @Override | |
1774 | + public void set(final Builder builder, final Object value) { | |
1775 | + super.set(builder, coerceType(value)); | |
1776 | + } | |
1777 | + @Override | |
1778 | + public Message.Builder newBuilder() { | |
1779 | + return (Message.Builder) invokeOrDie(newBuilderMethod, null); | |
1780 | + } | |
1781 | + } | |
1782 | + | |
1783 | + private static final class RepeatedMessageFieldAccessor | |
1784 | + extends RepeatedFieldAccessor { | |
1785 | + RepeatedMessageFieldAccessor( | |
1786 | + final FieldDescriptor descriptor, final String camelCaseName, | |
1787 | + final Class<? extends GeneratedMessage> messageClass, | |
1788 | + final Class<? extends Builder> builderClass) { | |
1789 | + super(descriptor, camelCaseName, messageClass, builderClass); | |
1790 | + | |
1791 | + newBuilderMethod = getMethodOrDie(type, "newBuilder"); | |
1792 | + } | |
1793 | + | |
1794 | + private final Method newBuilderMethod; | |
1795 | + | |
1796 | + private Object coerceType(final Object value) { | |
1797 | + if (type.isInstance(value)) { | |
1798 | + return value; | |
1799 | + } else { | |
1800 | + // The value is not the exact right message type. However, if it | |
1801 | + // is an alternative implementation of the same type -- e.g. a | |
1802 | + // DynamicMessage -- we should accept it. In this case we can make | |
1803 | + // a copy of the message. | |
1804 | + return ((Message.Builder) invokeOrDie(newBuilderMethod, null)) | |
1805 | + .mergeFrom((Message) value).build(); | |
1806 | + } | |
1807 | + } | |
1808 | + | |
1809 | + @Override | |
1810 | + public void setRepeated(final Builder builder, | |
1811 | + final int index, final Object value) { | |
1812 | + super.setRepeated(builder, index, coerceType(value)); | |
1813 | + } | |
1814 | + @Override | |
1815 | + public void addRepeated(final Builder builder, final Object value) { | |
1816 | + super.addRepeated(builder, coerceType(value)); | |
1817 | + } | |
1818 | + @Override | |
1819 | + public Message.Builder newBuilder() { | |
1820 | + return (Message.Builder) invokeOrDie(newBuilderMethod, null); | |
1821 | + } | |
1822 | + } | |
1823 | + } | |
1824 | + | |
1825 | + /** | |
1826 | + * Replaces this object in the output stream with a serialized form. | |
1827 | + * Part of Java's serialization magic. Generated sub-classes must override | |
1828 | + * this method by calling <code>return super.writeReplace();</code> | |
1829 | + * @return a SerializedForm of this message | |
1830 | + */ | |
1831 | + protected Object writeReplace() throws ObjectStreamException { | |
1832 | + return new GeneratedMessageLite.SerializedForm(this); | |
1833 | + } | |
1834 | +} |
@@ -0,0 +1,731 @@ | ||
1 | +// Protocol Buffers - Google's data interchange format | |
2 | +// Copyright 2008 Google Inc. All rights reserved. | |
3 | +// http://code.google.com/p/protobuf/ | |
4 | +// | |
5 | +// Redistribution and use in source and binary forms, with or without | |
6 | +// modification, are permitted provided that the following conditions are | |
7 | +// met: | |
8 | +// | |
9 | +// * Redistributions of source code must retain the above copyright | |
10 | +// notice, this list of conditions and the following disclaimer. | |
11 | +// * Redistributions in binary form must reproduce the above | |
12 | +// copyright notice, this list of conditions and the following disclaimer | |
13 | +// in the documentation and/or other materials provided with the | |
14 | +// distribution. | |
15 | +// * Neither the name of Google Inc. nor the names of its | |
16 | +// contributors may be used to endorse or promote products derived from | |
17 | +// this software without specific prior written permission. | |
18 | +// | |
19 | +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
20 | +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
21 | +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
22 | +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
23 | +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
24 | +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
25 | +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
26 | +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
27 | +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
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. | |
30 | + | |
31 | +package com.google.protobuf; | |
32 | + | |
33 | +import java.io.IOException; | |
34 | +import java.io.ObjectStreamException; | |
35 | +import java.io.Serializable; | |
36 | +import java.lang.reflect.InvocationTargetException; | |
37 | +import java.lang.reflect.Method; | |
38 | +import java.util.Collections; | |
39 | +import java.util.Iterator; | |
40 | +import java.util.List; | |
41 | +import java.util.Map; | |
42 | + | |
43 | +/** | |
44 | + * Lite version of {@link GeneratedMessage}. | |
45 | + * | |
46 | + * @author kenton@google.com Kenton Varda | |
47 | + */ | |
48 | +public abstract class GeneratedMessageLite extends AbstractMessageLite | |
49 | + implements Serializable { | |
50 | + private static final long serialVersionUID = 1L; | |
51 | + | |
52 | + protected GeneratedMessageLite() { | |
53 | + } | |
54 | + | |
55 | + protected GeneratedMessageLite(Builder builder) { | |
56 | + } | |
57 | + | |
58 | + @SuppressWarnings("unchecked") | |
59 | + public abstract static class Builder<MessageType extends GeneratedMessageLite, | |
60 | + BuilderType extends Builder> | |
61 | + extends AbstractMessageLite.Builder<BuilderType> { | |
62 | + protected Builder() {} | |
63 | + | |
64 | + //@Override (Java 1.6 override semantics, but we must support 1.5) | |
65 | + public BuilderType clear() { | |
66 | + return (BuilderType) this; | |
67 | + } | |
68 | + | |
69 | + // This is implemented here only to work around an apparent bug in the | |
70 | + // Java compiler and/or build system. See bug #1898463. The mere presence | |
71 | + // of this dummy clone() implementation makes it go away. | |
72 | + @Override | |
73 | + public BuilderType clone() { | |
74 | + throw new UnsupportedOperationException( | |
75 | + "This is supposed to be overridden by subclasses."); | |
76 | + } | |
77 | + | |
78 | + /** All subclasses implement this. */ | |
79 | + public abstract BuilderType mergeFrom(MessageType message); | |
80 | + | |
81 | + // Defined here for return type covariance. | |
82 | + public abstract MessageType getDefaultInstanceForType(); | |
83 | + | |
84 | + /** | |
85 | + * Called by subclasses to parse an unknown field. | |
86 | + * @return {@code true} unless the tag is an end-group tag. | |
87 | + */ | |
88 | + protected boolean parseUnknownField( | |
89 | + final CodedInputStream input, | |
90 | + final ExtensionRegistryLite extensionRegistry, | |
91 | + final int tag) throws IOException { | |
92 | + return input.skipField(tag); | |
93 | + } | |
94 | + } | |
95 | + | |
96 | + // ================================================================= | |
97 | + // Extensions-related stuff | |
98 | + | |
99 | + /** | |
100 | + * Lite equivalent of {@link com.google.protobuf.GeneratedMessage.ExtendableMessageOrBuilder}. | |
101 | + */ | |
102 | + public interface ExtendableMessageOrBuilder< | |
103 | + MessageType extends ExtendableMessage> extends MessageLiteOrBuilder { | |
104 | + | |
105 | + /** Check if a singular extension is present. */ | |
106 | + <Type> boolean hasExtension( | |
107 | + GeneratedExtension<MessageType, Type> extension); | |
108 | + | |
109 | + /** Get the number of elements in a repeated extension. */ | |
110 | + <Type> int getExtensionCount( | |
111 | + GeneratedExtension<MessageType, List<Type>> extension); | |
112 | + | |
113 | + /** Get the value of an extension. */ | |
114 | + <Type> Type getExtension(GeneratedExtension<MessageType, Type> extension); | |
115 | + | |
116 | + /** Get one element of a repeated extension. */ | |
117 | + <Type> Type getExtension( | |
118 | + GeneratedExtension<MessageType, List<Type>> extension, | |
119 | + int index); | |
120 | + } | |
121 | + | |
122 | + /** | |
123 | + * Lite equivalent of {@link GeneratedMessage.ExtendableMessage}. | |
124 | + */ | |
125 | + public abstract static class ExtendableMessage< | |
126 | + MessageType extends ExtendableMessage<MessageType>> | |
127 | + extends GeneratedMessageLite | |
128 | + implements ExtendableMessageOrBuilder<MessageType> { | |
129 | + | |
130 | + private final FieldSet<ExtensionDescriptor> extensions; | |
131 | + | |
132 | + protected ExtendableMessage() { | |
133 | + this.extensions = FieldSet.newFieldSet(); | |
134 | + } | |
135 | + | |
136 | + protected ExtendableMessage(ExtendableBuilder<MessageType, ?> builder) { | |
137 | + this.extensions = builder.buildExtensions(); | |
138 | + } | |
139 | + | |
140 | + private void verifyExtensionContainingType( | |
141 | + final GeneratedExtension<MessageType, ?> extension) { | |
142 | + if (extension.getContainingTypeDefaultInstance() != | |
143 | + getDefaultInstanceForType()) { | |
144 | + // This can only happen if someone uses unchecked operations. | |
145 | + throw new IllegalArgumentException( | |
146 | + "This extension is for a different message type. Please make " + | |
147 | + "sure that you are not suppressing any generics type warnings."); | |
148 | + } | |
149 | + } | |
150 | + | |
151 | + /** Check if a singular extension is present. */ | |
152 | + //@Override (Java 1.6 override semantics, but we must support 1.5) | |
153 | + public final <Type> boolean hasExtension( | |
154 | + final GeneratedExtension<MessageType, Type> extension) { | |
155 | + verifyExtensionContainingType(extension); | |
156 | + return extensions.hasField(extension.descriptor); | |
157 | + } | |
158 | + | |
159 | + /** Get the number of elements in a repeated extension. */ | |
160 | + //@Override (Java 1.6 override semantics, but we must support 1.5) | |
161 | + public final <Type> int getExtensionCount( | |
162 | + final GeneratedExtension<MessageType, List<Type>> extension) { | |
163 | + verifyExtensionContainingType(extension); | |
164 | + return extensions.getRepeatedFieldCount(extension.descriptor); | |
165 | + } | |
166 | + | |
167 | + /** Get the value of an extension. */ | |
168 | + //@Override (Java 1.6 override semantics, but we must support 1.5) | |
169 | + @SuppressWarnings("unchecked") | |
170 | + public final <Type> Type getExtension( | |
171 | + final GeneratedExtension<MessageType, Type> extension) { | |
172 | + verifyExtensionContainingType(extension); | |
173 | + final Object value = extensions.getField(extension.descriptor); | |
174 | + if (value == null) { | |
175 | + return extension.defaultValue; | |
176 | + } else { | |
177 | + return (Type) value; | |
178 | + } | |
179 | + } | |
180 | + | |
181 | + /** Get one element of a repeated extension. */ | |
182 | + //@Override (Java 1.6 override semantics, but we must support 1.5) | |
183 | + @SuppressWarnings("unchecked") | |
184 | + public final <Type> Type getExtension( | |
185 | + final GeneratedExtension<MessageType, List<Type>> extension, | |
186 | + final int index) { | |
187 | + verifyExtensionContainingType(extension); | |
188 | + return (Type) extensions.getRepeatedField(extension.descriptor, index); | |
189 | + } | |
190 | + | |
191 | + /** Called by subclasses to check if all extensions are initialized. */ | |
192 | + protected boolean extensionsAreInitialized() { | |
193 | + return extensions.isInitialized(); | |
194 | + } | |
195 | + | |
196 | + /** | |
197 | + * Used by subclasses to serialize extensions. Extension ranges may be | |
198 | + * interleaved with field numbers, but we must write them in canonical | |
199 | + * (sorted by field number) order. ExtensionWriter helps us write | |
200 | + * individual ranges of extensions at once. | |
201 | + */ | |
202 | + protected class ExtensionWriter { | |
203 | + // Imagine how much simpler this code would be if Java iterators had | |
204 | + // a way to get the next element without advancing the iterator. | |
205 | + | |
206 | + private final Iterator<Map.Entry<ExtensionDescriptor, Object>> iter = | |
207 | + extensions.iterator(); | |
208 | + private Map.Entry<ExtensionDescriptor, Object> next; | |
209 | + private final boolean messageSetWireFormat; | |
210 | + | |
211 | + private ExtensionWriter(boolean messageSetWireFormat) { | |
212 | + if (iter.hasNext()) { | |
213 | + next = iter.next(); | |
214 | + } | |
215 | + this.messageSetWireFormat = messageSetWireFormat; | |
216 | + } | |
217 | + | |
218 | + public void writeUntil(final int end, final CodedOutputStream output) | |
219 | + throws IOException { | |
220 | + while (next != null && next.getKey().getNumber() < end) { | |
221 | + ExtensionDescriptor extension = next.getKey(); | |
222 | + if (messageSetWireFormat && extension.getLiteJavaType() == | |
223 | + WireFormat.JavaType.MESSAGE && | |
224 | + !extension.isRepeated()) { | |
225 | + output.writeMessageSetExtension(extension.getNumber(), | |
226 | + (MessageLite) next.getValue()); | |
227 | + } else { | |
228 | + FieldSet.writeField(extension, next.getValue(), output); | |
229 | + } | |
230 | + if (iter.hasNext()) { | |
231 | + next = iter.next(); | |
232 | + } else { | |
233 | + next = null; | |
234 | + } | |
235 | + } | |
236 | + } | |
237 | + } | |
238 | + | |
239 | + protected ExtensionWriter newExtensionWriter() { | |
240 | + return new ExtensionWriter(false); | |
241 | + } | |
242 | + protected ExtensionWriter newMessageSetExtensionWriter() { | |
243 | + return new ExtensionWriter(true); | |
244 | + } | |
245 | + | |
246 | + /** Called by subclasses to compute the size of extensions. */ | |
247 | + protected int extensionsSerializedSize() { | |
248 | + return extensions.getSerializedSize(); | |
249 | + } | |
250 | + protected int extensionsSerializedSizeAsMessageSet() { | |
251 | + return extensions.getMessageSetSerializedSize(); | |
252 | + } | |
253 | + } | |
254 | + | |
255 | + /** | |
256 | + * Lite equivalent of {@link GeneratedMessage.ExtendableBuilder}. | |
257 | + */ | |
258 | + @SuppressWarnings("unchecked") | |
259 | + public abstract static class ExtendableBuilder< | |
260 | + MessageType extends ExtendableMessage<MessageType>, | |
261 | + BuilderType extends ExtendableBuilder<MessageType, BuilderType>> | |
262 | + extends Builder<MessageType, BuilderType> | |
263 | + implements ExtendableMessageOrBuilder<MessageType> { | |
264 | + protected ExtendableBuilder() {} | |
265 | + | |
266 | + private FieldSet<ExtensionDescriptor> extensions = FieldSet.emptySet(); | |
267 | + private boolean extensionsIsMutable; | |
268 | + | |
269 | + @Override | |
270 | + public BuilderType clear() { | |
271 | + extensions.clear(); | |
272 | + extensionsIsMutable = false; | |
273 | + return super.clear(); | |
274 | + } | |
275 | + | |
276 | + private void ensureExtensionsIsMutable() { | |
277 | + if (!extensionsIsMutable) { | |
278 | + extensions = extensions.clone(); | |
279 | + extensionsIsMutable = true; | |
280 | + } | |
281 | + } | |
282 | + | |
283 | + /** | |
284 | + * Called by the build code path to create a copy of the extensions for | |
285 | + * building the message. | |
286 | + */ | |
287 | + private FieldSet<ExtensionDescriptor> buildExtensions() { | |
288 | + extensions.makeImmutable(); | |
289 | + extensionsIsMutable = false; | |
290 | + return extensions; | |
291 | + } | |
292 | + | |
293 | + private void verifyExtensionContainingType( | |
294 | + final GeneratedExtension<MessageType, ?> extension) { | |
295 | + if (extension.getContainingTypeDefaultInstance() != | |
296 | + getDefaultInstanceForType()) { | |
297 | + // This can only happen if someone uses unchecked operations. | |
298 | + throw new IllegalArgumentException( | |
299 | + "This extension is for a different message type. Please make " + | |
300 | + "sure that you are not suppressing any generics type warnings."); | |
301 | + } | |
302 | + } | |
303 | + | |
304 | + /** Check if a singular extension is present. */ | |
305 | + //@Override (Java 1.6 override semantics, but we must support 1.5) | |
306 | + public final <Type> boolean hasExtension( | |
307 | + final GeneratedExtension<MessageType, Type> extension) { | |
308 | + verifyExtensionContainingType(extension); | |
309 | + return extensions.hasField(extension.descriptor); | |
310 | + } | |
311 | + | |
312 | + /** Get the number of elements in a repeated extension. */ | |
313 | + //@Override (Java 1.6 override semantics, but we must support 1.5) | |
314 | + public final <Type> int getExtensionCount( | |
315 | + final GeneratedExtension<MessageType, List<Type>> extension) { | |
316 | + verifyExtensionContainingType(extension); | |
317 | + return extensions.getRepeatedFieldCount(extension.descriptor); | |
318 | + } | |
319 | + | |
320 | + /** Get the value of an extension. */ | |
321 | + //@Override (Java 1.6 override semantics, but we must support 1.5) | |
322 | + @SuppressWarnings("unchecked") | |
323 | + public final <Type> Type getExtension( | |
324 | + final GeneratedExtension<MessageType, Type> extension) { | |
325 | + verifyExtensionContainingType(extension); | |
326 | + final Object value = extensions.getField(extension.descriptor); | |
327 | + if (value == null) { | |
328 | + return extension.defaultValue; | |
329 | + } else { | |
330 | + return (Type) value; | |
331 | + } | |
332 | + } | |
333 | + | |
334 | + /** Get one element of a repeated extension. */ | |
335 | + @SuppressWarnings("unchecked") | |
336 | + //@Override (Java 1.6 override semantics, but we must support 1.5) | |
337 | + public final <Type> Type getExtension( | |
338 | + final GeneratedExtension<MessageType, List<Type>> extension, | |
339 | + final int index) { | |
340 | + verifyExtensionContainingType(extension); | |
341 | + return (Type) extensions.getRepeatedField(extension.descriptor, index); | |
342 | + } | |
343 | + | |
344 | + // This is implemented here only to work around an apparent bug in the | |
345 | + // Java compiler and/or build system. See bug #1898463. The mere presence | |
346 | + // of this dummy clone() implementation makes it go away. | |
347 | + @Override | |
348 | + public BuilderType clone() { | |
349 | + throw new UnsupportedOperationException( | |
350 | + "This is supposed to be overridden by subclasses."); | |
351 | + } | |
352 | + | |
353 | + /** Set the value of an extension. */ | |
354 | + public final <Type> BuilderType setExtension( | |
355 | + final GeneratedExtension<MessageType, Type> extension, | |
356 | + final Type value) { | |
357 | + verifyExtensionContainingType(extension); | |
358 | + ensureExtensionsIsMutable(); | |
359 | + extensions.setField(extension.descriptor, value); | |
360 | + return (BuilderType) this; | |
361 | + } | |
362 | + | |
363 | + /** Set the value of one element of a repeated extension. */ | |
364 | + public final <Type> BuilderType setExtension( | |
365 | + final GeneratedExtension<MessageType, List<Type>> extension, | |
366 | + final int index, final Type value) { | |
367 | + verifyExtensionContainingType(extension); | |
368 | + ensureExtensionsIsMutable(); | |
369 | + extensions.setRepeatedField(extension.descriptor, index, value); | |
370 | + return (BuilderType) this; | |
371 | + } | |
372 | + | |
373 | + /** Append a value to a repeated extension. */ | |
374 | + public final <Type> BuilderType addExtension( | |
375 | + final GeneratedExtension<MessageType, List<Type>> extension, | |
376 | + final Type value) { | |
377 | + verifyExtensionContainingType(extension); | |
378 | + ensureExtensionsIsMutable(); | |
379 | + extensions.addRepeatedField(extension.descriptor, value); | |
380 | + return (BuilderType) this; | |
381 | + } | |
382 | + | |
383 | + /** Clear an extension. */ | |
384 | + public final <Type> BuilderType clearExtension( | |
385 | + final GeneratedExtension<MessageType, ?> extension) { | |
386 | + verifyExtensionContainingType(extension); | |
387 | + ensureExtensionsIsMutable(); | |
388 | + extensions.clearField(extension.descriptor); | |
389 | + return (BuilderType) this; | |
390 | + } | |
391 | + | |
392 | + /** Called by subclasses to check if all extensions are initialized. */ | |
393 | + protected boolean extensionsAreInitialized() { | |
394 | + return extensions.isInitialized(); | |
395 | + } | |
396 | + | |
397 | + /** | |
398 | + * Called by subclasses to parse an unknown field or an extension. | |
399 | + * @return {@code true} unless the tag is an end-group tag. | |
400 | + */ | |
401 | + @Override | |
402 | + protected boolean parseUnknownField( | |
403 | + final CodedInputStream input, | |
404 | + final ExtensionRegistryLite extensionRegistry, | |
405 | + final int tag) throws IOException { | |
406 | + final int wireType = WireFormat.getTagWireType(tag); | |
407 | + final int fieldNumber = WireFormat.getTagFieldNumber(tag); | |
408 | + | |
409 | + final GeneratedExtension<MessageType, ?> extension = | |
410 | + extensionRegistry.findLiteExtensionByNumber( | |
411 | + getDefaultInstanceForType(), fieldNumber); | |
412 | + | |
413 | + boolean unknown = false; | |
414 | + boolean packed = false; | |
415 | + if (extension == null) { | |
416 | + unknown = true; // Unknown field. | |
417 | + } else if (wireType == FieldSet.getWireFormatForFieldType( | |
418 | + extension.descriptor.getLiteType(), | |
419 | + false /* isPacked */)) { | |
420 | + packed = false; // Normal, unpacked value. | |
421 | + } else if (extension.descriptor.isRepeated && | |
422 | + extension.descriptor.type.isPackable() && | |
423 | + wireType == FieldSet.getWireFormatForFieldType( | |
424 | + extension.descriptor.getLiteType(), | |
425 | + true /* isPacked */)) { | |
426 | + packed = true; // Packed value. | |
427 | + } else { | |
428 | + unknown = true; // Wrong wire type. | |
429 | + } | |
430 | + | |
431 | + if (unknown) { // Unknown field or wrong wire type. Skip. | |
432 | + return input.skipField(tag); | |
433 | + } | |
434 | + | |
435 | + if (packed) { | |
436 | + final int length = input.readRawVarint32(); | |
437 | + final int limit = input.pushLimit(length); | |
438 | + if (extension.descriptor.getLiteType() == WireFormat.FieldType.ENUM) { | |
439 | + while (input.getBytesUntilLimit() > 0) { | |
440 | + final int rawValue = input.readEnum(); | |
441 | + final Object value = | |
442 | + extension.descriptor.getEnumType().findValueByNumber(rawValue); | |
443 | + if (value == null) { | |
444 | + // If the number isn't recognized as a valid value for this | |
445 | + // enum, drop it (don't even add it to unknownFields). | |
446 | + return true; | |
447 | + } | |
448 | + ensureExtensionsIsMutable(); | |
449 | + extensions.addRepeatedField(extension.descriptor, value); | |
450 | + } | |
451 | + } else { | |
452 | + while (input.getBytesUntilLimit() > 0) { | |
453 | + final Object value = | |
454 | + FieldSet.readPrimitiveField(input, | |
455 | + extension.descriptor.getLiteType()); | |
456 | + ensureExtensionsIsMutable(); | |
457 | + extensions.addRepeatedField(extension.descriptor, value); | |
458 | + } | |
459 | + } | |
460 | + input.popLimit(limit); | |
461 | + } else { | |
462 | + final Object value; | |
463 | + switch (extension.descriptor.getLiteJavaType()) { | |
464 | + case MESSAGE: { | |
465 | + MessageLite.Builder subBuilder = null; | |
466 | + if (!extension.descriptor.isRepeated()) { | |
467 | + MessageLite existingValue = | |
468 | + (MessageLite) extensions.getField(extension.descriptor); | |
469 | + if (existingValue != null) { | |
470 | + subBuilder = existingValue.toBuilder(); | |
471 | + } | |
472 | + } | |
473 | + if (subBuilder == null) { | |
474 | + subBuilder = extension.messageDefaultInstance.newBuilderForType(); | |
475 | + } | |
476 | + if (extension.descriptor.getLiteType() == | |
477 | + WireFormat.FieldType.GROUP) { | |
478 | + input.readGroup(extension.getNumber(), | |
479 | + subBuilder, extensionRegistry); | |
480 | + } else { | |
481 | + input.readMessage(subBuilder, extensionRegistry); | |
482 | + } | |
483 | + value = subBuilder.build(); | |
484 | + break; | |
485 | + } | |
486 | + case ENUM: | |
487 | + final int rawValue = input.readEnum(); | |
488 | + value = extension.descriptor.getEnumType() | |
489 | + .findValueByNumber(rawValue); | |
490 | + // If the number isn't recognized as a valid value for this enum, | |
491 | + // drop it. | |
492 | + if (value == null) { | |
493 | + return true; | |
494 | + } | |
495 | + break; | |
496 | + default: | |
497 | + value = FieldSet.readPrimitiveField(input, | |
498 | + extension.descriptor.getLiteType()); | |
499 | + break; | |
500 | + } | |
501 | + | |
502 | + if (extension.descriptor.isRepeated()) { | |
503 | + ensureExtensionsIsMutable(); | |
504 | + extensions.addRepeatedField(extension.descriptor, value); | |
505 | + } else { | |
506 | + ensureExtensionsIsMutable(); | |
507 | + extensions.setField(extension.descriptor, value); | |
508 | + } | |
509 | + } | |
510 | + | |
511 | + return true; | |
512 | + } | |
513 | + | |
514 | + protected final void mergeExtensionFields(final MessageType other) { | |
515 | + ensureExtensionsIsMutable(); | |
516 | + extensions.mergeFrom(((ExtendableMessage) other).extensions); | |
517 | + } | |
518 | + } | |
519 | + | |
520 | + // ----------------------------------------------------------------- | |
521 | + | |
522 | + /** For use by generated code only. */ | |
523 | + public static <ContainingType extends MessageLite, Type> | |
524 | + GeneratedExtension<ContainingType, Type> | |
525 | + newSingularGeneratedExtension( | |
526 | + final ContainingType containingTypeDefaultInstance, | |
527 | + final Type defaultValue, | |
528 | + final MessageLite messageDefaultInstance, | |
529 | + final Internal.EnumLiteMap<?> enumTypeMap, | |
530 | + final int number, | |
531 | + final WireFormat.FieldType type) { | |
532 | + return new GeneratedExtension<ContainingType, Type>( | |
533 | + containingTypeDefaultInstance, | |
534 | + defaultValue, | |
535 | + messageDefaultInstance, | |
536 | + new ExtensionDescriptor(enumTypeMap, number, type, | |
537 | + false /* isRepeated */, | |
538 | + false /* isPacked */)); | |
539 | + } | |
540 | + | |
541 | + /** For use by generated code only. */ | |
542 | + public static <ContainingType extends MessageLite, Type> | |
543 | + GeneratedExtension<ContainingType, Type> | |
544 | + newRepeatedGeneratedExtension( | |
545 | + final ContainingType containingTypeDefaultInstance, | |
546 | + final MessageLite messageDefaultInstance, | |
547 | + final Internal.EnumLiteMap<?> enumTypeMap, | |
548 | + final int number, | |
549 | + final WireFormat.FieldType type, | |
550 | + final boolean isPacked) { | |
551 | + @SuppressWarnings("unchecked") // Subclasses ensure Type is a List | |
552 | + Type emptyList = (Type) Collections.emptyList(); | |
553 | + return new GeneratedExtension<ContainingType, Type>( | |
554 | + containingTypeDefaultInstance, | |
555 | + emptyList, | |
556 | + messageDefaultInstance, | |
557 | + new ExtensionDescriptor( | |
558 | + enumTypeMap, number, type, true /* isRepeated */, isPacked)); | |
559 | + } | |
560 | + | |
561 | + private static final class ExtensionDescriptor | |
562 | + implements FieldSet.FieldDescriptorLite< | |
563 | + ExtensionDescriptor> { | |
564 | + private ExtensionDescriptor( | |
565 | + final Internal.EnumLiteMap<?> enumTypeMap, | |
566 | + final int number, | |
567 | + final WireFormat.FieldType type, | |
568 | + final boolean isRepeated, | |
569 | + final boolean isPacked) { | |
570 | + this.enumTypeMap = enumTypeMap; | |
571 | + this.number = number; | |
572 | + this.type = type; | |
573 | + this.isRepeated = isRepeated; | |
574 | + this.isPacked = isPacked; | |
575 | + } | |
576 | + | |
577 | + private final Internal.EnumLiteMap<?> enumTypeMap; | |
578 | + private final int number; | |
579 | + private final WireFormat.FieldType type; | |
580 | + private final boolean isRepeated; | |
581 | + private final boolean isPacked; | |
582 | + | |
583 | + public int getNumber() { | |
584 | + return number; | |
585 | + } | |
586 | + | |
587 | + public WireFormat.FieldType getLiteType() { | |
588 | + return type; | |
589 | + } | |
590 | + | |
591 | + public WireFormat.JavaType getLiteJavaType() { | |
592 | + return type.getJavaType(); | |
593 | + } | |
594 | + | |
595 | + public boolean isRepeated() { | |
596 | + return isRepeated; | |
597 | + } | |
598 | + | |
599 | + public boolean isPacked() { | |
600 | + return isPacked; | |
601 | + } | |
602 | + | |
603 | + public Internal.EnumLiteMap<?> getEnumType() { | |
604 | + return enumTypeMap; | |
605 | + } | |
606 | + | |
607 | + @SuppressWarnings("unchecked") | |
608 | + public MessageLite.Builder internalMergeFrom( | |
609 | + MessageLite.Builder to, MessageLite from) { | |
610 | + return ((Builder) to).mergeFrom((GeneratedMessageLite) from); | |
611 | + } | |
612 | + | |
613 | + public int compareTo(ExtensionDescriptor other) { | |
614 | + return number - other.number; | |
615 | + } | |
616 | + } | |
617 | + | |
618 | + /** | |
619 | + * Lite equivalent to {@link GeneratedMessage.GeneratedExtension}. | |
620 | + * | |
621 | + * Users should ignore the contents of this class and only use objects of | |
622 | + * this type as parameters to extension accessors and ExtensionRegistry.add(). | |
623 | + */ | |
624 | + public static final class GeneratedExtension< | |
625 | + ContainingType extends MessageLite, Type> { | |
626 | + | |
627 | + private GeneratedExtension( | |
628 | + final ContainingType containingTypeDefaultInstance, | |
629 | + final Type defaultValue, | |
630 | + final MessageLite messageDefaultInstance, | |
631 | + final ExtensionDescriptor descriptor) { | |
632 | + // Defensive checks to verify the correct initialization order of | |
633 | + // GeneratedExtensions and their related GeneratedMessages. | |
634 | + if (containingTypeDefaultInstance == null) { | |
635 | + throw new IllegalArgumentException( | |
636 | + "Null containingTypeDefaultInstance"); | |
637 | + } | |
638 | + if (descriptor.getLiteType() == WireFormat.FieldType.MESSAGE && | |
639 | + messageDefaultInstance == null) { | |
640 | + throw new IllegalArgumentException( | |
641 | + "Null messageDefaultInstance"); | |
642 | + } | |
643 | + this.containingTypeDefaultInstance = containingTypeDefaultInstance; | |
644 | + this.defaultValue = defaultValue; | |
645 | + this.messageDefaultInstance = messageDefaultInstance; | |
646 | + this.descriptor = descriptor; | |
647 | + } | |
648 | + | |
649 | + private final ContainingType containingTypeDefaultInstance; | |
650 | + private final Type defaultValue; | |
651 | + private final MessageLite messageDefaultInstance; | |
652 | + private final ExtensionDescriptor descriptor; | |
653 | + | |
654 | + /** | |
655 | + * Default instance of the type being extended, used to identify that type. | |
656 | + */ | |
657 | + public ContainingType getContainingTypeDefaultInstance() { | |
658 | + return containingTypeDefaultInstance; | |
659 | + } | |
660 | + | |
661 | + /** Get the field number. */ | |
662 | + public int getNumber() { | |
663 | + return descriptor.getNumber(); | |
664 | + } | |
665 | + | |
666 | + /** | |
667 | + * If the extension is an embedded message, this is the default instance of | |
668 | + * that type. | |
669 | + */ | |
670 | + public MessageLite getMessageDefaultInstance() { | |
671 | + return messageDefaultInstance; | |
672 | + } | |
673 | + } | |
674 | + | |
675 | + /** | |
676 | + * A serialized (serializable) form of the generated message. Stores the | |
677 | + * message as a class name and a byte array. | |
678 | + */ | |
679 | + static final class SerializedForm implements Serializable { | |
680 | + private static final long serialVersionUID = 0L; | |
681 | + | |
682 | + private String messageClassName; | |
683 | + private byte[] asBytes; | |
684 | + | |
685 | + /** | |
686 | + * Creates the serialized form by calling {@link com.google.protobuf.MessageLite#toByteArray}. | |
687 | + * @param regularForm the message to serialize | |
688 | + */ | |
689 | + SerializedForm(MessageLite regularForm) { | |
690 | + messageClassName = regularForm.getClass().getName(); | |
691 | + asBytes = regularForm.toByteArray(); | |
692 | + } | |
693 | + | |
694 | + /** | |
695 | + * When read from an ObjectInputStream, this method converts this object | |
696 | + * back to the regular form. Part of Java's serialization magic. | |
697 | + * @return a GeneratedMessage of the type that was serialized | |
698 | + */ | |
699 | + @SuppressWarnings("unchecked") | |
700 | + protected Object readResolve() throws ObjectStreamException { | |
701 | + try { | |
702 | + Class messageClass = Class.forName(messageClassName); | |
703 | + Method newBuilder = messageClass.getMethod("newBuilder"); | |
704 | + MessageLite.Builder builder = | |
705 | + (MessageLite.Builder) newBuilder.invoke(null); | |
706 | + builder.mergeFrom(asBytes); | |
707 | + return builder.buildPartial(); | |
708 | + } catch (ClassNotFoundException e) { | |
709 | + throw new RuntimeException("Unable to find proto buffer class", e); | |
710 | + } catch (NoSuchMethodException e) { | |
711 | + throw new RuntimeException("Unable to find newBuilder method", e); | |
712 | + } catch (IllegalAccessException e) { | |
713 | + throw new RuntimeException("Unable to call newBuilder method", e); | |
714 | + } catch (InvocationTargetException e) { | |
715 | + throw new RuntimeException("Error calling newBuilder", e.getCause()); | |
716 | + } catch (InvalidProtocolBufferException e) { | |
717 | + throw new RuntimeException("Unable to understand proto buffer", e); | |
718 | + } | |
719 | + } | |
720 | + } | |
721 | + | |
722 | + /** | |
723 | + * Replaces this object in the output stream with a serialized form. | |
724 | + * Part of Java's serialization magic. Generated sub-classes must override | |
725 | + * this method by calling <code>return super.writeReplace();</code> | |
726 | + * @return a SerializedForm of this message | |
727 | + */ | |
728 | + protected Object writeReplace() throws ObjectStreamException { | |
729 | + return new SerializedForm(this); | |
730 | + } | |
731 | +} |
@@ -0,0 +1,206 @@ | ||
1 | +// Protocol Buffers - Google's data interchange format | |
2 | +// Copyright 2008 Google Inc. All rights reserved. | |
3 | +// http://code.google.com/p/protobuf/ | |
4 | +// | |
5 | +// Redistribution and use in source and binary forms, with or without | |
6 | +// modification, are permitted provided that the following conditions are | |
7 | +// met: | |
8 | +// | |
9 | +// * Redistributions of source code must retain the above copyright | |
10 | +// notice, this list of conditions and the following disclaimer. | |
11 | +// * Redistributions in binary form must reproduce the above | |
12 | +// copyright notice, this list of conditions and the following disclaimer | |
13 | +// in the documentation and/or other materials provided with the | |
14 | +// distribution. | |
15 | +// * Neither the name of Google Inc. nor the names of its | |
16 | +// contributors may be used to endorse or promote products derived from | |
17 | +// this software without specific prior written permission. | |
18 | +// | |
19 | +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
20 | +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
21 | +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
22 | +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
23 | +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
24 | +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
25 | +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
26 | +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
27 | +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
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. | |
30 | + | |
31 | +package com.google.protobuf; | |
32 | + | |
33 | +import java.io.UnsupportedEncodingException; | |
34 | + | |
35 | +/** | |
36 | + * The classes contained within are used internally by the Protocol Buffer | |
37 | + * library and generated message implementations. They are public only because | |
38 | + * those generated messages do not reside in the {@code protobuf} package. | |
39 | + * Others should not use this class directly. | |
40 | + * | |
41 | + * @author kenton@google.com (Kenton Varda) | |
42 | + */ | |
43 | +public class Internal { | |
44 | + /** | |
45 | + * Helper called by generated code to construct default values for string | |
46 | + * fields. | |
47 | + * <p> | |
48 | + * The protocol compiler does not actually contain a UTF-8 decoder -- it | |
49 | + * just pushes UTF-8-encoded text around without touching it. The one place | |
50 | + * where this presents a problem is when generating Java string literals. | |
51 | + * Unicode characters in the string literal would normally need to be encoded | |
52 | + * using a Unicode escape sequence, which would require decoding them. | |
53 | + * To get around this, protoc instead embeds the UTF-8 bytes into the | |
54 | + * generated code and leaves it to the runtime library to decode them. | |
55 | + * <p> | |
56 | + * It gets worse, though. If protoc just generated a byte array, like: | |
57 | + * new byte[] {0x12, 0x34, 0x56, 0x78} | |
58 | + * Java actually generates *code* which allocates an array and then fills | |
59 | + * in each value. This is much less efficient than just embedding the bytes | |
60 | + * directly into the bytecode. To get around this, we need another | |
61 | + * work-around. String literals are embedded directly, so protoc actually | |
62 | + * generates a string literal corresponding to the bytes. The easiest way | |
63 | + * to do this is to use the ISO-8859-1 character set, which corresponds to | |
64 | + * the first 256 characters of the Unicode range. Protoc can then use | |
65 | + * good old CEscape to generate the string. | |
66 | + * <p> | |
67 | + * So we have a string literal which represents a set of bytes which | |
68 | + * represents another string. This function -- stringDefaultValue -- | |
69 | + * converts from the generated string to the string we actually want. The | |
70 | + * generated code calls this automatically. | |
71 | + */ | |
72 | + public static String stringDefaultValue(String bytes) { | |
73 | + try { | |
74 | + return new String(bytes.getBytes("ISO-8859-1"), "UTF-8"); | |
75 | + } catch (UnsupportedEncodingException e) { | |
76 | + // This should never happen since all JVMs are required to implement | |
77 | + // both of the above character sets. | |
78 | + throw new IllegalStateException( | |
79 | + "Java VM does not support a standard character set.", e); | |
80 | + } | |
81 | + } | |
82 | + | |
83 | + /** | |
84 | + * Helper called by generated code to construct default values for bytes | |
85 | + * fields. | |
86 | + * <p> | |
87 | + * This is a lot like {@link #stringDefaultValue}, but for bytes fields. | |
88 | + * In this case we only need the second of the two hacks -- allowing us to | |
89 | + * embed raw bytes as a string literal with ISO-8859-1 encoding. | |
90 | + */ | |
91 | + public static ByteString bytesDefaultValue(String bytes) { | |
92 | + try { | |
93 | + return ByteString.copyFrom(bytes.getBytes("ISO-8859-1")); | |
94 | + } catch (UnsupportedEncodingException e) { | |
95 | + // This should never happen since all JVMs are required to implement | |
96 | + // ISO-8859-1. | |
97 | + throw new IllegalStateException( | |
98 | + "Java VM does not support a standard character set.", e); | |
99 | + } | |
100 | + } | |
101 | + | |
102 | + /** | |
103 | + * Helper called by generated code to determine if a byte array is a valid | |
104 | + * UTF-8 encoded string such that the original bytes can be converted to | |
105 | + * a String object and then back to a byte array round tripping the bytes | |
106 | + * without loss. | |
107 | + * <p> | |
108 | + * This is inspired by UTF_8.java in sun.nio.cs. | |
109 | + * | |
110 | + * @param byteString the string to check | |
111 | + * @return whether the byte array is round trippable | |
112 | + */ | |
113 | + public static boolean isValidUtf8(ByteString byteString) { | |
114 | + int index = 0; | |
115 | + int size = byteString.size(); | |
116 | + // To avoid the masking, we could change this to use bytes; | |
117 | + // Then X > 0xC2 gets turned into X < -0xC2; X < 0x80 | |
118 | + // gets turned into X >= 0, etc. | |
119 | + | |
120 | + while (index < size) { | |
121 | + int byte1 = byteString.byteAt(index++) & 0xFF; | |
122 | + if (byte1 < 0x80) { | |
123 | + // fast loop for single bytes | |
124 | + continue; | |
125 | + | |
126 | + // we know from this point on that we have 2-4 byte forms | |
127 | + } else if (byte1 < 0xC2 || byte1 > 0xF4) { | |
128 | + // catch illegal first bytes: < C2 or > F4 | |
129 | + return false; | |
130 | + } | |
131 | + if (index >= size) { | |
132 | + // fail if we run out of bytes | |
133 | + return false; | |
134 | + } | |
135 | + int byte2 = byteString.byteAt(index++) & 0xFF; | |
136 | + if (byte2 < 0x80 || byte2 > 0xBF) { | |
137 | + // general trail-byte test | |
138 | + return false; | |
139 | + } | |
140 | + if (byte1 <= 0xDF) { | |
141 | + // two-byte form; general trail-byte test is sufficient | |
142 | + continue; | |
143 | + } | |
144 | + | |
145 | + // we know from this point on that we have 3 or 4 byte forms | |
146 | + if (index >= size) { | |
147 | + // fail if we run out of bytes | |
148 | + return false; | |
149 | + } | |
150 | + int byte3 = byteString.byteAt(index++) & 0xFF; | |
151 | + if (byte3 < 0x80 || byte3 > 0xBF) { | |
152 | + // general trail-byte test | |
153 | + return false; | |
154 | + } | |
155 | + if (byte1 <= 0xEF) { | |
156 | + // three-byte form. Vastly more frequent than four-byte forms | |
157 | + // The following has an extra test, but not worth restructuring | |
158 | + if (byte1 == 0xE0 && byte2 < 0xA0 || | |
159 | + byte1 == 0xED && byte2 > 0x9F) { | |
160 | + // check special cases of byte2 | |
161 | + return false; | |
162 | + } | |
163 | + | |
164 | + } else { | |
165 | + // four-byte form | |
166 | + | |
167 | + if (index >= size) { | |
168 | + // fail if we run out of bytes | |
169 | + return false; | |
170 | + } | |
171 | + int byte4 = byteString.byteAt(index++) & 0xFF; | |
172 | + if (byte4 < 0x80 || byte4 > 0xBF) { | |
173 | + // general trail-byte test | |
174 | + return false; | |
175 | + } | |
176 | + // The following has an extra test, but not worth restructuring | |
177 | + if (byte1 == 0xF0 && byte2 < 0x90 || | |
178 | + byte1 == 0xF4 && byte2 > 0x8F) { | |
179 | + // check special cases of byte2 | |
180 | + return false; | |
181 | + } | |
182 | + } | |
183 | + } | |
184 | + return true; | |
185 | + } | |
186 | + | |
187 | + /** | |
188 | + * Interface for an enum value or value descriptor, to be used in FieldSet. | |
189 | + * The lite library stores enum values directly in FieldSets but the full | |
190 | + * library stores EnumValueDescriptors in order to better support reflection. | |
191 | + */ | |
192 | + public interface EnumLite { | |
193 | + int getNumber(); | |
194 | + } | |
195 | + | |
196 | + /** | |
197 | + * Interface for an object which maps integers to {@link EnumLite}s. | |
198 | + * {@link Descriptors.EnumDescriptor} implements this interface by mapping | |
199 | + * numbers to {@link Descriptors.EnumValueDescriptor}s. Additionally, | |
200 | + * every generated enum type has a static method internalGetValueMap() which | |
201 | + * returns an implementation of this type that maps numbers to enum values. | |
202 | + */ | |
203 | + public interface EnumLiteMap<T extends EnumLite> { | |
204 | + T findValueByNumber(int number); | |
205 | + } | |
206 | +} |
@@ -0,0 +1,93 @@ | ||
1 | +// Protocol Buffers - Google's data interchange format | |
2 | +// Copyright 2008 Google Inc. All rights reserved. | |
3 | +// http://code.google.com/p/protobuf/ | |
4 | +// | |
5 | +// Redistribution and use in source and binary forms, with or without | |
6 | +// modification, are permitted provided that the following conditions are | |
7 | +// met: | |
8 | +// | |
9 | +// * Redistributions of source code must retain the above copyright | |
10 | +// notice, this list of conditions and the following disclaimer. | |
11 | +// * Redistributions in binary form must reproduce the above | |
12 | +// copyright notice, this list of conditions and the following disclaimer | |
13 | +// in the documentation and/or other materials provided with the | |
14 | +// distribution. | |
15 | +// * Neither the name of Google Inc. nor the names of its | |
16 | +// contributors may be used to endorse or promote products derived from | |
17 | +// this software without specific prior written permission. | |
18 | +// | |
19 | +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
20 | +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
21 | +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
22 | +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
23 | +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
24 | +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
25 | +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
26 | +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
27 | +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
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. | |
30 | + | |
31 | +package com.google.protobuf; | |
32 | + | |
33 | +import java.io.IOException; | |
34 | + | |
35 | +/** | |
36 | + * Thrown when a protocol message being parsed is invalid in some way, | |
37 | + * e.g. it contains a malformed varint or a negative byte length. | |
38 | + * | |
39 | + * @author kenton@google.com Kenton Varda | |
40 | + */ | |
41 | +public class InvalidProtocolBufferException extends IOException { | |
42 | + private static final long serialVersionUID = -1616151763072450476L; | |
43 | + | |
44 | + public InvalidProtocolBufferException(final String description) { | |
45 | + super(description); | |
46 | + } | |
47 | + | |
48 | + static InvalidProtocolBufferException truncatedMessage() { | |
49 | + return new InvalidProtocolBufferException( | |
50 | + "While parsing a protocol message, the input ended unexpectedly " + | |
51 | + "in the middle of a field. This could mean either than the " + | |
52 | + "input has been truncated or that an embedded message " + | |
53 | + "misreported its own length."); | |
54 | + } | |
55 | + | |
56 | + static InvalidProtocolBufferException negativeSize() { | |
57 | + return new InvalidProtocolBufferException( | |
58 | + "CodedInputStream encountered an embedded string or message " + | |
59 | + "which claimed to have negative size."); | |
60 | + } | |
61 | + | |
62 | + static InvalidProtocolBufferException malformedVarint() { | |
63 | + return new InvalidProtocolBufferException( | |
64 | + "CodedInputStream encountered a malformed varint."); | |
65 | + } | |
66 | + | |
67 | + static InvalidProtocolBufferException invalidTag() { | |
68 | + return new InvalidProtocolBufferException( | |
69 | + "Protocol message contained an invalid tag (zero)."); | |
70 | + } | |
71 | + | |
72 | + static InvalidProtocolBufferException invalidEndTag() { | |
73 | + return new InvalidProtocolBufferException( | |
74 | + "Protocol message end-group tag did not match expected tag."); | |
75 | + } | |
76 | + | |
77 | + static InvalidProtocolBufferException invalidWireType() { | |
78 | + return new InvalidProtocolBufferException( | |
79 | + "Protocol message tag had invalid wire type."); | |
80 | + } | |
81 | + | |
82 | + static InvalidProtocolBufferException recursionLimitExceeded() { | |
83 | + return new InvalidProtocolBufferException( | |
84 | + "Protocol message had too many levels of nesting. May be malicious. " + | |
85 | + "Use CodedInputStream.setRecursionLimit() to increase the depth limit."); | |
86 | + } | |
87 | + | |
88 | + static InvalidProtocolBufferException sizeLimitExceeded() { | |
89 | + return new InvalidProtocolBufferException( | |
90 | + "Protocol message was too large. May be malicious. " + | |
91 | + "Use CodedInputStream.setSizeLimit() to increase the size limit."); | |
92 | + } | |
93 | +} |
@@ -0,0 +1,155 @@ | ||
1 | +// Protocol Buffers - Google's data interchange format | |
2 | +// Copyright 2008 Google Inc. All rights reserved. | |
3 | +// http://code.google.com/p/protobuf/ | |
4 | +// | |
5 | +// Redistribution and use in source and binary forms, with or without | |
6 | +// modification, are permitted provided that the following conditions are | |
7 | +// met: | |
8 | +// | |
9 | +// * Redistributions of source code must retain the above copyright | |
10 | +// notice, this list of conditions and the following disclaimer. | |
11 | +// * Redistributions in binary form must reproduce the above | |
12 | +// copyright notice, this list of conditions and the following disclaimer | |
13 | +// in the documentation and/or other materials provided with the | |
14 | +// distribution. | |
15 | +// * Neither the name of Google Inc. nor the names of its | |
16 | +// contributors may be used to endorse or promote products derived from | |
17 | +// this software without specific prior written permission. | |
18 | +// | |
19 | +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
20 | +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
21 | +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
22 | +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
23 | +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
24 | +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
25 | +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
26 | +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
27 | +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
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. | |
30 | + | |
31 | +package com.google.protobuf; | |
32 | + | |
33 | +import java.util.List; | |
34 | +import java.util.AbstractList; | |
35 | +import java.util.ArrayList; | |
36 | +import java.util.RandomAccess; | |
37 | +import java.util.Collection; | |
38 | + | |
39 | +/** | |
40 | + * An implementation of {@link LazyStringList} that wraps an ArrayList. Each | |
41 | + * element is either a ByteString or a String. It caches the last one requested | |
42 | + * which is most likely the one needed next. This minimizes memory usage while | |
43 | + * satisfying the most common use cases. | |
44 | + * <p> | |
45 | + * <strong>Note that this implementation is not synchronized.</strong> | |
46 | + * If multiple threads access an <tt>ArrayList</tt> instance concurrently, | |
47 | + * and at least one of the threads modifies the list structurally, it | |
48 | + * <i>must</i> be synchronized externally. (A structural modification is | |
49 | + * any operation that adds or deletes one or more elements, or explicitly | |
50 | + * resizes the backing array; merely setting the value of an element is not | |
51 | + * a structural modification.) This is typically accomplished by | |
52 | + * synchronizing on some object that naturally encapsulates the list. | |
53 | + * <p> | |
54 | + * If the implementation is accessed via concurrent reads, this is thread safe. | |
55 | + * Conversions are done in a thread safe manner. It's possible that the | |
56 | + * conversion may happen more than once if two threads attempt to access the | |
57 | + * same element and the modifications were not visible to each other, but this | |
58 | + * will not result in any corruption of the list or change in behavior other | |
59 | + * than performance. | |
60 | + * | |
61 | + * @author jonp@google.com (Jon Perlow) | |
62 | + */ | |
63 | +public class LazyStringArrayList extends AbstractList<String> | |
64 | + implements LazyStringList, RandomAccess { | |
65 | + | |
66 | + public final static LazyStringList EMPTY = new UnmodifiableLazyStringList( | |
67 | + new LazyStringArrayList()); | |
68 | + | |
69 | + private final List<Object> list; | |
70 | + | |
71 | + public LazyStringArrayList() { | |
72 | + list = new ArrayList<Object>(); | |
73 | + } | |
74 | + | |
75 | + public LazyStringArrayList(List<String> from) { | |
76 | + list = new ArrayList<Object>(from); | |
77 | + } | |
78 | + | |
79 | + @Override | |
80 | + public String get(int index) { | |
81 | + Object o = list.get(index); | |
82 | + if (o instanceof String) { | |
83 | + return (String) o; | |
84 | + } else { | |
85 | + ByteString bs = (ByteString) o; | |
86 | + String s = bs.toStringUtf8(); | |
87 | + if (Internal.isValidUtf8(bs)) { | |
88 | + list.set(index, s); | |
89 | + } | |
90 | + return s; | |
91 | + } | |
92 | + } | |
93 | + | |
94 | + @Override | |
95 | + public int size() { | |
96 | + return list.size(); | |
97 | + } | |
98 | + | |
99 | + @Override | |
100 | + public String set(int index, String s) { | |
101 | + Object o = list.set(index, s); | |
102 | + return asString(o); | |
103 | + } | |
104 | + | |
105 | + @Override | |
106 | + public void add(int index, String element) { | |
107 | + list.add(index, element); | |
108 | + modCount++; | |
109 | + } | |
110 | + | |
111 | + @Override | |
112 | + public boolean addAll(int index, Collection<? extends String> c) { | |
113 | + boolean ret = list.addAll(index, c); | |
114 | + modCount++; | |
115 | + return ret; | |
116 | + } | |
117 | + | |
118 | + @Override | |
119 | + public String remove(int index) { | |
120 | + Object o = list.remove(index); | |
121 | + modCount++; | |
122 | + return asString(o); | |
123 | + } | |
124 | + | |
125 | + public void clear() { | |
126 | + list.clear(); | |
127 | + modCount++; | |
128 | + } | |
129 | + | |
130 | + // @Override | |
131 | + public void add(ByteString element) { | |
132 | + list.add(element); | |
133 | + modCount++; | |
134 | + } | |
135 | + | |
136 | + // @Override | |
137 | + public ByteString getByteString(int index) { | |
138 | + Object o = list.get(index); | |
139 | + if (o instanceof String) { | |
140 | + ByteString b = ByteString.copyFromUtf8((String) o); | |
141 | + list.set(index, b); | |
142 | + return b; | |
143 | + } else { | |
144 | + return (ByteString) o; | |
145 | + } | |
146 | + } | |
147 | + | |
148 | + private String asString(Object o) { | |
149 | + if (o instanceof String) { | |
150 | + return (String) o; | |
151 | + } else { | |
152 | + return ((ByteString) o).toStringUtf8(); | |
153 | + } | |
154 | + } | |
155 | +} |
@@ -0,0 +1,72 @@ | ||
1 | +// Protocol Buffers - Google's data interchange format | |
2 | +// Copyright 2008 Google Inc. All rights reserved. | |
3 | +// http://code.google.com/p/protobuf/ | |
4 | +// | |
5 | +// Redistribution and use in source and binary forms, with or without | |
6 | +// modification, are permitted provided that the following conditions are | |
7 | +// met: | |
8 | +// | |
9 | +// * Redistributions of source code must retain the above copyright | |
10 | +// notice, this list of conditions and the following disclaimer. | |
11 | +// * Redistributions in binary form must reproduce the above | |
12 | +// copyright notice, this list of conditions and the following disclaimer | |
13 | +// in the documentation and/or other materials provided with the | |
14 | +// distribution. | |
15 | +// * Neither the name of Google Inc. nor the names of its | |
16 | +// contributors may be used to endorse or promote products derived from | |
17 | +// this software without specific prior written permission. | |
18 | +// | |
19 | +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
20 | +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
21 | +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
22 | +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
23 | +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
24 | +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
25 | +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
26 | +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
27 | +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
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. | |
30 | + | |
31 | +package com.google.protobuf; | |
32 | + | |
33 | +import java.util.List; | |
34 | + | |
35 | +/** | |
36 | + * An interface extending List<String> that also provides access to the | |
37 | + * items of the list as UTF8-encoded ByteString objects. This is used by the | |
38 | + * protocol buffer implementation to support lazily converting bytes parsed | |
39 | + * over the wire to String objects until needed and also increases the | |
40 | + * efficiency of serialization if the String was never requested as the | |
41 | + * ByteString is already cached. | |
42 | + * <p> | |
43 | + * This only adds additional methods that are required for the use in the | |
44 | + * protocol buffer code in order to be able successfuly round trip byte arrays | |
45 | + * through parsing and serialization without conversion to strings. It's not | |
46 | + * attempting to support the functionality of say List<ByteString>, hence | |
47 | + * why only these two very specific methods are added. | |
48 | + * | |
49 | + * @author jonp@google.com (Jon Perlow) | |
50 | + */ | |
51 | +public interface LazyStringList extends List<String> { | |
52 | + | |
53 | + /** | |
54 | + * Returns the element at the specified position in this list as a ByteString. | |
55 | + * | |
56 | + * @param index index of the element to return | |
57 | + * @return the element at the specified position in this list | |
58 | + * @throws IndexOutOfBoundsException if the index is out of range | |
59 | + * (<tt>index < 0 || index >= size()</tt>) | |
60 | + */ | |
61 | + ByteString getByteString(int index); | |
62 | + | |
63 | + /** | |
64 | + * Appends the specified element to the end of this list (optional | |
65 | + * operation). | |
66 | + * | |
67 | + * @param element element to be appended to this list | |
68 | + * @throws UnsupportedOperationException if the <tt>add</tt> operation | |
69 | + * is not supported by this list | |
70 | + */ | |
71 | + void add(ByteString element); | |
72 | +} |
@@ -0,0 +1,215 @@ | ||
1 | +// Protocol Buffers - Google's data interchange format | |
2 | +// Copyright 2008 Google Inc. All rights reserved. | |
3 | +// http://code.google.com/p/protobuf/ | |
4 | +// | |
5 | +// Redistribution and use in source and binary forms, with or without | |
6 | +// modification, are permitted provided that the following conditions are | |
7 | +// met: | |
8 | +// | |
9 | +// * Redistributions of source code must retain the above copyright | |
10 | +// notice, this list of conditions and the following disclaimer. | |
11 | +// * Redistributions in binary form must reproduce the above | |
12 | +// copyright notice, this list of conditions and the following disclaimer | |
13 | +// in the documentation and/or other materials provided with the | |
14 | +// distribution. | |
15 | +// * Neither the name of Google Inc. nor the names of its | |
16 | +// contributors may be used to endorse or promote products derived from | |
17 | +// this software without specific prior written permission. | |
18 | +// | |
19 | +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
20 | +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
21 | +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
22 | +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
23 | +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
24 | +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
25 | +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
26 | +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
27 | +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
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. | |
30 | + | |
31 | +// TODO(kenton): Use generics? E.g. Builder<BuilderType extends Builder>, then | |
32 | +// mergeFrom*() could return BuilderType for better type-safety. | |
33 | + | |
34 | +package com.google.protobuf; | |
35 | + | |
36 | +import java.io.IOException; | |
37 | +import java.io.InputStream; | |
38 | +import java.util.Map; | |
39 | + | |
40 | +/** | |
41 | + * Abstract interface implemented by Protocol Message objects. | |
42 | + * <p> | |
43 | + * See also {@link MessageLite}, which defines most of the methods that typical | |
44 | + * users care about. {@link Message} adds to it methods that are not available | |
45 | + * in the "lite" runtime. The biggest added features are introspection and | |
46 | + * reflection -- i.e., getting descriptors for the message type and accessing | |
47 | + * the field values dynamically. | |
48 | + * | |
49 | + * @author kenton@google.com Kenton Varda | |
50 | + */ | |
51 | +public interface Message extends MessageLite, MessageOrBuilder { | |
52 | + | |
53 | + // ----------------------------------------------------------------- | |
54 | + // Comparison and hashing | |
55 | + | |
56 | + /** | |
57 | + * Compares the specified object with this message for equality. Returns | |
58 | + * <tt>true</tt> if the given object is a message of the same type (as | |
59 | + * defined by {@code getDescriptorForType()}) and has identical values for | |
60 | + * all of its fields. Subclasses must implement this; inheriting | |
61 | + * {@code Object.equals()} is incorrect. | |
62 | + * | |
63 | + * @param other object to be compared for equality with this message | |
64 | + * @return <tt>true</tt> if the specified object is equal to this message | |
65 | + */ | |
66 | + @Override | |
67 | + boolean equals(Object other); | |
68 | + | |
69 | + /** | |
70 | + * Returns the hash code value for this message. The hash code of a message | |
71 | + * should mix the message's type (object identity of the decsriptor) with its | |
72 | + * contents (known and unknown field values). Subclasses must implement this; | |
73 | + * inheriting {@code Object.hashCode()} is incorrect. | |
74 | + * | |
75 | + * @return the hash code value for this message | |
76 | + * @see Map#hashCode() | |
77 | + */ | |
78 | + @Override | |
79 | + int hashCode(); | |
80 | + | |
81 | + // ----------------------------------------------------------------- | |
82 | + // Convenience methods. | |
83 | + | |
84 | + /** | |
85 | + * Converts the message to a string in protocol buffer text format. This is | |
86 | + * just a trivial wrapper around {@link TextFormat#printToString(Message)}. | |
87 | + */ | |
88 | + @Override | |
89 | + String toString(); | |
90 | + | |
91 | + // ================================================================= | |
92 | + // Builders | |
93 | + | |
94 | + // (From MessageLite, re-declared here only for return type covariance.) | |
95 | + Builder newBuilderForType(); | |
96 | + Builder toBuilder(); | |
97 | + | |
98 | + /** | |
99 | + * Abstract interface implemented by Protocol Message builders. | |
100 | + */ | |
101 | + interface Builder extends MessageLite.Builder, MessageOrBuilder { | |
102 | + // (From MessageLite.Builder, re-declared here only for return type | |
103 | + // covariance.) | |
104 | + Builder clear(); | |
105 | + | |
106 | + /** | |
107 | + * Merge {@code other} into the message being built. {@code other} must | |
108 | + * have the exact same type as {@code this} (i.e. | |
109 | + * {@code getDescriptorForType() == other.getDescriptorForType()}). | |
110 | + * | |
111 | + * Merging occurs as follows. For each field:<br> | |
112 | + * * For singular primitive fields, if the field is set in {@code other}, | |
113 | + * then {@code other}'s value overwrites the value in this message.<br> | |
114 | + * * For singular message fields, if the field is set in {@code other}, | |
115 | + * it is merged into the corresponding sub-message of this message | |
116 | + * using the same merging rules.<br> | |
117 | + * * For repeated fields, the elements in {@code other} are concatenated | |
118 | + * with the elements in this message. | |
119 | + * | |
120 | + * This is equivalent to the {@code Message::MergeFrom} method in C++. | |
121 | + */ | |
122 | + Builder mergeFrom(Message other); | |
123 | + | |
124 | + // (From MessageLite.Builder, re-declared here only for return type | |
125 | + // covariance.) | |
126 | + Message build(); | |
127 | + Message buildPartial(); | |
128 | + Builder clone(); | |
129 | + Builder mergeFrom(CodedInputStream input) throws IOException; | |
130 | + Builder mergeFrom(CodedInputStream input, | |
131 | + ExtensionRegistryLite extensionRegistry) | |
132 | + throws IOException; | |
133 | + | |
134 | + /** | |
135 | + * Get the message's type's descriptor. | |
136 | + * See {@link Message#getDescriptorForType()}. | |
137 | + */ | |
138 | + Descriptors.Descriptor getDescriptorForType(); | |
139 | + | |
140 | + /** | |
141 | + * Create a Builder for messages of the appropriate type for the given | |
142 | + * field. Messages built with this can then be passed to setField(), | |
143 | + * setRepeatedField(), or addRepeatedField(). | |
144 | + */ | |
145 | + Builder newBuilderForField(Descriptors.FieldDescriptor field); | |
146 | + | |
147 | + /** | |
148 | + * Sets a field to the given value. The value must be of the correct type | |
149 | + * for this field, i.e. the same type that | |
150 | + * {@link Message#getField(Descriptors.FieldDescriptor)} would return. | |
151 | + */ | |
152 | + Builder setField(Descriptors.FieldDescriptor field, Object value); | |
153 | + | |
154 | + /** | |
155 | + * Clears the field. This is exactly equivalent to calling the generated | |
156 | + * "clear" accessor method corresponding to the field. | |
157 | + */ | |
158 | + Builder clearField(Descriptors.FieldDescriptor field); | |
159 | + | |
160 | + /** | |
161 | + * Sets an element of a repeated field to the given value. The value must | |
162 | + * be of the correct type for this field, i.e. the same type that | |
163 | + * {@link Message#getRepeatedField(Descriptors.FieldDescriptor,int)} would | |
164 | + * return. | |
165 | + * @throws IllegalArgumentException The field is not a repeated field, or | |
166 | + * {@code field.getContainingType() != getDescriptorForType()}. | |
167 | + */ | |
168 | + Builder setRepeatedField(Descriptors.FieldDescriptor field, | |
169 | + int index, Object value); | |
170 | + | |
171 | + /** | |
172 | + * Like {@code setRepeatedField}, but appends the value as a new element. | |
173 | + * @throws IllegalArgumentException The field is not a repeated field, or | |
174 | + * {@code field.getContainingType() != getDescriptorForType()}. | |
175 | + */ | |
176 | + Builder addRepeatedField(Descriptors.FieldDescriptor field, Object value); | |
177 | + | |
178 | + /** Set the {@link UnknownFieldSet} for this message. */ | |
179 | + Builder setUnknownFields(UnknownFieldSet unknownFields); | |
180 | + | |
181 | + /** | |
182 | + * Merge some unknown fields into the {@link UnknownFieldSet} for this | |
183 | + * message. | |
184 | + */ | |
185 | + Builder mergeUnknownFields(UnknownFieldSet unknownFields); | |
186 | + | |
187 | + // --------------------------------------------------------------- | |
188 | + // Convenience methods. | |
189 | + | |
190 | + // (From MessageLite.Builder, re-declared here only for return type | |
191 | + // covariance.) | |
192 | + Builder mergeFrom(ByteString data) throws InvalidProtocolBufferException; | |
193 | + Builder mergeFrom(ByteString data, | |
194 | + ExtensionRegistryLite extensionRegistry) | |
195 | + throws InvalidProtocolBufferException; | |
196 | + Builder mergeFrom(byte[] data) throws InvalidProtocolBufferException; | |
197 | + Builder mergeFrom(byte[] data, int off, int len) | |
198 | + throws InvalidProtocolBufferException; | |
199 | + Builder mergeFrom(byte[] data, | |
200 | + ExtensionRegistryLite extensionRegistry) | |
201 | + throws InvalidProtocolBufferException; | |
202 | + Builder mergeFrom(byte[] data, int off, int len, | |
203 | + ExtensionRegistryLite extensionRegistry) | |
204 | + throws InvalidProtocolBufferException; | |
205 | + Builder mergeFrom(InputStream input) throws IOException; | |
206 | + Builder mergeFrom(InputStream input, | |
207 | + ExtensionRegistryLite extensionRegistry) | |
208 | + throws IOException; | |
209 | + boolean mergeDelimitedFrom(InputStream input) | |
210 | + throws IOException; | |
211 | + boolean mergeDelimitedFrom(InputStream input, | |
212 | + ExtensionRegistryLite extensionRegistry) | |
213 | + throws IOException; | |
214 | + } | |
215 | +} |
@@ -0,0 +1,325 @@ | ||
1 | +// Protocol Buffers - Google's data interchange format | |
2 | +// Copyright 2008 Google Inc. All rights reserved. | |
3 | +// http://code.google.com/p/protobuf/ | |
4 | +// | |
5 | +// Redistribution and use in source and binary forms, with or without | |
6 | +// modification, are permitted provided that the following conditions are | |
7 | +// met: | |
8 | +// | |
9 | +// * Redistributions of source code must retain the above copyright | |
10 | +// notice, this list of conditions and the following disclaimer. | |
11 | +// * Redistributions in binary form must reproduce the above | |
12 | +// copyright notice, this list of conditions and the following disclaimer | |
13 | +// in the documentation and/or other materials provided with the | |
14 | +// distribution. | |
15 | +// * Neither the name of Google Inc. nor the names of its | |
16 | +// contributors may be used to endorse or promote products derived from | |
17 | +// this software without specific prior written permission. | |
18 | +// | |
19 | +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
20 | +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
21 | +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
22 | +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
23 | +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
24 | +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
25 | +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
26 | +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
27 | +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
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. | |
30 | + | |
31 | +// TODO(kenton): Use generics? E.g. Builder<BuilderType extends Builder>, then | |
32 | +// mergeFrom*() could return BuilderType for better type-safety. | |
33 | + | |
34 | +package com.google.protobuf; | |
35 | + | |
36 | +import java.io.IOException; | |
37 | +import java.io.InputStream; | |
38 | +import java.io.OutputStream; | |
39 | + | |
40 | +/** | |
41 | + * Abstract interface implemented by Protocol Message objects. | |
42 | + * | |
43 | + * <p>This interface is implemented by all protocol message objects. Non-lite | |
44 | + * messages additionally implement the Message interface, which is a subclass | |
45 | + * of MessageLite. Use MessageLite instead when you only need the subset of | |
46 | + * features which it supports -- namely, nothing that uses descriptors or | |
47 | + * reflection. You can instruct the protocol compiler to generate classes | |
48 | + * which implement only MessageLite, not the full Message interface, by adding | |
49 | + * the follow line to the .proto file: | |
50 | + * <pre> | |
51 | + * option optimize_for = LITE_RUNTIME; | |
52 | + * </pre> | |
53 | + * | |
54 | + * <p>This is particularly useful on resource-constrained systems where the | |
55 | + * full protocol buffers runtime library is too big. | |
56 | + * | |
57 | + * <p>Note that on non-constrained systems (e.g. servers) when you need to link | |
58 | + * in lots of protocol definitions, a better way to reduce total code footprint | |
59 | + * is to use {@code optimize_for = CODE_SIZE}. This will make the generated | |
60 | + * code smaller while still supporting all the same features (at the expense of | |
61 | + * speed). {@code optimize_for = LITE_RUNTIME} is best when you only have a | |
62 | + * small number of message types linked into your binary, in which case the | |
63 | + * size of the protocol buffers runtime itself is the biggest problem. | |
64 | + * | |
65 | + * @author kenton@google.com Kenton Varda | |
66 | + */ | |
67 | +public interface MessageLite extends MessageLiteOrBuilder { | |
68 | + | |
69 | + | |
70 | + /** | |
71 | + * Serializes the message and writes it to {@code output}. This does not | |
72 | + * flush or close the stream. | |
73 | + */ | |
74 | + void writeTo(CodedOutputStream output) throws IOException; | |
75 | + | |
76 | + /** | |
77 | + * Get the number of bytes required to encode this message. The result | |
78 | + * is only computed on the first call and memoized after that. | |
79 | + */ | |
80 | + int getSerializedSize(); | |
81 | + | |
82 | + // ----------------------------------------------------------------- | |
83 | + // Convenience methods. | |
84 | + | |
85 | + /** | |
86 | + * Serializes the message to a {@code ByteString} and returns it. This is | |
87 | + * just a trivial wrapper around | |
88 | + * {@link #writeTo(CodedOutputStream)}. | |
89 | + */ | |
90 | + ByteString toByteString(); | |
91 | + | |
92 | + /** | |
93 | + * Serializes the message to a {@code byte} array and returns it. This is | |
94 | + * just a trivial wrapper around | |
95 | + * {@link #writeTo(CodedOutputStream)}. | |
96 | + */ | |
97 | + byte[] toByteArray(); | |
98 | + | |
99 | + /** | |
100 | + * Serializes the message and writes it to {@code output}. This is just a | |
101 | + * trivial wrapper around {@link #writeTo(CodedOutputStream)}. This does | |
102 | + * not flush or close the stream. | |
103 | + * <p> | |
104 | + * NOTE: Protocol Buffers are not self-delimiting. Therefore, if you write | |
105 | + * any more data to the stream after the message, you must somehow ensure | |
106 | + * that the parser on the receiving end does not interpret this as being | |
107 | + * part of the protocol message. This can be done e.g. by writing the size | |
108 | + * of the message before the data, then making sure to limit the input to | |
109 | + * that size on the receiving end (e.g. by wrapping the InputStream in one | |
110 | + * which limits the input). Alternatively, just use | |
111 | + * {@link #writeDelimitedTo(OutputStream)}. | |
112 | + */ | |
113 | + void writeTo(OutputStream output) throws IOException; | |
114 | + | |
115 | + /** | |
116 | + * Like {@link #writeTo(OutputStream)}, but writes the size of the message | |
117 | + * as a varint before writing the data. This allows more data to be written | |
118 | + * to the stream after the message without the need to delimit the message | |
119 | + * data yourself. Use {@link Builder#mergeDelimitedFrom(InputStream)} (or | |
120 | + * the static method {@code YourMessageType.parseDelimitedFrom(InputStream)}) | |
121 | + * to parse messages written by this method. | |
122 | + */ | |
123 | + void writeDelimitedTo(OutputStream output) throws IOException; | |
124 | + | |
125 | + // ================================================================= | |
126 | + // Builders | |
127 | + | |
128 | + /** | |
129 | + * Constructs a new builder for a message of the same type as this message. | |
130 | + */ | |
131 | + Builder newBuilderForType(); | |
132 | + | |
133 | + /** | |
134 | + * Constructs a builder initialized with the current message. Use this to | |
135 | + * derive a new message from the current one. | |
136 | + */ | |
137 | + Builder toBuilder(); | |
138 | + | |
139 | + /** | |
140 | + * Abstract interface implemented by Protocol Message builders. | |
141 | + */ | |
142 | + interface Builder extends MessageLiteOrBuilder, Cloneable { | |
143 | + /** Resets all fields to their default values. */ | |
144 | + Builder clear(); | |
145 | + | |
146 | + /** | |
147 | + * Construct the final message. Once this is called, the Builder is no | |
148 | + * longer valid, and calling any other method will result in undefined | |
149 | + * behavior and may throw a NullPointerException. If you need to continue | |
150 | + * working with the builder after calling {@code build()}, {@code clone()} | |
151 | + * it first. | |
152 | + * @throws UninitializedMessageException The message is missing one or more | |
153 | + * required fields (i.e. {@link #isInitialized()} returns false). | |
154 | + * Use {@link #buildPartial()} to bypass this check. | |
155 | + */ | |
156 | + MessageLite build(); | |
157 | + | |
158 | + /** | |
159 | + * Like {@link #build()}, but does not throw an exception if the message | |
160 | + * is missing required fields. Instead, a partial message is returned. | |
161 | + * Once this is called, the Builder is no longer valid, and calling any | |
162 | + * will result in undefined behavior and may throw a NullPointerException. | |
163 | + * | |
164 | + * If you need to continue working with the builder after calling | |
165 | + * {@code buildPartial()}, {@code clone()} it first. | |
166 | + */ | |
167 | + MessageLite buildPartial(); | |
168 | + | |
169 | + /** | |
170 | + * Clones the Builder. | |
171 | + * @see Object#clone() | |
172 | + */ | |
173 | + Builder clone(); | |
174 | + | |
175 | + /** | |
176 | + * Parses a message of this type from the input and merges it with this | |
177 | + * message, as if using {@link Builder#mergeFrom(MessageLite)}. | |
178 | + * | |
179 | + * <p>Warning: This does not verify that all required fields are present in | |
180 | + * the input message. If you call {@link #build()} without setting all | |
181 | + * required fields, it will throw an {@link UninitializedMessageException}, | |
182 | + * which is a {@code RuntimeException} and thus might not be caught. There | |
183 | + * are a few good ways to deal with this: | |
184 | + * <ul> | |
185 | + * <li>Call {@link #isInitialized()} to verify that all required fields | |
186 | + * are set before building. | |
187 | + * <li>Parse the message separately using one of the static | |
188 | + * {@code parseFrom} methods, then use {@link #mergeFrom(MessageLite)} | |
189 | + * to merge it with this one. {@code parseFrom} will throw an | |
190 | + * {@link InvalidProtocolBufferException} (an {@code IOException}) | |
191 | + * if some required fields are missing. | |
192 | + * <li>Use {@code buildPartial()} to build, which ignores missing | |
193 | + * required fields. | |
194 | + * </ul> | |
195 | + * | |
196 | + * <p>Note: The caller should call | |
197 | + * {@link CodedInputStream#checkLastTagWas(int)} after calling this to | |
198 | + * verify that the last tag seen was the appropriate end-group tag, | |
199 | + * or zero for EOF. | |
200 | + */ | |
201 | + Builder mergeFrom(CodedInputStream input) throws IOException; | |
202 | + | |
203 | + /** | |
204 | + * Like {@link Builder#mergeFrom(CodedInputStream)}, but also | |
205 | + * parses extensions. The extensions that you want to be able to parse | |
206 | + * must be registered in {@code extensionRegistry}. Extensions not in | |
207 | + * the registry will be treated as unknown fields. | |
208 | + */ | |
209 | + Builder mergeFrom(CodedInputStream input, | |
210 | + ExtensionRegistryLite extensionRegistry) | |
211 | + throws IOException; | |
212 | + | |
213 | + // --------------------------------------------------------------- | |
214 | + // Convenience methods. | |
215 | + | |
216 | + /** | |
217 | + * Parse {@code data} as a message of this type and merge it with the | |
218 | + * message being built. This is just a small wrapper around | |
219 | + * {@link #mergeFrom(CodedInputStream)}. | |
220 | + * | |
221 | + * @return this | |
222 | + */ | |
223 | + Builder mergeFrom(ByteString data) throws InvalidProtocolBufferException; | |
224 | + | |
225 | + /** | |
226 | + * Parse {@code data} as a message of this type and merge it with the | |
227 | + * message being built. This is just a small wrapper around | |
228 | + * {@link #mergeFrom(CodedInputStream,ExtensionRegistry)}. | |
229 | + * | |
230 | + * @return this | |
231 | + */ | |
232 | + Builder mergeFrom(ByteString data, | |
233 | + ExtensionRegistryLite extensionRegistry) | |
234 | + throws InvalidProtocolBufferException; | |
235 | + | |
236 | + /** | |
237 | + * Parse {@code data} as a message of this type and merge it with the | |
238 | + * message being built. This is just a small wrapper around | |
239 | + * {@link #mergeFrom(CodedInputStream)}. | |
240 | + * | |
241 | + * @return this | |
242 | + */ | |
243 | + Builder mergeFrom(byte[] data) throws InvalidProtocolBufferException; | |
244 | + | |
245 | + /** | |
246 | + * Parse {@code data} as a message of this type and merge it with the | |
247 | + * message being built. This is just a small wrapper around | |
248 | + * {@link #mergeFrom(CodedInputStream)}. | |
249 | + * | |
250 | + * @return this | |
251 | + */ | |
252 | + Builder mergeFrom(byte[] data, int off, int len) | |
253 | + throws InvalidProtocolBufferException; | |
254 | + | |
255 | + /** | |
256 | + * Parse {@code data} as a message of this type and merge it with the | |
257 | + * message being built. This is just a small wrapper around | |
258 | + * {@link #mergeFrom(CodedInputStream,ExtensionRegistry)}. | |
259 | + * | |
260 | + * @return this | |
261 | + */ | |
262 | + Builder mergeFrom(byte[] data, | |
263 | + ExtensionRegistryLite extensionRegistry) | |
264 | + throws InvalidProtocolBufferException; | |
265 | + | |
266 | + /** | |
267 | + * Parse {@code data} as a message of this type and merge it with the | |
268 | + * message being built. This is just a small wrapper around | |
269 | + * {@link #mergeFrom(CodedInputStream,ExtensionRegistry)}. | |
270 | + * | |
271 | + * @return this | |
272 | + */ | |
273 | + Builder mergeFrom(byte[] data, int off, int len, | |
274 | + ExtensionRegistryLite extensionRegistry) | |
275 | + throws InvalidProtocolBufferException; | |
276 | + | |
277 | + /** | |
278 | + * Parse a message of this type from {@code input} and merge it with the | |
279 | + * message being built. This is just a small wrapper around | |
280 | + * {@link #mergeFrom(CodedInputStream)}. Note that this method always | |
281 | + * reads the <i>entire</i> input (unless it throws an exception). If you | |
282 | + * want it to stop earlier, you will need to wrap your input in some | |
283 | + * wrapper stream that limits reading. Or, use | |
284 | + * {@link MessageLite#writeDelimitedTo(OutputStream)} to write your message | |
285 | + * and {@link #mergeDelimitedFrom(InputStream)} to read it. | |
286 | + * <p> | |
287 | + * Despite usually reading the entire input, this does not close the stream. | |
288 | + * | |
289 | + * @return this | |
290 | + */ | |
291 | + Builder mergeFrom(InputStream input) throws IOException; | |
292 | + | |
293 | + /** | |
294 | + * Parse a message of this type from {@code input} and merge it with the | |
295 | + * message being built. This is just a small wrapper around | |
296 | + * {@link #mergeFrom(CodedInputStream,ExtensionRegistry)}. | |
297 | + * | |
298 | + * @return this | |
299 | + */ | |
300 | + Builder mergeFrom(InputStream input, | |
301 | + ExtensionRegistryLite extensionRegistry) | |
302 | + throws IOException; | |
303 | + | |
304 | + /** | |
305 | + * Like {@link #mergeFrom(InputStream)}, but does not read until EOF. | |
306 | + * Instead, the size of the message (encoded as a varint) is read first, | |
307 | + * then the message data. Use | |
308 | + * {@link MessageLite#writeDelimitedTo(OutputStream)} to write messages in | |
309 | + * this format. | |
310 | + * | |
311 | + * @returns True if successful, or false if the stream is at EOF when the | |
312 | + * method starts. Any other error (including reaching EOF during | |
313 | + * parsing) will cause an exception to be thrown. | |
314 | + */ | |
315 | + boolean mergeDelimitedFrom(InputStream input) | |
316 | + throws IOException; | |
317 | + | |
318 | + /** | |
319 | + * Like {@link #mergeDelimitedFrom(InputStream)} but supporting extensions. | |
320 | + */ | |
321 | + boolean mergeDelimitedFrom(InputStream input, | |
322 | + ExtensionRegistryLite extensionRegistry) | |
323 | + throws IOException; | |
324 | + } | |
325 | +} |
@@ -0,0 +1,58 @@ | ||
1 | +// Protocol Buffers - Google's data interchange format | |
2 | +// Copyright 2008 Google Inc. All rights reserved. | |
3 | +// http://code.google.com/p/protobuf/ | |
4 | +// | |
5 | +// Redistribution and use in source and binary forms, with or without | |
6 | +// modification, are permitted provided that the following conditions are | |
7 | +// met: | |
8 | +// | |
9 | +// * Redistributions of source code must retain the above copyright | |
10 | +// notice, this list of conditions and the following disclaimer. | |
11 | +// * Redistributions in binary form must reproduce the above | |
12 | +// copyright notice, this list of conditions and the following disclaimer | |
13 | +// in the documentation and/or other materials provided with the | |
14 | +// distribution. | |
15 | +// * Neither the name of Google Inc. nor the names of its | |
16 | +// contributors may be used to endorse or promote products derived from | |
17 | +// this software without specific prior written permission. | |
18 | +// | |
19 | +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
20 | +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
21 | +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
22 | +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
23 | +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
24 | +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
25 | +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
26 | +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
27 | +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
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. | |
30 | + | |
31 | +package com.google.protobuf; | |
32 | + | |
33 | +/** | |
34 | + * Base interface for methods common to {@link MessageLite} | |
35 | + * and {@link MessageLite.Builder} to provide type equivalency. | |
36 | + * | |
37 | + * @author jonp@google.com (Jon Perlow) | |
38 | + */ | |
39 | +public interface MessageLiteOrBuilder { | |
40 | + /** | |
41 | + * Get an instance of the type with no fields set. Because no fields are set, | |
42 | + * all getters for singular fields will return default values and repeated | |
43 | + * fields will appear empty. | |
44 | + * This may or may not be a singleton. This differs from the | |
45 | + * {@code getDefaultInstance()} method of generated message classes in that | |
46 | + * this method is an abstract method of the {@code MessageLite} interface | |
47 | + * whereas {@code getDefaultInstance()} is a static method of a specific | |
48 | + * class. They return the same thing. | |
49 | + */ | |
50 | + MessageLite getDefaultInstanceForType(); | |
51 | + | |
52 | + /** | |
53 | + * Returns true if all required fields in the message and all embedded | |
54 | + * messages are set, false otherwise. | |
55 | + */ | |
56 | + boolean isInitialized(); | |
57 | + | |
58 | +} |
@@ -0,0 +1,110 @@ | ||
1 | +// Protocol Buffers - Google's data interchange format | |
2 | +// Copyright 2008 Google Inc. All rights reserved. | |
3 | +// http://code.google.com/p/protobuf/ | |
4 | +// | |
5 | +// Redistribution and use in source and binary forms, with or without | |
6 | +// modification, are permitted provided that the following conditions are | |
7 | +// met: | |
8 | +// | |
9 | +// * Redistributions of source code must retain the above copyright | |
10 | +// notice, this list of conditions and the following disclaimer. | |
11 | +// * Redistributions in binary form must reproduce the above | |
12 | +// copyright notice, this list of conditions and the following disclaimer | |
13 | +// in the documentation and/or other materials provided with the | |
14 | +// distribution. | |
15 | +// * Neither the name of Google Inc. nor the names of its | |
16 | +// contributors may be used to endorse or promote products derived from | |
17 | +// this software without specific prior written permission. | |
18 | +// | |
19 | +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
20 | +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
21 | +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
22 | +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
23 | +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
24 | +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
25 | +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
26 | +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
27 | +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
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. | |
30 | + | |
31 | +package com.google.protobuf; | |
32 | + | |
33 | +import java.util.Map; | |
34 | + | |
35 | +/** | |
36 | + * Base interface for methods common to {@link Message} and | |
37 | + * {@link Message.Builder} to provide type equivalency. | |
38 | + * | |
39 | + * @author jonp@google.com (Jon Perlow) | |
40 | + */ | |
41 | +public interface MessageOrBuilder extends MessageLiteOrBuilder { | |
42 | + | |
43 | + // (From MessageLite, re-declared here only for return type covariance.) | |
44 | + //@Override (Java 1.6 override semantics, but we must support 1.5) | |
45 | + Message getDefaultInstanceForType(); | |
46 | + | |
47 | + /** | |
48 | + * Get the message's type's descriptor. This differs from the | |
49 | + * {@code getDescriptor()} method of generated message classes in that | |
50 | + * this method is an abstract method of the {@code Message} interface | |
51 | + * whereas {@code getDescriptor()} is a static method of a specific class. | |
52 | + * They return the same thing. | |
53 | + */ | |
54 | + Descriptors.Descriptor getDescriptorForType(); | |
55 | + | |
56 | + /** | |
57 | + * Returns a collection of all the fields in this message which are set | |
58 | + * and their corresponding values. A singular ("required" or "optional") | |
59 | + * field is set iff hasField() returns true for that field. A "repeated" | |
60 | + * field is set iff getRepeatedFieldSize() is greater than zero. The | |
61 | + * values are exactly what would be returned by calling | |
62 | + * {@link #getField(Descriptors.FieldDescriptor)} for each field. The map | |
63 | + * is guaranteed to be a sorted map, so iterating over it will return fields | |
64 | + * in order by field number. | |
65 | + * <br> | |
66 | + * If this is for a builder, the returned map may or may not reflect future | |
67 | + * changes to the builder. Either way, the returned map is itself | |
68 | + * unmodifiable. | |
69 | + */ | |
70 | + Map<Descriptors.FieldDescriptor, Object> getAllFields(); | |
71 | + | |
72 | + /** | |
73 | + * Returns true if the given field is set. This is exactly equivalent to | |
74 | + * calling the generated "has" accessor method corresponding to the field. | |
75 | + * @throws IllegalArgumentException The field is a repeated field, or | |
76 | + * {@code field.getContainingType() != getDescriptorForType()}. | |
77 | + */ | |
78 | + boolean hasField(Descriptors.FieldDescriptor field); | |
79 | + | |
80 | + /** | |
81 | + * Obtains the value of the given field, or the default value if it is | |
82 | + * not set. For primitive fields, the boxed primitive value is returned. | |
83 | + * For enum fields, the EnumValueDescriptor for the value is returend. For | |
84 | + * embedded message fields, the sub-message is returned. For repeated | |
85 | + * fields, a java.util.List is returned. | |
86 | + */ | |
87 | + Object getField(Descriptors.FieldDescriptor field); | |
88 | + | |
89 | + /** | |
90 | + * Gets the number of elements of a repeated field. This is exactly | |
91 | + * equivalent to calling the generated "Count" accessor method corresponding | |
92 | + * to the field. | |
93 | + * @throws IllegalArgumentException The field is not a repeated field, or | |
94 | + * {@code field.getContainingType() != getDescriptorForType()}. | |
95 | + */ | |
96 | + int getRepeatedFieldCount(Descriptors.FieldDescriptor field); | |
97 | + | |
98 | + /** | |
99 | + * Gets an element of a repeated field. For primitive fields, the boxed | |
100 | + * primitive value is returned. For enum fields, the EnumValueDescriptor | |
101 | + * for the value is returend. For embedded message fields, the sub-message | |
102 | + * is returned. | |
103 | + * @throws IllegalArgumentException The field is not a repeated field, or | |
104 | + * {@code field.getContainingType() != getDescriptorForType()}. | |
105 | + */ | |
106 | + Object getRepeatedField(Descriptors.FieldDescriptor field, int index); | |
107 | + | |
108 | + /** Get the {@link UnknownFieldSet} for this message. */ | |
109 | + UnknownFieldSet getUnknownFields(); | |
110 | +} |
@@ -0,0 +1,58 @@ | ||
1 | +// Protocol Buffers - Google's data interchange format | |
2 | +// Copyright 2008 Google Inc. All rights reserved. | |
3 | +// http://code.google.com/p/protobuf/ | |
4 | +// | |
5 | +// Redistribution and use in source and binary forms, with or without | |
6 | +// modification, are permitted provided that the following conditions are | |
7 | +// met: | |
8 | +// | |
9 | +// * Redistributions of source code must retain the above copyright | |
10 | +// notice, this list of conditions and the following disclaimer. | |
11 | +// * Redistributions in binary form must reproduce the above | |
12 | +// copyright notice, this list of conditions and the following disclaimer | |
13 | +// in the documentation and/or other materials provided with the | |
14 | +// distribution. | |
15 | +// * Neither the name of Google Inc. nor the names of its | |
16 | +// contributors may be used to endorse or promote products derived from | |
17 | +// this software without specific prior written permission. | |
18 | +// | |
19 | +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
20 | +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
21 | +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
22 | +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
23 | +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
24 | +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
25 | +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
26 | +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
27 | +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
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. | |
30 | + | |
31 | +package com.google.protobuf; | |
32 | + | |
33 | +import com.google.protobuf.Descriptors.EnumDescriptor; | |
34 | +import com.google.protobuf.Descriptors.EnumValueDescriptor; | |
35 | + | |
36 | +/** | |
37 | + * Interface of useful methods added to all enums generated by the protocol | |
38 | + * compiler. | |
39 | + */ | |
40 | +public interface ProtocolMessageEnum extends Internal.EnumLite { | |
41 | + | |
42 | + /** | |
43 | + * Return the value's numeric value as defined in the .proto file. | |
44 | + */ | |
45 | + int getNumber(); | |
46 | + | |
47 | + /** | |
48 | + * Return the value's descriptor, which contains information such as | |
49 | + * value name, number, and type. | |
50 | + */ | |
51 | + EnumValueDescriptor getValueDescriptor(); | |
52 | + | |
53 | + /** | |
54 | + * Return the enum type's descriptor, which contains information | |
55 | + * about each defined value, etc. | |
56 | + */ | |
57 | + EnumDescriptor getDescriptorForType(); | |
58 | +} |
@@ -0,0 +1,696 @@ | ||
1 | +// Protocol Buffers - Google's data interchange format | |
2 | +// Copyright 2008 Google Inc. All rights reserved. | |
3 | +// http://code.google.com/p/protobuf/ | |
4 | +// | |
5 | +// Redistribution and use in source and binary forms, with or without | |
6 | +// modification, are permitted provided that the following conditions are | |
7 | +// met: | |
8 | +// | |
9 | +// * Redistributions of source code must retain the above copyright | |
10 | +// notice, this list of conditions and the following disclaimer. | |
11 | +// * Redistributions in binary form must reproduce the above | |
12 | +// copyright notice, this list of conditions and the following disclaimer | |
13 | +// in the documentation and/or other materials provided with the | |
14 | +// distribution. | |
15 | +// * Neither the name of Google Inc. nor the names of its | |
16 | +// contributors may be used to endorse or promote products derived from | |
17 | +// this software without specific prior written permission. | |
18 | +// | |
19 | +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
20 | +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
21 | +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
22 | +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
23 | +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
24 | +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
25 | +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
26 | +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
27 | +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
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. | |
30 | + | |
31 | +package com.google.protobuf; | |
32 | + | |
33 | +import java.util.AbstractList; | |
34 | +import java.util.ArrayList; | |
35 | +import java.util.Collection; | |
36 | +import java.util.Collections; | |
37 | +import java.util.List; | |
38 | + | |
39 | +/** | |
40 | + * <code>RepeatedFieldBuilder</code> implements a structure that a protocol | |
41 | + * message uses to hold a repeated field of other protocol messages. It supports | |
42 | + * the classical use case of adding immutable {@link Message}'s to the | |
43 | + * repeated field and is highly optimized around this (no extra memory | |
44 | + * allocations and sharing of immutable arrays). | |
45 | + * <br> | |
46 | + * It also supports the additional use case of adding a {@link Message.Builder} | |
47 | + * to the repeated field and deferring conversion of that <code>Builder</code> | |
48 | + * to an immutable <code>Message</code>. In this way, it's possible to maintain | |
49 | + * a tree of <code>Builder</code>'s that acts as a fully read/write data | |
50 | + * structure. | |
51 | + * <br> | |
52 | + * Logically, one can think of a tree of builders as converting the entire tree | |
53 | + * to messages when build is called on the root or when any method is called | |
54 | + * that desires a Message instead of a Builder. In terms of the implementation, | |
55 | + * the <code>SingleFieldBuilder</code> and <code>RepeatedFieldBuilder</code> | |
56 | + * classes cache messages that were created so that messages only need to be | |
57 | + * created when some change occured in its builder or a builder for one of its | |
58 | + * descendants. | |
59 | + * | |
60 | + * @param <MType> the type of message for the field | |
61 | + * @param <BType> the type of builder for the field | |
62 | + * @param <IType> the common interface for the message and the builder | |
63 | + * | |
64 | + * @author jonp@google.com (Jon Perlow) | |
65 | + */ | |
66 | +public class RepeatedFieldBuilder | |
67 | + <MType extends GeneratedMessage, | |
68 | + BType extends GeneratedMessage.Builder, | |
69 | + IType extends MessageOrBuilder> | |
70 | + implements GeneratedMessage.BuilderParent { | |
71 | + | |
72 | + // Parent to send changes to. | |
73 | + private GeneratedMessage.BuilderParent parent; | |
74 | + | |
75 | + // List of messages. Never null. It may be immutable, in which case | |
76 | + // isMessagesListImmutable will be true. See note below. | |
77 | + private List<MType> messages; | |
78 | + | |
79 | + // Whether messages is an mutable array that can be modified. | |
80 | + private boolean isMessagesListMutable; | |
81 | + | |
82 | + // List of builders. May be null, in which case, no nested builders were | |
83 | + // created. If not null, entries represent the builder for that index. | |
84 | + private List<SingleFieldBuilder<MType, BType, IType>> builders; | |
85 | + | |
86 | + // Here are the invariants for messages and builders: | |
87 | + // 1. messages is never null and its count corresponds to the number of items | |
88 | + // in the repeated field. | |
89 | + // 2. If builders is non-null, messages and builders MUST always | |
90 | + // contain the same number of items. | |
91 | + // 3. Entries in either array can be null, but for any index, there MUST be | |
92 | + // either a Message in messages or a builder in builders. | |
93 | + // 4. If the builder at an index is non-null, the builder is | |
94 | + // authoritative. This is the case where a Builder was set on the index. | |
95 | + // Any message in the messages array MUST be ignored. | |
96 | + // t. If the builder at an index is null, the message in the messages | |
97 | + // list is authoritative. This is the case where a Message (not a Builder) | |
98 | + // was set directly for an index. | |
99 | + | |
100 | + // Indicates that we've built a message and so we are now obligated | |
101 | + // to dispatch dirty invalidations. See GeneratedMessage.BuilderListener. | |
102 | + private boolean isClean; | |
103 | + | |
104 | + // A view of this builder that exposes a List interface of messages. This is | |
105 | + // initialized on demand. This is fully backed by this object and all changes | |
106 | + // are reflected in it. Access to any item converts it to a message if it | |
107 | + // was a builder. | |
108 | + private MessageExternalList<MType, BType, IType> externalMessageList; | |
109 | + | |
110 | + // A view of this builder that exposes a List interface of builders. This is | |
111 | + // initialized on demand. This is fully backed by this object and all changes | |
112 | + // are reflected in it. Access to any item converts it to a builder if it | |
113 | + // was a message. | |
114 | + private BuilderExternalList<MType, BType, IType> externalBuilderList; | |
115 | + | |
116 | + // A view of this builder that exposes a List interface of the interface | |
117 | + // implemented by messages and builders. This is initialized on demand. This | |
118 | + // is fully backed by this object and all changes are reflected in it. | |
119 | + // Access to any item returns either a builder or message depending on | |
120 | + // what is most efficient. | |
121 | + private MessageOrBuilderExternalList<MType, BType, IType> | |
122 | + externalMessageOrBuilderList; | |
123 | + | |
124 | + /** | |
125 | + * Constructs a new builder with an empty list of messages. | |
126 | + * | |
127 | + * @param messages the current list of messages | |
128 | + * @param isMessagesListMutable Whether the messages list is mutable | |
129 | + * @param parent a listener to notify of changes | |
130 | + * @param isClean whether the builder is initially marked clean | |
131 | + */ | |
132 | + public RepeatedFieldBuilder( | |
133 | + List<MType> messages, | |
134 | + boolean isMessagesListMutable, | |
135 | + GeneratedMessage.BuilderParent parent, | |
136 | + boolean isClean) { | |
137 | + this.messages = messages; | |
138 | + this.isMessagesListMutable = isMessagesListMutable; | |
139 | + this.parent = parent; | |
140 | + this.isClean = isClean; | |
141 | + } | |
142 | + | |
143 | + public void dispose() { | |
144 | + // Null out parent so we stop sending it invalidations. | |
145 | + parent = null; | |
146 | + } | |
147 | + | |
148 | + /** | |
149 | + * Ensures that the list of messages is mutable so it can be updated. If it's | |
150 | + * immutable, a copy is made. | |
151 | + */ | |
152 | + private void ensureMutableMessageList() { | |
153 | + if (!isMessagesListMutable) { | |
154 | + messages = new ArrayList<MType>(messages); | |
155 | + isMessagesListMutable = true; | |
156 | + } | |
157 | + } | |
158 | + | |
159 | + /** | |
160 | + * Ensures that the list of builders is not null. If it's null, the list is | |
161 | + * created and initialized to be the same size as the messages list with | |
162 | + * null entries. | |
163 | + */ | |
164 | + private void ensureBuilders() { | |
165 | + if (this.builders == null) { | |
166 | + this.builders = | |
167 | + new ArrayList<SingleFieldBuilder<MType, BType, IType>>( | |
168 | + messages.size()); | |
169 | + for (int i = 0; i < messages.size(); i++) { | |
170 | + builders.add(null); | |
171 | + } | |
172 | + } | |
173 | + } | |
174 | + | |
175 | + /** | |
176 | + * Gets the count of items in the list. | |
177 | + * | |
178 | + * @return the count of items in the list. | |
179 | + */ | |
180 | + public int getCount() { | |
181 | + return messages.size(); | |
182 | + } | |
183 | + | |
184 | + /** | |
185 | + * Gets whether the list is empty. | |
186 | + * | |
187 | + * @return whether the list is empty | |
188 | + */ | |
189 | + public boolean isEmpty() { | |
190 | + return messages.isEmpty(); | |
191 | + } | |
192 | + | |
193 | + /** | |
194 | + * Get the message at the specified index. If the message is currently stored | |
195 | + * as a <code>Builder</code>, it is converted to a <code>Message</code> by | |
196 | + * calling {@link Message.Builder#buildPartial} on it. | |
197 | + * | |
198 | + * @param index the index of the message to get | |
199 | + * @return the message for the specified index | |
200 | + */ | |
201 | + public MType getMessage(int index) { | |
202 | + return getMessage(index, false); | |
203 | + } | |
204 | + | |
205 | + /** | |
206 | + * Get the message at the specified index. If the message is currently stored | |
207 | + * as a <code>Builder</code>, it is converted to a <code>Message</code> by | |
208 | + * calling {@link Message.Builder#buildPartial} on it. | |
209 | + * | |
210 | + * @param index the index of the message to get | |
211 | + * @param forBuild this is being called for build so we want to make sure | |
212 | + * we SingleFieldBuilder.build to send dirty invalidations | |
213 | + * @return the message for the specified index | |
214 | + */ | |
215 | + private MType getMessage(int index, boolean forBuild) { | |
216 | + if (this.builders == null) { | |
217 | + // We don't have any builders -- return the current Message. | |
218 | + // This is the case where no builder was created, so we MUST have a | |
219 | + // Message. | |
220 | + return messages.get(index); | |
221 | + } | |
222 | + | |
223 | + SingleFieldBuilder<MType, BType, IType> builder = builders.get(index); | |
224 | + if (builder == null) { | |
225 | + // We don't have a builder -- return the current message. | |
226 | + // This is the case where no builder was created for the entry at index, | |
227 | + // so we MUST have a message. | |
228 | + return messages.get(index); | |
229 | + | |
230 | + } else { | |
231 | + return forBuild ? builder.build() : builder.getMessage(); | |
232 | + } | |
233 | + } | |
234 | + | |
235 | + /** | |
236 | + * Gets a builder for the specified index. If no builder has been created for | |
237 | + * that index, a builder is created on demand by calling | |
238 | + * {@link Message#toBuilder}. | |
239 | + * | |
240 | + * @param index the index of the message to get | |
241 | + * @return The builder for that index | |
242 | + */ | |
243 | + public BType getBuilder(int index) { | |
244 | + ensureBuilders(); | |
245 | + SingleFieldBuilder<MType, BType, IType> builder = builders.get(index); | |
246 | + if (builder == null) { | |
247 | + MType message = messages.get(index); | |
248 | + builder = new SingleFieldBuilder<MType, BType, IType>( | |
249 | + message, this, isClean); | |
250 | + builders.set(index, builder); | |
251 | + } | |
252 | + return builder.getBuilder(); | |
253 | + } | |
254 | + | |
255 | + /** | |
256 | + * Gets the base class interface for the specified index. This may either be | |
257 | + * a builder or a message. It will return whatever is more efficient. | |
258 | + * | |
259 | + * @param index the index of the message to get | |
260 | + * @return the message or builder for the index as the base class interface | |
261 | + */ | |
262 | + @SuppressWarnings("unchecked") | |
263 | + public IType getMessageOrBuilder(int index) { | |
264 | + if (this.builders == null) { | |
265 | + // We don't have any builders -- return the current Message. | |
266 | + // This is the case where no builder was created, so we MUST have a | |
267 | + // Message. | |
268 | + return (IType) messages.get(index); | |
269 | + } | |
270 | + | |
271 | + SingleFieldBuilder<MType, BType, IType> builder = builders.get(index); | |
272 | + if (builder == null) { | |
273 | + // We don't have a builder -- return the current message. | |
274 | + // This is the case where no builder was created for the entry at index, | |
275 | + // so we MUST have a message. | |
276 | + return (IType) messages.get(index); | |
277 | + | |
278 | + } else { | |
279 | + return builder.getMessageOrBuilder(); | |
280 | + } | |
281 | + } | |
282 | + | |
283 | + /** | |
284 | + * Sets a message at the specified index replacing the existing item at | |
285 | + * that index. | |
286 | + * | |
287 | + * @param index the index to set. | |
288 | + * @param message the message to set | |
289 | + * @return the builder | |
290 | + */ | |
291 | + public RepeatedFieldBuilder<MType, BType, IType> setMessage( | |
292 | + int index, MType message) { | |
293 | + if (message == null) { | |
294 | + throw new NullPointerException(); | |
295 | + } | |
296 | + ensureMutableMessageList(); | |
297 | + messages.set(index, message); | |
298 | + if (builders != null) { | |
299 | + SingleFieldBuilder<MType, BType, IType> entry = | |
300 | + builders.set(index, null); | |
301 | + if (entry != null) { | |
302 | + entry.dispose(); | |
303 | + } | |
304 | + } | |
305 | + onChanged(); | |
306 | + incrementModCounts(); | |
307 | + return this; | |
308 | + } | |
309 | + | |
310 | + /** | |
311 | + * Appends the specified element to the end of this list. | |
312 | + * | |
313 | + * @param message the message to add | |
314 | + * @return the builder | |
315 | + */ | |
316 | + public RepeatedFieldBuilder<MType, BType, IType> addMessage( | |
317 | + MType message) { | |
318 | + if (message == null) { | |
319 | + throw new NullPointerException(); | |
320 | + } | |
321 | + ensureMutableMessageList(); | |
322 | + messages.add(message); | |
323 | + if (builders != null) { | |
324 | + builders.add(null); | |
325 | + } | |
326 | + onChanged(); | |
327 | + incrementModCounts(); | |
328 | + return this; | |
329 | + } | |
330 | + | |
331 | + /** | |
332 | + * Inserts the specified message at the specified position in this list. | |
333 | + * Shifts the element currently at that position (if any) and any subsequent | |
334 | + * elements to the right (adds one to their indices). | |
335 | + * | |
336 | + * @param index the index at which to insert the message | |
337 | + * @param message the message to add | |
338 | + * @return the builder | |
339 | + */ | |
340 | + public RepeatedFieldBuilder<MType, BType, IType> addMessage( | |
341 | + int index, MType message) { | |
342 | + if (message == null) { | |
343 | + throw new NullPointerException(); | |
344 | + } | |
345 | + ensureMutableMessageList(); | |
346 | + messages.add(index, message); | |
347 | + if (builders != null) { | |
348 | + builders.add(index, null); | |
349 | + } | |
350 | + onChanged(); | |
351 | + incrementModCounts(); | |
352 | + return this; | |
353 | + } | |
354 | + | |
355 | + /** | |
356 | + * Appends all of the messages in the specified collection to the end of | |
357 | + * this list, in the order that they are returned by the specified | |
358 | + * collection's iterator. | |
359 | + * | |
360 | + * @param values the messages to add | |
361 | + * @return the builder | |
362 | + */ | |
363 | + public RepeatedFieldBuilder<MType, BType, IType> addAllMessages( | |
364 | + Iterable<? extends MType> values) { | |
365 | + for (final MType value : values) { | |
366 | + if (value == null) { | |
367 | + throw new NullPointerException(); | |
368 | + } | |
369 | + } | |
370 | + if (values instanceof Collection) { | |
371 | + @SuppressWarnings("unchecked") final | |
372 | + Collection<MType> collection = (Collection<MType>) values; | |
373 | + if (collection.size() == 0) { | |
374 | + return this; | |
375 | + } | |
376 | + ensureMutableMessageList(); | |
377 | + for (MType value : values) { | |
378 | + addMessage(value); | |
379 | + } | |
380 | + } else { | |
381 | + ensureMutableMessageList(); | |
382 | + for (MType value : values) { | |
383 | + addMessage(value); | |
384 | + } | |
385 | + } | |
386 | + onChanged(); | |
387 | + incrementModCounts(); | |
388 | + return this; | |
389 | + } | |
390 | + | |
391 | + /** | |
392 | + * Appends a new builder to the end of this list and returns the builder. | |
393 | + * | |
394 | + * @param message the message to add which is the basis of the builder | |
395 | + * @return the new builder | |
396 | + */ | |
397 | + public BType addBuilder(MType message) { | |
398 | + ensureMutableMessageList(); | |
399 | + ensureBuilders(); | |
400 | + SingleFieldBuilder<MType, BType, IType> builder = | |
401 | + new SingleFieldBuilder<MType, BType, IType>( | |
402 | + message, this, isClean); | |
403 | + messages.add(null); | |
404 | + builders.add(builder); | |
405 | + onChanged(); | |
406 | + incrementModCounts(); | |
407 | + return builder.getBuilder(); | |
408 | + } | |
409 | + | |
410 | + /** | |
411 | + * Inserts a new builder at the specified position in this list. | |
412 | + * Shifts the element currently at that position (if any) and any subsequent | |
413 | + * elements to the right (adds one to their indices). | |
414 | + * | |
415 | + * @param index the index at which to insert the builder | |
416 | + * @param message the message to add which is the basis of the builder | |
417 | + * @return the builder | |
418 | + */ | |
419 | + public BType addBuilder(int index, MType message) { | |
420 | + ensureMutableMessageList(); | |
421 | + ensureBuilders(); | |
422 | + SingleFieldBuilder<MType, BType, IType> builder = | |
423 | + new SingleFieldBuilder<MType, BType, IType>( | |
424 | + message, this, isClean); | |
425 | + messages.add(index, null); | |
426 | + builders.add(index, builder); | |
427 | + onChanged(); | |
428 | + incrementModCounts(); | |
429 | + return builder.getBuilder(); | |
430 | + } | |
431 | + | |
432 | + /** | |
433 | + * Removes the element at the specified position in this list. Shifts any | |
434 | + * subsequent elements to the left (subtracts one from their indices). | |
435 | + * Returns the element that was removed from the list. | |
436 | + * | |
437 | + * @param index the index at which to remove the message | |
438 | + */ | |
439 | + public void remove(int index) { | |
440 | + ensureMutableMessageList(); | |
441 | + messages.remove(index); | |
442 | + if (builders != null) { | |
443 | + SingleFieldBuilder<MType, BType, IType> entry = | |
444 | + builders.remove(index); | |
445 | + if (entry != null) { | |
446 | + entry.dispose(); | |
447 | + } | |
448 | + } | |
449 | + onChanged(); | |
450 | + incrementModCounts(); | |
451 | + } | |
452 | + | |
453 | + /** | |
454 | + * Removes all of the elements from this list. | |
455 | + * The list will be empty after this call returns. | |
456 | + */ | |
457 | + public void clear() { | |
458 | + messages = Collections.emptyList(); | |
459 | + isMessagesListMutable = false; | |
460 | + if (builders != null) { | |
461 | + for (SingleFieldBuilder<MType, BType, IType> entry : | |
462 | + builders) { | |
463 | + if (entry != null) { | |
464 | + entry.dispose(); | |
465 | + } | |
466 | + } | |
467 | + builders = null; | |
468 | + } | |
469 | + onChanged(); | |
470 | + incrementModCounts(); | |
471 | + } | |
472 | + | |
473 | + /** | |
474 | + * Builds the list of messages from the builder and returns them. | |
475 | + * | |
476 | + * @return an immutable list of messages | |
477 | + */ | |
478 | + public List<MType> build() { | |
479 | + // Now that build has been called, we are required to dispatch | |
480 | + // invalidations. | |
481 | + isClean = true; | |
482 | + | |
483 | + if (!isMessagesListMutable && builders == null) { | |
484 | + // We still have an immutable list and we never created a builder. | |
485 | + return messages; | |
486 | + } | |
487 | + | |
488 | + boolean allMessagesInSync = true; | |
489 | + if (!isMessagesListMutable) { | |
490 | + // We still have an immutable list. Let's see if any of them are out | |
491 | + // of sync with their builders. | |
492 | + for (int i = 0; i < messages.size(); i++) { | |
493 | + Message message = messages.get(i); | |
494 | + SingleFieldBuilder<MType, BType, IType> builder = builders.get(i); | |
495 | + if (builder != null) { | |
496 | + if (builder.build() != message) { | |
497 | + allMessagesInSync = false; | |
498 | + break; | |
499 | + } | |
500 | + } | |
501 | + } | |
502 | + if (allMessagesInSync) { | |
503 | + // Immutable list is still in sync. | |
504 | + return messages; | |
505 | + } | |
506 | + } | |
507 | + | |
508 | + // Need to make sure messages is up to date | |
509 | + ensureMutableMessageList(); | |
510 | + for (int i = 0; i < messages.size(); i++) { | |
511 | + messages.set(i, getMessage(i, true)); | |
512 | + } | |
513 | + | |
514 | + // We're going to return our list as immutable so we mark that we can | |
515 | + // no longer update it. | |
516 | + messages = Collections.unmodifiableList(messages); | |
517 | + isMessagesListMutable = false; | |
518 | + return messages; | |
519 | + } | |
520 | + | |
521 | + /** | |
522 | + * Gets a view of the builder as a list of messages. The returned list is live | |
523 | + * and will reflect any changes to the underlying builder. | |
524 | + * | |
525 | + * @return the messages in the list | |
526 | + */ | |
527 | + public List<MType> getMessageList() { | |
528 | + if (externalMessageList == null) { | |
529 | + externalMessageList = | |
530 | + new MessageExternalList<MType, BType, IType>(this); | |
531 | + } | |
532 | + return externalMessageList; | |
533 | + } | |
534 | + | |
535 | + /** | |
536 | + * Gets a view of the builder as a list of builders. This returned list is | |
537 | + * live and will reflect any changes to the underlying builder. | |
538 | + * | |
539 | + * @return the builders in the list | |
540 | + */ | |
541 | + public List<BType> getBuilderList() { | |
542 | + if (externalBuilderList == null) { | |
543 | + externalBuilderList = | |
544 | + new BuilderExternalList<MType, BType, IType>(this); | |
545 | + } | |
546 | + return externalBuilderList; | |
547 | + } | |
548 | + | |
549 | + /** | |
550 | + * Gets a view of the builder as a list of MessageOrBuilders. This returned | |
551 | + * list is live and will reflect any changes to the underlying builder. | |
552 | + * | |
553 | + * @return the builders in the list | |
554 | + */ | |
555 | + public List<IType> getMessageOrBuilderList() { | |
556 | + if (externalMessageOrBuilderList == null) { | |
557 | + externalMessageOrBuilderList = | |
558 | + new MessageOrBuilderExternalList<MType, BType, IType>(this); | |
559 | + } | |
560 | + return externalMessageOrBuilderList; | |
561 | + } | |
562 | + | |
563 | + /** | |
564 | + * Called when a the builder or one of its nested children has changed | |
565 | + * and any parent should be notified of its invalidation. | |
566 | + */ | |
567 | + private void onChanged() { | |
568 | + if (isClean && parent != null) { | |
569 | + parent.markDirty(); | |
570 | + | |
571 | + // Don't keep dispatching invalidations until build is called again. | |
572 | + isClean = false; | |
573 | + } | |
574 | + } | |
575 | + | |
576 | + //@Override (Java 1.6 override semantics, but we must support 1.5) | |
577 | + public void markDirty() { | |
578 | + onChanged(); | |
579 | + } | |
580 | + | |
581 | + /** | |
582 | + * Increments the mod counts so that an ConcurrentModificationException can | |
583 | + * be thrown if calling code tries to modify the builder while its iterating | |
584 | + * the list. | |
585 | + */ | |
586 | + private void incrementModCounts() { | |
587 | + if (externalMessageList != null) { | |
588 | + externalMessageList.incrementModCount(); | |
589 | + } | |
590 | + if (externalBuilderList != null) { | |
591 | + externalBuilderList.incrementModCount(); | |
592 | + } | |
593 | + if (externalMessageOrBuilderList != null) { | |
594 | + externalMessageOrBuilderList.incrementModCount(); | |
595 | + } | |
596 | + } | |
597 | + | |
598 | + /** | |
599 | + * Provides a live view of the builder as a list of messages. | |
600 | + * | |
601 | + * @param <MType> the type of message for the field | |
602 | + * @param <BType> the type of builder for the field | |
603 | + * @param <IType> the common interface for the message and the builder | |
604 | + */ | |
605 | + private static class MessageExternalList< | |
606 | + MType extends GeneratedMessage, | |
607 | + BType extends GeneratedMessage.Builder, | |
608 | + IType extends MessageOrBuilder> | |
609 | + extends AbstractList<MType> implements List<MType> { | |
610 | + | |
611 | + RepeatedFieldBuilder<MType, BType, IType> builder; | |
612 | + | |
613 | + MessageExternalList( | |
614 | + RepeatedFieldBuilder<MType, BType, IType> builder) { | |
615 | + this.builder = builder; | |
616 | + } | |
617 | + | |
618 | + public int size() { | |
619 | + return this.builder.getCount(); | |
620 | + } | |
621 | + | |
622 | + public MType get(int index) { | |
623 | + return builder.getMessage(index); | |
624 | + } | |
625 | + | |
626 | + void incrementModCount() { | |
627 | + modCount++; | |
628 | + } | |
629 | + } | |
630 | + | |
631 | + /** | |
632 | + * Provides a live view of the builder as a list of builders. | |
633 | + * | |
634 | + * @param <MType> the type of message for the field | |
635 | + * @param <BType> the type of builder for the field | |
636 | + * @param <IType> the common interface for the message and the builder | |
637 | + */ | |
638 | + private static class BuilderExternalList< | |
639 | + MType extends GeneratedMessage, | |
640 | + BType extends GeneratedMessage.Builder, | |
641 | + IType extends MessageOrBuilder> | |
642 | + extends AbstractList<BType> implements List<BType> { | |
643 | + | |
644 | + RepeatedFieldBuilder<MType, BType, IType> builder; | |
645 | + | |
646 | + BuilderExternalList( | |
647 | + RepeatedFieldBuilder<MType, BType, IType> builder) { | |
648 | + this.builder = builder; | |
649 | + } | |
650 | + | |
651 | + public int size() { | |
652 | + return this.builder.getCount(); | |
653 | + } | |
654 | + | |
655 | + public BType get(int index) { | |
656 | + return builder.getBuilder(index); | |
657 | + } | |
658 | + | |
659 | + void incrementModCount() { | |
660 | + modCount++; | |
661 | + } | |
662 | + } | |
663 | + | |
664 | + /** | |
665 | + * Provides a live view of the builder as a list of builders. | |
666 | + * | |
667 | + * @param <MType> the type of message for the field | |
668 | + * @param <BType> the type of builder for the field | |
669 | + * @param <IType> the common interface for the message and the builder | |
670 | + */ | |
671 | + private static class MessageOrBuilderExternalList< | |
672 | + MType extends GeneratedMessage, | |
673 | + BType extends GeneratedMessage.Builder, | |
674 | + IType extends MessageOrBuilder> | |
675 | + extends AbstractList<IType> implements List<IType> { | |
676 | + | |
677 | + RepeatedFieldBuilder<MType, BType, IType> builder; | |
678 | + | |
679 | + MessageOrBuilderExternalList( | |
680 | + RepeatedFieldBuilder<MType, BType, IType> builder) { | |
681 | + this.builder = builder; | |
682 | + } | |
683 | + | |
684 | + public int size() { | |
685 | + return this.builder.getCount(); | |
686 | + } | |
687 | + | |
688 | + public IType get(int index) { | |
689 | + return builder.getMessageOrBuilder(index); | |
690 | + } | |
691 | + | |
692 | + void incrementModCount() { | |
693 | + modCount++; | |
694 | + } | |
695 | + } | |
696 | +} |
@@ -0,0 +1,47 @@ | ||
1 | +// Protocol Buffers - Google's data interchange format | |
2 | +// Copyright 2008 Google Inc. All rights reserved. | |
3 | +// http://code.google.com/p/protobuf/ | |
4 | +// | |
5 | +// Redistribution and use in source and binary forms, with or without | |
6 | +// modification, are permitted provided that the following conditions are | |
7 | +// met: | |
8 | +// | |
9 | +// * Redistributions of source code must retain the above copyright | |
10 | +// notice, this list of conditions and the following disclaimer. | |
11 | +// * Redistributions in binary form must reproduce the above | |
12 | +// copyright notice, this list of conditions and the following disclaimer | |
13 | +// in the documentation and/or other materials provided with the | |
14 | +// distribution. | |
15 | +// * Neither the name of Google Inc. nor the names of its | |
16 | +// contributors may be used to endorse or promote products derived from | |
17 | +// this software without specific prior written permission. | |
18 | +// | |
19 | +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
20 | +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
21 | +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
22 | +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
23 | +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
24 | +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
25 | +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
26 | +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
27 | +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
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. | |
30 | + | |
31 | +package com.google.protobuf; | |
32 | + | |
33 | +/** | |
34 | + * Interface for an RPC callback, normally called when an RPC completes. | |
35 | + * {@code ParameterType} is normally the method's response message type. | |
36 | + * | |
37 | + * <p>Starting with version 2.3.0, RPC implementations should not try to build | |
38 | + * on this, but should instead provide code generator plugins which generate | |
39 | + * code specific to the particular RPC implementation. This way the generated | |
40 | + * code can be more appropriate for the implementation in use and can avoid | |
41 | + * unnecessary layers of indirection. | |
42 | + * | |
43 | + * @author kenton@google.com Kenton Varda | |
44 | + */ | |
45 | +public interface RpcCallback<ParameterType> { | |
46 | + void run(ParameterType parameter); | |
47 | +} |
@@ -0,0 +1,71 @@ | ||
1 | +// Protocol Buffers - Google's data interchange format | |
2 | +// Copyright 2008 Google Inc. All rights reserved. | |
3 | +// http://code.google.com/p/protobuf/ | |
4 | +// | |
5 | +// Redistribution and use in source and binary forms, with or without | |
6 | +// modification, are permitted provided that the following conditions are | |
7 | +// met: | |
8 | +// | |
9 | +// * Redistributions of source code must retain the above copyright | |
10 | +// notice, this list of conditions and the following disclaimer. | |
11 | +// * Redistributions in binary form must reproduce the above | |
12 | +// copyright notice, this list of conditions and the following disclaimer | |
13 | +// in the documentation and/or other materials provided with the | |
14 | +// distribution. | |
15 | +// * Neither the name of Google Inc. nor the names of its | |
16 | +// contributors may be used to endorse or promote products derived from | |
17 | +// this software without specific prior written permission. | |
18 | +// | |
19 | +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
20 | +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
21 | +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
22 | +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
23 | +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
24 | +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
25 | +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
26 | +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
27 | +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
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. | |
30 | + | |
31 | +package com.google.protobuf; | |
32 | + | |
33 | +/** | |
34 | + * <p>Abstract interface for an RPC channel. An {@code RpcChannel} represents a | |
35 | + * communication line to a {@link Service} which can be used to call that | |
36 | + * {@link Service}'s methods. The {@link Service} may be running on another | |
37 | + * machine. Normally, you should not call an {@code RpcChannel} directly, but | |
38 | + * instead construct a stub {@link Service} wrapping it. Example: | |
39 | + * | |
40 | + * <pre> | |
41 | + * RpcChannel channel = rpcImpl.newChannel("remotehost.example.com:1234"); | |
42 | + * RpcController controller = rpcImpl.newController(); | |
43 | + * MyService service = MyService.newStub(channel); | |
44 | + * service.myMethod(controller, request, callback); | |
45 | + * </pre> | |
46 | + * | |
47 | + * <p>Starting with version 2.3.0, RPC implementations should not try to build | |
48 | + * on this, but should instead provide code generator plugins which generate | |
49 | + * code specific to the particular RPC implementation. This way the generated | |
50 | + * code can be more appropriate for the implementation in use and can avoid | |
51 | + * unnecessary layers of indirection. | |
52 | + * | |
53 | + * @author kenton@google.com Kenton Varda | |
54 | + */ | |
55 | +public interface RpcChannel { | |
56 | + /** | |
57 | + * Call the given method of the remote service. This method is similar to | |
58 | + * {@code Service.callMethod()} with one important difference: the caller | |
59 | + * decides the types of the {@code Message} objects, not the callee. The | |
60 | + * request may be of any type as long as | |
61 | + * {@code request.getDescriptor() == method.getInputType()}. | |
62 | + * The response passed to the callback will be of the same type as | |
63 | + * {@code responsePrototype} (which must have | |
64 | + * {@code getDescriptor() == method.getOutputType()}). | |
65 | + */ | |
66 | + void callMethod(Descriptors.MethodDescriptor method, | |
67 | + RpcController controller, | |
68 | + Message request, | |
69 | + Message responsePrototype, | |
70 | + RpcCallback<Message> done); | |
71 | +} |
@@ -0,0 +1,118 @@ | ||
1 | +// Protocol Buffers - Google's data interchange format | |
2 | +// Copyright 2008 Google Inc. All rights reserved. | |
3 | +// http://code.google.com/p/protobuf/ | |
4 | +// | |
5 | +// Redistribution and use in source and binary forms, with or without | |
6 | +// modification, are permitted provided that the following conditions are | |
7 | +// met: | |
8 | +// | |
9 | +// * Redistributions of source code must retain the above copyright | |
10 | +// notice, this list of conditions and the following disclaimer. | |
11 | +// * Redistributions in binary form must reproduce the above | |
12 | +// copyright notice, this list of conditions and the following disclaimer | |
13 | +// in the documentation and/or other materials provided with the | |
14 | +// distribution. | |
15 | +// * Neither the name of Google Inc. nor the names of its | |
16 | +// contributors may be used to endorse or promote products derived from | |
17 | +// this software without specific prior written permission. | |
18 | +// | |
19 | +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
20 | +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
21 | +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
22 | +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
23 | +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
24 | +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
25 | +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
26 | +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
27 | +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
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. | |
30 | + | |
31 | +package com.google.protobuf; | |
32 | + | |
33 | +/** | |
34 | + * <p>An {@code RpcController} mediates a single method call. The primary | |
35 | + * purpose of the controller is to provide a way to manipulate settings | |
36 | + * specific to the RPC implementation and to find out about RPC-level errors. | |
37 | + * | |
38 | + * <p>Starting with version 2.3.0, RPC implementations should not try to build | |
39 | + * on this, but should instead provide code generator plugins which generate | |
40 | + * code specific to the particular RPC implementation. This way the generated | |
41 | + * code can be more appropriate for the implementation in use and can avoid | |
42 | + * unnecessary layers of indirection. | |
43 | + * | |
44 | + * <p>The methods provided by the {@code RpcController} interface are intended | |
45 | + * to be a "least common denominator" set of features which we expect all | |
46 | + * implementations to support. Specific implementations may provide more | |
47 | + * advanced features (e.g. deadline propagation). | |
48 | + * | |
49 | + * @author kenton@google.com Kenton Varda | |
50 | + */ | |
51 | +public interface RpcController { | |
52 | + // ----------------------------------------------------------------- | |
53 | + // These calls may be made from the client side only. Their results | |
54 | + // are undefined on the server side (may throw RuntimeExceptions). | |
55 | + | |
56 | + /** | |
57 | + * Resets the RpcController to its initial state so that it may be reused in | |
58 | + * a new call. This can be called from the client side only. It must not | |
59 | + * be called while an RPC is in progress. | |
60 | + */ | |
61 | + void reset(); | |
62 | + | |
63 | + /** | |
64 | + * After a call has finished, returns true if the call failed. The possible | |
65 | + * reasons for failure depend on the RPC implementation. {@code failed()} | |
66 | + * most only be called on the client side, and must not be called before a | |
67 | + * call has finished. | |
68 | + */ | |
69 | + boolean failed(); | |
70 | + | |
71 | + /** | |
72 | + * If {@code failed()} is {@code true}, returns a human-readable description | |
73 | + * of the error. | |
74 | + */ | |
75 | + String errorText(); | |
76 | + | |
77 | + /** | |
78 | + * Advises the RPC system that the caller desires that the RPC call be | |
79 | + * canceled. The RPC system may cancel it immediately, may wait awhile and | |
80 | + * then cancel it, or may not even cancel the call at all. If the call is | |
81 | + * canceled, the "done" callback will still be called and the RpcController | |
82 | + * will indicate that the call failed at that time. | |
83 | + */ | |
84 | + void startCancel(); | |
85 | + | |
86 | + // ----------------------------------------------------------------- | |
87 | + // These calls may be made from the server side only. Their results | |
88 | + // are undefined on the client side (may throw RuntimeExceptions). | |
89 | + | |
90 | + /** | |
91 | + * Causes {@code failed()} to return true on the client side. {@code reason} | |
92 | + * will be incorporated into the message returned by {@code errorText()}. | |
93 | + * If you find you need to return machine-readable information about | |
94 | + * failures, you should incorporate it into your response protocol buffer | |
95 | + * and should NOT call {@code setFailed()}. | |
96 | + */ | |
97 | + void setFailed(String reason); | |
98 | + | |
99 | + /** | |
100 | + * If {@code true}, indicates that the client canceled the RPC, so the server | |
101 | + * may as well give up on replying to it. This method must be called on the | |
102 | + * server side only. The server should still call the final "done" callback. | |
103 | + */ | |
104 | + boolean isCanceled(); | |
105 | + | |
106 | + /** | |
107 | + * Asks that the given callback be called when the RPC is canceled. The | |
108 | + * parameter passed to the callback will always be {@code null}. The | |
109 | + * callback will always be called exactly once. If the RPC completes without | |
110 | + * being canceled, the callback will be called after completion. If the RPC | |
111 | + * has already been canceled when NotifyOnCancel() is called, the callback | |
112 | + * will be called immediately. | |
113 | + * | |
114 | + * <p>{@code notifyOnCancel()} must be called no more than once per request. | |
115 | + * It must be called on the server side only. | |
116 | + */ | |
117 | + void notifyOnCancel(RpcCallback<Object> callback); | |
118 | +} |
@@ -0,0 +1,135 @@ | ||
1 | +// Protocol Buffers - Google's data interchange format | |
2 | +// Copyright 2008 Google Inc. All rights reserved. | |
3 | +// http://code.google.com/p/protobuf/ | |
4 | +// | |
5 | +// Redistribution and use in source and binary forms, with or without | |
6 | +// modification, are permitted provided that the following conditions are | |
7 | +// met: | |
8 | +// | |
9 | +// * Redistributions of source code must retain the above copyright | |
10 | +// notice, this list of conditions and the following disclaimer. | |
11 | +// * Redistributions in binary form must reproduce the above | |
12 | +// copyright notice, this list of conditions and the following disclaimer | |
13 | +// in the documentation and/or other materials provided with the | |
14 | +// distribution. | |
15 | +// * Neither the name of Google Inc. nor the names of its | |
16 | +// contributors may be used to endorse or promote products derived from | |
17 | +// this software without specific prior written permission. | |
18 | +// | |
19 | +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
20 | +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
21 | +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
22 | +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
23 | +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
24 | +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
25 | +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
26 | +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
27 | +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
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. | |
30 | + | |
31 | +package com.google.protobuf; | |
32 | + | |
33 | +/** | |
34 | + * Grab-bag of utility functions useful when dealing with RPCs. | |
35 | + * | |
36 | + * @author kenton@google.com Kenton Varda | |
37 | + */ | |
38 | +public final class RpcUtil { | |
39 | + private RpcUtil() {} | |
40 | + | |
41 | + /** | |
42 | + * Take an {@code RpcCallback<Message>} and convert it to an | |
43 | + * {@code RpcCallback} accepting a specific message type. This is always | |
44 | + * type-safe (parameter type contravariance). | |
45 | + */ | |
46 | + @SuppressWarnings("unchecked") | |
47 | + public static <Type extends Message> RpcCallback<Type> | |
48 | + specializeCallback(final RpcCallback<Message> originalCallback) { | |
49 | + return (RpcCallback<Type>)originalCallback; | |
50 | + // The above cast works, but only due to technical details of the Java | |
51 | + // implementation. A more theoretically correct -- but less efficient -- | |
52 | + // implementation would be as follows: | |
53 | + // return new RpcCallback<Type>() { | |
54 | + // public void run(Type parameter) { | |
55 | + // originalCallback.run(parameter); | |
56 | + // } | |
57 | + // }; | |
58 | + } | |
59 | + | |
60 | + /** | |
61 | + * Take an {@code RpcCallback} accepting a specific message type and convert | |
62 | + * it to an {@code RpcCallback<Message>}. The generalized callback will | |
63 | + * accept any message object which has the same descriptor, and will convert | |
64 | + * it to the correct class before calling the original callback. However, | |
65 | + * if the generalized callback is given a message with a different descriptor, | |
66 | + * an exception will be thrown. | |
67 | + */ | |
68 | + public static <Type extends Message> | |
69 | + RpcCallback<Message> generalizeCallback( | |
70 | + final RpcCallback<Type> originalCallback, | |
71 | + final Class<Type> originalClass, | |
72 | + final Type defaultInstance) { | |
73 | + return new RpcCallback<Message>() { | |
74 | + public void run(final Message parameter) { | |
75 | + Type typedParameter; | |
76 | + try { | |
77 | + typedParameter = originalClass.cast(parameter); | |
78 | + } catch (ClassCastException ignored) { | |
79 | + typedParameter = copyAsType(defaultInstance, parameter); | |
80 | + } | |
81 | + originalCallback.run(typedParameter); | |
82 | + } | |
83 | + }; | |
84 | + } | |
85 | + | |
86 | + /** | |
87 | + * Creates a new message of type "Type" which is a copy of "source". "source" | |
88 | + * must have the same descriptor but may be a different class (e.g. | |
89 | + * DynamicMessage). | |
90 | + */ | |
91 | + @SuppressWarnings("unchecked") | |
92 | + private static <Type extends Message> Type copyAsType( | |
93 | + final Type typeDefaultInstance, final Message source) { | |
94 | + return (Type)typeDefaultInstance.newBuilderForType() | |
95 | + .mergeFrom(source) | |
96 | + .build(); | |
97 | + } | |
98 | + | |
99 | + /** | |
100 | + * Creates a callback which can only be called once. This may be useful for | |
101 | + * security, when passing a callback to untrusted code: most callbacks do | |
102 | + * not expect to be called more than once, so doing so may expose bugs if it | |
103 | + * is not prevented. | |
104 | + */ | |
105 | + public static <ParameterType> | |
106 | + RpcCallback<ParameterType> newOneTimeCallback( | |
107 | + final RpcCallback<ParameterType> originalCallback) { | |
108 | + return new RpcCallback<ParameterType>() { | |
109 | + private boolean alreadyCalled = false; | |
110 | + | |
111 | + public void run(final ParameterType parameter) { | |
112 | + synchronized(this) { | |
113 | + if (alreadyCalled) { | |
114 | + throw new AlreadyCalledException(); | |
115 | + } | |
116 | + alreadyCalled = true; | |
117 | + } | |
118 | + | |
119 | + originalCallback.run(parameter); | |
120 | + } | |
121 | + }; | |
122 | + } | |
123 | + | |
124 | + /** | |
125 | + * Exception thrown when a one-time callback is called more than once. | |
126 | + */ | |
127 | + public static final class AlreadyCalledException extends RuntimeException { | |
128 | + private static final long serialVersionUID = 5469741279507848266L; | |
129 | + | |
130 | + public AlreadyCalledException() { | |
131 | + super("This RpcCallback was already called and cannot be called " + | |
132 | + "multiple times."); | |
133 | + } | |
134 | + } | |
135 | +} |
@@ -0,0 +1,117 @@ | ||
1 | +// Protocol Buffers - Google's data interchange format | |
2 | +// Copyright 2008 Google Inc. All rights reserved. | |
3 | +// http://code.google.com/p/protobuf/ | |
4 | +// | |
5 | +// Redistribution and use in source and binary forms, with or without | |
6 | +// modification, are permitted provided that the following conditions are | |
7 | +// met: | |
8 | +// | |
9 | +// * Redistributions of source code must retain the above copyright | |
10 | +// notice, this list of conditions and the following disclaimer. | |
11 | +// * Redistributions in binary form must reproduce the above | |
12 | +// copyright notice, this list of conditions and the following disclaimer | |
13 | +// in the documentation and/or other materials provided with the | |
14 | +// distribution. | |
15 | +// * Neither the name of Google Inc. nor the names of its | |
16 | +// contributors may be used to endorse or promote products derived from | |
17 | +// this software without specific prior written permission. | |
18 | +// | |
19 | +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
20 | +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
21 | +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
22 | +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
23 | +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
24 | +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
25 | +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
26 | +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
27 | +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
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. | |
30 | + | |
31 | +package com.google.protobuf; | |
32 | + | |
33 | +/** | |
34 | + * Abstract base interface for protocol-buffer-based RPC services. Services | |
35 | + * themselves are abstract classes (implemented either by servers or as | |
36 | + * stubs), but they subclass this base interface. The methods of this | |
37 | + * interface can be used to call the methods of the service without knowing | |
38 | + * its exact type at compile time (analogous to the Message interface). | |
39 | + * | |
40 | + * <p>Starting with version 2.3.0, RPC implementations should not try to build | |
41 | + * on this, but should instead provide code generator plugins which generate | |
42 | + * code specific to the particular RPC implementation. This way the generated | |
43 | + * code can be more appropriate for the implementation in use and can avoid | |
44 | + * unnecessary layers of indirection. | |
45 | + * | |
46 | + * @author kenton@google.com Kenton Varda | |
47 | + */ | |
48 | +public interface Service { | |
49 | + /** | |
50 | + * Get the {@code ServiceDescriptor} describing this service and its methods. | |
51 | + */ | |
52 | + Descriptors.ServiceDescriptor getDescriptorForType(); | |
53 | + | |
54 | + /** | |
55 | + * <p>Call a method of the service specified by MethodDescriptor. This is | |
56 | + * normally implemented as a simple {@code switch()} that calls the standard | |
57 | + * definitions of the service's methods. | |
58 | + * | |
59 | + * <p>Preconditions: | |
60 | + * <ul> | |
61 | + * <li>{@code method.getService() == getDescriptorForType()} | |
62 | + * <li>{@code request} is of the exact same class as the object returned by | |
63 | + * {@code getRequestPrototype(method)}. | |
64 | + * <li>{@code controller} is of the correct type for the RPC implementation | |
65 | + * being used by this Service. For stubs, the "correct type" depends | |
66 | + * on the RpcChannel which the stub is using. Server-side Service | |
67 | + * implementations are expected to accept whatever type of | |
68 | + * {@code RpcController} the server-side RPC implementation uses. | |
69 | + * </ul> | |
70 | + * | |
71 | + * <p>Postconditions: | |
72 | + * <ul> | |
73 | + * <li>{@code done} will be called when the method is complete. This may be | |
74 | + * before {@code callMethod()} returns or it may be at some point in | |
75 | + * the future. | |
76 | + * <li>The parameter to {@code done} is the response. It must be of the | |
77 | + * exact same type as would be returned by | |
78 | + * {@code getResponsePrototype(method)}. | |
79 | + * <li>If the RPC failed, the parameter to {@code done} will be | |
80 | + * {@code null}. Further details about the failure can be found by | |
81 | + * querying {@code controller}. | |
82 | + * </ul> | |
83 | + */ | |
84 | + void callMethod(Descriptors.MethodDescriptor method, | |
85 | + RpcController controller, | |
86 | + Message request, | |
87 | + RpcCallback<Message> done); | |
88 | + | |
89 | + /** | |
90 | + * <p>{@code callMethod()} requires that the request passed in is of a | |
91 | + * particular subclass of {@code Message}. {@code getRequestPrototype()} | |
92 | + * gets the default instances of this type for a given method. You can then | |
93 | + * call {@code Message.newBuilderForType()} on this instance to | |
94 | + * construct a builder to build an object which you can then pass to | |
95 | + * {@code callMethod()}. | |
96 | + * | |
97 | + * <p>Example: | |
98 | + * <pre> | |
99 | + * MethodDescriptor method = | |
100 | + * service.getDescriptorForType().findMethodByName("Foo"); | |
101 | + * Message request = | |
102 | + * stub.getRequestPrototype(method).newBuilderForType() | |
103 | + * .mergeFrom(input).build(); | |
104 | + * service.callMethod(method, request, callback); | |
105 | + * </pre> | |
106 | + */ | |
107 | + Message getRequestPrototype(Descriptors.MethodDescriptor method); | |
108 | + | |
109 | + /** | |
110 | + * Like {@code getRequestPrototype()}, but gets a prototype of the response | |
111 | + * message. {@code getResponsePrototype()} is generally not needed because | |
112 | + * the {@code Service} implementation constructs the response message itself, | |
113 | + * but it may be useful in some cases to know ahead of time what type of | |
114 | + * object will be returned. | |
115 | + */ | |
116 | + Message getResponsePrototype(Descriptors.MethodDescriptor method); | |
117 | +} |
@@ -0,0 +1,52 @@ | ||
1 | +// Protocol Buffers - Google's data interchange format | |
2 | +// Copyright 2008 Google Inc. All rights reserved. | |
3 | +// http://code.google.com/p/protobuf/ | |
4 | +// | |
5 | +// Redistribution and use in source and binary forms, with or without | |
6 | +// modification, are permitted provided that the following conditions are | |
7 | +// met: | |
8 | +// | |
9 | +// * Redistributions of source code must retain the above copyright | |
10 | +// notice, this list of conditions and the following disclaimer. | |
11 | +// * Redistributions in binary form must reproduce the above | |
12 | +// copyright notice, this list of conditions and the following disclaimer | |
13 | +// in the documentation and/or other materials provided with the | |
14 | +// distribution. | |
15 | +// * Neither the name of Google Inc. nor the names of its | |
16 | +// contributors may be used to endorse or promote products derived from | |
17 | +// this software without specific prior written permission. | |
18 | +// | |
19 | +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
20 | +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
21 | +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
22 | +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
23 | +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
24 | +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
25 | +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
26 | +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
27 | +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
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. | |
30 | + | |
31 | +package com.google.protobuf; | |
32 | + | |
33 | +/** | |
34 | + * Thrown by blocking RPC methods when a failure occurs. | |
35 | + * | |
36 | + * @author cpovirk@google.com (Chris Povirk) | |
37 | + */ | |
38 | +public class ServiceException extends Exception { | |
39 | + private static final long serialVersionUID = -1219262335729891920L; | |
40 | + | |
41 | + public ServiceException(final String message) { | |
42 | + super(message); | |
43 | + } | |
44 | + | |
45 | + public ServiceException(final Throwable cause) { | |
46 | + super(cause); | |
47 | + } | |
48 | + | |
49 | + public ServiceException(final String message, final Throwable cause) { | |
50 | + super(message, cause); | |
51 | + } | |
52 | +} |
@@ -0,0 +1,241 @@ | ||
1 | +// Protocol Buffers - Google's data interchange format | |
2 | +// Copyright 2008 Google Inc. All rights reserved. | |
3 | +// http://code.google.com/p/protobuf/ | |
4 | +// | |
5 | +// Redistribution and use in source and binary forms, with or without | |
6 | +// modification, are permitted provided that the following conditions are | |
7 | +// met: | |
8 | +// | |
9 | +// * Redistributions of source code must retain the above copyright | |
10 | +// notice, this list of conditions and the following disclaimer. | |
11 | +// * Redistributions in binary form must reproduce the above | |
12 | +// copyright notice, this list of conditions and the following disclaimer | |
13 | +// in the documentation and/or other materials provided with the | |
14 | +// distribution. | |
15 | +// * Neither the name of Google Inc. nor the names of its | |
16 | +// contributors may be used to endorse or promote products derived from | |
17 | +// this software without specific prior written permission. | |
18 | +// | |
19 | +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
20 | +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
21 | +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
22 | +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
23 | +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
24 | +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
25 | +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
26 | +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
27 | +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
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. | |
30 | + | |
31 | +package com.google.protobuf; | |
32 | + | |
33 | +/** | |
34 | + * <code>SingleFieldBuilder</code> implements a structure that a protocol | |
35 | + * message uses to hold a single field of another protocol message. It supports | |
36 | + * the classical use case of setting an immutable {@link Message} as the value | |
37 | + * of the field and is highly optimized around this. | |
38 | + * <br> | |
39 | + * It also supports the additional use case of setting a {@link Message.Builder} | |
40 | + * as the field and deferring conversion of that <code>Builder</code> | |
41 | + * to an immutable <code>Message</code>. In this way, it's possible to maintain | |
42 | + * a tree of <code>Builder</code>'s that acts as a fully read/write data | |
43 | + * structure. | |
44 | + * <br> | |
45 | + * Logically, one can think of a tree of builders as converting the entire tree | |
46 | + * to messages when build is called on the root or when any method is called | |
47 | + * that desires a Message instead of a Builder. In terms of the implementation, | |
48 | + * the <code>SingleFieldBuilder</code> and <code>RepeatedFieldBuilder</code> | |
49 | + * classes cache messages that were created so that messages only need to be | |
50 | + * created when some change occured in its builder or a builder for one of its | |
51 | + * descendants. | |
52 | + * | |
53 | + * @param <MType> the type of message for the field | |
54 | + * @param <BType> the type of builder for the field | |
55 | + * @param <IType> the common interface for the message and the builder | |
56 | + * | |
57 | + * @author jonp@google.com (Jon Perlow) | |
58 | + */ | |
59 | +public class SingleFieldBuilder | |
60 | + <MType extends GeneratedMessage, | |
61 | + BType extends GeneratedMessage.Builder, | |
62 | + IType extends MessageOrBuilder> | |
63 | + implements GeneratedMessage.BuilderParent { | |
64 | + | |
65 | + // Parent to send changes to. | |
66 | + private GeneratedMessage.BuilderParent parent; | |
67 | + | |
68 | + // Invariant: one of builder or message fields must be non-null. | |
69 | + | |
70 | + // If set, this is the case where we are backed by a builder. In this case, | |
71 | + // message field represents a cached message for the builder (or null if | |
72 | + // there is no cached message). | |
73 | + private BType builder; | |
74 | + | |
75 | + // If builder is non-null, this represents a cached message from the builder. | |
76 | + // If builder is null, this is the authoritative message for the field. | |
77 | + private MType message; | |
78 | + | |
79 | + // Indicates that we've built a message and so we are now obligated | |
80 | + // to dispatch dirty invalidations. See GeneratedMessage.BuilderListener. | |
81 | + private boolean isClean; | |
82 | + | |
83 | + public SingleFieldBuilder( | |
84 | + MType message, | |
85 | + GeneratedMessage.BuilderParent parent, | |
86 | + boolean isClean) { | |
87 | + if (message == null) { | |
88 | + throw new NullPointerException(); | |
89 | + } | |
90 | + this.message = message; | |
91 | + this.parent = parent; | |
92 | + this.isClean = isClean; | |
93 | + } | |
94 | + | |
95 | + public void dispose() { | |
96 | + // Null out parent so we stop sending it invalidations. | |
97 | + parent = null; | |
98 | + } | |
99 | + | |
100 | + /** | |
101 | + * Get the message for the field. If the message is currently stored | |
102 | + * as a <code>Builder</code>, it is converted to a <code>Message</code> by | |
103 | + * calling {@link Message.Builder#buildPartial} on it. If no message has | |
104 | + * been set, returns the default instance of the message. | |
105 | + * | |
106 | + * @return the message for the field | |
107 | + */ | |
108 | + @SuppressWarnings("unchecked") | |
109 | + public MType getMessage() { | |
110 | + if (message == null) { | |
111 | + // If message is null, the invariant is that we must be have a builder. | |
112 | + message = (MType) builder.buildPartial(); | |
113 | + } | |
114 | + return message; | |
115 | + } | |
116 | + | |
117 | + /** | |
118 | + * Builds the message and returns it. | |
119 | + * | |
120 | + * @return the message | |
121 | + */ | |
122 | + public MType build() { | |
123 | + // Now that build has been called, we are required to dispatch | |
124 | + // invalidations. | |
125 | + isClean = true; | |
126 | + return getMessage(); | |
127 | + } | |
128 | + | |
129 | + /** | |
130 | + * Gets a builder for the field. If no builder has been created yet, a | |
131 | + * builder is created on demand by calling {@link Message#toBuilder}. | |
132 | + * | |
133 | + * @return The builder for the field | |
134 | + */ | |
135 | + @SuppressWarnings("unchecked") | |
136 | + public BType getBuilder() { | |
137 | + if (builder == null) { | |
138 | + // builder.mergeFrom() on a fresh builder | |
139 | + // does not create any sub-objects with independent clean/dirty states, | |
140 | + // therefore setting the builder itself to clean without actually calling | |
141 | + // build() cannot break any invariants. | |
142 | + builder = (BType) message.newBuilderForType(this); | |
143 | + builder.mergeFrom(message); // no-op if message is the default message | |
144 | + builder.markClean(); | |
145 | + } | |
146 | + return builder; | |
147 | + } | |
148 | + | |
149 | + /** | |
150 | + * Gets the base class interface for the field. This may either be a builder | |
151 | + * or a message. It will return whatever is more efficient. | |
152 | + * | |
153 | + * @return the message or builder for the field as the base class interface | |
154 | + */ | |
155 | + @SuppressWarnings("unchecked") | |
156 | + public IType getMessageOrBuilder() { | |
157 | + if (builder != null) { | |
158 | + return (IType) builder; | |
159 | + } else { | |
160 | + return (IType) message; | |
161 | + } | |
162 | + } | |
163 | + | |
164 | + /** | |
165 | + * Sets a message for the field replacing any existing value. | |
166 | + * | |
167 | + * @param message the message to set | |
168 | + * @return the builder | |
169 | + */ | |
170 | + public SingleFieldBuilder<MType, BType, IType> setMessage( | |
171 | + MType message) { | |
172 | + if (message == null) { | |
173 | + throw new NullPointerException(); | |
174 | + } | |
175 | + this.message = message; | |
176 | + if (builder != null) { | |
177 | + builder.dispose(); | |
178 | + builder = null; | |
179 | + } | |
180 | + onChanged(); | |
181 | + return this; | |
182 | + } | |
183 | + | |
184 | + /** | |
185 | + * Merges the field from another field. | |
186 | + * | |
187 | + * @param value the value to merge from | |
188 | + * @return the builder | |
189 | + */ | |
190 | + public SingleFieldBuilder<MType, BType, IType> mergeFrom( | |
191 | + MType value) { | |
192 | + if (builder == null && message == message.getDefaultInstanceForType()) { | |
193 | + message = value; | |
194 | + } else { | |
195 | + getBuilder().mergeFrom(value); | |
196 | + } | |
197 | + onChanged(); | |
198 | + return this; | |
199 | + } | |
200 | + | |
201 | + /** | |
202 | + * Clears the value of the field. | |
203 | + * | |
204 | + * @return the builder | |
205 | + */ | |
206 | + @SuppressWarnings("unchecked") | |
207 | + public SingleFieldBuilder<MType, BType, IType> clear() { | |
208 | + message = (MType) (message != null ? | |
209 | + message.getDefaultInstanceForType() : | |
210 | + builder.getDefaultInstanceForType()); | |
211 | + if (builder != null) { | |
212 | + builder.dispose(); | |
213 | + builder = null; | |
214 | + } | |
215 | + onChanged(); | |
216 | + return this; | |
217 | + } | |
218 | + | |
219 | + /** | |
220 | + * Called when a the builder or one of its nested children has changed | |
221 | + * and any parent should be notified of its invalidation. | |
222 | + */ | |
223 | + private void onChanged() { | |
224 | + // If builder is null, this is the case where onChanged is being called | |
225 | + // from setMessage or clear. | |
226 | + if (builder != null) { | |
227 | + message = null; | |
228 | + } | |
229 | + if (isClean && parent != null) { | |
230 | + parent.markDirty(); | |
231 | + | |
232 | + // Don't keep dispatching invalidations until build is called again. | |
233 | + isClean = false; | |
234 | + } | |
235 | + } | |
236 | + | |
237 | + //@Override (Java 1.6 override semantics, but we must support 1.5) | |
238 | + public void markDirty() { | |
239 | + onChanged(); | |
240 | + } | |
241 | +} |
@@ -0,0 +1,618 @@ | ||
1 | +// Protocol Buffers - Google's data interchange format | |
2 | +// Copyright 2008 Google Inc. All rights reserved. | |
3 | +// http://code.google.com/p/protobuf/ | |
4 | +// | |
5 | +// Redistribution and use in source and binary forms, with or without | |
6 | +// modification, are permitted provided that the following conditions are | |
7 | +// met: | |
8 | +// | |
9 | +// * Redistributions of source code must retain the above copyright | |
10 | +// notice, this list of conditions and the following disclaimer. | |
11 | +// * Redistributions in binary form must reproduce the above | |
12 | +// copyright notice, this list of conditions and the following disclaimer | |
13 | +// in the documentation and/or other materials provided with the | |
14 | +// distribution. | |
15 | +// * Neither the name of Google Inc. nor the names of its | |
16 | +// contributors may be used to endorse or promote products derived from | |
17 | +// this software without specific prior written permission. | |
18 | +// | |
19 | +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
20 | +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
21 | +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
22 | +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
23 | +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
24 | +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
25 | +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
26 | +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
27 | +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
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. | |
30 | + | |
31 | +package com.google.protobuf; | |
32 | + | |
33 | +import java.util.AbstractMap; | |
34 | +import java.util.AbstractSet; | |
35 | +import java.util.ArrayList; | |
36 | +import java.util.Collections; | |
37 | +import java.util.Iterator; | |
38 | +import java.util.TreeMap; | |
39 | +import java.util.List; | |
40 | +import java.util.Map; | |
41 | +import java.util.NoSuchElementException; | |
42 | +import java.util.Set; | |
43 | +import java.util.SortedMap; | |
44 | + | |
45 | +/** | |
46 | + * A custom map implementation from FieldDescriptor to Object optimized to | |
47 | + * minimize the number of memory allocations for instances with a small number | |
48 | + * of mappings. The implementation stores the first {@code k} mappings in an | |
49 | + * array for a configurable value of {@code k}, allowing direct access to the | |
50 | + * corresponding {@code Entry}s without the need to create an Iterator. The | |
51 | + * remaining entries are stored in an overflow map. Iteration over the entries | |
52 | + * in the map should be done as follows: | |
53 | + * | |
54 | + * <pre> | |
55 | + * for (int i = 0; i < fieldMap.getNumArrayEntries(); i++) { | |
56 | + * process(fieldMap.getArrayEntryAt(i)); | |
57 | + * } | |
58 | + * for (Map.Entry<K, V> entry : fieldMap.getOverflowEntries()) { | |
59 | + * process(entry); | |
60 | + * } | |
61 | + * </pre> | |
62 | + * | |
63 | + * The resulting iteration is in order of ascending field tag number. The | |
64 | + * object returned by {@link #entrySet()} adheres to the same contract but is | |
65 | + * less efficient as it necessarily involves creating an object for iteration. | |
66 | + * <p> | |
67 | + * The tradeoff for this memory efficiency is that the worst case running time | |
68 | + * of the {@code put()} operation is {@code O(k + lg n)}, which happens when | |
69 | + * entries are added in descending order. {@code k} should be chosen such that | |
70 | + * it covers enough common cases without adversely affecting larger maps. In | |
71 | + * practice, the worst case scenario does not happen for extensions because | |
72 | + * extension fields are serialized and deserialized in order of ascending tag | |
73 | + * number, but the worst case scenario can happen for DynamicMessages. | |
74 | + * <p> | |
75 | + * The running time for all other operations is similar to that of | |
76 | + * {@code TreeMap}. | |
77 | + * <p> | |
78 | + * Instances are not thread-safe until {@link #makeImmutable()} is called, | |
79 | + * after which any modifying operation will result in an | |
80 | + * {@link UnsupportedOperationException}. | |
81 | + * | |
82 | + * @author darick@google.com Darick Tong | |
83 | + */ | |
84 | +// This class is final for all intents and purposes because the constructor is | |
85 | +// private. However, the FieldDescriptor-specific logic is encapsulated in | |
86 | +// a subclass to aid testability of the core logic. | |
87 | +class SmallSortedMap<K extends Comparable<K>, V> extends AbstractMap<K, V> { | |
88 | + | |
89 | + /** | |
90 | + * Creates a new instance for mapping FieldDescriptors to their values. | |
91 | + * The {@link #makeImmutable()} implementation will convert the List values | |
92 | + * of any repeated fields to unmodifiable lists. | |
93 | + * | |
94 | + * @param arraySize The size of the entry array containing the | |
95 | + * lexicographically smallest mappings. | |
96 | + */ | |
97 | + static <FieldDescriptorType extends | |
98 | + FieldSet.FieldDescriptorLite<FieldDescriptorType>> | |
99 | + SmallSortedMap<FieldDescriptorType, Object> newFieldMap(int arraySize) { | |
100 | + return new SmallSortedMap<FieldDescriptorType, Object>(arraySize) { | |
101 | + @Override | |
102 | + @SuppressWarnings("unchecked") | |
103 | + public void makeImmutable() { | |
104 | + if (!isImmutable()) { | |
105 | + for (int i = 0; i < getNumArrayEntries(); i++) { | |
106 | + final Map.Entry<FieldDescriptorType, Object> entry = | |
107 | + getArrayEntryAt(i); | |
108 | + if (entry.getKey().isRepeated()) { | |
109 | + final List value = (List) entry.getValue(); | |
110 | + entry.setValue(Collections.unmodifiableList(value)); | |
111 | + } | |
112 | + } | |
113 | + for (Map.Entry<FieldDescriptorType, Object> entry : | |
114 | + getOverflowEntries()) { | |
115 | + if (entry.getKey().isRepeated()) { | |
116 | + final List value = (List) entry.getValue(); | |
117 | + entry.setValue(Collections.unmodifiableList(value)); | |
118 | + } | |
119 | + } | |
120 | + } | |
121 | + super.makeImmutable(); | |
122 | + } | |
123 | + }; | |
124 | + } | |
125 | + | |
126 | + /** | |
127 | + * Creates a new instance for testing. | |
128 | + * | |
129 | + * @param arraySize The size of the entry array containing the | |
130 | + * lexicographically smallest mappings. | |
131 | + */ | |
132 | + static <K extends Comparable<K>, V> SmallSortedMap<K, V> newInstanceForTest( | |
133 | + int arraySize) { | |
134 | + return new SmallSortedMap<K, V>(arraySize); | |
135 | + } | |
136 | + | |
137 | + private final int maxArraySize; | |
138 | + // The "entry array" is actually a List because generic arrays are not | |
139 | + // allowed. ArrayList also nicely handles the entry shifting on inserts and | |
140 | + // removes. | |
141 | + private List<Entry> entryList; | |
142 | + private Map<K, V> overflowEntries; | |
143 | + private boolean isImmutable; | |
144 | + // The EntrySet is a stateless view of the Map. It's initialized the first | |
145 | + // time it is requested and reused henceforth. | |
146 | + private volatile EntrySet lazyEntrySet; | |
147 | + | |
148 | + /** | |
149 | + * @code arraySize Size of the array in which the lexicographically smallest | |
150 | + * mappings are stored. (i.e. the {@code k} referred to in the class | |
151 | + * documentation). | |
152 | + */ | |
153 | + private SmallSortedMap(int arraySize) { | |
154 | + this.maxArraySize = arraySize; | |
155 | + this.entryList = Collections.emptyList(); | |
156 | + this.overflowEntries = Collections.emptyMap(); | |
157 | + } | |
158 | + | |
159 | + /** Make this map immutable from this point forward. */ | |
160 | + public void makeImmutable() { | |
161 | + if (!isImmutable) { | |
162 | + // Note: There's no need to wrap the entryList in an unmodifiableList | |
163 | + // because none of the list's accessors are exposed. The iterator() of | |
164 | + // overflowEntries, on the other hand, is exposed so it must be made | |
165 | + // unmodifiable. | |
166 | + overflowEntries = overflowEntries.isEmpty() ? | |
167 | + Collections.<K, V>emptyMap() : | |
168 | + Collections.unmodifiableMap(overflowEntries); | |
169 | + isImmutable = true; | |
170 | + } | |
171 | + } | |
172 | + | |
173 | + /** @return Whether {@link #makeImmutable()} has been called. */ | |
174 | + public boolean isImmutable() { | |
175 | + return isImmutable; | |
176 | + } | |
177 | + | |
178 | + /** @return The number of entries in the entry array. */ | |
179 | + public int getNumArrayEntries() { | |
180 | + return entryList.size(); | |
181 | + } | |
182 | + | |
183 | + /** @return The array entry at the given {@code index}. */ | |
184 | + public Map.Entry<K, V> getArrayEntryAt(int index) { | |
185 | + return entryList.get(index); | |
186 | + } | |
187 | + | |
188 | + /** @return There number of overflow entries. */ | |
189 | + public int getNumOverflowEntries() { | |
190 | + return overflowEntries.size(); | |
191 | + } | |
192 | + | |
193 | + /** @return An iterable over the overflow entries. */ | |
194 | + public Iterable<Map.Entry<K, V>> getOverflowEntries() { | |
195 | + return overflowEntries.isEmpty() ? | |
196 | + EmptySet.<Map.Entry<K, V>>iterable() : | |
197 | + overflowEntries.entrySet(); | |
198 | + } | |
199 | + | |
200 | + @Override | |
201 | + public int size() { | |
202 | + return entryList.size() + overflowEntries.size(); | |
203 | + } | |
204 | + | |
205 | + /** | |
206 | + * The implementation throws a {@code ClassCastException} if o is not an | |
207 | + * object of type {@code K}. | |
208 | + * | |
209 | + * {@inheritDoc} | |
210 | + */ | |
211 | + @Override | |
212 | + public boolean containsKey(Object o) { | |
213 | + @SuppressWarnings("unchecked") | |
214 | + final K key = (K) o; | |
215 | + return binarySearchInArray(key) >= 0 || overflowEntries.containsKey(key); | |
216 | + } | |
217 | + | |
218 | + /** | |
219 | + * The implementation throws a {@code ClassCastException} if o is not an | |
220 | + * object of type {@code K}. | |
221 | + * | |
222 | + * {@inheritDoc} | |
223 | + */ | |
224 | + @Override | |
225 | + public V get(Object o) { | |
226 | + @SuppressWarnings("unchecked") | |
227 | + final K key = (K) o; | |
228 | + final int index = binarySearchInArray(key); | |
229 | + if (index >= 0) { | |
230 | + return entryList.get(index).getValue(); | |
231 | + } | |
232 | + return overflowEntries.get(key); | |
233 | + } | |
234 | + | |
235 | + @Override | |
236 | + public V put(K key, V value) { | |
237 | + checkMutable(); | |
238 | + final int index = binarySearchInArray(key); | |
239 | + if (index >= 0) { | |
240 | + // Replace existing array entry. | |
241 | + return entryList.get(index).setValue(value); | |
242 | + } | |
243 | + ensureEntryArrayMutable(); | |
244 | + final int insertionPoint = -(index + 1); | |
245 | + if (insertionPoint >= maxArraySize) { | |
246 | + // Put directly in overflow. | |
247 | + return getOverflowEntriesMutable().put(key, value); | |
248 | + } | |
249 | + // Insert new Entry in array. | |
250 | + if (entryList.size() == maxArraySize) { | |
251 | + // Shift the last array entry into overflow. | |
252 | + final Entry lastEntryInArray = entryList.remove(maxArraySize - 1); | |
253 | + getOverflowEntriesMutable().put(lastEntryInArray.getKey(), | |
254 | + lastEntryInArray.getValue()); | |
255 | + } | |
256 | + entryList.add(insertionPoint, new Entry(key, value)); | |
257 | + return null; | |
258 | + } | |
259 | + | |
260 | + @Override | |
261 | + public void clear() { | |
262 | + checkMutable(); | |
263 | + if (!entryList.isEmpty()) { | |
264 | + entryList.clear(); | |
265 | + } | |
266 | + if (!overflowEntries.isEmpty()) { | |
267 | + overflowEntries.clear(); | |
268 | + } | |
269 | + } | |
270 | + | |
271 | + /** | |
272 | + * The implementation throws a {@code ClassCastException} if o is not an | |
273 | + * object of type {@code K}. | |
274 | + * | |
275 | + * {@inheritDoc} | |
276 | + */ | |
277 | + @Override | |
278 | + public V remove(Object o) { | |
279 | + checkMutable(); | |
280 | + @SuppressWarnings("unchecked") | |
281 | + final K key = (K) o; | |
282 | + final int index = binarySearchInArray(key); | |
283 | + if (index >= 0) { | |
284 | + return removeArrayEntryAt(index); | |
285 | + } | |
286 | + // overflowEntries might be Collections.unmodifiableMap(), so only | |
287 | + // call remove() if it is non-empty. | |
288 | + if (overflowEntries.isEmpty()) { | |
289 | + return null; | |
290 | + } else { | |
291 | + return overflowEntries.remove(key); | |
292 | + } | |
293 | + } | |
294 | + | |
295 | + private V removeArrayEntryAt(int index) { | |
296 | + checkMutable(); | |
297 | + final V removed = entryList.remove(index).getValue(); | |
298 | + if (!overflowEntries.isEmpty()) { | |
299 | + // Shift the first entry in the overflow to be the last entry in the | |
300 | + // array. | |
301 | + final Iterator<Map.Entry<K, V>> iterator = | |
302 | + getOverflowEntriesMutable().entrySet().iterator(); | |
303 | + entryList.add(new Entry(iterator.next())); | |
304 | + iterator.remove(); | |
305 | + } | |
306 | + return removed; | |
307 | + } | |
308 | + | |
309 | + /** | |
310 | + * @param key The key to find in the entry array. | |
311 | + * @return The returned integer position follows the same semantics as the | |
312 | + * value returned by {@link java.util.Arrays#binarySearch()}. | |
313 | + */ | |
314 | + private int binarySearchInArray(K key) { | |
315 | + int left = 0; | |
316 | + int right = entryList.size() - 1; | |
317 | + | |
318 | + // Optimization: For the common case in which entries are added in | |
319 | + // ascending tag order, check the largest element in the array before | |
320 | + // doing a full binary search. | |
321 | + if (right >= 0) { | |
322 | + int cmp = key.compareTo(entryList.get(right).getKey()); | |
323 | + if (cmp > 0) { | |
324 | + return -(right + 2); // Insert point is after "right". | |
325 | + } else if (cmp == 0) { | |
326 | + return right; | |
327 | + } | |
328 | + } | |
329 | + | |
330 | + while (left <= right) { | |
331 | + int mid = (left + right) / 2; | |
332 | + int cmp = key.compareTo(entryList.get(mid).getKey()); | |
333 | + if (cmp < 0) { | |
334 | + right = mid - 1; | |
335 | + } else if (cmp > 0) { | |
336 | + left = mid + 1; | |
337 | + } else { | |
338 | + return mid; | |
339 | + } | |
340 | + } | |
341 | + return -(left + 1); | |
342 | + } | |
343 | + | |
344 | + /** | |
345 | + * Similar to the AbstractMap implementation of {@code keySet()} and | |
346 | + * {@code values()}, the entry set is created the first time this method is | |
347 | + * called, and returned in response to all subsequent calls. | |
348 | + * | |
349 | + * {@inheritDoc} | |
350 | + */ | |
351 | + @Override | |
352 | + public Set<Map.Entry<K, V>> entrySet() { | |
353 | + if (lazyEntrySet == null) { | |
354 | + lazyEntrySet = new EntrySet(); | |
355 | + } | |
356 | + return lazyEntrySet; | |
357 | + } | |
358 | + | |
359 | + /** | |
360 | + * @throws UnsupportedOperationException if {@link #makeImmutable()} has | |
361 | + * has been called. | |
362 | + */ | |
363 | + private void checkMutable() { | |
364 | + if (isImmutable) { | |
365 | + throw new UnsupportedOperationException(); | |
366 | + } | |
367 | + } | |
368 | + | |
369 | + /** | |
370 | + * @return a {@link SortedMap} to which overflow entries mappings can be | |
371 | + * added or removed. | |
372 | + * @throws UnsupportedOperationException if {@link #makeImmutable()} has been | |
373 | + * called. | |
374 | + */ | |
375 | + @SuppressWarnings("unchecked") | |
376 | + private SortedMap<K, V> getOverflowEntriesMutable() { | |
377 | + checkMutable(); | |
378 | + if (overflowEntries.isEmpty() && !(overflowEntries instanceof TreeMap)) { | |
379 | + overflowEntries = new TreeMap<K, V>(); | |
380 | + } | |
381 | + return (SortedMap<K, V>) overflowEntries; | |
382 | + } | |
383 | + | |
384 | + /** | |
385 | + * Lazily creates the entry list. Any code that adds to the list must first | |
386 | + * call this method. | |
387 | + */ | |
388 | + private void ensureEntryArrayMutable() { | |
389 | + checkMutable(); | |
390 | + if (entryList.isEmpty() && !(entryList instanceof ArrayList)) { | |
391 | + entryList = new ArrayList<Entry>(maxArraySize); | |
392 | + } | |
393 | + } | |
394 | + | |
395 | + /** | |
396 | + * Entry implementation that implements Comparable in order to support | |
397 | + * binary search witin the entry array. Also checks mutability in | |
398 | + * {@link #setValue()}. | |
399 | + */ | |
400 | + private class Entry implements Map.Entry<K, V>, Comparable<Entry> { | |
401 | + | |
402 | + private final K key; | |
403 | + private V value; | |
404 | + | |
405 | + Entry(Map.Entry<K, V> copy) { | |
406 | + this(copy.getKey(), copy.getValue()); | |
407 | + } | |
408 | + | |
409 | + Entry(K key, V value) { | |
410 | + this.key = key; | |
411 | + this.value = value; | |
412 | + } | |
413 | + | |
414 | + //@Override (Java 1.6 override semantics, but we must support 1.5) | |
415 | + public K getKey() { | |
416 | + return key; | |
417 | + } | |
418 | + | |
419 | + //@Override (Java 1.6 override semantics, but we must support 1.5) | |
420 | + public V getValue() { | |
421 | + return value; | |
422 | + } | |
423 | + | |
424 | + //@Override (Java 1.6 override semantics, but we must support 1.5) | |
425 | + public int compareTo(Entry other) { | |
426 | + return getKey().compareTo(other.getKey()); | |
427 | + } | |
428 | + | |
429 | + //@Override (Java 1.6 override semantics, but we must support 1.5) | |
430 | + public V setValue(V newValue) { | |
431 | + checkMutable(); | |
432 | + final V oldValue = this.value; | |
433 | + this.value = newValue; | |
434 | + return oldValue; | |
435 | + } | |
436 | + | |
437 | + @Override | |
438 | + public boolean equals(Object o) { | |
439 | + if (o == this) { | |
440 | + return true; | |
441 | + } | |
442 | + if (!(o instanceof Map.Entry)) { | |
443 | + return false; | |
444 | + } | |
445 | + @SuppressWarnings("unchecked") | |
446 | + Map.Entry<?, ?> other = (Map.Entry<?, ?>) o; | |
447 | + return equals(key, other.getKey()) && equals(value, other.getValue()); | |
448 | + } | |
449 | + | |
450 | + @Override | |
451 | + public int hashCode() { | |
452 | + return (key == null ? 0 : key.hashCode()) ^ | |
453 | + (value == null ? 0 : value.hashCode()); | |
454 | + } | |
455 | + | |
456 | + @Override | |
457 | + public String toString() { | |
458 | + return key + "=" + value; | |
459 | + } | |
460 | + | |
461 | + /** equals() that handles null values. */ | |
462 | + private boolean equals(Object o1, Object o2) { | |
463 | + return o1 == null ? o2 == null : o1.equals(o2); | |
464 | + } | |
465 | + } | |
466 | + | |
467 | + /** | |
468 | + * Stateless view of the entries in the field map. | |
469 | + */ | |
470 | + private class EntrySet extends AbstractSet<Map.Entry<K, V>> { | |
471 | + | |
472 | + @Override | |
473 | + public Iterator<Map.Entry<K, V>> iterator() { | |
474 | + return new EntryIterator(); | |
475 | + } | |
476 | + | |
477 | + @Override | |
478 | + public int size() { | |
479 | + return SmallSortedMap.this.size(); | |
480 | + } | |
481 | + | |
482 | + /** | |
483 | + * Throws a {@link ClassCastException} if o is not of the expected type. | |
484 | + * | |
485 | + * {@inheritDoc} | |
486 | + */ | |
487 | + @Override | |
488 | + public boolean contains(Object o) { | |
489 | + @SuppressWarnings("unchecked") | |
490 | + final Map.Entry<K, V> entry = (Map.Entry<K, V>) o; | |
491 | + final V existing = get(entry.getKey()); | |
492 | + final V value = entry.getValue(); | |
493 | + return existing == value || | |
494 | + (existing != null && existing.equals(value)); | |
495 | + } | |
496 | + | |
497 | + @Override | |
498 | + public boolean add(Map.Entry<K, V> entry) { | |
499 | + if (!contains(entry)) { | |
500 | + put(entry.getKey(), entry.getValue()); | |
501 | + return true; | |
502 | + } | |
503 | + return false; | |
504 | + } | |
505 | + | |
506 | + /** | |
507 | + * Throws a {@link ClassCastException} if o is not of the expected type. | |
508 | + * | |
509 | + * {@inheritDoc} | |
510 | + */ | |
511 | + @Override | |
512 | + public boolean remove(Object o) { | |
513 | + @SuppressWarnings("unchecked") | |
514 | + final Map.Entry<K, V> entry = (Map.Entry<K, V>) o; | |
515 | + if (contains(entry)) { | |
516 | + SmallSortedMap.this.remove(entry.getKey()); | |
517 | + return true; | |
518 | + } | |
519 | + return false; | |
520 | + } | |
521 | + | |
522 | + @Override | |
523 | + public void clear() { | |
524 | + SmallSortedMap.this.clear(); | |
525 | + } | |
526 | + } | |
527 | + | |
528 | + /** | |
529 | + * Iterator implementation that switches from the entry array to the overflow | |
530 | + * entries appropriately. | |
531 | + */ | |
532 | + private class EntryIterator implements Iterator<Map.Entry<K, V>> { | |
533 | + | |
534 | + private int pos = -1; | |
535 | + private boolean nextCalledBeforeRemove; | |
536 | + private Iterator<Map.Entry<K, V>> lazyOverflowIterator; | |
537 | + | |
538 | + //@Override (Java 1.6 override semantics, but we must support 1.5) | |
539 | + public boolean hasNext() { | |
540 | + return (pos + 1) < entryList.size() || | |
541 | + getOverflowIterator().hasNext(); | |
542 | + } | |
543 | + | |
544 | + //@Override (Java 1.6 override semantics, but we must support 1.5) | |
545 | + public Map.Entry<K, V> next() { | |
546 | + nextCalledBeforeRemove = true; | |
547 | + // Always increment pos so that we know whether the last returned value | |
548 | + // was from the array or from overflow. | |
549 | + if (++pos < entryList.size()) { | |
550 | + return entryList.get(pos); | |
551 | + } | |
552 | + return getOverflowIterator().next(); | |
553 | + } | |
554 | + | |
555 | + //@Override (Java 1.6 override semantics, but we must support 1.5) | |
556 | + public void remove() { | |
557 | + if (!nextCalledBeforeRemove) { | |
558 | + throw new IllegalStateException("remove() was called before next()"); | |
559 | + } | |
560 | + nextCalledBeforeRemove = false; | |
561 | + checkMutable(); | |
562 | + | |
563 | + if (pos < entryList.size()) { | |
564 | + removeArrayEntryAt(pos--); | |
565 | + } else { | |
566 | + getOverflowIterator().remove(); | |
567 | + } | |
568 | + } | |
569 | + | |
570 | + /** | |
571 | + * It is important to create the overflow iterator only after the array | |
572 | + * entries have been iterated over because the overflow entry set changes | |
573 | + * when the client calls remove() on the array entries, which invalidates | |
574 | + * any existing iterators. | |
575 | + */ | |
576 | + private Iterator<Map.Entry<K, V>> getOverflowIterator() { | |
577 | + if (lazyOverflowIterator == null) { | |
578 | + lazyOverflowIterator = overflowEntries.entrySet().iterator(); | |
579 | + } | |
580 | + return lazyOverflowIterator; | |
581 | + } | |
582 | + } | |
583 | + | |
584 | + /** | |
585 | + * Helper class that holds immutable instances of an Iterable/Iterator that | |
586 | + * we return when the overflow entries is empty. This eliminates the creation | |
587 | + * of an Iterator object when there is nothing to iterate over. | |
588 | + */ | |
589 | + private static class EmptySet { | |
590 | + | |
591 | + private static final Iterator<Object> ITERATOR = new Iterator<Object>() { | |
592 | + //@Override (Java 1.6 override semantics, but we must support 1.5) | |
593 | + public boolean hasNext() { | |
594 | + return false; | |
595 | + } | |
596 | + //@Override (Java 1.6 override semantics, but we must support 1.5) | |
597 | + public Object next() { | |
598 | + throw new NoSuchElementException(); | |
599 | + } | |
600 | + //@Override (Java 1.6 override semantics, but we must support 1.5) | |
601 | + public void remove() { | |
602 | + throw new UnsupportedOperationException(); | |
603 | + } | |
604 | + }; | |
605 | + | |
606 | + private static final Iterable<Object> ITERABLE = new Iterable<Object>() { | |
607 | + //@Override (Java 1.6 override semantics, but we must support 1.5) | |
608 | + public Iterator<Object> iterator() { | |
609 | + return ITERATOR; | |
610 | + } | |
611 | + }; | |
612 | + | |
613 | + @SuppressWarnings("unchecked") | |
614 | + static <T> Iterable<T> iterable() { | |
615 | + return (Iterable<T>) ITERABLE; | |
616 | + } | |
617 | + } | |
618 | +} |
@@ -0,0 +1,1476 @@ | ||
1 | +// Protocol Buffers - Google's data interchange format | |
2 | +// Copyright 2008 Google Inc. All rights reserved. | |
3 | +// http://code.google.com/p/protobuf/ | |
4 | +// | |
5 | +// Redistribution and use in source and binary forms, with or without | |
6 | +// modification, are permitted provided that the following conditions are | |
7 | +// met: | |
8 | +// | |
9 | +// * Redistributions of source code must retain the above copyright | |
10 | +// notice, this list of conditions and the following disclaimer. | |
11 | +// * Redistributions in binary form must reproduce the above | |
12 | +// copyright notice, this list of conditions and the following disclaimer | |
13 | +// in the documentation and/or other materials provided with the | |
14 | +// distribution. | |
15 | +// * Neither the name of Google Inc. nor the names of its | |
16 | +// contributors may be used to endorse or promote products derived from | |
17 | +// this software without specific prior written permission. | |
18 | +// | |
19 | +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
20 | +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
21 | +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
22 | +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
23 | +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
24 | +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
25 | +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
26 | +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
27 | +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
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. | |
30 | + | |
31 | +package com.google.protobuf; | |
32 | + | |
33 | +import com.google.protobuf.Descriptors.Descriptor; | |
34 | +import com.google.protobuf.Descriptors.FieldDescriptor; | |
35 | +import com.google.protobuf.Descriptors.EnumDescriptor; | |
36 | +import com.google.protobuf.Descriptors.EnumValueDescriptor; | |
37 | + | |
38 | +import java.io.IOException; | |
39 | +import java.nio.CharBuffer; | |
40 | +import java.math.BigInteger; | |
41 | +import java.util.ArrayList; | |
42 | +import java.util.List; | |
43 | +import java.util.Locale; | |
44 | +import java.util.Map; | |
45 | +import java.util.regex.Matcher; | |
46 | +import java.util.regex.Pattern; | |
47 | + | |
48 | +/** | |
49 | + * Provide text parsing and formatting support for proto2 instances. | |
50 | + * The implementation largely follows google/protobuf/text_format.cc. | |
51 | + * | |
52 | + * @author wenboz@google.com Wenbo Zhu | |
53 | + * @author kenton@google.com Kenton Varda | |
54 | + */ | |
55 | +public final class TextFormat { | |
56 | + private TextFormat() {} | |
57 | + | |
58 | + private static final Printer DEFAULT_PRINTER = new Printer(false); | |
59 | + private static final Printer SINGLE_LINE_PRINTER = new Printer(true); | |
60 | + | |
61 | + /** | |
62 | + * Outputs a textual representation of the Protocol Message supplied into | |
63 | + * the parameter output. (This representation is the new version of the | |
64 | + * classic "ProtocolPrinter" output from the original Protocol Buffer system) | |
65 | + */ | |
66 | + public static void print(final Message message, final Appendable output) | |
67 | + throws IOException { | |
68 | + DEFAULT_PRINTER.print(message, new TextGenerator(output)); | |
69 | + } | |
70 | + | |
71 | + /** Outputs a textual representation of {@code fields} to {@code output}. */ | |
72 | + public static void print(final UnknownFieldSet fields, | |
73 | + final Appendable output) | |
74 | + throws IOException { | |
75 | + DEFAULT_PRINTER.printUnknownFields(fields, new TextGenerator(output)); | |
76 | + } | |
77 | + | |
78 | + /** | |
79 | + * Generates a human readable form of this message, useful for debugging and | |
80 | + * other purposes, with no newline characters. | |
81 | + */ | |
82 | + public static String shortDebugString(final Message message) { | |
83 | + try { | |
84 | + final StringBuilder sb = new StringBuilder(); | |
85 | + SINGLE_LINE_PRINTER.print(message, new TextGenerator(sb)); | |
86 | + // Single line mode currently might have an extra space at the end. | |
87 | + return sb.toString().trim(); | |
88 | + } catch (IOException e) { | |
89 | + throw new IllegalStateException(e); | |
90 | + } | |
91 | + } | |
92 | + | |
93 | + /** | |
94 | + * Generates a human readable form of the unknown fields, useful for debugging | |
95 | + * and other purposes, with no newline characters. | |
96 | + */ | |
97 | + public static String shortDebugString(final UnknownFieldSet fields) { | |
98 | + try { | |
99 | + final StringBuilder sb = new StringBuilder(); | |
100 | + SINGLE_LINE_PRINTER.printUnknownFields(fields, new TextGenerator(sb)); | |
101 | + // Single line mode currently might have an extra space at the end. | |
102 | + return sb.toString().trim(); | |
103 | + } catch (IOException e) { | |
104 | + throw new IllegalStateException(e); | |
105 | + } | |
106 | + } | |
107 | + | |
108 | + /** | |
109 | + * Like {@code print()}, but writes directly to a {@code String} and | |
110 | + * returns it. | |
111 | + */ | |
112 | + public static String printToString(final Message message) { | |
113 | + try { | |
114 | + final StringBuilder text = new StringBuilder(); | |
115 | + print(message, text); | |
116 | + return text.toString(); | |
117 | + } catch (IOException e) { | |
118 | + throw new IllegalStateException(e); | |
119 | + } | |
120 | + } | |
121 | + | |
122 | + /** | |
123 | + * Like {@code print()}, but writes directly to a {@code String} and | |
124 | + * returns it. | |
125 | + */ | |
126 | + public static String printToString(final UnknownFieldSet fields) { | |
127 | + try { | |
128 | + final StringBuilder text = new StringBuilder(); | |
129 | + print(fields, text); | |
130 | + return text.toString(); | |
131 | + } catch (IOException e) { | |
132 | + throw new IllegalStateException(e); | |
133 | + } | |
134 | + } | |
135 | + | |
136 | + public static void printField(final FieldDescriptor field, | |
137 | + final Object value, | |
138 | + final Appendable output) | |
139 | + throws IOException { | |
140 | + DEFAULT_PRINTER.printField(field, value, new TextGenerator(output)); | |
141 | + } | |
142 | + | |
143 | + public static String printFieldToString(final FieldDescriptor field, | |
144 | + final Object value) { | |
145 | + try { | |
146 | + final StringBuilder text = new StringBuilder(); | |
147 | + printField(field, value, text); | |
148 | + return text.toString(); | |
149 | + } catch (IOException e) { | |
150 | + throw new IllegalStateException(e); | |
151 | + } | |
152 | + } | |
153 | + | |
154 | + /** | |
155 | + * Outputs a textual representation of the value of given field value. | |
156 | + * | |
157 | + * @param field the descriptor of the field | |
158 | + * @param value the value of the field | |
159 | + * @param output the output to which to append the formatted value | |
160 | + * @throws ClassCastException if the value is not appropriate for the | |
161 | + * given field descriptor | |
162 | + * @throws IOException if there is an exception writing to the output | |
163 | + */ | |
164 | + public static void printFieldValue(final FieldDescriptor field, | |
165 | + final Object value, | |
166 | + final Appendable output) | |
167 | + throws IOException { | |
168 | + DEFAULT_PRINTER.printFieldValue(field, value, new TextGenerator(output)); | |
169 | + } | |
170 | + | |
171 | + /** | |
172 | + * Outputs a textual representation of the value of an unknown field. | |
173 | + * | |
174 | + * @param tag the field's tag number | |
175 | + * @param value the value of the field | |
176 | + * @param output the output to which to append the formatted value | |
177 | + * @throws ClassCastException if the value is not appropriate for the | |
178 | + * given field descriptor | |
179 | + * @throws IOException if there is an exception writing to the output | |
180 | + */ | |
181 | + public static void printUnknownFieldValue(final int tag, | |
182 | + final Object value, | |
183 | + final Appendable output) | |
184 | + throws IOException { | |
185 | + printUnknownFieldValue(tag, value, new TextGenerator(output)); | |
186 | + } | |
187 | + | |
188 | + private static void printUnknownFieldValue(final int tag, | |
189 | + final Object value, | |
190 | + final TextGenerator generator) | |
191 | + throws IOException { | |
192 | + switch (WireFormat.getTagWireType(tag)) { | |
193 | + case WireFormat.WIRETYPE_VARINT: | |
194 | + generator.print(unsignedToString((Long) value)); | |
195 | + break; | |
196 | + case WireFormat.WIRETYPE_FIXED32: | |
197 | + generator.print( | |
198 | + String.format((Locale) null, "0x%08x", (Integer) value)); | |
199 | + break; | |
200 | + case WireFormat.WIRETYPE_FIXED64: | |
201 | + generator.print(String.format((Locale) null, "0x%016x", (Long) value)); | |
202 | + break; | |
203 | + case WireFormat.WIRETYPE_LENGTH_DELIMITED: | |
204 | + generator.print("\""); | |
205 | + generator.print(escapeBytes((ByteString) value)); | |
206 | + generator.print("\""); | |
207 | + break; | |
208 | + case WireFormat.WIRETYPE_START_GROUP: | |
209 | + DEFAULT_PRINTER.printUnknownFields((UnknownFieldSet) value, generator); | |
210 | + break; | |
211 | + default: | |
212 | + throw new IllegalArgumentException("Bad tag: " + tag); | |
213 | + } | |
214 | + } | |
215 | + | |
216 | + /** Helper class for converting protobufs to text. */ | |
217 | + private static final class Printer { | |
218 | + /** Whether to omit newlines from the output. */ | |
219 | + final boolean singleLineMode; | |
220 | + | |
221 | + private Printer(final boolean singleLineMode) { | |
222 | + this.singleLineMode = singleLineMode; | |
223 | + } | |
224 | + | |
225 | + private void print(final Message message, final TextGenerator generator) | |
226 | + throws IOException { | |
227 | + for (Map.Entry<FieldDescriptor, Object> field | |
228 | + : message.getAllFields().entrySet()) { | |
229 | + printField(field.getKey(), field.getValue(), generator); | |
230 | + } | |
231 | + printUnknownFields(message.getUnknownFields(), generator); | |
232 | + } | |
233 | + | |
234 | + private void printField(final FieldDescriptor field, final Object value, | |
235 | + final TextGenerator generator) throws IOException { | |
236 | + if (field.isRepeated()) { | |
237 | + // Repeated field. Print each element. | |
238 | + for (Object element : (List<?>) value) { | |
239 | + printSingleField(field, element, generator); | |
240 | + } | |
241 | + } else { | |
242 | + printSingleField(field, value, generator); | |
243 | + } | |
244 | + } | |
245 | + | |
246 | + private void printSingleField(final FieldDescriptor field, | |
247 | + final Object value, | |
248 | + final TextGenerator generator) | |
249 | + throws IOException { | |
250 | + if (field.isExtension()) { | |
251 | + generator.print("["); | |
252 | + // We special-case MessageSet elements for compatibility with proto1. | |
253 | + if (field.getContainingType().getOptions().getMessageSetWireFormat() | |
254 | + && (field.getType() == FieldDescriptor.Type.MESSAGE) | |
255 | + && (field.isOptional()) | |
256 | + // object equality | |
257 | + && (field.getExtensionScope() == field.getMessageType())) { | |
258 | + generator.print(field.getMessageType().getFullName()); | |
259 | + } else { | |
260 | + generator.print(field.getFullName()); | |
261 | + } | |
262 | + generator.print("]"); | |
263 | + } else { | |
264 | + if (field.getType() == FieldDescriptor.Type.GROUP) { | |
265 | + // Groups must be serialized with their original capitalization. | |
266 | + generator.print(field.getMessageType().getName()); | |
267 | + } else { | |
268 | + generator.print(field.getName()); | |
269 | + } | |
270 | + } | |
271 | + | |
272 | + if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { | |
273 | + if (singleLineMode) { | |
274 | + generator.print(" { "); | |
275 | + } else { | |
276 | + generator.print(" {\n"); | |
277 | + generator.indent(); | |
278 | + } | |
279 | + } else { | |
280 | + generator.print(": "); | |
281 | + } | |
282 | + | |
283 | + printFieldValue(field, value, generator); | |
284 | + | |
285 | + if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { | |
286 | + if (singleLineMode) { | |
287 | + generator.print("} "); | |
288 | + } else { | |
289 | + generator.outdent(); | |
290 | + generator.print("}\n"); | |
291 | + } | |
292 | + } else { | |
293 | + if (singleLineMode) { | |
294 | + generator.print(" "); | |
295 | + } else { | |
296 | + generator.print("\n"); | |
297 | + } | |
298 | + } | |
299 | + } | |
300 | + | |
301 | + private void printFieldValue(final FieldDescriptor field, | |
302 | + final Object value, | |
303 | + final TextGenerator generator) | |
304 | + throws IOException { | |
305 | + switch (field.getType()) { | |
306 | + case INT32: | |
307 | + case SINT32: | |
308 | + case SFIXED32: | |
309 | + generator.print(((Integer) value).toString()); | |
310 | + break; | |
311 | + | |
312 | + case INT64: | |
313 | + case SINT64: | |
314 | + case SFIXED64: | |
315 | + generator.print(((Long) value).toString()); | |
316 | + break; | |
317 | + | |
318 | + case BOOL: | |
319 | + generator.print(((Boolean) value).toString()); | |
320 | + break; | |
321 | + | |
322 | + case FLOAT: | |
323 | + generator.print(((Float) value).toString()); | |
324 | + break; | |
325 | + | |
326 | + case DOUBLE: | |
327 | + generator.print(((Double) value).toString()); | |
328 | + break; | |
329 | + | |
330 | + case UINT32: | |
331 | + case FIXED32: | |
332 | + generator.print(unsignedToString((Integer) value)); | |
333 | + break; | |
334 | + | |
335 | + case UINT64: | |
336 | + case FIXED64: | |
337 | + generator.print(unsignedToString((Long) value)); | |
338 | + break; | |
339 | + | |
340 | + case STRING: | |
341 | + generator.print("\""); | |
342 | + generator.print(escapeText((String) value)); | |
343 | + generator.print("\""); | |
344 | + break; | |
345 | + | |
346 | + case BYTES: | |
347 | + generator.print("\""); | |
348 | + generator.print(escapeBytes((ByteString) value)); | |
349 | + generator.print("\""); | |
350 | + break; | |
351 | + | |
352 | + case ENUM: | |
353 | + generator.print(((EnumValueDescriptor) value).getName()); | |
354 | + break; | |
355 | + | |
356 | + case MESSAGE: | |
357 | + case GROUP: | |
358 | + print((Message) value, generator); | |
359 | + break; | |
360 | + } | |
361 | + } | |
362 | + | |
363 | + private void printUnknownFields(final UnknownFieldSet unknownFields, | |
364 | + final TextGenerator generator) | |
365 | + throws IOException { | |
366 | + for (Map.Entry<Integer, UnknownFieldSet.Field> entry : | |
367 | + unknownFields.asMap().entrySet()) { | |
368 | + final int number = entry.getKey(); | |
369 | + final UnknownFieldSet.Field field = entry.getValue(); | |
370 | + printUnknownField(number, WireFormat.WIRETYPE_VARINT, | |
371 | + field.getVarintList(), generator); | |
372 | + printUnknownField(number, WireFormat.WIRETYPE_FIXED32, | |
373 | + field.getFixed32List(), generator); | |
374 | + printUnknownField(number, WireFormat.WIRETYPE_FIXED64, | |
375 | + field.getFixed64List(), generator); | |
376 | + printUnknownField(number, WireFormat.WIRETYPE_LENGTH_DELIMITED, | |
377 | + field.getLengthDelimitedList(), generator); | |
378 | + for (final UnknownFieldSet value : field.getGroupList()) { | |
379 | + generator.print(entry.getKey().toString()); | |
380 | + if (singleLineMode) { | |
381 | + generator.print(" { "); | |
382 | + } else { | |
383 | + generator.print(" {\n"); | |
384 | + generator.indent(); | |
385 | + } | |
386 | + printUnknownFields(value, generator); | |
387 | + if (singleLineMode) { | |
388 | + generator.print("} "); | |
389 | + } else { | |
390 | + generator.outdent(); | |
391 | + generator.print("}\n"); | |
392 | + } | |
393 | + } | |
394 | + } | |
395 | + } | |
396 | + | |
397 | + private void printUnknownField(final int number, | |
398 | + final int wireType, | |
399 | + final List<?> values, | |
400 | + final TextGenerator generator) | |
401 | + throws IOException { | |
402 | + for (final Object value : values) { | |
403 | + generator.print(String.valueOf(number)); | |
404 | + generator.print(": "); | |
405 | + printUnknownFieldValue(wireType, value, generator); | |
406 | + generator.print(singleLineMode ? " " : "\n"); | |
407 | + } | |
408 | + } | |
409 | + } | |
410 | + | |
411 | + /** Convert an unsigned 32-bit integer to a string. */ | |
412 | + private static String unsignedToString(final int value) { | |
413 | + if (value >= 0) { | |
414 | + return Integer.toString(value); | |
415 | + } else { | |
416 | + return Long.toString(((long) value) & 0x00000000FFFFFFFFL); | |
417 | + } | |
418 | + } | |
419 | + | |
420 | + /** Convert an unsigned 64-bit integer to a string. */ | |
421 | + private static String unsignedToString(final long value) { | |
422 | + if (value >= 0) { | |
423 | + return Long.toString(value); | |
424 | + } else { | |
425 | + // Pull off the most-significant bit so that BigInteger doesn't think | |
426 | + // the number is negative, then set it again using setBit(). | |
427 | + return BigInteger.valueOf(value & 0x7FFFFFFFFFFFFFFFL) | |
428 | + .setBit(63).toString(); | |
429 | + } | |
430 | + } | |
431 | + | |
432 | + /** | |
433 | + * An inner class for writing text to the output stream. | |
434 | + */ | |
435 | + private static final class TextGenerator { | |
436 | + private final Appendable output; | |
437 | + private final StringBuilder indent = new StringBuilder(); | |
438 | + private boolean atStartOfLine = true; | |
439 | + | |
440 | + private TextGenerator(final Appendable output) { | |
441 | + this.output = output; | |
442 | + } | |
443 | + | |
444 | + /** | |
445 | + * Indent text by two spaces. After calling Indent(), two spaces will be | |
446 | + * inserted at the beginning of each line of text. Indent() may be called | |
447 | + * multiple times to produce deeper indents. | |
448 | + */ | |
449 | + public void indent() { | |
450 | + indent.append(" "); | |
451 | + } | |
452 | + | |
453 | + /** | |
454 | + * Reduces the current indent level by two spaces, or crashes if the indent | |
455 | + * level is zero. | |
456 | + */ | |
457 | + public void outdent() { | |
458 | + final int length = indent.length(); | |
459 | + if (length == 0) { | |
460 | + throw new IllegalArgumentException( | |
461 | + " Outdent() without matching Indent()."); | |
462 | + } | |
463 | + indent.delete(length - 2, length); | |
464 | + } | |
465 | + | |
466 | + /** | |
467 | + * Print text to the output stream. | |
468 | + */ | |
469 | + public void print(final CharSequence text) throws IOException { | |
470 | + final int size = text.length(); | |
471 | + int pos = 0; | |
472 | + | |
473 | + for (int i = 0; i < size; i++) { | |
474 | + if (text.charAt(i) == '\n') { | |
475 | + write(text.subSequence(pos, size), i - pos + 1); | |
476 | + pos = i + 1; | |
477 | + atStartOfLine = true; | |
478 | + } | |
479 | + } | |
480 | + write(text.subSequence(pos, size), size - pos); | |
481 | + } | |
482 | + | |
483 | + private void write(final CharSequence data, final int size) | |
484 | + throws IOException { | |
485 | + if (size == 0) { | |
486 | + return; | |
487 | + } | |
488 | + if (atStartOfLine) { | |
489 | + atStartOfLine = false; | |
490 | + output.append(indent); | |
491 | + } | |
492 | + output.append(data); | |
493 | + } | |
494 | + } | |
495 | + | |
496 | + // ================================================================= | |
497 | + // Parsing | |
498 | + | |
499 | + /** | |
500 | + * Represents a stream of tokens parsed from a {@code String}. | |
501 | + * | |
502 | + * <p>The Java standard library provides many classes that you might think | |
503 | + * would be useful for implementing this, but aren't. For example: | |
504 | + * | |
505 | + * <ul> | |
506 | + * <li>{@code java.io.StreamTokenizer}: This almost does what we want -- or, | |
507 | + * at least, something that would get us close to what we want -- except | |
508 | + * for one fatal flaw: It automatically un-escapes strings using Java | |
509 | + * escape sequences, which do not include all the escape sequences we | |
510 | + * need to support (e.g. '\x'). | |
511 | + * <li>{@code java.util.Scanner}: This seems like a great way at least to | |
512 | + * parse regular expressions out of a stream (so we wouldn't have to load | |
513 | + * the entire input into a single string before parsing). Sadly, | |
514 | + * {@code Scanner} requires that tokens be delimited with some delimiter. | |
515 | + * Thus, although the text "foo:" should parse to two tokens ("foo" and | |
516 | + * ":"), {@code Scanner} would recognize it only as a single token. | |
517 | + * Furthermore, {@code Scanner} provides no way to inspect the contents | |
518 | + * of delimiters, making it impossible to keep track of line and column | |
519 | + * numbers. | |
520 | + * </ul> | |
521 | + * | |
522 | + * <p>Luckily, Java's regular expression support does manage to be useful to | |
523 | + * us. (Barely: We need {@code Matcher.usePattern()}, which is new in | |
524 | + * Java 1.5.) So, we can use that, at least. Unfortunately, this implies | |
525 | + * that we need to have the entire input in one contiguous string. | |
526 | + */ | |
527 | + private static final class Tokenizer { | |
528 | + private final CharSequence text; | |
529 | + private final Matcher matcher; | |
530 | + private String currentToken; | |
531 | + | |
532 | + // The character index within this.text at which the current token begins. | |
533 | + private int pos = 0; | |
534 | + | |
535 | + // The line and column numbers of the current token. | |
536 | + private int line = 0; | |
537 | + private int column = 0; | |
538 | + | |
539 | + // The line and column numbers of the previous token (allows throwing | |
540 | + // errors *after* consuming). | |
541 | + private int previousLine = 0; | |
542 | + private int previousColumn = 0; | |
543 | + | |
544 | + // We use possesive quantifiers (*+ and ++) because otherwise the Java | |
545 | + // regex matcher has stack overflows on large inputs. | |
546 | + private static final Pattern WHITESPACE = | |
547 | + Pattern.compile("(\\s|(#.*$))++", Pattern.MULTILINE); | |
548 | + private static final Pattern TOKEN = Pattern.compile( | |
549 | + "[a-zA-Z_][0-9a-zA-Z_+-]*+|" + // an identifier | |
550 | + "[.]?[0-9+-][0-9a-zA-Z_.+-]*+|" + // a number | |
551 | + "\"([^\"\n\\\\]|\\\\.)*+(\"|\\\\?$)|" + // a double-quoted string | |
552 | + "\'([^\'\n\\\\]|\\\\.)*+(\'|\\\\?$)", // a single-quoted string | |
553 | + Pattern.MULTILINE); | |
554 | + | |
555 | + private static final Pattern DOUBLE_INFINITY = Pattern.compile( | |
556 | + "-?inf(inity)?", | |
557 | + Pattern.CASE_INSENSITIVE); | |
558 | + private static final Pattern FLOAT_INFINITY = Pattern.compile( | |
559 | + "-?inf(inity)?f?", | |
560 | + Pattern.CASE_INSENSITIVE); | |
561 | + private static final Pattern FLOAT_NAN = Pattern.compile( | |
562 | + "nanf?", | |
563 | + Pattern.CASE_INSENSITIVE); | |
564 | + | |
565 | + /** Construct a tokenizer that parses tokens from the given text. */ | |
566 | + private Tokenizer(final CharSequence text) { | |
567 | + this.text = text; | |
568 | + this.matcher = WHITESPACE.matcher(text); | |
569 | + skipWhitespace(); | |
570 | + nextToken(); | |
571 | + } | |
572 | + | |
573 | + /** Are we at the end of the input? */ | |
574 | + public boolean atEnd() { | |
575 | + return currentToken.length() == 0; | |
576 | + } | |
577 | + | |
578 | + /** Advance to the next token. */ | |
579 | + public void nextToken() { | |
580 | + previousLine = line; | |
581 | + previousColumn = column; | |
582 | + | |
583 | + // Advance the line counter to the current position. | |
584 | + while (pos < matcher.regionStart()) { | |
585 | + if (text.charAt(pos) == '\n') { | |
586 | + ++line; | |
587 | + column = 0; | |
588 | + } else { | |
589 | + ++column; | |
590 | + } | |
591 | + ++pos; | |
592 | + } | |
593 | + | |
594 | + // Match the next token. | |
595 | + if (matcher.regionStart() == matcher.regionEnd()) { | |
596 | + // EOF | |
597 | + currentToken = ""; | |
598 | + } else { | |
599 | + matcher.usePattern(TOKEN); | |
600 | + if (matcher.lookingAt()) { | |
601 | + currentToken = matcher.group(); | |
602 | + matcher.region(matcher.end(), matcher.regionEnd()); | |
603 | + } else { | |
604 | + // Take one character. | |
605 | + currentToken = String.valueOf(text.charAt(pos)); | |
606 | + matcher.region(pos + 1, matcher.regionEnd()); | |
607 | + } | |
608 | + | |
609 | + skipWhitespace(); | |
610 | + } | |
611 | + } | |
612 | + | |
613 | + /** | |
614 | + * Skip over any whitespace so that the matcher region starts at the next | |
615 | + * token. | |
616 | + */ | |
617 | + private void skipWhitespace() { | |
618 | + matcher.usePattern(WHITESPACE); | |
619 | + if (matcher.lookingAt()) { | |
620 | + matcher.region(matcher.end(), matcher.regionEnd()); | |
621 | + } | |
622 | + } | |
623 | + | |
624 | + /** | |
625 | + * If the next token exactly matches {@code token}, consume it and return | |
626 | + * {@code true}. Otherwise, return {@code false} without doing anything. | |
627 | + */ | |
628 | + public boolean tryConsume(final String token) { | |
629 | + if (currentToken.equals(token)) { | |
630 | + nextToken(); | |
631 | + return true; | |
632 | + } else { | |
633 | + return false; | |
634 | + } | |
635 | + } | |
636 | + | |
637 | + /** | |
638 | + * If the next token exactly matches {@code token}, consume it. Otherwise, | |
639 | + * throw a {@link ParseException}. | |
640 | + */ | |
641 | + public void consume(final String token) throws ParseException { | |
642 | + if (!tryConsume(token)) { | |
643 | + throw parseException("Expected \"" + token + "\"."); | |
644 | + } | |
645 | + } | |
646 | + | |
647 | + /** | |
648 | + * Returns {@code true} if the next token is an integer, but does | |
649 | + * not consume it. | |
650 | + */ | |
651 | + public boolean lookingAtInteger() { | |
652 | + if (currentToken.length() == 0) { | |
653 | + return false; | |
654 | + } | |
655 | + | |
656 | + final char c = currentToken.charAt(0); | |
657 | + return ('0' <= c && c <= '9') || | |
658 | + c == '-' || c == '+'; | |
659 | + } | |
660 | + | |
661 | + /** | |
662 | + * If the next token is an identifier, consume it and return its value. | |
663 | + * Otherwise, throw a {@link ParseException}. | |
664 | + */ | |
665 | + public String consumeIdentifier() throws ParseException { | |
666 | + for (int i = 0; i < currentToken.length(); i++) { | |
667 | + final char c = currentToken.charAt(i); | |
668 | + if (('a' <= c && c <= 'z') || | |
669 | + ('A' <= c && c <= 'Z') || | |
670 | + ('0' <= c && c <= '9') || | |
671 | + (c == '_') || (c == '.')) { | |
672 | + // OK | |
673 | + } else { | |
674 | + throw parseException("Expected identifier."); | |
675 | + } | |
676 | + } | |
677 | + | |
678 | + final String result = currentToken; | |
679 | + nextToken(); | |
680 | + return result; | |
681 | + } | |
682 | + | |
683 | + /** | |
684 | + * If the next token is a 32-bit signed integer, consume it and return its | |
685 | + * value. Otherwise, throw a {@link ParseException}. | |
686 | + */ | |
687 | + public int consumeInt32() throws ParseException { | |
688 | + try { | |
689 | + final int result = parseInt32(currentToken); | |
690 | + nextToken(); | |
691 | + return result; | |
692 | + } catch (NumberFormatException e) { | |
693 | + throw integerParseException(e); | |
694 | + } | |
695 | + } | |
696 | + | |
697 | + /** | |
698 | + * If the next token is a 32-bit unsigned integer, consume it and return its | |
699 | + * value. Otherwise, throw a {@link ParseException}. | |
700 | + */ | |
701 | + public int consumeUInt32() throws ParseException { | |
702 | + try { | |
703 | + final int result = parseUInt32(currentToken); | |
704 | + nextToken(); | |
705 | + return result; | |
706 | + } catch (NumberFormatException e) { | |
707 | + throw integerParseException(e); | |
708 | + } | |
709 | + } | |
710 | + | |
711 | + /** | |
712 | + * If the next token is a 64-bit signed integer, consume it and return its | |
713 | + * value. Otherwise, throw a {@link ParseException}. | |
714 | + */ | |
715 | + public long consumeInt64() throws ParseException { | |
716 | + try { | |
717 | + final long result = parseInt64(currentToken); | |
718 | + nextToken(); | |
719 | + return result; | |
720 | + } catch (NumberFormatException e) { | |
721 | + throw integerParseException(e); | |
722 | + } | |
723 | + } | |
724 | + | |
725 | + /** | |
726 | + * If the next token is a 64-bit unsigned integer, consume it and return its | |
727 | + * value. Otherwise, throw a {@link ParseException}. | |
728 | + */ | |
729 | + public long consumeUInt64() throws ParseException { | |
730 | + try { | |
731 | + final long result = parseUInt64(currentToken); | |
732 | + nextToken(); | |
733 | + return result; | |
734 | + } catch (NumberFormatException e) { | |
735 | + throw integerParseException(e); | |
736 | + } | |
737 | + } | |
738 | + | |
739 | + /** | |
740 | + * If the next token is a double, consume it and return its value. | |
741 | + * Otherwise, throw a {@link ParseException}. | |
742 | + */ | |
743 | + public double consumeDouble() throws ParseException { | |
744 | + // We need to parse infinity and nan separately because | |
745 | + // Double.parseDouble() does not accept "inf", "infinity", or "nan". | |
746 | + if (DOUBLE_INFINITY.matcher(currentToken).matches()) { | |
747 | + final boolean negative = currentToken.startsWith("-"); | |
748 | + nextToken(); | |
749 | + return negative ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY; | |
750 | + } | |
751 | + if (currentToken.equalsIgnoreCase("nan")) { | |
752 | + nextToken(); | |
753 | + return Double.NaN; | |
754 | + } | |
755 | + try { | |
756 | + final double result = Double.parseDouble(currentToken); | |
757 | + nextToken(); | |
758 | + return result; | |
759 | + } catch (NumberFormatException e) { | |
760 | + throw floatParseException(e); | |
761 | + } | |
762 | + } | |
763 | + | |
764 | + /** | |
765 | + * If the next token is a float, consume it and return its value. | |
766 | + * Otherwise, throw a {@link ParseException}. | |
767 | + */ | |
768 | + public float consumeFloat() throws ParseException { | |
769 | + // We need to parse infinity and nan separately because | |
770 | + // Float.parseFloat() does not accept "inf", "infinity", or "nan". | |
771 | + if (FLOAT_INFINITY.matcher(currentToken).matches()) { | |
772 | + final boolean negative = currentToken.startsWith("-"); | |
773 | + nextToken(); | |
774 | + return negative ? Float.NEGATIVE_INFINITY : Float.POSITIVE_INFINITY; | |
775 | + } | |
776 | + if (FLOAT_NAN.matcher(currentToken).matches()) { | |
777 | + nextToken(); | |
778 | + return Float.NaN; | |
779 | + } | |
780 | + try { | |
781 | + final float result = Float.parseFloat(currentToken); | |
782 | + nextToken(); | |
783 | + return result; | |
784 | + } catch (NumberFormatException e) { | |
785 | + throw floatParseException(e); | |
786 | + } | |
787 | + } | |
788 | + | |
789 | + /** | |
790 | + * If the next token is a boolean, consume it and return its value. | |
791 | + * Otherwise, throw a {@link ParseException}. | |
792 | + */ | |
793 | + public boolean consumeBoolean() throws ParseException { | |
794 | + if (currentToken.equals("true") || | |
795 | + currentToken.equals("t") || | |
796 | + currentToken.equals("1")) { | |
797 | + nextToken(); | |
798 | + return true; | |
799 | + } else if (currentToken.equals("false") || | |
800 | + currentToken.equals("f") || | |
801 | + currentToken.equals("0")) { | |
802 | + nextToken(); | |
803 | + return false; | |
804 | + } else { | |
805 | + throw parseException("Expected \"true\" or \"false\"."); | |
806 | + } | |
807 | + } | |
808 | + | |
809 | + /** | |
810 | + * If the next token is a string, consume it and return its (unescaped) | |
811 | + * value. Otherwise, throw a {@link ParseException}. | |
812 | + */ | |
813 | + public String consumeString() throws ParseException { | |
814 | + return consumeByteString().toStringUtf8(); | |
815 | + } | |
816 | + | |
817 | + /** | |
818 | + * If the next token is a string, consume it, unescape it as a | |
819 | + * {@link ByteString}, and return it. Otherwise, throw a | |
820 | + * {@link ParseException}. | |
821 | + */ | |
822 | + public ByteString consumeByteString() throws ParseException { | |
823 | + List<ByteString> list = new ArrayList<ByteString>(); | |
824 | + consumeByteString(list); | |
825 | + while (currentToken.startsWith("'") || currentToken.startsWith("\"")) { | |
826 | + consumeByteString(list); | |
827 | + } | |
828 | + return ByteString.copyFrom(list); | |
829 | + } | |
830 | + | |
831 | + /** | |
832 | + * Like {@link #consumeByteString()} but adds each token of the string to | |
833 | + * the given list. String literals (whether bytes or text) may come in | |
834 | + * multiple adjacent tokens which are automatically concatenated, like in | |
835 | + * C or Python. | |
836 | + */ | |
837 | + private void consumeByteString(List<ByteString> list) throws ParseException { | |
838 | + final char quote = currentToken.length() > 0 ? currentToken.charAt(0) | |
839 | + : '\0'; | |
840 | + if (quot |
差分はサイズ制限により省略されました。全ての差分を見るためにはローカルクライアントを利用してください。