| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.commons.validator.routines.checkdigit; |
| |
| import java.io.Serializable; |
| |
| /** |
| * Abstract <b>Modulus</b> Check digit calculation/validation. |
| * <p> |
| * Provides a <i>base</i> class for building <i>modulus</i> Check |
| * Digit routines. |
| * <p> |
| * This implementation only handles <i>single-digit numeric</i> codes, such as |
| * <b>EAN-13</b>. For <i>alphanumeric</i> codes such as <b>EAN-128</b> you |
| * will need to implement/override the <code>toInt()</code> and |
| * <code>toChar()</code> methods. |
| * <p> |
| * |
| * @version $Revision$ |
| * @since Validator 1.4 |
| */ |
| public abstract class ModulusCheckDigit implements CheckDigit, Serializable { |
| |
| private static final long serialVersionUID = 2948962251251528941L; |
| |
| // N.B. The modulus can be > 10 provided that the implementing class overrides toCheckDigit and toInt |
| // (for example as in ISBN10CheckDigit) |
| private final int modulus; |
| |
| /** |
| * Construct a {@link CheckDigit} routine for a specified modulus. |
| * |
| * @param modulus The modulus value to use for the check digit calculation |
| */ |
| public ModulusCheckDigit(int modulus) { |
| this.modulus = modulus; |
| } |
| |
| /** |
| * Return the modulus value this check digit routine is based on. |
| * |
| * @return The modulus value this check digit routine is based on |
| */ |
| public int getModulus() { |
| return modulus; |
| } |
| |
| /** |
| * Validate a modulus check digit for a code. |
| * |
| * @param code The code to validate |
| * @return <code>true</code> if the check digit is valid, otherwise |
| * <code>false</code> |
| */ |
| @Override |
| public boolean isValid(String code) { |
| if (code == null || code.isEmpty()) { |
| return false; |
| } |
| try { |
| int modulusResult = calculateModulus(code, true); |
| return (modulusResult == 0); |
| } catch (CheckDigitException ex) { |
| return false; |
| } |
| } |
| |
| /** |
| * Calculate a modulus <i>Check Digit</i> for a code which does not yet have one. |
| * |
| * @param code The code for which to calculate the Check Digit; |
| * the check digit should not be included |
| * @return The calculated Check Digit |
| * @throws CheckDigitException if an error occurs calculating the check digit |
| */ |
| @Override |
| public String calculate(String code) throws CheckDigitException { |
| if (code == null || code.isEmpty()) { |
| throw new CheckDigitException("Code is missing"); |
| } |
| int modulusResult = calculateModulus(code, false); |
| int charValue = (modulus - modulusResult) % modulus; |
| return toCheckDigit(charValue); |
| } |
| |
| /** |
| * Calculate the modulus for a code. |
| * |
| * @param code The code to calculate the modulus for. |
| * @param includesCheckDigit Whether the code includes the Check Digit or not. |
| * @return The modulus value |
| * @throws CheckDigitException if an error occurs calculating the modulus |
| * for the specified code |
| */ |
| protected int calculateModulus(String code, boolean includesCheckDigit) throws CheckDigitException { |
| int total = 0; |
| for (int i = 0; i < code.length(); i++) { |
| int lth = code.length() + (includesCheckDigit ? 0 : 1); |
| int leftPos = i + 1; |
| int rightPos = lth - i; |
| int charValue = toInt(code.charAt(i), leftPos, rightPos); |
| total += weightedValue(charValue, leftPos, rightPos); |
| } |
| if (total == 0) { |
| throw new CheckDigitException("Invalid code, sum is zero"); |
| } |
| return total % modulus; |
| } |
| |
| /** |
| * Calculates the <i>weighted</i> value of a character in the |
| * code at a specified position. |
| * <p> |
| * Some modulus routines weight the value of a character |
| * depending on its position in the code (e.g. ISBN-10), while |
| * others use different weighting factors for odd/even positions |
| * (e.g. EAN or Luhn). Implement the appropriate mechanism |
| * required by overriding this method. |
| * |
| * @param charValue The numeric value of the character |
| * @param leftPos The position of the character in the code, counting from left to right |
| * @param rightPos The positionof the character in the code, counting from right to left |
| * @return The weighted value of the character |
| * @throws CheckDigitException if an error occurs calculating |
| * the weighted value |
| */ |
| protected abstract int weightedValue(int charValue, int leftPos, int rightPos) |
| throws CheckDigitException; |
| |
| |
| /** |
| * Convert a character at a specified position to an integer value. |
| * <p> |
| * <b>Note:</b> this implementation only handlers numeric values |
| * For non-numeric characters, override this method to provide |
| * character-->integer conversion. |
| * |
| * @param character The character to convert |
| * @param leftPos The position of the character in the code, counting from left to right (for identifiying the position in the string) |
| * @param rightPos The position of the character in the code, counting from right to left (not used here) |
| * @return The integer value of the character |
| * @throws CheckDigitException if character is non-numeric |
| */ |
| protected int toInt(char character, int leftPos, int rightPos) |
| throws CheckDigitException { |
| if (Character.isDigit(character)) { |
| return Character.getNumericValue(character); |
| } |
| throw new CheckDigitException("Invalid Character[" + |
| leftPos + "] = '" + character + "'"); |
| } |
| |
| /** |
| * Convert an integer value to a check digit. |
| * <p> |
| * <b>Note:</b> this implementation only handles single-digit numeric values |
| * For non-numeric characters, override this method to provide |
| * integer-->character conversion. |
| * |
| * @param charValue The integer value of the character |
| * @return The converted character |
| * @throws CheckDigitException if integer character value |
| * doesn't represent a numeric character |
| */ |
| protected String toCheckDigit(int charValue) |
| throws CheckDigitException { |
| if (charValue >= 0 && charValue <= 9) { // CHECKSTYLE IGNORE MagicNumber |
| return Integer.toString(charValue); |
| } |
| throw new CheckDigitException("Invalid Check Digit Value =" + |
| + charValue); |
| } |
| |
| /** |
| * Add together the individual digits in a number. |
| * |
| * @param number The number whose digits are to be added |
| * @return The sum of the digits |
| */ |
| public static int sumDigits(int number) { |
| int total = 0; |
| int todo = number; |
| while (todo > 0) { |
| total += todo % 10; // CHECKSTYLE IGNORE MagicNumber |
| todo = todo / 10; // CHECKSTYLE IGNORE MagicNumber |
| } |
| return total; |
| } |
| |
| } |