Ported .Net Core commits:
hondacrx: - Initial commit: Switch to .Net Core 2.0 - Fix build and removed not needed files Fabi: - Updated solution platforms. - Changed folder structure. - Change library target framework to netstandard2.0. - Updated solution platforms again... - Removed windows specific kernel32 function usage (Ctrl-C handler).
This commit is contained in:
@@ -0,0 +1,390 @@
|
||||
#region Copyright notice and license
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2017 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.
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Google.Protobuf.Reflection
|
||||
{
|
||||
/// <summary>
|
||||
/// Container for a set of custom options specified within a message, field etc.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This type is publicly immutable, but internally mutable. It is only populated
|
||||
/// by the descriptor parsing code - by the time any user code is able to see an instance,
|
||||
/// it will be fully initialized.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// If an option is requested using the incorrect method, an answer may still be returned: all
|
||||
/// of the numeric types are represented internally using 64-bit integers, for example. It is up to
|
||||
/// the caller to ensure that they make the appropriate method call for the option they're interested in.
|
||||
/// Note that enum options are simply stored as integers, so the value should be fetched using
|
||||
/// <see cref="TryGetInt32(int, out int)"/> and then cast appropriately.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Repeated options are currently not supported. Asking for a single value of an option
|
||||
/// which was actually repeated will return the last value, except for message types where
|
||||
/// all the set values are merged together.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public sealed class CustomOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Singleton for all descriptors with an empty set of options.
|
||||
/// </summary>
|
||||
internal static readonly CustomOptions Empty = new CustomOptions();
|
||||
|
||||
/// <summary>
|
||||
/// A sequence of values per field. This needs to be per field rather than per tag to allow correct deserialization
|
||||
/// of repeated fields which could be "int, ByteString, int" - unlikely as that is. The fact that values are boxed
|
||||
/// is unfortunate; we might be able to use a struct instead, and we could combine uint and ulong values.
|
||||
/// </summary>
|
||||
private readonly Dictionary<int, List<FieldValue>> valuesByField = new Dictionary<int, List<FieldValue>>();
|
||||
|
||||
private CustomOptions() { }
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a Boolean value for the specified option field.
|
||||
/// </summary>
|
||||
/// <param name="field">The field to fetch the value for.</param>
|
||||
/// <param name="value">The output variable to populate.</param>
|
||||
/// <returns><c>true</c> if a suitable value for the field was found; <c>false</c> otherwise.</returns>
|
||||
public bool TryGetBool(int field, out bool value)
|
||||
{
|
||||
ulong? tmp = GetLastNumericValue(field);
|
||||
value = tmp == 1UL;
|
||||
return tmp != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a signed 32-bit integer value for the specified option field.
|
||||
/// </summary>
|
||||
/// <param name="field">The field to fetch the value for.</param>
|
||||
/// <param name="value">The output variable to populate.</param>
|
||||
/// <returns><c>true</c> if a suitable value for the field was found; <c>false</c> otherwise.</returns>
|
||||
public bool TryGetInt32(int field, out int value)
|
||||
{
|
||||
ulong? tmp = GetLastNumericValue(field);
|
||||
value = (int) tmp.GetValueOrDefault();
|
||||
return tmp != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a signed 64-bit integer value for the specified option field.
|
||||
/// </summary>
|
||||
/// <param name="field">The field to fetch the value for.</param>
|
||||
/// <param name="value">The output variable to populate.</param>
|
||||
/// <returns><c>true</c> if a suitable value for the field was found; <c>false</c> otherwise.</returns>
|
||||
public bool TryGetInt64(int field, out long value)
|
||||
{
|
||||
ulong? tmp = GetLastNumericValue(field);
|
||||
value = (long) tmp.GetValueOrDefault();
|
||||
return tmp != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves an unsigned 32-bit integer value for the specified option field,
|
||||
/// assuming a fixed-length representation.
|
||||
/// </summary>
|
||||
/// <param name="field">The field to fetch the value for.</param>
|
||||
/// <param name="value">The output variable to populate.</param>
|
||||
/// <returns><c>true</c> if a suitable value for the field was found; <c>false</c> otherwise.</returns>
|
||||
public bool TryGetFixed32(int field, out uint value) => TryGetUInt32(field, out value);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves an unsigned 64-bit integer value for the specified option field,
|
||||
/// assuming a fixed-length representation.
|
||||
/// </summary>
|
||||
/// <param name="field">The field to fetch the value for.</param>
|
||||
/// <param name="value">The output variable to populate.</param>
|
||||
/// <returns><c>true</c> if a suitable value for the field was found; <c>false</c> otherwise.</returns>
|
||||
public bool TryGetFixed64(int field, out ulong value) => TryGetUInt64(field, out value);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a signed 32-bit integer value for the specified option field,
|
||||
/// assuming a fixed-length representation.
|
||||
/// </summary>
|
||||
/// <param name="field">The field to fetch the value for.</param>
|
||||
/// <param name="value">The output variable to populate.</param>
|
||||
/// <returns><c>true</c> if a suitable value for the field was found; <c>false</c> otherwise.</returns>
|
||||
public bool TryGetSFixed32(int field, out int value) => TryGetInt32(field, out value);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a signed 64-bit integer value for the specified option field,
|
||||
/// assuming a fixed-length representation.
|
||||
/// </summary>
|
||||
/// <param name="field">The field to fetch the value for.</param>
|
||||
/// <param name="value">The output variable to populate.</param>
|
||||
/// <returns><c>true</c> if a suitable value for the field was found; <c>false</c> otherwise.</returns>
|
||||
public bool TryGetSFixed64(int field, out long value) => TryGetInt64(field, out value);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a signed 32-bit integer value for the specified option field,
|
||||
/// assuming a zigzag encoding.
|
||||
/// </summary>
|
||||
/// <param name="field">The field to fetch the value for.</param>
|
||||
/// <param name="value">The output variable to populate.</param>
|
||||
/// <returns><c>true</c> if a suitable value for the field was found; <c>false</c> otherwise.</returns>
|
||||
public bool TryGetSInt32(int field, out int value)
|
||||
{
|
||||
ulong? tmp = GetLastNumericValue(field);
|
||||
value = CodedInputStream.DecodeZigZag32((uint) tmp.GetValueOrDefault());
|
||||
return tmp != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a signed 64-bit integer value for the specified option field,
|
||||
/// assuming a zigzag encoding.
|
||||
/// </summary>
|
||||
/// <param name="field">The field to fetch the value for.</param>
|
||||
/// <param name="value">The output variable to populate.</param>
|
||||
/// <returns><c>true</c> if a suitable value for the field was found; <c>false</c> otherwise.</returns>
|
||||
public bool TryGetSInt64(int field, out long value)
|
||||
{
|
||||
ulong? tmp = GetLastNumericValue(field);
|
||||
value = CodedInputStream.DecodeZigZag64(tmp.GetValueOrDefault());
|
||||
return tmp != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves an unsigned 32-bit integer value for the specified option field.
|
||||
/// </summary>
|
||||
/// <param name="field">The field to fetch the value for.</param>
|
||||
/// <param name="value">The output variable to populate.</param>
|
||||
/// <returns><c>true</c> if a suitable value for the field was found; <c>false</c> otherwise.</returns>
|
||||
public bool TryGetUInt32(int field, out uint value)
|
||||
{
|
||||
ulong? tmp = GetLastNumericValue(field);
|
||||
value = (uint) tmp.GetValueOrDefault();
|
||||
return tmp != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves an unsigned 64-bit integer value for the specified option field.
|
||||
/// </summary>
|
||||
/// <param name="field">The field to fetch the value for.</param>
|
||||
/// <param name="value">The output variable to populate.</param>
|
||||
/// <returns><c>true</c> if a suitable value for the field was found; <c>false</c> otherwise.</returns>
|
||||
public bool TryGetUInt64(int field, out ulong value)
|
||||
{
|
||||
ulong? tmp = GetLastNumericValue(field);
|
||||
value = tmp.GetValueOrDefault();
|
||||
return tmp != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a 32-bit floating point value for the specified option field.
|
||||
/// </summary>
|
||||
/// <param name="field">The field to fetch the value for.</param>
|
||||
/// <param name="value">The output variable to populate.</param>
|
||||
/// <returns><c>true</c> if a suitable value for the field was found; <c>false</c> otherwise.</returns>
|
||||
public bool TryGetFloat(int field, out float value)
|
||||
{
|
||||
ulong? tmp = GetLastNumericValue(field);
|
||||
int int32 = (int) tmp.GetValueOrDefault();
|
||||
byte[] bytes = BitConverter.GetBytes(int32);
|
||||
value = BitConverter.ToSingle(bytes, 0);
|
||||
return tmp != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a 64-bit floating point value for the specified option field.
|
||||
/// </summary>
|
||||
/// <param name="field">The field to fetch the value for.</param>
|
||||
/// <param name="value">The output variable to populate.</param>
|
||||
/// <returns><c>true</c> if a suitable value for the field was found; <c>false</c> otherwise.</returns>
|
||||
public bool TryGetDouble(int field, out double value)
|
||||
{
|
||||
ulong? tmp = GetLastNumericValue(field);
|
||||
value = BitConverter.Int64BitsToDouble((long) tmp.GetValueOrDefault());
|
||||
return tmp != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a string value for the specified option field.
|
||||
/// </summary>
|
||||
/// <param name="field">The field to fetch the value for.</param>
|
||||
/// <param name="value">The output variable to populate.</param>
|
||||
/// <returns><c>true</c> if a suitable value for the field was found; <c>false</c> otherwise.</returns>
|
||||
public bool TryGetString(int field, out string value)
|
||||
{
|
||||
ByteString bytes = GetLastByteStringValue(field);
|
||||
value = bytes?.ToStringUtf8();
|
||||
return bytes != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a bytes value for the specified option field.
|
||||
/// </summary>
|
||||
/// <param name="field">The field to fetch the value for.</param>
|
||||
/// <param name="value">The output variable to populate.</param>
|
||||
/// <returns><c>true</c> if a suitable value for the field was found; <c>false</c> otherwise.</returns>
|
||||
public bool TryGetBytes(int field, out ByteString value)
|
||||
{
|
||||
ByteString bytes = GetLastByteStringValue(field);
|
||||
value = bytes;
|
||||
return bytes != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a message value for the specified option field.
|
||||
/// </summary>
|
||||
/// <param name="field">The field to fetch the value for.</param>
|
||||
/// <param name="value">The output variable to populate.</param>
|
||||
/// <returns><c>true</c> if a suitable value for the field was found; <c>false</c> otherwise.</returns>
|
||||
public bool TryGetMessage<T>(int field, out T value) where T : class, IMessage, new()
|
||||
{
|
||||
value = null;
|
||||
List<FieldValue> values;
|
||||
if (!valuesByField.TryGetValue(field, out values))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
foreach (FieldValue fieldValue in values)
|
||||
{
|
||||
if (fieldValue.ByteString != null)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
value = new T();
|
||||
}
|
||||
value.MergeFrom(fieldValue.ByteString);
|
||||
}
|
||||
}
|
||||
return value != null;
|
||||
}
|
||||
|
||||
private ulong? GetLastNumericValue(int field)
|
||||
{
|
||||
List<FieldValue> values;
|
||||
if (!valuesByField.TryGetValue(field, out values))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
for (int i = values.Count - 1; i >= 0; i--)
|
||||
{
|
||||
// A non-bytestring value is a numeric value
|
||||
if (values[i].ByteString == null)
|
||||
{
|
||||
return values[i].Number;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private ByteString GetLastByteStringValue(int field)
|
||||
{
|
||||
List<FieldValue> values;
|
||||
if (!valuesByField.TryGetValue(field, out values))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
for (int i = values.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (values[i].ByteString != null)
|
||||
{
|
||||
return values[i].ByteString;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads an unknown field, either parsing it and storing it or skipping it.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If the current set of options is empty and we manage to read a field, a new set of options
|
||||
/// will be created and returned. Otherwise, the return value is <c>this</c>. This allows
|
||||
/// us to start with a singleton empty set of options and just create new ones where necessary.
|
||||
/// </remarks>
|
||||
/// <param name="input">Input stream to read from. </param>
|
||||
/// <returns>The resulting set of custom options, either <c>this</c> or a new set.</returns>
|
||||
internal CustomOptions ReadOrSkipUnknownField(CodedInputStream input)
|
||||
{
|
||||
var tag = input.LastTag;
|
||||
var field = WireFormat.GetTagFieldNumber(tag);
|
||||
switch (WireFormat.GetTagWireType(tag))
|
||||
{
|
||||
case WireFormat.WireType.LengthDelimited:
|
||||
return AddValue(field, new FieldValue(input.ReadBytes()));
|
||||
case WireFormat.WireType.Fixed32:
|
||||
return AddValue(field, new FieldValue(input.ReadFixed32()));
|
||||
case WireFormat.WireType.Fixed64:
|
||||
return AddValue(field, new FieldValue(input.ReadFixed64()));
|
||||
case WireFormat.WireType.Varint:
|
||||
return AddValue(field, new FieldValue(input.ReadRawVarint64()));
|
||||
// For StartGroup, EndGroup or any wire format we don't understand,
|
||||
// just use the normal behavior (call SkipLastField).
|
||||
default:
|
||||
input.SkipLastField();
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
private CustomOptions AddValue(int field, FieldValue value)
|
||||
{
|
||||
var ret = valuesByField.Count == 0 ? new CustomOptions() : this;
|
||||
List<FieldValue> valuesForField;
|
||||
if (!ret.valuesByField.TryGetValue(field, out valuesForField))
|
||||
{
|
||||
// Expect almost all
|
||||
valuesForField = new List<FieldValue>(1);
|
||||
ret.valuesByField[field] = valuesForField;
|
||||
}
|
||||
valuesForField.Add(value);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// All field values can be stored as a byte string or a 64-bit integer.
|
||||
/// This struct avoids unnecessary boxing.
|
||||
/// </summary>
|
||||
private struct FieldValue
|
||||
{
|
||||
internal ulong Number { get; }
|
||||
internal ByteString ByteString { get; }
|
||||
|
||||
internal FieldValue(ulong number)
|
||||
{
|
||||
Number = number;
|
||||
ByteString = null;
|
||||
}
|
||||
|
||||
internal FieldValue(ByteString byteString)
|
||||
{
|
||||
Number = 0;
|
||||
ByteString = byteString;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,85 @@
|
||||
#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.
|
||||
#endregion
|
||||
|
||||
namespace Google.Protobuf.Reflection
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for nearly all descriptors, providing common functionality.
|
||||
/// </summary>
|
||||
public abstract class DescriptorBase : IDescriptor
|
||||
{
|
||||
private readonly FileDescriptor file;
|
||||
private readonly string fullName;
|
||||
private readonly int index;
|
||||
|
||||
internal DescriptorBase(FileDescriptor file, string fullName, int index)
|
||||
{
|
||||
this.file = file;
|
||||
this.fullName = fullName;
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
/// <value>
|
||||
/// The index of this descriptor within its parent descriptor.
|
||||
/// </value>
|
||||
/// <remarks>
|
||||
/// This returns the index of this descriptor within its parent, for
|
||||
/// this descriptor's type. (There can be duplicate values for different
|
||||
/// types, e.g. one enum type with index 0 and one message type with index 0.)
|
||||
/// </remarks>
|
||||
public int Index
|
||||
{
|
||||
get { return index; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the name of the entity (field, message etc) being described.
|
||||
/// </summary>
|
||||
public abstract string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The fully qualified name of the descriptor's target.
|
||||
/// </summary>
|
||||
public string FullName
|
||||
{
|
||||
get { return fullName; }
|
||||
}
|
||||
|
||||
/// <value>
|
||||
/// The file this descriptor was declared in.
|
||||
/// </value>
|
||||
public FileDescriptor File
|
||||
{
|
||||
get { return file; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,368 @@
|
||||
#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.
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Google.Protobuf.Reflection
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains lookup tables containing all the descriptors defined in a particular file.
|
||||
/// </summary>
|
||||
internal sealed class DescriptorPool
|
||||
{
|
||||
private readonly IDictionary<string, IDescriptor> descriptorsByName =
|
||||
new Dictionary<string, IDescriptor>();
|
||||
|
||||
private readonly IDictionary<DescriptorIntPair, FieldDescriptor> fieldsByNumber =
|
||||
new Dictionary<DescriptorIntPair, FieldDescriptor>();
|
||||
|
||||
private readonly IDictionary<DescriptorIntPair, EnumValueDescriptor> enumValuesByNumber =
|
||||
new Dictionary<DescriptorIntPair, EnumValueDescriptor>();
|
||||
|
||||
private readonly HashSet<FileDescriptor> dependencies;
|
||||
|
||||
internal DescriptorPool(FileDescriptor[] dependencyFiles)
|
||||
{
|
||||
dependencies = new HashSet<FileDescriptor>();
|
||||
for (int i = 0; i < dependencyFiles.Length; i++)
|
||||
{
|
||||
dependencies.Add(dependencyFiles[i]);
|
||||
ImportPublicDependencies(dependencyFiles[i]);
|
||||
}
|
||||
|
||||
foreach (FileDescriptor dependency in dependencyFiles)
|
||||
{
|
||||
AddPackage(dependency.Package, dependency);
|
||||
}
|
||||
}
|
||||
|
||||
private void ImportPublicDependencies(FileDescriptor file)
|
||||
{
|
||||
foreach (FileDescriptor dependency in file.PublicDependencies)
|
||||
{
|
||||
if (dependencies.Add(dependency))
|
||||
{
|
||||
ImportPublicDependencies(dependency);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds a symbol of the given name within the pool.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of symbol to look for</typeparam>
|
||||
/// <param name="fullName">Fully-qualified name to look up</param>
|
||||
/// <returns>The symbol with the given name and type,
|
||||
/// or null if the symbol doesn't exist or has the wrong type</returns>
|
||||
internal T FindSymbol<T>(string fullName) where T : class
|
||||
{
|
||||
IDescriptor result;
|
||||
descriptorsByName.TryGetValue(fullName, out result);
|
||||
T descriptor = result as T;
|
||||
if (descriptor != null)
|
||||
{
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
// dependencies contains direct dependencies and any *public* dependencies
|
||||
// of those dependencies (transitively)... so we don't need to recurse here.
|
||||
foreach (FileDescriptor dependency in dependencies)
|
||||
{
|
||||
dependency.DescriptorPool.descriptorsByName.TryGetValue(fullName, out result);
|
||||
descriptor = result as T;
|
||||
if (descriptor != null)
|
||||
{
|
||||
return descriptor;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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).
|
||||
/// </summary>
|
||||
internal void AddPackage(string fullName, FileDescriptor file)
|
||||
{
|
||||
int dotpos = fullName.LastIndexOf('.');
|
||||
String name;
|
||||
if (dotpos != -1)
|
||||
{
|
||||
AddPackage(fullName.Substring(0, dotpos), file);
|
||||
name = fullName.Substring(dotpos + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
name = fullName;
|
||||
}
|
||||
|
||||
IDescriptor old;
|
||||
if (descriptorsByName.TryGetValue(fullName, out old))
|
||||
{
|
||||
if (!(old is PackageDescriptor))
|
||||
{
|
||||
throw new DescriptorValidationException(file,
|
||||
"\"" + name +
|
||||
"\" is already defined (as something other than a " +
|
||||
"package) in file \"" + old.File.Name + "\".");
|
||||
}
|
||||
}
|
||||
descriptorsByName[fullName] = new PackageDescriptor(name, fullName, file);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a symbol to the symbol table.
|
||||
/// </summary>
|
||||
/// <exception cref="DescriptorValidationException">The symbol already existed
|
||||
/// in the symbol table.</exception>
|
||||
internal void AddSymbol(IDescriptor descriptor)
|
||||
{
|
||||
ValidateSymbolName(descriptor);
|
||||
String fullName = descriptor.FullName;
|
||||
|
||||
IDescriptor old;
|
||||
if (descriptorsByName.TryGetValue(fullName, out old))
|
||||
{
|
||||
int dotPos = fullName.LastIndexOf('.');
|
||||
string message;
|
||||
if (descriptor.File == old.File)
|
||||
{
|
||||
if (dotPos == -1)
|
||||
{
|
||||
message = "\"" + fullName + "\" is already defined.";
|
||||
}
|
||||
else
|
||||
{
|
||||
message = "\"" + fullName.Substring(dotPos + 1) + "\" is already defined in \"" +
|
||||
fullName.Substring(0, dotPos) + "\".";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
message = "\"" + fullName + "\" is already defined in file \"" + old.File.Name + "\".";
|
||||
}
|
||||
throw new DescriptorValidationException(descriptor, message);
|
||||
}
|
||||
descriptorsByName[fullName] = descriptor;
|
||||
}
|
||||
|
||||
private static readonly Regex ValidationRegex = new Regex("^[_A-Za-z][_A-Za-z0-9]*$",
|
||||
FrameworkPortability.CompiledRegexWhereAvailable);
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the descriptor's name is valid (i.e. it contains
|
||||
/// only letters, digits and underscores, and does not start with a digit).
|
||||
/// </summary>
|
||||
/// <param name="descriptor"></param>
|
||||
private static void ValidateSymbolName(IDescriptor descriptor)
|
||||
{
|
||||
if (descriptor.Name == "")
|
||||
{
|
||||
throw new DescriptorValidationException(descriptor, "Missing name.");
|
||||
}
|
||||
if (!ValidationRegex.IsMatch(descriptor.Name))
|
||||
{
|
||||
throw new DescriptorValidationException(descriptor,
|
||||
"\"" + descriptor.Name + "\" is not a valid identifier.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the field with the given number in the given descriptor,
|
||||
/// or null if it can't be found.
|
||||
/// </summary>
|
||||
internal FieldDescriptor FindFieldByNumber(MessageDescriptor messageDescriptor, int number)
|
||||
{
|
||||
FieldDescriptor ret;
|
||||
fieldsByNumber.TryGetValue(new DescriptorIntPair(messageDescriptor, number), out ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
internal EnumValueDescriptor FindEnumValueByNumber(EnumDescriptor enumDescriptor, int number)
|
||||
{
|
||||
EnumValueDescriptor ret;
|
||||
enumValuesByNumber.TryGetValue(new DescriptorIntPair(enumDescriptor, number), out ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a field to the fieldsByNumber table.
|
||||
/// </summary>
|
||||
/// <exception cref="DescriptorValidationException">A field with the same
|
||||
/// containing type and number already exists.</exception>
|
||||
internal void AddFieldByNumber(FieldDescriptor field)
|
||||
{
|
||||
DescriptorIntPair key = new DescriptorIntPair(field.ContainingType, field.FieldNumber);
|
||||
FieldDescriptor old;
|
||||
if (fieldsByNumber.TryGetValue(key, out old))
|
||||
{
|
||||
throw new DescriptorValidationException(field, "Field number " + field.FieldNumber +
|
||||
"has already been used in \"" +
|
||||
field.ContainingType.FullName +
|
||||
"\" by field \"" + old.Name + "\".");
|
||||
}
|
||||
fieldsByNumber[key] = field;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.)
|
||||
/// </summary>
|
||||
internal void AddEnumValueByNumber(EnumValueDescriptor enumValue)
|
||||
{
|
||||
DescriptorIntPair key = new DescriptorIntPair(enumValue.EnumDescriptor, enumValue.Number);
|
||||
if (!enumValuesByNumber.ContainsKey(key))
|
||||
{
|
||||
enumValuesByNumber[key] = enumValue;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
internal IDescriptor LookupSymbol(string name, IDescriptor relativeTo)
|
||||
{
|
||||
IDescriptor result;
|
||||
if (name.StartsWith("."))
|
||||
{
|
||||
// Fully-qualified name.
|
||||
result = FindSymbol<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.
|
||||
int firstPartLength = name.IndexOf('.');
|
||||
string firstPart = firstPartLength == -1 ? name : name.Substring(0, firstPartLength);
|
||||
|
||||
// We will search each parent scope of "relativeTo" looking for the
|
||||
// symbol.
|
||||
StringBuilder scopeToTry = new StringBuilder(relativeTo.FullName);
|
||||
|
||||
while (true)
|
||||
{
|
||||
// Chop off the last component of the scope.
|
||||
|
||||
int dotpos = scopeToTry.ToString().LastIndexOf(".");
|
||||
if (dotpos == -1)
|
||||
{
|
||||
result = FindSymbol<IDescriptor>(name);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
scopeToTry.Length = dotpos + 1;
|
||||
|
||||
// Append firstPart and try to find.
|
||||
scopeToTry.Append(firstPart);
|
||||
result = FindSymbol<IDescriptor>(scopeToTry.ToString());
|
||||
|
||||
if (result != null)
|
||||
{
|
||||
if (firstPartLength != -1)
|
||||
{
|
||||
// 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<IDescriptor>(scopeToTry.ToString());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Not found. Remove the name so we can try again.
|
||||
scopeToTry.Length = dotpos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (result == null)
|
||||
{
|
||||
throw new DescriptorValidationException(relativeTo, "\"" + name + "\" is not defined.");
|
||||
}
|
||||
else
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Struct used to hold the keys for the fieldByNumber table.
|
||||
/// </summary>
|
||||
private struct DescriptorIntPair : IEquatable<DescriptorIntPair>
|
||||
{
|
||||
private readonly int number;
|
||||
private readonly IDescriptor descriptor;
|
||||
|
||||
internal DescriptorIntPair(IDescriptor descriptor, int number)
|
||||
{
|
||||
this.number = number;
|
||||
this.descriptor = descriptor;
|
||||
}
|
||||
|
||||
public bool Equals(DescriptorIntPair other)
|
||||
{
|
||||
return descriptor == other.descriptor
|
||||
&& number == other.number;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj is DescriptorIntPair)
|
||||
{
|
||||
return Equals((DescriptorIntPair) obj);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return descriptor.GetHashCode()*((1 << 16) - 1) + number;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
#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.
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Google.Protobuf.Reflection
|
||||
{
|
||||
/// <summary>
|
||||
/// Internal class containing utility methods when working with descriptors.
|
||||
/// </summary>
|
||||
internal static class DescriptorUtil
|
||||
{
|
||||
/// <summary>
|
||||
/// Equivalent to Func[TInput, int, TOutput] but usable in .NET 2.0. Only used to convert
|
||||
/// arrays.
|
||||
/// </summary>
|
||||
internal delegate TOutput IndexedConverter<TInput, TOutput>(TInput element, int index);
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given array into a read-only list, applying the specified conversion to
|
||||
/// each input element.
|
||||
/// </summary>
|
||||
internal static IList<TOutput> ConvertAndMakeReadOnly<TInput, TOutput>
|
||||
(IList<TInput> input, IndexedConverter<TInput, TOutput> converter)
|
||||
{
|
||||
TOutput[] array = new TOutput[input.Count];
|
||||
for (int i = 0; i < array.Length; i++)
|
||||
{
|
||||
array[i] = converter(input[i], i);
|
||||
}
|
||||
return new ReadOnlyCollection<TOutput>(array);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
#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.
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
|
||||
namespace Google.Protobuf.Reflection
|
||||
{
|
||||
/// <summary>
|
||||
/// Thrown when building descriptors fails because the source DescriptorProtos
|
||||
/// are not valid.
|
||||
/// </summary>
|
||||
public sealed class DescriptorValidationException : Exception
|
||||
{
|
||||
private readonly String name;
|
||||
private readonly string description;
|
||||
|
||||
/// <value>
|
||||
/// The full name of the descriptor where the error occurred.
|
||||
/// </value>
|
||||
public String ProblemSymbolName
|
||||
{
|
||||
get { return name; }
|
||||
}
|
||||
|
||||
/// <value>
|
||||
/// A human-readable description of the error. (The Message property
|
||||
/// is made up of the descriptor's name and this description.)
|
||||
/// </value>
|
||||
public string Description
|
||||
{
|
||||
get { return description; }
|
||||
}
|
||||
|
||||
internal DescriptorValidationException(IDescriptor problemDescriptor, string description) :
|
||||
base(problemDescriptor.FullName + ": " + description)
|
||||
{
|
||||
// Note that problemDescriptor may be partially uninitialized, so we
|
||||
// don't want to expose it directly to the user. So, we only provide
|
||||
// the name and the original proto.
|
||||
name = problemDescriptor.FullName;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
internal DescriptorValidationException(IDescriptor problemDescriptor, string description, Exception cause) :
|
||||
base(problemDescriptor.FullName + ": " + description, cause)
|
||||
{
|
||||
name = problemDescriptor.FullName;
|
||||
this.description = description;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
#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.
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Google.Protobuf.Reflection
|
||||
{
|
||||
/// <summary>
|
||||
/// Descriptor for an enum type in a .proto file.
|
||||
/// </summary>
|
||||
public sealed class EnumDescriptor : DescriptorBase
|
||||
{
|
||||
private readonly EnumDescriptorProto proto;
|
||||
private readonly MessageDescriptor containingType;
|
||||
private readonly IList<EnumValueDescriptor> values;
|
||||
private readonly Type clrType;
|
||||
|
||||
internal EnumDescriptor(EnumDescriptorProto proto, FileDescriptor file, MessageDescriptor parent, int index, Type clrType)
|
||||
: base(file, file.ComputeFullName(parent, proto.Name), index)
|
||||
{
|
||||
this.proto = proto;
|
||||
this.clrType = clrType;
|
||||
containingType = parent;
|
||||
|
||||
if (proto.Value.Count == 0)
|
||||
{
|
||||
// We cannot allow enums with no values because this would mean there
|
||||
// would be no valid default value for fields of this type.
|
||||
throw new DescriptorValidationException(this, "Enums must contain at least one value.");
|
||||
}
|
||||
|
||||
values = DescriptorUtil.ConvertAndMakeReadOnly(proto.Value,
|
||||
(value, i) => new EnumValueDescriptor(value, file, this, i));
|
||||
|
||||
File.DescriptorPool.AddSymbol(this);
|
||||
}
|
||||
|
||||
internal EnumDescriptorProto Proto { get { return proto; } }
|
||||
|
||||
/// <summary>
|
||||
/// The brief name of the descriptor's target.
|
||||
/// </summary>
|
||||
public override string Name { get { return proto.Name; } }
|
||||
|
||||
/// <summary>
|
||||
/// The CLR type for this enum. For generated code, this will be a CLR enum type.
|
||||
/// </summary>
|
||||
public Type ClrType { get { return clrType; } }
|
||||
|
||||
/// <value>
|
||||
/// If this is a nested type, get the outer descriptor, otherwise null.
|
||||
/// </value>
|
||||
public MessageDescriptor ContainingType
|
||||
{
|
||||
get { return containingType; }
|
||||
}
|
||||
|
||||
/// <value>
|
||||
/// An unmodifiable list of defined value descriptors for this enum.
|
||||
/// </value>
|
||||
public IList<EnumValueDescriptor> Values
|
||||
{
|
||||
get { return values; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds an enum value by number. If multiple enum values have the
|
||||
/// same number, this returns the first defined value with that number.
|
||||
/// If there is no value for the given number, this returns <c>null</c>.
|
||||
/// </summary>
|
||||
public EnumValueDescriptor FindValueByNumber(int number)
|
||||
{
|
||||
return File.DescriptorPool.FindEnumValueByNumber(this, number);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds an enum value by name.
|
||||
/// </summary>
|
||||
/// <param name="name">The unqualified name of the value (e.g. "FOO").</param>
|
||||
/// <returns>The value's descriptor, or null if not found.</returns>
|
||||
public EnumValueDescriptor FindValueByName(string name)
|
||||
{
|
||||
return File.DescriptorPool.FindSymbol<EnumValueDescriptor>(FullName + "." + name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The (possibly empty) set of custom options for this enum.
|
||||
/// </summary>
|
||||
public CustomOptions CustomOptions => Proto.Options?.CustomOptions ?? CustomOptions.Empty;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
#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.
|
||||
#endregion
|
||||
|
||||
namespace Google.Protobuf.Reflection
|
||||
{
|
||||
/// <summary>
|
||||
/// Descriptor for a single enum value within an enum in a .proto file.
|
||||
/// </summary>
|
||||
public sealed class EnumValueDescriptor : DescriptorBase
|
||||
{
|
||||
private readonly EnumDescriptor enumDescriptor;
|
||||
private readonly EnumValueDescriptorProto proto;
|
||||
|
||||
internal EnumValueDescriptor(EnumValueDescriptorProto proto, FileDescriptor file,
|
||||
EnumDescriptor parent, int index)
|
||||
: base(file, parent.FullName + "." + proto.Name, index)
|
||||
{
|
||||
this.proto = proto;
|
||||
enumDescriptor = parent;
|
||||
file.DescriptorPool.AddSymbol(this);
|
||||
file.DescriptorPool.AddEnumValueByNumber(this);
|
||||
}
|
||||
|
||||
internal EnumValueDescriptorProto Proto { get { return proto; } }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the name of the enum value described by this object.
|
||||
/// </summary>
|
||||
public override string Name { get { return proto.Name; } }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the number associated with this enum value.
|
||||
/// </summary>
|
||||
public int Number { get { return Proto.Number; } }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the enum descriptor that this value is part of.
|
||||
/// </summary>
|
||||
public EnumDescriptor EnumDescriptor { get { return enumDescriptor; } }
|
||||
|
||||
/// <summary>
|
||||
/// The (possibly empty) set of custom options for this enum value.
|
||||
/// </summary>
|
||||
public CustomOptions CustomOptions => Proto.Options?.CustomOptions ?? CustomOptions.Empty;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
#region Copyright notice and license
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2015 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.
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Google.Protobuf.Reflection
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for field accessors.
|
||||
/// </summary>
|
||||
internal abstract class FieldAccessorBase : IFieldAccessor
|
||||
{
|
||||
private readonly Func<IMessage, object> getValueDelegate;
|
||||
private readonly FieldDescriptor descriptor;
|
||||
|
||||
internal FieldAccessorBase(PropertyInfo property, FieldDescriptor descriptor)
|
||||
{
|
||||
this.descriptor = descriptor;
|
||||
getValueDelegate = ReflectionUtil.CreateFuncIMessageObject(property.GetGetMethod());
|
||||
}
|
||||
|
||||
public FieldDescriptor Descriptor { get { return descriptor; } }
|
||||
|
||||
public object GetValue(IMessage message)
|
||||
{
|
||||
return getValueDelegate(message);
|
||||
}
|
||||
|
||||
public abstract void Clear(IMessage message);
|
||||
public abstract void SetValue(IMessage message, object value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,347 @@
|
||||
#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.
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
|
||||
namespace Google.Protobuf.Reflection
|
||||
{
|
||||
/// <summary>
|
||||
/// Descriptor for a field or extension within a message in a .proto file.
|
||||
/// </summary>
|
||||
public sealed class FieldDescriptor : DescriptorBase, IComparable<FieldDescriptor>
|
||||
{
|
||||
private EnumDescriptor enumType;
|
||||
private MessageDescriptor messageType;
|
||||
private FieldType fieldType;
|
||||
private readonly string propertyName; // Annoyingly, needed in Crosslink.
|
||||
private IFieldAccessor accessor;
|
||||
|
||||
/// <summary>
|
||||
/// Get the field's containing message type.
|
||||
/// </summary>
|
||||
public MessageDescriptor ContainingType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the oneof containing this field, or <c>null</c> if it is not part of a oneof.
|
||||
/// </summary>
|
||||
public OneofDescriptor ContainingOneof { get; }
|
||||
|
||||
/// <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 string JsonName { get; }
|
||||
|
||||
internal FieldDescriptorProto Proto { get; }
|
||||
|
||||
internal FieldDescriptor(FieldDescriptorProto proto, FileDescriptor file,
|
||||
MessageDescriptor parent, int index, string propertyName)
|
||||
: base(file, file.ComputeFullName(parent, proto.Name), index)
|
||||
{
|
||||
Proto = proto;
|
||||
if (proto.Type != 0)
|
||||
{
|
||||
fieldType = GetFieldTypeFromProtoType(proto.Type);
|
||||
}
|
||||
|
||||
if (FieldNumber <= 0)
|
||||
{
|
||||
throw new DescriptorValidationException(this, "Field numbers must be positive integers.");
|
||||
}
|
||||
ContainingType = parent;
|
||||
// OneofIndex "defaults" to -1 due to a hack in FieldDescriptor.OnConstruction.
|
||||
if (proto.OneofIndex != -1)
|
||||
{
|
||||
if (proto.OneofIndex < 0 || proto.OneofIndex >= parent.Proto.OneofDecl.Count)
|
||||
{
|
||||
throw new DescriptorValidationException(this,
|
||||
$"FieldDescriptorProto.oneof_index is out of range for type {parent.Name}");
|
||||
}
|
||||
ContainingOneof = parent.Oneofs[proto.OneofIndex];
|
||||
}
|
||||
|
||||
file.DescriptorPool.AddSymbol(this);
|
||||
// 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.
|
||||
this.propertyName = propertyName;
|
||||
JsonName = Proto.JsonName == "" ? JsonFormatter.ToJsonName(Proto.Name) : Proto.JsonName;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The brief name of the descriptor's target.
|
||||
/// </summary>
|
||||
public override string Name => Proto.Name;
|
||||
|
||||
/// <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 IFieldAccessor Accessor => accessor;
|
||||
|
||||
/// <summary>
|
||||
/// Maps a field type as included in the .proto file to a FieldType.
|
||||
/// </summary>
|
||||
private static FieldType GetFieldTypeFromProtoType(FieldDescriptorProto.Types.Type type)
|
||||
{
|
||||
switch (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;
|
||||
default:
|
||||
throw new ArgumentException("Invalid type specified");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <c>true</c> if this field is a repeated field; <c>false</c> otherwise.
|
||||
/// </summary>
|
||||
public bool IsRepeated => Proto.Label == FieldDescriptorProto.Types.Label.Repeated;
|
||||
|
||||
/// <summary>
|
||||
/// Returns <c>true</c> if this field is a map field; <c>false</c> otherwise.
|
||||
/// </summary>
|
||||
public bool IsMap => fieldType == FieldType.Message && messageType.Proto.Options != null && messageType.Proto.Options.MapEntry;
|
||||
|
||||
/// <summary>
|
||||
/// Returns <c>true</c> if this field is a packed, repeated field; <c>false</c> otherwise.
|
||||
/// </summary>
|
||||
public bool IsPacked =>
|
||||
// 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.
|
||||
Proto.Options == null || Proto.Options.Packed;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the type of the field.
|
||||
/// </summary>
|
||||
public FieldType FieldType => fieldType;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the field number declared in the proto file.
|
||||
/// </summary>
|
||||
public int FieldNumber => Proto.Number;
|
||||
|
||||
/// <summary>
|
||||
/// Compares this descriptor with another one, ordering in "canonical" order
|
||||
/// which simply means ascending order by field number. <paramref name="other"/>
|
||||
/// must be a field of the same type, i.e. the <see cref="ContainingType"/> of
|
||||
/// both fields must be the same.
|
||||
/// </summary>
|
||||
public int CompareTo(FieldDescriptor other)
|
||||
{
|
||||
if (other.ContainingType != ContainingType)
|
||||
{
|
||||
throw new ArgumentException("FieldDescriptors can only be compared to other FieldDescriptors " +
|
||||
"for fields of the same message type.");
|
||||
}
|
||||
return FieldNumber - other.FieldNumber;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For enum fields, returns the field's type.
|
||||
/// </summary>
|
||||
public EnumDescriptor EnumType
|
||||
{
|
||||
get
|
||||
{
|
||||
if (fieldType != FieldType.Enum)
|
||||
{
|
||||
throw new InvalidOperationException("EnumType is only valid for enum fields.");
|
||||
}
|
||||
return enumType;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For embedded message and group fields, returns the field's type.
|
||||
/// </summary>
|
||||
public MessageDescriptor MessageType
|
||||
{
|
||||
get
|
||||
{
|
||||
if (fieldType != FieldType.Message)
|
||||
{
|
||||
throw new InvalidOperationException("MessageType is only valid for message fields.");
|
||||
}
|
||||
return messageType;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The (possibly empty) set of custom options for this field.
|
||||
/// </summary>
|
||||
public CustomOptions CustomOptions => Proto.Options?.CustomOptions ?? CustomOptions.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Look up and cross-link all field types etc.
|
||||
/// </summary>
|
||||
internal void CrossLink()
|
||||
{
|
||||
if (Proto.TypeName != "")
|
||||
{
|
||||
IDescriptor typeDescriptor =
|
||||
File.DescriptorPool.LookupSymbol(Proto.TypeName, this);
|
||||
|
||||
if (Proto.Type != 0)
|
||||
{
|
||||
// Choose field type based on symbol.
|
||||
if (typeDescriptor is MessageDescriptor)
|
||||
{
|
||||
fieldType = FieldType.Message;
|
||||
}
|
||||
else if (typeDescriptor is EnumDescriptor)
|
||||
{
|
||||
fieldType = FieldType.Enum;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new DescriptorValidationException(this, $"\"{Proto.TypeName}\" is not a type.");
|
||||
}
|
||||
}
|
||||
|
||||
if (fieldType == FieldType.Message)
|
||||
{
|
||||
if (!(typeDescriptor is MessageDescriptor))
|
||||
{
|
||||
throw new DescriptorValidationException(this, $"\"{Proto.TypeName}\" is not a message type.");
|
||||
}
|
||||
messageType = (MessageDescriptor) typeDescriptor;
|
||||
|
||||
if (Proto.DefaultValue != "")
|
||||
{
|
||||
throw new DescriptorValidationException(this, "Messages can't have default values.");
|
||||
}
|
||||
}
|
||||
else if (fieldType == FieldType.Enum)
|
||||
{
|
||||
if (!(typeDescriptor is EnumDescriptor))
|
||||
{
|
||||
throw new DescriptorValidationException(this, $"\"{Proto.TypeName}\" is not an enum type.");
|
||||
}
|
||||
enumType = (EnumDescriptor) typeDescriptor;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new DescriptorValidationException(this, "Field with primitive type has type_name.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (fieldType == FieldType.Message || fieldType == FieldType.Enum)
|
||||
{
|
||||
throw new DescriptorValidationException(this, "Field with message or enum type missing type_name.");
|
||||
}
|
||||
}
|
||||
|
||||
// Note: no attempt to perform any default value parsing
|
||||
|
||||
File.DescriptorPool.AddFieldByNumber(this);
|
||||
|
||||
if (ContainingType != null && ContainingType.Proto.Options != null && ContainingType.Proto.Options.MessageSetWireFormat)
|
||||
{
|
||||
throw new DescriptorValidationException(this, "MessageSet format is not supported.");
|
||||
}
|
||||
accessor = CreateAccessor();
|
||||
}
|
||||
|
||||
private IFieldAccessor CreateAccessor()
|
||||
{
|
||||
// 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 (propertyName == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var property = ContainingType.ClrType.GetProperty(propertyName);
|
||||
if (property == null)
|
||||
{
|
||||
throw new DescriptorValidationException(this, $"Property {propertyName} not found in {ContainingType.ClrType}");
|
||||
}
|
||||
return IsMap ? new MapFieldAccessor(property, this)
|
||||
: IsRepeated ? new RepeatedFieldAccessor(property, this)
|
||||
: (IFieldAccessor) new SingleFieldAccessor(property, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
#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.
|
||||
#endregion
|
||||
|
||||
namespace Google.Protobuf.Reflection
|
||||
{
|
||||
/// <summary>
|
||||
/// Enumeration of all the possible field types.
|
||||
/// </summary>
|
||||
public enum FieldType
|
||||
{
|
||||
/// <summary>
|
||||
/// The <c>double</c> field type.
|
||||
/// </summary>
|
||||
Double,
|
||||
/// <summary>
|
||||
/// The <c>float</c> field type.
|
||||
/// </summary>
|
||||
Float,
|
||||
/// <summary>
|
||||
/// The <c>int64</c> field type.
|
||||
/// </summary>
|
||||
Int64,
|
||||
/// <summary>
|
||||
/// The <c>uint64</c> field type.
|
||||
/// </summary>
|
||||
UInt64,
|
||||
/// <summary>
|
||||
/// The <c>int32</c> field type.
|
||||
/// </summary>
|
||||
Int32,
|
||||
/// <summary>
|
||||
/// The <c>fixed64</c> field type.
|
||||
/// </summary>
|
||||
Fixed64,
|
||||
/// <summary>
|
||||
/// The <c>fixed32</c> field type.
|
||||
/// </summary>
|
||||
Fixed32,
|
||||
/// <summary>
|
||||
/// The <c>bool</c> field type.
|
||||
/// </summary>
|
||||
Bool,
|
||||
/// <summary>
|
||||
/// The <c>string</c> field type.
|
||||
/// </summary>
|
||||
String,
|
||||
/// <summary>
|
||||
/// The field type used for groups (not supported in this implementation).
|
||||
/// </summary>
|
||||
Group,
|
||||
/// <summary>
|
||||
/// The field type used for message fields.
|
||||
/// </summary>
|
||||
Message,
|
||||
/// <summary>
|
||||
/// The <c>bytes</c> field type.
|
||||
/// </summary>
|
||||
Bytes,
|
||||
/// <summary>
|
||||
/// The <c>uint32</c> field type.
|
||||
/// </summary>
|
||||
UInt32,
|
||||
/// <summary>
|
||||
/// The <c>sfixed32</c> field type.
|
||||
/// </summary>
|
||||
SFixed32,
|
||||
/// <summary>
|
||||
/// The <c>sfixed64</c> field type.
|
||||
/// </summary>
|
||||
SFixed64,
|
||||
/// <summary>
|
||||
/// The <c>sint32</c> field type.
|
||||
/// </summary>
|
||||
SInt32,
|
||||
/// <summary>
|
||||
/// The <c>sint64</c> field type.
|
||||
/// </summary>
|
||||
SInt64,
|
||||
/// <summary>
|
||||
/// The field type used for enum fields.
|
||||
/// </summary>
|
||||
Enum
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,338 @@
|
||||
#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.
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Google.Protobuf.Reflection
|
||||
{
|
||||
/// <summary>
|
||||
/// Describes a .proto file, including everything defined within.
|
||||
/// IDescriptor is implemented such that the File property returns this descriptor,
|
||||
/// and the FullName is the same as the Name.
|
||||
/// </summary>
|
||||
public sealed class FileDescriptor : IDescriptor
|
||||
{
|
||||
private FileDescriptor(ByteString descriptorData, FileDescriptorProto proto, FileDescriptor[] dependencies, DescriptorPool pool, bool allowUnknownDependencies, GeneratedClrTypeInfo generatedCodeInfo)
|
||||
{
|
||||
SerializedData = descriptorData;
|
||||
DescriptorPool = pool;
|
||||
Proto = proto;
|
||||
Dependencies = new ReadOnlyCollection<FileDescriptor>((FileDescriptor[]) dependencies.Clone());
|
||||
|
||||
PublicDependencies = DeterminePublicDependencies(this, proto, dependencies, allowUnknownDependencies);
|
||||
|
||||
pool.AddPackage(Package, this);
|
||||
|
||||
MessageTypes = DescriptorUtil.ConvertAndMakeReadOnly(proto.MessageType,
|
||||
(message, index) =>
|
||||
new MessageDescriptor(message, this, null, index, generatedCodeInfo.NestedTypes[index]));
|
||||
|
||||
EnumTypes = DescriptorUtil.ConvertAndMakeReadOnly(proto.EnumType,
|
||||
(enumType, index) =>
|
||||
new EnumDescriptor(enumType, this, null, index, generatedCodeInfo.NestedEnums[index]));
|
||||
|
||||
Services = DescriptorUtil.ConvertAndMakeReadOnly(proto.Service,
|
||||
(service, index) =>
|
||||
new ServiceDescriptor(service, this, index));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the full name of a descriptor within this file, with an optional parent message.
|
||||
/// </summary>
|
||||
internal string ComputeFullName(MessageDescriptor parent, string name)
|
||||
{
|
||||
if (parent != null)
|
||||
{
|
||||
return parent.FullName + "." + name;
|
||||
}
|
||||
if (Package.Length > 0)
|
||||
{
|
||||
return Package + "." + name;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extracts public dependencies from direct dependencies. This is a static method despite its
|
||||
/// first parameter, as the value we're in the middle of constructing is only used for exceptions.
|
||||
/// </summary>
|
||||
private static IList<FileDescriptor> DeterminePublicDependencies(FileDescriptor @this, FileDescriptorProto proto, FileDescriptor[] dependencies, bool allowUnknownDependencies)
|
||||
{
|
||||
var nameToFileMap = new Dictionary<string, FileDescriptor>();
|
||||
foreach (var file in dependencies)
|
||||
{
|
||||
nameToFileMap[file.Name] = file;
|
||||
}
|
||||
var publicDependencies = new List<FileDescriptor>();
|
||||
for (int i = 0; i < proto.PublicDependency.Count; i++)
|
||||
{
|
||||
int index = proto.PublicDependency[i];
|
||||
if (index < 0 || index >= proto.Dependency.Count)
|
||||
{
|
||||
throw new DescriptorValidationException(@this, "Invalid public dependency index.");
|
||||
}
|
||||
string name = proto.Dependency[index];
|
||||
FileDescriptor file = nameToFileMap[name];
|
||||
if (file == null)
|
||||
{
|
||||
if (!allowUnknownDependencies)
|
||||
{
|
||||
throw new DescriptorValidationException(@this, "Invalid public dependency: " + name);
|
||||
}
|
||||
// Ignore unknown dependencies.
|
||||
}
|
||||
else
|
||||
{
|
||||
publicDependencies.Add(file);
|
||||
}
|
||||
}
|
||||
return new ReadOnlyCollection<FileDescriptor>(publicDependencies);
|
||||
}
|
||||
|
||||
/// <value>
|
||||
/// The descriptor in its protocol message representation.
|
||||
/// </value>
|
||||
internal FileDescriptorProto Proto { get; }
|
||||
|
||||
/// <value>
|
||||
/// The file name.
|
||||
/// </value>
|
||||
public string Name => Proto.Name;
|
||||
|
||||
/// <summary>
|
||||
/// The package as declared in the .proto file. This may or may not
|
||||
/// be equivalent to the .NET namespace of the generated classes.
|
||||
/// </summary>
|
||||
public string Package => Proto.Package;
|
||||
|
||||
/// <value>
|
||||
/// Unmodifiable list of top-level message types declared in this file.
|
||||
/// </value>
|
||||
public IList<MessageDescriptor> MessageTypes { get; }
|
||||
|
||||
/// <value>
|
||||
/// Unmodifiable list of top-level enum types declared in this file.
|
||||
/// </value>
|
||||
public IList<EnumDescriptor> EnumTypes { get; }
|
||||
|
||||
/// <value>
|
||||
/// Unmodifiable list of top-level services declared in this file.
|
||||
/// </value>
|
||||
public IList<ServiceDescriptor> Services { get; }
|
||||
|
||||
/// <value>
|
||||
/// Unmodifiable list of this file's dependencies (imports).
|
||||
/// </value>
|
||||
public IList<FileDescriptor> Dependencies { get; }
|
||||
|
||||
/// <value>
|
||||
/// Unmodifiable list of this file's public dependencies (public imports).
|
||||
/// </value>
|
||||
public IList<FileDescriptor> PublicDependencies { get; }
|
||||
|
||||
/// <value>
|
||||
/// The original serialized binary form of this descriptor.
|
||||
/// </value>
|
||||
public ByteString SerializedData { get; }
|
||||
|
||||
/// <value>
|
||||
/// Implementation of IDescriptor.FullName - just returns the same as Name.
|
||||
/// </value>
|
||||
string IDescriptor.FullName => Name;
|
||||
|
||||
/// <value>
|
||||
/// Implementation of IDescriptor.File - just returns this descriptor.
|
||||
/// </value>
|
||||
FileDescriptor IDescriptor.File => this;
|
||||
|
||||
/// <value>
|
||||
/// Pool containing symbol descriptors.
|
||||
/// </value>
|
||||
internal DescriptorPool DescriptorPool { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Finds a type (message, enum, service or extension) in the file by name. Does not find nested types.
|
||||
/// </summary>
|
||||
/// <param name="name">The unqualified type name to look for.</param>
|
||||
/// <typeparam name="T">The type of descriptor to look for</typeparam>
|
||||
/// <returns>The type's descriptor, or null if not found.</returns>
|
||||
public T FindTypeByName<T>(String name)
|
||||
where T : class, IDescriptor
|
||||
{
|
||||
// Don't allow looking up nested types. This will make optimization
|
||||
// easier later.
|
||||
if (name.IndexOf('.') != -1)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (Package.Length > 0)
|
||||
{
|
||||
name = Package + "." + name;
|
||||
}
|
||||
T result = DescriptorPool.FindSymbol<T>(name);
|
||||
if (result != null && result.File == this)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds a FileDescriptor from its protocol buffer representation.
|
||||
/// </summary>
|
||||
/// <param name="descriptorData">The original serialized descriptor data.
|
||||
/// We have only limited proto2 support, so serializing FileDescriptorProto
|
||||
/// would not necessarily give us this.</param>
|
||||
/// <param name="proto">The protocol message form of the FileDescriptor.</param>
|
||||
/// <param name="dependencies">FileDescriptors corresponding to all of the
|
||||
/// file's dependencies, in the exact order listed in the .proto file. May be null,
|
||||
/// in which case it is treated as an empty array.</param>
|
||||
/// <param name="allowUnknownDependencies">Whether unknown dependencies are ignored (true) or cause an exception to be thrown (false).</param>
|
||||
/// <param name="generatedCodeInfo">Details about generated code, for the purposes of reflection.</param>
|
||||
/// <exception cref="DescriptorValidationException">If <paramref name="proto"/> is not
|
||||
/// a valid descriptor. This can occur for a number of reasons, such as a field
|
||||
/// having an undefined type or because two messages were defined with the same name.</exception>
|
||||
private static FileDescriptor BuildFrom(ByteString descriptorData, FileDescriptorProto proto, FileDescriptor[] dependencies, bool allowUnknownDependencies, GeneratedClrTypeInfo generatedCodeInfo)
|
||||
{
|
||||
// Building descriptors involves two steps: translating and linking.
|
||||
// In the translation step (implemented by FileDescriptor's
|
||||
// constructor), we build an object tree mirroring the
|
||||
// FileDescriptorProto's tree and put all of the descriptors into the
|
||||
// DescriptorPool's lookup tables. In the linking step, we look up all
|
||||
// type references in the DescriptorPool, so that, for example, a
|
||||
// FieldDescriptor for an embedded message contains a pointer directly
|
||||
// to the Descriptor for that message's type. We also detect undefined
|
||||
// types in the linking step.
|
||||
if (dependencies == null)
|
||||
{
|
||||
dependencies = new FileDescriptor[0];
|
||||
}
|
||||
|
||||
DescriptorPool pool = new DescriptorPool(dependencies);
|
||||
FileDescriptor result = new FileDescriptor(descriptorData, proto, dependencies, pool, allowUnknownDependencies, generatedCodeInfo);
|
||||
|
||||
// Validate that the dependencies we've been passed (as FileDescriptors) are actually the ones we
|
||||
// need.
|
||||
if (dependencies.Length != proto.Dependency.Count)
|
||||
{
|
||||
throw new DescriptorValidationException(
|
||||
result,
|
||||
"Dependencies passed to FileDescriptor.BuildFrom() don't match " +
|
||||
"those listed in the FileDescriptorProto.");
|
||||
}
|
||||
|
||||
result.CrossLink();
|
||||
return result;
|
||||
}
|
||||
|
||||
private void CrossLink()
|
||||
{
|
||||
foreach (MessageDescriptor message in MessageTypes)
|
||||
{
|
||||
message.CrossLink();
|
||||
}
|
||||
|
||||
foreach (ServiceDescriptor service in Services)
|
||||
{
|
||||
service.CrossLink();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a descriptor for generated code.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method is only designed to be used by the results of generating code with protoc,
|
||||
/// which creates the appropriate dependencies etc. It has to be public because the generated
|
||||
/// code is "external", but should not be called directly by end users.
|
||||
/// </remarks>
|
||||
public static FileDescriptor FromGeneratedCode(
|
||||
byte[] descriptorData,
|
||||
FileDescriptor[] dependencies,
|
||||
GeneratedClrTypeInfo generatedCodeInfo)
|
||||
{
|
||||
FileDescriptorProto proto;
|
||||
try
|
||||
{
|
||||
proto = FileDescriptorProto.Parser.ParseFrom(descriptorData);
|
||||
}
|
||||
catch (InvalidProtocolBufferException e)
|
||||
{
|
||||
throw new ArgumentException("Failed to parse protocol buffer descriptor for generated code.", e);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// When building descriptors for generated code, we allow unknown
|
||||
// dependencies by default.
|
||||
return BuildFrom(ByteString.CopyFrom(descriptorData), proto, dependencies, true, generatedCodeInfo);
|
||||
}
|
||||
catch (DescriptorValidationException e)
|
||||
{
|
||||
throw new ArgumentException($"Invalid embedded descriptor for \"{proto.Name}\".", e);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="System.String" /> that represents this instance.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A <see cref="System.String" /> that represents this instance.
|
||||
/// </returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return $"FileDescriptor for {Name}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the file descriptor for descriptor.proto.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is used for protos which take a direct dependency on <c>descriptor.proto</c>, typically for
|
||||
/// annotations. While <c>descriptor.proto</c> is a proto2 file, it is built into the Google.Protobuf
|
||||
/// runtime for reflection purposes. The messages are internal to the runtime as they would require
|
||||
/// proto2 semantics for full support, but the file descriptor is available via this property. The
|
||||
/// C# codegen in protoc automatically uses this property when it detects a dependency on <c>descriptor.proto</c>.
|
||||
/// </remarks>
|
||||
/// <value>
|
||||
/// The file descriptor for <c>descriptor.proto</c>.
|
||||
/// </value>
|
||||
public static FileDescriptor DescriptorProtoFileDescriptor { get { return DescriptorReflection.Descriptor; } }
|
||||
|
||||
/// <summary>
|
||||
/// The (possibly empty) set of custom options for this file.
|
||||
/// </summary>
|
||||
public CustomOptions CustomOptions => Proto.Options?.CustomOptions ?? CustomOptions.Empty;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
#region Copyright notice and license
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2015 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.
|
||||
#endregion
|
||||
using System;
|
||||
|
||||
namespace Google.Protobuf.Reflection
|
||||
{
|
||||
/// <summary>
|
||||
/// Extra information provided by generated code when initializing a message or file descriptor.
|
||||
/// These are constructed as required, and are not long-lived. Hand-written code should
|
||||
/// never need to use this type.
|
||||
/// </summary>
|
||||
public sealed class GeneratedClrTypeInfo
|
||||
{
|
||||
private static readonly string[] EmptyNames = new string[0];
|
||||
private static readonly GeneratedClrTypeInfo[] EmptyCodeInfo = new GeneratedClrTypeInfo[0];
|
||||
|
||||
/// <summary>
|
||||
/// Irrelevant for file descriptors; the CLR type for the message for message descriptors.
|
||||
/// </summary>
|
||||
public Type ClrType { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Irrelevant for file descriptors; the parser for message descriptors.
|
||||
/// </summary>
|
||||
public MessageParser Parser { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Irrelevant for file descriptors; the CLR property names (in message descriptor field order)
|
||||
/// for fields in the message for message descriptors.
|
||||
/// </summary>
|
||||
public string[] PropertyNames { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Irrelevant for file descriptors; the CLR property "base" names (in message descriptor oneof order)
|
||||
/// for oneofs in the message for message descriptors. It is expected that for a oneof name of "Foo",
|
||||
/// there will be a "FooCase" property and a "ClearFoo" method.
|
||||
/// </summary>
|
||||
public string[] OneofNames { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The reflection information for types within this file/message descriptor. Elements may be null
|
||||
/// if there is no corresponding generated type, e.g. for map entry types.
|
||||
/// </summary>
|
||||
public GeneratedClrTypeInfo[] NestedTypes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The CLR types for enums within this file/message descriptor.
|
||||
/// </summary>
|
||||
public Type[] NestedEnums { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a GeneratedClrTypeInfo for a message descriptor, with nested types, nested enums, the CLR type, property names and oneof names.
|
||||
/// Each array parameter may be null, to indicate a lack of values.
|
||||
/// The parameter order is designed to make it feasible to format the generated code readably.
|
||||
/// </summary>
|
||||
public GeneratedClrTypeInfo(Type clrType, MessageParser parser, string[] propertyNames, string[] oneofNames, Type[] nestedEnums, GeneratedClrTypeInfo[] nestedTypes)
|
||||
{
|
||||
NestedTypes = nestedTypes ?? EmptyCodeInfo;
|
||||
NestedEnums = nestedEnums ?? ReflectionUtil.EmptyTypes;
|
||||
ClrType = clrType;
|
||||
Parser = parser;
|
||||
PropertyNames = propertyNames ?? EmptyNames;
|
||||
OneofNames = oneofNames ?? EmptyNames;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a GeneratedClrTypeInfo for a file descriptor, with only types and enums.
|
||||
/// </summary>
|
||||
public GeneratedClrTypeInfo(Type[] nestedEnums, GeneratedClrTypeInfo[] nestedTypes)
|
||||
: this(null, null, null, null, nestedEnums, nestedTypes)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
#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.
|
||||
#endregion
|
||||
|
||||
namespace Google.Protobuf.Reflection
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface implemented by all descriptor types.
|
||||
/// </summary>
|
||||
public interface IDescriptor
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the name of the entity (message, field etc) being described.
|
||||
/// </summary>
|
||||
string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the fully-qualified name of the entity being described.
|
||||
/// </summary>
|
||||
string FullName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the descriptor for the .proto file that this entity is part of.
|
||||
/// </summary>
|
||||
FileDescriptor File { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
#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.
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
namespace Google.Protobuf.Reflection
|
||||
{
|
||||
/// <summary>
|
||||
/// Allows fields to be reflectively accessed.
|
||||
/// </summary>
|
||||
public interface IFieldAccessor
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the descriptor associated with this field.
|
||||
/// </summary>
|
||||
FieldDescriptor Descriptor { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Clears the field in the specified message. (For repeated fields,
|
||||
/// this clears the list.)
|
||||
/// </summary>
|
||||
void Clear(IMessage message);
|
||||
|
||||
/// <summary>
|
||||
/// Fetches the field value. For repeated values, this will be an
|
||||
/// <see cref="IList"/> implementation. For map values, this will be an
|
||||
/// <see cref="IDictionary"/> implementation.
|
||||
/// </summary>
|
||||
object GetValue(IMessage message);
|
||||
|
||||
/// <summary>
|
||||
/// Mutator for single "simple" fields only.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Repeated fields are mutated by fetching the value and manipulating it as a list.
|
||||
/// Map fields are mutated by fetching the value and manipulating it as a dictionary.
|
||||
/// </remarks>
|
||||
/// <exception cref="InvalidOperationException">The field is not a "simple" field.</exception>
|
||||
void SetValue(IMessage message, object value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
#region Copyright notice and license
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2015 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.
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Google.Protobuf.Reflection
|
||||
{
|
||||
/// <summary>
|
||||
/// Accessor for map fields.
|
||||
/// </summary>
|
||||
internal sealed class MapFieldAccessor : FieldAccessorBase
|
||||
{
|
||||
internal MapFieldAccessor(PropertyInfo property, FieldDescriptor descriptor) : base(property, descriptor)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Clear(IMessage message)
|
||||
{
|
||||
IDictionary list = (IDictionary) GetValue(message);
|
||||
list.Clear();
|
||||
}
|
||||
|
||||
public override void SetValue(IMessage message, object value)
|
||||
{
|
||||
throw new InvalidOperationException("SetValue is not implemented for map fields");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,326 @@
|
||||
#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.
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
#if NET35
|
||||
// Needed for ReadOnlyDictionary, which does not exist in .NET 3.5
|
||||
using Google.Protobuf.Collections;
|
||||
#endif
|
||||
|
||||
namespace Google.Protobuf.Reflection
|
||||
{
|
||||
/// <summary>
|
||||
/// Describes a message type.
|
||||
/// </summary>
|
||||
public sealed class MessageDescriptor : DescriptorBase
|
||||
{
|
||||
private static readonly HashSet<string> WellKnownTypeNames = new HashSet<string>
|
||||
{
|
||||
"google/protobuf/any.proto",
|
||||
"google/protobuf/api.proto",
|
||||
"google/protobuf/duration.proto",
|
||||
"google/protobuf/empty.proto",
|
||||
"google/protobuf/wrappers.proto",
|
||||
"google/protobuf/timestamp.proto",
|
||||
"google/protobuf/field_mask.proto",
|
||||
"google/protobuf/source_context.proto",
|
||||
"google/protobuf/struct.proto",
|
||||
"google/protobuf/type.proto",
|
||||
};
|
||||
|
||||
private readonly IList<FieldDescriptor> fieldsInDeclarationOrder;
|
||||
private readonly IList<FieldDescriptor> fieldsInNumberOrder;
|
||||
private readonly IDictionary<string, FieldDescriptor> jsonFieldMap;
|
||||
|
||||
internal MessageDescriptor(DescriptorProto proto, FileDescriptor file, MessageDescriptor parent, int typeIndex, GeneratedClrTypeInfo generatedCodeInfo)
|
||||
: base(file, file.ComputeFullName(parent, proto.Name), typeIndex)
|
||||
{
|
||||
Proto = proto;
|
||||
Parser = generatedCodeInfo?.Parser;
|
||||
ClrType = generatedCodeInfo?.ClrType;
|
||||
ContainingType = parent;
|
||||
|
||||
// Note use of generatedCodeInfo. rather than generatedCodeInfo?. here... we don't expect
|
||||
// to see any nested oneofs, types or enums in "not actually generated" code... we do
|
||||
// expect fields though (for map entry messages).
|
||||
Oneofs = DescriptorUtil.ConvertAndMakeReadOnly(
|
||||
proto.OneofDecl,
|
||||
(oneof, index) =>
|
||||
new OneofDescriptor(oneof, file, this, index, generatedCodeInfo.OneofNames[index]));
|
||||
|
||||
NestedTypes = DescriptorUtil.ConvertAndMakeReadOnly(
|
||||
proto.NestedType,
|
||||
(type, index) =>
|
||||
new MessageDescriptor(type, file, this, index, generatedCodeInfo.NestedTypes[index]));
|
||||
|
||||
EnumTypes = DescriptorUtil.ConvertAndMakeReadOnly(
|
||||
proto.EnumType,
|
||||
(type, index) =>
|
||||
new EnumDescriptor(type, file, this, index, generatedCodeInfo.NestedEnums[index]));
|
||||
|
||||
fieldsInDeclarationOrder = DescriptorUtil.ConvertAndMakeReadOnly(
|
||||
proto.Field,
|
||||
(field, index) =>
|
||||
new FieldDescriptor(field, file, this, index, generatedCodeInfo?.PropertyNames[index]));
|
||||
fieldsInNumberOrder = new ReadOnlyCollection<FieldDescriptor>(fieldsInDeclarationOrder.OrderBy(field => field.FieldNumber).ToArray());
|
||||
// TODO: Use field => field.Proto.JsonName when we're confident it's appropriate. (And then use it in the formatter, too.)
|
||||
jsonFieldMap = CreateJsonFieldMap(fieldsInNumberOrder);
|
||||
file.DescriptorPool.AddSymbol(this);
|
||||
Fields = new FieldCollection(this);
|
||||
}
|
||||
|
||||
private static ReadOnlyDictionary<string, FieldDescriptor> CreateJsonFieldMap(IList<FieldDescriptor> fields)
|
||||
{
|
||||
var map = new Dictionary<string, FieldDescriptor>();
|
||||
foreach (var field in fields)
|
||||
{
|
||||
map[field.Name] = field;
|
||||
map[field.JsonName] = field;
|
||||
}
|
||||
return new ReadOnlyDictionary<string, FieldDescriptor>(map);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The brief name of the descriptor's target.
|
||||
/// </summary>
|
||||
public override string Name => Proto.Name;
|
||||
|
||||
internal DescriptorProto Proto { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The CLR type used to represent message instances from this descriptor.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <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, so this property will return null in such cases.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// For wrapper types (<see cref="Google.Protobuf.WellKnownTypes.StringValue"/> and the like), the type returned here
|
||||
/// will be the generated message type, not the native type used by reflection for fields of those types. Code
|
||||
/// using reflection should call <see cref="IsWrapperType"/> to determine whether a message descriptor represents
|
||||
/// a wrapper type, and handle the result appropriately.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public Type ClrType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// A parser for this message type.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// As <see cref="MessageDescriptor"/> is not generic, this cannot be statically
|
||||
/// typed to the relevant type, but it should produce objects of a type compatible with <see cref="ClrType"/>.
|
||||
/// </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. No message parser object is created for
|
||||
/// such messages, so this property will return null in such cases.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// For wrapper types (<see cref="Google.Protobuf.WellKnownTypes.StringValue"/> and the like), the parser returned here
|
||||
/// will be the generated message type, not the native type used by reflection for fields of those types. Code
|
||||
/// using reflection should call <see cref="IsWrapperType"/> to determine whether a message descriptor represents
|
||||
/// a wrapper type, and handle the result appropriately.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public MessageParser Parser { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether this message is one of the "well known types" which may have runtime/protoc support.
|
||||
/// </summary>
|
||||
internal bool IsWellKnownType => File.Package == "google.protobuf" && WellKnownTypeNames.Contains(File.Name);
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether this message is one of the "wrapper types" used for fields which represent primitive values
|
||||
/// with the addition of presence.
|
||||
/// </summary>
|
||||
internal bool IsWrapperType => File.Package == "google.protobuf" && File.Name == "google/protobuf/wrappers.proto";
|
||||
|
||||
/// <value>
|
||||
/// If this is a nested type, get the outer descriptor, otherwise null.
|
||||
/// </value>
|
||||
public MessageDescriptor ContainingType { get; }
|
||||
|
||||
/// <value>
|
||||
/// A collection of fields, which can be retrieved by name or field number.
|
||||
/// </value>
|
||||
public FieldCollection Fields { get; }
|
||||
|
||||
/// <value>
|
||||
/// An unmodifiable list of this message type's nested types.
|
||||
/// </value>
|
||||
public IList<MessageDescriptor> NestedTypes { get; }
|
||||
|
||||
/// <value>
|
||||
/// An unmodifiable list of this message type's enum types.
|
||||
/// </value>
|
||||
public IList<EnumDescriptor> EnumTypes { get; }
|
||||
|
||||
/// <value>
|
||||
/// An unmodifiable list of the "oneof" field collections in this message type.
|
||||
/// </value>
|
||||
public IList<OneofDescriptor> Oneofs { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Finds a field by field name.
|
||||
/// </summary>
|
||||
/// <param name="name">The unqualified name of the field (e.g. "foo").</param>
|
||||
/// <returns>The field's descriptor, or null if not found.</returns>
|
||||
public FieldDescriptor FindFieldByName(String name) => File.DescriptorPool.FindSymbol<FieldDescriptor>(FullName + "." + name);
|
||||
|
||||
/// <summary>
|
||||
/// Finds a field by field number.
|
||||
/// </summary>
|
||||
/// <param name="number">The field number within this message type.</param>
|
||||
/// <returns>The field's descriptor, or null if not found.</returns>
|
||||
public FieldDescriptor FindFieldByNumber(int number) => File.DescriptorPool.FindFieldByNumber(this, number);
|
||||
|
||||
/// <summary>
|
||||
/// Finds a nested descriptor by name. The is valid for fields, nested
|
||||
/// message types, oneofs and enums.
|
||||
/// </summary>
|
||||
/// <param name="name">The unqualified name of the descriptor, e.g. "Foo"</param>
|
||||
/// <returns>The descriptor, or null if not found.</returns>
|
||||
public T FindDescriptor<T>(string name) where T : class, IDescriptor =>
|
||||
File.DescriptorPool.FindSymbol<T>(FullName + "." + name);
|
||||
|
||||
/// <summary>
|
||||
/// The (possibly empty) set of custom options for this message.
|
||||
/// </summary>
|
||||
public CustomOptions CustomOptions => Proto.Options?.CustomOptions ?? CustomOptions.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Looks up and cross-links all fields and nested types.
|
||||
/// </summary>
|
||||
internal void CrossLink()
|
||||
{
|
||||
foreach (MessageDescriptor message in NestedTypes)
|
||||
{
|
||||
message.CrossLink();
|
||||
}
|
||||
|
||||
foreach (FieldDescriptor field in fieldsInDeclarationOrder)
|
||||
{
|
||||
field.CrossLink();
|
||||
}
|
||||
|
||||
foreach (OneofDescriptor oneof in Oneofs)
|
||||
{
|
||||
oneof.CrossLink();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A collection to simplify retrieving the field accessor for a particular field.
|
||||
/// </summary>
|
||||
public sealed class FieldCollection
|
||||
{
|
||||
private readonly MessageDescriptor messageDescriptor;
|
||||
|
||||
internal FieldCollection(MessageDescriptor messageDescriptor)
|
||||
{
|
||||
this.messageDescriptor = messageDescriptor;
|
||||
}
|
||||
|
||||
/// <value>
|
||||
/// Returns the fields in the message as an immutable list, in the order in which they
|
||||
/// are declared in the source .proto file.
|
||||
/// </value>
|
||||
public IList<FieldDescriptor> InDeclarationOrder() => messageDescriptor.fieldsInDeclarationOrder;
|
||||
|
||||
/// <value>
|
||||
/// Returns the fields in the message as an immutable list, in ascending field number
|
||||
/// order. Field numbers need not be contiguous, so there is no direct mapping from the
|
||||
/// index in the list to the field number; to retrieve a field by field number, it is better
|
||||
/// to use the <see cref="FieldCollection"/> indexer.
|
||||
/// </value>
|
||||
public IList<FieldDescriptor> InFieldNumberOrder() => messageDescriptor.fieldsInNumberOrder;
|
||||
|
||||
// TODO: consider making this public in the future. (Being conservative for now...)
|
||||
|
||||
/// <value>
|
||||
/// Returns a read-only dictionary mapping the field names in this message as they're available
|
||||
/// in the JSON representation to the field descriptors. For example, a field <c>foo_bar</c>
|
||||
/// in the message would result two entries, one with a key <c>fooBar</c> and one with a key
|
||||
/// <c>foo_bar</c>, both referring to the same field.
|
||||
/// </value>
|
||||
internal IDictionary<string, FieldDescriptor> ByJsonName() => messageDescriptor.jsonFieldMap;
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the descriptor for the field with the given number.
|
||||
/// </summary>
|
||||
/// <param name="number">Number of the field to retrieve the descriptor for</param>
|
||||
/// <returns>The accessor for the given field</returns>
|
||||
/// <exception cref="KeyNotFoundException">The message descriptor does not contain a field
|
||||
/// with the given number</exception>
|
||||
public FieldDescriptor this[int number]
|
||||
{
|
||||
get
|
||||
{
|
||||
var fieldDescriptor = messageDescriptor.FindFieldByNumber(number);
|
||||
if (fieldDescriptor == null)
|
||||
{
|
||||
throw new KeyNotFoundException("No such field number");
|
||||
}
|
||||
return fieldDescriptor;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the descriptor for the field with the given name.
|
||||
/// </summary>
|
||||
/// <param name="name">Name of the field to retrieve the descriptor for</param>
|
||||
/// <returns>The descriptor for the given field</returns>
|
||||
/// <exception cref="KeyNotFoundException">The message descriptor does not contain a field
|
||||
/// with the given name</exception>
|
||||
public FieldDescriptor this[string name]
|
||||
{
|
||||
get
|
||||
{
|
||||
var fieldDescriptor = messageDescriptor.FindFieldByName(name);
|
||||
if (fieldDescriptor == null)
|
||||
{
|
||||
throw new KeyNotFoundException("No such field name");
|
||||
}
|
||||
return fieldDescriptor;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
#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.
|
||||
#endregion
|
||||
|
||||
namespace Google.Protobuf.Reflection
|
||||
{
|
||||
/// <summary>
|
||||
/// Describes a single method in a service.
|
||||
/// </summary>
|
||||
public sealed class MethodDescriptor : DescriptorBase
|
||||
{
|
||||
private readonly MethodDescriptorProto proto;
|
||||
private readonly ServiceDescriptor service;
|
||||
private MessageDescriptor inputType;
|
||||
private MessageDescriptor outputType;
|
||||
|
||||
/// <value>
|
||||
/// The service this method belongs to.
|
||||
/// </value>
|
||||
public ServiceDescriptor Service { get { return service; } }
|
||||
|
||||
/// <value>
|
||||
/// The method's input type.
|
||||
/// </value>
|
||||
public MessageDescriptor InputType { get { return inputType; } }
|
||||
|
||||
/// <value>
|
||||
/// The method's input type.
|
||||
/// </value>
|
||||
public MessageDescriptor OutputType { get { return outputType; } }
|
||||
|
||||
/// <value>
|
||||
/// Indicates if client streams multiple requests.
|
||||
/// </value>
|
||||
public bool IsClientStreaming { get { return proto.ClientStreaming; } }
|
||||
|
||||
/// <value>
|
||||
/// Indicates if server streams multiple responses.
|
||||
/// </value>
|
||||
public bool IsServerStreaming { get { return proto.ServerStreaming; } }
|
||||
|
||||
/// <summary>
|
||||
/// The (possibly empty) set of custom options for this method.
|
||||
/// </summary>
|
||||
public CustomOptions CustomOptions => Proto.Options?.CustomOptions ?? CustomOptions.Empty;
|
||||
|
||||
internal MethodDescriptor(MethodDescriptorProto proto, FileDescriptor file,
|
||||
ServiceDescriptor parent, int index)
|
||||
: base(file, parent.FullName + "." + proto.Name, index)
|
||||
{
|
||||
this.proto = proto;
|
||||
service = parent;
|
||||
file.DescriptorPool.AddSymbol(this);
|
||||
}
|
||||
|
||||
internal MethodDescriptorProto Proto { get { return proto; } }
|
||||
|
||||
/// <summary>
|
||||
/// The brief name of the descriptor's target.
|
||||
/// </summary>
|
||||
public override string Name { get { return proto.Name; } }
|
||||
|
||||
internal void CrossLink()
|
||||
{
|
||||
IDescriptor lookup = File.DescriptorPool.LookupSymbol(Proto.InputType, this);
|
||||
if (!(lookup is MessageDescriptor))
|
||||
{
|
||||
throw new DescriptorValidationException(this, "\"" + Proto.InputType + "\" is not a message type.");
|
||||
}
|
||||
inputType = (MessageDescriptor) lookup;
|
||||
|
||||
lookup = File.DescriptorPool.LookupSymbol(Proto.OutputType, this);
|
||||
if (!(lookup is MessageDescriptor))
|
||||
{
|
||||
throw new DescriptorValidationException(this, "\"" + Proto.OutputType + "\" is not a message type.");
|
||||
}
|
||||
outputType = (MessageDescriptor) lookup;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
#region Copyright notice and license
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2015 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.
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Google.Protobuf.Reflection
|
||||
{
|
||||
/// <summary>
|
||||
/// Reflection access for a oneof, allowing clear and "get case" actions.
|
||||
/// </summary>
|
||||
public sealed class OneofAccessor
|
||||
{
|
||||
private readonly Func<IMessage, int> caseDelegate;
|
||||
private readonly Action<IMessage> clearDelegate;
|
||||
private OneofDescriptor descriptor;
|
||||
|
||||
internal OneofAccessor(PropertyInfo caseProperty, MethodInfo clearMethod, OneofDescriptor descriptor)
|
||||
{
|
||||
if (!caseProperty.CanRead)
|
||||
{
|
||||
throw new ArgumentException("Cannot read from property");
|
||||
}
|
||||
this.descriptor = descriptor;
|
||||
caseDelegate = ReflectionUtil.CreateFuncIMessageT<int>(caseProperty.GetGetMethod());
|
||||
|
||||
this.descriptor = descriptor;
|
||||
clearDelegate = ReflectionUtil.CreateActionIMessage(clearMethod);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the descriptor for this oneof.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The descriptor of the oneof.
|
||||
/// </value>
|
||||
public OneofDescriptor Descriptor { get { return descriptor; } }
|
||||
|
||||
/// <summary>
|
||||
/// Clears the oneof in the specified message.
|
||||
/// </summary>
|
||||
public void Clear(IMessage message)
|
||||
{
|
||||
clearDelegate(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates which field in the oneof is set for specified message
|
||||
/// </summary>
|
||||
public FieldDescriptor GetCaseFieldDescriptor(IMessage message)
|
||||
{
|
||||
int fieldNumber = caseDelegate(message);
|
||||
if (fieldNumber > 0)
|
||||
{
|
||||
return descriptor.ContainingType.FindFieldByNumber(fieldNumber);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
#region Copyright notice and license
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2015 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.
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Google.Protobuf.Reflection
|
||||
{
|
||||
/// <summary>
|
||||
/// Describes a "oneof" field collection in a message type: a set of
|
||||
/// fields of which at most one can be set in any particular message.
|
||||
/// </summary>
|
||||
public sealed class OneofDescriptor : DescriptorBase
|
||||
{
|
||||
private readonly OneofDescriptorProto proto;
|
||||
private MessageDescriptor containingType;
|
||||
private IList<FieldDescriptor> fields;
|
||||
private readonly OneofAccessor accessor;
|
||||
|
||||
internal OneofDescriptor(OneofDescriptorProto proto, FileDescriptor file, MessageDescriptor parent, int index, string clrName)
|
||||
: base(file, file.ComputeFullName(parent, proto.Name), index)
|
||||
{
|
||||
this.proto = proto;
|
||||
containingType = parent;
|
||||
|
||||
file.DescriptorPool.AddSymbol(this);
|
||||
accessor = CreateAccessor(clrName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The brief name of the descriptor's target.
|
||||
/// </summary>
|
||||
public override string Name { get { return proto.Name; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the message type containing this oneof.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The message type containing this oneof.
|
||||
/// </value>
|
||||
public MessageDescriptor ContainingType
|
||||
{
|
||||
get { return containingType; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the fields within this oneof, in declaration order.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The fields within this oneof, in declaration order.
|
||||
/// </value>
|
||||
public IList<FieldDescriptor> Fields { get { return fields; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets an accessor for reflective access to the values associated with the oneof
|
||||
/// in a particular message.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The accessor used for reflective access.
|
||||
/// </value>
|
||||
public OneofAccessor Accessor { get { return accessor; } }
|
||||
|
||||
/// <summary>
|
||||
/// The (possibly empty) set of custom options for this oneof.
|
||||
/// </summary>
|
||||
public CustomOptions CustomOptions => proto.Options?.CustomOptions ?? CustomOptions.Empty;
|
||||
|
||||
internal void CrossLink()
|
||||
{
|
||||
List<FieldDescriptor> fieldCollection = new List<FieldDescriptor>();
|
||||
foreach (var field in ContainingType.Fields.InDeclarationOrder())
|
||||
{
|
||||
if (field.ContainingOneof == this)
|
||||
{
|
||||
fieldCollection.Add(field);
|
||||
}
|
||||
}
|
||||
fields = new ReadOnlyCollection<FieldDescriptor>(fieldCollection);
|
||||
}
|
||||
|
||||
private OneofAccessor CreateAccessor(string clrName)
|
||||
{
|
||||
var caseProperty = containingType.ClrType.GetProperty(clrName + "Case");
|
||||
if (caseProperty == null)
|
||||
{
|
||||
throw new DescriptorValidationException(this, $"Property {clrName}Case not found in {containingType.ClrType}");
|
||||
}
|
||||
var clearMethod = containingType.ClrType.GetMethod("Clear" + clrName);
|
||||
if (clearMethod == null)
|
||||
{
|
||||
throw new DescriptorValidationException(this, $"Method Clear{clrName} not found in {containingType.ClrType}");
|
||||
}
|
||||
|
||||
return new OneofAccessor(caseProperty, clearMethod, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
#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.
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
|
||||
namespace Google.Protobuf.Reflection
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies the original name (in the .proto file) of a named element,
|
||||
/// such as an enum value.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Field)]
|
||||
public class OriginalNameAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of the element in the .proto file.
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// If the name is preferred in the .proto file.
|
||||
/// </summary>
|
||||
public bool PreferredAlias { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new attribute instance for the given name.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the element in the .proto file.</param>
|
||||
public OriginalNameAttribute(string name)
|
||||
{
|
||||
Name = ProtoPreconditions.CheckNotNull(name, nameof(name));
|
||||
PreferredAlias = true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
#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.
|
||||
#endregion
|
||||
|
||||
namespace Google.Protobuf.Reflection
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a package in the symbol table. We use PackageDescriptors
|
||||
/// just as placeholders so that someone cannot define, say, a message type
|
||||
/// that has the same name as an existing package.
|
||||
/// </summary>
|
||||
internal sealed class PackageDescriptor : IDescriptor
|
||||
{
|
||||
private readonly string name;
|
||||
private readonly string fullName;
|
||||
private readonly FileDescriptor file;
|
||||
|
||||
internal PackageDescriptor(string name, string fullName, FileDescriptor file)
|
||||
{
|
||||
this.file = file;
|
||||
this.fullName = fullName;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public string Name
|
||||
{
|
||||
get { return name; }
|
||||
}
|
||||
|
||||
public string FullName
|
||||
{
|
||||
get { return fullName; }
|
||||
}
|
||||
|
||||
public FileDescriptor File
|
||||
{
|
||||
get { return file; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
#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.
|
||||
#endregion
|
||||
|
||||
// This file just contains partial classes for any autogenerated classes that need additional support.
|
||||
namespace Google.Protobuf.Reflection
|
||||
{
|
||||
internal partial class FieldDescriptorProto
|
||||
{
|
||||
// We can't tell the difference between "explicitly set to 0" and "not set"
|
||||
// in proto3, but we need to tell the difference for OneofIndex. descriptor.proto
|
||||
// is really a proto2 file, but the runtime doesn't know about proto2 semantics...
|
||||
// We fake it by defaulting to -1.
|
||||
partial void OnConstruction()
|
||||
{
|
||||
OneofIndex = -1;
|
||||
}
|
||||
}
|
||||
|
||||
internal partial class FieldOptions
|
||||
{
|
||||
// We can't tell the difference between "explicitly set to false" and "not set"
|
||||
// in proto3, but we need to tell the difference for FieldDescriptor.IsPacked.
|
||||
// This won't work if we ever need to support proto2, but at that point we'll be
|
||||
// able to remove this hack and use field presence instead.
|
||||
partial void OnConstruction()
|
||||
{
|
||||
Packed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
#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.
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Google.Protobuf.Reflection
|
||||
{
|
||||
/// <summary>
|
||||
/// The methods in this class are somewhat evil, and should not be tampered with lightly.
|
||||
/// Basically they allow the creation of relatively weakly typed delegates from MethodInfos
|
||||
/// which are more strongly typed. They do this by creating an appropriate strongly typed
|
||||
/// delegate from the MethodInfo, and then calling that within an anonymous method.
|
||||
/// Mind-bending stuff (at least to your humble narrator) but the resulting delegates are
|
||||
/// very fast compared with calling Invoke later on.
|
||||
/// </summary>
|
||||
internal static class ReflectionUtil
|
||||
{
|
||||
/// <summary>
|
||||
/// Empty Type[] used when calling GetProperty to force property instead of indexer fetching.
|
||||
/// </summary>
|
||||
internal static readonly Type[] EmptyTypes = new Type[0];
|
||||
|
||||
/// <summary>
|
||||
/// Creates a delegate which will cast the argument to the appropriate method target type,
|
||||
/// call the method on it, then convert the result to object.
|
||||
/// </summary>
|
||||
internal static Func<IMessage, object> CreateFuncIMessageObject(MethodInfo method)
|
||||
{
|
||||
ParameterExpression parameter = Expression.Parameter(typeof(IMessage), "p");
|
||||
Expression downcast = Expression.Convert(parameter, method.DeclaringType);
|
||||
Expression call = Expression.Call(downcast, method);
|
||||
Expression upcast = Expression.Convert(call, typeof(object));
|
||||
return Expression.Lambda<Func<IMessage, object>>(upcast, parameter).Compile();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a delegate which will cast the argument to the appropriate method target type,
|
||||
/// call the method on it, then convert the result to the specified type.
|
||||
/// </summary>
|
||||
internal static Func<IMessage, T> CreateFuncIMessageT<T>(MethodInfo method)
|
||||
{
|
||||
ParameterExpression parameter = Expression.Parameter(typeof(IMessage), "p");
|
||||
Expression downcast = Expression.Convert(parameter, method.DeclaringType);
|
||||
Expression call = Expression.Call(downcast, method);
|
||||
Expression upcast = Expression.Convert(call, typeof(T));
|
||||
return Expression.Lambda<Func<IMessage, T>>(upcast, parameter).Compile();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a delegate which will execute the given method after casting the first argument to
|
||||
/// the target type of the method, and the second argument to the first parameter type of the method.
|
||||
/// </summary>
|
||||
internal static Action<IMessage, object> CreateActionIMessageObject(MethodInfo method)
|
||||
{
|
||||
ParameterExpression targetParameter = Expression.Parameter(typeof(IMessage), "target");
|
||||
ParameterExpression argParameter = Expression.Parameter(typeof(object), "arg");
|
||||
Expression castTarget = Expression.Convert(targetParameter, method.DeclaringType);
|
||||
Expression castArgument = Expression.Convert(argParameter, method.GetParameters()[0].ParameterType);
|
||||
Expression call = Expression.Call(castTarget, method, castArgument);
|
||||
return Expression.Lambda<Action<IMessage, object>>(call, targetParameter, argParameter).Compile();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a delegate which will execute the given method after casting the first argument to
|
||||
/// the target type of the method.
|
||||
/// </summary>
|
||||
internal static Action<IMessage> CreateActionIMessage(MethodInfo method)
|
||||
{
|
||||
ParameterExpression targetParameter = Expression.Parameter(typeof(IMessage), "target");
|
||||
Expression castTarget = Expression.Convert(targetParameter, method.DeclaringType);
|
||||
Expression call = Expression.Call(castTarget, method);
|
||||
return Expression.Lambda<Action<IMessage>>(call, targetParameter).Compile();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
#region Copyright notice and license
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2015 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.
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Google.Protobuf.Reflection
|
||||
{
|
||||
/// <summary>
|
||||
/// Accessor for repeated fields.
|
||||
/// </summary>
|
||||
internal sealed class RepeatedFieldAccessor : FieldAccessorBase
|
||||
{
|
||||
internal RepeatedFieldAccessor(PropertyInfo property, FieldDescriptor descriptor) : base(property, descriptor)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Clear(IMessage message)
|
||||
{
|
||||
IList list = (IList) GetValue(message);
|
||||
list.Clear();
|
||||
}
|
||||
|
||||
public override void SetValue(IMessage message, object value)
|
||||
{
|
||||
throw new InvalidOperationException("SetValue is not implemented for repeated fields");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
#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.
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Google.Protobuf.Reflection
|
||||
{
|
||||
/// <summary>
|
||||
/// Describes a service type.
|
||||
/// </summary>
|
||||
public sealed class ServiceDescriptor : DescriptorBase
|
||||
{
|
||||
private readonly ServiceDescriptorProto proto;
|
||||
private readonly IList<MethodDescriptor> methods;
|
||||
|
||||
internal ServiceDescriptor(ServiceDescriptorProto proto, FileDescriptor file, int index)
|
||||
: base(file, file.ComputeFullName(null, proto.Name), index)
|
||||
{
|
||||
this.proto = proto;
|
||||
methods = DescriptorUtil.ConvertAndMakeReadOnly(proto.Method,
|
||||
(method, i) => new MethodDescriptor(method, file, this, i));
|
||||
|
||||
file.DescriptorPool.AddSymbol(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The brief name of the descriptor's target.
|
||||
/// </summary>
|
||||
public override string Name { get { return proto.Name; } }
|
||||
|
||||
internal ServiceDescriptorProto Proto { get { return proto; } }
|
||||
|
||||
/// <value>
|
||||
/// An unmodifiable list of methods in this service.
|
||||
/// </value>
|
||||
public IList<MethodDescriptor> Methods
|
||||
{
|
||||
get { return methods; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds a method by name.
|
||||
/// </summary>
|
||||
/// <param name="name">The unqualified name of the method (e.g. "Foo").</param>
|
||||
/// <returns>The method's decsriptor, or null if not found.</returns>
|
||||
public MethodDescriptor FindMethodByName(String name)
|
||||
{
|
||||
return File.DescriptorPool.FindSymbol<MethodDescriptor>(FullName + "." + name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The (possibly empty) set of custom options for this service.
|
||||
/// </summary>
|
||||
public CustomOptions CustomOptions => Proto.Options?.CustomOptions ?? CustomOptions.Empty;
|
||||
|
||||
internal void CrossLink()
|
||||
{
|
||||
foreach (MethodDescriptor method in methods)
|
||||
{
|
||||
method.CrossLink();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
#region Copyright notice and license
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2015 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.
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Google.Protobuf.Reflection
|
||||
{
|
||||
/// <summary>
|
||||
/// Accessor for single fields.
|
||||
/// </summary>
|
||||
internal sealed class SingleFieldAccessor : FieldAccessorBase
|
||||
{
|
||||
// All the work here is actually done in the constructor - it creates the appropriate delegates.
|
||||
// There are various cases to consider, based on the property type (message, string/bytes, or "genuine" primitive)
|
||||
// and proto2 vs proto3 for non-message types, as proto3 doesn't support "full" presence detection or default
|
||||
// values.
|
||||
|
||||
private readonly Action<IMessage, object> setValueDelegate;
|
||||
private readonly Action<IMessage> clearDelegate;
|
||||
|
||||
internal SingleFieldAccessor(PropertyInfo property, FieldDescriptor descriptor) : base(property, descriptor)
|
||||
{
|
||||
if (!property.CanWrite)
|
||||
{
|
||||
throw new ArgumentException("Not all required properties/methods available");
|
||||
}
|
||||
setValueDelegate = ReflectionUtil.CreateActionIMessageObject(property.GetSetMethod());
|
||||
|
||||
var clrType = property.PropertyType;
|
||||
|
||||
// TODO: Validate that this is a reasonable single field? (Should be a value type, a message type, or string/ByteString.)
|
||||
object defaultValue =
|
||||
descriptor.FieldType == FieldType.Message ? null
|
||||
: clrType == typeof(string) ? ""
|
||||
: clrType == typeof(ByteString) ? ByteString.Empty
|
||||
: Activator.CreateInstance(clrType);
|
||||
clearDelegate = message => SetValue(message, defaultValue);
|
||||
}
|
||||
|
||||
public override void Clear(IMessage message)
|
||||
{
|
||||
clearDelegate(message);
|
||||
}
|
||||
|
||||
public override void SetValue(IMessage message, object value)
|
||||
{
|
||||
setValueDelegate(message, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
#region Copyright notice and license
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2015 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.
|
||||
#endregion
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Google.Protobuf.Reflection
|
||||
{
|
||||
/// <summary>
|
||||
/// An immutable registry of types which can be looked up by their full name.
|
||||
/// </summary>
|
||||
public sealed class TypeRegistry
|
||||
{
|
||||
/// <summary>
|
||||
/// An empty type registry, containing no types.
|
||||
/// </summary>
|
||||
public static TypeRegistry Empty { get; } = new TypeRegistry(new Dictionary<string, MessageDescriptor>());
|
||||
|
||||
private readonly Dictionary<string, MessageDescriptor> fullNameToMessageMap;
|
||||
|
||||
private TypeRegistry(Dictionary<string, MessageDescriptor> fullNameToMessageMap)
|
||||
{
|
||||
this.fullNameToMessageMap = fullNameToMessageMap;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to find a message descriptor by its full name.
|
||||
/// </summary>
|
||||
/// <param name="fullName">The full name of the message, which is the dot-separated
|
||||
/// combination of package, containing messages and message name</param>
|
||||
/// <returns>The message descriptor corresponding to <paramref name="fullName"/> or null
|
||||
/// if there is no such message descriptor.</returns>
|
||||
public MessageDescriptor Find(string fullName)
|
||||
{
|
||||
MessageDescriptor ret;
|
||||
// Ignore the return value as ret will end up with the right value either way.
|
||||
fullNameToMessageMap.TryGetValue(fullName, out ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a type registry from the specified set of file descriptors.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is a convenience overload for <see cref="FromFiles(IEnumerable{FileDescriptor})"/>
|
||||
/// to allow calls such as <c>TypeRegistry.FromFiles(descriptor1, descriptor2)</c>.
|
||||
/// </remarks>
|
||||
/// <param name="fileDescriptors">The set of files to include in the registry. Must not contain null values.</param>
|
||||
/// <returns>A type registry for the given files.</returns>
|
||||
public static TypeRegistry FromFiles(params FileDescriptor[] fileDescriptors)
|
||||
{
|
||||
return FromFiles((IEnumerable<FileDescriptor>) fileDescriptors);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a type registry from the specified set of file descriptors.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// All message types within all the specified files are added to the registry, and
|
||||
/// the dependencies of the specified files are also added, recursively.
|
||||
/// </remarks>
|
||||
/// <param name="fileDescriptors">The set of files to include in the registry. Must not contain null values.</param>
|
||||
/// <returns>A type registry for the given files.</returns>
|
||||
public static TypeRegistry FromFiles(IEnumerable<FileDescriptor> fileDescriptors)
|
||||
{
|
||||
ProtoPreconditions.CheckNotNull(fileDescriptors, nameof(fileDescriptors));
|
||||
var builder = new Builder();
|
||||
foreach (var file in fileDescriptors)
|
||||
{
|
||||
builder.AddFile(file);
|
||||
}
|
||||
return builder.Build();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a type registry from the file descriptor parents of the specified set of message descriptors.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is a convenience overload for <see cref="FromMessages(IEnumerable{MessageDescriptor})"/>
|
||||
/// to allow calls such as <c>TypeRegistry.FromFiles(descriptor1, descriptor2)</c>.
|
||||
/// </remarks>
|
||||
/// <param name="messageDescriptors">The set of message descriptors to use to identify file descriptors to include in the registry.
|
||||
/// Must not contain null values.</param>
|
||||
/// <returns>A type registry for the given files.</returns>
|
||||
public static TypeRegistry FromMessages(params MessageDescriptor[] messageDescriptors)
|
||||
{
|
||||
return FromMessages((IEnumerable<MessageDescriptor>) messageDescriptors);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a type registry from the file descriptor parents of the specified set of message descriptors.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
/// <param name="messageDescriptors">The set of message descriptors to use to identify file descriptors to include in the registry.
|
||||
/// Must not contain null values.</param>
|
||||
/// <returns>A type registry for the given files.</returns>
|
||||
public static TypeRegistry FromMessages(IEnumerable<MessageDescriptor> messageDescriptors)
|
||||
{
|
||||
ProtoPreconditions.CheckNotNull(messageDescriptors, nameof(messageDescriptors));
|
||||
return FromFiles(messageDescriptors.Select(md => md.File));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builder class which isn't exposed, but acts as a convenient alternative to passing round two dictionaries in recursive calls.
|
||||
/// </summary>
|
||||
private class Builder
|
||||
{
|
||||
private readonly Dictionary<string, MessageDescriptor> types;
|
||||
private readonly HashSet<string> fileDescriptorNames;
|
||||
|
||||
internal Builder()
|
||||
{
|
||||
types = new Dictionary<string, MessageDescriptor>();
|
||||
fileDescriptorNames = new HashSet<string>();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user