Created
January 6, 2020 06:37
-
-
Save rodion-m/3ae9898fbac690407c5e6b975fb9f15c to your computer and use it in GitHub Desktop.
EAN13 Check Digit Validator (Java)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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; | |
/** | |
* <b>Check Digit</b> calculation and validation. | |
* <p> | |
* The logic for validating check digits has previously been | |
* embedded within the logic for specific code validation, which | |
* includes other validations such as verifying the format | |
* or length of a code. {@link CheckDigit} provides for separating out | |
* the check digit calculation logic enabling it to be more easily | |
* tested and reused. | |
* </p> | |
* <p> | |
* Although Commons Validator is primarily concerned with validation, | |
* {@link CheckDigit} also defines behaviour for calculating/generating check | |
* digits, since it makes sense that users will want to (re-)use the | |
* same logic for both. The {@link org.apache.commons.validator.routines.ISBNValidator} | |
* makes specific use of this feature by providing the facility to validate ISBN-10 codes | |
* and then convert them to the new ISBN-13 standard. | |
* </p> | |
* <p> | |
* CheckDigit is used by the new generic @link CodeValidator} implementation. | |
* </p> | |
* | |
* <h3>Implementations</h3> | |
* See the | |
* <a href="package-summary.html">Package Summary</a> for a full | |
* list of implementations provided within Commons Validator. | |
* | |
* @see org.apache.commons.validator.routines.CodeValidator | |
* @version $Revision: 1649287 $ | |
* @since Validator 1.4 | |
*/ | |
public interface CheckDigit { | |
/** | |
* Calculates the <i>Check Digit</i> for a code. | |
* | |
* @param code The code to calculate the Check Digit for. | |
* The string must not include the check digit | |
* @return The calculated Check Digit | |
* @throws CheckDigitException if an error occurs. | |
*/ | |
String calculate(String code) throws CheckDigitException; | |
/** | |
* Validates the check digit for the code. | |
* | |
* @param code The code to validate, the string must include the check digit. | |
* @return <code>true</code> if the check digit is valid, otherwise | |
* <code>false</code>. | |
*/ | |
boolean isValid(String code); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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; | |
/** | |
* Check Digit calculation/validation error. | |
* | |
* @version $Revision: 1649191 $ | |
* @since Validator 1.4 | |
*/ | |
public class CheckDigitException extends Exception { | |
private static final long serialVersionUID = -3519894732624685477L; | |
/** | |
* Construct an Exception with no message. | |
*/ | |
public CheckDigitException() { | |
} | |
/** | |
* Construct an Exception with a message. | |
* | |
* @param msg The error message. | |
*/ | |
public CheckDigitException(String msg) { | |
super(msg); | |
} | |
/** | |
* Construct an Exception with a message and | |
* the underlying cause. | |
* | |
* @param msg The error message. | |
* @param cause The underlying cause of the error | |
*/ | |
public CheckDigitException(String msg, Throwable cause) { | |
super(msg, cause); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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; | |
/** | |
* Modulus 10 <b>EAN-13</b> / <b>UPC</b> / <b>ISBN-13</b> Check Digit | |
* calculation/validation. | |
* <p> | |
* Check digit calculation is based on <i>modulus 10</i> with digits in | |
* an <i>odd</i> position (from right to left) being weighted 1 and <i>even</i> | |
* position digits being weighted 3. | |
* <p> | |
* For further information see: | |
* <ul> | |
* <li>EAN-13 - see | |
* <a href="http://en.wikipedia.org/wiki/European_Article_Number">Wikipedia - | |
* European Article Number</a>.</li> | |
* <li>UPC - see | |
* <a href="http://en.wikipedia.org/wiki/Universal_Product_Code">Wikipedia - | |
* Universal Product Code</a>.</li> | |
* <li>ISBN-13 - see | |
* <a href="http://en.wikipedia.org/wiki/ISBN">Wikipedia - International | |
* Standard Book Number (ISBN)</a>.</li> | |
* </ul> | |
* | |
* @version $Revision: 1739356 $ | |
* @since Validator 1.4 | |
*/ | |
public final class EAN13CheckDigit extends ModulusCheckDigit { | |
private static final long serialVersionUID = 1726347093230424107L; | |
/** Singleton EAN-13 Check Digit instance */ | |
public static final CheckDigit EAN13_CHECK_DIGIT = new EAN13CheckDigit(); | |
/** weighting given to digits depending on their right position */ | |
private static final int[] POSITION_WEIGHT = new int[] {3, 1}; | |
/** | |
* Construct a modulus 10 Check Digit routine for EAN/UPC. | |
*/ | |
public EAN13CheckDigit() { | |
super(10); // CHECKSTYLE IGNORE MagicNumber | |
} | |
/** | |
* <p>Calculates the <i>weighted</i> value of a character in the | |
* code at a specified position.</p> | |
* | |
* <p>For EAN-13 (from right to left) <b>odd</b> digits are weighted | |
* with a factor of <b>one</b> and <b>even</b> digits with a factor | |
* of <b>three</b>.</p> | |
* | |
* @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. | |
*/ | |
@Override | |
protected int weightedValue(int charValue, int leftPos, int rightPos) { | |
int weight = POSITION_WEIGHT[rightPos % 2]; | |
return charValue * weight; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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: 1739357 $ | |
* @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.length() == 0) { | |
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.length() == 0) { | |
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; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment