#region Copyright notice and license
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System.Collections.Generic;
using Grpc.Core.Utils;
using Google.Protobuf.Reflection;
namespace Grpc.Reflection
{
/// Registry of protobuf symbols
public class SymbolRegistry
{
private readonly Dictionary filesByName;
private readonly Dictionary filesBySymbol;
private SymbolRegistry(Dictionary filesByName, Dictionary filesBySymbol)
{
this.filesByName = new Dictionary(filesByName);
this.filesBySymbol = new Dictionary(filesBySymbol);
}
///
/// Creates a symbol registry from the specified set of file descriptors.
///
/// The set of files to include in the registry. Must not contain null values.
/// A symbol registry for the given files.
public static SymbolRegistry FromFiles(IEnumerable fileDescriptors)
{
GrpcPreconditions.CheckNotNull(fileDescriptors);
var builder = new Builder();
foreach (var file in fileDescriptors)
{
builder.AddFile(file);
}
return builder.Build();
}
///
/// Gets file descriptor for given file name (including package path). Returns null if not found.
///
public FileDescriptor FileByName(string filename)
{
FileDescriptor file;
filesByName.TryGetValue(filename, out file);
return file;
}
///
/// Gets file descriptor that contains definition of given symbol full name (including package path). Returns null if not found.
///
public FileDescriptor FileContainingSymbol(string symbol)
{
FileDescriptor file;
filesBySymbol.TryGetValue(symbol, out file);
return file;
}
///
/// Builder class which isn't exposed, but acts as a convenient alternative to passing round two dictionaries in recursive calls.
///
private class Builder
{
private readonly Dictionary filesByName;
private readonly Dictionary filesBySymbol;
internal Builder()
{
filesByName = new Dictionary();
filesBySymbol = new Dictionary();
}
internal void AddFile(FileDescriptor fileDescriptor)
{
if (filesByName.ContainsKey(fileDescriptor.Name))
{
return;
}
filesByName.Add(fileDescriptor.Name, fileDescriptor);
foreach (var dependency in fileDescriptor.Dependencies)
{
AddFile(dependency);
}
foreach (var enumeration in fileDescriptor.EnumTypes)
{
AddEnum(enumeration);
}
foreach (var message in fileDescriptor.MessageTypes)
{
AddMessage(message);
}
foreach (var service in fileDescriptor.Services)
{
AddService(service);
}
}
private void AddEnum(EnumDescriptor enumDescriptor)
{
filesBySymbol[enumDescriptor.FullName] = enumDescriptor.File;
}
private void AddMessage(MessageDescriptor messageDescriptor)
{
foreach (var nestedEnum in messageDescriptor.EnumTypes)
{
AddEnum(nestedEnum);
}
foreach (var nestedType in messageDescriptor.NestedTypes)
{
AddMessage(nestedType);
}
filesBySymbol[messageDescriptor.FullName] = messageDescriptor.File;
}
private void AddService(ServiceDescriptor serviceDescriptor)
{
foreach (var method in serviceDescriptor.Methods)
{
filesBySymbol[method.FullName] = method.File;
}
filesBySymbol[serviceDescriptor.FullName] = serviceDescriptor.File;
}
internal SymbolRegistry Build()
{
return new SymbolRegistry(filesByName, filesBySymbol);
}
}
}
}