-
-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathValidationRuleSet.cs
More file actions
225 lines (197 loc) · 7.58 KB
/
ValidationRuleSet.cs
File metadata and controls
225 lines (197 loc) · 7.58 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
namespace ByteBard.AsyncAPI.Validations
{
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using ByteBard.AsyncAPI.Exceptions;
/// <summary>
/// The rule set of the validation.
/// </summary>
public sealed class ValidationRuleSet : IEnumerable<ValidationRule>
{
private IDictionary<Type, IList<ValidationRule>> rules = new Dictionary<Type, IList<ValidationRule>>();
private static ValidationRuleSet defaultRuleSet;
private IList<ValidationRule> emptyRules = new List<ValidationRule>();
/// <summary>
/// Retrieve the rules that are related to a specific type.
/// </summary>
/// <param name="type">The type that is to be validated.</param>
/// <returns>Either the rules related to the type, or an empty list.</returns>
public IList<ValidationRule> FindRules(Type type)
{
IList<ValidationRule> results = null;
this.rules.TryGetValue(type, out results);
return results ?? this.emptyRules;
}
/// <summary>
/// Retrieve the rules that are related to a specific type and version.
/// </summary>
/// <param name="type">The type that is to be validated.</param>
/// <param name="version">The AsyncAPI version to filter rules by. If null, all rules are returned.</param>
/// <returns>Either the rules related to the type and version, or an empty list.</returns>
public IList<ValidationRule> FindRules(Type type, AsyncApiVersion? version)
{
var allRules = this.FindRules(type);
if (version == null)
{
return allRules;
}
return allRules.Where(r =>
r.ApplicableVersions == null ||
r.ApplicableVersions.Contains(version.Value)).ToList();
}
/// <summary>
/// Gets the default validation rule sets.
/// </summary>
/// <remarks>
/// This is a method instead of a property to signal that a new default ruleset object is created
/// per call. Making this a property may be misleading callers to think the returned rulesets from multiple calls
/// are the same objects.
/// </remarks>
public static ValidationRuleSet GetDefaultRuleSet()
{
// Reflection can be an expensive operation, so we cache the default rule set that has already been built.
if (defaultRuleSet == null)
{
defaultRuleSet = BuildDefaultRuleSet();
}
// We create a new instance of ValidationRuleSet per call as a safeguard
// against unintentional modification of the private _defaultRuleSet.
return new ValidationRuleSet(defaultRuleSet);
}
/// <summary>
/// Return Ruleset with no rules.
/// </summary>
public static ValidationRuleSet GetEmptyRuleSet()
{
// We create a new instance of ValidationRuleSet per call as a safeguard
// against unintentional modification of the private _defaultRuleSet.
return new ValidationRuleSet();
}
/// <summary>
/// Initializes a new instance of the <see cref="ValidationRuleSet"/> class.
/// </summary>
public ValidationRuleSet()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ValidationRuleSet"/> class.
/// </summary>
/// <param name="ruleSet">Rule set to be copied from.</param>
public ValidationRuleSet(ValidationRuleSet ruleSet)
{
if (ruleSet == null)
{
return;
}
foreach (ValidationRule rule in ruleSet)
{
this.Add(rule);
}
}
/// <summary>
/// Initializes a new instance of the <see cref="ValidationRuleSet"/> class.
/// </summary>
/// <param name="rules">Rules to be contained in this ruleset.</param>
public ValidationRuleSet(IEnumerable<ValidationRule> rules)
{
if (rules == null)
{
return;
}
foreach (ValidationRule rule in rules)
{
this.Add(rule);
}
}
/// <summary>
/// Gets the rules in this rule set.
/// </summary>
public IList<ValidationRule> Rules
{
get
{
return this.rules.Values.SelectMany(v => v).ToList();
}
}
/// <summary>
/// Add the new rule into the rule set.
/// </summary>
/// <param name="rule">The rule.</param>
public void Add(ValidationRule rule)
{
if (!this.rules.ContainsKey(rule.ElementType))
{
this.rules[rule.ElementType] = new List<ValidationRule>();
}
if (this.rules[rule.ElementType].Contains(rule))
{
throw new AsyncApiException("Rules cannot be added twice");
}
this.rules[rule.ElementType].Add(rule);
}
/// <summary>
/// Get the enumerator.
/// </summary>
/// <returns>The enumerator.</returns>
public IEnumerator<ValidationRule> GetEnumerator()
{
foreach (var ruleList in this.rules.Values)
{
foreach (var rule in ruleList)
{
yield return rule;
}
}
}
/// <summary>
/// Get the enumerator.
/// </summary>
/// <returns>The enumerator.</returns>
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
private static ValidationRuleSet BuildDefaultRuleSet()
{
ValidationRuleSet ruleSet = new ValidationRuleSet();
Type validationRuleType = typeof(ValidationRule);
IEnumerable<PropertyInfo> ruleProperties = typeof(ValidationRuleSet).Assembly.GetTypes()
.Where(t => t.IsClass
&& t != typeof(object)
&& t.GetCustomAttributes(typeof(AsyncApiRuleAttribute), false).Any())
.SelectMany(t2 => t2.GetProperties(BindingFlags.Static | BindingFlags.Public)
.Where(p => validationRuleType.IsAssignableFrom(p.PropertyType)));
foreach (var property in ruleProperties)
{
var propertyValue = property.GetValue(null); // static property
ValidationRule rule = propertyValue as ValidationRule;
if (rule != null)
{
var versionAttribute = property.GetCustomAttribute<AsyncApiVersionRuleAttribute>();
if (versionAttribute != null)
{
rule.ApplicableVersions = versionAttribute.Versions;
}
ruleSet.Add(rule);
}
}
return ruleSet;
}
}
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
public class AsyncApiRuleAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class AsyncApiVersionRuleAttribute : Attribute
{
public AsyncApiVersion[] Versions { get; }
public AsyncApiVersionRuleAttribute(params AsyncApiVersion[] versions)
{
Versions = versions;
}
}
}