001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.commons.validator.routines;
018
019 import java.io.Serializable;
020 import org.apache.commons.validator.routines.checkdigit.EAN13CheckDigit;
021 import org.apache.commons.validator.routines.checkdigit.ISBN10CheckDigit;
022 import org.apache.commons.validator.routines.checkdigit.CheckDigitException;
023
024 /**
025 * <b>ISBN-10</b> and <b>ISBN-13</b> Code Validation.
026 * <p>
027 * This validator validates the code is either a valid ISBN-10
028 * (using a {@link CodeValidator} with the {@link ISBN10CheckDigit})
029 * or a valid ISBN-13 code (using a {@link CodeValidator} with the
030 * the {@link EAN13CheckDigit} routine).
031 * <p>
032 * The <code>validate()</code> methods return the ISBN code with formatting
033 * characters removed if valid or <code>null</code> if invalid.
034 * <p>
035 * This validator also provides the facility to convert ISBN-10 codes to
036 * ISBN-13 if the <code>convert</code> property is <code>true</code>.
037 * <p>
038 * From 1st January 2007 the book industry will start to use a new 13 digit
039 * ISBN number (rather than this 10 digit ISBN number). ISBN-13 codes are
040 * <a href="http://en.wikipedia.org/wiki/European_Article_Number">EAN</a>
041 * codes, for more information see:</p>
042 *
043 * <ul>
044 * <li><a href="http://en.wikipedia.org/wiki/ISBN">Wikipedia - International
045 * Standard Book Number (ISBN)</a>.</li>
046 * <li>EAN - see
047 * <a href="http://en.wikipedia.org/wiki/European_Article_Number">Wikipedia -
048 * European Article Number</a>.</li>
049 * <li><a href="http://www.isbn.org/standards/home/isbn/transition.asp">ISBN-13
050 * Transition details</a>.</li>
051 * </ul>
052 *
053 * @version $Revision: 594917 $ $Date: 2007-11-14 16:36:40 +0100 (Mi, 14. Nov 2007) $
054 * @since Validator 1.4
055 */
056 public class ISBNValidator implements Serializable {
057
058 private static final String SEP = "(?:\\-|\\s)";
059 private static final String GROUP = "(\\d{1,5})";
060 private static final String PUBLISHER = "(\\d{1,7})";
061 private static final String TITLE = "(\\d{1,6})";
062
063 /**
064 * ISBN-10 consists of 4 groups of numbers separated by either dashes (-)
065 * or spaces. The first group is 1-5 characters, second 1-7, third 1-6,
066 * and fourth is 1 digit or an X.
067 */
068 static final String ISBN10_REGEX =
069 "^(?:(\\d{9}[0-9X])|(?:" + GROUP + SEP + PUBLISHER + SEP + TITLE + SEP + "([0-9X])))$";
070
071 /**
072 * ISBN-13 consists of 5 groups of numbers separated by either dashes (-)
073 * or spaces. The first group is 978 or 979, the second group is
074 * 1-5 characters, third 1-7, fourth 1-6, and fifth is 1 digit.
075 */
076 static final String ISBN13_REGEX =
077 "^(978|979)(?:(\\d{10})|(?:" + SEP + GROUP + SEP + PUBLISHER + SEP + TITLE + SEP + "([0-9])))$";
078
079 /** ISBN Code Validator (which converts ISBN-10 codes to ISBN-13 */
080 private static final ISBNValidator ISBN_VALIDATOR = new ISBNValidator();
081
082 /** ISBN Code Validator (which converts ISBN-10 codes to ISBN-13 */
083 private static final ISBNValidator ISBN_VALIDATOR_NO_CONVERT = new ISBNValidator(false);
084
085
086 /** ISBN-10 Code Validator */
087 private CodeValidator isbn10Validator = new CodeValidator(ISBN10_REGEX, 10, ISBN10CheckDigit.INSTANCE);
088
089 /** ISBN-13 Code Validator */
090 private CodeValidator isbn13Validator = new CodeValidator(ISBN13_REGEX, 13, EAN13CheckDigit.INSTANCE);
091
092 private final boolean convert;
093
094 /**
095 * Return a singleton instance of the ISBN validator which
096 * converts ISBN-10 codes to ISBN-13.
097 *
098 * @return A singleton instance of the ISBN validator.
099 */
100 public static ISBNValidator getInstance() {
101 return ISBN_VALIDATOR;
102 }
103
104 /**
105 * Return a singleton instance of the ISBN validator specifying
106 * whether ISBN-10 codes should be converted to ISBN-13.
107 *
108 * @param convert <code>true</code> if valid ISBN-10 codes
109 * should be converted to ISBN-13 codes or <code>false</code>
110 * if valid ISBN-10 codes should be returned unchanged.
111 * @return A singleton instance of the ISBN validator.
112 */
113 public static ISBNValidator getInstance(boolean convert) {
114 return (convert ? ISBN_VALIDATOR : ISBN_VALIDATOR_NO_CONVERT);
115 }
116
117 /**
118 * Construct an ISBN validator which converts ISBN-10 codes
119 * to ISBN-13.
120 */
121 public ISBNValidator() {
122 this(true);
123 }
124
125 /**
126 * Construct an ISBN validator indicating whether
127 * ISBN-10 codes should be converted to ISBN-13.
128 *
129 * @param convert <code>true</code> if valid ISBN-10 codes
130 * should be converted to ISBN-13 codes or <code>false</code>
131 * if valid ISBN-10 codes should be returned unchanged.
132 */
133 public ISBNValidator(boolean convert) {
134 this.convert = convert;
135 }
136
137 /**
138 * Check the code is either a valid ISBN-10 or ISBN-13 code.
139 *
140 * @param code The code to validate.
141 * @return <code>true</code> if a valid ISBN-10 or
142 * ISBN-13 code, otherwise <code>false</code>.
143 */
144 public boolean isValid(String code) {
145 return (isValidISBN13(code) || isValidISBN10(code));
146 }
147
148 /**
149 * Check the code is a valid ISBN-10 code.
150 *
151 * @param code The code to validate.
152 * @return <code>true</code> if a valid ISBN-10
153 * code, otherwise <code>false</code>.
154 */
155 public boolean isValidISBN10(String code) {
156 return isbn10Validator.isValid(code);
157 }
158
159 /**
160 * Check the code is a valid ISBN-13 code.
161 *
162 * @param code The code to validate.
163 * @return <code>true</code> if a valid ISBN-13
164 * code, otherwise <code>false</code>.
165 */
166 public boolean isValidISBN13(String code) {
167 return isbn13Validator.isValid(code);
168 }
169
170 /**
171 * Check the code is either a valid ISBN-10 or ISBN-13 code.
172 * <p>
173 * If valid, this method returns the ISBN code with
174 * formatting characters removed (i.e. space or hyphen).
175 * <p>
176 * Converts an ISBN-10 codes to ISBN-13 if
177 * <code>convertToISBN13</code> is <code>true</code>.
178 *
179 * @param code The code to validate.
180 * @return A valid ISBN code if valid, otherwise <code>null</code>.
181 */
182 public String validate(String code) {
183 String result = validateISBN13(code);
184 if (result == null) {
185 result = validateISBN10(code);
186 if (result != null && convert) {
187 result = convertToISBN13(result);
188 }
189 }
190 return result;
191 }
192
193 /**
194 * Check the code is a valid ISBN-10 code.
195 * <p>
196 * If valid, this method returns the ISBN-10 code with
197 * formatting characters removed (i.e. space or hyphen).
198 *
199 * @param code The code to validate.
200 * @return A valid ISBN-10 code if valid,
201 * otherwise <code>null</code>.
202 */
203 public String validateISBN10(String code) {
204 Object result = isbn10Validator.validate(code);
205 return (result == null ? null : result.toString());
206 }
207
208 /**
209 * Check the code is a valid ISBN-13 code.
210 * <p>
211 * If valid, this method returns the ISBN-13 code with
212 * formatting characters removed (i.e. space or hyphen).
213 *
214 * @param code The code to validate.
215 * @return A valid ISBN-13 code if valid,
216 * otherwise <code>null</code>.
217 */
218 public String validateISBN13(String code) {
219 Object result = isbn13Validator.validate(code);
220 return (result == null ? null : result.toString());
221 }
222
223 /**
224 * Convert an ISBN-10 code to an ISBN-13 code.
225 * <p>
226 * This method requires a valid ISBN-10 with NO formatting
227 * characters.
228 *
229 * @param isbn10 The ISBN-10 code to convert
230 * @return A converted ISBN-13 code or <code>null</code>
231 * if the ISBN-10 code is not valid
232 */
233 public String convertToISBN13(String isbn10) {
234
235 if (isbn10 == null) {
236 return null;
237 }
238
239 String input = isbn10.trim();
240 if (input.length() != 10) {
241 throw new IllegalArgumentException("Invalid length " + input.length() + " for '" + input + "'");
242 }
243
244 // Calculate the new ISBN-13 code
245 String isbn13 = "978" + input.substring(0, 9);
246 try {
247 String checkDigit = isbn13Validator.getCheckDigit().calculate(isbn13);
248 isbn13 += checkDigit;
249 return isbn13;
250 } catch (CheckDigitException e) {
251 throw new IllegalArgumentException("Check digit error for '" + input + "' - " + e.getMessage());
252 }
253
254 }
255
256 }