~ubuntu-branches/ubuntu/oneiric/mozc/oneiric

« back to all changes in this revision

Viewing changes to protobuf/files/java/src/main/java/com/google/protobuf/AbstractMessage.java

  • Committer: Bazaar Package Importer
  • Author(s): Nobuhiro Iwamatsu
  • Date: 2010-07-14 03:26:47 UTC
  • mfrom: (1.1.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20100714032647-13qjisj6m8cm8jdx
Tags: 0.12.410.102-1
* New upstream release (Closes: #588971).
  - Add mozc-server, mozc-utils-gui and scim-mozc packages.
* Update debian/rules.
  Add --gypdir option to build_mozc.py.
* Update debian/control.
  - Bumped standards-version to 3.9.0.
  - Update description.
* Add mozc icon (Closes: #588972).
* Add patch which revises issue 18.
  ibus_mozc_issue18.patch
* kFreeBSD build support.
  support_kfreebsd.patch

Show diffs side-by-side

added added

removed removed

Lines of Context:
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.ArrayList;
39
 
import java.util.List;
40
 
import java.util.Map;
41
 
 
42
 
/**
43
 
 * A partial implementation of the {@link Message} interface which implements
44
 
 * as many methods of that interface as possible in terms of other methods.
45
 
 *
46
 
 * @author kenton@google.com Kenton Varda
47
 
 */
48
 
public abstract class AbstractMessage extends AbstractMessageLite
49
 
                                      implements Message {
50
 
  @SuppressWarnings("unchecked")
51
 
  public boolean isInitialized() {
52
 
    // Check that all required fields are present.
53
 
    for (final FieldDescriptor field : getDescriptorForType().getFields()) {
54
 
      if (field.isRequired()) {
55
 
        if (!hasField(field)) {
56
 
          return false;
57
 
        }
58
 
      }
59
 
    }
60
 
 
61
 
    // Check that embedded messages are initialized.
62
 
    for (final Map.Entry<FieldDescriptor, Object> entry :
63
 
        getAllFields().entrySet()) {
64
 
      final FieldDescriptor field = entry.getKey();
65
 
      if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
66
 
        if (field.isRepeated()) {
67
 
          for (final Message element : (List<Message>) entry.getValue()) {
68
 
            if (!element.isInitialized()) {
69
 
              return false;
70
 
            }
71
 
          }
72
 
        } else {
73
 
          if (!((Message) entry.getValue()).isInitialized()) {
74
 
            return false;
75
 
          }
76
 
        }
77
 
      }
78
 
    }
79
 
 
80
 
    return true;
81
 
  }
82
 
 
83
 
  @Override
84
 
  public final String toString() {
85
 
    return TextFormat.printToString(this);
86
 
  }
87
 
 
88
 
  public void writeTo(final CodedOutputStream output) throws IOException {
89
 
    final boolean isMessageSet =
90
 
        getDescriptorForType().getOptions().getMessageSetWireFormat();
91
 
 
92
 
    for (final Map.Entry<FieldDescriptor, Object> entry :
93
 
        getAllFields().entrySet()) {
94
 
      final FieldDescriptor field = entry.getKey();
95
 
      final Object value = entry.getValue();
96
 
      if (isMessageSet && field.isExtension() &&
97
 
          field.getType() == FieldDescriptor.Type.MESSAGE &&
98
 
          !field.isRepeated()) {
99
 
        output.writeMessageSetExtension(field.getNumber(), (Message) value);
100
 
      } else {
101
 
        FieldSet.writeField(field, value, output);
102
 
      }
103
 
    }
104
 
 
105
 
    final UnknownFieldSet unknownFields = getUnknownFields();
106
 
    if (isMessageSet) {
107
 
      unknownFields.writeAsMessageSetTo(output);
108
 
    } else {
109
 
      unknownFields.writeTo(output);
110
 
    }
111
 
  }
112
 
 
113
 
  private int memoizedSize = -1;
114
 
 
115
 
  public int getSerializedSize() {
116
 
    int size = memoizedSize;
117
 
    if (size != -1) {
118
 
      return size;
119
 
    }
120
 
 
121
 
    size = 0;
122
 
    final boolean isMessageSet =
123
 
        getDescriptorForType().getOptions().getMessageSetWireFormat();
124
 
 
125
 
    for (final Map.Entry<FieldDescriptor, Object> entry :
126
 
        getAllFields().entrySet()) {
127
 
      final FieldDescriptor field = entry.getKey();
128
 
      final Object value = entry.getValue();
129
 
      if (isMessageSet && field.isExtension() &&
130
 
          field.getType() == FieldDescriptor.Type.MESSAGE &&
131
 
          !field.isRepeated()) {
132
 
        size += CodedOutputStream.computeMessageSetExtensionSize(
133
 
            field.getNumber(), (Message) value);
134
 
      } else {
135
 
        size += FieldSet.computeFieldSize(field, value);
136
 
      }
137
 
    }
138
 
 
139
 
    final UnknownFieldSet unknownFields = getUnknownFields();
140
 
    if (isMessageSet) {
141
 
      size += unknownFields.getSerializedSizeAsMessageSet();
142
 
    } else {
143
 
      size += unknownFields.getSerializedSize();
144
 
    }
145
 
 
146
 
    memoizedSize = size;
147
 
    return size;
148
 
  }
149
 
 
150
 
  @Override
151
 
  public boolean equals(final Object other) {
152
 
    if (other == this) {
153
 
      return true;
154
 
    }
155
 
    if (!(other instanceof Message)) {
156
 
      return false;
157
 
    }
158
 
    final Message otherMessage = (Message) other;
159
 
    if (getDescriptorForType() != otherMessage.getDescriptorForType()) {
160
 
      return false;
161
 
    }
162
 
    return getAllFields().equals(otherMessage.getAllFields()) &&
163
 
        getUnknownFields().equals(otherMessage.getUnknownFields());
164
 
  }
165
 
 
166
 
  @Override
167
 
  public int hashCode() {
168
 
    int hash = 41;
169
 
    hash = (19 * hash) + getDescriptorForType().hashCode();
170
 
    hash = (53 * hash) + getAllFields().hashCode();
171
 
    hash = (29 * hash) + getUnknownFields().hashCode();
172
 
    return hash;
173
 
  }
174
 
 
175
 
  // =================================================================
176
 
 
177
 
  /**
178
 
   * A partial implementation of the {@link Message.Builder} interface which
179
 
   * implements as many methods of that interface as possible in terms of
180
 
   * other methods.
181
 
   */
182
 
  @SuppressWarnings("unchecked")
183
 
  public static abstract class Builder<BuilderType extends Builder>
184
 
      extends AbstractMessageLite.Builder<BuilderType>
185
 
      implements Message.Builder {
186
 
    // The compiler produces an error if this is not declared explicitly.
187
 
    @Override
188
 
    public abstract BuilderType clone();
189
 
 
190
 
    public BuilderType clear() {
191
 
      for (final Map.Entry<FieldDescriptor, Object> entry :
192
 
           getAllFields().entrySet()) {
193
 
        clearField(entry.getKey());
194
 
      }
195
 
      return (BuilderType) this;
196
 
    }
197
 
 
198
 
    public BuilderType mergeFrom(final Message other) {
199
 
      if (other.getDescriptorForType() != getDescriptorForType()) {
200
 
        throw new IllegalArgumentException(
201
 
          "mergeFrom(Message) can only merge messages of the same type.");
202
 
      }
203
 
 
204
 
      // Note:  We don't attempt to verify that other's fields have valid
205
 
      //   types.  Doing so would be a losing battle.  We'd have to verify
206
 
      //   all sub-messages as well, and we'd have to make copies of all of
207
 
      //   them to insure that they don't change after verification (since
208
 
      //   the Message interface itself cannot enforce immutability of
209
 
      //   implementations).
210
 
      // TODO(kenton):  Provide a function somewhere called makeDeepCopy()
211
 
      //   which allows people to make secure deep copies of messages.
212
 
 
213
 
      for (final Map.Entry<FieldDescriptor, Object> entry :
214
 
           other.getAllFields().entrySet()) {
215
 
        final FieldDescriptor field = entry.getKey();
216
 
        if (field.isRepeated()) {
217
 
          for (final Object element : (List)entry.getValue()) {
218
 
            addRepeatedField(field, element);
219
 
          }
220
 
        } else if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
221
 
          final Message existingValue = (Message)getField(field);
222
 
          if (existingValue == existingValue.getDefaultInstanceForType()) {
223
 
            setField(field, entry.getValue());
224
 
          } else {
225
 
            setField(field,
226
 
              existingValue.newBuilderForType()
227
 
                .mergeFrom(existingValue)
228
 
                .mergeFrom((Message)entry.getValue())
229
 
                .build());
230
 
          }
231
 
        } else {
232
 
          setField(field, entry.getValue());
233
 
        }
234
 
      }
235
 
 
236
 
      mergeUnknownFields(other.getUnknownFields());
237
 
 
238
 
      return (BuilderType) this;
239
 
    }
240
 
 
241
 
    @Override
242
 
    public BuilderType mergeFrom(final CodedInputStream input)
243
 
                                 throws IOException {
244
 
      return mergeFrom(input, ExtensionRegistry.getEmptyRegistry());
245
 
    }
246
 
 
247
 
    @Override
248
 
    public BuilderType mergeFrom(
249
 
        final CodedInputStream input,
250
 
        final ExtensionRegistryLite extensionRegistry)
251
 
        throws IOException {
252
 
      final UnknownFieldSet.Builder unknownFields =
253
 
        UnknownFieldSet.newBuilder(getUnknownFields());
254
 
      while (true) {
255
 
        final int tag = input.readTag();
256
 
        if (tag == 0) {
257
 
          break;
258
 
        }
259
 
 
260
 
        if (!mergeFieldFrom(input, unknownFields, extensionRegistry,
261
 
                            this, tag)) {
262
 
          // end group tag
263
 
          break;
264
 
        }
265
 
      }
266
 
      setUnknownFields(unknownFields.build());
267
 
      return (BuilderType) this;
268
 
    }
269
 
 
270
 
    /**
271
 
     * Like {@link #mergeFrom(CodedInputStream, UnknownFieldSet.Builder,
272
 
     * ExtensionRegistryLite, Message.Builder)}, but parses a single field.
273
 
     * Package-private because it is used by GeneratedMessage.ExtendableMessage.
274
 
     * @param tag The tag, which should have already been read.
275
 
     * @return {@code true} unless the tag is an end-group tag.
276
 
     */
277
 
    static boolean mergeFieldFrom(
278
 
        final CodedInputStream input,
279
 
        final UnknownFieldSet.Builder unknownFields,
280
 
        final ExtensionRegistryLite extensionRegistry,
281
 
        final Message.Builder builder,
282
 
        final int tag) throws IOException {
283
 
      final Descriptor type = builder.getDescriptorForType();
284
 
 
285
 
      if (type.getOptions().getMessageSetWireFormat() &&
286
 
          tag == WireFormat.MESSAGE_SET_ITEM_TAG) {
287
 
        mergeMessageSetExtensionFromCodedStream(
288
 
          input, unknownFields, extensionRegistry, builder);
289
 
        return true;
290
 
      }
291
 
 
292
 
      final int wireType = WireFormat.getTagWireType(tag);
293
 
      final int fieldNumber = WireFormat.getTagFieldNumber(tag);
294
 
 
295
 
      final FieldDescriptor field;
296
 
      Message defaultInstance = null;
297
 
 
298
 
      if (type.isExtensionNumber(fieldNumber)) {
299
 
        // extensionRegistry may be either ExtensionRegistry or
300
 
        // ExtensionRegistryLite.  Since the type we are parsing is a full
301
 
        // message, only a full ExtensionRegistry could possibly contain
302
 
        // extensions of it.  Otherwise we will treat the registry as if it
303
 
        // were empty.
304
 
        if (extensionRegistry instanceof ExtensionRegistry) {
305
 
          final ExtensionRegistry.ExtensionInfo extension =
306
 
            ((ExtensionRegistry) extensionRegistry)
307
 
              .findExtensionByNumber(type, fieldNumber);
308
 
          if (extension == null) {
309
 
            field = null;
310
 
          } else {
311
 
            field = extension.descriptor;
312
 
            defaultInstance = extension.defaultInstance;
313
 
            if (defaultInstance == null &&
314
 
                field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
315
 
              throw new IllegalStateException(
316
 
                  "Message-typed extension lacked default instance: " +
317
 
                  field.getFullName());
318
 
            }
319
 
          }
320
 
        } else {
321
 
          field = null;
322
 
        }
323
 
      } else {
324
 
        field = type.findFieldByNumber(fieldNumber);
325
 
      }
326
 
 
327
 
      boolean unknown = false;
328
 
      boolean packed = false;
329
 
      if (field == null) {
330
 
        unknown = true;  // Unknown field.
331
 
      } else if (wireType == FieldSet.getWireFormatForFieldType(
332
 
                   field.getLiteType(),
333
 
                   false  /* isPacked */)) {
334
 
        packed = false;
335
 
      } else if (field.isPackable() &&
336
 
                 wireType == FieldSet.getWireFormatForFieldType(
337
 
                   field.getLiteType(),
338
 
                   true  /* isPacked */)) {
339
 
        packed = true;
340
 
      } else {
341
 
        unknown = true;  // Unknown wire type.
342
 
      }
343
 
 
344
 
      if (unknown) {  // Unknown field or wrong wire type.  Skip.
345
 
        return unknownFields.mergeFieldFrom(tag, input);
346
 
      }
347
 
 
348
 
      if (packed) {
349
 
        final int length = input.readRawVarint32();
350
 
        final int limit = input.pushLimit(length);
351
 
        if (field.getLiteType() == WireFormat.FieldType.ENUM) {
352
 
          while (input.getBytesUntilLimit() > 0) {
353
 
            final int rawValue = input.readEnum();
354
 
            final Object value = field.getEnumType().findValueByNumber(rawValue);
355
 
            if (value == null) {
356
 
              // If the number isn't recognized as a valid value for this
357
 
              // enum, drop it (don't even add it to unknownFields).
358
 
              return true;
359
 
            }
360
 
            builder.addRepeatedField(field, value);
361
 
          }
362
 
        } else {
363
 
          while (input.getBytesUntilLimit() > 0) {
364
 
            final Object value =
365
 
              FieldSet.readPrimitiveField(input, field.getLiteType());
366
 
            builder.addRepeatedField(field, value);
367
 
          }
368
 
        }
369
 
        input.popLimit(limit);
370
 
      } else {
371
 
        final Object value;
372
 
        switch (field.getType()) {
373
 
          case GROUP: {
374
 
            final Message.Builder subBuilder;
375
 
            if (defaultInstance != null) {
376
 
              subBuilder = defaultInstance.newBuilderForType();
377
 
            } else {
378
 
              subBuilder = builder.newBuilderForField(field);
379
 
            }
380
 
            if (!field.isRepeated()) {
381
 
              subBuilder.mergeFrom((Message) builder.getField(field));
382
 
            }
383
 
            input.readGroup(field.getNumber(), subBuilder, extensionRegistry);
384
 
            value = subBuilder.build();
385
 
            break;
386
 
          }
387
 
          case MESSAGE: {
388
 
            final Message.Builder subBuilder;
389
 
            if (defaultInstance != null) {
390
 
              subBuilder = defaultInstance.newBuilderForType();
391
 
            } else {
392
 
              subBuilder = builder.newBuilderForField(field);
393
 
            }
394
 
            if (!field.isRepeated()) {
395
 
              subBuilder.mergeFrom((Message) builder.getField(field));
396
 
            }
397
 
            input.readMessage(subBuilder, extensionRegistry);
398
 
            value = subBuilder.build();
399
 
            break;
400
 
          }
401
 
          case ENUM:
402
 
            final int rawValue = input.readEnum();
403
 
            value = field.getEnumType().findValueByNumber(rawValue);
404
 
            // If the number isn't recognized as a valid value for this enum,
405
 
            // drop it.
406
 
            if (value == null) {
407
 
              unknownFields.mergeVarintField(fieldNumber, rawValue);
408
 
              return true;
409
 
            }
410
 
            break;
411
 
          default:
412
 
            value = FieldSet.readPrimitiveField(input, field.getLiteType());
413
 
            break;
414
 
        }
415
 
 
416
 
        if (field.isRepeated()) {
417
 
          builder.addRepeatedField(field, value);
418
 
        } else {
419
 
          builder.setField(field, value);
420
 
        }
421
 
      }
422
 
 
423
 
      return true;
424
 
    }
425
 
 
426
 
    /** Called by {@code #mergeFieldFrom()} to parse a MessageSet extension. */
427
 
    private static void mergeMessageSetExtensionFromCodedStream(
428
 
        final CodedInputStream input,
429
 
        final UnknownFieldSet.Builder unknownFields,
430
 
        final ExtensionRegistryLite extensionRegistry,
431
 
        final Message.Builder builder) throws IOException {
432
 
      final Descriptor type = builder.getDescriptorForType();
433
 
 
434
 
      // The wire format for MessageSet is:
435
 
      //   message MessageSet {
436
 
      //     repeated group Item = 1 {
437
 
      //       required int32 typeId = 2;
438
 
      //       required bytes message = 3;
439
 
      //     }
440
 
      //   }
441
 
      // "typeId" is the extension's field number.  The extension can only be
442
 
      // a message type, where "message" contains the encoded bytes of that
443
 
      // message.
444
 
      //
445
 
      // In practice, we will probably never see a MessageSet item in which
446
 
      // the message appears before the type ID, or where either field does not
447
 
      // appear exactly once.  However, in theory such cases are valid, so we
448
 
      // should be prepared to accept them.
449
 
 
450
 
      int typeId = 0;
451
 
      ByteString rawBytes = null;  // If we encounter "message" before "typeId"
452
 
      Message.Builder subBuilder = null;
453
 
      FieldDescriptor field = null;
454
 
 
455
 
      while (true) {
456
 
        final int tag = input.readTag();
457
 
        if (tag == 0) {
458
 
          break;
459
 
        }
460
 
 
461
 
        if (tag == WireFormat.MESSAGE_SET_TYPE_ID_TAG) {
462
 
          typeId = input.readUInt32();
463
 
          // Zero is not a valid type ID.
464
 
          if (typeId != 0) {
465
 
            final ExtensionRegistry.ExtensionInfo extension;
466
 
 
467
 
            // extensionRegistry may be either ExtensionRegistry or
468
 
            // ExtensionRegistryLite.  Since the type we are parsing is a full
469
 
            // message, only a full ExtensionRegistry could possibly contain
470
 
            // extensions of it.  Otherwise we will treat the registry as if it
471
 
            // were empty.
472
 
            if (extensionRegistry instanceof ExtensionRegistry) {
473
 
              extension = ((ExtensionRegistry) extensionRegistry)
474
 
                  .findExtensionByNumber(type, typeId);
475
 
            } else {
476
 
              extension = null;
477
 
            }
478
 
 
479
 
            if (extension != null) {
480
 
              field = extension.descriptor;
481
 
              subBuilder = extension.defaultInstance.newBuilderForType();
482
 
              final Message originalMessage = (Message)builder.getField(field);
483
 
              if (originalMessage != null) {
484
 
                subBuilder.mergeFrom(originalMessage);
485
 
              }
486
 
              if (rawBytes != null) {
487
 
                // We already encountered the message.  Parse it now.
488
 
                subBuilder.mergeFrom(
489
 
                  CodedInputStream.newInstance(rawBytes.newInput()));
490
 
                rawBytes = null;
491
 
              }
492
 
            } else {
493
 
              // Unknown extension number.  If we already saw data, put it
494
 
              // in rawBytes.
495
 
              if (rawBytes != null) {
496
 
                unknownFields.mergeField(typeId,
497
 
                  UnknownFieldSet.Field.newBuilder()
498
 
                    .addLengthDelimited(rawBytes)
499
 
                    .build());
500
 
                rawBytes = null;
501
 
              }
502
 
            }
503
 
          }
504
 
        } else if (tag == WireFormat.MESSAGE_SET_MESSAGE_TAG) {
505
 
          if (typeId == 0) {
506
 
            // We haven't seen a type ID yet, so we have to store the raw bytes
507
 
            // for now.
508
 
            rawBytes = input.readBytes();
509
 
          } else if (subBuilder == null) {
510
 
            // We don't know how to parse this.  Ignore it.
511
 
            unknownFields.mergeField(typeId,
512
 
              UnknownFieldSet.Field.newBuilder()
513
 
                .addLengthDelimited(input.readBytes())
514
 
                .build());
515
 
          } else {
516
 
            // We already know the type, so we can parse directly from the input
517
 
            // with no copying.  Hooray!
518
 
            input.readMessage(subBuilder, extensionRegistry);
519
 
          }
520
 
        } else {
521
 
          // Unknown tag.  Skip it.
522
 
          if (!input.skipField(tag)) {
523
 
            break;  // end of group
524
 
          }
525
 
        }
526
 
      }
527
 
 
528
 
      input.checkLastTagWas(WireFormat.MESSAGE_SET_ITEM_END_TAG);
529
 
 
530
 
      if (subBuilder != null) {
531
 
        builder.setField(field, subBuilder.build());
532
 
      }
533
 
    }
534
 
 
535
 
    public BuilderType mergeUnknownFields(final UnknownFieldSet unknownFields) {
536
 
      setUnknownFields(
537
 
        UnknownFieldSet.newBuilder(getUnknownFields())
538
 
                       .mergeFrom(unknownFields)
539
 
                       .build());
540
 
      return (BuilderType) this;
541
 
    }
542
 
 
543
 
    /**
544
 
     * Construct an UninitializedMessageException reporting missing fields in
545
 
     * the given message.
546
 
     */
547
 
    protected static UninitializedMessageException
548
 
        newUninitializedMessageException(Message message) {
549
 
      return new UninitializedMessageException(findMissingFields(message));
550
 
    }
551
 
 
552
 
    /**
553
 
     * Populates {@code this.missingFields} with the full "path" of each
554
 
     * missing required field in the given message.
555
 
     */
556
 
    private static List<String> findMissingFields(final Message message) {
557
 
      final List<String> results = new ArrayList<String>();
558
 
      findMissingFields(message, "", results);
559
 
      return results;
560
 
    }
561
 
 
562
 
    /** Recursive helper implementing {@link #findMissingFields(Message)}. */
563
 
    private static void findMissingFields(final Message message,
564
 
                                          final String prefix,
565
 
                                          final List<String> results) {
566
 
      for (final FieldDescriptor field :
567
 
          message.getDescriptorForType().getFields()) {
568
 
        if (field.isRequired() && !message.hasField(field)) {
569
 
          results.add(prefix + field.getName());
570
 
        }
571
 
      }
572
 
 
573
 
      for (final Map.Entry<FieldDescriptor, Object> entry :
574
 
           message.getAllFields().entrySet()) {
575
 
        final FieldDescriptor field = entry.getKey();
576
 
        final Object value = entry.getValue();
577
 
 
578
 
        if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
579
 
          if (field.isRepeated()) {
580
 
            int i = 0;
581
 
            for (final Object element : (List) value) {
582
 
              findMissingFields((Message) element,
583
 
                                subMessagePrefix(prefix, field, i++),
584
 
                                results);
585
 
            }
586
 
          } else {
587
 
            if (message.hasField(field)) {
588
 
              findMissingFields((Message) value,
589
 
                                subMessagePrefix(prefix, field, -1),
590
 
                                results);
591
 
            }
592
 
          }
593
 
        }
594
 
      }
595
 
    }
596
 
 
597
 
    private static String subMessagePrefix(final String prefix,
598
 
                                           final FieldDescriptor field,
599
 
                                           final int index) {
600
 
      final StringBuilder result = new StringBuilder(prefix);
601
 
      if (field.isExtension()) {
602
 
        result.append('(')
603
 
              .append(field.getFullName())
604
 
              .append(')');
605
 
      } else {
606
 
        result.append(field.getName());
607
 
      }
608
 
      if (index != -1) {
609
 
        result.append('[')
610
 
              .append(index)
611
 
              .append(']');
612
 
      }
613
 
      result.append('.');
614
 
      return result.toString();
615
 
    }
616
 
 
617
 
    // ===============================================================
618
 
    // The following definitions seem to be required in order to make javac
619
 
    // not produce weird errors like:
620
 
    //
621
 
    // java/com/google/protobuf/DynamicMessage.java:203: types
622
 
    //   com.google.protobuf.AbstractMessage.Builder<
623
 
    //     com.google.protobuf.DynamicMessage.Builder> and
624
 
    //   com.google.protobuf.AbstractMessage.Builder<
625
 
    //     com.google.protobuf.DynamicMessage.Builder> are incompatible; both
626
 
    //   define mergeFrom(com.google.protobuf.ByteString), but with unrelated
627
 
    //   return types.
628
 
    //
629
 
    // Strangely, these lines are only needed if javac is invoked separately
630
 
    // on AbstractMessage.java and AbstractMessageLite.java.  If javac is
631
 
    // invoked on both simultaneously, it works.  (Or maybe the important
632
 
    // point is whether or not DynamicMessage.java is compiled together with
633
 
    // AbstractMessageLite.java -- not sure.)  I suspect this is a compiler
634
 
    // bug.
635
 
 
636
 
    @Override
637
 
    public BuilderType mergeFrom(final ByteString data)
638
 
        throws InvalidProtocolBufferException {
639
 
      return super.mergeFrom(data);
640
 
    }
641
 
 
642
 
    @Override
643
 
    public BuilderType mergeFrom(
644
 
        final ByteString data,
645
 
        final ExtensionRegistryLite extensionRegistry)
646
 
        throws InvalidProtocolBufferException {
647
 
      return super.mergeFrom(data, extensionRegistry);
648
 
    }
649
 
 
650
 
    @Override
651
 
    public BuilderType mergeFrom(final byte[] data)
652
 
        throws InvalidProtocolBufferException {
653
 
      return super.mergeFrom(data);
654
 
    }
655
 
 
656
 
    @Override
657
 
    public BuilderType mergeFrom(
658
 
        final byte[] data, final int off, final int len)
659
 
        throws InvalidProtocolBufferException {
660
 
      return super.mergeFrom(data, off, len);
661
 
    }
662
 
 
663
 
    @Override
664
 
    public BuilderType mergeFrom(
665
 
        final byte[] data,
666
 
        final ExtensionRegistryLite extensionRegistry)
667
 
        throws InvalidProtocolBufferException {
668
 
      return super.mergeFrom(data, extensionRegistry);
669
 
    }
670
 
 
671
 
    @Override
672
 
    public BuilderType mergeFrom(
673
 
        final byte[] data, final int off, final int len,
674
 
        final ExtensionRegistryLite extensionRegistry)
675
 
        throws InvalidProtocolBufferException {
676
 
      return super.mergeFrom(data, off, len, extensionRegistry);
677
 
    }
678
 
 
679
 
    @Override
680
 
    public BuilderType mergeFrom(final InputStream input)
681
 
        throws IOException {
682
 
      return super.mergeFrom(input);
683
 
    }
684
 
 
685
 
    @Override
686
 
    public BuilderType mergeFrom(
687
 
        final InputStream input,
688
 
        final ExtensionRegistryLite extensionRegistry)
689
 
        throws IOException {
690
 
      return super.mergeFrom(input, extensionRegistry);
691
 
    }
692
 
 
693
 
    @Override
694
 
    public boolean mergeDelimitedFrom(final InputStream input)
695
 
        throws IOException {
696
 
      return super.mergeDelimitedFrom(input);
697
 
    }
698
 
 
699
 
    @Override
700
 
    public boolean mergeDelimitedFrom(
701
 
        final InputStream input,
702
 
        final ExtensionRegistryLite extensionRegistry)
703
 
        throws IOException {
704
 
      return super.mergeDelimitedFrom(input, extensionRegistry);
705
 
    }
706
 
 
707
 
  }
708
 
}