Free cookie consent management tool by TermsFeed Policy Generator

source: branches/RemoveBackwardsCompatibility/HeuristicLab.ExtLibs/HeuristicLab.ProtobufCS/2.4.1/ProtobufCS/src/ProtocolBuffers/Descriptors/FileDescriptor.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: 20.6 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.Collections.ObjectModel;
35using System.IO;
36using Google.ProtocolBuffers.DescriptorProtos;
37using FileOptions = Google.ProtocolBuffers.DescriptorProtos.FileOptions;
38
39namespace Google.ProtocolBuffers.Descriptors
40{
41    /// <summary>
42    /// Describes a .proto file, including everything defined within.
43    /// IDescriptor is implemented such that the File property returns this descriptor,
44    /// and the FullName is the same as the Name.
45    /// </summary>
46    public sealed class FileDescriptor : IDescriptor<FileDescriptorProto>
47    {
48        private FileDescriptorProto proto;
49        private readonly IList<MessageDescriptor> messageTypes;
50        private readonly IList<EnumDescriptor> enumTypes;
51        private readonly IList<ServiceDescriptor> services;
52        private readonly IList<FieldDescriptor> extensions;
53        private readonly IList<FileDescriptor> dependencies;
54        private readonly DescriptorPool pool;
55        private CSharpFileOptions csharpFileOptions;
56        private readonly object optionsLock = new object();
57
58        private FileDescriptor(FileDescriptorProto proto, FileDescriptor[] dependencies, DescriptorPool pool)
59        {
60            this.pool = pool;
61            this.proto = proto;
62            this.dependencies = new ReadOnlyCollection<FileDescriptor>((FileDescriptor[]) dependencies.Clone());
63
64            pool.AddPackage(Package, this);
65
66            messageTypes = DescriptorUtil.ConvertAndMakeReadOnly(proto.MessageTypeList,
67                                                                 (message, index) =>
68                                                                 new MessageDescriptor(message, this, null, index));
69
70            enumTypes = DescriptorUtil.ConvertAndMakeReadOnly(proto.EnumTypeList,
71                                                              (enumType, index) =>
72                                                              new EnumDescriptor(enumType, this, null, index));
73
74            services = DescriptorUtil.ConvertAndMakeReadOnly(proto.ServiceList,
75                                                             (service, index) =>
76                                                             new ServiceDescriptor(service, this, index));
77
78            extensions = DescriptorUtil.ConvertAndMakeReadOnly(proto.ExtensionList,
79                                                               (field, index) =>
80                                                               new FieldDescriptor(field, this, null, index, true));
81        }
82
83
84        /// <summary>
85        /// Allows a file descriptor to be configured with a set of external options, e.g. from the
86        /// command-line arguments to protogen.
87        /// </summary>
88        public void ConfigureWithDefaultOptions(CSharpFileOptions options)
89        {
90            csharpFileOptions = BuildOrFakeWithDefaultOptions(options);
91        }
92
93        private CSharpFileOptions BuildOrFakeWithDefaultOptions(CSharpFileOptions defaultOptions)
94        {
95            // Fix for being able to relocate these files to any directory structure
96            if (proto.Package == "google.protobuf")
97            {
98                string filename = Path.GetFileName(proto.Name);
99                // TODO(jonskeet): Check if we could use FileDescriptorProto.Descriptor.Name - interesting bootstrap issues)
100                if (filename == "descriptor.proto")
101                {
102                    return new CSharpFileOptions.Builder
103                               {
104                                   Namespace = "Google.ProtocolBuffers.DescriptorProtos",
105                                   UmbrellaClassname = "DescriptorProtoFile",
106                                   NestClasses = false,
107                                   MultipleFiles = false,
108                                   PublicClasses = true,
109                                   OutputDirectory = defaultOptions.OutputDirectory,
110                                   IgnoreGoogleProtobuf = defaultOptions.IgnoreGoogleProtobuf
111                               }.Build();
112                }
113                if (filename == "csharp_options.proto")
114                {
115                    return new CSharpFileOptions.Builder
116                               {
117                                   Namespace = "Google.ProtocolBuffers.DescriptorProtos",
118                                   UmbrellaClassname = "CSharpOptions",
119                                   NestClasses = false,
120                                   MultipleFiles = false,
121                                   PublicClasses = true,
122                                   OutputDirectory = defaultOptions.OutputDirectory,
123                                   IgnoreGoogleProtobuf = defaultOptions.IgnoreGoogleProtobuf
124                               }.Build();
125                }
126            }
127            CSharpFileOptions.Builder builder = defaultOptions.ToBuilder();
128            if (proto.Options.HasExtension(DescriptorProtos.CSharpOptions.CSharpFileOptions))
129            {
130                builder.MergeFrom(proto.Options.GetExtension(DescriptorProtos.CSharpOptions.CSharpFileOptions));
131            }
132            if (!builder.HasNamespace)
133            {
134                builder.Namespace = Package;
135            }
136            if (!builder.HasUmbrellaClassname)
137            {
138                int lastSlash = Name.LastIndexOf('/');
139                string baseName = Name.Substring(lastSlash + 1);
140                builder.UmbrellaClassname = NameHelpers.UnderscoresToPascalCase(NameHelpers.StripProto(baseName));
141            }
142
143            // Auto-fix for name collision by placing umbrella class into a new namespace.  This
144            // still won't fix the collisions with nesting enabled; however, you have to turn that on explicitly anyway.
145            if (!builder.NestClasses && !builder.HasUmbrellaNamespace)
146            {
147                bool collision = false;
148                foreach (IDescriptor d in MessageTypes)
149                {
150                    collision |= d.Name == builder.UmbrellaClassname;
151                }
152                foreach (IDescriptor d in Services)
153                {
154                    collision |= d.Name == builder.UmbrellaClassname;
155                }
156                foreach (IDescriptor d in EnumTypes)
157                {
158                    collision |= d.Name == builder.UmbrellaClassname;
159                }
160                if (collision)
161                {
162                    builder.UmbrellaNamespace = "Proto";
163                }
164            }
165
166            return builder.Build();
167        }
168
169        /// <value>
170        /// The descriptor in its protocol message representation.
171        /// </value>
172        public FileDescriptorProto Proto
173        {
174            get { return proto; }
175        }
176
177        /// <value>
178        /// The <see cref="DescriptorProtos.FileOptions" /> defined in <c>descriptor.proto</c>.
179        /// </value>
180        public FileOptions Options
181        {
182            get { return proto.Options; }
183        }
184
185        /// <summary>
186        /// Returns the C#-specific options for this file descriptor. This will always be
187        /// completely filled in.
188        /// </summary>
189        public CSharpFileOptions CSharpOptions
190        {
191            get
192            {
193                lock (optionsLock)
194                {
195                    if (csharpFileOptions == null)
196                    {
197                        csharpFileOptions = BuildOrFakeWithDefaultOptions(CSharpFileOptions.DefaultInstance);
198                    }
199                }
200                return csharpFileOptions;
201            }
202        }
203
204        /// <value>
205        /// The file name.
206        /// </value>
207        public string Name
208        {
209            get { return proto.Name; }
210        }
211
212        /// <summary>
213        /// The package as declared in the .proto file. This may or may not
214        /// be equivalent to the .NET namespace of the generated classes.
215        /// </summary>
216        public string Package
217        {
218            get { return proto.Package; }
219        }
220
221        /// <value>
222        /// Unmodifiable list of top-level message types declared in this file.
223        /// </value>
224        public IList<MessageDescriptor> MessageTypes
225        {
226            get { return messageTypes; }
227        }
228
229        /// <value>
230        /// Unmodifiable list of top-level enum types declared in this file.
231        /// </value>
232        public IList<EnumDescriptor> EnumTypes
233        {
234            get { return enumTypes; }
235        }
236
237        /// <value>
238        /// Unmodifiable list of top-level services declared in this file.
239        /// </value>
240        public IList<ServiceDescriptor> Services
241        {
242            get { return services; }
243        }
244
245        /// <value>
246        /// Unmodifiable list of top-level extensions declared in this file.
247        /// </value>
248        public IList<FieldDescriptor> Extensions
249        {
250            get { return extensions; }
251        }
252
253        /// <value>
254        /// Unmodifiable list of this file's dependencies (imports).
255        /// </value>
256        public IList<FileDescriptor> Dependencies
257        {
258            get { return dependencies; }
259        }
260
261        /// <value>
262        /// Implementation of IDescriptor.FullName - just returns the same as Name.
263        /// </value>
264        string IDescriptor.FullName
265        {
266            get { return Name; }
267        }
268
269        /// <value>
270        /// Implementation of IDescriptor.File - just returns this descriptor.
271        /// </value>
272        FileDescriptor IDescriptor.File
273        {
274            get { return this; }
275        }
276
277        /// <value>
278        /// Protocol buffer describing this descriptor.
279        /// </value>
280        IMessage IDescriptor.Proto
281        {
282            get { return Proto; }
283        }
284
285        /// <value>
286        /// Pool containing symbol descriptors.
287        /// </value>
288        internal DescriptorPool DescriptorPool
289        {
290            get { return pool; }
291        }
292
293        /// <summary>
294        /// Finds a type (message, enum, service or extension) in the file by name. Does not find nested types.
295        /// </summary>
296        /// <param name="name">The unqualified type name to look for.</param>
297        /// <typeparam name="T">The type of descriptor to look for (or ITypeDescriptor for any)</typeparam>
298        /// <returns>The type's descriptor, or null if not found.</returns>
299        public T FindTypeByName<T>(String name)
300            where T : class, IDescriptor
301        {
302            // Don't allow looking up nested types.  This will make optimization
303            // easier later.
304            if (name.IndexOf('.') != -1)
305            {
306                return null;
307            }
308            if (Package.Length > 0)
309            {
310                name = Package + "." + name;
311            }
312            T result = pool.FindSymbol<T>(name);
313            if (result != null && result.File == this)
314            {
315                return result;
316            }
317            return null;
318        }
319
320        /// <summary>
321        /// Builds a FileDescriptor from its protocol buffer representation.
322        /// </summary>
323        /// <param name="proto">The protocol message form of the FileDescriptor.</param>
324        /// <param name="dependencies">FileDescriptors corresponding to all of the
325        /// file's dependencies, in the exact order listed in the .proto file. May be null,
326        /// in which case it is treated as an empty array.</param>
327        /// <exception cref="DescriptorValidationException">If <paramref name="proto"/> is not
328        /// a valid descriptor. This can occur for a number of reasons, such as a field
329        /// having an undefined type or because two messages were defined with the same name.</exception>
330        public static FileDescriptor BuildFrom(FileDescriptorProto proto, FileDescriptor[] dependencies)
331        {
332            // Building descriptors involves two steps: translating and linking.
333            // In the translation step (implemented by FileDescriptor's
334            // constructor), we build an object tree mirroring the
335            // FileDescriptorProto's tree and put all of the descriptors into the
336            // DescriptorPool's lookup tables.  In the linking step, we look up all
337            // type references in the DescriptorPool, so that, for example, a
338            // FieldDescriptor for an embedded message contains a pointer directly
339            // to the Descriptor for that message's type.  We also detect undefined
340            // types in the linking step.
341            if (dependencies == null)
342            {
343                dependencies = new FileDescriptor[0];
344            }
345
346            DescriptorPool pool = new DescriptorPool(dependencies);
347            FileDescriptor result = new FileDescriptor(proto, dependencies, pool);
348
349            if (dependencies.Length != proto.DependencyCount)
350            {
351                throw new DescriptorValidationException(result,
352                                                        "Dependencies passed to FileDescriptor.BuildFrom() don't match " +
353                                                        "those listed in the FileDescriptorProto.");
354            }
355            for (int i = 0; i < proto.DependencyCount; i++)
356            {
357                if (dependencies[i].Name != proto.DependencyList[i])
358                {
359                    throw new DescriptorValidationException(result,
360                                                            "Dependencies passed to FileDescriptor.BuildFrom() don't match " +
361                                                            "those listed in the FileDescriptorProto.");
362                }
363            }
364
365            result.CrossLink();
366            return result;
367        }
368
369        private void CrossLink()
370        {
371            foreach (MessageDescriptor message in messageTypes)
372            {
373                message.CrossLink();
374            }
375
376            foreach (ServiceDescriptor service in services)
377            {
378                service.CrossLink();
379            }
380
381            foreach (FieldDescriptor extension in extensions)
382            {
383                extension.CrossLink();
384            }
385
386            foreach (MessageDescriptor message in messageTypes)
387            {
388                message.CheckRequiredFields();
389            }
390        }
391
392        /// <summary>
393        /// This method is to be called by generated code only.  It is equivalent
394        /// to BuildFrom except that the FileDescriptorProto is encoded in
395        /// protocol buffer wire format. This overload is maintained for backward
396        /// compatibility with source code generated before the custom options were available
397        /// (and working).
398        /// </summary>
399        public static FileDescriptor InternalBuildGeneratedFileFrom(byte[] descriptorData, FileDescriptor[] dependencies)
400        {
401            return InternalBuildGeneratedFileFrom(descriptorData, dependencies, x => null);
402        }
403
404        /// <summary>
405        /// This delegate should be used by generated code only. When calling
406        /// FileDescriptor.InternalBuildGeneratedFileFrom, the caller can provide
407        /// a callback which assigns the global variables defined in the generated code
408        /// which point at parts of the FileDescriptor. The callback returns an
409        /// Extension Registry which contains any extensions which might be used in
410        /// the descriptor - that is, extensions of the various "Options" messages defined
411        /// in descriptor.proto. The callback may also return null to indicate that
412        /// no extensions are used in the descriptor.
413        /// </summary>
414        /// <param name="descriptor"></param>
415        /// <returns></returns>
416        public delegate ExtensionRegistry InternalDescriptorAssigner(FileDescriptor descriptor);
417
418        public static FileDescriptor InternalBuildGeneratedFileFrom(byte[] descriptorData,
419                                                                    FileDescriptor[] dependencies,
420                                                                    InternalDescriptorAssigner descriptorAssigner)
421        {
422            FileDescriptorProto proto;
423            try
424            {
425                proto = FileDescriptorProto.ParseFrom(descriptorData);
426            }
427            catch (InvalidProtocolBufferException e)
428            {
429                throw new ArgumentException("Failed to parse protocol buffer descriptor for generated code.", e);
430            }
431
432            FileDescriptor result;
433            try
434            {
435                result = BuildFrom(proto, dependencies);
436            }
437            catch (DescriptorValidationException e)
438            {
439                throw new ArgumentException("Invalid embedded descriptor for \"" + proto.Name + "\".", e);
440            }
441
442            ExtensionRegistry registry = descriptorAssigner(result);
443
444            if (registry != null)
445            {
446                // We must re-parse the proto using the registry.
447                try
448                {
449                    proto = FileDescriptorProto.ParseFrom(descriptorData, registry);
450                }
451                catch (InvalidProtocolBufferException e)
452                {
453                    throw new ArgumentException("Failed to parse protocol buffer descriptor for generated code.", e);
454                }
455
456                result.ReplaceProto(proto);
457            }
458            return result;
459        }
460
461        /// <summary>
462        /// Replace our FileDescriptorProto with the given one, which is
463        /// identical except that it might contain extensions that weren't present
464        /// in the original. This method is needed for bootstrapping when a file
465        /// defines custom options. The options may be defined in the file itself,
466        /// so we can't actually parse them until we've constructed the descriptors,
467        /// but to construct the decsriptors we have to have parsed the descriptor
468        /// protos. So, we have to parse the descriptor protos a second time after
469        /// constructing the descriptors.
470        /// </summary>
471        private void ReplaceProto(FileDescriptorProto newProto)
472        {
473            proto = newProto;
474
475            for (int i = 0; i < messageTypes.Count; i++)
476            {
477                messageTypes[i].ReplaceProto(proto.GetMessageType(i));
478            }
479
480            for (int i = 0; i < enumTypes.Count; i++)
481            {
482                enumTypes[i].ReplaceProto(proto.GetEnumType(i));
483            }
484
485            for (int i = 0; i < services.Count; i++)
486            {
487                services[i].ReplaceProto(proto.GetService(i));
488            }
489
490            for (int i = 0; i < extensions.Count; i++)
491            {
492                extensions[i].ReplaceProto(proto.GetExtension(i));
493            }
494        }
495    }
496}
Note: See TracBrowser for help on using the repository browser.