diff options
author | Jon Skeet <jonskeet@google.com> | 2015-07-14 10:24:52 +0100 |
---|---|---|
committer | Jon Skeet <jonskeet@google.com> | 2015-07-14 10:24:52 +0100 |
commit | 9f37de960fec523c42b47d982f3fdab5b2d4d4d9 (patch) | |
tree | 63b35102573a757990bb8af5c4a5fde9fe737572 /csharp/src/ProtocolBuffers/Reflection | |
parent | 24f8626cc918b5aa58d6d3377d295a95f7049776 (diff) | |
download | protobuf-9f37de960fec523c42b47d982f3fdab5b2d4d4d9.tar.gz protobuf-9f37de960fec523c42b47d982f3fdab5b2d4d4d9.tar.bz2 protobuf-9f37de960fec523c42b47d982f3fdab5b2d4d4d9.zip |
Changing reflection namespace (part 1)
- Move types into Google.Protobuf.Reflection
- Change codegen to reflect that in generated types
Generated code changes coming in part 2
Diffstat (limited to 'csharp/src/ProtocolBuffers/Reflection')
23 files changed, 2647 insertions, 0 deletions
diff --git a/csharp/src/ProtocolBuffers/Reflection/DescriptorBase.cs b/csharp/src/ProtocolBuffers/Reflection/DescriptorBase.cs new file mode 100644 index 00000000..0300cd58 --- /dev/null +++ b/csharp/src/ProtocolBuffers/Reflection/DescriptorBase.cs @@ -0,0 +1,82 @@ +#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; } + } + + 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; } + } + } +}
\ No newline at end of file diff --git a/csharp/src/ProtocolBuffers/Reflection/DescriptorPool.cs b/csharp/src/ProtocolBuffers/Reflection/DescriptorPool.cs new file mode 100644 index 00000000..157ea400 --- /dev/null +++ b/csharp/src/ProtocolBuffers/Reflection/DescriptorPool.cs @@ -0,0 +1,365 @@ +#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; + } + + 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> + internal IDescriptor LookupSymbol(string name, IDescriptor relativeTo) + { + // TODO(jonskeet): This could be optimized in a number of ways. + + 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. + + // TODO(jonskeet): Make this more efficient. May not be worth using StringBuilder at all + 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; + } + } + } +}
\ No newline at end of file diff --git a/csharp/src/ProtocolBuffers/Reflection/DescriptorUtil.cs b/csharp/src/ProtocolBuffers/Reflection/DescriptorUtil.cs new file mode 100644 index 00000000..af31dfb1 --- /dev/null +++ b/csharp/src/ProtocolBuffers/Reflection/DescriptorUtil.cs @@ -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.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); + } + } +}
\ No newline at end of file diff --git a/csharp/src/ProtocolBuffers/Reflection/DescriptorValidationException.cs b/csharp/src/ProtocolBuffers/Reflection/DescriptorValidationException.cs new file mode 100644 index 00000000..143671db --- /dev/null +++ b/csharp/src/ProtocolBuffers/Reflection/DescriptorValidationException.cs @@ -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; + } + } +}
\ No newline at end of file diff --git a/csharp/src/ProtocolBuffers/Reflection/EnumDescriptor.cs b/csharp/src/ProtocolBuffers/Reflection/EnumDescriptor.cs new file mode 100644 index 00000000..eca85a06 --- /dev/null +++ b/csharp/src/ProtocolBuffers/Reflection/EnumDescriptor.cs @@ -0,0 +1,109 @@ +#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 Google.Protobuf.DescriptorProtos; + +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; + + internal EnumDescriptor(EnumDescriptorProto proto, FileDescriptor file, MessageDescriptor parent, int index) + : base(file, file.ComputeFullName(parent, proto.Name), index) + { + this.proto = proto; + 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; } } + + /// <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); + } + } +}
\ No newline at end of file diff --git a/csharp/src/ProtocolBuffers/Reflection/EnumValueDescriptor.cs b/csharp/src/ProtocolBuffers/Reflection/EnumValueDescriptor.cs new file mode 100644 index 00000000..9c225676 --- /dev/null +++ b/csharp/src/ProtocolBuffers/Reflection/EnumValueDescriptor.cs @@ -0,0 +1,63 @@ +#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 Google.Protobuf.DescriptorProtos; + +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; } } + + public override string Name { get { return proto.Name; } } + + public int Number { get { return Proto.Number; } } + + public EnumDescriptor EnumDescriptor { get { return enumDescriptor; } } + } +}
\ No newline at end of file diff --git a/csharp/src/ProtocolBuffers/Reflection/FieldAccessorBase.cs b/csharp/src/ProtocolBuffers/Reflection/FieldAccessorBase.cs new file mode 100644 index 00000000..39a63b47 --- /dev/null +++ b/csharp/src/ProtocolBuffers/Reflection/FieldAccessorBase.cs @@ -0,0 +1,67 @@ +#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<object, object> getValueDelegate; + private readonly FieldDescriptor descriptor; + + internal FieldAccessorBase(Type type, string propertyName, FieldDescriptor descriptor) + { + PropertyInfo property = type.GetProperty(propertyName); + if (property == null || !property.CanRead) + { + throw new ArgumentException("Not all required properties/methods available"); + } + this.descriptor = descriptor; + getValueDelegate = ReflectionUtil.CreateFuncObjectObject(property.GetGetMethod()); + } + + public FieldDescriptor Descriptor { get { return descriptor; } } + + public object GetValue(object message) + { + return getValueDelegate(message); + } + + public abstract void Clear(object message); + public abstract void SetValue(object message, object value); + } +} diff --git a/csharp/src/ProtocolBuffers/Reflection/FieldAccessorTable.cs b/csharp/src/ProtocolBuffers/Reflection/FieldAccessorTable.cs new file mode 100644 index 00000000..24fcbc64 --- /dev/null +++ b/csharp/src/ProtocolBuffers/Reflection/FieldAccessorTable.cs @@ -0,0 +1,97 @@ +#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.ObjectModel; + +namespace Google.Protobuf.Reflection +{ + /// <summary> + /// Provides access to fields in generated messages via reflection. + /// </summary> + public sealed class FieldAccessorTable + { + private readonly ReadOnlyCollection<IFieldAccessor> accessors; + private readonly ReadOnlyCollection<OneofAccessor> oneofs; + private readonly MessageDescriptor descriptor; + + /// <summary> + /// Constructs a FieldAccessorTable for a particular message class. + /// Only one FieldAccessorTable should be constructed per class. + /// </summary> + /// <param name="type">The CLR type for the message.</param> + /// <param name="descriptor">The type's descriptor</param> + /// <param name="propertyNames">The Pascal-case names of all the field-based properties in the message.</param> + public FieldAccessorTable(Type type, MessageDescriptor descriptor, string[] propertyNames, string[] oneofPropertyNames) + { + this.descriptor = descriptor; + var accessorsArray = new IFieldAccessor[descriptor.Fields.Count]; + for (int i = 0; i < accessorsArray.Length; i++) + { + var field = descriptor.Fields[i]; + var name = propertyNames[i]; + accessorsArray[i] = + field.IsMap ? new MapFieldAccessor(type, name, field) + : field.IsRepeated ? new RepeatedFieldAccessor(type, name, field) + : (IFieldAccessor) new SingleFieldAccessor(type, name, field); + } + accessors = new ReadOnlyCollection<IFieldAccessor>(accessorsArray); + var oneofsArray = new OneofAccessor[descriptor.Oneofs.Count]; + for (int i = 0; i < oneofsArray.Length; i++) + { + var oneof = descriptor.Oneofs[i]; + oneofsArray[i] = new OneofAccessor(type, oneofPropertyNames[i], oneof); + } + oneofs = new ReadOnlyCollection<OneofAccessor>(oneofsArray); + } + + // TODO: Validate the name here... should possibly make this type a more "general reflection access" type, + // bearing in mind the oneof parts to come as well. + /// <summary> + /// Returns all of the field accessors for the message type. + /// </summary> + public ReadOnlyCollection<IFieldAccessor> Accessors { get { return accessors; } } + + public ReadOnlyCollection<OneofAccessor> Oneofs { get { return oneofs; } } + + // TODO: Review this, as it's easy to get confused between FieldNumber and Index. + // Currently only used to get an accessor related to a oneof... maybe just make that simpler? + public IFieldAccessor this[int fieldNumber] + { + get + { + FieldDescriptor field = descriptor.FindFieldByNumber(fieldNumber); + return accessors[field.Index]; + } + } + } +}
\ No newline at end of file diff --git a/csharp/src/ProtocolBuffers/Reflection/FieldDescriptor.cs b/csharp/src/ProtocolBuffers/Reflection/FieldDescriptor.cs new file mode 100644 index 00000000..99f85517 --- /dev/null +++ b/csharp/src/ProtocolBuffers/Reflection/FieldDescriptor.cs @@ -0,0 +1,293 @@ +#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 Google.Protobuf.DescriptorProtos; + +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 readonly FieldDescriptorProto proto; + private EnumDescriptor enumType; + private MessageDescriptor messageType; + private readonly MessageDescriptor containingType; + private readonly OneofDescriptor containingOneof; + private FieldType fieldType; + + internal FieldDescriptor(FieldDescriptorProto proto, FileDescriptor file, + MessageDescriptor parent, int index) + : base(file, file.ComputeFullName(parent, proto.Name), index) + { + this.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); + } + + /// <summary> + /// The brief name of the descriptor's target. + /// </summary> + public override string Name { get { return proto.Name; } } + + internal FieldDescriptorProto Proto { get { return proto; } } + + /// <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.TYPE_DOUBLE: + return FieldType.Double; + case FieldDescriptorProto.Types.Type.TYPE_FLOAT: + return FieldType.Float; + case FieldDescriptorProto.Types.Type.TYPE_INT64: + return FieldType.Int64; + case FieldDescriptorProto.Types.Type.TYPE_UINT64: + return FieldType.UInt64; + case FieldDescriptorProto.Types.Type.TYPE_INT32: + return FieldType.Int32; + case FieldDescriptorProto.Types.Type.TYPE_FIXED64: + return FieldType.Fixed64; + case FieldDescriptorProto.Types.Type.TYPE_FIXED32: + return FieldType.Fixed32; + case FieldDescriptorProto.Types.Type.TYPE_BOOL: + return FieldType.Bool; + case FieldDescriptorProto.Types.Type.TYPE_STRING: + return FieldType.String; + case FieldDescriptorProto.Types.Type.TYPE_GROUP: + return FieldType.Group; + case FieldDescriptorProto.Types.Type.TYPE_MESSAGE: + return FieldType.Message; + case FieldDescriptorProto.Types.Type.TYPE_BYTES: + return FieldType.Bytes; + case FieldDescriptorProto.Types.Type.TYPE_UINT32: + return FieldType.UInt32; + case FieldDescriptorProto.Types.Type.TYPE_ENUM: + return FieldType.Enum; + case FieldDescriptorProto.Types.Type.TYPE_SFIXED32: + return FieldType.SFixed32; + case FieldDescriptorProto.Types.Type.TYPE_SFIXED64: + return FieldType.SFixed64; + case FieldDescriptorProto.Types.Type.TYPE_SINT32: + return FieldType.SInt32; + case FieldDescriptorProto.Types.Type.TYPE_SINT64: + return FieldType.SInt64; + default: + throw new ArgumentException("Invalid type specified"); + } + } + + public bool IsRepeated + { + get { return Proto.Label == FieldDescriptorProto.Types.Label.LABEL_REPEATED; } + } + + public bool IsMap + { + get { return fieldType == FieldType.Message && messageType.Proto.Options != null && messageType.Proto.Options.MapEntry; } + } + + public bool IsPacked + { + get { return Proto.Options != null && Proto.Options.Packed; } + } + + /// <summary> + /// Get the field's containing type. For extensions, this is the type being + /// extended, not the location where the extension was defined. See + /// <see cref="ExtensionScope" />. + /// </summary> + public MessageDescriptor ContainingType + { + get { return containingType; } + } + + public OneofDescriptor ContainingOneof + { + get { return containingOneof; } + } + + public FieldType FieldType + { + get { return fieldType; } + } + + public int FieldNumber + { + get { return 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 enum fields."); + } + return messageType; + } + } + + /// <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."); + } + } + } +}
\ No newline at end of file diff --git a/csharp/src/ProtocolBuffers/Reflection/FieldType.cs b/csharp/src/ProtocolBuffers/Reflection/FieldType.cs new file mode 100644 index 00000000..41fa702d --- /dev/null +++ b/csharp/src/ProtocolBuffers/Reflection/FieldType.cs @@ -0,0 +1,60 @@ +#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. The odd formatting is to make it very clear + /// which attribute applies to which value, while maintaining a compact format. + /// </summary> + public enum FieldType + { + Double, + Float, + Int64, + UInt64, + Int32, + Fixed64, + Fixed32, + Bool, + String, + Group, + Message, + Bytes, + UInt32, + SFixed32, + SFixed64, + SInt32, + SInt64, + Enum + } +}
\ No newline at end of file diff --git a/csharp/src/ProtocolBuffers/Reflection/FileDescriptor.cs b/csharp/src/ProtocolBuffers/Reflection/FileDescriptor.cs new file mode 100644 index 00000000..7738e3b9 --- /dev/null +++ b/csharp/src/ProtocolBuffers/Reflection/FileDescriptor.cs @@ -0,0 +1,354 @@ +#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 Google.Protobuf.DescriptorProtos; +using FileOptions = Google.Protobuf.DescriptorProtos.FileOptions; + +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 readonly FileDescriptorProto proto; + private readonly IList<MessageDescriptor> messageTypes; + private readonly IList<EnumDescriptor> enumTypes; + private readonly IList<ServiceDescriptor> services; + private readonly IList<FileDescriptor> dependencies; + private readonly IList<FileDescriptor> publicDependencies; + private readonly DescriptorPool pool; + + public enum ProtoSyntax + { + Proto2, + Proto3 + } + + public ProtoSyntax Syntax + { + get { return proto.Syntax == "proto3" ? ProtoSyntax.Proto3 : ProtoSyntax.Proto2; } + } + + private FileDescriptor(FileDescriptorProto proto, FileDescriptor[] dependencies, DescriptorPool pool, bool allowUnknownDependencies) + { + this.pool = pool; + this.proto = proto; + this.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)); + + enumTypes = DescriptorUtil.ConvertAndMakeReadOnly(proto.EnumType, + (enumType, index) => + new EnumDescriptor(enumType, this, null, 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 { return proto; } + } + + /// <value> + /// The file name. + /// </value> + public string Name + { + get { return 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 + { + get { return proto.Package; } + } + + /// <value> + /// Unmodifiable list of top-level message types declared in this file. + /// </value> + public IList<MessageDescriptor> MessageTypes + { + get { return messageTypes; } + } + + /// <value> + /// Unmodifiable list of top-level enum types declared in this file. + /// </value> + public IList<EnumDescriptor> EnumTypes + { + get { return enumTypes; } + } + + /// <value> + /// Unmodifiable list of top-level services declared in this file. + /// </value> + public IList<ServiceDescriptor> Services + { + get { return services; } + } + + /// <value> + /// Unmodifiable list of this file's dependencies (imports). + /// </value> + public IList<FileDescriptor> Dependencies + { + get { return dependencies; } + } + + /// <value> + /// Unmodifiable list of this file's public dependencies (public imports). + /// </value> + public IList<FileDescriptor> PublicDependencies + { + get { return publicDependencies; } + } + + /// <value> + /// Implementation of IDescriptor.FullName - just returns the same as Name. + /// </value> + string IDescriptor.FullName + { + get { return Name; } + } + + /// <value> + /// Implementation of IDescriptor.File - just returns this descriptor. + /// </value> + FileDescriptor IDescriptor.File + { + get { return this; } + } + + /// <value> + /// Pool containing symbol descriptors. + /// </value> + internal DescriptorPool DescriptorPool + { + get { return pool; } + } + + /// <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 (or ITypeDescriptor for any)</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 = pool.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="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> + /// <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(FileDescriptorProto proto, FileDescriptor[] dependencies, bool allowUnknownDependencies) + { + // 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(proto, dependencies, pool, allowUnknownDependencies); + + // TODO(jonskeet): Reinstate these checks, or get rid of them entirely. They aren't in the Java code, + // and fail for the CustomOptions test right now. (We get "descriptor.proto" vs "google/protobuf/descriptor.proto".) + //if (dependencies.Length != proto.DependencyCount) + //{ + // throw new DescriptorValidationException(result, + // "Dependencies passed to FileDescriptor.BuildFrom() don't match " + + // "those listed in the FileDescriptorProto."); + //} + //for (int i = 0; i < proto.DependencyCount; i++) + //{ + // if (dependencies[i].Name != proto.DependencyList[i]) + // { + // 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(); + } + } + + public static FileDescriptor InternalBuildGeneratedFileFrom(byte[] descriptorData, + FileDescriptor[] dependencies) + { + 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(proto, dependencies, true); + } + catch (DescriptorValidationException e) + { + throw new ArgumentException("Invalid embedded descriptor for \"" + proto.Name + "\".", e); + } + } + + public override string ToString() + { + return "FileDescriptor for " + proto.Name; + } + } +}
\ No newline at end of file diff --git a/csharp/src/ProtocolBuffers/Reflection/IDescriptor.cs b/csharp/src/ProtocolBuffers/Reflection/IDescriptor.cs new file mode 100644 index 00000000..6506db1b --- /dev/null +++ b/csharp/src/ProtocolBuffers/Reflection/IDescriptor.cs @@ -0,0 +1,44 @@ +#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 + { + string Name { get; } + string FullName { get; } + FileDescriptor File { get; } + } +}
\ No newline at end of file diff --git a/csharp/src/ProtocolBuffers/Reflection/IFieldAccessor.cs b/csharp/src/ProtocolBuffers/Reflection/IFieldAccessor.cs new file mode 100644 index 00000000..b631d34f --- /dev/null +++ b/csharp/src/ProtocolBuffers/Reflection/IFieldAccessor.cs @@ -0,0 +1,70 @@ +#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> + /// Allows fields to be reflectively accessed. + /// </summary> + public interface IFieldAccessor + { + /// <summary> + /// Returns the descriptor associated with this field. + /// </summary> + FieldDescriptor Descriptor { get; } + + // TODO: Should the argument type for these messages by IReflectedMessage? + + /// <summary> + /// Clears the field in the specified message. (For repeated fields, + /// this clears the list.) + /// </summary> + void Clear(object 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(object 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, or the message is frozen.</exception> + void SetValue(object message, object value); + } +}
\ No newline at end of file diff --git a/csharp/src/ProtocolBuffers/Reflection/MapFieldAccessor.cs b/csharp/src/ProtocolBuffers/Reflection/MapFieldAccessor.cs new file mode 100644 index 00000000..317fbd8d --- /dev/null +++ b/csharp/src/ProtocolBuffers/Reflection/MapFieldAccessor.cs @@ -0,0 +1,58 @@ +#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; + +namespace Google.Protobuf.Reflection +{ + /// <summary> + /// Accessor for map fields. + /// </summary> + internal sealed class MapFieldAccessor : FieldAccessorBase + { + internal MapFieldAccessor(Type type, string propertyName, FieldDescriptor descriptor) : base(type, propertyName, descriptor) + { + } + + public override void Clear(object message) + { + IDictionary list = (IDictionary) GetValue(message); + list.Clear(); + } + + public override void SetValue(object message, object value) + { + throw new InvalidOperationException("SetValue is not implemented for map fields"); + } + } +} diff --git a/csharp/src/ProtocolBuffers/Reflection/MessageDescriptor.cs b/csharp/src/ProtocolBuffers/Reflection/MessageDescriptor.cs new file mode 100644 index 00000000..2bb4fbed --- /dev/null +++ b/csharp/src/ProtocolBuffers/Reflection/MessageDescriptor.cs @@ -0,0 +1,173 @@ +#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 Google.Protobuf.DescriptorProtos; + +namespace Google.Protobuf.Reflection +{ + /// <summary> + /// Describes a message type. + /// </summary> + public sealed class MessageDescriptor : DescriptorBase + { + private readonly DescriptorProto proto; + private readonly MessageDescriptor containingType; + private readonly IList<MessageDescriptor> nestedTypes; + private readonly IList<EnumDescriptor> enumTypes; + private readonly IList<FieldDescriptor> fields; + private readonly IList<OneofDescriptor> oneofs; + + internal MessageDescriptor(DescriptorProto proto, FileDescriptor file, MessageDescriptor parent, int typeIndex) + : base(file, file.ComputeFullName(parent, proto.Name), typeIndex) + { + this.proto = proto; + containingType = parent; + + oneofs = DescriptorUtil.ConvertAndMakeReadOnly(proto.OneofDecl, + (oneof, index) => + new OneofDescriptor(oneof, file, this, index)); + + nestedTypes = DescriptorUtil.ConvertAndMakeReadOnly(proto.NestedType, + (type, index) => + new MessageDescriptor(type, file, this, index)); + + enumTypes = DescriptorUtil.ConvertAndMakeReadOnly(proto.EnumType, + (type, index) => + new EnumDescriptor(type, file, this, index)); + + // TODO(jonskeet): Sort fields first? + fields = DescriptorUtil.ConvertAndMakeReadOnly(proto.Field, + (field, index) => + new FieldDescriptor(field, file, this, index)); + file.DescriptorPool.AddSymbol(this); + } + + /// <summary> + /// The brief name of the descriptor's target. + /// </summary> + public override string Name { get { return proto.Name; } } + + internal DescriptorProto Proto { get { return proto; } } + + /// <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 this message type's fields. + /// </value> + public IList<FieldDescriptor> Fields + { + get { return fields; } + } + + /// <value> + /// An unmodifiable list of this message type's nested types. + /// </value> + public IList<MessageDescriptor> NestedTypes + { + get { return nestedTypes; } + } + + /// <value> + /// An unmodifiable list of this message type's enum types. + /// </value> + public IList<EnumDescriptor> EnumTypes + { + get { return enumTypes; } + } + + public IList<OneofDescriptor> Oneofs + { + get { return oneofs; } + } + + /// <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) + { + return 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) + { + return 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 + { + return File.DescriptorPool.FindSymbol<T>(FullName + "." + name); + } + + /// <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 fields) + { + field.CrossLink(); + } + + foreach (OneofDescriptor oneof in oneofs) + { + oneof.CrossLink(); + } + } + } +}
\ No newline at end of file diff --git a/csharp/src/ProtocolBuffers/Reflection/MethodDescriptor.cs b/csharp/src/ProtocolBuffers/Reflection/MethodDescriptor.cs new file mode 100644 index 00000000..03aba5a1 --- /dev/null +++ b/csharp/src/ProtocolBuffers/Reflection/MethodDescriptor.cs @@ -0,0 +1,95 @@ +#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 Google.Protobuf.DescriptorProtos; + +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; } } + + 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; + } + } +}
\ No newline at end of file diff --git a/csharp/src/ProtocolBuffers/Reflection/OneofAccessor.cs b/csharp/src/ProtocolBuffers/Reflection/OneofAccessor.cs new file mode 100644 index 00000000..7a11d36b --- /dev/null +++ b/csharp/src/ProtocolBuffers/Reflection/OneofAccessor.cs @@ -0,0 +1,85 @@ +#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<object, int> caseDelegate; + private readonly Action<object> clearDelegate; + private OneofDescriptor descriptor; + + internal OneofAccessor(Type type, string propertyName, OneofDescriptor descriptor) + { + PropertyInfo property = type.GetProperty(propertyName + "Case"); + if (property == null || !property.CanRead) + { + throw new ArgumentException("Not all required properties/methods available"); + } + this.descriptor = descriptor; + caseDelegate = ReflectionUtil.CreateFuncObjectT<int>(property.GetGetMethod()); + + this.descriptor = descriptor; + MethodInfo clearMethod = type.GetMethod("Clear" + propertyName); + clearDelegate = ReflectionUtil.CreateActionObject(clearMethod); + } + + public OneofDescriptor Descriptor { get { return descriptor; } } + + /// <summary> + /// Clears the oneof in the specified message. + /// </summary> + public void Clear(object message) + { + clearDelegate(message); + } + + /// <summary> + /// Indicates which field in the oneof is set for specified message + /// </summary> + public FieldDescriptor GetCaseFieldDescriptor(object message) + { + int fieldNumber = caseDelegate(message); + if (fieldNumber > 0) + { + return descriptor.ContainingType.FindFieldByNumber(fieldNumber); + } + return null; + } + } +} diff --git a/csharp/src/ProtocolBuffers/Reflection/OneofDescriptor.cs b/csharp/src/ProtocolBuffers/Reflection/OneofDescriptor.cs new file mode 100644 index 00000000..94033c7e --- /dev/null +++ b/csharp/src/ProtocolBuffers/Reflection/OneofDescriptor.cs @@ -0,0 +1,79 @@ +#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; +using Google.Protobuf.DescriptorProtos; + +namespace Google.Protobuf.Reflection +{ + public sealed class OneofDescriptor : DescriptorBase + { + private readonly OneofDescriptorProto proto; + private MessageDescriptor containingType; + private IList<FieldDescriptor> fields; + + internal OneofDescriptor(OneofDescriptorProto proto, FileDescriptor file, MessageDescriptor parent, int index) + : base(file, file.ComputeFullName(parent, proto.Name), index) + { + this.proto = proto; + containingType = parent; + + file.DescriptorPool.AddSymbol(this); + } + + /// <summary> + /// The brief name of the descriptor's target. + /// </summary> + public override string Name { get { return proto.Name; } } + + public MessageDescriptor ContainingType + { + get { return containingType; } + } + + public IList<FieldDescriptor> Fields { get { return fields; } } + + internal void CrossLink() + { + List<FieldDescriptor> fieldCollection = new List<FieldDescriptor>(); + foreach (var field in ContainingType.Fields) + { + if (field.ContainingOneof == this) + { + fieldCollection.Add(field); + } + } + fields = new ReadOnlyCollection<FieldDescriptor>(fieldCollection); + } + } +} diff --git a/csharp/src/ProtocolBuffers/Reflection/PackageDescriptor.cs b/csharp/src/ProtocolBuffers/Reflection/PackageDescriptor.cs new file mode 100644 index 00000000..e547d834 --- /dev/null +++ b/csharp/src/ProtocolBuffers/Reflection/PackageDescriptor.cs @@ -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; } + } + } +}
\ No newline at end of file diff --git a/csharp/src/ProtocolBuffers/Reflection/ReflectionUtil.cs b/csharp/src/ProtocolBuffers/Reflection/ReflectionUtil.cs new file mode 100644 index 00000000..d0dc3e8b --- /dev/null +++ b/csharp/src/ProtocolBuffers/Reflection/ReflectionUtil.cs @@ -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<object, object> CreateFuncObjectObject(MethodInfo method) + { + ParameterExpression parameter = Expression.Parameter(typeof(object), "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<object, 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<object, T> CreateFuncObjectT<T>(MethodInfo method) + { + ParameterExpression parameter = Expression.Parameter(typeof(object), "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<object, 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<object, object> CreateActionObjectObject(MethodInfo method) + { + ParameterExpression targetParameter = Expression.Parameter(typeof(object), "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<object, 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<object> CreateActionObject(MethodInfo method) + { + ParameterExpression targetParameter = Expression.Parameter(typeof(object), "target"); + Expression castTarget = Expression.Convert(targetParameter, method.DeclaringType); + Expression call = Expression.Call(castTarget, method); + return Expression.Lambda<Action<object>>(call, targetParameter).Compile(); + } + } +}
\ No newline at end of file diff --git a/csharp/src/ProtocolBuffers/Reflection/RepeatedFieldAccessor.cs b/csharp/src/ProtocolBuffers/Reflection/RepeatedFieldAccessor.cs new file mode 100644 index 00000000..0ada7567 --- /dev/null +++ b/csharp/src/ProtocolBuffers/Reflection/RepeatedFieldAccessor.cs @@ -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; + +namespace Google.Protobuf.Reflection +{ + /// <summary> + /// Accessor for repeated fields. + /// </summary> + internal sealed class RepeatedFieldAccessor : FieldAccessorBase + { + internal RepeatedFieldAccessor(Type type, string propertyName, FieldDescriptor descriptor) : base(type, propertyName, descriptor) + { + } + + public override void Clear(object message) + { + IList list = (IList) GetValue(message); + list.Clear(); + } + + public override void SetValue(object message, object value) + { + throw new InvalidOperationException("SetValue is not implemented for repeated fields"); + } + + } +} diff --git a/csharp/src/ProtocolBuffers/Reflection/ServiceDescriptor.cs b/csharp/src/ProtocolBuffers/Reflection/ServiceDescriptor.cs new file mode 100644 index 00000000..75925eb3 --- /dev/null +++ b/csharp/src/ProtocolBuffers/Reflection/ServiceDescriptor.cs @@ -0,0 +1,90 @@ +#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 Google.Protobuf.DescriptorProtos; + +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); + } + + internal void CrossLink() + { + foreach (MethodDescriptor method in methods) + { + method.CrossLink(); + } + } + } +}
\ No newline at end of file diff --git a/csharp/src/ProtocolBuffers/Reflection/SingleFieldAccessor.cs b/csharp/src/ProtocolBuffers/Reflection/SingleFieldAccessor.cs new file mode 100644 index 00000000..8c24e46e --- /dev/null +++ b/csharp/src/ProtocolBuffers/Reflection/SingleFieldAccessor.cs @@ -0,0 +1,85 @@ +#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<object, object> setValueDelegate; + private readonly Action<object> clearDelegate; + + internal SingleFieldAccessor(Type type, string propertyName, FieldDescriptor descriptor) : base(type, propertyName, descriptor) + { + PropertyInfo property = type.GetProperty(propertyName); + // We know there *is* such a property, or the base class constructor would have thrown. We should be able to write + // to it though. + if (!property.CanWrite) + { + throw new ArgumentException("Not all required properties/methods available"); + } + setValueDelegate = ReflectionUtil.CreateActionObjectObject(property.GetSetMethod()); + + var clrType = property.PropertyType; + + // TODO: What should clear on a oneof member do? Clear the oneof? + + // TODO: Validate that this is a reasonable single field? (Should be a value type, a message type, or string/ByteString.) + object defaultValue = + typeof(IMessage).IsAssignableFrom(clrType) ? null + : clrType == typeof(string) ? "" + : clrType == typeof(ByteString) ? ByteString.Empty + : Activator.CreateInstance(clrType); + clearDelegate = message => SetValue(message, defaultValue); + } + + public override void Clear(object message) + { + clearDelegate(message); + } + + public override void SetValue(object message, object value) + { + setValueDelegate(message, value); + } + } +} |