-
Notifications
You must be signed in to change notification settings - Fork 0
/
Ean13.cs
141 lines (104 loc) · 4.6 KB
/
Ean13.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
140
141
using System;
using System.Linq;
using System.Text.RegularExpressions;
namespace IsbnTools
{
public class Ean13
{
private static readonly Regex CleanUpRegex = new Regex("[^0-9Xx]");
private Ean13(string digits) : this(digits, CalculateEan13CheckDigit(digits))
{
}
protected Ean13(string digits, int checkDigit)
{
if (digits == null) throw new ArgumentNullException("digits");
digits = digits.Trim();
if (digits.Any(c => c < '0' || c > '9'))
throw new ArgumentOutOfRangeException("digits", "Only digits are allowed.");
if (digits.Length < 12)
throw new ArgumentException("EAN-13 is too short.", "digits");
if (digits.Length == 13)
throw new ArgumentException("EAN-13 must not include the check digit.", "digits");
if (digits.Length > 13)
throw new ArgumentException("EAN-13 is too long.", "digits");
if (checkDigit < 0 || checkDigit > 9)
throw new ArgumentOutOfRangeException("checkDigit", "EAN-13 check digits must be a single digit (0-9).");
Digits = digits;
CheckDigit = checkDigit;
CalculatedCheckDigit = CalculateEan13CheckDigit(Digits);
}
public int CalculatedCheckDigit { get; protected set; }
public int CheckDigit { get; protected set; }
public string Digits { get; protected set; }
public static Ean13 Parse(string input, RangeMessage rangeMessage)
{
if (input == null) return null;
string clean = CleanUpRegex.Replace(input, "").ToUpperInvariant();
if (clean.Length == 9 || clean.Length == 10)
{
return Parse("978" + clean.Substring(0, 9), rangeMessage);
}
if (clean.Length == 12 || clean.Length == 13)
{
string country = clean.Substring(0, 3);
if (rangeMessage.FindUccPrefix(country) != null)
{
Isbn13 isbn = ParseIsbn13(clean, rangeMessage);
if (isbn != null)
return isbn;
}
if (clean.Length == 13)
{
return new Ean13(clean.Substring(0, 12), clean[12] - 48);
}
return new Ean13(clean.Substring(0, 12));
}
return null;
}
protected static int CalculateEan13CheckDigit(string digits)
{
int num = digits.Select((t, i) => (t - 48)*(i%2 == 0 ? 1 : 3)).Sum();
return (10 - num%10)%10;
}
public override string ToString()
{
return ToString("-", false);
}
public virtual string ToString(string seperator, bool forceValidCheckDigit)
{
return Digits.Insert(3, seperator) + seperator + (forceValidCheckDigit ? CalculatedCheckDigit : CheckDigit);
}
private static Isbn13 ParseIsbn13(string isbn, RangeMessage rangeMessage)
{
if (isbn == null) return null;
isbn = isbn.Trim();
RegistrationGroup group = rangeMessage.FindGroup(isbn);
if (group == null)
{
// No publisher group found. Unable to format.
return null;
}
int publisherIndex = group.UccPrefix.Prefix.Length + group.GroupIdentifier.Length;
// rules in the range message are always padded to 7 digits
// we can safely use the check digit too
string paddedIsbn = isbn + "0000000000000";
int paddedRange = int.Parse(paddedIsbn.Substring(publisherIndex, 7));
Range range = group.Rules.Single(x => paddedRange >= x.Start && paddedRange <= x.End);
if (range.Length == 0)
{
// Range not defined for use.
return null;
}
string publisher = isbn.Substring(publisherIndex, range.Length);
int titleIndex = publisherIndex + range.Length;
int titleLength = 13 - titleIndex - 1;
string title = isbn.Substring(titleIndex, titleLength);
if (isbn.Length == 13)
{
int checkDigit = int.Parse(isbn.Substring(12, 1));
return new Isbn13(group, publisher, title, checkDigit);
}
return new Isbn13(group, publisher, title);
}
}
}