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 }