| OLD | NEW |
| 1 #region Copyright notice and license | 1 #region Copyright notice and license |
| 2 // Protocol Buffers - Google's data interchange format | 2 // Protocol Buffers - Google's data interchange format |
| 3 // Copyright 2008 Google Inc. All rights reserved. | 3 // Copyright 2008 Google Inc. All rights reserved. |
| 4 // https://developers.google.com/protocol-buffers/ | 4 // https://developers.google.com/protocol-buffers/ |
| 5 // | 5 // |
| 6 // Redistribution and use in source and binary forms, with or without | 6 // Redistribution and use in source and binary forms, with or without |
| 7 // modification, are permitted provided that the following conditions are | 7 // modification, are permitted provided that the following conditions are |
| 8 // met: | 8 // met: |
| 9 // | 9 // |
| 10 // * Redistributions of source code must retain the above copyright | 10 // * Redistributions of source code must retain the above copyright |
| (...skipping 12 matching lines...) Expand all Loading... |
| 23 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | 23 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 24 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 24 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 25 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 25 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 26 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 26 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 27 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 27 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 28 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 28 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 29 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 29 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 30 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 30 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 31 #endregion | 31 #endregion |
| 32 | 32 |
| 33 using Google.Protobuf.Compatibility; |
| 33 using System; | 34 using System; |
| 34 using System.Linq; | |
| 35 using Google.Protobuf.Compatibility; | |
| 36 | 35 |
| 37 namespace Google.Protobuf.Reflection | 36 namespace Google.Protobuf.Reflection |
| 38 { | 37 { |
| 39 /// <summary> | 38 /// <summary> |
| 40 /// Descriptor for a field or extension within a message in a .proto file. | 39 /// Descriptor for a field or extension within a message in a .proto file. |
| 41 /// </summary> | 40 /// </summary> |
| 42 public sealed class FieldDescriptor : DescriptorBase, IComparable<FieldDescr
iptor> | 41 public sealed class FieldDescriptor : DescriptorBase, IComparable<FieldDescr
iptor> |
| 43 { | 42 { |
| 44 private readonly FieldDescriptorProto proto; | |
| 45 private EnumDescriptor enumType; | 43 private EnumDescriptor enumType; |
| 46 private MessageDescriptor messageType; | 44 private MessageDescriptor messageType; |
| 47 private readonly MessageDescriptor containingType; | |
| 48 private readonly OneofDescriptor containingOneof; | |
| 49 private FieldType fieldType; | 45 private FieldType fieldType; |
| 50 private readonly string propertyName; // Annoyingly, needed in Crosslink
. | 46 private readonly string propertyName; // Annoyingly, needed in Crosslink
. |
| 51 private IFieldAccessor accessor; | 47 private IFieldAccessor accessor; |
| 52 | 48 |
| 49 /// <summary> |
| 50 /// Get the field's containing message type. |
| 51 /// </summary> |
| 52 public MessageDescriptor ContainingType { get; } |
| 53 |
| 54 /// <summary> |
| 55 /// Returns the oneof containing this field, or <c>null</c> if it is not
part of a oneof. |
| 56 /// </summary> |
| 57 public OneofDescriptor ContainingOneof { get; } |
| 58 |
| 59 /// <summary> |
| 60 /// The effective JSON name for this field. This is usually the lower-ca
mel-cased form of the field name, |
| 61 /// but can be overridden using the <c>json_name</c> option in the .prot
o file. |
| 62 /// </summary> |
| 63 public string JsonName { get; } |
| 64 |
| 65 internal FieldDescriptorProto Proto { get; } |
| 66 |
| 53 internal FieldDescriptor(FieldDescriptorProto proto, FileDescriptor file
, | 67 internal FieldDescriptor(FieldDescriptorProto proto, FileDescriptor file
, |
| 54 MessageDescriptor parent, int index, string pro
pertyName) | 68 MessageDescriptor parent, int index, string pro
pertyName) |
| 55 : base(file, file.ComputeFullName(parent, proto.Name), index) | 69 : base(file, file.ComputeFullName(parent, proto.Name), index) |
| 56 { | 70 { |
| 57 this.proto = proto; | 71 Proto = proto; |
| 58 if (proto.Type != 0) | 72 if (proto.Type != 0) |
| 59 { | 73 { |
| 60 fieldType = GetFieldTypeFromProtoType(proto.Type); | 74 fieldType = GetFieldTypeFromProtoType(proto.Type); |
| 61 } | 75 } |
| 62 | 76 |
| 63 if (FieldNumber <= 0) | 77 if (FieldNumber <= 0) |
| 64 { | 78 { |
| 65 throw new DescriptorValidationException(this, "Field numbers mus
t be positive integers."); | 79 throw new DescriptorValidationException(this, "Field numbers mus
t be positive integers."); |
| 66 } | 80 } |
| 67 containingType = parent; | 81 ContainingType = parent; |
| 68 // OneofIndex "defaults" to -1 due to a hack in FieldDescriptor.OnCo
nstruction. | 82 // OneofIndex "defaults" to -1 due to a hack in FieldDescriptor.OnCo
nstruction. |
| 69 if (proto.OneofIndex != -1) | 83 if (proto.OneofIndex != -1) |
| 70 { | 84 { |
| 71 if (proto.OneofIndex < 0 || proto.OneofIndex >= parent.Proto.One
ofDecl.Count) | 85 if (proto.OneofIndex < 0 || proto.OneofIndex >= parent.Proto.One
ofDecl.Count) |
| 72 { | 86 { |
| 73 throw new DescriptorValidationException(this, | 87 throw new DescriptorValidationException(this, |
| 74 $"FieldDescriptorProto.oneof_index is out of range for t
ype {parent.Name}"); | 88 $"FieldDescriptorProto.oneof_index is out of range for t
ype {parent.Name}"); |
| 75 } | 89 } |
| 76 containingOneof = parent.Oneofs[proto.OneofIndex]; | 90 ContainingOneof = parent.Oneofs[proto.OneofIndex]; |
| 77 } | 91 } |
| 78 | 92 |
| 79 file.DescriptorPool.AddSymbol(this); | 93 file.DescriptorPool.AddSymbol(this); |
| 80 // We can't create the accessor until we've cross-linked, unfortunat
ely, as we | 94 // We can't create the accessor until we've cross-linked, unfortunat
ely, as we |
| 81 // may not know whether the type of the field is a map or not. Remem
ber the property name | 95 // may not know whether the type of the field is a map or not. Remem
ber the property name |
| 82 // for later. | 96 // for later. |
| 83 // We could trust the generated code and check whether the type of t
he property is | 97 // We could trust the generated code and check whether the type of t
he property is |
| 84 // a MapField, but that feels a tad nasty. | 98 // a MapField, but that feels a tad nasty. |
| 85 this.propertyName = propertyName; | 99 this.propertyName = propertyName; |
| 100 JsonName = Proto.JsonName == "" ? JsonFormatter.ToCamelCase(Proto.N
ame) : Proto.JsonName; |
| 86 } | 101 } |
| 102 |
| 87 | 103 |
| 88 /// <summary> | 104 /// <summary> |
| 89 /// The brief name of the descriptor's target. | 105 /// The brief name of the descriptor's target. |
| 90 /// </summary> | 106 /// </summary> |
| 91 public override string Name { get { return proto.Name; } } | 107 public override string Name => Proto.Name; |
| 92 | |
| 93 internal FieldDescriptorProto Proto { get { return proto; } } | |
| 94 | 108 |
| 95 /// <summary> | 109 /// <summary> |
| 96 /// Returns the accessor for this field. | 110 /// Returns the accessor for this field. |
| 97 /// </summary> | 111 /// </summary> |
| 98 /// <remarks> | 112 /// <remarks> |
| 99 /// <para> | 113 /// <para> |
| 100 /// While a <see cref="FieldDescriptor"/> describes the field, it does n
ot provide | 114 /// While a <see cref="FieldDescriptor"/> describes the field, it does n
ot provide |
| 101 /// any way of obtaining or changing the value of the field within a spe
cific message; | 115 /// any way of obtaining or changing the value of the field within a spe
cific message; |
| 102 /// that is the responsibility of the accessor. | 116 /// that is the responsibility of the accessor. |
| 103 /// </para> | 117 /// </para> |
| 104 /// <para> | 118 /// <para> |
| 105 /// The value returned by this property will be non-null for all regular
fields. However, | 119 /// The value returned by this property will be non-null for all regular
fields. However, |
| 106 /// if a message containing a map field is introspected, the list of nes
ted messages will include | 120 /// if a message containing a map field is introspected, the list of nes
ted messages will include |
| 107 /// an auto-generated nested key/value pair message for the field. This
is not represented in any | 121 /// an auto-generated nested key/value pair message for the field. This
is not represented in any |
| 108 /// generated type, and the value of the map field itself is represented
by a dictionary in the | 122 /// generated type, and the value of the map field itself is represented
by a dictionary in the |
| 109 /// reflection API. There are never instances of those "hidden" messages
, so no accessor is provided | 123 /// reflection API. There are never instances of those "hidden" messages
, so no accessor is provided |
| 110 /// and this property will return null. | 124 /// and this property will return null. |
| 111 /// </para> | 125 /// </para> |
| 112 /// </remarks> | 126 /// </remarks> |
| 113 public IFieldAccessor Accessor { get { return accessor; } } | 127 public IFieldAccessor Accessor => accessor; |
| 114 | 128 |
| 115 /// <summary> | 129 /// <summary> |
| 116 /// Maps a field type as included in the .proto file to a FieldType. | 130 /// Maps a field type as included in the .proto file to a FieldType. |
| 117 /// </summary> | 131 /// </summary> |
| 118 private static FieldType GetFieldTypeFromProtoType(FieldDescriptorProto.
Types.Type type) | 132 private static FieldType GetFieldTypeFromProtoType(FieldDescriptorProto.
Types.Type type) |
| 119 { | 133 { |
| 120 switch (type) | 134 switch (type) |
| 121 { | 135 { |
| 122 case FieldDescriptorProto.Types.Type.TYPE_DOUBLE: | 136 case FieldDescriptorProto.Types.Type.Double: |
| 123 return FieldType.Double; | 137 return FieldType.Double; |
| 124 case FieldDescriptorProto.Types.Type.TYPE_FLOAT: | 138 case FieldDescriptorProto.Types.Type.Float: |
| 125 return FieldType.Float; | 139 return FieldType.Float; |
| 126 case FieldDescriptorProto.Types.Type.TYPE_INT64: | 140 case FieldDescriptorProto.Types.Type.Int64: |
| 127 return FieldType.Int64; | 141 return FieldType.Int64; |
| 128 case FieldDescriptorProto.Types.Type.TYPE_UINT64: | 142 case FieldDescriptorProto.Types.Type.Uint64: |
| 129 return FieldType.UInt64; | 143 return FieldType.UInt64; |
| 130 case FieldDescriptorProto.Types.Type.TYPE_INT32: | 144 case FieldDescriptorProto.Types.Type.Int32: |
| 131 return FieldType.Int32; | 145 return FieldType.Int32; |
| 132 case FieldDescriptorProto.Types.Type.TYPE_FIXED64: | 146 case FieldDescriptorProto.Types.Type.Fixed64: |
| 133 return FieldType.Fixed64; | 147 return FieldType.Fixed64; |
| 134 case FieldDescriptorProto.Types.Type.TYPE_FIXED32: | 148 case FieldDescriptorProto.Types.Type.Fixed32: |
| 135 return FieldType.Fixed32; | 149 return FieldType.Fixed32; |
| 136 case FieldDescriptorProto.Types.Type.TYPE_BOOL: | 150 case FieldDescriptorProto.Types.Type.Bool: |
| 137 return FieldType.Bool; | 151 return FieldType.Bool; |
| 138 case FieldDescriptorProto.Types.Type.TYPE_STRING: | 152 case FieldDescriptorProto.Types.Type.String: |
| 139 return FieldType.String; | 153 return FieldType.String; |
| 140 case FieldDescriptorProto.Types.Type.TYPE_GROUP: | 154 case FieldDescriptorProto.Types.Type.Group: |
| 141 return FieldType.Group; | 155 return FieldType.Group; |
| 142 case FieldDescriptorProto.Types.Type.TYPE_MESSAGE: | 156 case FieldDescriptorProto.Types.Type.Message: |
| 143 return FieldType.Message; | 157 return FieldType.Message; |
| 144 case FieldDescriptorProto.Types.Type.TYPE_BYTES: | 158 case FieldDescriptorProto.Types.Type.Bytes: |
| 145 return FieldType.Bytes; | 159 return FieldType.Bytes; |
| 146 case FieldDescriptorProto.Types.Type.TYPE_UINT32: | 160 case FieldDescriptorProto.Types.Type.Uint32: |
| 147 return FieldType.UInt32; | 161 return FieldType.UInt32; |
| 148 case FieldDescriptorProto.Types.Type.TYPE_ENUM: | 162 case FieldDescriptorProto.Types.Type.Enum: |
| 149 return FieldType.Enum; | 163 return FieldType.Enum; |
| 150 case FieldDescriptorProto.Types.Type.TYPE_SFIXED32: | 164 case FieldDescriptorProto.Types.Type.Sfixed32: |
| 151 return FieldType.SFixed32; | 165 return FieldType.SFixed32; |
| 152 case FieldDescriptorProto.Types.Type.TYPE_SFIXED64: | 166 case FieldDescriptorProto.Types.Type.Sfixed64: |
| 153 return FieldType.SFixed64; | 167 return FieldType.SFixed64; |
| 154 case FieldDescriptorProto.Types.Type.TYPE_SINT32: | 168 case FieldDescriptorProto.Types.Type.Sint32: |
| 155 return FieldType.SInt32; | 169 return FieldType.SInt32; |
| 156 case FieldDescriptorProto.Types.Type.TYPE_SINT64: | 170 case FieldDescriptorProto.Types.Type.Sint64: |
| 157 return FieldType.SInt64; | 171 return FieldType.SInt64; |
| 158 default: | 172 default: |
| 159 throw new ArgumentException("Invalid type specified"); | 173 throw new ArgumentException("Invalid type specified"); |
| 160 } | 174 } |
| 161 } | 175 } |
| 162 | 176 |
| 163 /// <summary> | 177 /// <summary> |
| 164 /// Returns <c>true</c> if this field is a repeated field; <c>false</c>
otherwise. | 178 /// Returns <c>true</c> if this field is a repeated field; <c>false</c>
otherwise. |
| 165 /// </summary> | 179 /// </summary> |
| 166 public bool IsRepeated | 180 public bool IsRepeated => Proto.Label == FieldDescriptorProto.Types.Labe
l.Repeated; |
| 167 { | |
| 168 get { return Proto.Label == FieldDescriptorProto.Types.Label.LABEL_R
EPEATED; } | |
| 169 } | |
| 170 | 181 |
| 171 /// <summary> | 182 /// <summary> |
| 172 /// Returns <c>true</c> if this field is a map field; <c>false</c> other
wise. | 183 /// Returns <c>true</c> if this field is a map field; <c>false</c> other
wise. |
| 173 /// </summary> | 184 /// </summary> |
| 174 public bool IsMap | 185 public bool IsMap => fieldType == FieldType.Message && messageType.Proto
.Options != null && messageType.Proto.Options.MapEntry; |
| 175 { | |
| 176 get { return fieldType == FieldType.Message && messageType.Proto.Opt
ions != null && messageType.Proto.Options.MapEntry; } | |
| 177 } | |
| 178 | 186 |
| 179 /// <summary> | 187 /// <summary> |
| 180 /// Returns <c>true</c> if this field is a packed, repeated field; <c>fa
lse</c> otherwise. | 188 /// Returns <c>true</c> if this field is a packed, repeated field; <c>fa
lse</c> otherwise. |
| 181 /// </summary> | 189 /// </summary> |
| 182 public bool IsPacked | 190 public bool IsPacked => |
| 183 { | |
| 184 // Note the || rather than && here - we're effectively defaulting to
packed, because that *is* | 191 // Note the || rather than && here - we're effectively defaulting to
packed, because that *is* |
| 185 // the default in proto3, which is all we support. We may give the w
rong result for the protos | 192 // the default in proto3, which is all we support. We may give the w
rong result for the protos |
| 186 // within descriptor.proto, but that's okay, as they're never expose
d and we don't use IsPacked | 193 // within descriptor.proto, but that's okay, as they're never expose
d and we don't use IsPacked |
| 187 // within the runtime. | 194 // within the runtime. |
| 188 get { return Proto.Options == null || Proto.Options.Packed; } | 195 Proto.Options == null || Proto.Options.Packed; |
| 189 } | 196 |
| 190 | |
| 191 /// <summary> | |
| 192 /// Get the field's containing message type. | |
| 193 /// </summary> | |
| 194 public MessageDescriptor ContainingType | |
| 195 { | |
| 196 get { return containingType; } | |
| 197 } | |
| 198 | |
| 199 /// <summary> | |
| 200 /// Returns the oneof containing this field, or <c>null</c> if it is not
part of a oneof. | |
| 201 /// </summary> | |
| 202 public OneofDescriptor ContainingOneof | |
| 203 { | |
| 204 get { return containingOneof; } | |
| 205 } | |
| 206 | |
| 207 /// <summary> | 197 /// <summary> |
| 208 /// Returns the type of the field. | 198 /// Returns the type of the field. |
| 209 /// </summary> | 199 /// </summary> |
| 210 public FieldType FieldType | 200 public FieldType FieldType => fieldType; |
| 211 { | |
| 212 get { return fieldType; } | |
| 213 } | |
| 214 | 201 |
| 215 /// <summary> | 202 /// <summary> |
| 216 /// Returns the field number declared in the proto file. | 203 /// Returns the field number declared in the proto file. |
| 217 /// </summary> | 204 /// </summary> |
| 218 public int FieldNumber | 205 public int FieldNumber => Proto.Number; |
| 219 { | |
| 220 get { return Proto.Number; } | |
| 221 } | |
| 222 | 206 |
| 223 /// <summary> | 207 /// <summary> |
| 224 /// Compares this descriptor with another one, ordering in "canonical" o
rder | 208 /// Compares this descriptor with another one, ordering in "canonical" o
rder |
| 225 /// which simply means ascending order by field number. <paramref name="
other"/> | 209 /// which simply means ascending order by field number. <paramref name="
other"/> |
| 226 /// must be a field of the same type, i.e. the <see cref="ContainingType
"/> of | 210 /// must be a field of the same type, i.e. the <see cref="ContainingType
"/> of |
| 227 /// both fields must be the same. | 211 /// both fields must be the same. |
| 228 /// </summary> | 212 /// </summary> |
| 229 public int CompareTo(FieldDescriptor other) | 213 public int CompareTo(FieldDescriptor other) |
| 230 { | 214 { |
| 231 if (other.containingType != containingType) | 215 if (other.ContainingType != ContainingType) |
| 232 { | 216 { |
| 233 throw new ArgumentException("FieldDescriptors can only be compar
ed to other FieldDescriptors " + | 217 throw new ArgumentException("FieldDescriptors can only be compar
ed to other FieldDescriptors " + |
| 234 "for fields of the same message type
."); | 218 "for fields of the same message type
."); |
| 235 } | 219 } |
| 236 return FieldNumber - other.FieldNumber; | 220 return FieldNumber - other.FieldNumber; |
| 237 } | 221 } |
| 238 | 222 |
| 239 /// <summary> | 223 /// <summary> |
| 240 /// For enum fields, returns the field's type. | 224 /// For enum fields, returns the field's type. |
| 241 /// </summary> | 225 /// </summary> |
| (...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 324 if (fieldType == FieldType.Message || fieldType == FieldType.Enu
m) | 308 if (fieldType == FieldType.Message || fieldType == FieldType.Enu
m) |
| 325 { | 309 { |
| 326 throw new DescriptorValidationException(this, "Field with me
ssage or enum type missing type_name."); | 310 throw new DescriptorValidationException(this, "Field with me
ssage or enum type missing type_name."); |
| 327 } | 311 } |
| 328 } | 312 } |
| 329 | 313 |
| 330 // Note: no attempt to perform any default value parsing | 314 // Note: no attempt to perform any default value parsing |
| 331 | 315 |
| 332 File.DescriptorPool.AddFieldByNumber(this); | 316 File.DescriptorPool.AddFieldByNumber(this); |
| 333 | 317 |
| 334 if (containingType != null && containingType.Proto.Options != null &
& containingType.Proto.Options.MessageSetWireFormat) | 318 if (ContainingType != null && ContainingType.Proto.Options != null &
& ContainingType.Proto.Options.MessageSetWireFormat) |
| 335 { | 319 { |
| 336 throw new DescriptorValidationException(this, "MessageSet format
is not supported."); | 320 throw new DescriptorValidationException(this, "MessageSet format
is not supported."); |
| 337 } | 321 } |
| 338 accessor = CreateAccessor(propertyName); | 322 accessor = CreateAccessor(); |
| 339 } | 323 } |
| 340 | 324 |
| 341 private IFieldAccessor CreateAccessor(string propertyName) | 325 private IFieldAccessor CreateAccessor() |
| 342 { | 326 { |
| 343 // If we're given no property name, that's because we really don't w
ant an accessor. | 327 // If we're given no property name, that's because we really don't w
ant an accessor. |
| 344 // (At the moment, that means it's a map entry message...) | 328 // (At the moment, that means it's a map entry message...) |
| 345 if (propertyName == null) | 329 if (propertyName == null) |
| 346 { | 330 { |
| 347 return null; | 331 return null; |
| 348 } | 332 } |
| 349 var property = containingType.ClrType.GetProperty(propertyName); | 333 var property = ContainingType.ClrType.GetProperty(propertyName); |
| 350 if (property == null) | 334 if (property == null) |
| 351 { | 335 { |
| 352 throw new DescriptorValidationException(this, $"Property {proper
tyName} not found in {containingType.ClrType}"); | 336 throw new DescriptorValidationException(this, $"Property {proper
tyName} not found in {ContainingType.ClrType}"); |
| 353 } | 337 } |
| 354 return IsMap ? new MapFieldAccessor(property, this) | 338 return IsMap ? new MapFieldAccessor(property, this) |
| 355 : IsRepeated ? new RepeatedFieldAccessor(property, this) | 339 : IsRepeated ? new RepeatedFieldAccessor(property, this) |
| 356 : (IFieldAccessor) new SingleFieldAccessor(property, this); | 340 : (IFieldAccessor) new SingleFieldAccessor(property, this); |
| 357 } | 341 } |
| 358 } | 342 } |
| 359 } | 343 } |
| OLD | NEW |