Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.ExtLibs/HeuristicLab.ProtobufCS/0.9.1/ProtobufCS/src/ProtocolBuffers/Descriptors/DescriptorPool.cs @ 4068

Last change on this file since 4068 was 4068, checked in by swagner, 14 years ago

Sorted usings and removed unused usings in entire solution (#1094)

File size: 12.0 KB
Line 
1using System;
2// Protocol Buffers - Google's data interchange format
3// Copyright 2008 Google Inc.  All rights reserved.
4// http://github.com/jskeet/dotnet-protobufs/
5// Original C++/Java/Python code:
6// http://code.google.com/p/protobuf/
7//
8// Redistribution and use in source and binary forms, with or without
9// modification, are permitted provided that the following conditions are
10// met:
11//
12//     * Redistributions of source code must retain the above copyright
13// notice, this list of conditions and the following disclaimer.
14//     * Redistributions in binary form must reproduce the above
15// copyright notice, this list of conditions and the following disclaimer
16// in the documentation and/or other materials provided with the
17// distribution.
18//     * Neither the name of Google Inc. nor the names of its
19// contributors may be used to endorse or promote products derived from
20// this software without specific prior written permission.
21//
22// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
28// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33using System.Collections.Generic;
34using System.Text;
35using System.Text.RegularExpressions;
36
37namespace Google.ProtocolBuffers.Descriptors {
38  /// <summary>
39  /// Contains lookup tables containing all the descriptors defined in a particular file.
40  /// </summary>
41  internal sealed class DescriptorPool {
42
43    private readonly IDictionary<string, IDescriptor> descriptorsByName =
44        new Dictionary<string, IDescriptor>();
45    private readonly IDictionary<DescriptorIntPair, FieldDescriptor> fieldsByNumber =
46        new Dictionary<DescriptorIntPair, FieldDescriptor>();
47    private readonly IDictionary<DescriptorIntPair, EnumValueDescriptor> enumValuesByNumber =
48        new Dictionary<DescriptorIntPair, EnumValueDescriptor>();
49    private readonly DescriptorPool[] dependencies;
50
51    internal DescriptorPool(FileDescriptor[] dependencyFiles) {
52      dependencies = new DescriptorPool[dependencyFiles.Length];
53      for (int i = 0; i < dependencyFiles.Length; i++) {
54        dependencies[i] = dependencyFiles[i].DescriptorPool;
55      }
56
57      foreach (FileDescriptor dependency in dependencyFiles) {
58        AddPackage(dependency.Package, dependency);
59      }
60    }
61
62    /// <summary>
63    /// Finds a symbol of the given name within the pool.
64    /// </summary>
65    /// <typeparam name="T">The type of symbol to look for</typeparam>
66    /// <param name="fullName">Fully-qualified name to look up</param>
67    /// <returns>The symbol with the given name and type,
68    /// or null if the symbol doesn't exist or has the wrong type</returns>
69    internal T FindSymbol<T>(string fullName) where T : class, IDescriptor {
70      IDescriptor result;
71      descriptorsByName.TryGetValue(fullName, out result);
72      T descriptor = result as T;
73      if (descriptor != null) {
74        return descriptor;
75      }
76
77      foreach (DescriptorPool dependency in dependencies) {
78        dependency.descriptorsByName.TryGetValue(fullName, out result);
79        descriptor = result as T;
80        if (descriptor != null) {
81          return descriptor;
82        }
83      }
84
85      return null;
86    }
87
88    /// <summary>
89    /// Adds a package to the symbol tables. If a package by the same name
90    /// already exists, that is fine, but if some other kind of symbol
91    /// exists under the same name, an exception is thrown. If the package
92    /// has multiple components, this also adds the parent package(s).
93    /// </summary>
94    internal void AddPackage(string fullName, FileDescriptor file) {
95      int dotpos = fullName.LastIndexOf('.');
96      String name;
97      if (dotpos != -1) {
98        AddPackage(fullName.Substring(0, dotpos), file);
99        name = fullName.Substring(dotpos + 1);
100      } else {
101        name = fullName;
102      }
103
104      IDescriptor old;
105      if (descriptorsByName.TryGetValue(fullName, out old)) {
106        if (!(old is PackageDescriptor)) {
107          throw new DescriptorValidationException(file,
108           "\"" + name + "\" is already defined (as something other than a " +
109            "package) in file \"" + old.File.Name + "\".");
110        }
111      }
112      descriptorsByName[fullName] = new PackageDescriptor(name, fullName, file);
113    }
114
115    /// <summary>
116    /// Adds a symbol to the symbol table.
117    /// </summary>
118    /// <exception cref="DescriptorValidationException">The symbol already existed
119    /// in the symbol table.</exception>
120    internal void AddSymbol(IDescriptor descriptor) {
121      ValidateSymbolName(descriptor);
122      String fullName = descriptor.FullName;
123
124      IDescriptor old;
125      if (descriptorsByName.TryGetValue(fullName, out old)) {
126        int dotPos = fullName.LastIndexOf('.');
127        string message;
128        if (descriptor.File == old.File) {
129          if (dotPos == -1) {
130            message = "\"" + fullName + "\" is already defined.";
131          } else {
132            message = "\"" + fullName.Substring(dotPos + 1) + "\" is already defined in \"" + fullName.Substring(0, dotPos) + "\".";
133          }
134        } else {
135          message = "\"" + fullName + "\" is already defined in file \"" + old.File.Name + "\".";
136        }
137        throw new DescriptorValidationException(descriptor, message);
138      }
139      descriptorsByName[fullName] = descriptor;
140    }
141
142    private static readonly Regex ValidationRegex = new Regex("^[_A-Za-z][_A-Za-z0-9]*$", SilverlightCompatibility.CompiledRegexWhereAvailable);
143
144    /// <summary>
145    /// Verifies that the descriptor's name is valid (i.e. it contains
146    /// only letters, digits and underscores, and does not start with a digit).
147    /// </summary>
148    /// <param name="descriptor"></param>
149    private static void ValidateSymbolName(IDescriptor descriptor) {
150      if (descriptor.Name == "") {
151        throw new DescriptorValidationException(descriptor, "Missing name.");
152      }
153      if (!ValidationRegex.IsMatch(descriptor.Name)) {
154        throw new DescriptorValidationException(descriptor,
155            "\"" + descriptor.Name + "\" is not a valid identifier.");
156      }
157    }
158
159    /// <summary>
160    /// Returns the field with the given number in the given descriptor,
161    /// or null if it can't be found.
162    /// </summary>
163    internal FieldDescriptor FindFieldByNumber(MessageDescriptor messageDescriptor, int number) {
164      FieldDescriptor ret;
165      fieldsByNumber.TryGetValue(new DescriptorIntPair(messageDescriptor, number), out ret);
166      return ret;
167    }
168
169    internal EnumValueDescriptor FindEnumValueByNumber(EnumDescriptor enumDescriptor, int number) {
170      EnumValueDescriptor ret;
171      enumValuesByNumber.TryGetValue(new DescriptorIntPair(enumDescriptor, number), out ret);
172      return ret;
173    }
174
175    /// <summary>
176    /// Adds a field to the fieldsByNumber table.
177    /// </summary>
178    /// <exception cref="DescriptorValidationException">A field with the same
179    /// containing type and number already exists.</exception>
180    internal void AddFieldByNumber(FieldDescriptor field) {
181      DescriptorIntPair key = new DescriptorIntPair(field.ContainingType, field.FieldNumber);
182      FieldDescriptor old;
183      if (fieldsByNumber.TryGetValue(key, out old)) {
184        throw new DescriptorValidationException(field, "Field number " + field.FieldNumber +
185          "has already been used in \"" + field.ContainingType.FullName +
186          "\" by field \"" + old.Name + "\".");
187      }
188      fieldsByNumber[key] = field;
189    }
190
191    /// <summary>
192    /// Adds an enum value to the enumValuesByNumber table. If an enum value
193    /// with the same type and number already exists, this method does nothing.
194    /// (This is allowed; the first value defined with the number takes precedence.)
195    /// </summary>
196    internal void AddEnumValueByNumber(EnumValueDescriptor enumValue) {
197      DescriptorIntPair key = new DescriptorIntPair(enumValue.EnumDescriptor, enumValue.Number);
198      if (!enumValuesByNumber.ContainsKey(key)) {
199        enumValuesByNumber[key] = enumValue;
200      }
201    }
202
203    /// <summary>
204    /// Looks up a descriptor by name, relative to some other descriptor.
205    /// The name may be fully-qualified (with a leading '.'), partially-qualified,
206    /// or unqualified. C++-like name lookup semantics are used to search for the
207    /// matching descriptor.
208    /// </summary>
209    public IDescriptor LookupSymbol(string name, IDescriptor relativeTo) {
210      // TODO(jonskeet):  This could be optimized in a number of ways.
211
212      IDescriptor result;
213      if (name.StartsWith(".")) {
214        // Fully-qualified name.
215        result = FindSymbol<IDescriptor>(name.Substring(1));
216      } else {
217        // If "name" is a compound identifier, we want to search for the
218        // first component of it, then search within it for the rest.
219        int firstPartLength = name.IndexOf('.');
220        string firstPart = firstPartLength == -1 ? name : name.Substring(0, firstPartLength);
221
222        // We will search each parent scope of "relativeTo" looking for the
223        // symbol.
224        StringBuilder scopeToTry = new StringBuilder(relativeTo.FullName);
225
226        while (true) {
227          // Chop off the last component of the scope.
228
229          // TODO(jonskeet): Make this more efficient. May not be worth using StringBuilder at all
230          int dotpos = scopeToTry.ToString().LastIndexOf(".");
231          if (dotpos == -1) {
232            result = FindSymbol<IDescriptor>(name);
233            break;
234          } else {
235            scopeToTry.Length = dotpos + 1;
236
237            // Append firstPart and try to find.
238            scopeToTry.Append(firstPart);
239            result = FindSymbol<IDescriptor>(scopeToTry.ToString());
240
241            if (result != null) {
242              if (firstPartLength != -1) {
243                // We only found the first part of the symbol.  Now look for
244                // the whole thing.  If this fails, we *don't* want to keep
245                // searching parent scopes.
246                scopeToTry.Length = dotpos + 1;
247                scopeToTry.Append(name);
248                result = FindSymbol<IDescriptor>(scopeToTry.ToString());
249              }
250              break;
251            }
252
253            // Not found.  Remove the name so we can try again.
254            scopeToTry.Length = dotpos;
255          }
256        }
257      }
258
259      if (result == null) {
260        throw new DescriptorValidationException(relativeTo, "\"" + name + "\" is not defined.");
261      } else {
262        return result;
263      }
264    }
265
266    /// <summary>
267    /// Struct used to hold the keys for the fieldByNumber table.
268    /// </summary>
269    struct DescriptorIntPair : IEquatable<DescriptorIntPair> {
270
271      private readonly int number;
272      private readonly IDescriptor descriptor;
273
274      internal DescriptorIntPair(IDescriptor descriptor, int number) {
275        this.number = number;
276        this.descriptor = descriptor;
277      }
278
279      public bool Equals(DescriptorIntPair other) {
280        return descriptor == other.descriptor
281            && number == other.number;
282      }
283
284      public override bool Equals(object obj) {
285        if (obj is DescriptorIntPair) {
286          return Equals((DescriptorIntPair)obj);
287        }
288        return false;
289      }
290
291      public override int GetHashCode() {
292        return descriptor.GetHashCode() * ((1 << 16) - 1) + number;
293      }
294    }
295  }
296}
Note: See TracBrowser for help on using the repository browser.