/** * This Source Code Form is subject to the terms of the Mozilla Public License, * v. 2.0. If a copy of the MPL was not distributed with this file, You can * obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under * the terms of the Healthcare Disclaimer located at http://openmrs.org/license. * * Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS * graphic logo is a trademark of OpenMRS Inc. */ package org.openmrs.patient.impl; import org.openmrs.patient.UnallowedIdentifierException; /** * The Verhoeff Check Digit Validator catches all single errors and all adjacent transpositions. * See: http://www.cs.utsa.edu/~wagner/laws/verhoeff.html and Wagner, Neal. * "Verhoeff's Decimal Error Detection". The Laws of Cryptography with Java Code. p 54. San Antonio, * TX: 2003. http://www.cs.utsa.edu/~wagner/lawsbookcolor/laws.pdf */ public class VerhoeffIdentifierValidator extends BaseHyphenatedIdentifierValidator { private static final String ALLOWED_CHARS = "0123456789"; private static final String VERHOEFF_NAME = "Verhoeff Check Digit Validator."; private static final int VERHOEFF_ID_LENGTH = 10; private static final int VERHOEFF_UNDECORATED_ID_LENGTH = VERHOEFF_ID_LENGTH - 2; /** * @see org.openmrs.patient.impl.BaseHyphenatedIdentifierValidator#getAllowedCharacters() */ @Override public String getAllowedCharacters() { return ALLOWED_CHARS; } /** * @see org.openmrs.patient.impl.BaseHyphenatedIdentifierValidator#getName() */ @Override public String getName() { return VERHOEFF_NAME; } /** * @see org.openmrs.patient.impl.BaseHyphenatedIdentifierValidator#getCheckDigit(java.lang.String) */ @Override protected int getCheckDigit(String undecoratedIdentifier) { int[] a = getBase(Integer.parseInt(undecoratedIdentifier), undecoratedIdentifier.length()); insertCheck(a); return a[0]; } /** * Override to disallow numeric check digits and identifiers that are not exactly * VERHOEFF_ID_LENGTH long. * * @see org.openmrs.patient.impl.BaseHyphenatedIdentifierValidator#isValid(java.lang.String) */ @Override public boolean isValid(String identifier) throws UnallowedIdentifierException { boolean result = super.isValid(identifier); if (Character.isDigit(identifier.charAt(identifier.length() - 1))) { throw new UnallowedIdentifierException("Check digit can not be numeric."); } if (identifier.length() != VERHOEFF_ID_LENGTH) { throw new UnallowedIdentifierException("Identifier must be " + VERHOEFF_ID_LENGTH + " digits long."); } return result; } /** * Override to disallow identifiers that are not exactly VERHOEFF_UNDECORATED_ID_LENGTH long. * * @see org.openmrs.patient.impl.BaseHyphenatedIdentifierValidator#getValidIdentifier(java.lang.String) * @should get valid identifier */ @Override public String getValidIdentifier(String undecoratedIdentifier) throws UnallowedIdentifierException { String result = super.getValidIdentifier(undecoratedIdentifier); if (undecoratedIdentifier.length() != VERHOEFF_UNDECORATED_ID_LENGTH) { throw new UnallowedIdentifierException("Undecorated identifier must be " + VERHOEFF_UNDECORATED_ID_LENGTH + " digits long."); } return result; } private int[] getBase(int num, int length) { int[] a = new int[length + 1]; int x = 1; for (int i = length; i-- > 0;) { int y = num / x; a[i + 1] = y % 10; x = x * 10; } return a; } private int insertCheck(int[] a) { int check = 0; for (int i = 1; i < a.length; i++) { check = op[check][F[i % 8][a[i]]]; } a[0] = inv[check]; return a[0]; } public VerhoeffIdentifierValidator() { F[0] = F0; F[1] = F1; for (int i = 2; i < 8; i++) { F[i] = new int[10]; for (int j = 0; j < 10; j++) { F[i][j] = F[i - 1][F[1][j]]; } } } private int[][] F = new int[8][]; private static final int[] F0 = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; private static final int[] F1 = { 1, 5, 7, 6, 2, 8, 3, 0, 9, 4 }; private static final int[][] op = { { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, { 1, 2, 3, 4, 0, 6, 7, 8, 9, 5 }, { 2, 3, 4, 0, 1, 7, 8, 9, 5, 6 }, { 3, 4, 0, 1, 2, 8, 9, 5, 6, 7 }, { 4, 0, 1, 2, 3, 9, 5, 6, 7, 8 }, { 5, 9, 8, 7, 6, 0, 4, 3, 2, 1 }, { 6, 5, 9, 8, 7, 1, 0, 4, 3, 2 }, { 7, 6, 5, 9, 8, 2, 1, 0, 4, 3 }, { 8, 7, 6, 5, 9, 3, 2, 1, 0, 4 }, { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 } }; private static final int[] inv = { 0, 4, 3, 2, 1, 5, 6, 7, 8, 9 }; }