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));
         }


Reply via email to