| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266 | using System.Collections.Generic;using System;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 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 + "\".");        }      }      // TODO(jonskeet): Check issue 25 wrt the ordering of these parameters      descriptorsByName[fullName] = new PackageDescriptor(fullName, name, 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]*$", RegexOptions.Compiled);    /// <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>    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;      }    }  }}
 |