1 #region Copyright notice and license 2 // Protocol Buffers - Google's data interchange format 3 // Copyright 2008 Google Inc. All rights reserved. 4 // 5 // Use of this source code is governed by a BSD-style 6 // license that can be found in the LICENSE file or at 7 // https://developers.google.com/open-source/licenses/bsd 8 #endregion 9 10 using System; 11 using System.Collections; 12 using System.Collections.Generic; 13 using System.Linq; 14 15 namespace Google.Protobuf 16 { 17 /// <summary> 18 /// Provides extensions to messages while parsing. This API is experimental and subject to change. 19 /// </summary> 20 public sealed class ExtensionRegistry : ICollection<Extension>, IDeepCloneable<ExtensionRegistry> 21 { 22 internal sealed class ExtensionComparer : IEqualityComparer<Extension> 23 { Equals(Extension a, Extension b)24 public bool Equals(Extension a, Extension b) 25 { 26 return new ObjectIntPair<Type>(a.TargetType, a.FieldNumber).Equals(new ObjectIntPair<Type>(b.TargetType, b.FieldNumber)); 27 } GetHashCode(Extension a)28 public int GetHashCode(Extension a) 29 { 30 return new ObjectIntPair<Type>(a.TargetType, a.FieldNumber).GetHashCode(); 31 } 32 33 internal static ExtensionComparer Instance = new ExtensionComparer(); 34 } 35 private readonly IDictionary<ObjectIntPair<Type>, Extension> extensions; 36 37 /// <summary> 38 /// Creates a new empty extension registry 39 /// </summary> ExtensionRegistry()40 public ExtensionRegistry() 41 { 42 extensions = new Dictionary<ObjectIntPair<Type>, Extension>(); 43 } 44 ExtensionRegistry(IDictionary<ObjectIntPair<Type>, Extension> collection)45 private ExtensionRegistry(IDictionary<ObjectIntPair<Type>, Extension> collection) 46 { 47 extensions = collection.ToDictionary(k => k.Key, v => v.Value); 48 } 49 50 /// <summary> 51 /// Gets the total number of extensions in this extension registry 52 /// </summary> 53 public int Count => extensions.Count; 54 55 /// <summary> 56 /// Returns whether the registry is readonly 57 /// </summary> 58 bool ICollection<Extension>.IsReadOnly => false; 59 ContainsInputField(uint lastTag, Type target, out Extension extension)60 internal bool ContainsInputField(uint lastTag, Type target, out Extension extension) 61 { 62 return extensions.TryGetValue(new ObjectIntPair<Type>(target, WireFormat.GetTagFieldNumber(lastTag)), out extension); 63 } 64 65 /// <summary> 66 /// Adds the specified extension to the registry 67 /// </summary> Add(Extension extension)68 public void Add(Extension extension) 69 { 70 ProtoPreconditions.CheckNotNull(extension, nameof(extension)); 71 72 extensions.Add(new ObjectIntPair<Type>(extension.TargetType, extension.FieldNumber), extension); 73 } 74 75 /// <summary> 76 /// Adds the specified extensions to the registry 77 /// </summary> AddRange(IEnumerable<Extension> extensions)78 public void AddRange(IEnumerable<Extension> extensions) 79 { 80 ProtoPreconditions.CheckNotNull(extensions, nameof(extensions)); 81 82 foreach (var extension in extensions) 83 { 84 Add(extension); 85 } 86 } 87 88 /// <summary> 89 /// Clears the registry of all values 90 /// </summary> Clear()91 public void Clear() 92 { 93 extensions.Clear(); 94 } 95 96 /// <summary> 97 /// Gets whether the extension registry contains the specified extension 98 /// </summary> Contains(Extension item)99 public bool Contains(Extension item) 100 { 101 ProtoPreconditions.CheckNotNull(item, nameof(item)); 102 103 return extensions.ContainsKey(new ObjectIntPair<Type>(item.TargetType, item.FieldNumber)); 104 } 105 106 /// <summary> 107 /// Copies the arrays in the registry set to the specified array at the specified index 108 /// </summary> 109 /// <param name="array">The array to copy to</param> 110 /// <param name="arrayIndex">The array index to start at</param> CopyTo(Extension[] array, int arrayIndex)111 void ICollection<Extension>.CopyTo(Extension[] array, int arrayIndex) 112 { 113 ProtoPreconditions.CheckNotNull(array, nameof(array)); 114 if (arrayIndex < 0 || arrayIndex >= array.Length) 115 { 116 throw new ArgumentOutOfRangeException(nameof(arrayIndex)); 117 } 118 if (array.Length - arrayIndex < Count) 119 { 120 throw new ArgumentException("The provided array is shorter than the number of elements in the registry"); 121 } 122 123 for (int i = 0; i < array.Length; i++) 124 { 125 Extension extension = array[i]; 126 extensions.Add(new ObjectIntPair<Type>(extension.TargetType, extension.FieldNumber), extension); 127 } 128 } 129 130 /// <summary> 131 /// Returns an enumerator to enumerate through the items in the registry 132 /// </summary> 133 /// <returns>Returns an enumerator for the extensions in this registry</returns> GetEnumerator()134 public IEnumerator<Extension> GetEnumerator() 135 { 136 return extensions.Values.GetEnumerator(); 137 } 138 139 /// <summary> 140 /// Removes the specified extension from the set 141 /// </summary> 142 /// <param name="item">The extension</param> 143 /// <returns><c>true</c> if the extension was removed, otherwise <c>false</c></returns> Remove(Extension item)144 public bool Remove(Extension item) 145 { 146 ProtoPreconditions.CheckNotNull(item, nameof(item)); 147 148 return extensions.Remove(new ObjectIntPair<Type>(item.TargetType, item.FieldNumber)); 149 } 150 IEnumerable.GetEnumerator()151 IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); 152 153 /// <summary> 154 /// Clones the registry into a new registry 155 /// </summary> Clone()156 public ExtensionRegistry Clone() 157 { 158 return new ExtensionRegistry(extensions); 159 } 160 } 161 } 162