-
-
Notifications
You must be signed in to change notification settings - Fork 30
/
IbanValidator.cs
139 lines (124 loc) · 3.8 KB
/
IbanValidator.cs
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
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using IbanNet.Registry;
using IbanNet.Validation;
using IbanNet.Validation.Rules;
namespace IbanNet
{
/// <summary>
/// Represents the default IBAN validator.
/// </summary>
public class IbanValidator : IIbanValidator, ICountryValidationSupport
{
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private readonly Lazy<IReadOnlyCollection<CountryInfo>> _registry;
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private readonly Collection<IIbanValidationRule> _rules;
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private readonly object _lockObject = new object();
private readonly IStructureValidationFactory _structureValidationFactory;
private Dictionary<string, CountryInfo> _structures;
/// <summary>
/// Initializes a new instance of the <see cref="IbanValidator"/> class.
/// </summary>
public IbanValidator()
: this(new Lazy<IReadOnlyCollection<CountryInfo>>(() => new IbanRegistry(), LazyThreadSafetyMode.ExecutionAndPublication))
{
}
/// <summary>
/// Initializes a new instance of the <see cref="IbanValidator"/> class with specified registry.
/// </summary>
/// <param name="registry">The IBAN registry containing IBAN/BBAN/SEPA information per country.</param>
// ReSharper disable once MemberCanBePrivate.Global
public IbanValidator(Lazy<IReadOnlyCollection<CountryInfo>> registry)
{
_registry = registry ?? throw new ArgumentNullException(nameof(registry));
_structureValidationFactory = new CachedStructureValidationFactory(new SwiftStructureValidationFactory());
_rules = new Collection<IIbanValidationRule>
{
new NotNullRule(),
new NoIllegalCharactersRule(),
new HasCountryCodeRule(),
new HasIbanChecksumRule(),
new IsValidCountryCodeRule(),
new IsValidLengthRule(),
new IsMatchingStructureRule(_structureValidationFactory),
new Mod97Rule()
};
}
/// <summary>
/// Gets the supported countries.
/// </summary>
// TODO: v4, change to dictionary for faster lookup.
public IEnumerable<CountryInfo> SupportedCountries => ((ICountryValidationSupport)this).SupportedCountries.Values;
/// <summary>
/// Gets the supported countries.
/// </summary>
IReadOnlyDictionary<string, CountryInfo> ICountryValidationSupport.SupportedCountries
{
get
{
InitRegistry();
return new ReadOnlyDictionary<string, CountryInfo>(_structures);
}
}
/// <summary>
/// Validates the specified IBAN for correctness.
/// </summary>
/// <param name="iban">The IBAN value.</param>
/// <returns>a validation result, indicating if the IBAN is valid or not</returns>
public ValidationResult Validate(string iban)
{
InitRegistry();
string normalizedIban = Iban.Normalize(iban);
var context = new ValidationContext
{
Value = normalizedIban,
Result = IbanValidationResult.Valid,
Country = GetMatchingCountry(normalizedIban)
};
foreach (IIbanValidationRule rule in _rules)
{
rule.Validate(context);
if (context.Result != IbanValidationResult.Valid)
{
break;
}
}
return new ValidationResult
{
Value = normalizedIban?.ToUpperInvariant(),
Result = context.Result,
Country = context.Country
};
}
private void InitRegistry()
{
if (_structures != null)
{
return;
}
lock (_lockObject)
{
_structures = _structures ?? _registry.Value
.ToDictionary(
kvp => kvp.TwoLetterISORegionName
);
}
}
private CountryInfo GetMatchingCountry(string iban)
{
string countryCode = iban.GetCountryCode();
if (countryCode == null)
{
return null;
}
_structures.TryGetValue(countryCode, out CountryInfo matchedCountry);
return matchedCountry;
}
}
}