All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.commons.validator.routines.checkdigit.IsoIec7064PureSystem Maven / Gradle / Ivy

The newest version!
/*
 * 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;

/**
 * Abstract implementation for five check digit calculation/validation defined in the ISO/IEC 7064 standard.
 * 
    *
  • ISO/IEC 7064, MOD 11-2 applies to numeric strings ({@link IsoIecPure11System})
  • *
  • ISO/IEC 7064, MOD 37-2 applies to alphanumeric strings ({@link IsoIecPure37System})
  • *
  • ISO/IEC 7064, MOD 97-10 applies to numeric strings ({@link IsoIecPure97System})
  • *
  • ISO/IEC 7064, MOD 661-26 applies to alphabetic strings ({@link IsoIecPure661System})
  • *
  • ISO/IEC 7064, MOD 1271_36 applies to alphanumeric strings ({@link IsoIecPure1271System})
  • *
*

* Pure system algorithms use a single modulus value {@link #getModulus()} and a radix {@link #getRadix()}, * f.i. the modulus for MOD 11-2 is 11, the radix is 2. * There is also an alternative polynomial implementation for the pure systems, * see {@link IsoIec7064PurePolynomialSystem} and subclasses. *

*

* The standard also defines hybrid systems with two modulus values (see {@link IsoIec7064HybridSystem}). *

* * @author EUG https://github.com/homebeaver * @since 1.10.0 */ public abstract class IsoIec7064PureSystem extends ModulusCheckDigit { private static final long serialVersionUID = 8956070914814659350L; /** * Radix is the second number following “MOD” in the ISO/IEC designation, f.i. 2 for "MOD 11-2" * @return the radix of the Check Digit routine */ protected abstract int getRadix(); /** * MOD 11-2 check characters are “0” to “9” plus “X” for example. * @return a String containing characters the check digit is build from. * This can be {@link IsoIecConstants#NUMERIC}, {@link IsoIecConstants#ALPHABETIC}, {@link IsoIecConstants#ALPHANUMERIC} * , {@link IsoIecConstants#NUMERIC_PLUS_X}, {@link IsoIecConstants#ALPHANUMERIC_PLUS_STAR} */ protected abstract String getCharacterSet(); private final int checkdigitLength; /** * Constructs a modulus Check Digit routine. * @param modulus the first number following “MOD” in the ISO/IEC designation, f.i. 11 for "MOD 11-2" * @param checkdigitLength the check digit length is one or two * @see #getRadix() */ IsoIec7064PureSystem(final int modulus, final int checkdigitLength) { super(modulus); this.checkdigitLength = checkdigitLength; } /** * Returns the lentgth of the check digit for the system * @return checkdigitLength can be one ore two chars */ public int getCheckdigitLength() { return checkdigitLength; } @Override public boolean isValid(final String code) { if (code == null) { return false; } try { if (code.length() < getCheckdigitLength()) { throw new CheckDigitException(CheckDigitException.invalidCode(code, "too short")); } final int cd = toInt(code.substring(code.length() - getCheckdigitLength())); if (cd >= getModulus()) { throw new CheckDigitException(CheckDigitException.invalidCode(code, "check digit = " + cd)); } final int cm = calculateModulus(code, true); return 1 == (cd + cm) % getModulus(); } catch (final CheckDigitException ex) { return false; } } @Override public String calculate(final String code) throws CheckDigitException { if (code == null) { throw new CheckDigitException(CheckDigitException.MISSING_CODE); } final int m = getModulus(); final int cm = calculateModulus(code, false); // now compute what checksum will be congruent to 1 mod M int checksum = (m - cm + 1) % m; return toCheckDigit(checksum); } @Override protected int calculateModulus(final String code, final boolean includesCheckDigit) throws CheckDigitException { final int m = getModulus(); final int r = getRadix(); // process the code int p = 0; int l = includesCheckDigit ? code.length() - getCheckdigitLength() : code.length(); for (int i = 0; i < l; i++) { final int leftPos = i + 1; final int rightPos = l - i; final int charValue = toInt(code.charAt(i), leftPos, rightPos); p = (p + charValue) * r % m; } // if we want a double check digit we perform one additional pass with charValue = 0 if (getCheckdigitLength() == 2) { p = p * r % m; } return p; } @Override protected String toCheckDigit(final int checksum) throws CheckDigitException { String chars = getCharacterSet(); if (getCheckdigitLength() == 2) { int second = checksum % getRadix(); int first = (checksum - second) / getRadix(); return "" + chars.charAt(first) + chars.charAt(second); } else { return "" + chars.charAt(checksum); } } private int toInt(final String character) throws CheckDigitException { //assert character.length() == checkdigitLength; if (character.length() == 1) { int pos = getCharacterSet().indexOf(character); if (pos == -1 ) { throw new CheckDigitException(CheckDigitException.invalidCharacter(character)); } return pos; } // checkdigitLength == 2 int p0 = getCharacterSet().indexOf(character.charAt(0)); if (p0 == -1 ) { throw new CheckDigitException(CheckDigitException.invalidCharacter(character)); } int p1 = getCharacterSet().indexOf(character.charAt(1)); if (p1 == -1 ) { throw new CheckDigitException(CheckDigitException.invalidCharacter(character)); } return p0 * getRadix() + p1; } /** * {@inheritDoc} *

* Overrides to handle numeric, alphabetic or alphanumeric values respectively. *

*/ @Override protected int toInt(final char character, final int leftPos, final int rightPos) throws CheckDigitException { if (getCharacterSet().indexOf(character) == -1) { throw new CheckDigitException(CheckDigitException.invalidCharacter(character, leftPos)); } return Character.getNumericValue(character); } /** * {@inheritDoc} *

* Overrides because there is no need for a weight in ISO/IEC 7064 pure recursive/iterative computation. * However for polynomial calculation this method is used. *

*

* Weights are defined in subclasses for the first fifteen positions only. * The series can be extended indefinitely, using the formula *

     * wi = r^(i - 1) (mod M)
     * 
* where wi is the weight for Position i. *

* NOTE: do not use {@code Math.pow} for high results, f.i. {@code 10^23 (mod 97) = 56}. * Calculate it iteratively instead. *

*/ @Override protected int weightedValue(int charValue, int leftPos, int rightPos) throws CheckDigitException { final int r = getRadix(); double pow = Math.pow(r, rightPos); if (pow > Long.MAX_VALUE) { long p = r; // r^1 for (int i = 1; i < rightPos; i++) { p = p * r; if (p > Integer.MAX_VALUE) { p = p % getModulus(); } } p = p % getModulus(); // System.out.println("weight for " + charValue + " at rightPos=" + rightPos + " is " + p + " =>weightedValue=" + (charValue * p)); return charValue * (int) p; } double wi = Math.pow(r, rightPos) % getModulus(); // System.out.println("weight for " + charValue + " at rightPos=" + rightPos + " is " + wi + " =>weightedValue=" + (charValue * wi)); return charValue * (int) wi; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy