diff options
author | Jan Tattermusch <jtattermusch@google.com> | 2015-04-16 09:55:22 -0700 |
---|---|---|
committer | Jan Tattermusch <jtattermusch@google.com> | 2015-04-16 09:55:22 -0700 |
commit | 84fa3e2bbc876428efd0c99700f146115a9d5162 (patch) | |
tree | 47baa5dafe3c3ab1c49fe2ca44e9f3ffd0e0b127 /csharp/src/ProtocolBuffers/Descriptors/DescriptorPool.cs | |
parent | 813d6d652d8091e9365bfbd01efb5f2ee8bdfbce (diff) | |
parent | 836a273544f55c88d8d0988ef0117cb0636707c6 (diff) | |
download | protobuf-84fa3e2bbc876428efd0c99700f146115a9d5162.tar.gz protobuf-84fa3e2bbc876428efd0c99700f146115a9d5162.tar.bz2 protobuf-84fa3e2bbc876428efd0c99700f146115a9d5162.zip |
Imported protobuf-csharp-port into csharp/ tree
Diffstat (limited to 'csharp/src/ProtocolBuffers/Descriptors/DescriptorPool.cs')
-rw-r--r-- | csharp/src/ProtocolBuffers/Descriptors/DescriptorPool.cs | 352 |
1 files changed, 352 insertions, 0 deletions
diff --git a/csharp/src/ProtocolBuffers/Descriptors/DescriptorPool.cs b/csharp/src/ProtocolBuffers/Descriptors/DescriptorPool.cs new file mode 100644 index 00000000..ae819801 --- /dev/null +++ b/csharp/src/ProtocolBuffers/Descriptors/DescriptorPool.cs @@ -0,0 +1,352 @@ +// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// http://github.com/jskeet/dotnet-protobufs/
+// Original C++/Java/Python code:
+// http://code.google.com/p/protobuf/
+//
+// 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.
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Text.RegularExpressions;
+
+namespace Google.ProtocolBuffers.Descriptors
+{
+ /// <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 DescriptorPool[] dependencies;
+
+ internal DescriptorPool(FileDescriptor[] dependencyFiles)
+ {
+ dependencies = new DescriptorPool[dependencyFiles.Length];
+ for (int i = 0; i < dependencyFiles.Length; i++)
+ {
+ dependencies[i] = dependencyFiles[i].DescriptorPool;
+ }
+
+ foreach (FileDescriptor dependency in dependencyFiles)
+ {
+ AddPackage(dependency.Package, 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
+ {
+ IDescriptor result;
+ descriptorsByName.TryGetValue(fullName, out result);
+ T descriptor = result as T;
+ if (descriptor != null)
+ {
+ return descriptor;
+ }
+
+ foreach (DescriptorPool dependency in dependencies)
+ {
+ dependency.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>
+ public 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 |