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.checkdigit; 018 019 /** 020 * Abstract <b>Modulus</b> Check digit calculation/validation. 021 * <p> 022 * Provides a <i>base</i> class for building <i>modulus</i> Check 023 * Digit routines. 024 * <p> 025 * This implementation only handles <i>numeric</i> codes, such as 026 * <b>EAN-13</b>. For <i>alphanumeric</i> codes such as <b>EAN-128</b> you 027 * will need to implement/override the <code>toInt()</code> and 028 * <code>toChar()</code> methods. 029 * <p> 030 * 031 * @version $Revision: 594917 $ $Date: 2007-11-14 16:36:40 +0100 (Mi, 14. Nov 2007) $ 032 * @since Validator 1.4 033 */ 034 public abstract class ModulusCheckDigit implements CheckDigit { 035 036 private final int modulus; 037 038 /** 039 * Construct a {@link CheckDigit} routine for a specified modulus. 040 * 041 * @param modulus The modulus value to use for the check digit calculation 042 */ 043 public ModulusCheckDigit(int modulus) { 044 this.modulus = modulus; 045 } 046 047 /** 048 * Return the modulus value this check digit routine is based on. 049 * 050 * @return The modulus value this check digit routine is based on 051 */ 052 public int getModulus() { 053 return modulus; 054 } 055 056 /** 057 * Validate a modulus check digit for a code. 058 * 059 * @param code The code to validate 060 * @return <code>true</code> if the check digit is valid, otherwise 061 * <code>false</code> 062 */ 063 public boolean isValid(String code) { 064 if (code == null || code.length() == 0) { 065 return false; 066 } 067 try { 068 int modulusResult = calculateModulus(code, true); 069 return (modulusResult == 0); 070 } catch (CheckDigitException ex) { 071 return false; 072 } 073 } 074 075 /** 076 * Calculate a modulus <i>Check Digit</i> for a code. 077 * 078 * @param code The code to calculate the Check Digit for 079 * @return The calculated Check Digit 080 * @throws CheckDigitException if an error occurs calculating 081 * the check digit for the specified code 082 */ 083 public String calculate(String code) throws CheckDigitException { 084 if (code == null || code.length() == 0) { 085 throw new CheckDigitException("Code is missing"); 086 } 087 int modulusResult = calculateModulus(code, false); 088 int charValue = (modulus - modulusResult) % modulus; 089 return toCheckDigit(charValue); 090 } 091 092 /** 093 * Calculate the modulus for a code. 094 * 095 * @param code The code to calculate the modulus for. 096 * @param includesCheckDigit Whether the code includes the Check Digit or not. 097 * @return The modulus value 098 * @throws CheckDigitException if an error occurs calculating the modulus 099 * for the specified code 100 */ 101 protected int calculateModulus(String code, boolean includesCheckDigit) throws CheckDigitException { 102 int total = 0; 103 for (int i = 0; i < code.length(); i++) { 104 int lth = code.length() + (includesCheckDigit ? 0 : 1); 105 int leftPos = i + 1; 106 int rightPos = lth - i; 107 int charValue = toInt(code.charAt(i), leftPos, rightPos); 108 total += weightedValue(charValue, leftPos, rightPos); 109 } 110 if (total == 0) { 111 throw new CheckDigitException("Invalid code, sum is zero"); 112 } 113 return (total % modulus); 114 } 115 116 /** 117 * Calculates the <i>weighted</i> value of a character in the 118 * code at a specified position. 119 * <p> 120 * Some modulus routines weight the value of a character 121 * depending on its position in the code (e.g. ISBN-10), while 122 * others use different weighting factors for odd/even positions 123 * (e.g. EAN or Luhn). Implement the appropriate mechanism 124 * required by overriding this method. 125 * 126 * @param charValue The numeric value of the character 127 * @param leftPos The position of the character in the code, counting from left to right 128 * @param rightPos The positionof the character in the code, counting from right to left 129 * @return The weighted value of the character 130 * @throws CheckDigitException if an error occurs calculating 131 * the weighted value 132 */ 133 protected abstract int weightedValue(int charValue, int leftPos, int rightPos) 134 throws CheckDigitException; 135 136 137 /** 138 * Convert a character at a specified position to an integer value. 139 * <p> 140 * <b>Note:</b> this implementation only handlers numeric values 141 * For non-numeric characters, override this method to provide 142 * character-->integer conversion. 143 * 144 * @param character The character to convert 145 * @param leftPos The position of the character in the code, counting from left to right 146 * @param rightPos The positionof the character in the code, counting from right to left 147 * @return The integer value of the character 148 * @throws CheckDigitException if character is non-numeric 149 */ 150 protected int toInt(char character, int leftPos, int rightPos) 151 throws CheckDigitException { 152 if (Character.isDigit(character)) { 153 return Character.getNumericValue(character); 154 } else { 155 throw new CheckDigitException("Invalid Character[" + 156 leftPos + "] = '" + character + "'"); 157 } 158 } 159 160 /** 161 * Convert an integer value to a check digit. 162 * <p> 163 * <b>Note:</b> this implementation only handles numeric values 164 * For non-numeric characters, override this method to provide 165 * integer-->character conversion. 166 * 167 * @param charValue The integer value of the character 168 * @return The converted character 169 * @throws CheckDigitException if integer character value 170 * doesn't represent a numeric character 171 */ 172 protected String toCheckDigit(int charValue) 173 throws CheckDigitException { 174 if (charValue >= 0 && charValue <= 9) { 175 return Integer.toString(charValue); 176 } else { 177 throw new CheckDigitException("Invalid Check Digit Value =" + 178 + charValue); 179 } 180 } 181 182 /** 183 * Add together the individual digits in a number. 184 * 185 * @param number The number whose digits are to be added 186 * @return The sum of the digits 187 */ 188 public static int sumDigits(int number) { 189 int total = 0; 190 int todo = number; 191 while (todo > 0) { 192 total += todo % 10; 193 todo = todo / 10; 194 } 195 return total; 196 } 197 198 }