1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.commons.validator.routines;
18
19 import java.io.Serializable;
20 import org.apache.commons.validator.routines.checkdigit.EAN13CheckDigit;
21 import org.apache.commons.validator.routines.checkdigit.ISBN10CheckDigit;
22 import org.apache.commons.validator.routines.checkdigit.CheckDigitException;
23
24 /**
25 * <b>ISBN-10</b> and <b>ISBN-13</b> Code Validation.
26 * <p>
27 * This validator validates the code is either a valid ISBN-10
28 * (using a {@link CodeValidator} with the {@link ISBN10CheckDigit})
29 * or a valid ISBN-13 code (using a {@link CodeValidator} with the
30 * the {@link EAN13CheckDigit} routine).
31 * <p>
32 * The <code>validate()</code> methods return the ISBN code with formatting
33 * characters removed if valid or <code>null</code> if invalid.
34 * <p>
35 * This validator also provides the facility to convert ISBN-10 codes to
36 * ISBN-13 if the <code>convert</code> property is <code>true</code>.
37 * <p>
38 * From 1st January 2007 the book industry will start to use a new 13 digit
39 * ISBN number (rather than this 10 digit ISBN number). ISBN-13 codes are
40 * <a href="http://en.wikipedia.org/wiki/European_Article_Number">EAN</a>
41 * codes, for more information see:</p>
42 *
43 * <ul>
44 * <li><a href="http://en.wikipedia.org/wiki/ISBN">Wikipedia - International
45 * Standard Book Number (ISBN)</a>.</li>
46 * <li>EAN - see
47 * <a href="http://en.wikipedia.org/wiki/European_Article_Number">Wikipedia -
48 * European Article Number</a>.</li>
49 * <li><a href="http://www.isbn.org/standards/home/isbn/transition.asp">ISBN-13
50 * Transition details</a>.</li>
51 * </ul>
52 *
53 * @version $Revision: 1441678 $ $Date: 2013-02-01 20:31:07 -0500 (Fri, 01 Feb 2013) $
54 * @since Validator 1.4
55 */
56 public class ISBNValidator implements Serializable {
57
58 private static final long serialVersionUID = 4319515687976420405L;
59
60 private static final String SEP = "(?:\\-|\\s)";
61 private static final String GROUP = "(\\d{1,5})";
62 private static final String PUBLISHER = "(\\d{1,7})";
63 private static final String TITLE = "(\\d{1,6})";
64
65 /**
66 * ISBN-10 consists of 4 groups of numbers separated by either dashes (-)
67 * or spaces. The first group is 1-5 characters, second 1-7, third 1-6,
68 * and fourth is 1 digit or an X.
69 */
70 static final String ISBN10_REGEX =
71 "^(?:(\\d{9}[0-9X])|(?:" + GROUP + SEP + PUBLISHER + SEP + TITLE + SEP + "([0-9X])))$";
72
73 /**
74 * ISBN-13 consists of 5 groups of numbers separated by either dashes (-)
75 * or spaces. The first group is 978 or 979, the second group is
76 * 1-5 characters, third 1-7, fourth 1-6, and fifth is 1 digit.
77 */
78 static final String ISBN13_REGEX =
79 "^(978|979)(?:(\\d{10})|(?:" + SEP + GROUP + SEP + PUBLISHER + SEP + TITLE + SEP + "([0-9])))$";
80
81 /** ISBN Code Validator (which converts ISBN-10 codes to ISBN-13 */
82 private static final ISBNValidator ISBN_VALIDATOR = new ISBNValidator();
83
84 /** ISBN Code Validator (which converts ISBN-10 codes to ISBN-13 */
85 private static final ISBNValidator ISBN_VALIDATOR_NO_CONVERT = new ISBNValidator(false);
86
87
88 /** ISBN-10 Code Validator */
89 private final CodeValidator isbn10Validator = new CodeValidator(ISBN10_REGEX, 10, ISBN10CheckDigit.ISBN10_CHECK_DIGIT);
90
91 /** ISBN-13 Code Validator */
92 private final CodeValidator isbn13Validator = new CodeValidator(ISBN13_REGEX, 13, EAN13CheckDigit.EAN13_CHECK_DIGIT);
93
94 private final boolean convert;
95
96 /**
97 * Return a singleton instance of the ISBN validator which
98 * converts ISBN-10 codes to ISBN-13.
99 *
100 * @return A singleton instance of the ISBN validator.
101 */
102 public static ISBNValidator getInstance() {
103 return ISBN_VALIDATOR;
104 }
105
106 /**
107 * Return a singleton instance of the ISBN validator specifying
108 * whether ISBN-10 codes should be converted to ISBN-13.
109 *
110 * @param convert <code>true</code> if valid ISBN-10 codes
111 * should be converted to ISBN-13 codes or <code>false</code>
112 * if valid ISBN-10 codes should be returned unchanged.
113 * @return A singleton instance of the ISBN validator.
114 */
115 public static ISBNValidator getInstance(boolean convert) {
116 return (convert ? ISBN_VALIDATOR : ISBN_VALIDATOR_NO_CONVERT);
117 }
118
119 /**
120 * Construct an ISBN validator which converts ISBN-10 codes
121 * to ISBN-13.
122 */
123 public ISBNValidator() {
124 this(true);
125 }
126
127 /**
128 * Construct an ISBN validator indicating whether
129 * ISBN-10 codes should be converted to ISBN-13.
130 *
131 * @param convert <code>true</code> if valid ISBN-10 codes
132 * should be converted to ISBN-13 codes or <code>false</code>
133 * if valid ISBN-10 codes should be returned unchanged.
134 */
135 public ISBNValidator(boolean convert) {
136 this.convert = convert;
137 }
138
139 /**
140 * Check the code is either a valid ISBN-10 or ISBN-13 code.
141 *
142 * @param code The code to validate.
143 * @return <code>true</code> if a valid ISBN-10 or
144 * ISBN-13 code, otherwise <code>false</code>.
145 */
146 public boolean isValid(String code) {
147 return (isValidISBN13(code) || isValidISBN10(code));
148 }
149
150 /**
151 * Check the code is a valid ISBN-10 code.
152 *
153 * @param code The code to validate.
154 * @return <code>true</code> if a valid ISBN-10
155 * code, otherwise <code>false</code>.
156 */
157 public boolean isValidISBN10(String code) {
158 return isbn10Validator.isValid(code);
159 }
160
161 /**
162 * Check the code is a valid ISBN-13 code.
163 *
164 * @param code The code to validate.
165 * @return <code>true</code> if a valid ISBN-13
166 * code, otherwise <code>false</code>.
167 */
168 public boolean isValidISBN13(String code) {
169 return isbn13Validator.isValid(code);
170 }
171
172 /**
173 * Check the code is either a valid ISBN-10 or ISBN-13 code.
174 * <p>
175 * If valid, this method returns the ISBN code with
176 * formatting characters removed (i.e. space or hyphen).
177 * <p>
178 * Converts an ISBN-10 codes to ISBN-13 if
179 * <code>convertToISBN13</code> is <code>true</code>.
180 *
181 * @param code The code to validate.
182 * @return A valid ISBN code if valid, otherwise <code>null</code>.
183 */
184 public String validate(String code) {
185 String result = validateISBN13(code);
186 if (result == null) {
187 result = validateISBN10(code);
188 if (result != null && convert) {
189 result = convertToISBN13(result);
190 }
191 }
192 return result;
193 }
194
195 /**
196 * Check the code is a valid ISBN-10 code.
197 * <p>
198 * If valid, this method returns the ISBN-10 code with
199 * formatting characters removed (i.e. space or hyphen).
200 *
201 * @param code The code to validate.
202 * @return A valid ISBN-10 code if valid,
203 * otherwise <code>null</code>.
204 */
205 public String validateISBN10(String code) {
206 Object result = isbn10Validator.validate(code);
207 return (result == null ? null : result.toString());
208 }
209
210 /**
211 * Check the code is a valid ISBN-13 code.
212 * <p>
213 * If valid, this method returns the ISBN-13 code with
214 * formatting characters removed (i.e. space or hyphen).
215 *
216 * @param code The code to validate.
217 * @return A valid ISBN-13 code if valid,
218 * otherwise <code>null</code>.
219 */
220 public String validateISBN13(String code) {
221 Object result = isbn13Validator.validate(code);
222 return (result == null ? null : result.toString());
223 }
224
225 /**
226 * Convert an ISBN-10 code to an ISBN-13 code.
227 * <p>
228 * This method requires a valid ISBN-10 with NO formatting
229 * characters.
230 *
231 * @param isbn10 The ISBN-10 code to convert
232 * @return A converted ISBN-13 code or <code>null</code>
233 * if the ISBN-10 code is not valid
234 */
235 public String convertToISBN13(String isbn10) {
236
237 if (isbn10 == null) {
238 return null;
239 }
240
241 String input = isbn10.trim();
242 if (input.length() != 10) {
243 throw new IllegalArgumentException("Invalid length " + input.length() + " for '" + input + "'");
244 }
245
246 // Calculate the new ISBN-13 code
247 String isbn13 = "978" + input.substring(0, 9);
248 try {
249 String checkDigit = isbn13Validator.getCheckDigit().calculate(isbn13);
250 isbn13 += checkDigit;
251 return isbn13;
252 } catch (CheckDigitException e) {
253 throw new IllegalArgumentException("Check digit error for '" + input + "' - " + e.getMessage());
254 }
255
256 }
257
258 }