Free cookie consent management tool by TermsFeed Policy Generator

source: branches/WebJobManager/HeuristicLab.ExtLibs/HeuristicLab.ProtobufCS/2.4.1/ProtobufCS/src/ProtocolBuffers/Descriptors/FieldDescriptor.cs

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

#1897:

  • Removed protocol buffers 0.9.1
  • Added protocol buffers 2.4.1
  • Updated proto processing command
File size: 27.0 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.Reflection;
35using Google.ProtocolBuffers.Collections;
36using Google.ProtocolBuffers.DescriptorProtos;
37
38namespace Google.ProtocolBuffers.Descriptors
39{
40    /// <summary>
41    /// Descriptor for a field or extension within a message in a .proto file.
42    /// </summary>
43    public sealed class FieldDescriptor : IndexedDescriptorBase<FieldDescriptorProto, FieldOptions>,
44                                          IComparable<FieldDescriptor>, IFieldDescriptorLite
45    {
46        private readonly MessageDescriptor extensionScope;
47        private EnumDescriptor enumType;
48        private MessageDescriptor messageType;
49        private MessageDescriptor containingType;
50        private object defaultValue;
51        private FieldType fieldType;
52        private MappedType mappedType;
53
54        private CSharpFieldOptions csharpFieldOptions;
55        private readonly object optionsLock = new object();
56
57        internal FieldDescriptor(FieldDescriptorProto proto, FileDescriptor file,
58                                 MessageDescriptor parent, int index, bool isExtension)
59            : base(proto, file, ComputeFullName(file, parent, proto.Name), index)
60        {
61            if (proto.HasType)
62            {
63                fieldType = GetFieldTypeFromProtoType(proto.Type);
64                mappedType = FieldTypeToMappedTypeMap[fieldType];
65            }
66
67            if (FieldNumber <= 0)
68            {
69                throw new DescriptorValidationException(this,
70                                                        "Field numbers must be positive integers.");
71            }
72
73            if (isExtension)
74            {
75                if (!proto.HasExtendee)
76                {
77                    throw new DescriptorValidationException(this,
78                                                            "FieldDescriptorProto.Extendee not set for extension field.");
79                }
80                containingType = null; // Will be filled in when cross-linking
81                if (parent != null)
82                {
83                    extensionScope = parent;
84                }
85                else
86                {
87                    extensionScope = null;
88                }
89            }
90            else
91            {
92                if (proto.HasExtendee)
93                {
94                    throw new DescriptorValidationException(this,
95                                                            "FieldDescriptorProto.Extendee set for non-extension field.");
96                }
97                containingType = parent;
98                extensionScope = null;
99            }
100
101            file.DescriptorPool.AddSymbol(this);
102        }
103
104        private CSharpFieldOptions BuildOrFakeCSharpOptions()
105        {
106            // TODO(jonskeet): Check if we could use FileDescriptorProto.Descriptor.Name - interesting bootstrap issues
107            if (File.Proto.Name == "google/protobuf/csharp_options.proto")
108            {
109                if (Name == "csharp_field_options")
110                {
111                    return new CSharpFieldOptions.Builder {PropertyName = "CSharpFieldOptions"}.Build();
112                }
113                if (Name == "csharp_file_options")
114                {
115                    return new CSharpFieldOptions.Builder {PropertyName = "CSharpFileOptions"}.Build();
116                }
117            }
118            CSharpFieldOptions.Builder builder = CSharpFieldOptions.CreateBuilder();
119            if (Proto.Options.HasExtension(DescriptorProtos.CSharpOptions.CSharpFieldOptions))
120            {
121                builder.MergeFrom(Proto.Options.GetExtension(DescriptorProtos.CSharpOptions.CSharpFieldOptions));
122            }
123            if (!builder.HasPropertyName)
124            {
125                string fieldName = FieldType == FieldType.Group ? MessageType.Name : Name;
126                string propertyName = NameHelpers.UnderscoresToPascalCase(fieldName);
127                if (propertyName == ContainingType.Name)
128                {
129                    propertyName += "_";
130                }
131                builder.PropertyName = propertyName;
132            }
133            return builder.Build();
134        }
135
136        /// <summary>
137        /// Maps a field type as included in the .proto file to a FieldType.
138        /// </summary>
139        private static FieldType GetFieldTypeFromProtoType(FieldDescriptorProto.Types.Type type)
140        {
141            switch (type)
142            {
143                case FieldDescriptorProto.Types.Type.TYPE_DOUBLE:
144                    return FieldType.Double;
145                case FieldDescriptorProto.Types.Type.TYPE_FLOAT:
146                    return FieldType.Float;
147                case FieldDescriptorProto.Types.Type.TYPE_INT64:
148                    return FieldType.Int64;
149                case FieldDescriptorProto.Types.Type.TYPE_UINT64:
150                    return FieldType.UInt64;
151                case FieldDescriptorProto.Types.Type.TYPE_INT32:
152                    return FieldType.Int32;
153                case FieldDescriptorProto.Types.Type.TYPE_FIXED64:
154                    return FieldType.Fixed64;
155                case FieldDescriptorProto.Types.Type.TYPE_FIXED32:
156                    return FieldType.Fixed32;
157                case FieldDescriptorProto.Types.Type.TYPE_BOOL:
158                    return FieldType.Bool;
159                case FieldDescriptorProto.Types.Type.TYPE_STRING:
160                    return FieldType.String;
161                case FieldDescriptorProto.Types.Type.TYPE_GROUP:
162                    return FieldType.Group;
163                case FieldDescriptorProto.Types.Type.TYPE_MESSAGE:
164                    return FieldType.Message;
165                case FieldDescriptorProto.Types.Type.TYPE_BYTES:
166                    return FieldType.Bytes;
167                case FieldDescriptorProto.Types.Type.TYPE_UINT32:
168                    return FieldType.UInt32;
169                case FieldDescriptorProto.Types.Type.TYPE_ENUM:
170                    return FieldType.Enum;
171                case FieldDescriptorProto.Types.Type.TYPE_SFIXED32:
172                    return FieldType.SFixed32;
173                case FieldDescriptorProto.Types.Type.TYPE_SFIXED64:
174                    return FieldType.SFixed64;
175                case FieldDescriptorProto.Types.Type.TYPE_SINT32:
176                    return FieldType.SInt32;
177                case FieldDescriptorProto.Types.Type.TYPE_SINT64:
178                    return FieldType.SInt64;
179                default:
180                    throw new ArgumentException("Invalid type specified");
181            }
182        }
183
184        /// <summary>
185        /// Returns the default value for a mapped type.
186        /// </summary>
187        private static object GetDefaultValueForMappedType(MappedType type)
188        {
189            switch (type)
190            {
191                case MappedType.Int32:
192                    return 0;
193                case MappedType.Int64:
194                    return (long) 0;
195                case MappedType.UInt32:
196                    return (uint) 0;
197                case MappedType.UInt64:
198                    return (ulong) 0;
199                case MappedType.Single:
200                    return (float) 0;
201                case MappedType.Double:
202                    return (double) 0;
203                case MappedType.Boolean:
204                    return false;
205                case MappedType.String:
206                    return "";
207                case MappedType.ByteString:
208                    return ByteString.Empty;
209                case MappedType.Message:
210                    return null;
211                case MappedType.Enum:
212                    return null;
213                default:
214                    throw new ArgumentException("Invalid type specified");
215            }
216        }
217
218        public bool IsRequired
219        {
220            get { return Proto.Label == FieldDescriptorProto.Types.Label.LABEL_REQUIRED; }
221        }
222
223        public bool IsOptional
224        {
225            get { return Proto.Label == FieldDescriptorProto.Types.Label.LABEL_OPTIONAL; }
226        }
227
228        public bool IsRepeated
229        {
230            get { return Proto.Label == FieldDescriptorProto.Types.Label.LABEL_REPEATED; }
231        }
232
233        public bool IsPacked
234        {
235            get { return Proto.Options.Packed; }
236        }
237
238        /// <valule>
239        /// Indicates whether or not the field had an explicitly-defined default value.
240        /// </value>
241        public bool HasDefaultValue
242        {
243            get { return Proto.HasDefaultValue; }
244        }
245
246        /// <value>
247        /// The field's default value. Valid for all types except messages
248        /// and groups. For all other types, the object returned is of the
249        /// same class that would be returned by IMessage[this].
250        /// For repeated fields this will always be an empty immutable list compatible with IList[object].
251        /// For message fields it will always be null. For singular values, it will depend on the descriptor.
252        /// </value>
253        public object DefaultValue
254        {
255            get
256            {
257                if (MappedType == MappedType.Message)
258                {
259                    throw new InvalidOperationException(
260                        "FieldDescriptor.DefaultValue called on an embedded message field.");
261                }
262                return defaultValue;
263            }
264        }
265
266        /// <value>
267        /// Indicates whether or not this field is an extension.
268        /// </value>
269        public bool IsExtension
270        {
271            get { return Proto.HasExtendee; }
272        }
273
274        /*
275     * Get the field's containing type. For extensions, this is the type being
276     * extended, not the location where the extension was defined.  See
277     * {@link #getExtensionScope()}.
278     */
279
280        /// <summary>
281        /// Get the field's containing type. For extensions, this is the type being
282        /// extended, not the location where the extension was defined. See
283        /// <see cref="ExtensionScope" />.
284        /// </summary>
285        public MessageDescriptor ContainingType
286        {
287            get { return containingType; }
288        }
289
290        /// <summary>
291        /// Returns the C#-specific options for this field descriptor. This will always be
292        /// completely filled in.
293        /// </summary>
294        public CSharpFieldOptions CSharpOptions
295        {
296            get
297            {
298                lock (optionsLock)
299                {
300                    if (csharpFieldOptions == null)
301                    {
302                        csharpFieldOptions = BuildOrFakeCSharpOptions();
303                    }
304                }
305                return csharpFieldOptions;
306            }
307        }
308
309        /// <summary>
310        /// For extensions defined nested within message types, gets
311        /// the outer type. Not valid for non-extension fields.
312        /// </summary>
313        /// <example>
314        /// <code>
315        /// message Foo {
316        ///   extensions 1000 to max;
317        /// }
318        /// extend Foo {
319        ///   optional int32 baz = 1234;
320        /// }
321        /// message Bar {
322        ///   extend Foo {
323        ///     optional int32 qux = 4321;
324        ///   }
325        /// }
326        /// </code>
327        /// The containing type for both <c>baz</c> and <c>qux</c> is <c>Foo</c>.
328        /// However, the extension scope for <c>baz</c> is <c>null</c> while
329        /// the extension scope for <c>qux</c> is <c>Bar</c>.
330        /// </example>
331        public MessageDescriptor ExtensionScope
332        {
333            get
334            {
335                if (!IsExtension)
336                {
337                    throw new InvalidOperationException("This field is not an extension.");
338                }
339                return extensionScope;
340            }
341        }
342
343        public MappedType MappedType
344        {
345            get { return mappedType; }
346        }
347
348        public FieldType FieldType
349        {
350            get { return fieldType; }
351        }
352
353        public bool IsCLSCompliant
354        {
355            get
356            {
357                return mappedType != MappedType.UInt32 &&
358                    mappedType != MappedType.UInt64 &&
359                    !NameHelpers.UnderscoresToPascalCase(Name).StartsWith("_");
360            }
361        }
362
363        public int FieldNumber
364        {
365            get { return Proto.Number; }
366        }
367
368        /// <summary>
369        /// Compares this descriptor with another one, ordering in "canonical" order
370        /// which simply means ascending order by field number. <paramref name="other"/>
371        /// must be a field of the same type, i.e. the <see cref="ContainingType"/> of
372        /// both fields must be the same.
373        /// </summary>
374        public int CompareTo(FieldDescriptor other)
375        {
376            if (other.containingType != containingType)
377            {
378                throw new ArgumentException("FieldDescriptors can only be compared to other FieldDescriptors " +
379                                            "for fields of the same message type.");
380            }
381            return FieldNumber - other.FieldNumber;
382        }
383
384        /// <summary>
385        /// Compares this descriptor with another one, ordering in "canonical" order
386        /// which simply means ascending order by field number. <paramref name="other"/>
387        /// must be a field of the same type, i.e. the <see cref="ContainingType"/> of
388        /// both fields must be the same.
389        /// </summary>
390        public int CompareTo(IFieldDescriptorLite other)
391        {
392            return FieldNumber - other.FieldNumber;
393        }
394
395        IEnumLiteMap IFieldDescriptorLite.EnumType
396        {
397            get { return EnumType; }
398        }
399
400        bool IFieldDescriptorLite.MessageSetWireFormat
401        {
402            get { return ContainingType.Options.MessageSetWireFormat; }
403        }
404
405        /// <summary>
406        /// For enum fields, returns the field's type.
407        /// </summary>
408        public EnumDescriptor EnumType
409        {
410            get
411            {
412                if (MappedType != MappedType.Enum)
413                {
414                    throw new InvalidOperationException("EnumType is only valid for enum fields.");
415                }
416                return enumType;
417            }
418        }
419
420        /// <summary>
421        /// For embedded message and group fields, returns the field's type.
422        /// </summary>
423        public MessageDescriptor MessageType
424        {
425            get
426            {
427                if (MappedType != MappedType.Message)
428                {
429                    throw new InvalidOperationException("MessageType is only valid for enum fields.");
430                }
431                return messageType;
432            }
433        }
434
435        /// <summary>
436        /// Immutable mapping from field type to mapped type. Built using the attributes on
437        /// FieldType values.
438        /// </summary>
439        public static readonly IDictionary<FieldType, MappedType> FieldTypeToMappedTypeMap = MapFieldTypes();
440
441        private static IDictionary<FieldType, MappedType> MapFieldTypes()
442        {
443            var map = new Dictionary<FieldType, MappedType>();
444            foreach (FieldInfo field in typeof (FieldType).GetFields(BindingFlags.Static | BindingFlags.Public))
445            {
446                FieldType fieldType = (FieldType) field.GetValue(null);
447                FieldMappingAttribute mapping =
448                    (FieldMappingAttribute) field.GetCustomAttributes(typeof (FieldMappingAttribute), false)[0];
449                map[fieldType] = mapping.MappedType;
450            }
451            return Dictionaries.AsReadOnly(map);
452        }
453
454        /// <summary>
455        /// Look up and cross-link all field types etc.
456        /// </summary>
457        internal void CrossLink()
458        {
459            if (Proto.HasExtendee)
460            {
461                IDescriptor extendee = File.DescriptorPool.LookupSymbol(Proto.Extendee, this);
462                if (!(extendee is MessageDescriptor))
463                {
464                    throw new DescriptorValidationException(this, "\"" + Proto.Extendee + "\" is not a message type.");
465                }
466                containingType = (MessageDescriptor) extendee;
467
468                if (!containingType.IsExtensionNumber(FieldNumber))
469                {
470                    throw new DescriptorValidationException(this,
471                                                            "\"" + containingType.FullName + "\" does not declare " +
472                                                            FieldNumber + " as an extension number.");
473                }
474            }
475
476            if (Proto.HasTypeName)
477            {
478                IDescriptor typeDescriptor =
479                    File.DescriptorPool.LookupSymbol(Proto.TypeName, this);
480
481                if (!Proto.HasType)
482                {
483                    // Choose field type based on symbol.
484                    if (typeDescriptor is MessageDescriptor)
485                    {
486                        fieldType = FieldType.Message;
487                        mappedType = MappedType.Message;
488                    }
489                    else if (typeDescriptor is EnumDescriptor)
490                    {
491                        fieldType = FieldType.Enum;
492                        mappedType = MappedType.Enum;
493                    }
494                    else
495                    {
496                        throw new DescriptorValidationException(this, "\"" + Proto.TypeName + "\" is not a type.");
497                    }
498                }
499
500                if (MappedType == MappedType.Message)
501                {
502                    if (!(typeDescriptor is MessageDescriptor))
503                    {
504                        throw new DescriptorValidationException(this,
505                                                                "\"" + Proto.TypeName + "\" is not a message type.");
506                    }
507                    messageType = (MessageDescriptor) typeDescriptor;
508
509                    if (Proto.HasDefaultValue)
510                    {
511                        throw new DescriptorValidationException(this, "Messages can't have default values.");
512                    }
513                }
514                else if (MappedType == Descriptors.MappedType.Enum)
515                {
516                    if (!(typeDescriptor is EnumDescriptor))
517                    {
518                        throw new DescriptorValidationException(this, "\"" + Proto.TypeName + "\" is not an enum type.");
519                    }
520                    enumType = (EnumDescriptor) typeDescriptor;
521                }
522                else
523                {
524                    throw new DescriptorValidationException(this, "Field with primitive type has type_name.");
525                }
526            }
527            else
528            {
529                if (MappedType == MappedType.Message || MappedType == MappedType.Enum)
530                {
531                    throw new DescriptorValidationException(this, "Field with message or enum type missing type_name.");
532                }
533            }
534
535            // We don't attempt to parse the default value until here because for
536            // enums we need the enum type's descriptor.
537            if (Proto.HasDefaultValue)
538            {
539                if (IsRepeated)
540                {
541                    throw new DescriptorValidationException(this, "Repeated fields cannot have default values.");
542                }
543
544                try
545                {
546                    switch (FieldType)
547                    {
548                        case FieldType.Int32:
549                        case FieldType.SInt32:
550                        case FieldType.SFixed32:
551                            defaultValue = TextFormat.ParseInt32(Proto.DefaultValue);
552                            break;
553                        case FieldType.UInt32:
554                        case FieldType.Fixed32:
555                            defaultValue = TextFormat.ParseUInt32(Proto.DefaultValue);
556                            break;
557                        case FieldType.Int64:
558                        case FieldType.SInt64:
559                        case FieldType.SFixed64:
560                            defaultValue = TextFormat.ParseInt64(Proto.DefaultValue);
561                            break;
562                        case FieldType.UInt64:
563                        case FieldType.Fixed64:
564                            defaultValue = TextFormat.ParseUInt64(Proto.DefaultValue);
565                            break;
566                        case FieldType.Float:
567                            defaultValue = TextFormat.ParseFloat(Proto.DefaultValue);
568                            break;
569                        case FieldType.Double:
570                            defaultValue = TextFormat.ParseDouble(Proto.DefaultValue);
571                            break;
572                        case FieldType.Bool:
573                            if (Proto.DefaultValue == "true")
574                            {
575                                defaultValue = true;
576                            }
577                            else if (Proto.DefaultValue == "false")
578                            {
579                                defaultValue = false;
580                            }
581                            else
582                            {
583                                throw new FormatException("Boolean values must be \"true\" or \"false\"");
584                            }
585                            break;
586                        case FieldType.String:
587                            defaultValue = Proto.DefaultValue;
588                            break;
589                        case FieldType.Bytes:
590                            try
591                            {
592                                defaultValue = TextFormat.UnescapeBytes(Proto.DefaultValue);
593                            }
594                            catch (FormatException e)
595                            {
596                                throw new DescriptorValidationException(this,
597                                                                        "Couldn't parse default value: " + e.Message);
598                            }
599                            break;
600                        case FieldType.Enum:
601                            defaultValue = enumType.FindValueByName(Proto.DefaultValue);
602                            if (defaultValue == null)
603                            {
604                                throw new DescriptorValidationException(this,
605                                                                        "Unknown enum default value: \"" +
606                                                                        Proto.DefaultValue + "\"");
607                            }
608                            break;
609                        case FieldType.Message:
610                        case FieldType.Group:
611                            throw new DescriptorValidationException(this, "Message type had default value.");
612                    }
613                }
614                catch (FormatException e)
615                {
616                    DescriptorValidationException validationException =
617                        new DescriptorValidationException(this,
618                                                          "Could not parse default value: \"" + Proto.DefaultValue +
619                                                          "\"", e);
620                    throw validationException;
621                }
622            }
623            else
624            {
625                // Determine the default default for this field.
626                if (IsRepeated)
627                {
628                    defaultValue = Lists<object>.Empty;
629                }
630                else
631                {
632                    switch (MappedType)
633                    {
634                        case MappedType.Enum:
635                            // We guarantee elsewhere that an enum type always has at least
636                            // one possible value.
637                            defaultValue = enumType.Values[0];
638                            break;
639                        case MappedType.Message:
640                            defaultValue = null;
641                            break;
642                        default:
643                            defaultValue = GetDefaultValueForMappedType(MappedType);
644                            break;
645                    }
646                }
647            }
648
649            if (!IsExtension)
650            {
651                File.DescriptorPool.AddFieldByNumber(this);
652            }
653
654            if (containingType != null && containingType.Options.MessageSetWireFormat)
655            {
656                if (IsExtension)
657                {
658                    if (!IsOptional || FieldType != FieldType.Message)
659                    {
660                        throw new DescriptorValidationException(this,
661                                                                "Extensions of MessageSets must be optional messages.");
662                    }
663                }
664                else
665                {
666                    throw new DescriptorValidationException(this, "MessageSets cannot have fields, only extensions.");
667                }
668            }
669        }
670    }
671}
Note: See TracBrowser for help on using the repository browser.