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: 1715435 $ 54 * @since Validator 1.4 55 */ 56 public class ISBNValidator implements Serializable { 57 58 private static final int ISBN_10_LEN = 10; 59 60 private static final long serialVersionUID = 4319515687976420405L; 61 62 private static final String SEP = "(?:\\-|\\s)"; 63 private static final String GROUP = "(\\d{1,5})"; 64 private static final String PUBLISHER = "(\\d{1,7})"; 65 private static final String TITLE = "(\\d{1,6})"; 66 67 /** 68 * ISBN-10 consists of 4 groups of numbers separated by either dashes (-) 69 * or spaces. The first group is 1-5 characters, second 1-7, third 1-6, 70 * and fourth is 1 digit or an X. 71 */ 72 static final String ISBN10_REGEX = 73 "^(?:(\\d{9}[0-9X])|(?:" + GROUP + SEP + PUBLISHER + SEP + TITLE + SEP + "([0-9X])))$"; 74 75 /** 76 * ISBN-13 consists of 5 groups of numbers separated by either dashes (-) 77 * or spaces. The first group is 978 or 979, the second group is 78 * 1-5 characters, third 1-7, fourth 1-6, and fifth is 1 digit. 79 */ 80 static final String ISBN13_REGEX = 81 "^(978|979)(?:(\\d{10})|(?:" + SEP + GROUP + SEP + PUBLISHER + SEP + TITLE + SEP + "([0-9])))$"; 82 83 /** ISBN Code Validator (which converts ISBN-10 codes to ISBN-13 */ 84 private static final ISBNValidator ISBN_VALIDATOR = new ISBNValidator(); 85 86 /** ISBN Code Validator (which converts ISBN-10 codes to ISBN-13 */ 87 private static final ISBNValidator ISBN_VALIDATOR_NO_CONVERT = new ISBNValidator(false); 88 89 90 /** ISBN-10 Code Validator */ 91 private final CodeValidator isbn10Validator = new CodeValidator(ISBN10_REGEX, 10, ISBN10CheckDigit.ISBN10_CHECK_DIGIT); 92 93 /** ISBN-13 Code Validator */ 94 private final CodeValidator isbn13Validator = new CodeValidator(ISBN13_REGEX, 13, EAN13CheckDigit.EAN13_CHECK_DIGIT); 95 96 private final boolean convert; 97 98 /** 99 * Return a singleton instance of the ISBN validator which 100 * converts ISBN-10 codes to ISBN-13. 101 * 102 * @return A singleton instance of the ISBN validator. 103 */ 104 public static ISBNValidator getInstance() { 105 return ISBN_VALIDATOR; 106 } 107 108 /** 109 * Return a singleton instance of the ISBN validator specifying 110 * whether ISBN-10 codes should be converted to ISBN-13. 111 * 112 * @param convert <code>true</code> if valid ISBN-10 codes 113 * should be converted to ISBN-13 codes or <code>false</code> 114 * if valid ISBN-10 codes should be returned unchanged. 115 * @return A singleton instance of the ISBN validator. 116 */ 117 public static ISBNValidator getInstance(boolean convert) { 118 return (convert ? ISBN_VALIDATOR : ISBN_VALIDATOR_NO_CONVERT); 119 } 120 121 /** 122 * Construct an ISBN validator which converts ISBN-10 codes 123 * to ISBN-13. 124 */ 125 public ISBNValidator() { 126 this(true); 127 } 128 129 /** 130 * Construct an ISBN validator indicating whether 131 * ISBN-10 codes should be converted to ISBN-13. 132 * 133 * @param convert <code>true</code> if valid ISBN-10 codes 134 * should be converted to ISBN-13 codes or <code>false</code> 135 * if valid ISBN-10 codes should be returned unchanged. 136 */ 137 public ISBNValidator(boolean convert) { 138 this.convert = convert; 139 } 140 141 /** 142 * Check the code is either a valid ISBN-10 or ISBN-13 code. 143 * 144 * @param code The code to validate. 145 * @return <code>true</code> if a valid ISBN-10 or 146 * ISBN-13 code, otherwise <code>false</code>. 147 */ 148 public boolean isValid(String code) { 149 return (isValidISBN13(code) || isValidISBN10(code)); 150 } 151 152 /** 153 * Check the code is a valid ISBN-10 code. 154 * 155 * @param code The code to validate. 156 * @return <code>true</code> if a valid ISBN-10 157 * code, otherwise <code>false</code>. 158 */ 159 public boolean isValidISBN10(String code) { 160 return isbn10Validator.isValid(code); 161 } 162 163 /** 164 * Check the code is a valid ISBN-13 code. 165 * 166 * @param code The code to validate. 167 * @return <code>true</code> if a valid ISBN-13 168 * code, otherwise <code>false</code>. 169 */ 170 public boolean isValidISBN13(String code) { 171 return isbn13Validator.isValid(code); 172 } 173 174 /** 175 * Check the code is either a valid ISBN-10 or ISBN-13 code. 176 * <p> 177 * If valid, this method returns the ISBN code with 178 * formatting characters removed (i.e. space or hyphen). 179 * <p> 180 * Converts an ISBN-10 codes to ISBN-13 if 181 * <code>convertToISBN13</code> is <code>true</code>. 182 * 183 * @param code The code to validate. 184 * @return A valid ISBN code if valid, otherwise <code>null</code>. 185 */ 186 public String validate(String code) { 187 String result = validateISBN13(code); 188 if (result == null) { 189 result = validateISBN10(code); 190 if (result != null && convert) { 191 result = convertToISBN13(result); 192 } 193 } 194 return result; 195 } 196 197 /** 198 * Check the code is a valid ISBN-10 code. 199 * <p> 200 * If valid, this method returns the ISBN-10 code with 201 * formatting characters removed (i.e. space or hyphen). 202 * 203 * @param code The code to validate. 204 * @return A valid ISBN-10 code if valid, 205 * otherwise <code>null</code>. 206 */ 207 public String validateISBN10(String code) { 208 Object result = isbn10Validator.validate(code); 209 return (result == null ? null : result.toString()); 210 } 211 212 /** 213 * Check the code is a valid ISBN-13 code. 214 * <p> 215 * If valid, this method returns the ISBN-13 code with 216 * formatting characters removed (i.e. space or hyphen). 217 * 218 * @param code The code to validate. 219 * @return A valid ISBN-13 code if valid, 220 * otherwise <code>null</code>. 221 */ 222 public String validateISBN13(String code) { 223 Object result = isbn13Validator.validate(code); 224 return (result == null ? null : result.toString()); 225 } 226 227 /** 228 * Convert an ISBN-10 code to an ISBN-13 code. 229 * <p> 230 * This method requires a valid ISBN-10 with NO formatting 231 * characters. 232 * 233 * @param isbn10 The ISBN-10 code to convert 234 * @return A converted ISBN-13 code or <code>null</code> 235 * if the ISBN-10 code is not valid 236 */ 237 public String convertToISBN13(String isbn10) { 238 239 if (isbn10 == null) { 240 return null; 241 } 242 243 String input = isbn10.trim(); 244 if (input.length() != ISBN_10_LEN) { 245 throw new IllegalArgumentException("Invalid length " + input.length() + " for '" + input + "'"); 246 } 247 248 // Calculate the new ISBN-13 code (drop the original checkdigit) 249 String isbn13 = "978" + input.substring(0, ISBN_10_LEN - 1); 250 try { 251 String checkDigit = isbn13Validator.getCheckDigit().calculate(isbn13); 252 isbn13 += checkDigit; 253 return isbn13; 254 } catch (CheckDigitException e) { 255 throw new IllegalArgumentException("Check digit error for '" + input + "' - " + e.getMessage()); 256 } 257 258 } 259 260 }