Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/HeuristicLab.ExtLibs/HeuristicLab.ProtobufCS/2.4.1/ProtobufCS/src/ProtocolBuffers/Descriptors/DescriptorPool.cs

Last change on this file was 8295, checked in by abeham, 12 years ago

#1897:

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