1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package org.apache.commons.validator;
23
24 import org.apache.oro.text.perl.Perl5Util;
25
26 /***
27 * A class for validating 10 digit ISBN codes.
28 * Based on this
29 * <a href="http://www.isbn.org/standards/home/isbn/international/html/usm4.htm">
30 * algorithm</a>
31 *
32 * @since Validator 1.2.0
33 */
34 public class ISBNValidator {
35
36 private static final String SEP = "(//-|//s)";
37 private static final String GROUP = "(//d{1,5})";
38 private static final String PUBLISHER = "(//d{1,7})";
39 private static final String TITLE = "(//d{1,6})";
40 private static final String CHECK = "([0-9X])";
41
42 /***
43 * ISBN consists of 4 groups of numbers separated by either dashes (-)
44 * or spaces. The first group is 1-5 characters, second 1-7, third 1-6,
45 * and fourth is 1 digit or an X.
46 */
47 private static final String ISBN_PATTERN =
48 "/^" + GROUP + SEP + PUBLISHER + SEP + TITLE + SEP + CHECK + "$/";
49
50 /***
51 * Default Constructor.
52 */
53 public ISBNValidator() {
54 super();
55 }
56
57 /***
58 * If the ISBN is formatted with space or dash separators its format is
59 * validated. Then the digits in the number are weighted, summed, and
60 * divided by 11 according to the ISBN algorithm. If the result is zero,
61 * the ISBN is valid. This method accepts formatted or raw ISBN codes.
62 *
63 * @param isbn Candidate ISBN number to be validated. <code>null</code> is
64 * considered invalid.
65 * @return true if the string is a valid ISBN code.
66 */
67 public boolean isValid(String isbn) {
68 if (isbn == null || isbn.length() < 10 || isbn.length() > 13) {
69 return false;
70 }
71
72 if (isFormatted(isbn) && !isValidPattern(isbn)) {
73 return false;
74 }
75
76 isbn = clean(isbn);
77 if (isbn.length() != 10) {
78 return false;
79 }
80
81 return (sum(isbn) % 11) == 0;
82 }
83
84 /***
85 * Returns the sum of the weighted ISBN characters.
86 */
87 private int sum(String isbn) {
88 int total = 0;
89 for (int i = 0; i < 9; i++) {
90 int weight = 10 - i;
91 total += (weight * toInt(isbn.charAt(i)));
92 }
93 total += toInt(isbn.charAt(9));
94 return total;
95 }
96
97 /***
98 * Removes all non-digit characters except for 'X' which is a valid ISBN
99 * character.
100 */
101 private String clean(String isbn) {
102 StringBuffer buf = new StringBuffer(10);
103
104 for (int i = 0; i < isbn.length(); i++) {
105 char digit = isbn.charAt(i);
106 if (Character.isDigit(digit) || (digit == 'X')) {
107 buf.append(digit);
108 }
109 }
110
111 return buf.toString();
112 }
113
114 /***
115 * Returns the numeric value represented by the character. If the
116 * character is not a digit but an 'X', 10 is returned.
117 */
118 private int toInt(char ch) {
119 return (ch == 'X') ? 10 : Character.getNumericValue(ch);
120 }
121
122 /***
123 * Returns true if the ISBN contains one of the separator characters space
124 * or dash.
125 */
126 private boolean isFormatted(String isbn) {
127 return ((isbn.indexOf('-') != -1) || (isbn.indexOf(' ') != -1));
128 }
129
130 /***
131 * Returns true if the ISBN is formatted properly.
132 */
133 private boolean isValidPattern(String isbn) {
134 return new Perl5Util().match(ISBN_PATTERN, isbn);
135 }
136
137 }