#Region "Microsoft.VisualBasic::30aae81439e9e8cfab7b3b6897c8f3bf, Google.Protobuf\Reflection\Descriptor\DescriptorPool.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 .
' /********************************************************************************/
' Summaries:
' Class DescriptorPool
'
' Constructor: (+1 Overloads) Sub New
'
' Function: FindEnumValueByNumber, FindFieldByNumber, FindSymbol, LookupSymbol
'
' Sub: AddEnumValueByNumber, AddFieldByNumber, AddPackage, AddSymbol, ImportPublicDependencies
' ValidateSymbolName
' Structure DescriptorIntPair
'
' Constructor: (+1 Overloads) Sub New
' Function: (+2 Overloads) Equals, GetHashCode
'
'
'
'
' /********************************************************************************/
#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
Imports System.Collections.Generic
Imports System.Text
Imports System.Text.RegularExpressions
Namespace Google.Protobuf.Reflection
'''
''' Contains lookup tables containing all the descriptors defined in a particular file.
'''
Friend NotInheritable Class DescriptorPool
Private ReadOnly descriptorsByName As IDictionary(Of String, IDescriptor) = New Dictionary(Of String, IDescriptor)()
Private ReadOnly fieldsByNumber As IDictionary(Of DescriptorIntPair, FieldDescriptor) = New Dictionary(Of DescriptorIntPair, FieldDescriptor)()
Private ReadOnly enumValuesByNumber As IDictionary(Of DescriptorIntPair, EnumValueDescriptor) = New Dictionary(Of DescriptorIntPair, EnumValueDescriptor)()
Private ReadOnly dependencies As HashSet(Of FileDescriptor)
Friend Sub New(dependencyFiles As FileDescriptor())
dependencies = New HashSet(Of FileDescriptor)()
For i = 0 To dependencyFiles.Length - 1
dependencies.Add(dependencyFiles(i))
ImportPublicDependencies(dependencyFiles(i))
Next
For Each dependency In dependencyFiles
AddPackage(dependency.Package, dependency)
Next
End Sub
Private Sub ImportPublicDependencies(file As FileDescriptor)
For Each dependency In file.PublicDependencies
If dependencies.Add(dependency) Then
ImportPublicDependencies(dependency)
End If
Next
End Sub
'''
''' Finds a symbol of the given name within the pool.
'''
''' The type of symbol to look for
''' Fully-qualified name to look up
''' The symbol with the given name and type,
''' or null if the symbol doesn't exist or has the wrong type
Friend Function FindSymbol(Of T As Class)(fullName As String) As T
Dim result As IDescriptor
descriptorsByName.TryGetValue(fullName, result)
Dim descriptor As T = TryCast(result, T)
If descriptor IsNot Nothing Then
Return descriptor
End If
' dependencies contains direct dependencies and any *public* dependencies
' of those dependencies (transitively)... so we don't need to recurse here.
For Each dependency In dependencies
dependency.DescriptorPool.descriptorsByName.TryGetValue(fullName, result)
descriptor = TryCast(result, T)
If descriptor IsNot Nothing Then
Return descriptor
End If
Next
Return Nothing
End Function
'''
''' Adds a package to the symbol tables. If a package by the same name
''' already exists, that is fine, but if some other kind of symbol
''' exists under the same name, an exception is thrown. If the package
''' has multiple components, this also adds the parent package(s).
'''
Friend Sub AddPackage(fullName As String, file As FileDescriptor)
Dim dotpos = fullName.LastIndexOf("."c)
Dim name As String
If dotpos <> -1 Then
AddPackage(fullName.Substring(0, dotpos), file)
name = fullName.Substring(dotpos + 1)
Else
name = fullName
End If
Dim old As IDescriptor
If descriptorsByName.TryGetValue(fullName, old) Then
If Not (TypeOf old Is PackageDescriptor) Then
Throw New DescriptorValidationException(file, """" & name & """ is already defined (as something other than a " & "package) in file """ & old.File.Name & """.")
End If
End If
descriptorsByName(fullName) = New PackageDescriptor(name, fullName, file)
End Sub
'''
''' Adds a symbol to the symbol table.
'''
''' The symbol already existed
''' in the symbol table.
Friend Sub AddSymbol(descriptor As IDescriptor)
ValidateSymbolName(descriptor)
Dim fullName = descriptor.FullName
Dim old As IDescriptor
If descriptorsByName.TryGetValue(fullName, old) Then
Dim dotPos = fullName.LastIndexOf("."c)
Dim message As String
If descriptor.File Is old.File Then
If dotPos = -1 Then
message = """" & fullName & """ is already defined."
Else
message = """" & fullName.Substring(dotPos + 1) & """ is already defined in """ & fullName.Substring(0, dotPos) & """."
End If
Else
message = """" & fullName & """ is already defined in file """ & old.File.Name & """."
End If
Throw New DescriptorValidationException(descriptor, message)
End If
descriptorsByName(fullName) = descriptor
End Sub
Private Shared ReadOnly ValidationRegex As Regex = New Regex("^[_A-Za-z][_A-Za-z0-9]*$", CompiledRegexWhereAvailable)
'''
''' Verifies that the descriptor's name is valid (i.e. it contains
''' only letters, digits and underscores, and does not start with a digit).
'''
'''
Private Shared Sub ValidateSymbolName(descriptor As IDescriptor)
If Equals(descriptor.Name, "") Then
Throw New DescriptorValidationException(descriptor, "Missing name.")
End If
If Not ValidationRegex.IsMatch(descriptor.Name) Then
Throw New DescriptorValidationException(descriptor, """" & descriptor.Name & """ is not a valid identifier.")
End If
End Sub
'''
''' Returns the field with the given number in the given descriptor,
''' or null if it can't be found.
'''
Friend Function FindFieldByNumber(messageDescriptor As MessageDescriptor, number As Integer) As FieldDescriptor
Dim ret As FieldDescriptor
fieldsByNumber.TryGetValue(New DescriptorIntPair(messageDescriptor, number), ret)
Return ret
End Function
Friend Function FindEnumValueByNumber(enumDescriptor As EnumDescriptor, number As Integer) As EnumValueDescriptor
Dim ret As EnumValueDescriptor
enumValuesByNumber.TryGetValue(New DescriptorIntPair(enumDescriptor, number), ret)
Return ret
End Function
'''
''' Adds a field to the fieldsByNumber table.
'''
''' A field with the same
''' containing type and number already exists.
Friend Sub AddFieldByNumber(field As FieldDescriptor)
Dim key As DescriptorIntPair = New DescriptorIntPair(field.ContainingType, field.FieldNumber)
Dim old As FieldDescriptor
If fieldsByNumber.TryGetValue(key, old) Then
Throw New DescriptorValidationException(field, "Field number " & field.FieldNumber & "has already been used in """ & field.ContainingType.FullName & """ by field """ & old.Name & """.")
End If
fieldsByNumber(key) = field
End Sub
'''
''' Adds an enum value to the enumValuesByNumber table. If an enum value
''' with the same type and number already exists, this method does nothing.
''' (This is allowed; the first value defined with the number takes precedence.)
'''
Friend Sub AddEnumValueByNumber(enumValue As EnumValueDescriptor)
Dim key As DescriptorIntPair = New DescriptorIntPair(enumValue.EnumDescriptor, enumValue.Number)
If Not enumValuesByNumber.ContainsKey(key) Then
enumValuesByNumber(key) = enumValue
End If
End Sub
'''
''' Looks up a descriptor by name, relative to some other descriptor.
''' The name may be fully-qualified (with a leading '.'), partially-qualified,
''' or unqualified. C++-like name lookup semantics are used to search for the
''' matching descriptor.
'''
'''
''' This isn't heavily optimized, but it's only used during cross linking anyway.
''' If it starts being used more widely, we should look at performance more carefully.
'''
Friend Function LookupSymbol(name As String, relativeTo As IDescriptor) As IDescriptor
Dim result As IDescriptor
If name.StartsWith(".") Then
' Fully-qualified name.
result = FindSymbol(Of IDescriptor)(name.Substring(1))
Else
' If "name" is a compound identifier, we want to search for the
' first component of it, then search within it for the rest.
Dim firstPartLength = name.IndexOf("."c)
Dim firstPart = If(firstPartLength = -1, name, name.Substring(0, firstPartLength))
' We will search each parent scope of "relativeTo" looking for the
' symbol.
Dim scopeToTry As StringBuilder = New StringBuilder(relativeTo.FullName)
While True
' Chop off the last component of the scope.
Dim dotpos As Integer = scopeToTry.ToString().LastIndexOf(".")
If dotpos = -1 Then
result = FindSymbol(Of IDescriptor)(name)
Exit While
Else
scopeToTry.Length = dotpos + 1
' Append firstPart and try to find.
scopeToTry.Append(firstPart)
result = FindSymbol(Of IDescriptor)(scopeToTry.ToString())
If result IsNot Nothing Then
If firstPartLength <> -1 Then
' We only found the first part of the symbol. Now look for
' the whole thing. If this fails, we *don't* want to keep
' searching parent scopes.
scopeToTry.Length = dotpos + 1
scopeToTry.Append(name)
result = FindSymbol(Of IDescriptor)(scopeToTry.ToString())
End If
Exit While
End If
' Not found. Remove the name so we can try again.
scopeToTry.Length = dotpos
End If
End While
End If
If result Is Nothing Then
Throw New DescriptorValidationException(relativeTo, """" & name & """ is not defined.")
Else
Return result
End If
End Function
'''
''' Struct used to hold the keys for the fieldByNumber table.
'''
Private Structure DescriptorIntPair
Implements IEquatable(Of DescriptorIntPair)
Private ReadOnly number As Integer
Private ReadOnly descriptor As IDescriptor
Friend Sub New(descriptor As IDescriptor, number As Integer)
Me.number = number
Me.descriptor = descriptor
End Sub
Public Overloads Function Equals(other As DescriptorIntPair) As Boolean Implements IEquatable(Of DescriptorIntPair).Equals
Return descriptor Is other.descriptor AndAlso number = other.number
End Function
Public Overrides Function Equals(obj As Object) As Boolean
If TypeOf obj Is DescriptorIntPair Then
Return Equals(CType(obj, DescriptorIntPair))
End If
Return False
End Function
Public Overrides Function GetHashCode() As Integer
Return descriptor.GetHashCode() * ((1 << 16) - 1) + number
End Function
End Structure
End Class
End Namespace