You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by se...@apache.org on 2017/02/12 18:27:04 UTC
svn commit: r1782688 - in /commons/proper/validator/trunk/src:
changes/changes.xml
main/java/org/apache/commons/validator/routines/CreditCardValidator.java
test/java/org/apache/commons/validator/routines/CreditCardValidatorTest.java
Author: sebb
Date: Sun Feb 12 18:27:04 2017
New Revision: 1782688
URL: http://svn.apache.org/viewvc?rev=1782688&view=rev
Log:
VALIDATOR-415 Simplify building new CreditCard validators
Modified:
commons/proper/validator/trunk/src/changes/changes.xml
commons/proper/validator/trunk/src/main/java/org/apache/commons/validator/routines/CreditCardValidator.java
commons/proper/validator/trunk/src/test/java/org/apache/commons/validator/routines/CreditCardValidatorTest.java
Modified: commons/proper/validator/trunk/src/changes/changes.xml
URL: http://svn.apache.org/viewvc/commons/proper/validator/trunk/src/changes/changes.xml?rev=1782688&r1=1782687&r2=1782688&view=diff
==============================================================================
--- commons/proper/validator/trunk/src/changes/changes.xml (original)
+++ commons/proper/validator/trunk/src/changes/changes.xml Sun Feb 12 18:27:04 2017
@@ -65,13 +65,21 @@ The <action> type attribute can be add,u
<body>
<release version="1.5.2" date="TBA" description="
-This is a maintenance release.
+This is primarily a maintenance release.
All projects are encouraged to update to this release of
Apache Commons Validator.
Commons Validator requires Java 1.6 or later.
+Main enhancements
+=================
+
+* Modulus Ten Check Digit Implementation
+
+* Generic CreditCard validation (syntax and checkdigit only; does not check IIN)
+
+* CreditCard validation specification by numeric range
IMPORTANT NOTES
===============
@@ -90,6 +98,9 @@ The dependencies for Validator have not
For the current list of dependencies, please see
http://commons.apache.org/validator/dependencies.html
">
+ <action issue="VALIDATOR-415" type="add" dev="sebb">
+ Simplify building new CreditCard validators
+ </action>
<action issue="VALIDATOR-413" type="add" dev="sebb">
Generic CreditCard validation
</action>
Modified: commons/proper/validator/trunk/src/main/java/org/apache/commons/validator/routines/CreditCardValidator.java
URL: http://svn.apache.org/viewvc/commons/proper/validator/trunk/src/main/java/org/apache/commons/validator/routines/CreditCardValidator.java?rev=1782688&r1=1782687&r2=1782688&view=diff
==============================================================================
--- commons/proper/validator/trunk/src/main/java/org/apache/commons/validator/routines/CreditCardValidator.java (original)
+++ commons/proper/validator/trunk/src/main/java/org/apache/commons/validator/routines/CreditCardValidator.java Sun Feb 12 18:27:04 2017
@@ -38,14 +38,40 @@ import java.util.ArrayList;
*
* <p>
* configures the validator to only pass American Express and Visa cards.
- * If a card type is not directly supported by this class, you can implement
- * the CreditCardType interface and pass an instance into the
- * <code>addAllowedCardType</code> method.
+ * If a card type is not directly supported by this class, you can create an
+ * instance of the {@link CodeValidator} class and pass it to a {@link CreditCardValidator}
+ * constructor along with any existing validators. For example:
* </p>
*
+ * <pre>
+ * <code>CreditCardValidator ccv = new CreditCardValidator(
+ * new CodeValidator[] {
+ * CreditCardValidator.AMEX_VALIDATOR,
+ * CreditCardValidator.VISA_VALIDATOR,
+ * new CodeValidator("^(4)(\\d{12,18})$", LUHN_VALIDATOR) // add VPAY
+ * };</code>
+ * </pre>
+ *
+ * <p>
+ * Alternatively you can define a validator using the {@link CreditCardRange} class.
+ * For example:
+ * </p>
+ *
+ * <pre>
+ * <code>CreditCardValidator ccv = new CreditCardValidator(
+ * new CreditCardRange[]{
+ * new CreditCardRange("300", "305", 14, 14), // Diners
+ * new CreditCardRange("3095", null, 14, 14), // Diners
+ * new CreditCardRange("36", null, 14, 14), // Diners
+ * new CreditCardRange("38", "39", 14, 14), // Diners
+ * }
+ * );
+ * </code>
+ * </pre>
+ * <p>
+ * This can be combined with a list of {@code CodeValidator}s
+ * </p>
* <p>
- * For a similar implementation in Perl, reference Sean M. Burke's
- * <a href="http://www.speech.cs.cmu.edu/~sburke/pub/luhn_lib.html">script</a>.
* More information can be found in Michael Gilleland's essay
* <a href="http://web.archive.org/web/20120614072656/http://www.merriampark.com/anatomycc.htm">Anatomy of Credit Card Numbers</a>.
* </p>
@@ -62,6 +88,38 @@ public class CreditCardValidator impleme
private static final int MAX_CC_LENGTH = 19; // maximum allowed length
/**
+ * Class that represents a credit card range.
+ */
+ public static class CreditCardRange {
+ final String low; // e.g. 34 or 644
+ final String high; // e.g. 34 or 65
+ final int minLen; // e.g. 16
+ final int maxLen; // e.g. 19
+
+ /**
+ * Create a credit card range specifier for use in validation
+ * of the number syntax including the IIN range.
+ * <p>
+ * The low and high parameters may be shorter than the length
+ * of an IIN (currently 6 digits) in which case subsequent digits
+ * are ignored and may range from 0-9.
+ * <b>
+ * The low and high parameters may be different lengths.
+ * e.g. Discover "644" and "65".
+ * @param low the low digits of the IIN range
+ * @param high the high digits of the IIN range
+ * @param minLen the minimum length of the entire number
+ * @param maxLen the maximum length of the entire number
+ */
+ public CreditCardRange(String low, String high, int minLen, int maxLen) {
+ this.low = low;
+ this.high = high;
+ this.minLen = minLen;
+ this.maxLen = maxLen;
+ }
+ }
+
+ /**
* Option specifying that no cards are allowed. This is useful if
* you want only custom card types to validate so you turn off the
* default cards with this option.
@@ -256,6 +314,39 @@ public class CreditCardValidator impleme
}
/**
+ * Create a new CreditCardValidator with the specified {@link CreditCardRange}s.
+ * @param creditCardRanges Set of valid code validators
+ * @since 1.5.2
+ */
+ public CreditCardValidator(CreditCardRange[] creditCardRanges) {
+ if (creditCardRanges == null) {
+ throw new IllegalArgumentException("Card ranges are missing");
+ }
+ Collections.addAll(cardTypes, createRangeValidator(creditCardRanges, LUHN_VALIDATOR));
+ }
+
+ /**
+ * Create a new CreditCardValidator with the specified {@link CodeValidator}s
+ * and {@link CreditCardRange}s.
+ * <p>
+ * This can be used to combine predefined validators such as {@link #MASTERCARD_VALIDATOR}
+ * with additional validators using the simpler {@link CreditCardRange}s.
+ * @param creditCardValidators Set of valid code validators
+ * @param creditCardRanges Set of valid code validators
+ * @since 1.5.2
+ */
+ public CreditCardValidator(CodeValidator[] creditCardValidators, CreditCardRange[] creditCardRanges) {
+ if (creditCardValidators == null) {
+ throw new IllegalArgumentException("Card validators are missing");
+ }
+ if (creditCardRanges == null) {
+ throw new IllegalArgumentException("Card ranges are missing");
+ }
+ Collections.addAll(cardTypes, creditCardValidators);
+ Collections.addAll(cardTypes, createRangeValidator(creditCardRanges, LUHN_VALIDATOR));
+ }
+
+ /**
* Create a new generic CreditCardValidator which validates the syntax and check digit only.
* Does not check the Issuer Identification Number (IIN)
*
@@ -328,6 +419,47 @@ public class CreditCardValidator impleme
return null;
}
+
+ // package protected for unit test access
+ static CodeValidator createRangeValidator(final CreditCardRange[] creditCardRanges, final CheckDigit digitCheck ) {
+ return new CodeValidator(
+ // must be numeric (rest of validation is done later)
+ new RegexValidator("(\\d+)") {
+ private static final long serialVersionUID = 1L;
+ private CreditCardRange[] ccr = creditCardRanges.clone();
+ @Override
+ // must return full string
+ public String validate(String value) {
+ if (super.match(value) != null) {
+ int length = value.length();
+ for(CreditCardRange range : ccr) {
+ if (length >= range.minLen && length <= range.maxLen) {
+ if (range.high == null) { // single prefix only
+ if (value.startsWith(range.low)) {
+ return value;
+ }
+ } else if (range.low.compareTo(value) <= 0 // no need to trim value here
+ &&
+ // here we have to ignore digits beyond the prefix
+ range.high.compareTo(value.substring(0, range.high.length())) >= 0) {
+ return value;
+ }
+ }
+ }
+ }
+ return null;
+ }
+ @Override
+ public boolean isValid(String value) {
+ return validate(value) != null;
+ }
+ @Override
+ public String[] match(String value) {
+ return new String[]{validate(value)};
+ }
+ }, digitCheck);
+ }
+
/**
* Tests whether the given flag is on. If the flag is not a power of 2
* (ie. 3) this tests whether the combination of flags is on.
Modified: commons/proper/validator/trunk/src/test/java/org/apache/commons/validator/routines/CreditCardValidatorTest.java
URL: http://svn.apache.org/viewvc/commons/proper/validator/trunk/src/test/java/org/apache/commons/validator/routines/CreditCardValidatorTest.java?rev=1782688&r1=1782687&r2=1782688&view=diff
==============================================================================
--- commons/proper/validator/trunk/src/test/java/org/apache/commons/validator/routines/CreditCardValidatorTest.java (original)
+++ commons/proper/validator/trunk/src/test/java/org/apache/commons/validator/routines/CreditCardValidatorTest.java Sun Feb 12 18:27:04 2017
@@ -18,6 +18,7 @@ package org.apache.commons.validator.rou
import junit.framework.TestCase;
import org.apache.commons.validator.routines.checkdigit.LuhnCheckDigit;
+import org.apache.commons.validator.routines.CreditCardValidator.CreditCardRange;
/**
* Test the CreditCardValidator class.
@@ -555,6 +556,52 @@ public class CreditCardValidatorTest ext
for(String s : VALID_CARDS) {
assertTrue(s, ccv.isValid(s));
}
+ for(String s : ERROR_CARDS) {
+ assertFalse(s, ccv.isValid(s));
+ }
+ }
+
+ public void testRangeGeneratorNoLuhn() {
+ CodeValidator cv = CreditCardValidator.createRangeValidator(
+ new CreditCardRange[]{
+ new CreditCardRange("1",null,6,7),
+ new CreditCardRange("644","65", 8, 8)
+ },
+ null);
+ assertTrue(cv.isValid("1990000"));
+ assertTrue(cv.isValid("199000"));
+ assertFalse(cv.isValid("000000"));
+ assertFalse(cv.isValid("099999"));
+ assertFalse(cv.isValid("200000"));
+
+ assertFalse(cv.isValid("64399999"));
+ assertTrue(cv.isValid("64400000"));
+ assertTrue(cv.isValid("64900000"));
+ assertTrue(cv.isValid("65000000"));
+ assertTrue(cv.isValid("65999999"));
+ assertFalse(cv.isValid("66000000"));
+ }
+
+ public void testRangeGenerator() {
+ CreditCardValidator ccv = new CreditCardValidator(
+ new CodeValidator[] {
+ CreditCardValidator.AMEX_VALIDATOR,
+ CreditCardValidator.VISA_VALIDATOR,
+ CreditCardValidator.MASTERCARD_VALIDATOR,
+ CreditCardValidator.DISCOVER_VALIDATOR,
+ },
+ // Add missing validator
+ new CreditCardRange[]{
+ new CreditCardRange("300", "305", 14, 14), // Diners
+ new CreditCardRange("3095", null, 14, 14), // Diners
+ new CreditCardRange("36", null, 14, 14), // Diners
+ new CreditCardRange("38", "39", 14, 14), // Diners
+ }
+ // we don't have any VPAY examples yet that aren't handled by VISA
+ );
+ for(String s : VALID_CARDS) {
+ assertTrue(s, ccv.isValid(s));
+ }
for(String s : ERROR_CARDS) {
assertFalse(s, ccv.isValid(s));
}