View Javadoc

1   /*
2    * $Id: ISBNValidator.java 280974 2005-09-15 00:06:59Z niallp $
3    * $Rev: 280974 $
4    * $Date: 2005-09-15 01:06:59 +0100 (Thu, 15 Sep 2005) $
5    *
6    * ====================================================================
7    * Copyright 2004-2005 The Apache Software Foundation
8    *
9    * Licensed under the Apache License, Version 2.0 (the "License");
10   * you may not use this file except in compliance with the License.
11   * You may obtain a copy of the License at
12   *
13   *     http://www.apache.org/licenses/LICENSE-2.0
14   *
15   * Unless required by applicable law or agreed to in writing, software
16   * distributed under the License is distributed on an "AS IS" BASIS,
17   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18   * See the License for the specific language governing permissions and
19   * limitations under the License.
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)); // add check digit
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 }