Subversion Repositories Code-Repo

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
92 Kevin 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
}