Updates referencesource to .NET 4.7
[mono-project.git] / mcs / class / referencesource / System.Data.Entity / System / Data / Common / CommandTrees / ExpressionBuilder / Internal / EnumerableValidator.cs
blob9010a31ed64d8f21ecc9a4b3706e9533678ce225
1 //---------------------------------------------------------------------
2 // <copyright file="EnumerableValidator.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
4 // </copyright>
5 //
6 // @owner Microsoft
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
10 namespace System.Data.Common.CommandTrees.ExpressionBuilder.Internal
12 using System.Collections.Generic;
13 using System.Data.Common.Utils;
14 using System.Diagnostics;
16 /// <summary>
17 /// Validates an input enumerable argument with a specific element type,
18 /// converting each input element into an instance of a specific output element type,
19 /// then producing a final result of another specific type.
20 /// </summary>
21 /// <typeparam name="TElementIn">The element type of the input enumerable</typeparam>
22 /// <typeparam name="TElementOut">The element type that input elements are converted to</typeparam>
23 /// <typeparam name="TResult">The type of the final result</typeparam>
24 internal sealed class EnumerableValidator<TElementIn, TElementOut, TResult>
26 private readonly string argumentName;
27 private readonly IEnumerable<TElementIn> target;
29 internal EnumerableValidator(IEnumerable<TElementIn> argument, string argumentName)
31 this.argumentName = argumentName;
32 this.target = argument;
35 private bool allowEmpty;
36 private int expectedElementCount = -1;
37 private Func<TElementIn, int, TElementOut> map;
38 private Func<List<TElementOut>, TResult> collect;
39 private Func<TElementIn, int, string> deriveName;
41 /// <summary>
42 /// Gets or sets a value that determines whether an exception is thrown if the enumerable argument is empty.
43 /// </summary>
44 /// <remarks>
45 /// AllowEmpty is ignored if <see cref="ExpectedElementCount"/> is set.
46 /// If ExpectedElementCount is set to zero, an empty collection will not cause an exception to be thrown,
47 /// even if AllowEmpty is set to <c>false</c>.
48 /// </remarks>
49 public bool AllowEmpty { get { return this.allowEmpty; } set { this.allowEmpty = value; } }
51 /// <summary>
52 /// Gets or set a value that determines the number of elements expected in the enumerable argument.
53 /// A value of <c>-1</c> indicates that any number of elements is permitted, including zero.
54 /// Use <see cref="AllowEmpty"/> to disallow an empty list when ExpectedElementCount is set to -1.
55 /// </summary>
56 public int ExpectedElementCount { get { return this.expectedElementCount; } set { this.expectedElementCount = value; } }
58 /// <summary>
59 /// Gets or sets the function used to convert an element from the enumerable argument into an instance of
60 /// the desired output element type. The position of the input element is also specified as an argument to this function.
61 /// </summary>
62 public Func<TElementIn, int, TElementOut> ConvertElement { get { return this.map; } set { this.map = value; } }
64 /// <summary>
65 /// Gets or sets the function used to create the output collection from a list of converted enumerable elements.
66 /// </summary>
67 public Func<List<TElementOut>, TResult> CreateResult { get { return this.collect; } set { this.collect = value; } }
69 /// <summary>
70 /// Gets or sets an optional function that can retrieve the name of an element from the enumerable argument.
71 /// If this function is set, duplicate input element names will result in an exception. Null or empty names will
72 /// not result in an exception. If specified, this function will be called after <see cref="ConvertElement"/>.
73 /// </summary>
74 public Func<TElementIn, int, string> GetName { get { return this.deriveName; } set { this.deriveName = value; } }
76 /// <summary>
77 /// Validates the input enumerable, converting each input element and producing the final instance of <typeparamref name="TResult"/> as a result.
78 /// </summary>
79 /// <returns>The instance of <typeparamref name="TResult"/> produced by calling the <see cref="CreateResult"/> function
80 /// on the list of elements produced by calling the <see cref="ConvertElement"/> function on each element of the input enumerable.</returns>
81 /// <exception cref="ArgumentNullException">If the input enumerable itself is null</exception>
82 /// <exception cref="ArgumentNullException">If <typeparamref name="TElementIn"/> is a nullable type and any element of the input enumerable is null.</exception>
83 /// <exception cref="ArgumentException">If <see cref="ExpectedElementCount"/> is set and the actual number of input elements is not equal to this value.</exception>
84 /// <exception cref="ArgumentException">If <see cref="ExpectedElementCount"/> is -1, <see cref="AllowEmpty"/> is set to <c>false</c> and the input enumerable is empty.</exception>
85 /// <exception cref="ArgumentException">If <see cref="GetName"/> is set and a duplicate name is derived for more than one input element.</exception>
86 /// <remarks>Other exceptions may be thrown by the <see cref="ConvertElement"/> and <see cref="CreateResult"/> functions, and by the <see cref="GetName"/> function, if specified.</remarks>
87 internal TResult Validate()
89 return EnumerableValidator<TElementIn, TElementOut, TResult>.Validate(this.target,
90 this.argumentName,
91 this.ExpectedElementCount,
92 this.AllowEmpty,
93 this.ConvertElement,
94 this.CreateResult,
95 this.GetName);
98 private static TResult Validate(IEnumerable<TElementIn> argument,
99 string argumentName,
100 int expectedElementCount,
101 bool allowEmpty,
102 Func<TElementIn, int, TElementOut> map,
103 Func<List<TElementOut>, TResult> collect,
104 Func<TElementIn, int, string> deriveName)
106 Debug.Assert(map != null, "Set EnumerableValidator.ConvertElement before calling validate");
107 Debug.Assert(collect != null, "Set EnumerableValidator.CreateResult before calling validate");
109 EntityUtil.CheckArgumentNull(argument, argumentName);
111 bool checkNull = (default(TElementIn) == null);
112 bool checkCount = (expectedElementCount != -1);
113 Dictionary<string, int> nameIndex = null;
114 if (deriveName != null)
116 nameIndex = new Dictionary<string, int>();
119 int pos = 0;
120 List<TElementOut> validatedElements = new List<TElementOut>();
121 foreach (TElementIn elementIn in argument)
123 // More elements in 'arguments' than expected?
124 if (checkCount && pos == expectedElementCount)
126 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_ExpressionList_IncorrectElementCount, argumentName);
129 if (checkNull && elementIn == null)
131 // Don't call FormatIndex unless an exception is actually being thrown
132 throw EntityUtil.ArgumentNull(StringUtil.FormatIndex(argumentName, pos));
135 TElementOut elementOut = map(elementIn, pos);
136 validatedElements.Add(elementOut);
138 if (deriveName != null)
140 string name = deriveName(elementIn, pos);
141 Debug.Assert(name != null, "GetName should not produce null");
142 int foundIndex = -1;
143 if (nameIndex.TryGetValue(name, out foundIndex))
145 throw EntityUtil.Argument(
146 System.Data.Entity.Strings.Cqt_Util_CheckListDuplicateName(foundIndex, pos, name),
147 StringUtil.FormatIndex(argumentName, pos)
150 nameIndex[name] = pos;
153 pos++;
156 // If an expected count was specified, the actual count must match
157 if (checkCount)
159 if (pos != expectedElementCount)
161 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_ExpressionList_IncorrectElementCount, argumentName);
164 else
166 // No expected count was specified, simply verify empty vs. non-empty.
167 if (0 == pos && !allowEmpty)
169 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Util_CheckListEmptyInvalid, argumentName);
173 return collect(validatedElements);