MessageDescriptor.cs 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. // Protocol Buffers - Google's data interchange format
  2. // Copyright 2008 Google Inc.
  3. // http://code.google.com/p/protobuf/
  4. //
  5. // Licensed under the Apache License, Version 2.0 (the "License");
  6. // you may not use this file except in compliance with the License.
  7. // You may obtain a copy of the License at
  8. //
  9. // http://www.apache.org/licenses/LICENSE-2.0
  10. //
  11. // Unless required by applicable law or agreed to in writing, software
  12. // distributed under the License is distributed on an "AS IS" BASIS,
  13. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. // See the License for the specific language governing permissions and
  15. // limitations under the License.
  16. using System.Collections.Generic;
  17. using Google.ProtocolBuffers.DescriptorProtos;
  18. namespace Google.ProtocolBuffers.Descriptors {
  19. /// <summary>
  20. /// Describes a message type.
  21. /// </summary>
  22. public sealed class MessageDescriptor : IndexedDescriptorBase<DescriptorProto, MessageOptions> {
  23. private readonly MessageDescriptor containingType;
  24. private readonly IList<MessageDescriptor> nestedTypes;
  25. private readonly IList<EnumDescriptor> enumTypes;
  26. private readonly IList<FieldDescriptor> fields;
  27. private readonly IList<FieldDescriptor> extensions;
  28. private bool hasRequiredFields;
  29. internal MessageDescriptor(DescriptorProto proto, FileDescriptor file, MessageDescriptor parent, int typeIndex)
  30. : base(proto, file, ComputeFullName(file, parent, proto.Name), typeIndex) {
  31. containingType = parent;
  32. nestedTypes = DescriptorUtil.ConvertAndMakeReadOnly(proto.NestedTypeList,
  33. (type, index) => new MessageDescriptor(type, file, this, index));
  34. enumTypes = DescriptorUtil.ConvertAndMakeReadOnly(proto.EnumTypeList,
  35. (type, index) => new EnumDescriptor(type, file, this, index));
  36. fields = DescriptorUtil.ConvertAndMakeReadOnly(proto.FieldList,
  37. (field, index) => new FieldDescriptor(field, file, this, index, false));
  38. extensions = DescriptorUtil.ConvertAndMakeReadOnly(proto.ExtensionList,
  39. (field, index) => new FieldDescriptor(field, file, this, index, true));
  40. file.DescriptorPool.AddSymbol(this);
  41. }
  42. /// <value>
  43. /// If this is a nested type, get the outer descriptor, otherwise null.
  44. /// </value>
  45. public MessageDescriptor ContainingType {
  46. get { return containingType; }
  47. }
  48. /// <value>
  49. /// An unmodifiable list of this message type's fields.
  50. /// </value>
  51. public IList<FieldDescriptor> Fields {
  52. get { return fields; }
  53. }
  54. /// <value>
  55. /// An unmodifiable list of this message type's extensions.
  56. /// </value>
  57. public IList<FieldDescriptor> Extensions {
  58. get { return extensions; }
  59. }
  60. /// <value>
  61. /// An unmodifiable list of this message type's nested types.
  62. /// </value>
  63. public IList<MessageDescriptor> NestedTypes {
  64. get { return nestedTypes; }
  65. }
  66. /// <value>
  67. /// An unmodifiable list of this message type's enum types.
  68. /// </value>
  69. public IList<EnumDescriptor> EnumTypes {
  70. get { return enumTypes; }
  71. }
  72. /// <summary>
  73. /// Returns a pre-computed result as to whether this message
  74. /// has required fields. This includes optional fields which are
  75. /// message types which in turn have required fields, and any
  76. /// extension fields.
  77. /// </summary>
  78. internal bool HasRequiredFields {
  79. get { return hasRequiredFields; }
  80. }
  81. /// <summary>
  82. /// Determines if the given field number is an extension.
  83. /// </summary>
  84. public bool IsExtensionNumber(int number) {
  85. foreach (DescriptorProto.Types.ExtensionRange range in Proto.ExtensionRangeList) {
  86. if (range.Start <= number && number < range.End) {
  87. return true;
  88. }
  89. }
  90. return false;
  91. }
  92. /// <summary>
  93. /// Finds a field by field number.
  94. /// </summary>
  95. /// <param name="number">The field number within this message type.</param>
  96. /// <returns>The field's descriptor, or null if not found.</returns>
  97. public FieldDescriptor FindFieldByNumber(int number) {
  98. return File.DescriptorPool.FindFieldByNumber(this, number);
  99. }
  100. /// <summary>
  101. /// Finds a nested descriptor by name. The is valid for fields, nested
  102. /// message types and enums.
  103. /// </summary>
  104. /// <param name="name">The unqualified name of the descriptor, e.g. "Foo"</param>
  105. /// <returns>The descriptor, or null if not found.</returns>
  106. public T FindDescriptor<T>(string name)
  107. where T : class, IDescriptor {
  108. return File.DescriptorPool.FindSymbol<T>(FullName + "." + name);
  109. }
  110. /// <summary>
  111. /// Looks up and cross-links all fields, nested types, and extensions.
  112. /// </summary>
  113. internal void CrossLink() {
  114. foreach (MessageDescriptor message in nestedTypes) {
  115. message.CrossLink();
  116. }
  117. foreach (FieldDescriptor field in fields) {
  118. field.CrossLink();
  119. }
  120. foreach (FieldDescriptor extension in extensions) {
  121. extension.CrossLink();
  122. }
  123. }
  124. internal void CheckRequiredFields() {
  125. IDictionary<MessageDescriptor, byte> alreadySeen = new Dictionary<MessageDescriptor, byte>();
  126. hasRequiredFields = CheckRequiredFields(alreadySeen);
  127. }
  128. private bool CheckRequiredFields(IDictionary<MessageDescriptor,byte> alreadySeen) {
  129. if (alreadySeen.ContainsKey(this)) {
  130. // The type is already in the cache. This means that either:
  131. // a. The type has no required fields.
  132. // b. We are in the midst of checking if the type has required fields,
  133. // somewhere up the stack. In this case, we know that if the type
  134. // has any required fields, they'll be found when we return to it,
  135. // and the whole call to HasRequiredFields() will return true.
  136. // Therefore, we don't have to check if this type has required fields
  137. // here.
  138. return false;
  139. }
  140. alreadySeen[this] = 0; // Value is irrelevant; we want set semantics
  141. // If the type allows extensions, an extension with message type could contain
  142. // required fields, so we have to be conservative and assume such an
  143. // extension exists.
  144. if (Proto.ExtensionRangeCount != 0) {
  145. return true;
  146. }
  147. foreach (FieldDescriptor field in Fields) {
  148. if (field.IsRequired) {
  149. return true;
  150. }
  151. if (field.MappedType == MappedType.Message) {
  152. if (field.MessageType.CheckRequiredFields(alreadySeen)) {
  153. return true;
  154. }
  155. }
  156. }
  157. return false;
  158. }
  159. }
  160. }