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