/** * The contents of this file are subject to the OpenMRS Public License * Version 1.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://license.openmrs.org * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the * License for the specific language governing rights and limitations * under the License. * * Copyright (C) OpenMRS, LLC. All Rights Reserved. */ package org.openmrs.patient.impl; import org.openmrs.patient.UnallowedIdentifierException; /** * A IdentifierValidator based on the Regenstrief Institute's version of the Luhn Algorithm. */ public class LuhnIdentifierValidator extends BaseHyphenatedIdentifierValidator { private static final String ALLOWED_CHARS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_"; private static final String LUHN_NAME = "Luhn CheckDigit Validator"; @Override protected int getCheckDigit(String undecoratedIdentifier) { // remove leading or trailing whitespace, convert to uppercase String trimmedUppercaseUndecoratedIdentifier = undecoratedIdentifier.trim().toUpperCase(); // this will privatebe a running total int sum = 0; // loop through digits from right to left for (int i = 0; i < trimmedUppercaseUndecoratedIdentifier.length(); i++) { // set ch to "current" character to be processed char ch = trimmedUppercaseUndecoratedIdentifier.charAt(trimmedUppercaseUndecoratedIdentifier.length() - i - 1); // our "digit" is calculated using ASCII value - 48 int digit = (int) ch - 48; // weight will be the current digit's contribution to // the running total int weight; if (i % 2 == 0) { // for alternating digits starting with the rightmost, we // use our formula this is the same as multiplying x 2 and // adding digits together for values 0 to 9. Using the // following formula allows us to gracefully calculate a // weight for non-numeric "digits" as well (from their // ASCII value - 48). weight = (2 * digit) - (int) (digit / 5) * 9; } else { // even-positioned digits just contribute their ascii // value minus 48 weight = digit; } // keep a running total of weights sum += weight; } // avoid sum less than 10 (if characters below "0" allowed, // this could happen) sum = Math.abs(sum) + 10; // check digit is amount needed to reach next number // divisible by ten return (10 - (sum % 10)) % 10; } /** * @see org.openmrs.patient.IdentifierValidator#getName() */ @Override public String getName() { return LUHN_NAME; } /** * @see org.openmrs.patient.IdentifierValidator#getAllowedCharacters() */ @Override public String getAllowedCharacters() { return ALLOWED_CHARS; } /** * @see org.openmrs.patient.IdentifierValidator#getValidIdentifier(java.lang.String) * @should get valid identifier * @should fail with invalid identifiers */ @Override public String getValidIdentifier(String undecoratedIdentifier) throws UnallowedIdentifierException { checkAllowedIdentifier(undecoratedIdentifier); int checkDigit = getCheckDigit(undecoratedIdentifier); String result = undecoratedIdentifier + "-" + checkDigit; return result; } }