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 }