You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
392 lines
17 KiB
392 lines
17 KiB
#Region "Microsoft.VisualBasic::a2296fe33c8bd460a495d653fb91baaa, Google.Protobuf\Reflection\Descriptor\FieldDescriptor.vb"
|
|
|
|
' Author:
|
|
'
|
|
' asuka (amethyst.asuka@gcmodeller.org)
|
|
' xie (genetics@smrucc.org)
|
|
' xieguigang (xie.guigang@live.com)
|
|
'
|
|
' Copyright (c) 2018 GPL3 Licensed
|
|
'
|
|
'
|
|
' GNU GENERAL PUBLIC LICENSE (GPL3)
|
|
'
|
|
'
|
|
' This program is free software: you can redistribute it and/or modify
|
|
' it under the terms of the GNU General Public License as published by
|
|
' the Free Software Foundation, either version 3 of the License, or
|
|
' (at your option) any later version.
|
|
'
|
|
' This program is distributed in the hope that it will be useful,
|
|
' but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
' GNU General Public License for more details.
|
|
'
|
|
' You should have received a copy of the GNU General Public License
|
|
' along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
' /********************************************************************************/
|
|
|
|
' Summaries:
|
|
|
|
' Class FieldDescriptor
|
|
'
|
|
' Properties: Accessor, ContainingOneof, ContainingType, EnumType, FieldNumber
|
|
' FieldType, IsMap, IsPacked, IsRepeated, JsonName
|
|
' MessageType, Name, Proto
|
|
'
|
|
' Constructor: (+1 Overloads) Sub New
|
|
'
|
|
' Function: CompareTo, CreateAccessor, GetFieldTypeFromProtoType
|
|
'
|
|
' Sub: CrossLink
|
|
'
|
|
'
|
|
' /********************************************************************************/
|
|
|
|
#End Region
|
|
|
|
#Region "Copyright notice and license"
|
|
' Protocol Buffers - Google's data interchange format
|
|
' Copyright 2008 Google Inc. All rights reserved.
|
|
' https://developers.google.com/protocol-buffers/
|
|
'
|
|
' Redistribution and use in source and binary forms, with or without
|
|
' modification, are permitted provided that the following conditions are
|
|
' met:
|
|
'
|
|
' * Redistributions of source code must retain the above copyright
|
|
' notice, this list of conditions and the following disclaimer.
|
|
' * Redistributions in binary form must reproduce the above
|
|
' copyright notice, this list of conditions and the following disclaimer
|
|
' in the documentation and/or other materials provided with the
|
|
' distribution.
|
|
' * Neither the name of Google Inc. nor the names of its
|
|
' contributors may be used to endorse or promote products derived from
|
|
' this software without specific prior written permission.
|
|
'
|
|
' THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
' "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
' LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
' A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
' OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
' SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
' LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
' DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
' THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
' (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
' OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
#End Region
|
|
|
|
Imports System
|
|
|
|
Namespace Google.Protobuf.Reflection
|
|
''' <summary>
|
|
''' Descriptor for a field or extension within a message in a .proto file.
|
|
''' </summary>
|
|
Public NotInheritable Class FieldDescriptor
|
|
Inherits DescriptorBase
|
|
Implements IComparable(Of FieldDescriptor)
|
|
|
|
Private enumTypeField As EnumDescriptor
|
|
Private messageTypeField As MessageDescriptor
|
|
Private fieldTypeField As FieldType
|
|
Private ReadOnly propertyName As String ' Annoyingly, needed in Crosslink.
|
|
Private accessorField As IFieldAccessor
|
|
|
|
''' <summary>
|
|
''' Get the field's containing message type.
|
|
''' </summary>
|
|
Public ReadOnly Property ContainingType As MessageDescriptor
|
|
|
|
''' <summary>
|
|
''' Returns the oneof containing this field, or <c>null</c> if it is not part of a oneof.
|
|
''' </summary>
|
|
Public ReadOnly Property ContainingOneof As OneofDescriptor
|
|
|
|
''' <summary>
|
|
''' The effective JSON name for this field. This is usually the lower-camel-cased form of the field name,
|
|
''' but can be overridden using the <c>json_name</c> option in the .proto file.
|
|
''' </summary>
|
|
Public ReadOnly Property JsonName As String
|
|
Friend ReadOnly Property Proto As FieldDescriptorProto
|
|
|
|
Friend Sub New(proto As FieldDescriptorProto, file As FileDescriptor, parent As MessageDescriptor, index As Integer, propertyName As String)
|
|
MyBase.New(file, file.ComputeFullName(parent, proto.Name), index)
|
|
Me.Proto = proto
|
|
|
|
If proto.Type <> 0 Then
|
|
fieldTypeField = FieldDescriptor.GetFieldTypeFromProtoType(proto.Type)
|
|
End If
|
|
|
|
If FieldNumber <= 0 Then
|
|
Throw New DescriptorValidationException(Me, "Field numbers must be positive integers.")
|
|
End If
|
|
|
|
ContainingType = parent
|
|
' OneofIndex "defaults" to -1 due to a hack in FieldDescriptor.OnConstruction.
|
|
If proto.OneofIndex <> -1 Then
|
|
If proto.OneofIndex < 0 OrElse proto.OneofIndex >= parent.Proto.OneofDecl.Count Then
|
|
Throw New DescriptorValidationException(Me, $"FieldDescriptorProto.oneof_index is out of range for type {parent.Name}")
|
|
End If
|
|
|
|
ContainingOneof = parent.Oneofs(proto.OneofIndex)
|
|
End If
|
|
|
|
file.DescriptorPool.AddSymbol(Me)
|
|
' We can't create the accessor until we've cross-linked, unfortunately, as we
|
|
' may not know whether the type of the field is a map or not. Remember the property name
|
|
' for later.
|
|
' We could trust the generated code and check whether the type of the property is
|
|
' a MapField, but that feels a tad nasty.
|
|
Me.propertyName = propertyName
|
|
JsonName = If(Equals(Me.Proto.JsonName, ""), JsonFormatter.ToCamelCase(Me.Proto.Name), Me.Proto.JsonName)
|
|
End Sub
|
|
|
|
|
|
''' <summary>
|
|
''' The brief name of the descriptor's target.
|
|
''' </summary>
|
|
Public Overrides ReadOnly Property Name As String
|
|
Get
|
|
Return Proto.Name
|
|
End Get
|
|
End Property
|
|
|
|
''' <summary>
|
|
''' Returns the accessor for this field.
|
|
''' </summary>
|
|
''' <remarks>
|
|
''' <para>
|
|
''' While a <see cref="FieldDescriptor"/> describes the field, it does not provide
|
|
''' any way of obtaining or changing the value of the field within a specific message;
|
|
''' that is the responsibility of the accessor.
|
|
''' </para>
|
|
''' <para>
|
|
''' The value returned by this property will be non-null for all regular fields. However,
|
|
''' if a message containing a map field is introspected, the list of nested messages will include
|
|
''' an auto-generated nested key/value pair message for the field. This is not represented in any
|
|
''' generated type, and the value of the map field itself is represented by a dictionary in the
|
|
''' reflection API. There are never instances of those "hidden" messages, so no accessor is provided
|
|
''' and this property will return null.
|
|
''' </para>
|
|
''' </remarks>
|
|
Public ReadOnly Property Accessor As IFieldAccessor
|
|
Get
|
|
Return accessorField
|
|
End Get
|
|
End Property
|
|
|
|
''' <summary>
|
|
''' Maps a field type as included in the .proto file to a FieldType.
|
|
''' </summary>
|
|
Private Shared Function GetFieldTypeFromProtoType(type As FieldDescriptorProto.Types.Type) As FieldType
|
|
Select Case type
|
|
Case FieldDescriptorProto.Types.Type.Double
|
|
Return FieldType.Double
|
|
Case FieldDescriptorProto.Types.Type.Float
|
|
Return FieldType.Float
|
|
Case FieldDescriptorProto.Types.Type.Int64
|
|
Return FieldType.Int64
|
|
Case FieldDescriptorProto.Types.Type.Uint64
|
|
Return FieldType.UInt64
|
|
Case FieldDescriptorProto.Types.Type.Int32
|
|
Return FieldType.Int32
|
|
Case FieldDescriptorProto.Types.Type.Fixed64
|
|
Return FieldType.Fixed64
|
|
Case FieldDescriptorProto.Types.Type.Fixed32
|
|
Return FieldType.Fixed32
|
|
Case FieldDescriptorProto.Types.Type.Bool
|
|
Return FieldType.Bool
|
|
Case FieldDescriptorProto.Types.Type.String
|
|
Return FieldType.String
|
|
Case FieldDescriptorProto.Types.Type.Group
|
|
Return FieldType.Group
|
|
Case FieldDescriptorProto.Types.Type.Message
|
|
Return FieldType.Message
|
|
Case FieldDescriptorProto.Types.Type.Bytes
|
|
Return FieldType.Bytes
|
|
Case FieldDescriptorProto.Types.Type.Uint32
|
|
Return FieldType.UInt32
|
|
Case FieldDescriptorProto.Types.Type.Enum
|
|
Return FieldType.Enum
|
|
Case FieldDescriptorProto.Types.Type.Sfixed32
|
|
Return FieldType.SFixed32
|
|
Case FieldDescriptorProto.Types.Type.Sfixed64
|
|
Return FieldType.SFixed64
|
|
Case FieldDescriptorProto.Types.Type.Sint32
|
|
Return FieldType.SInt32
|
|
Case FieldDescriptorProto.Types.Type.Sint64
|
|
Return FieldType.SInt64
|
|
Case Else
|
|
Throw New ArgumentException("Invalid type specified")
|
|
End Select
|
|
End Function
|
|
|
|
''' <summary>
|
|
''' Returns <c>true</c> if this field is a repeated field; <c>false</c> otherwise.
|
|
''' </summary>
|
|
Public ReadOnly Property IsRepeated As Boolean
|
|
Get
|
|
Return Proto.Label = FieldDescriptorProto.Types.Label.Repeated
|
|
End Get
|
|
End Property
|
|
|
|
''' <summary>
|
|
''' Returns <c>true</c> if this field is a map field; <c>false</c> otherwise.
|
|
''' </summary>
|
|
Public ReadOnly Property IsMap As Boolean
|
|
Get
|
|
Return fieldTypeField = FieldType.Message AndAlso messageTypeField.Proto.Options IsNot Nothing AndAlso messageTypeField.Proto.Options.MapEntry
|
|
End Get
|
|
End Property
|
|
|
|
''' <summary>
|
|
''' Returns <c>true</c> if this field is a packed, repeated field; <c>false</c> otherwise.
|
|
''' </summary>
|
|
Public ReadOnly Property IsPacked As Boolean
|
|
Get
|
|
' Note the || rather than && here - we're effectively defaulting to packed, because that *is*
|
|
' the default in proto3, which is all we support. We may give the wrong result for the protos
|
|
' within descriptor.proto, but that's okay, as they're never exposed and we don't use IsPacked
|
|
' within the runtime.
|
|
Return Proto.Options Is Nothing OrElse Proto.Options.Packed
|
|
End Get
|
|
End Property
|
|
|
|
''' <summary>
|
|
''' Returns the type of the field.
|
|
''' </summary>
|
|
Public ReadOnly Property FieldType As FieldType
|
|
Get
|
|
Return fieldTypeField
|
|
End Get
|
|
End Property
|
|
|
|
''' <summary>
|
|
''' Returns the field number declared in the proto file.
|
|
''' </summary>
|
|
Public ReadOnly Property FieldNumber As Integer
|
|
Get
|
|
Return Proto.Number
|
|
End Get
|
|
End Property
|
|
|
|
''' <summary>
|
|
''' Compares this descriptor with another one, ordering in "canonical" order
|
|
''' which simply means ascending order by field number. <paramrefname="other"/>
|
|
''' must be a field of the same type, i.e. the <see cref="ContainingType"/> of
|
|
''' both fields must be the same.
|
|
''' </summary>
|
|
Public Function CompareTo(other As FieldDescriptor) As Integer Implements IComparable(Of FieldDescriptor).CompareTo
|
|
If other.ContainingType IsNot ContainingType Then
|
|
Throw New ArgumentException("FieldDescriptors can only be compared to other FieldDescriptors " & "for fields of the same message type.")
|
|
End If
|
|
|
|
Return FieldNumber - other.FieldNumber
|
|
End Function
|
|
|
|
''' <summary>
|
|
''' For enum fields, returns the field's type.
|
|
''' </summary>
|
|
Public ReadOnly Property EnumType As EnumDescriptor
|
|
Get
|
|
|
|
If fieldTypeField <> FieldType.Enum Then
|
|
Throw New InvalidOperationException("EnumType is only valid for enum fields.")
|
|
End If
|
|
|
|
Return enumTypeField
|
|
End Get
|
|
End Property
|
|
|
|
''' <summary>
|
|
''' For embedded message and group fields, returns the field's type.
|
|
''' </summary>
|
|
Public ReadOnly Property MessageType As MessageDescriptor
|
|
Get
|
|
|
|
If fieldTypeField <> FieldType.Message Then
|
|
Throw New InvalidOperationException("MessageType is only valid for message fields.")
|
|
End If
|
|
|
|
Return messageTypeField
|
|
End Get
|
|
End Property
|
|
|
|
''' <summary>
|
|
''' Look up and cross-link all field types etc.
|
|
''' </summary>
|
|
Friend Sub CrossLink()
|
|
If Not Equals(Proto.TypeName, "") Then
|
|
Dim typeDescriptor = File.DescriptorPool.LookupSymbol(Proto.TypeName, Me)
|
|
|
|
If Proto.Type <> 0 Then
|
|
' Choose field type based on symbol.
|
|
If TypeOf typeDescriptor Is MessageDescriptor Then
|
|
fieldTypeField = FieldType.Message
|
|
ElseIf TypeOf typeDescriptor Is EnumDescriptor Then
|
|
fieldTypeField = FieldType.Enum
|
|
Else
|
|
Throw New DescriptorValidationException(Me, $"""{Proto.TypeName}"" is not a type.")
|
|
End If
|
|
End If
|
|
|
|
If fieldTypeField = FieldType.Message Then
|
|
If Not (TypeOf typeDescriptor Is MessageDescriptor) Then
|
|
Throw New DescriptorValidationException(Me, $"""{Proto.TypeName}"" is not a message type.")
|
|
End If
|
|
|
|
messageTypeField = CType(typeDescriptor, MessageDescriptor)
|
|
|
|
If Not Equals(Proto.DefaultValue, "") Then
|
|
Throw New DescriptorValidationException(Me, "Messages can't have default values.")
|
|
End If
|
|
ElseIf fieldTypeField = FieldType.Enum Then
|
|
|
|
If Not (TypeOf typeDescriptor Is EnumDescriptor) Then
|
|
Throw New DescriptorValidationException(Me, $"""{Proto.TypeName}"" is not an enum type.")
|
|
End If
|
|
|
|
enumTypeField = CType(typeDescriptor, EnumDescriptor)
|
|
Else
|
|
Throw New DescriptorValidationException(Me, "Field with primitive type has type_name.")
|
|
End If
|
|
Else
|
|
|
|
If fieldTypeField = FieldType.Message OrElse fieldTypeField = FieldType.Enum Then
|
|
Throw New DescriptorValidationException(Me, "Field with message or enum type missing type_name.")
|
|
End If
|
|
End If
|
|
|
|
' Note: no attempt to perform any default value parsing
|
|
|
|
File.DescriptorPool.AddFieldByNumber(Me)
|
|
|
|
If ContainingType IsNot Nothing AndAlso ContainingType.Proto.Options IsNot Nothing AndAlso ContainingType.Proto.Options.MessageSetWireFormat Then
|
|
Throw New DescriptorValidationException(Me, "MessageSet format is not supported.")
|
|
End If
|
|
|
|
accessorField = CreateAccessor()
|
|
End Sub
|
|
|
|
Private Function CreateAccessor() As IFieldAccessor
|
|
' If we're given no property name, that's because we really don't want an accessor.
|
|
' (At the moment, that means it's a map entry message...)
|
|
If Equals(propertyName, Nothing) Then
|
|
Return Nothing
|
|
End If
|
|
|
|
Dim [property] = ContainingType.ClrType.GetProperty(propertyName)
|
|
|
|
If [property] Is Nothing Then
|
|
Throw New DescriptorValidationException(Me, $"Property {propertyName} not found in {ContainingType.ClrType}")
|
|
End If
|
|
|
|
Return If(IsMap, New MapFieldAccessor([property], Me), If(IsRepeated, New RepeatedFieldAccessor([property], Me), CType(New SingleFieldAccessor([property], Me), IFieldAccessor)))
|
|
End Function
|
|
End Class
|
|
End Namespace
|