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 2020/07/25 22:48:57 UTC

[commons-validator] branch master updated: VALIDATOR-471 Allow DomainValidator overrides

This is an automated email from the ASF dual-hosted git repository.

sebb pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-validator.git


The following commit(s) were added to refs/heads/master by this push:
     new 2ca2057  VALIDATOR-471 Allow DomainValidator overrides
2ca2057 is described below

commit 2ca205750ad3e0a02b180369d2a36a39d3103796
Author: Sebb <se...@apache.org>
AuthorDate: Sat Jul 25 23:48:47 2020 +0100

    VALIDATOR-471 Allow DomainValidator overrides
---
 src/changes/changes.xml                            |   6 +-
 .../validator/routines/DomainValidator.java        | 161 ++++++++++++++++++---
 .../routines/DomainValidatorStartupTest.java       |  33 +++++
 3 files changed, 177 insertions(+), 23 deletions(-)

diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index b42fc9d..a136ba9 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -78,6 +78,7 @@ Apache Commons Validator.
 
 
  * CreditCard validation specification by numeric range
+ * DomainValidator instances can be created with local overrides
 
 
  IMPORTANT NOTES
@@ -98,12 +99,15 @@ Apache Commons Validator.
 For the current list of dependencies, please see
 http://commons.apache.org/validator/dependencies.html
   ">
+    <action issue="VALIDATOR-471" type="add" dev="sebb">
+    Allow DomainValidator overrides at run-time
+    </action>
     <action issue="VALIDATOR-438" type="fix" dev="sebb">
     IBANValidator fails for El Salvador
     Add definition
     </action>
     <action type="update" dev="sebb">
-    IANA TLD lists: Updated to Version 2018092800, Last Updated Fri Sep 28 07:07:02 2018 UTC
+    IANA TLD lists: Updated to Version 2020072500, Last Updated Sat Jul 25 07:07:01 2020 UTC
     </action>
     <action issue="VALIDATOR-423" type="add" dev="sebb">
     Add ISINValidator
diff --git a/src/main/java/org/apache/commons/validator/routines/DomainValidator.java b/src/main/java/org/apache/commons/validator/routines/DomainValidator.java
index a9a4988..86ce204 100644
--- a/src/main/java/org/apache/commons/validator/routines/DomainValidator.java
+++ b/src/main/java/org/apache/commons/validator/routines/DomainValidator.java
@@ -19,6 +19,7 @@ package org.apache.commons.validator.routines;
 import java.io.Serializable;
 import java.net.IDN;
 import java.util.Arrays;
+import java.util.List;
 import java.util.Locale;
 
 /**
@@ -90,17 +91,21 @@ public class DomainValidator implements Serializable {
 
     private final boolean allowLocal;
 
-    /**
-     * Singleton instance of this validator, which
-     *  doesn't consider local addresses as valid.
-     */
-    private static final DomainValidator DOMAIN_VALIDATOR = new DomainValidator(false);
+    private static class LazyHolder { // IODH
 
-    /**
-     * Singleton instance of this validator, which does
-     *  consider local addresses valid.
-     */
-    private static final DomainValidator DOMAIN_VALIDATOR_WITH_LOCAL = new DomainValidator(true);
+        /**
+         * Singleton instance of this validator, which
+         *  doesn't consider local addresses as valid.
+         */
+        private static final DomainValidator DOMAIN_VALIDATOR = new DomainValidator(false);
+
+        /**
+         * Singleton instance of this validator, which does
+         *  consider local addresses valid.
+         */
+        private static final DomainValidator DOMAIN_VALIDATOR_WITH_LOCAL = new DomainValidator(true);
+
+    }
 
     /**
      * The above instances must only be returned via the getInstance() methods.
@@ -126,7 +131,7 @@ public class DomainValidator implements Serializable {
      */
     public static synchronized DomainValidator getInstance() {
         inUse = true;
-        return DOMAIN_VALIDATOR;
+        return LazyHolder.DOMAIN_VALIDATOR;
     }
 
     /**
@@ -138,18 +143,90 @@ public class DomainValidator implements Serializable {
     public static synchronized DomainValidator getInstance(boolean allowLocal) {
         inUse = true;
         if(allowLocal) {
-            return DOMAIN_VALIDATOR_WITH_LOCAL;
+            return LazyHolder.DOMAIN_VALIDATOR_WITH_LOCAL;
         }
-        return DOMAIN_VALIDATOR;
+        return LazyHolder.DOMAIN_VALIDATOR;
+    }
+
+    /**
+     * Returns a new instance of this validator.
+     * The user can provide a list of {@link Item} entries which can
+     * be used to override the generic and country code lists.
+     * Note that any such entries override values provided by the
+     * {@link #updateTLDOverride(ArrayType, String[])} method
+     * If an entry for a particular type is not provided, then
+     * the class override (if any) is retained.
+     *
+     * @param allowLocal Should local addresses be considered valid?
+     * @param items - array of {@link Item} entries
+     * @return an instance of this validator
+     * @since 1.7
+     */
+    public static synchronized DomainValidator getInstance(boolean allowLocal, List<Item> items) {
+        inUse = true;
+        return new DomainValidator(allowLocal, items);
     }
 
+    // intance variables allowing local overrides
+    final String[] mycountryCodeTLDsMinus;
+    final String[] mycountryCodeTLDsPlus;
+    final String[] mygenericTLDsPlus;
+    final String[] mygenericTLDsMinus;
+    
     /**
      * Private constructor. 
-     * This does not set the inUse flag - that is done by getInstance.
-     * This is to allow the static shared instances to be created.
     */
-    private DomainValidator(boolean allowLocal) {
+     DomainValidator(boolean allowLocal) {
         this.allowLocal = allowLocal;
+        // link to class overrides
+        mycountryCodeTLDsMinus = countryCodeTLDsMinus;
+        mycountryCodeTLDsPlus = countryCodeTLDsPlus;
+        mygenericTLDsPlus = genericTLDsPlus;
+        mygenericTLDsMinus = genericTLDsMinus;
+    }
+
+    /**
+     * Private constructor, allowing local overrides
+     * @since 1.7
+    */
+    DomainValidator(boolean allowLocal,  List<Item> items) { 
+        this.allowLocal = allowLocal;
+
+        // default to class overrides
+        String[] ccMinus = countryCodeTLDsMinus;
+        String[] ccPlus = countryCodeTLDsPlus;
+        String[] genMinus = genericTLDsMinus;
+        String[] genPlus = genericTLDsPlus;
+
+        // apply the instance overrides
+        for(Item item: items) {
+            switch(item.type) {
+            case COUNTRY_CODE_MINUS: {
+                ccMinus = item.values.clone();
+                break;
+            }
+            case COUNTRY_CODE_PLUS: {
+                ccPlus = item.values.clone();
+                break;
+            }
+            case GENERIC_MINUS: {
+                genMinus = item.values.clone();
+                break;
+            }
+            case GENERIC_PLUS: {
+                genPlus = item.values.clone();
+                break;
+            }
+            default:
+                break;
+            }
+        }
+
+        // init the instance overrides
+        mycountryCodeTLDsMinus = ccMinus;
+        mycountryCodeTLDsPlus = ccPlus;
+        mygenericTLDsMinus = genMinus;
+        mygenericTLDsPlus = genPlus;
     }
 
     /**
@@ -238,8 +315,8 @@ public class DomainValidator implements Serializable {
      */
     public boolean isValidGenericTld(String gTld) {
         final String key = chompLeadingDot(unicodeToASCII(gTld).toLowerCase(Locale.ENGLISH));
-        return (arrayContains(GENERIC_TLDS, key) || arrayContains(genericTLDsPlus, key))
-                && !arrayContains(genericTLDsMinus, key);
+        return (arrayContains(GENERIC_TLDS, key) || arrayContains(mygenericTLDsPlus, key))
+                && !arrayContains(mygenericTLDsMinus, key);
     }
 
     /**
@@ -251,8 +328,8 @@ public class DomainValidator implements Serializable {
      */
     public boolean isValidCountryCodeTld(String ccTld) {
         final String key = chompLeadingDot(unicodeToASCII(ccTld).toLowerCase(Locale.ENGLISH));
-        return (arrayContains(COUNTRY_CODE_TLDS, key) || arrayContains(countryCodeTLDsPlus, key))
-                && !arrayContains(countryCodeTLDsMinus, key);
+        return (arrayContains(COUNTRY_CODE_TLDS, key) || arrayContains(mycountryCodeTLDsPlus, key))
+                && !arrayContains(mycountryCodeTLDsMinus, key);
     }
 
     /**
@@ -1933,7 +2010,20 @@ public class DomainValidator implements Serializable {
         /** Get a copy of the local table */
         LOCAL_RO
         ;
-    };
+    }
+
+    /**
+     * Used to specify overrides when creating a new class.
+     * @since 1.7
+     */
+    public static class Item {
+        final ArrayType type;
+        final String[] values;
+        public Item(ArrayType type, String[] values) {
+            this.type = type;
+            this.values = values; // no need to copy here
+        }
+    }
 
     /**
      * Update one of the TLD override arrays.
@@ -1992,7 +2082,7 @@ public class DomainValidator implements Serializable {
     }
 
     /**
-     * Get a copy of the internal array.
+     * Get a copy of a class level internal array.
      * @param table the array type (any of the enum values)
      * @return a copy of the array
      * @throws IllegalArgumentException if the table type is unexpected (should not happen)
@@ -2032,6 +2122,33 @@ public class DomainValidator implements Serializable {
     }
 
     /**
+     * Get a copy of an instance level internal array.
+     * @param table the array type (any of the enum values)
+     * @return a copy of the array
+     * @throws IllegalArgumentException if the table type is unexpected, e.g. GENERIC_RO
+     * @since 1.7
+     */
+    public String [] getOverrides(ArrayType table) {
+        final String array[];
+        switch(table) {
+        case COUNTRY_CODE_MINUS:
+            array = mycountryCodeTLDsMinus;
+            break;
+        case COUNTRY_CODE_PLUS:
+            array = mycountryCodeTLDsPlus;
+            break;
+        case GENERIC_MINUS:
+            array = mygenericTLDsMinus;
+            break;
+        case GENERIC_PLUS:
+            array = mygenericTLDsPlus;
+            break;
+        default:
+            throw new IllegalArgumentException("Unexpected enum value: " + table);
+        }
+        return Arrays.copyOf(array, array.length); // clone the array
+    }
+    /**
      * Converts potentially Unicode input to punycode.
      * If conversion fails, returns the original input.
      * 
diff --git a/src/test/java/org/apache/commons/validator/routines/DomainValidatorStartupTest.java b/src/test/java/org/apache/commons/validator/routines/DomainValidatorStartupTest.java
index abf6792..40ceb91 100644
--- a/src/test/java/org/apache/commons/validator/routines/DomainValidatorStartupTest.java
+++ b/src/test/java/org/apache/commons/validator/routines/DomainValidatorStartupTest.java
@@ -18,6 +18,9 @@ package org.apache.commons.validator.routines;
 
 import static org.junit.Assert.*;
 
+import java.util.ArrayList;
+import java.util.List;
+
 import org.apache.commons.validator.routines.DomainValidator.ArrayType;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -166,4 +169,34 @@ public class DomainValidatorStartupTest {
             // expected
         }
     }
+
+    @Test
+    public void testInstanceOverride() { // Show that the instance picks up static values
+        DomainValidator.updateTLDOverride(ArrayType.GENERIC_PLUS, new String[]{"gp"});
+        DomainValidator.updateTLDOverride(ArrayType.GENERIC_MINUS, new String[]{"com"});
+        DomainValidator.updateTLDOverride(ArrayType.COUNTRY_CODE_PLUS, new String[]{"cp"});
+        DomainValidator.updateTLDOverride(ArrayType.COUNTRY_CODE_MINUS, new String[]{"ch"});
+        DomainValidator validator = DomainValidator.getInstance(false);
+        assertTrue(validator.isValidGenericTld("gp"));
+        assertFalse(validator.isValidGenericTld("com"));
+        assertTrue(validator.isValidCountryCodeTld("cp"));
+        assertFalse(validator.isValidCountryCodeTld("ch"));
+
+        // show we can override them for a new instance
+        List<DomainValidator.Item> items = new ArrayList<>();
+        items.add(new DomainValidator.Item(ArrayType.GENERIC_MINUS,new String[]{""}));
+        items.add(new DomainValidator.Item(ArrayType.COUNTRY_CODE_MINUS,new String[]{""}));
+        validator = DomainValidator.getInstance(false, items);
+        assertTrue(validator.isValidGenericTld("gp"));
+        assertTrue(validator.isValidGenericTld("com")); // Should be true again
+        assertTrue(validator.isValidCountryCodeTld("cp"));
+        assertTrue(validator.isValidCountryCodeTld("ch")); // Should be true again
+
+        // Show the class overrides are unaffected
+        validator = DomainValidator.getInstance(false);
+        assertTrue(validator.isValidGenericTld("gp"));
+        assertFalse(validator.isValidGenericTld("com"));
+        assertTrue(validator.isValidCountryCodeTld("cp"));
+        assertFalse(validator.isValidCountryCodeTld("ch"));
+    }
 }