#region Copyright notice and license
// Protocol Buffers - Google's data interchange format
// Copyright 2015 Google Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
#endregion
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
namespace Google.Protobuf.Reflection
{
///
/// An immutable registry of types which can be looked up by their full name.
///
[DebuggerDisplay("Count = {fullNameToMessageMap.Count}")]
[DebuggerTypeProxy(typeof(TypeRegistryDebugView))]
public sealed class TypeRegistry
{
///
/// An empty type registry, containing no types.
///
public static TypeRegistry Empty { get; } = new TypeRegistry(new Dictionary());
private readonly Dictionary fullNameToMessageMap;
private TypeRegistry(Dictionary fullNameToMessageMap)
{
this.fullNameToMessageMap = fullNameToMessageMap;
}
///
/// Attempts to find a message descriptor by its full name.
///
/// The full name of the message, which is the dot-separated
/// combination of package, containing messages and message name
/// The message descriptor corresponding to or null
/// if there is no such message descriptor.
public MessageDescriptor Find(string fullName)
{
// Ignore the return value as ret will end up with the right value either way.
fullNameToMessageMap.TryGetValue(fullName, out MessageDescriptor ret);
return ret;
}
///
/// Creates a type registry from the specified set of file descriptors.
///
///
/// This is a convenience overload for
/// to allow calls such as TypeRegistry.FromFiles(descriptor1, descriptor2).
///
/// The set of files to include in the registry. Must not contain null values.
/// A type registry for the given files.
public static TypeRegistry FromFiles(params FileDescriptor[] fileDescriptors)
{
return FromFiles((IEnumerable) fileDescriptors);
}
///
/// Creates a type registry from the specified set of file descriptors.
///
///
/// All message types within all the specified files are added to the registry, and
/// the dependencies of the specified files are also added, recursively.
///
/// The set of files to include in the registry. Must not contain null values.
/// A type registry for the given files.
public static TypeRegistry FromFiles(IEnumerable fileDescriptors)
{
ProtoPreconditions.CheckNotNull(fileDescriptors, nameof(fileDescriptors));
var builder = new Builder();
foreach (var file in fileDescriptors)
{
builder.AddFile(file);
}
return builder.Build();
}
///
/// Creates a type registry from the file descriptor parents of the specified set of message descriptors.
///
///
/// This is a convenience overload for
/// to allow calls such as TypeRegistry.FromFiles(descriptor1, descriptor2).
///
/// The set of message descriptors to use to identify file descriptors to include in the registry.
/// Must not contain null values.
/// A type registry for the given files.
public static TypeRegistry FromMessages(params MessageDescriptor[] messageDescriptors)
{
return FromMessages((IEnumerable) messageDescriptors);
}
///
/// Creates a type registry from the file descriptor parents of the specified set of message descriptors.
///
///
/// The specified message descriptors are only used to identify their file descriptors; the returned registry
/// contains all the types within the file descriptors which contain the specified message descriptors (and
/// the dependencies of those files), not just the specified messages.
///
/// The set of message descriptors to use to identify file descriptors to include in the registry.
/// Must not contain null values.
/// A type registry for the given files.
public static TypeRegistry FromMessages(IEnumerable messageDescriptors)
{
ProtoPreconditions.CheckNotNull(messageDescriptors, nameof(messageDescriptors));
return FromFiles(messageDescriptors.Select(md => md.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 types;
private readonly HashSet fileDescriptorNames;
internal Builder()
{
types = new Dictionary();
fileDescriptorNames = new HashSet();
}
internal void AddFile(FileDescriptor fileDescriptor)
{
if (!fileDescriptorNames.Add(fileDescriptor.Name))
{
return;
}
foreach (var dependency in fileDescriptor.Dependencies)
{
AddFile(dependency);
}
foreach (var message in fileDescriptor.MessageTypes)
{
AddMessage(message);
}
}
private void AddMessage(MessageDescriptor messageDescriptor)
{
foreach (var nestedType in messageDescriptor.NestedTypes)
{
AddMessage(nestedType);
}
// This will overwrite any previous entry. Given that each file should
// only be added once, this could be a problem such as package A.B with type C,
// and package A with type B.C... it's unclear what we should do in that case.
types[messageDescriptor.FullName] = messageDescriptor;
}
internal TypeRegistry Build()
{
return new TypeRegistry(types);
}
}
private sealed class TypeRegistryDebugView
{
private readonly TypeRegistry list;
public TypeRegistryDebugView(TypeRegistry list)
{
this.list = list;
}
[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
public KeyValuePair[] Items => list.fullNameToMessageMap.ToArray();
}
}
}