1#region Copyright notice and license
2// Protocol Buffers - Google's data interchange format
3// Copyright 2008 Google Inc.  All rights reserved.
4// https://developers.google.com/protocol-buffers/
5//
6// Redistribution and use in source and binary forms, with or without
7// modification, are permitted provided that the following conditions are
8// met:
9//
10//     * Redistributions of source code must retain the above copyright
11// notice, this list of conditions and the following disclaimer.
12//     * Redistributions in binary form must reproduce the above
13// copyright notice, this list of conditions and the following disclaimer
14// in the documentation and/or other materials provided with the
15// distribution.
16//     * Neither the name of Google Inc. nor the names of its
17// contributors may be used to endorse or promote products derived from
18// this software without specific prior written permission.
19//
20// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31#endregion
32
33using System.Collections.Generic;
34using System.Collections.ObjectModel;
35using System.Linq;
36
37namespace Google.Protobuf.Reflection
38{
39    /// <summary>
40    /// A collection to simplify retrieving the descriptors of extensions in a descriptor for a message
41    /// </summary>
42    public sealed class ExtensionCollection
43    {
44        private IDictionary<MessageDescriptor, IList<FieldDescriptor>> extensionsByTypeInDeclarationOrder;
45        private IDictionary<MessageDescriptor, IList<FieldDescriptor>> extensionsByTypeInNumberOrder;
46
47        internal ExtensionCollection(FileDescriptor file, Extension[] extensions)
48        {
49            UnorderedExtensions = DescriptorUtil.ConvertAndMakeReadOnly(
50                file.Proto.Extension,
51                (extension, i) => {
52                    if (extensions?.Length != 0)
53                    {
54                        return new FieldDescriptor(extension, file, null, i, null, extensions?[i]);
55                    }
56                    else
57                    {
58                        return new FieldDescriptor(extension, file, null, i, null, null); // return null if there's no extensions in this array for old code-gen
59                    }
60                });
61        }
62
63        internal ExtensionCollection(MessageDescriptor message, Extension[] extensions)
64        {
65            UnorderedExtensions = DescriptorUtil.ConvertAndMakeReadOnly(
66                message.Proto.Extension,
67                (extension, i) => {
68                    if (extensions?.Length != 0)
69                    {
70                        return new FieldDescriptor(extension, message.File, message, i, null, extensions?[i]);
71                    }
72                    else
73                    {
74                        return new FieldDescriptor(extension, message.File, message, i, null, null);
75                    }
76                });
77        }
78
79        /// <summary>
80        /// Returns a readonly list of all the extensions defined in this type in
81        /// the order they were defined in the source .proto file
82        /// </summary>
83        public IList<FieldDescriptor> UnorderedExtensions { get; }
84
85        /// <summary>
86        /// Returns a readonly list of all the extensions define in this type that extend
87        /// the provided descriptor type in the order they were defined in the source .proto file
88        /// </summary>
89        public IList<FieldDescriptor> GetExtensionsInDeclarationOrder(MessageDescriptor descriptor)
90        {
91            return extensionsByTypeInDeclarationOrder[descriptor];
92        }
93
94        /// <summary>
95        /// Returns a readonly list of all the extensions define in this type that extend
96        /// the provided descriptor type in accending field order
97        /// </summary>
98        public IList<FieldDescriptor> GetExtensionsInNumberOrder(MessageDescriptor descriptor)
99        {
100            return extensionsByTypeInNumberOrder[descriptor];
101        }
102
103        internal void CrossLink()
104        {
105            Dictionary<MessageDescriptor, IList<FieldDescriptor>> declarationOrder = new Dictionary<MessageDescriptor, IList<FieldDescriptor>>();
106            foreach (FieldDescriptor descriptor in UnorderedExtensions)
107            {
108                descriptor.CrossLink();
109
110                IList<FieldDescriptor> list;
111                if (!declarationOrder.TryGetValue(descriptor.ExtendeeType, out list))
112                {
113                    list = new List<FieldDescriptor>();
114                    declarationOrder.Add(descriptor.ExtendeeType, list);
115                }
116
117                list.Add(descriptor);
118            }
119
120            extensionsByTypeInDeclarationOrder = declarationOrder
121                .ToDictionary(kvp => kvp.Key, kvp => (IList<FieldDescriptor>)new ReadOnlyCollection<FieldDescriptor>(kvp.Value));
122            extensionsByTypeInNumberOrder = declarationOrder
123                .ToDictionary(kvp => kvp.Key, kvp => (IList<FieldDescriptor>)new ReadOnlyCollection<FieldDescriptor>(kvp.Value.OrderBy(field => field.FieldNumber).ToArray()));
124        }
125    }
126}
127