Coverage Report - org.apache.commons.validator.routines.checkdigit.ModulusCheckDigit
 
Classes in this File Line Coverage Branch Coverage Complexity
ModulusCheckDigit
94%
35/37
91%
22/24
3.222
 
 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.checkdigit;
 18  
 
 19  
 import java.io.Serializable;
 20  
 
 21  
 /**
 22  
  * Abstract <b>Modulus</b> Check digit calculation/validation.
 23  
  * <p>
 24  
  * Provides a <i>base</i> class for building <i>modulus</i> Check
 25  
  * Digit routines.
 26  
  * <p>
 27  
  * This implementation only handles <i>single-digit numeric</i> codes, such as
 28  
  * <b>EAN-13</b>. For <i>alphanumeric</i> codes such as <b>EAN-128</b> you
 29  
  * will need to implement/override the <code>toInt()</code> and
 30  
  * <code>toChar()</code> methods.
 31  
  * <p>
 32  
  *
 33  
  * @version $Revision: 1739357 $
 34  
  * @since Validator 1.4
 35  
  */
 36  
 public abstract class ModulusCheckDigit implements CheckDigit, Serializable {
 37  
 
 38  
     private static final long serialVersionUID = 2948962251251528941L;
 39  
 
 40  
     // N.B. The modulus can be > 10 provided that the implementing class overrides toCheckDigit and toInt
 41  
     // (for example as in ISBN10CheckDigit)
 42  
     private final int modulus;
 43  
 
 44  
     /**
 45  
      * Construct a {@link CheckDigit} routine for a specified modulus.
 46  
      *
 47  
      * @param modulus The modulus value to use for the check digit calculation
 48  
      */
 49  46
     public ModulusCheckDigit(int modulus) {
 50  46
         this.modulus = modulus;
 51  46
     }
 52  
 
 53  
     /**
 54  
      * Return the modulus value this check digit routine is based on.
 55  
      *
 56  
      * @return The modulus value this check digit routine is based on
 57  
      */
 58  
     public int getModulus() {
 59  0
         return modulus;
 60  
     }
 61  
 
 62  
     /**
 63  
      * Validate a modulus check digit for a code.
 64  
      *
 65  
      * @param code The code to validate
 66  
      * @return <code>true</code> if the check digit is valid, otherwise
 67  
      * <code>false</code>
 68  
      */
 69  
     @Override
 70  
     public boolean isValid(String code) {
 71  4910
         if (code == null || code.length() == 0) {
 72  16
             return false;
 73  
         }
 74  
         try {
 75  4894
             int modulusResult = calculateModulus(code, true);
 76  1167
             return (modulusResult == 0);
 77  3727
         } catch (CheckDigitException  ex) {
 78  3727
             return false;
 79  
         }
 80  
     }
 81  
 
 82  
     /**
 83  
      * Calculate a modulus <i>Check Digit</i> for a code which does not yet have one.
 84  
      *
 85  
      * @param code The code for which to calculate the Check Digit; 
 86  
      * the check digit should not be included
 87  
      * @return The calculated Check Digit
 88  
      * @throws CheckDigitException if an error occurs calculating the check digit
 89  
      */
 90  
     @Override
 91  
     public String calculate(String code) throws CheckDigitException {
 92  167
         if (code == null || code.length() == 0) {
 93  26
             throw new CheckDigitException("Code is missing");
 94  
         }
 95  141
         int modulusResult = calculateModulus(code, false);
 96  117
         int charValue = (modulus - modulusResult) % modulus;
 97  117
         return toCheckDigit(charValue);
 98  
     }
 99  
 
 100  
     /**
 101  
      * Calculate the modulus for a code.
 102  
      *
 103  
      * @param code The code to calculate the modulus for.
 104  
      * @param includesCheckDigit Whether the code includes the Check Digit or not.
 105  
      * @return The modulus value
 106  
      * @throws CheckDigitException if an error occurs calculating the modulus
 107  
      * for the specified code
 108  
      */
 109  
     protected int calculateModulus(String code, boolean includesCheckDigit) throws CheckDigitException {
 110  4637
         int total = 0;
 111  48549
         for (int i = 0; i < code.length(); i++) {
 112  47238
             int lth = code.length() + (includesCheckDigit ? 0 : 1);
 113  47238
             int leftPos  = i + 1;
 114  47238
             int rightPos = lth - i;
 115  47238
             int charValue = toInt(code.charAt(i), leftPos, rightPos);
 116  43912
             total += weightedValue(charValue, leftPos, rightPos);
 117  
         }
 118  1311
         if (total == 0) {
 119  27
             throw new CheckDigitException("Invalid code, sum is zero");
 120  
         }
 121  1284
         return total % modulus;
 122  
     }
 123  
 
 124  
     /**
 125  
      * Calculates the <i>weighted</i> value of a character in the
 126  
      * code at a specified position.
 127  
      * <p>
 128  
      * Some modulus routines weight the value of a character
 129  
      * depending on its position in the code (e.g. ISBN-10), while
 130  
      * others use different weighting factors for odd/even positions
 131  
      * (e.g. EAN or Luhn). Implement the appropriate mechanism
 132  
      * required by overriding this method.
 133  
      *
 134  
      * @param charValue The numeric value of the character
 135  
      * @param leftPos The position of the character in the code, counting from left to right
 136  
      * @param rightPos The positionof the character in the code, counting from right to left
 137  
      * @return The weighted value of the character
 138  
      * @throws CheckDigitException if an error occurs calculating
 139  
      * the weighted value
 140  
      */
 141  
     protected abstract int weightedValue(int charValue, int leftPos, int rightPos)
 142  
             throws CheckDigitException;
 143  
 
 144  
 
 145  
     /**
 146  
      * Convert a character at a specified position to an integer value.
 147  
      * <p>
 148  
      * <b>Note:</b> this implementation only handlers numeric values
 149  
      * For non-numeric characters, override this method to provide
 150  
      * character--&gt;integer conversion.
 151  
      *
 152  
      * @param character The character to convert
 153  
      * @param leftPos The position of the character in the code, counting from left to right (for identifiying the position in the string)
 154  
      * @param rightPos The position of the character in the code, counting from right to left (not used here)
 155  
      * @return The integer value of the character
 156  
      * @throws CheckDigitException if character is non-numeric
 157  
      */
 158  
     protected int toInt(char character, int leftPos, int rightPos)
 159  
             throws CheckDigitException {
 160  35497
         if (Character.isDigit(character)) {
 161  33032
             return Character.getNumericValue(character);
 162  
         }
 163  2465
         throw new CheckDigitException("Invalid Character[" +
 164  
                 leftPos + "] = '" + character + "'");
 165  
     }
 166  
 
 167  
     /**
 168  
      * Convert an integer value to a check digit.
 169  
      * <p>
 170  
      * <b>Note:</b> this implementation only handles single-digit numeric values
 171  
      * For non-numeric characters, override this method to provide
 172  
      * integer--&gt;character conversion.
 173  
      *
 174  
      * @param charValue The integer value of the character
 175  
      * @return The converted character
 176  
      * @throws CheckDigitException if integer character value
 177  
      * doesn't represent a numeric character
 178  
      */
 179  
     protected String toCheckDigit(int charValue)
 180  
             throws CheckDigitException {
 181  113
         if (charValue >= 0 && charValue <= 9) { // CHECKSTYLE IGNORE MagicNumber
 182  113
             return Integer.toString(charValue);
 183  
         }
 184  0
         throw new CheckDigitException("Invalid Check Digit Value =" +
 185  
                 + charValue);
 186  
     }
 187  
 
 188  
     /**
 189  
      * Add together the individual digits in a number.
 190  
      *
 191  
      * @param number The number whose digits are to be added
 192  
      * @return The sum of the digits
 193  
      */
 194  
     public static int sumDigits(int number) {
 195  7857
         int total = 0;
 196  7857
         int todo = number;
 197  16480
         while (todo > 0) {
 198  8623
             total += todo % 10; // CHECKSTYLE IGNORE MagicNumber
 199  8623
             todo  = todo / 10; // CHECKSTYLE IGNORE MagicNumber
 200  
         }
 201  7857
         return total;
 202  
     }
 203  
 
 204  
 }