Index: trunk/test/unit/org/openstreetmap/josm/data/validation/routines/DomainValidatorIT.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/data/validation/routines/DomainValidatorIT.java	(revision 9853)
+++ trunk/test/unit/org/openstreetmap/josm/data/validation/routines/DomainValidatorIT.java	(revision 9853)
@@ -0,0 +1,390 @@
+/*
+ * 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.openstreetmap.josm.data.validation.routines;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.BufferedReader;
+import java.io.Closeable;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.net.HttpURLConnection;
+import java.net.IDN;
+import java.net.URL;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.junit.Test;
+
+/**
+ * Integration tests for the DomainValidator.
+ *
+ * @version $Revision: 1723861 $
+ */
+public class DomainValidatorIT {
+
+    /**
+     * Download and process local copy of http://data.iana.org/TLD/tlds-alpha-by-domain.txt
+     * Check if the internal TLD table is up to date
+     * Check if the internal TLD tables have any spurious entries
+     * @throws Exception if an error occurs
+     */
+    @Test
+    public void testIanaTldList() throws Exception {
+        // Check the arrays first as this affects later checks
+        // Doing this here makes it easier when updating the lists
+        boolean OK = true;
+        for (String list : new String[]{"INFRASTRUCTURE_TLDS", "COUNTRY_CODE_TLDS", "GENERIC_TLDS", "LOCAL_TLDS"}) {
+            OK &= isSortedLowerCase(list);
+        }
+        if (!OK) {
+            System.out.println("Fix arrays before retrying; cannot continue");
+            return;
+        }
+        Set<String> ianaTlds = new HashSet<>(); // keep for comparison with array contents
+        DomainValidator dv = DomainValidator.getInstance();
+        File txtFile = new File(System.getProperty("java.io.tmpdir"), "tlds-alpha-by-domain.txt");
+        long timestamp = download(txtFile, "http://data.iana.org/TLD/tlds-alpha-by-domain.txt", 0L);
+        final File htmlFile = new File(System.getProperty("java.io.tmpdir"), "tlds-alpha-by-domain.html");
+        // N.B. sometimes the html file may be updated a day or so after the txt file
+        // if the txt file contains entries not found in the html file, try again in a day or two
+        download(htmlFile, "http://www.iana.org/domains/root/db", timestamp);
+
+        BufferedReader br = new BufferedReader(new FileReader(txtFile));
+        String line;
+        final String header;
+        line = br.readLine(); // header
+        if (line != null && line.startsWith("# Version ")) {
+            header = line.substring(2);
+        } else {
+            br.close();
+            throw new IOException("File does not have expected Version header");
+        }
+        final boolean generateUnicodeTlds = false; // Change this to generate Unicode TLDs as well
+
+        // Parse html page to get entries
+        Map<String, String[]> htmlInfo = getHtmlInfo(htmlFile);
+        Map<String, String> missingTLD = new TreeMap<>(); // stores entry and comments as String[]
+        Map<String, String> missingCC = new TreeMap<>();
+        while ((line = br.readLine()) != null) {
+            if (!line.startsWith("#")) {
+                final String unicodeTld; // only different from asciiTld if that was punycode
+                final String asciiTld = line.toLowerCase(Locale.ENGLISH);
+                if (line.startsWith("XN--")) {
+                    unicodeTld = IDN.toUnicode(line);
+                } else {
+                    unicodeTld = asciiTld;
+                }
+                if (!dv.isValidTld(asciiTld)) {
+                    String[] info = htmlInfo.get(asciiTld);
+                    if (info != null) {
+                        String type = info[0];
+                        String comment = info[1];
+                        if ("country-code".equals(type)) { // Which list to use?
+                            missingCC.put(asciiTld, unicodeTld + " " + comment);
+                            if (generateUnicodeTlds) {
+                                missingCC.put(unicodeTld, asciiTld + " " + comment);
+                            }
+                        } else {
+                            missingTLD.put(asciiTld, unicodeTld + " " + comment);
+                            if (generateUnicodeTlds) {
+                                missingTLD.put(unicodeTld, asciiTld + " " + comment);
+                            }
+                        }
+                    } else {
+                        System.err.println("Expected to find HTML info for "+ asciiTld);
+                    }
+                }
+                ianaTlds.add(asciiTld);
+                // Don't merge these conditions; generateUnicodeTlds is final so needs to be separate to avoid a warning
+                if (generateUnicodeTlds) {
+                    if (!unicodeTld.equals(asciiTld)) {
+                        ianaTlds.add(unicodeTld);
+                    }
+                }
+            }
+        }
+        br.close();
+        // List html entries not in TLD text list
+        for (String key : (new TreeMap<>(htmlInfo)).keySet()) {
+            if (!ianaTlds.contains(key)) {
+                if (isNotInRootZone(key)) {
+                    System.out.println("INFO: HTML entry not yet in root zone: "+key);
+                } else {
+                    System.err.println("WARN: Expected to find text entry for html: "+key);
+                }
+            }
+        }
+        if (!missingTLD.isEmpty()) {
+            printMap(header, missingTLD, "TLD");
+            fail("missing TLD");
+        }
+        if (!missingCC.isEmpty()) {
+            printMap(header, missingCC, "CC");
+            fail("missing CC");
+        }
+        // Check if internal tables contain any additional entries
+        assertTrue(isInIanaList("INFRASTRUCTURE_TLDS", ianaTlds));
+        assertTrue(isInIanaList("COUNTRY_CODE_TLDS", ianaTlds));
+        assertTrue(isInIanaList("GENERIC_TLDS", ianaTlds));
+        // Don't check local TLDS assertTrue(isInIanaList("LOCAL_TLDS", ianaTlds));
+    }
+
+    private static void printMap(final String header, Map<String, String> map, String string) {
+        System.out.println("Entries missing from "+ string +" List\n");
+        if (header != null) {
+            System.out.println("        // Taken from " + header);
+        }
+        Iterator<Map.Entry<String, String>> it = map.entrySet().iterator();
+        while (it.hasNext()) {
+            Map.Entry<String, String> me = it.next();
+            System.out.println("        \"" + me.getKey() + "\", // " + me.getValue());
+        }
+        System.out.println("\nDone");
+    }
+
+    private static Map<String, String[]> getHtmlInfo(final File f) throws IOException {
+        final Map<String, String[]> info = new HashMap<>();
+
+//        <td><span class="domain tld"><a href="/domains/root/db/ax.html">.ax</a></span></td>
+        final Pattern domain = Pattern.compile(".*<a href=\"/domains/root/db/([^.]+)\\.html");
+//        <td>country-code</td>
+        final Pattern type = Pattern.compile("\\s+<td>([^<]+)</td>");
+//        <!-- <td>Åland Islands<br/><span class="tld-table-so">Ålands landskapsregering</span></td> </td> -->
+//        <td>Ålands landskapsregering</td>
+        final Pattern comment = Pattern.compile("\\s+<td>([^<]+)</td>");
+
+        final BufferedReader br = new BufferedReader(new FileReader(f));
+        String line;
+        while ((line = br.readLine()) != null) {
+            Matcher m = domain.matcher(line);
+            if (m.lookingAt()) {
+                String dom = m.group(1);
+                String typ = "??";
+                String com = "??";
+                line = br.readLine();
+                while (line != null && line.matches("^\\s*$")) { // extra blank lines introduced
+                    line = br.readLine();
+                }
+                Matcher t = type.matcher(line);
+                if (t.lookingAt()) {
+                    typ = t.group(1);
+                    line = br.readLine();
+                    if (line != null && line.matches("\\s+<!--.*")) {
+                        while (line != null && !line.matches(".*-->.*")) {
+                            line = br.readLine();
+                        }
+                        line = br.readLine();
+                    }
+                    // Should have comment; is it wrapped?
+                    while (line != null && !line.matches(".*</td>.*")) {
+                        line += " " +br.readLine();
+                    }
+                    Matcher n = comment.matcher(line);
+                    if (n.lookingAt()) {
+                        com = n.group(1);
+                    }
+                    // Don't save unused entries
+                    if (com.contains("Not assigned") || com.contains("Retired") || typ.equals("test")) {
+//                        System.out.println("Ignored: " + typ + " " + dom + " " +com);
+                    } else {
+                        info.put(dom.toLowerCase(Locale.ENGLISH), new String[]{typ, com});
+//                        System.out.println("Storing: " + typ + " " + dom + " " +com);
+                    }
+                } else {
+                    System.err.println("Unexpected type: " + line);
+                }
+            }
+        }
+        br.close();
+        return info;
+    }
+
+    /*
+     * Download a file if it is more recent than our cached copy.
+     * Unfortunately the server does not seem to honour If-Modified-Since for the
+     * Html page, so we check if it is newer than the txt file and skip download if so
+     */
+    private static long download(File f, String tldurl, long timestamp) throws IOException {
+        final int HOUR = 60*60*1000; // an hour in ms
+        final long modTime;
+        // For testing purposes, don't download files more than once an hour
+        if (f.canRead()) {
+            modTime = f.lastModified();
+            if (modTime > System.currentTimeMillis()-HOUR) {
+                System.out.println("Skipping download - found recent " + f);
+                return modTime;
+            }
+        } else {
+            modTime = 0;
+        }
+        HttpURLConnection hc = (HttpURLConnection) new URL(tldurl).openConnection();
+        if (modTime > 0) {
+            SimpleDateFormat sdf = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z"); //Sun, 06 Nov 1994 08:49:37 GMT
+            String since = sdf.format(new Date(modTime));
+            hc.addRequestProperty("If-Modified-Since", since);
+            System.out.println("Found " + f + " with date " + since);
+        }
+        if (hc.getResponseCode() == 304) {
+            System.out.println("Already have most recent " + tldurl);
+        } else {
+            System.out.println("Downloading " + tldurl);
+            byte[] buff = new byte[1024];
+            InputStream is = hc.getInputStream();
+
+            FileOutputStream fos = new FileOutputStream(f);
+            int len;
+            while ((len = is.read(buff)) != -1) {
+                fos.write(buff, 0, len);
+            }
+            fos.close();
+            is.close();
+            System.out.println("Done");
+        }
+        return f.lastModified();
+    }
+
+    /**
+     * Check whether the domain is in the root zone currently.
+     * Reads the URL http://www.iana.org/domains/root/db/*domain*.html
+     * (using a local disk cache)
+     * and checks for the string "This domain is not present in the root zone at this time."
+     * @param domain the domain to check
+     * @return true if the string is found
+     */
+    private static boolean isNotInRootZone(String domain) {
+        String tldurl = "http://www.iana.org/domains/root/db/" + domain + ".html";
+        BufferedReader in = null;
+        try {
+            File rootCheck = new File(System.getProperty("java.io.tmpdir"), "tld_" + domain + ".html");
+            download(rootCheck, tldurl, 0L);
+            in = new BufferedReader(new FileReader(rootCheck));
+            String inputLine;
+            while ((inputLine = in.readLine()) != null) {
+                if (inputLine.contains("This domain is not present in the root zone at this time.")) {
+                    return true;
+                }
+            }
+            in.close();
+        } catch (IOException e) {
+            e.printStackTrace();
+        } finally {
+            closeQuietly(in);
+        }
+        return false;
+    }
+
+    private static void closeQuietly(Closeable in) {
+        if (in != null) {
+            try {
+                in.close();
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    // isInIanaList and isSorted are split into two methods.
+    // If/when access to the arrays is possible without reflection, the intermediate
+    // methods can be dropped
+    private static boolean isInIanaList(String arrayName, Set<String> ianaTlds) throws Exception {
+        Field f = DomainValidator.class.getDeclaredField(arrayName);
+        final boolean isPrivate = Modifier.isPrivate(f.getModifiers());
+        if (isPrivate) {
+            f.setAccessible(true);
+        }
+        String[] array = (String[]) f.get(null);
+        try {
+            return isInIanaList(arrayName, array, ianaTlds);
+        } finally {
+            if (isPrivate) {
+                f.setAccessible(false);
+            }
+        }
+    }
+
+    private static boolean isInIanaList(String name, String[] array, Set<String> ianaTlds) {
+        boolean ok = true;
+        for (int i = 0; i < array.length; i++) {
+            if (!ianaTlds.contains(array[i])) {
+                System.out.println(name + " contains unexpected value: " + array[i]);
+                ok = false;
+            }
+        }
+        return ok;
+    }
+
+    private static boolean isSortedLowerCase(String arrayName) throws Exception {
+        Field f = DomainValidator.class.getDeclaredField(arrayName);
+        final boolean isPrivate = Modifier.isPrivate(f.getModifiers());
+        if (isPrivate) {
+            f.setAccessible(true);
+        }
+        String[] array = (String[]) f.get(null);
+        try {
+            return isSortedLowerCase(arrayName, array);
+        } finally {
+            if (isPrivate) {
+                f.setAccessible(false);
+            }
+        }
+    }
+
+    private static boolean isLowerCase(String string) {
+        return string.equals(string.toLowerCase(Locale.ENGLISH));
+    }
+
+    // Check if an array is strictly sorted - and lowerCase
+    private static boolean isSortedLowerCase(String name, String[] array) {
+        boolean sorted = true;
+        boolean strictlySorted = true;
+        final int length = array.length;
+        boolean lowerCase = isLowerCase(array[length-1]); // Check the last entry
+        for (int i = 0; i < length-1; i++) { // compare all but last entry with next
+            final String entry = array[i];
+            final String nextEntry = array[i+1];
+            final int cmp = entry.compareTo(nextEntry);
+            if (cmp > 0) { // out of order
+                System.out.println("Out of order entry: " + entry + " < " + nextEntry + " in " + name);
+                sorted = false;
+            } else if (cmp == 0) {
+                strictlySorted = false;
+                System.out.println("Duplicated entry: " + entry + " in " + name);
+            }
+            if (!isLowerCase(entry)) {
+                System.out.println("Non lowerCase entry: " + entry + " in " + name);
+                lowerCase = false;
+            }
+        }
+        return sorted && strictlySorted && lowerCase;
+    }
+}
Index: trunk/test/unit/org/openstreetmap/josm/data/validation/routines/DomainValidatorTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/data/validation/routines/DomainValidatorTest.java	(revision 9853)
+++ trunk/test/unit/org/openstreetmap/josm/data/validation/routines/DomainValidatorTest.java	(revision 9853)
@@ -0,0 +1,510 @@
+/*
+ * 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.openstreetmap.josm.data.validation.routines;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.net.IDN;
+import java.util.Locale;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.validation.routines.DomainValidator.ArrayType;
+
+/**
+ * Tests for the DomainValidator.
+ *
+ * @version $Revision: 1723861 $
+ */
+public class DomainValidatorTest {
+
+    private DomainValidator validator;
+
+    /**
+     * Setup test.
+     */
+    @Before
+    public void setUp() {
+        validator = DomainValidator.getInstance();
+        DomainValidator.clearTLDOverrides(); // N.B. this clears the inUse flag, allowing overrides
+    }
+
+    /**
+     * Test valid domains.
+     */
+    @Test
+    public void testValidDomains() {
+        assertTrue("apache.org should validate", validator.isValid("apache.org"));
+        assertTrue("www.google.com should validate", validator.isValid("www.google.com"));
+
+        assertTrue("test-domain.com should validate", validator.isValid("test-domain.com"));
+        assertTrue("test---domain.com should validate", validator.isValid("test---domain.com"));
+        assertTrue("test-d-o-m-ain.com should validate", validator.isValid("test-d-o-m-ain.com"));
+        assertTrue("two-letter domain label should validate", validator.isValid("as.uk"));
+
+        assertTrue("case-insensitive ApAchE.Org should validate", validator.isValid("ApAchE.Org"));
+
+        assertTrue("single-character domain label should validate", validator.isValid("z.com"));
+
+        assertTrue("i.have.an-example.domain.name should validate", validator.isValid("i.have.an-example.domain.name"));
+    }
+
+    /**
+     * Test invalid domains.
+     */
+    @Test
+    public void testInvalidDomains() {
+        assertFalse("bare TLD .org shouldn't validate", validator.isValid(".org"));
+        assertFalse("domain name with spaces shouldn't validate", validator.isValid(" apache.org "));
+        assertFalse("domain name containing spaces shouldn't validate", validator.isValid("apa che.org"));
+        assertFalse("domain name starting with dash shouldn't validate", validator.isValid("-testdomain.name"));
+        assertFalse("domain name ending with dash shouldn't validate", validator.isValid("testdomain-.name"));
+        assertFalse("domain name starting with multiple dashes shouldn't validate", validator.isValid("---c.com"));
+        assertFalse("domain name ending with multiple dashes shouldn't validate", validator.isValid("c--.com"));
+        assertFalse("domain name with invalid TLD shouldn't validate", validator.isValid("apache.rog"));
+
+        assertFalse("URL shouldn't validate", validator.isValid("http://www.apache.org"));
+        assertFalse("Empty string shouldn't validate as domain name", validator.isValid(" "));
+        assertFalse("Null shouldn't validate as domain name", validator.isValid(null));
+    }
+
+    /**
+     * Test top-level domains.
+     */
+    @Test
+    public void testTopLevelDomains() {
+        // infrastructure TLDs
+        assertTrue(".arpa should validate as iTLD", validator.isValidInfrastructureTld(".arpa"));
+        assertFalse(".com shouldn't validate as iTLD", validator.isValidInfrastructureTld(".com"));
+
+        // generic TLDs
+        assertTrue(".name should validate as gTLD", validator.isValidGenericTld(".name"));
+        assertFalse(".us shouldn't validate as gTLD", validator.isValidGenericTld(".us"));
+
+        // country code TLDs
+        assertTrue(".uk should validate as ccTLD", validator.isValidCountryCodeTld(".uk"));
+        assertFalse(".org shouldn't validate as ccTLD", validator.isValidCountryCodeTld(".org"));
+
+        // case-insensitive
+        assertTrue(".COM should validate as TLD", validator.isValidTld(".COM"));
+        assertTrue(".BiZ should validate as TLD", validator.isValidTld(".BiZ"));
+
+        // corner cases
+        assertFalse("invalid TLD shouldn't validate", validator.isValid(".nope")); // TODO this is not guaranteed invalid forever
+        assertFalse("empty string shouldn't validate as TLD", validator.isValid(""));
+        assertFalse("null shouldn't validate as TLD", validator.isValid(null));
+    }
+
+    /**
+     * Test "allow local" parameter.
+     */
+    @Test
+    public void testAllowLocal() {
+       DomainValidator noLocal = DomainValidator.getInstance(false);
+       DomainValidator allowLocal = DomainValidator.getInstance(true);
+
+       // Default is false, and should use singletons
+       assertEquals(noLocal, validator);
+
+       // Default won't allow local
+       assertFalse("localhost.localdomain should validate", noLocal.isValid("localhost.localdomain"));
+       assertFalse("localhost should validate", noLocal.isValid("localhost"));
+
+       // But it may be requested
+       assertTrue("localhost.localdomain should validate", allowLocal.isValid("localhost.localdomain"));
+       assertTrue("localhost should validate", allowLocal.isValid("localhost"));
+       assertTrue("hostname should validate", allowLocal.isValid("hostname"));
+       assertTrue("machinename should validate", allowLocal.isValid("machinename"));
+
+       // Check the localhost one with a few others
+       assertTrue("apache.org should validate", allowLocal.isValid("apache.org"));
+       assertFalse("domain name with spaces shouldn't validate", allowLocal.isValid(" apache.org "));
+    }
+
+    /**
+     * Test IDN.
+     */
+    @Test
+    public void testIDN() {
+       assertTrue("b\u00fccher.ch in IDN should validate", validator.isValid("www.xn--bcher-kva.ch"));
+    }
+
+    /**
+     * Test IDN with Java >= 6.
+     */
+    @Test
+    public void testIDNJava6OrLater() {
+        String version = System.getProperty("java.version");
+        if (version.compareTo("1.6") < 0) {
+            System.out.println("Cannot run Unicode IDN tests");
+            return; // Cannot run the test
+        } // xn--d1abbgf6aiiy.xn--p1ai http://президент.рф
+       assertTrue("b\u00fccher.ch should validate", validator.isValid("www.b\u00fccher.ch"));
+       assertTrue("xn--d1abbgf6aiiy.xn--p1ai should validate", validator.isValid("xn--d1abbgf6aiiy.xn--p1ai"));
+       assertTrue("президент.рф should validate", validator.isValid("президент.рф"));
+       assertFalse("www.\uFFFD.ch FFFD should fail", validator.isValid("www.\uFFFD.ch"));
+    }
+
+    /**
+     * RFC2396: domainlabel   = alphanum | alphanum *( alphanum | "-" ) alphanum
+     */
+    @Test
+    public void testRFC2396domainlabel() { // use fixed valid TLD
+        assertTrue("a.ch should validate", validator.isValid("a.ch"));
+        assertTrue("9.ch should validate", validator.isValid("9.ch"));
+        assertTrue("az.ch should validate", validator.isValid("az.ch"));
+        assertTrue("09.ch should validate", validator.isValid("09.ch"));
+        assertTrue("9-1.ch should validate", validator.isValid("9-1.ch"));
+        assertFalse("91-.ch should not validate", validator.isValid("91-.ch"));
+        assertFalse("-.ch should not validate", validator.isValid("-.ch"));
+    }
+
+    /**
+     * RFC2396 toplabel = alpha | alpha *( alphanum | "-" ) alphanum
+     */
+    @Test
+    public void testRFC2396toplabel() {
+        // These tests use non-existent TLDs so currently need to use a package protected method
+        assertTrue("a.c (alpha) should validate", validator.isValidDomainSyntax("a.c"));
+        assertTrue("a.cc (alpha alpha) should validate", validator.isValidDomainSyntax("a.cc"));
+        assertTrue("a.c9 (alpha alphanum) should validate", validator.isValidDomainSyntax("a.c9"));
+        assertTrue("a.c-9 (alpha - alphanum) should validate", validator.isValidDomainSyntax("a.c-9"));
+        assertTrue("a.c-z (alpha - alpha) should validate", validator.isValidDomainSyntax("a.c-z"));
+
+        assertFalse("a.9c (alphanum alpha) should fail", validator.isValidDomainSyntax("a.9c"));
+        assertFalse("a.c- (alpha -) should fail", validator.isValidDomainSyntax("a.c-"));
+        assertFalse("a.- (-) should fail", validator.isValidDomainSyntax("a.-"));
+        assertFalse("a.-9 (- alphanum) should fail", validator.isValidDomainSyntax("a.-9"));
+    }
+
+    /**
+     * rfc1123
+     */
+    @Test
+    public void testDomainNoDots() {
+        assertTrue("a (alpha) should validate", validator.isValidDomainSyntax("a"));
+        assertTrue("9 (alphanum) should validate", validator.isValidDomainSyntax("9"));
+        assertTrue("c-z (alpha - alpha) should validate", validator.isValidDomainSyntax("c-z"));
+
+        assertFalse("c- (alpha -) should fail", validator.isValidDomainSyntax("c-"));
+        assertFalse("-c (- alpha) should fail", validator.isValidDomainSyntax("-c"));
+        assertFalse("- (-) should fail", validator.isValidDomainSyntax("-"));
+    }
+
+    /**
+     * Non-regression test for VALIDATOR-297
+     */
+    @Test
+    public void testValidator297() {
+        assertTrue("xn--d1abbgf6aiiy.xn--p1ai should validate", validator.isValid("xn--d1abbgf6aiiy.xn--p1ai")); // This uses a valid TLD
+     }
+
+    /**
+     * Non-regression test for VALIDATOR-306
+     * labels are a max of 63 chars and domains 253
+     */
+    @Test
+    public void testValidator306() {
+        final String longString = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz0123456789A";
+        assertEquals(63, longString.length()); // 26 * 2 + 11
+
+        assertTrue("63 chars label should validate", validator.isValidDomainSyntax(longString+".com"));
+        assertFalse("64 chars label should fail", validator.isValidDomainSyntax(longString+"x.com"));
+
+        assertTrue("63 chars TLD should validate", validator.isValidDomainSyntax("test."+longString));
+        assertFalse("64 chars TLD should fail", validator.isValidDomainSyntax("test.x"+longString));
+
+        final String longDomain =
+                longString
+                + "." + longString
+                + "." + longString
+                + "." + longString.substring(0, 61);
+        assertEquals(253, longDomain.length());
+        assertTrue("253 chars domain should validate", validator.isValidDomainSyntax(longDomain));
+        assertFalse("254 chars domain should fail", validator.isValidDomainSyntax(longDomain+"x"));
+    }
+
+    /**
+     *  Check that IDN.toASCII behaves as it should (when wrapped by DomainValidator.unicodeToASCII)
+     *  Tests show that method incorrectly trims a trailing "." character
+     */
+    @Test
+    public void testUnicodeToASCII() {
+        String[] asciidots = {
+                "",
+                ",",
+                ".", // fails IDN.toASCII, but should pass wrapped version
+                "a.", // ditto
+                "a.b",
+                "a..b",
+                "a...b",
+                ".a",
+                "..a",
+        };
+        for (String s : asciidots) {
+            assertEquals(s, DomainValidator.unicodeToASCII(s));
+        }
+        // RFC3490 3.1. 1)
+//      Whenever dots are used as label separators, the following
+//      characters MUST be recognized as dots: U+002E (full stop), U+3002
+//      (ideographic full stop), U+FF0E (fullwidth full stop), U+FF61
+//      (halfwidth ideographic full stop).
+        final String[][] otherDots = {
+                {"b\u3002", "b."},
+                {"b\uFF0E", "b."},
+                {"b\uFF61", "b."},
+                {"\u3002", "."},
+                {"\uFF0E", "."},
+                {"\uFF61", "."},
+        };
+        for (String[] s : otherDots) {
+            assertEquals(s[1], DomainValidator.unicodeToASCII(s[0]));
+        }
+    }
+
+    /**
+     * Check if IDN.toASCII is broken or not
+     */
+    @Test
+    public void testIsIDNtoASCIIBroken() {
+        System.out.println(">>DomainValidatorTest.testIsIDNtoASCIIBroken()");
+        final String input = ".";
+        final boolean ok = input.equals(IDN.toASCII(input));
+        System.out.println("IDN.toASCII is " + (ok ? "OK" : "BROKEN"));
+        String[] props = {
+        "java.version", //    Java Runtime Environment version
+        "java.vendor", // Java Runtime Environment vendor
+        "java.vm.specification.version", //   Java Virtual Machine specification version
+        "java.vm.specification.vendor", //    Java Virtual Machine specification vendor
+        "java.vm.specification.name", //  Java Virtual Machine specification name
+        "java.vm.version", // Java Virtual Machine implementation version
+        "java.vm.vendor", //  Java Virtual Machine implementation vendor
+        "java.vm.name", //    Java Virtual Machine implementation name
+        "java.specification.version", //  Java Runtime Environment specification version
+        "java.specification.vendor", //   Java Runtime Environment specification vendor
+        "java.specification.name", // Java Runtime Environment specification name
+        "java.class.version", //  Java class format version number
+        };
+        for (String t : props) {
+            System.out.println(t + "=" + System.getProperty(t));
+        }
+        System.out.println("<<DomainValidatorTest.testIsIDNtoASCIIBroken()");
+    }
+
+    /**
+     * Check array is sorted and is lower-case
+     * @throws Exception if an error occurs
+     */
+    @Test
+    public void test_INFRASTRUCTURE_TLDS_sortedAndLowerCase() throws Exception {
+        final boolean sorted = isSortedLowerCase("INFRASTRUCTURE_TLDS");
+        assertTrue(sorted);
+    }
+
+    /**
+     * Check array is sorted and is lower-case
+     * @throws Exception if an error occurs
+     */
+    @Test
+    public void test_COUNTRY_CODE_TLDS_sortedAndLowerCase() throws Exception {
+        final boolean sorted = isSortedLowerCase("COUNTRY_CODE_TLDS");
+        assertTrue(sorted);
+    }
+
+    /**
+     * Check array is sorted and is lower-case
+     * @throws Exception if an error occurs
+     */
+    @Test
+    public void test_GENERIC_TLDS_sortedAndLowerCase() throws Exception {
+        final boolean sorted = isSortedLowerCase("GENERIC_TLDS");
+        assertTrue(sorted);
+    }
+
+    /**
+     * Check array is sorted and is lower-case
+     * @throws Exception if an error occurs
+     */
+    @Test
+    public void test_LOCAL_TLDS_sortedAndLowerCase() throws Exception {
+        final boolean sorted = isSortedLowerCase("LOCAL_TLDS");
+        assertTrue(sorted);
+    }
+
+    /**
+     * Test enum visibility
+     */
+    @Test
+    public void testEnumIsPublic() {
+        assertTrue(Modifier.isPublic(DomainValidator.ArrayType.class.getModifiers()));
+    }
+
+    /**
+     * Test update base arrays
+     */
+    @Test
+    public void testUpdateBaseArrays() {
+        try {
+            DomainValidator.updateTLDOverride(ArrayType.COUNTRY_CODE_RO, new String[]{"com"});
+            fail("Expected IllegalArgumentException");
+        } catch (IllegalArgumentException iae) {
+            // expected
+            Main.debug(iae.getMessage());
+        }
+        try {
+            DomainValidator.updateTLDOverride(ArrayType.GENERIC_RO, new String[]{"com"});
+            fail("Expected IllegalArgumentException");
+        } catch (IllegalArgumentException iae) {
+            // expected
+            Main.debug(iae.getMessage());
+        }
+        try {
+            DomainValidator.updateTLDOverride(ArrayType.INFRASTRUCTURE_RO, new String[]{"com"});
+            fail("Expected IllegalArgumentException");
+        } catch (IllegalArgumentException iae) {
+            // expected
+            Main.debug(iae.getMessage());
+        }
+        try {
+            DomainValidator.updateTLDOverride(ArrayType.LOCAL_RO, new String[]{"com"});
+            fail("Expected IllegalArgumentException");
+        } catch (IllegalArgumentException iae) {
+            // expected
+            Main.debug(iae.getMessage());
+        }
+    }
+
+    /**
+     * Test get array.
+     */
+    @Test
+    public void testGetArray() {
+        assertNotNull(DomainValidator.getTLDEntries(ArrayType.COUNTRY_CODE_MINUS));
+        assertNotNull(DomainValidator.getTLDEntries(ArrayType.COUNTRY_CODE_PLUS));
+        assertNotNull(DomainValidator.getTLDEntries(ArrayType.GENERIC_MINUS));
+        assertNotNull(DomainValidator.getTLDEntries(ArrayType.GENERIC_PLUS));
+        assertNotNull(DomainValidator.getTLDEntries(ArrayType.COUNTRY_CODE_RO));
+        assertNotNull(DomainValidator.getTLDEntries(ArrayType.GENERIC_RO));
+        assertNotNull(DomainValidator.getTLDEntries(ArrayType.INFRASTRUCTURE_RO));
+        assertNotNull(DomainValidator.getTLDEntries(ArrayType.LOCAL_RO));
+    }
+
+    /**
+     * Test update country code.
+     */
+    @Test
+    public void testUpdateCountryCode() {
+        assertFalse(validator.isValidCountryCodeTld("com")); // cannot be valid
+        DomainValidator.updateTLDOverride(ArrayType.COUNTRY_CODE_PLUS, new String[]{"com"});
+        assertTrue(validator.isValidCountryCodeTld("com")); // it is now!
+        DomainValidator.updateTLDOverride(ArrayType.COUNTRY_CODE_MINUS, new String[]{"com"});
+        assertFalse(validator.isValidCountryCodeTld("com")); // show that minus overrides the rest
+
+        assertTrue(validator.isValidCountryCodeTld("ch"));
+        DomainValidator.updateTLDOverride(ArrayType.COUNTRY_CODE_MINUS, new String[]{"ch"});
+        assertFalse(validator.isValidCountryCodeTld("ch"));
+        DomainValidator.updateTLDOverride(ArrayType.COUNTRY_CODE_MINUS, new String[]{"xx"});
+        assertTrue(validator.isValidCountryCodeTld("ch"));
+    }
+
+    /**
+     * Test update generic.
+     */
+    @Test
+    public void testUpdateGeneric() {
+        assertFalse(validator.isValidGenericTld("ch")); // cannot be valid
+        DomainValidator.updateTLDOverride(ArrayType.GENERIC_PLUS, new String[]{"ch"});
+        assertTrue(validator.isValidGenericTld("ch")); // it is now!
+        DomainValidator.updateTLDOverride(ArrayType.GENERIC_MINUS, new String[]{"ch"});
+        assertFalse(validator.isValidGenericTld("ch")); // show that minus overrides the rest
+
+        assertTrue(validator.isValidGenericTld("com"));
+        DomainValidator.updateTLDOverride(ArrayType.GENERIC_MINUS, new String[]{"com"});
+        assertFalse(validator.isValidGenericTld("com"));
+        DomainValidator.updateTLDOverride(ArrayType.GENERIC_MINUS, new String[]{"xx"}); // change the minus list
+        assertTrue(validator.isValidGenericTld("com"));
+    }
+
+    /**
+     * Test cannot update.
+     */
+    @Test
+    public void testCannotUpdate() {
+        DomainValidator.updateTLDOverride(ArrayType.GENERIC_PLUS, new String[]{"ch"}); // OK
+        DomainValidator dv = DomainValidator.getInstance();
+        assertNotNull(dv);
+        try {
+            DomainValidator.updateTLDOverride(ArrayType.GENERIC_PLUS, new String[]{"ch"});
+            fail("Expected IllegalStateException");
+        } catch (IllegalStateException ise) {
+            // expected
+            Main.debug(ise.getMessage());
+        }
+    }
+
+    private static boolean isSortedLowerCase(String arrayName) throws Exception {
+        Field f = DomainValidator.class.getDeclaredField(arrayName);
+        final boolean isPrivate = Modifier.isPrivate(f.getModifiers());
+        if (isPrivate) {
+            f.setAccessible(true);
+        }
+        String[] array = (String[]) f.get(null);
+        try {
+            return isSortedLowerCase(arrayName, array);
+        } finally {
+            if (isPrivate) {
+                f.setAccessible(false);
+            }
+        }
+    }
+
+    private static boolean isLowerCase(String string) {
+        return string.equals(string.toLowerCase(Locale.ENGLISH));
+    }
+
+    // Check if an array is strictly sorted - and lowerCase
+    private static boolean isSortedLowerCase(String name, String[] array) {
+        boolean sorted = true;
+        boolean strictlySorted = true;
+        final int length = array.length;
+        boolean lowerCase = isLowerCase(array[length-1]); // Check the last entry
+        for (int i = 0; i < length-1; i++) { // compare all but last entry with next
+            final String entry = array[i];
+            final String nextEntry = array[i+1];
+            final int cmp = entry.compareTo(nextEntry);
+            if (cmp > 0) { // out of order
+                System.out.println("Out of order entry: " + entry + " < " + nextEntry + " in " + name);
+                sorted = false;
+            } else if (cmp == 0) {
+                strictlySorted = false;
+                System.out.println("Duplicated entry: " + entry + " in " + name);
+            }
+            if (!isLowerCase(entry)) {
+                System.out.println("Non lowerCase entry: " + entry + " in " + name);
+                lowerCase = false;
+            }
+        }
+        return sorted && strictlySorted && lowerCase;
+    }
+}
Index: trunk/test/unit/org/openstreetmap/josm/data/validation/routines/EmailValidatorTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/data/validation/routines/EmailValidatorTest.java	(revision 9853)
+++ trunk/test/unit/org/openstreetmap/josm/data/validation/routines/EmailValidatorTest.java	(revision 9853)
@@ -0,0 +1,576 @@
+/*
+ * 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.openstreetmap.josm.data.validation.routines;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+
+/**
+ * Performs Validation Test for e-mail validations.
+ *
+ *
+ * @version $Revision: 1723573 $
+ */
+public class EmailValidatorTest {
+
+    /**
+     * The key used to retrieve the set of validation
+     * rules from the xml file.
+     */
+    protected static String FORM_KEY = "emailForm";
+
+    /**
+     * The key used to retrieve the validator action.
+     */
+    protected static String ACTION = "email";
+
+    private EmailValidator validator;
+
+    /**
+     * Setup
+     */
+    @Before
+    public void setUp() {
+        validator = EmailValidator.getInstance();
+    }
+
+    /**
+     * Tests the e-mail validation.
+     */
+    @Test
+    public void testEmail()  {
+        assertTrue(validator.isValid("jsmith@apache.org"));
+    }
+
+    /**
+     * Tests the email validation with numeric domains.
+     */
+    @Test
+    public void testEmailWithNumericAddress()  {
+        assertTrue(validator.isValid("someone@[216.109.118.76]"));
+        assertTrue(validator.isValid("someone@yahoo.com"));
+    }
+
+    /**
+     * Tests the e-mail validation.
+     */
+    @Test
+    public void testEmailExtension()  {
+        assertTrue(validator.isValid("jsmith@apache.org"));
+
+        assertTrue(validator.isValid("jsmith@apache.com"));
+
+        assertTrue(validator.isValid("jsmith@apache.net"));
+
+        assertTrue(validator.isValid("jsmith@apache.info"));
+
+        assertFalse(validator.isValid("jsmith@apache."));
+
+        assertFalse(validator.isValid("jsmith@apache.c"));
+
+        assertTrue(validator.isValid("someone@yahoo.museum"));
+
+        assertFalse(validator.isValid("someone@yahoo.mu-seum"));
+    }
+
+    /**
+     * Tests the e-mail validation with a dash in
+     * the address.
+     */
+    @Test
+    public void testEmailWithDash()  {
+        assertTrue(validator.isValid("andy.noble@data-workshop.com"));
+
+        assertFalse(validator.isValid("andy-noble@data-workshop.-com"));
+
+        assertFalse(validator.isValid("andy-noble@data-workshop.c-om"));
+
+        assertFalse(validator.isValid("andy-noble@data-workshop.co-m"));
+    }
+
+    /**
+     * Tests the e-mail validation with a dot at the end of
+     * the address.
+     */
+    @Test
+    public void testEmailWithDotEnd()  {
+        assertFalse(validator.isValid("andy.noble@data-workshop.com."));
+    }
+
+    /**
+     * Tests the e-mail validation with an RCS-noncompliant character in
+     * the address.
+     */
+    @Test
+    public void testEmailWithBogusCharacter()  {
+
+        assertFalse(validator.isValid("andy.noble@\u008fdata-workshop.com"));
+
+        // The ' character is valid in an email username.
+        assertTrue(validator.isValid("andy.o'reilly@data-workshop.com"));
+
+        // But not in the domain name.
+        assertFalse(validator.isValid("andy@o'reilly.data-workshop.com"));
+
+        // The + character is valid in an email username.
+        assertTrue(validator.isValid("foo+bar@i.am.not.in.us.example.com"));
+
+        // But not in the domain name
+        assertFalse(validator.isValid("foo+bar@example+3.com"));
+
+        // Domains with only special characters aren't allowed (VALIDATOR-286)
+        assertFalse(validator.isValid("test@%*.com"));
+        assertFalse(validator.isValid("test@^&#.com"));
+    }
+
+    /**
+     * Non-regression test for VALIDATOR-315
+     */
+    @Test
+    public void testVALIDATOR_315() {
+        assertFalse(validator.isValid("me@at&t.net"));
+        assertTrue(validator.isValid("me@att.net")); // Make sure TLD is not the cause of the failure
+    }
+
+    /**
+     * Non-regression test for VALIDATOR-278
+     */
+    @Test
+    public void testVALIDATOR_278() {
+        assertFalse(validator.isValid("someone@-test.com")); // hostname starts with dash/hyphen
+        assertFalse(validator.isValid("someone@test-.com")); // hostname ends with dash/hyphen
+    }
+
+    /**
+     * Non-regression test for VALIDATOR-235
+     */
+    @Test
+    public void testValidator235() {
+        String version = System.getProperty("java.version");
+        if (version.compareTo("1.6") < 0) {
+            System.out.println("Cannot run Unicode IDN tests");
+            return; // Cannot run the test
+        }
+        assertTrue("xn--d1abbgf6aiiy.xn--p1ai should validate", validator.isValid("someone@xn--d1abbgf6aiiy.xn--p1ai"));
+        assertTrue("президент.рф should validate", validator.isValid("someone@президент.рф"));
+        assertTrue("www.b\u00fccher.ch should validate", validator.isValid("someone@www.b\u00fccher.ch"));
+        assertFalse("www.\uFFFD.ch FFFD should fail", validator.isValid("someone@www.\uFFFD.ch"));
+        assertTrue("www.b\u00fccher.ch should validate", validator.isValid("someone@www.b\u00fccher.ch"));
+        assertFalse("www.\uFFFD.ch FFFD should fail", validator.isValid("someone@www.\uFFFD.ch"));
+    }
+
+    /**
+     * Tests the email validation with commas.
+     */
+    @Test
+    public void testEmailWithCommas()  {
+        assertFalse(validator.isValid("joeblow@apa,che.org"));
+
+        assertFalse(validator.isValid("joeblow@apache.o,rg"));
+
+        assertFalse(validator.isValid("joeblow@apache,org"));
+    }
+
+    /**
+     * Tests the email validation with spaces.
+     */
+    @Test
+    public void testEmailWithSpaces()  {
+        assertFalse(validator.isValid("joeblow @apache.org")); // TODO - this should be valid?
+
+        assertFalse(validator.isValid("joeblow@ apache.org"));
+
+        assertTrue(validator.isValid(" joeblow@apache.org")); // TODO - this should be valid?
+
+        assertTrue(validator.isValid("joeblow@apache.org "));
+
+        assertFalse(validator.isValid("joe blow@apache.org "));
+
+        assertFalse(validator.isValid("joeblow@apa che.org "));
+    }
+
+    /**
+     * Tests the email validation with ascii control characters.
+     * (i.e. Ascii chars 0 - 31 and 127)
+     */
+    @Test
+    public void testEmailWithControlChars()  {
+        for (char c = 0; c < 32; c++) {
+            assertFalse("Test control char " + ((int) c), validator.isValid("foo" + c + "bar@domain.com"));
+        }
+        assertFalse("Test control char 127", validator.isValid("foo" + ((char) 127) + "bar@domain.com"));
+    }
+
+    /**
+     * Test that @localhost and @localhost.localdomain
+     *  addresses are declared as valid when requested.
+     */
+    @Test
+    public void testEmailLocalhost() {
+       // Check the default is not to allow
+       EmailValidator noLocal = EmailValidator.getInstance(false);
+       EmailValidator allowLocal = EmailValidator.getInstance(true);
+       assertEquals(validator, noLocal);
+
+       // Depends on the validator
+       assertTrue(
+             "@localhost.localdomain should be accepted but wasn't",
+             allowLocal.isValid("joe@localhost.localdomain")
+       );
+       assertTrue(
+             "@localhost should be accepted but wasn't",
+             allowLocal.isValid("joe@localhost")
+       );
+
+       assertFalse(
+             "@localhost.localdomain should be accepted but wasn't",
+             noLocal.isValid("joe@localhost.localdomain")
+       );
+       assertFalse(
+             "@localhost should be accepted but wasn't",
+             noLocal.isValid("joe@localhost")
+       );
+    }
+
+    /**
+     * VALIDATOR-296 - A / or a ! is valid in the user part,
+     *  but not in the domain part
+     */
+    @Test
+    public void testEmailWithSlashes() {
+       assertTrue(
+             "/ and ! valid in username",
+             validator.isValid("joe!/blow@apache.org")
+       );
+       assertFalse(
+             "/ not valid in domain",
+             validator.isValid("joe@ap/ache.org")
+       );
+       assertFalse(
+             "! not valid in domain",
+             validator.isValid("joe@apac!he.org")
+       );
+    }
+
+    /**
+     * Write this test according to parts of RFC, as opposed to the type of character
+     * that is being tested.
+     */
+    @Test
+    public void testEmailUserName()  {
+
+        assertTrue(validator.isValid("joe1blow@apache.org"));
+
+        assertTrue(validator.isValid("joe$blow@apache.org"));
+
+        assertTrue(validator.isValid("joe-@apache.org"));
+
+        assertTrue(validator.isValid("joe_@apache.org"));
+
+        assertTrue(validator.isValid("joe+@apache.org")); // + is valid unquoted
+
+        assertTrue(validator.isValid("joe!@apache.org")); // ! is valid unquoted
+
+        assertTrue(validator.isValid("joe*@apache.org")); // * is valid unquoted
+
+        assertTrue(validator.isValid("joe'@apache.org")); // ' is valid unquoted
+
+        assertTrue(validator.isValid("joe%45@apache.org")); // % is valid unquoted
+
+        assertTrue(validator.isValid("joe?@apache.org")); // ? is valid unquoted
+
+        assertTrue(validator.isValid("joe&@apache.org")); // & ditto
+
+        assertTrue(validator.isValid("joe=@apache.org")); // = ditto
+
+        assertTrue(validator.isValid("+joe@apache.org")); // + is valid unquoted
+
+        assertTrue(validator.isValid("!joe@apache.org")); // ! is valid unquoted
+
+        assertTrue(validator.isValid("*joe@apache.org")); // * is valid unquoted
+
+        assertTrue(validator.isValid("'joe@apache.org")); // ' is valid unquoted
+
+        assertTrue(validator.isValid("%joe45@apache.org")); // % is valid unquoted
+
+        assertTrue(validator.isValid("?joe@apache.org")); // ? is valid unquoted
+
+        assertTrue(validator.isValid("&joe@apache.org")); // & ditto
+
+        assertTrue(validator.isValid("=joe@apache.org")); // = ditto
+
+        assertTrue(validator.isValid("+@apache.org")); // + is valid unquoted
+
+        assertTrue(validator.isValid("!@apache.org")); // ! is valid unquoted
+
+        assertTrue(validator.isValid("*@apache.org")); // * is valid unquoted
+
+        assertTrue(validator.isValid("'@apache.org")); // ' is valid unquoted
+
+        assertTrue(validator.isValid("%@apache.org")); // % is valid unquoted
+
+        assertTrue(validator.isValid("?@apache.org")); // ? is valid unquoted
+
+        assertTrue(validator.isValid("&@apache.org")); // & ditto
+
+        assertTrue(validator.isValid("=@apache.org")); // = ditto
+
+
+        //UnQuoted Special characters are invalid
+
+        assertFalse(validator.isValid("joe.@apache.org")); // . not allowed at end of local part
+
+        assertFalse(validator.isValid(".joe@apache.org")); // . not allowed at start of local part
+
+        assertFalse(validator.isValid(".@apache.org")); // . not allowed alone
+
+        assertTrue(validator.isValid("joe.ok@apache.org")); // . allowed embedded
+
+        assertFalse(validator.isValid("joe..ok@apache.org")); // .. not allowed embedded
+
+        assertFalse(validator.isValid("..@apache.org")); // .. not allowed alone
+
+        assertFalse(validator.isValid("joe(@apache.org"));
+
+        assertFalse(validator.isValid("joe)@apache.org"));
+
+        assertFalse(validator.isValid("joe,@apache.org"));
+
+        assertFalse(validator.isValid("joe;@apache.org"));
+
+
+        //Quoted Special characters are valid
+        assertTrue(validator.isValid("\"joe.\"@apache.org"));
+
+        assertTrue(validator.isValid("\".joe\"@apache.org"));
+
+        assertTrue(validator.isValid("\"joe+\"@apache.org"));
+
+        assertTrue(validator.isValid("\"joe!\"@apache.org"));
+
+        assertTrue(validator.isValid("\"joe*\"@apache.org"));
+
+        assertTrue(validator.isValid("\"joe'\"@apache.org"));
+
+        assertTrue(validator.isValid("\"joe(\"@apache.org"));
+
+        assertTrue(validator.isValid("\"joe)\"@apache.org"));
+
+        assertTrue(validator.isValid("\"joe,\"@apache.org"));
+
+        assertTrue(validator.isValid("\"joe%45\"@apache.org"));
+
+        assertTrue(validator.isValid("\"joe;\"@apache.org"));
+
+        assertTrue(validator.isValid("\"joe?\"@apache.org"));
+
+        assertTrue(validator.isValid("\"joe&\"@apache.org"));
+
+        assertTrue(validator.isValid("\"joe=\"@apache.org"));
+
+        assertTrue(validator.isValid("\"..\"@apache.org"));
+
+        // escaped quote character valid in quoted string
+        assertTrue(validator.isValid("\"john\\\"doe\"@apache.org"));
+
+        assertTrue(validator.isValid("john56789.john56789.john56789.john56789.john56789.john56789.john@example.com"));
+
+        assertFalse(validator.isValid("john56789.john56789.john56789.john56789.john56789.john56789.john5@example.com"));
+
+        assertTrue(validator.isValid("\\>escape\\\\special\\^characters\\<@example.com"));
+
+        assertTrue(validator.isValid("Abc\\@def@example.com"));
+
+        assertFalse(validator.isValid("Abc@def@example.com"));
+
+        assertTrue(validator.isValid("space\\ monkey@example.com"));
+    }
+
+    /**
+     * These test values derive directly from RFC 822 &
+     * Mail::RFC822::Address & RFC::RFC822::Address perl test.pl
+     * For traceability don't combine these test values with other tests.
+     */
+    private static final ResultPair[] testEmailFromPerl = {
+        new ResultPair("abigail@example.com", true),
+        new ResultPair("abigail@example.com ", true),
+        new ResultPair(" abigail@example.com", true),
+        new ResultPair("abigail @example.com ", true),
+        new ResultPair("*@example.net", true),
+        new ResultPair("\"\\\"\"@foo.bar", true),
+        new ResultPair("fred&barny@example.com", true),
+        new ResultPair("---@example.com", true),
+        new ResultPair("foo-bar@example.net", true),
+        new ResultPair("\"127.0.0.1\"@[127.0.0.1]", true),
+        new ResultPair("Abigail <abigail@example.com>", true),
+        new ResultPair("Abigail<abigail@example.com>", true),
+        new ResultPair("Abigail<@a,@b,@c:abigail@example.com>", true),
+        new ResultPair("\"This is a phrase\"<abigail@example.com>", true),
+        new ResultPair("\"Abigail \"<abigail@example.com>", true),
+        new ResultPair("\"Joe & J. Harvey\" <example @Org>", true),
+        new ResultPair("Abigail <abigail @ example.com>", true),
+        new ResultPair("Abigail made this <  abigail   @   example  .    com    >", true),
+        new ResultPair("Abigail(the bitch)@example.com", true),
+        new ResultPair("Abigail <abigail @ example . (bar) com >", true),
+        new ResultPair("Abigail < (one)  abigail (two) @(three)example . (bar) com (quz) >", true),
+        new ResultPair("Abigail (foo) (((baz)(nested) (comment)) ! ) < (one)  abigail (two) @(three)example . (bar) com (quz) >", true),
+        new ResultPair("Abigail <abigail(fo\\(o)@example.com>", true),
+        new ResultPair("Abigail <abigail(fo\\)o)@example.com> ", true),
+        new ResultPair("(foo) abigail@example.com", true),
+        new ResultPair("abigail@example.com (foo)", true),
+        new ResultPair("\"Abi\\\"gail\" <abigail@example.com>", true),
+        new ResultPair("abigail@[example.com]", true),
+        new ResultPair("abigail@[exa\\[ple.com]", true),
+        new ResultPair("abigail@[exa\\]ple.com]", true),
+        new ResultPair("\":sysmail\"@  Some-Group. Some-Org", true),
+        new ResultPair("Muhammed.(I am  the greatest) Ali @(the)Vegas.WBA", true),
+        new ResultPair("mailbox.sub1.sub2@this-domain", true),
+        new ResultPair("sub-net.mailbox@sub-domain.domain", true),
+        new ResultPair("name:;", true),
+        new ResultPair("':;", true),
+        new ResultPair("name:   ;", true),
+        new ResultPair("Alfred Neuman <Neuman@BBN-TENEXA>", true),
+        new ResultPair("Neuman@BBN-TENEXA", true),
+        new ResultPair("\"George, Ted\" <Shared@Group.Arpanet>", true),
+        new ResultPair("Wilt . (the  Stilt) Chamberlain@NBA.US", true),
+        new ResultPair("Cruisers:  Port@Portugal, Jones@SEA;", true),
+        new ResultPair("$@[]", true),
+        new ResultPair("*()@[]", true),
+        new ResultPair("\"quoted ( brackets\" ( a comment )@example.com", true),
+        new ResultPair("\"Joe & J. Harvey\"\\x0D\\x0A     <ddd\\@ Org>", true),
+        new ResultPair("\"Joe &\\x0D\\x0A J. Harvey\" <ddd \\@ Org>", true),
+        new ResultPair("Gourmets:  Pompous Person <WhoZiWhatZit\\@Cordon-Bleu>,\\x0D\\x0A" +
+            "        Childs\\@WGBH.Boston, \"Galloping Gourmet\"\\@\\x0D\\x0A" +
+            "        ANT.Down-Under (Australian National Television),\\x0D\\x0A" +
+            "        Cheapie\\@Discount-Liquors;", true),
+        new ResultPair("   Just a string", false),
+        new ResultPair("string", false),
+        new ResultPair("(comment)", false),
+        new ResultPair("()@example.com", false),
+        new ResultPair("fred(&)barny@example.com", false),
+        new ResultPair("fred\\ barny@example.com", false),
+        new ResultPair("Abigail <abi gail @ example.com>", false),
+        new ResultPair("Abigail <abigail(fo(o)@example.com>", false),
+        new ResultPair("Abigail <abigail(fo)o)@example.com>", false),
+        new ResultPair("\"Abi\"gail\" <abigail@example.com>", false),
+        new ResultPair("abigail@[exa]ple.com]", false),
+        new ResultPair("abigail@[exa[ple.com]", false),
+        new ResultPair("abigail@[exaple].com]", false),
+        new ResultPair("abigail@", false),
+        new ResultPair("@example.com", false),
+        new ResultPair("phrase: abigail@example.com abigail@example.com ;", false),
+        new ResultPair("invalid�char@example.com", false)
+    };
+
+    /**
+     * Write this test based on perl Mail::RFC822::Address
+     * which takes its example email address directly from RFC822
+     *
+     * FIXME This test fails so disable it with a leading _ for 1.1.4 release.
+     * The real solution is to fix the email parsing.
+     */
+    @Ignore("This test fails so disable it for 1.1.4 release. The real solution is to fix the email parsing")
+    @Test
+    public void testEmailFromPerl()  {
+        for (int index = 0; index < testEmailFromPerl.length; index++) {
+            String item = testEmailFromPerl[index].item;
+            if (testEmailFromPerl[index].valid) {
+                assertTrue("Should be OK: "+item, validator.isValid(item));
+            } else {
+                assertFalse("Should fail: "+item, validator.isValid(item));
+            }
+        }
+    }
+
+    /**
+     * Non-regression test for VALIDATOR-293
+     */
+    @Test
+    public void testValidator293() {
+        assertTrue(validator.isValid("abc-@abc.com"));
+        assertTrue(validator.isValid("abc_@abc.com"));
+        assertTrue(validator.isValid("abc-def@abc.com"));
+        assertTrue(validator.isValid("abc_def@abc.com"));
+        assertFalse(validator.isValid("abc@abc_def.com"));
+    }
+
+    /**
+     * Non-regression test for VALIDATOR-365
+     */
+    @Test
+    public void testValidator365() {
+        assertFalse(validator.isValid(
+                "Loremipsumdolorsitametconsecteturadipiscingelit.Nullavitaeligulamattisrhoncusnuncegestasmattisleo."+
+                "Donecnonsapieninmagnatristiquedictumaacturpis.Fusceorciduifacilisisutsapieneuconsequatpharetralectus."+
+                "Quisqueenimestpulvinarutquamvitaeportamattisex.Nullamquismaurisplaceratconvallisjustoquisportamauris."+
+                "Innullalacusconvalliseufringillautvenenatissitametdiam.Maecenasluctusligulascelerisquepulvinarfeugiat."+
+                "Sedmolestienullaaliquetorciluctusidpharetranislfinibus.Suspendissemalesuadatinciduntduisitametportaarcusollicitudinnec."+
+                "Donecetmassamagna.Curabitururnadiampretiumveldignissimporttitorfringillaeuneque."+
+                "Duisantetelluspharetraidtinciduntinterdummolestiesitametfelis.Utquisquamsitametantesagittisdapibusacnonodio."+
+                "Namrutrummolestiediamidmattis.Cumsociisnatoquepenatibusetmagnisdisparturientmontesnasceturridiculusmus."+
+                "Morbiposueresedmetusacconsectetur.Etiamquisipsumvitaejustotempusmaximus.Sedultriciesplaceratvolutpat."+
+                "Integerlacuslectusmaximusacornarequissagittissitametjusto."+
+                "Cumsociisnatoquepenatibusetmagnisdisparturientmontesnasceturridiculusmus.Maecenasindictumpurussedrutrumex.Nullafacilisi."+
+                "Integerfinibusfinibusmietpharetranislfaucibusvel.Maecenasegetdolorlacinialobortisjustovelullamcorpersem."+
+                "Vivamusaliquetpurusidvariusornaresapienrisusrutrumnisitinciduntmollissemnequeidmetus."+
+                "Etiamquiseleifendpurus.Nuncfelisnuncscelerisqueiddignissimnecfinibusalibero."+
+                "Nuncsemperenimnequesitamethendreritpurusfacilisisac.Maurisdapibussemperfelisdignissimgravida."+
+                "Aeneanultricesblanditnequealiquamfinibusodioscelerisqueac.Aliquamnecmassaeumaurisfaucibusfringilla."+
+                "Etiamconsequatligulanisisitametaliquamnibhtemporquis.Nuncinterdumdignissimnullaatsodalesarcusagittiseu."+
+                "Proinpharetrametusneclacuspulvinarsedvolutpatliberoornare.Sedligulanislpulvinarnonlectuseublanditfacilisisante."+
+                "Sedmollisnislalacusauctorsuscipit.Inhachabitasseplateadictumst.Phasellussitametvelittemporvenenatisfeliseuegestasrisus."+
+                "Aliquameteratsitametnibhcommodofinibus.Morbiefficiturodiovelpulvinariaculis."+
+                "Aeneantemporipsummassaaconsecteturturpisfaucibusultrices.Praesentsodalesmaurisquisportafermentum."+
+                "Etiamnisinislvenenatisvelauctorutullamcorperinjusto.Proinvelligulaerat.Phasellusvestibulumgravidamassanonfeugiat."+
+                "Maecenaspharetraeuismodmetusegetefficitur.Suspendisseamet@gmail.com"));
+    }
+
+    /**
+     * Tests the e-mail validation with a user at a TLD
+     *
+     * http://tools.ietf.org/html/rfc5321#section-2.3.5
+     * (In the case of a top-level domain used by itself in an
+     * email address, a single string is used without any dots)
+     */
+    @Test
+    public void testEmailAtTLD() {
+        EmailValidator val = EmailValidator.getInstance(false, true);
+        assertTrue(val.isValid("test@com"));
+    }
+
+    /**
+     * Non-regression test for VALIDATOR-359
+     */
+    @Test
+    public void testValidator359() {
+        EmailValidator val = EmailValidator.getInstance(false, true);
+        assertFalse(val.isValid("test@.com"));
+    }
+
+    /**
+     * Non-regression test for VALIDATOR-374
+     */
+    @Test
+    public void testValidator374() {
+        assertTrue(validator.isValid("abc@school.school"));
+    }
+}
Index: trunk/test/unit/org/openstreetmap/josm/data/validation/routines/InetAddressValidatorTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/data/validation/routines/InetAddressValidatorTest.java	(revision 9853)
+++ trunk/test/unit/org/openstreetmap/josm/data/validation/routines/InetAddressValidatorTest.java	(revision 9853)
@@ -0,0 +1,617 @@
+/*
+ * 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.openstreetmap.josm.data.validation.routines;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Test cases for InetAddressValidator.
+ *
+ * @version $Revision: 1649163 $
+ */
+public class InetAddressValidatorTest {
+
+    private InetAddressValidator validator;
+
+    /**
+     * Setup
+     */
+    @Before
+    public void setUp() {
+        validator = new InetAddressValidator();
+    }
+
+    /**
+     * Test IPs that point to real, well-known hosts (without actually looking them up).
+     */
+    @Test
+    public void testInetAddressesFromTheWild() {
+        assertTrue("www.apache.org IP should be valid",       validator.isValid("140.211.11.130"));
+        assertTrue("www.l.google.com IP should be valid",     validator.isValid("72.14.253.103"));
+        assertTrue("fsf.org IP should be valid",              validator.isValid("199.232.41.5"));
+        assertTrue("appscs.ign.com IP should be valid",       validator.isValid("216.35.123.87"));
+    }
+
+    /**
+     * Non-regression test for VALIDATOR-335
+     */
+    @Test
+    public void testVALIDATOR_335() {
+        assertTrue("2001:0438:FFFE:0000:0000:0000:0000:0A35 should be valid",
+                validator.isValid("2001:0438:FFFE:0000:0000:0000:0000:0A35"));
+    }
+
+    /**
+     * Test valid and invalid IPs from each address class.
+     */
+    @Test
+    public void testInetAddressesByClass() {
+        assertTrue("class A IP should be valid",              validator.isValid("24.25.231.12"));
+        assertFalse("illegal class A IP should be invalid",   validator.isValid("2.41.32.324"));
+
+        assertTrue("class B IP should be valid",              validator.isValid("135.14.44.12"));
+        assertFalse("illegal class B IP should be invalid",   validator.isValid("154.123.441.123"));
+
+        assertTrue("class C IP should be valid",              validator.isValid("213.25.224.32"));
+        assertFalse("illegal class C IP should be invalid",   validator.isValid("201.543.23.11"));
+
+        assertTrue("class D IP should be valid",              validator.isValid("229.35.159.6"));
+        assertFalse("illegal class D IP should be invalid",   validator.isValid("231.54.11.987"));
+
+        assertTrue("class E IP should be valid",              validator.isValid("248.85.24.92"));
+        assertFalse("illegal class E IP should be invalid",   validator.isValid("250.21.323.48"));
+    }
+
+    /**
+     * Test reserved IPs.
+     */
+    @Test
+    public void testReservedInetAddresses() {
+        assertTrue("localhost IP should be valid",            validator.isValid("127.0.0.1"));
+        assertTrue("broadcast IP should be valid",            validator.isValid("255.255.255.255"));
+    }
+
+    /**
+     * Test obviously broken IPs.
+     */
+    @Test
+    public void testBrokenInetAddresses() {
+        assertFalse("IP with characters should be invalid",     validator.isValid("124.14.32.abc"));
+        assertFalse("IP with leading zeroes should be invalid", validator.isValid("124.14.32.01"));
+        assertFalse("IP with three groups should be invalid",   validator.isValid("23.64.12"));
+        assertFalse("IP with five groups should be invalid",    validator.isValid("26.34.23.77.234"));
+    }
+
+    // CHECKSTYLE.OFF: LineLength
+    // CHECKSTYLE.OFF: MethodLengthCheck
+    // CHECKSTYLE.OFF: ExecutableStatementCount
+
+    /**
+     * Test IPv6 addresses.
+     * <p>These tests were ported from a
+     * <a href="http://download.dartware.com/thirdparty/test-ipv6-regex.pl">Perl script</a>.</p>
+     */
+    @Test
+    public void testIPv6() {
+        // The original Perl script contained a lot of duplicate tests.
+        // I removed the duplicates I noticed, but there may be more.
+        assertFalse("IPV6 empty string should be invalid", validator.isValidInet6Address("")); // empty string
+        assertTrue("IPV6 ::1 should be valid", validator.isValidInet6Address("::1")); // loopback, compressed, non-routable
+        assertTrue("IPV6 :: should be valid", validator.isValidInet6Address("::")); // unspecified, compressed, non-routable
+        assertTrue("IPV6 0:0:0:0:0:0:0:1 should be valid", validator.isValidInet6Address("0:0:0:0:0:0:0:1")); // loopback, full
+        assertTrue("IPV6 0:0:0:0:0:0:0:0 should be valid", validator.isValidInet6Address("0:0:0:0:0:0:0:0")); // unspecified, full
+        assertTrue("IPV6 2001:DB8:0:0:8:800:200C:417A should be valid", validator.isValidInet6Address("2001:DB8:0:0:8:800:200C:417A")); // unicast, full
+        assertTrue("IPV6 FF01:0:0:0:0:0:0:101 should be valid", validator.isValidInet6Address("FF01:0:0:0:0:0:0:101")); // multicast, full
+        assertTrue("IPV6 2001:DB8::8:800:200C:417A should be valid", validator.isValidInet6Address("2001:DB8::8:800:200C:417A")); // unicast, compressed
+        assertTrue("IPV6 FF01::101 should be valid", validator.isValidInet6Address("FF01::101")); // multicast, compressed
+        assertFalse("IPV6 2001:DB8:0:0:8:800:200C:417A:221 should be invalid", validator.isValidInet6Address("2001:DB8:0:0:8:800:200C:417A:221")); // unicast, full
+        assertFalse("IPV6 FF01::101::2 should be invalid", validator.isValidInet6Address("FF01::101::2")); // multicast, compressed
+        assertTrue("IPV6 fe80::217:f2ff:fe07:ed62 should be valid", validator.isValidInet6Address("fe80::217:f2ff:fe07:ed62"));
+        assertTrue("IPV6 2001:0000:1234:0000:0000:C1C0:ABCD:0876 should be valid", validator.isValidInet6Address("2001:0000:1234:0000:0000:C1C0:ABCD:0876"));
+        assertTrue("IPV6 3ffe:0b00:0000:0000:0001:0000:0000:000a should be valid", validator.isValidInet6Address("3ffe:0b00:0000:0000:0001:0000:0000:000a"));
+        assertTrue("IPV6 FF02:0000:0000:0000:0000:0000:0000:0001 should be valid", validator.isValidInet6Address("FF02:0000:0000:0000:0000:0000:0000:0001"));
+        assertTrue("IPV6 0000:0000:0000:0000:0000:0000:0000:0001 should be valid", validator.isValidInet6Address("0000:0000:0000:0000:0000:0000:0000:0001"));
+        assertTrue("IPV6 0000:0000:0000:0000:0000:0000:0000:0000 should be valid", validator.isValidInet6Address("0000:0000:0000:0000:0000:0000:0000:0000"));
+        assertFalse("IPV6 02001:0000:1234:0000:0000:C1C0:ABCD:0876 should be invalid", validator.isValidInet6Address("02001:0000:1234:0000:0000:C1C0:ABCD:0876")); // extra 0 not allowed!
+        assertFalse("IPV6 2001:0000:1234:0000:00001:C1C0:ABCD:0876 should be invalid", validator.isValidInet6Address("2001:0000:1234:0000:00001:C1C0:ABCD:0876")); // extra 0 not allowed!
+        assertFalse("IPV6 2001:0000:1234:0000:0000:C1C0:ABCD:0876 0 should be invalid", validator.isValidInet6Address("2001:0000:1234:0000:0000:C1C0:ABCD:0876 0")); // junk after valid address
+        assertFalse("IPV6 2001:0000:1234: 0000:0000:C1C0:ABCD:0876 should be invalid", validator.isValidInet6Address("2001:0000:1234: 0000:0000:C1C0:ABCD:0876")); // internal space
+        assertFalse("IPV6 3ffe:0b00:0000:0001:0000:0000:000a should be invalid", validator.isValidInet6Address("3ffe:0b00:0000:0001:0000:0000:000a")); // seven segments
+        assertFalse("IPV6 FF02:0000:0000:0000:0000:0000:0000:0000:0001 should be invalid", validator.isValidInet6Address("FF02:0000:0000:0000:0000:0000:0000:0000:0001")); // nine segments
+        assertFalse("IPV6 3ffe:b00::1::a should be invalid", validator.isValidInet6Address("3ffe:b00::1::a")); // double "::"
+        assertFalse("IPV6 ::1111:2222:3333:4444:5555:6666:: should be invalid", validator.isValidInet6Address("::1111:2222:3333:4444:5555:6666::")); // double "::"
+        assertTrue("IPV6 2::10 should be valid", validator.isValidInet6Address("2::10"));
+        assertTrue("IPV6 ff02::1 should be valid", validator.isValidInet6Address("ff02::1"));
+        assertTrue("IPV6 fe80:: should be valid", validator.isValidInet6Address("fe80::"));
+        assertTrue("IPV6 2002:: should be valid", validator.isValidInet6Address("2002::"));
+        assertTrue("IPV6 2001:db8:: should be valid", validator.isValidInet6Address("2001:db8::"));
+        assertTrue("IPV6 2001:0db8:1234:: should be valid", validator.isValidInet6Address("2001:0db8:1234::"));
+        assertTrue("IPV6 ::ffff:0:0 should be valid", validator.isValidInet6Address("::ffff:0:0"));
+        assertTrue("IPV6 1:2:3:4:5:6:7:8 should be valid", validator.isValidInet6Address("1:2:3:4:5:6:7:8"));
+        assertTrue("IPV6 1:2:3:4:5:6::8 should be valid", validator.isValidInet6Address("1:2:3:4:5:6::8"));
+        assertTrue("IPV6 1:2:3:4:5::8 should be valid", validator.isValidInet6Address("1:2:3:4:5::8"));
+        assertTrue("IPV6 1:2:3:4::8 should be valid", validator.isValidInet6Address("1:2:3:4::8"));
+        assertTrue("IPV6 1:2:3::8 should be valid", validator.isValidInet6Address("1:2:3::8"));
+        assertTrue("IPV6 1:2::8 should be valid", validator.isValidInet6Address("1:2::8"));
+        assertTrue("IPV6 1::8 should be valid", validator.isValidInet6Address("1::8"));
+        assertTrue("IPV6 1::2:3:4:5:6:7 should be valid", validator.isValidInet6Address("1::2:3:4:5:6:7"));
+        assertTrue("IPV6 1::2:3:4:5:6 should be valid", validator.isValidInet6Address("1::2:3:4:5:6"));
+        assertTrue("IPV6 1::2:3:4:5 should be valid", validator.isValidInet6Address("1::2:3:4:5"));
+        assertTrue("IPV6 1::2:3:4 should be valid", validator.isValidInet6Address("1::2:3:4"));
+        assertTrue("IPV6 1::2:3 should be valid", validator.isValidInet6Address("1::2:3"));
+        assertTrue("IPV6 ::2:3:4:5:6:7:8 should be valid", validator.isValidInet6Address("::2:3:4:5:6:7:8"));
+        assertTrue("IPV6 ::2:3:4:5:6:7 should be valid", validator.isValidInet6Address("::2:3:4:5:6:7"));
+        assertTrue("IPV6 ::2:3:4:5:6 should be valid", validator.isValidInet6Address("::2:3:4:5:6"));
+        assertTrue("IPV6 ::2:3:4:5 should be valid", validator.isValidInet6Address("::2:3:4:5"));
+        assertTrue("IPV6 ::2:3:4 should be valid", validator.isValidInet6Address("::2:3:4"));
+        assertTrue("IPV6 ::2:3 should be valid", validator.isValidInet6Address("::2:3"));
+        assertTrue("IPV6 ::8 should be valid", validator.isValidInet6Address("::8"));
+        assertTrue("IPV6 1:2:3:4:5:6:: should be valid", validator.isValidInet6Address("1:2:3:4:5:6::"));
+        assertTrue("IPV6 1:2:3:4:5:: should be valid", validator.isValidInet6Address("1:2:3:4:5::"));
+        assertTrue("IPV6 1:2:3:4:: should be valid", validator.isValidInet6Address("1:2:3:4::"));
+        assertTrue("IPV6 1:2:3:: should be valid", validator.isValidInet6Address("1:2:3::"));
+        assertTrue("IPV6 1:2:: should be valid", validator.isValidInet6Address("1:2::"));
+        assertTrue("IPV6 1:: should be valid", validator.isValidInet6Address("1::"));
+        assertTrue("IPV6 1:2:3:4:5::7:8 should be valid", validator.isValidInet6Address("1:2:3:4:5::7:8"));
+        assertFalse("IPV6 1:2:3::4:5::7:8 should be invalid", validator.isValidInet6Address("1:2:3::4:5::7:8")); // Double "::"
+        assertFalse("IPV6 12345::6:7:8 should be invalid", validator.isValidInet6Address("12345::6:7:8"));
+        assertTrue("IPV6 1:2:3:4::7:8 should be valid", validator.isValidInet6Address("1:2:3:4::7:8"));
+        assertTrue("IPV6 1:2:3::7:8 should be valid", validator.isValidInet6Address("1:2:3::7:8"));
+        assertTrue("IPV6 1:2::7:8 should be valid", validator.isValidInet6Address("1:2::7:8"));
+        assertTrue("IPV6 1::7:8 should be valid", validator.isValidInet6Address("1::7:8"));
+        // IPv4 addresses as dotted-quads
+        assertTrue("IPV6 1:2:3:4:5:6:1.2.3.4 should be valid", validator.isValidInet6Address("1:2:3:4:5:6:1.2.3.4"));
+        assertTrue("IPV6 1:2:3:4:5::1.2.3.4 should be valid", validator.isValidInet6Address("1:2:3:4:5::1.2.3.4"));
+        assertTrue("IPV6 1:2:3:4::1.2.3.4 should be valid", validator.isValidInet6Address("1:2:3:4::1.2.3.4"));
+        assertTrue("IPV6 1:2:3::1.2.3.4 should be valid", validator.isValidInet6Address("1:2:3::1.2.3.4"));
+        assertTrue("IPV6 1:2::1.2.3.4 should be valid", validator.isValidInet6Address("1:2::1.2.3.4"));
+        assertTrue("IPV6 1::1.2.3.4 should be valid", validator.isValidInet6Address("1::1.2.3.4"));
+        assertTrue("IPV6 1:2:3:4::5:1.2.3.4 should be valid", validator.isValidInet6Address("1:2:3:4::5:1.2.3.4"));
+        assertTrue("IPV6 1:2:3::5:1.2.3.4 should be valid", validator.isValidInet6Address("1:2:3::5:1.2.3.4"));
+        assertTrue("IPV6 1:2::5:1.2.3.4 should be valid", validator.isValidInet6Address("1:2::5:1.2.3.4"));
+        assertTrue("IPV6 1::5:1.2.3.4 should be valid", validator.isValidInet6Address("1::5:1.2.3.4"));
+        assertTrue("IPV6 1::5:11.22.33.44 should be valid", validator.isValidInet6Address("1::5:11.22.33.44"));
+        assertFalse("IPV6 1::5:400.2.3.4 should be invalid", validator.isValidInet6Address("1::5:400.2.3.4"));
+        assertFalse("IPV6 1::5:260.2.3.4 should be invalid", validator.isValidInet6Address("1::5:260.2.3.4"));
+        assertFalse("IPV6 1::5:256.2.3.4 should be invalid", validator.isValidInet6Address("1::5:256.2.3.4"));
+        assertFalse("IPV6 1::5:1.256.3.4 should be invalid", validator.isValidInet6Address("1::5:1.256.3.4"));
+        assertFalse("IPV6 1::5:1.2.256.4 should be invalid", validator.isValidInet6Address("1::5:1.2.256.4"));
+        assertFalse("IPV6 1::5:1.2.3.256 should be invalid", validator.isValidInet6Address("1::5:1.2.3.256"));
+        assertFalse("IPV6 1::5:300.2.3.4 should be invalid", validator.isValidInet6Address("1::5:300.2.3.4"));
+        assertFalse("IPV6 1::5:1.300.3.4 should be invalid", validator.isValidInet6Address("1::5:1.300.3.4"));
+        assertFalse("IPV6 1::5:1.2.300.4 should be invalid", validator.isValidInet6Address("1::5:1.2.300.4"));
+        assertFalse("IPV6 1::5:1.2.3.300 should be invalid", validator.isValidInet6Address("1::5:1.2.3.300"));
+        assertFalse("IPV6 1::5:900.2.3.4 should be invalid", validator.isValidInet6Address("1::5:900.2.3.4"));
+        assertFalse("IPV6 1::5:1.900.3.4 should be invalid", validator.isValidInet6Address("1::5:1.900.3.4"));
+        assertFalse("IPV6 1::5:1.2.900.4 should be invalid", validator.isValidInet6Address("1::5:1.2.900.4"));
+        assertFalse("IPV6 1::5:1.2.3.900 should be invalid", validator.isValidInet6Address("1::5:1.2.3.900"));
+        assertFalse("IPV6 1::5:300.300.300.300 should be invalid", validator.isValidInet6Address("1::5:300.300.300.300"));
+        assertFalse("IPV6 1::5:3000.30.30.30 should be invalid", validator.isValidInet6Address("1::5:3000.30.30.30"));
+        assertFalse("IPV6 1::400.2.3.4 should be invalid", validator.isValidInet6Address("1::400.2.3.4"));
+        assertFalse("IPV6 1::260.2.3.4 should be invalid", validator.isValidInet6Address("1::260.2.3.4"));
+        assertFalse("IPV6 1::256.2.3.4 should be invalid", validator.isValidInet6Address("1::256.2.3.4"));
+        assertFalse("IPV6 1::1.256.3.4 should be invalid", validator.isValidInet6Address("1::1.256.3.4"));
+        assertFalse("IPV6 1::1.2.256.4 should be invalid", validator.isValidInet6Address("1::1.2.256.4"));
+        assertFalse("IPV6 1::1.2.3.256 should be invalid", validator.isValidInet6Address("1::1.2.3.256"));
+        assertFalse("IPV6 1::300.2.3.4 should be invalid", validator.isValidInet6Address("1::300.2.3.4"));
+        assertFalse("IPV6 1::1.300.3.4 should be invalid", validator.isValidInet6Address("1::1.300.3.4"));
+        assertFalse("IPV6 1::1.2.300.4 should be invalid", validator.isValidInet6Address("1::1.2.300.4"));
+        assertFalse("IPV6 1::1.2.3.300 should be invalid", validator.isValidInet6Address("1::1.2.3.300"));
+        assertFalse("IPV6 1::900.2.3.4 should be invalid", validator.isValidInet6Address("1::900.2.3.4"));
+        assertFalse("IPV6 1::1.900.3.4 should be invalid", validator.isValidInet6Address("1::1.900.3.4"));
+        assertFalse("IPV6 1::1.2.900.4 should be invalid", validator.isValidInet6Address("1::1.2.900.4"));
+        assertFalse("IPV6 1::1.2.3.900 should be invalid", validator.isValidInet6Address("1::1.2.3.900"));
+        assertFalse("IPV6 1::300.300.300.300 should be invalid", validator.isValidInet6Address("1::300.300.300.300"));
+        assertFalse("IPV6 1::3000.30.30.30 should be invalid", validator.isValidInet6Address("1::3000.30.30.30"));
+        assertFalse("IPV6 ::400.2.3.4 should be invalid", validator.isValidInet6Address("::400.2.3.4"));
+        assertFalse("IPV6 ::260.2.3.4 should be invalid", validator.isValidInet6Address("::260.2.3.4"));
+        assertFalse("IPV6 ::256.2.3.4 should be invalid", validator.isValidInet6Address("::256.2.3.4"));
+        assertFalse("IPV6 ::1.256.3.4 should be invalid", validator.isValidInet6Address("::1.256.3.4"));
+        assertFalse("IPV6 ::1.2.256.4 should be invalid", validator.isValidInet6Address("::1.2.256.4"));
+        assertFalse("IPV6 ::1.2.3.256 should be invalid", validator.isValidInet6Address("::1.2.3.256"));
+        assertFalse("IPV6 ::300.2.3.4 should be invalid", validator.isValidInet6Address("::300.2.3.4"));
+        assertFalse("IPV6 ::1.300.3.4 should be invalid", validator.isValidInet6Address("::1.300.3.4"));
+        assertFalse("IPV6 ::1.2.300.4 should be invalid", validator.isValidInet6Address("::1.2.300.4"));
+        assertFalse("IPV6 ::1.2.3.300 should be invalid", validator.isValidInet6Address("::1.2.3.300"));
+        assertFalse("IPV6 ::900.2.3.4 should be invalid", validator.isValidInet6Address("::900.2.3.4"));
+        assertFalse("IPV6 ::1.900.3.4 should be invalid", validator.isValidInet6Address("::1.900.3.4"));
+        assertFalse("IPV6 ::1.2.900.4 should be invalid", validator.isValidInet6Address("::1.2.900.4"));
+        assertFalse("IPV6 ::1.2.3.900 should be invalid", validator.isValidInet6Address("::1.2.3.900"));
+        assertFalse("IPV6 ::300.300.300.300 should be invalid", validator.isValidInet6Address("::300.300.300.300"));
+        assertFalse("IPV6 ::3000.30.30.30 should be invalid", validator.isValidInet6Address("::3000.30.30.30"));
+        assertTrue("IPV6 fe80::217:f2ff:254.7.237.98 should be valid", validator.isValidInet6Address("fe80::217:f2ff:254.7.237.98"));
+        assertTrue("IPV6 ::ffff:192.168.1.26 should be valid", validator.isValidInet6Address("::ffff:192.168.1.26"));
+        assertFalse("IPV6 2001:1:1:1:1:1:255Z255X255Y255 should be invalid", validator.isValidInet6Address("2001:1:1:1:1:1:255Z255X255Y255")); // garbage instead of "." in IPv4
+        assertFalse("IPV6 ::ffff:192x168.1.26 should be invalid", validator.isValidInet6Address("::ffff:192x168.1.26")); // ditto
+        assertTrue("IPV6 ::ffff:192.168.1.1 should be valid", validator.isValidInet6Address("::ffff:192.168.1.1"));
+        assertTrue("IPV6 0:0:0:0:0:0:13.1.68.3 should be valid", validator.isValidInet6Address("0:0:0:0:0:0:13.1.68.3")); // IPv4-compatible IPv6 address, full, deprecated
+        assertTrue("IPV6 0:0:0:0:0:FFFF:129.144.52.38 should be valid", validator.isValidInet6Address("0:0:0:0:0:FFFF:129.144.52.38")); // IPv4-mapped IPv6 address, full
+        assertTrue("IPV6 ::13.1.68.3 should be valid", validator.isValidInet6Address("::13.1.68.3")); // IPv4-compatible IPv6 address, compressed, deprecated
+        assertTrue("IPV6 ::FFFF:129.144.52.38 should be valid", validator.isValidInet6Address("::FFFF:129.144.52.38")); // IPv4-mapped IPv6 address, compressed
+        assertTrue("IPV6 fe80:0:0:0:204:61ff:254.157.241.86 should be valid", validator.isValidInet6Address("fe80:0:0:0:204:61ff:254.157.241.86"));
+        assertTrue("IPV6 fe80::204:61ff:254.157.241.86 should be valid", validator.isValidInet6Address("fe80::204:61ff:254.157.241.86"));
+        assertTrue("IPV6 ::ffff:12.34.56.78 should be valid", validator.isValidInet6Address("::ffff:12.34.56.78"));
+        assertFalse("IPV6 ::ffff:2.3.4 should be invalid", validator.isValidInet6Address("::ffff:2.3.4"));
+        assertFalse("IPV6 ::ffff:257.1.2.3 should be invalid", validator.isValidInet6Address("::ffff:257.1.2.3"));
+        assertFalse("IPV6 1.2.3.4 should be invalid", validator.isValidInet6Address("1.2.3.4"));
+        assertFalse("IPV6 1.2.3.4:1111:2222:3333:4444::5555 should be invalid", validator.isValidInet6Address("1.2.3.4:1111:2222:3333:4444::5555"));
+        assertFalse("IPV6 1.2.3.4:1111:2222:3333::5555 should be invalid", validator.isValidInet6Address("1.2.3.4:1111:2222:3333::5555"));
+        assertFalse("IPV6 1.2.3.4:1111:2222::5555 should be invalid", validator.isValidInet6Address("1.2.3.4:1111:2222::5555"));
+        assertFalse("IPV6 1.2.3.4:1111::5555 should be invalid", validator.isValidInet6Address("1.2.3.4:1111::5555"));
+        assertFalse("IPV6 1.2.3.4::5555 should be invalid", validator.isValidInet6Address("1.2.3.4::5555"));
+        assertFalse("IPV6 1.2.3.4:: should be invalid", validator.isValidInet6Address("1.2.3.4::"));
+        // Testing IPv4 addresses represented as dotted-quads
+        // Leading zeroes in IPv4 addresses not allowed: some systems treat the leading "0" in ".086" as the start of an octal number
+        // Update: The BNF in RFC-3986 explicitly defines the dec-octet (for IPv4 addresses) not to have a leading zero
+        assertFalse("IPV6 fe80:0000:0000:0000:0204:61ff:254.157.241.086 should be invalid", validator.isValidInet6Address("fe80:0000:0000:0000:0204:61ff:254.157.241.086"));
+        assertTrue("IPV6 ::ffff:192.0.2.128 should be valid", validator.isValidInet6Address("::ffff:192.0.2.128")); // but this is OK, since there's a single digit
+        assertFalse("IPV6 XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:1.2.3.4 should be invalid", validator.isValidInet6Address("XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:1.2.3.4"));
+        assertFalse("IPV6 1111:2222:3333:4444:5555:6666:00.00.00.00 should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555:6666:00.00.00.00"));
+        assertFalse("IPV6 1111:2222:3333:4444:5555:6666:000.000.000.000 should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555:6666:000.000.000.000"));
+        assertFalse("IPV6 1111:2222:3333:4444:5555:6666:256.256.256.256 should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555:6666:256.256.256.256"));
+        assertTrue("IPV6 fe80:0000:0000:0000:0204:61ff:fe9d:f156 should be valid", validator.isValidInet6Address("fe80:0000:0000:0000:0204:61ff:fe9d:f156"));
+        assertTrue("IPV6 fe80:0:0:0:204:61ff:fe9d:f156 should be valid", validator.isValidInet6Address("fe80:0:0:0:204:61ff:fe9d:f156"));
+        assertTrue("IPV6 fe80::204:61ff:fe9d:f156 should be valid", validator.isValidInet6Address("fe80::204:61ff:fe9d:f156"));
+        assertFalse("IPV6 : should be invalid", validator.isValidInet6Address(":"));
+        assertTrue("IPV6 ::ffff:c000:280 should be valid", validator.isValidInet6Address("::ffff:c000:280"));
+        assertFalse("IPV6 1111:2222:3333:4444::5555: should be invalid", validator.isValidInet6Address("1111:2222:3333:4444::5555:"));
+        assertFalse("IPV6 1111:2222:3333::5555: should be invalid", validator.isValidInet6Address("1111:2222:3333::5555:"));
+        assertFalse("IPV6 1111:2222::5555: should be invalid", validator.isValidInet6Address("1111:2222::5555:"));
+        assertFalse("IPV6 1111::5555: should be invalid", validator.isValidInet6Address("1111::5555:"));
+        assertFalse("IPV6 ::5555: should be invalid", validator.isValidInet6Address("::5555:"));
+        assertFalse("IPV6 ::: should be invalid", validator.isValidInet6Address(":::"));
+        assertFalse("IPV6 1111: should be invalid", validator.isValidInet6Address("1111:"));
+        assertFalse("IPV6 :1111:2222:3333:4444::5555 should be invalid", validator.isValidInet6Address(":1111:2222:3333:4444::5555"));
+        assertFalse("IPV6 :1111:2222:3333::5555 should be invalid", validator.isValidInet6Address(":1111:2222:3333::5555"));
+        assertFalse("IPV6 :1111:2222::5555 should be invalid", validator.isValidInet6Address(":1111:2222::5555"));
+        assertFalse("IPV6 :1111::5555 should be invalid", validator.isValidInet6Address(":1111::5555"));
+        assertFalse("IPV6 :::5555 should be invalid", validator.isValidInet6Address(":::5555"));
+        assertTrue("IPV6 2001:0db8:85a3:0000:0000:8a2e:0370:7334 should be valid", validator.isValidInet6Address("2001:0db8:85a3:0000:0000:8a2e:0370:7334"));
+        assertTrue("IPV6 2001:db8:85a3:0:0:8a2e:370:7334 should be valid", validator.isValidInet6Address("2001:db8:85a3:0:0:8a2e:370:7334"));
+        assertTrue("IPV6 2001:db8:85a3::8a2e:370:7334 should be valid", validator.isValidInet6Address("2001:db8:85a3::8a2e:370:7334"));
+        assertTrue("IPV6 2001:0db8:0000:0000:0000:0000:1428:57ab should be valid", validator.isValidInet6Address("2001:0db8:0000:0000:0000:0000:1428:57ab"));
+        assertTrue("IPV6 2001:0db8:0000:0000:0000::1428:57ab should be valid", validator.isValidInet6Address("2001:0db8:0000:0000:0000::1428:57ab"));
+        assertTrue("IPV6 2001:0db8:0:0:0:0:1428:57ab should be valid", validator.isValidInet6Address("2001:0db8:0:0:0:0:1428:57ab"));
+        assertTrue("IPV6 2001:0db8:0:0::1428:57ab should be valid", validator.isValidInet6Address("2001:0db8:0:0::1428:57ab"));
+        assertTrue("IPV6 2001:0db8::1428:57ab should be valid", validator.isValidInet6Address("2001:0db8::1428:57ab"));
+        assertTrue("IPV6 2001:db8::1428:57ab should be valid", validator.isValidInet6Address("2001:db8::1428:57ab"));
+        assertTrue("IPV6 ::ffff:0c22:384e should be valid", validator.isValidInet6Address("::ffff:0c22:384e"));
+        assertTrue("IPV6 2001:0db8:1234:0000:0000:0000:0000:0000 should be valid", validator.isValidInet6Address("2001:0db8:1234:0000:0000:0000:0000:0000"));
+        assertTrue("IPV6 2001:0db8:1234:ffff:ffff:ffff:ffff:ffff should be valid", validator.isValidInet6Address("2001:0db8:1234:ffff:ffff:ffff:ffff:ffff"));
+        assertTrue("IPV6 2001:db8:a::123 should be valid", validator.isValidInet6Address("2001:db8:a::123"));
+        assertFalse("IPV6 123 should be invalid", validator.isValidInet6Address("123"));
+        assertFalse("IPV6 ldkfj should be invalid", validator.isValidInet6Address("ldkfj"));
+        assertFalse("IPV6 2001::FFD3::57ab should be invalid", validator.isValidInet6Address("2001::FFD3::57ab"));
+        assertFalse("IPV6 2001:db8:85a3::8a2e:37023:7334 should be invalid", validator.isValidInet6Address("2001:db8:85a3::8a2e:37023:7334"));
+        assertFalse("IPV6 2001:db8:85a3::8a2e:370k:7334 should be invalid", validator.isValidInet6Address("2001:db8:85a3::8a2e:370k:7334"));
+        assertFalse("IPV6 1:2:3:4:5:6:7:8:9 should be invalid", validator.isValidInet6Address("1:2:3:4:5:6:7:8:9"));
+        assertFalse("IPV6 1::2::3 should be invalid", validator.isValidInet6Address("1::2::3"));
+        assertFalse("IPV6 1:::3:4:5 should be invalid", validator.isValidInet6Address("1:::3:4:5"));
+        assertFalse("IPV6 1:2:3::4:5:6:7:8:9 should be invalid", validator.isValidInet6Address("1:2:3::4:5:6:7:8:9"));
+        assertTrue("IPV6 1111:2222:3333:4444:5555:6666:7777:8888 should be valid", validator.isValidInet6Address("1111:2222:3333:4444:5555:6666:7777:8888"));
+        assertTrue("IPV6 1111:2222:3333:4444:5555:6666:7777:: should be valid", validator.isValidInet6Address("1111:2222:3333:4444:5555:6666:7777::"));
+        assertTrue("IPV6 1111:2222:3333:4444:5555:6666:: should be valid", validator.isValidInet6Address("1111:2222:3333:4444:5555:6666::"));
+        assertTrue("IPV6 1111:2222:3333:4444:5555:: should be valid", validator.isValidInet6Address("1111:2222:3333:4444:5555::"));
+        assertTrue("IPV6 1111:2222:3333:4444:: should be valid", validator.isValidInet6Address("1111:2222:3333:4444::"));
+        assertTrue("IPV6 1111:2222:3333:: should be valid", validator.isValidInet6Address("1111:2222:3333::"));
+        assertTrue("IPV6 1111:2222:: should be valid", validator.isValidInet6Address("1111:2222::"));
+        assertTrue("IPV6 1111:: should be valid", validator.isValidInet6Address("1111::"));
+        assertTrue("IPV6 1111:2222:3333:4444:5555:6666::8888 should be valid", validator.isValidInet6Address("1111:2222:3333:4444:5555:6666::8888"));
+        assertTrue("IPV6 1111:2222:3333:4444:5555::8888 should be valid", validator.isValidInet6Address("1111:2222:3333:4444:5555::8888"));
+        assertTrue("IPV6 1111:2222:3333:4444::8888 should be valid", validator.isValidInet6Address("1111:2222:3333:4444::8888"));
+        assertTrue("IPV6 1111:2222:3333::8888 should be valid", validator.isValidInet6Address("1111:2222:3333::8888"));
+        assertTrue("IPV6 1111:2222::8888 should be valid", validator.isValidInet6Address("1111:2222::8888"));
+        assertTrue("IPV6 1111::8888 should be valid", validator.isValidInet6Address("1111::8888"));
+        assertTrue("IPV6 ::8888 should be valid", validator.isValidInet6Address("::8888"));
+        assertTrue("IPV6 1111:2222:3333:4444:5555::7777:8888 should be valid", validator.isValidInet6Address("1111:2222:3333:4444:5555::7777:8888"));
+        assertTrue("IPV6 1111:2222:3333:4444::7777:8888 should be valid", validator.isValidInet6Address("1111:2222:3333:4444::7777:8888"));
+        assertTrue("IPV6 1111:2222:3333::7777:8888 should be valid", validator.isValidInet6Address("1111:2222:3333::7777:8888"));
+        assertTrue("IPV6 1111:2222::7777:8888 should be valid", validator.isValidInet6Address("1111:2222::7777:8888"));
+        assertTrue("IPV6 1111::7777:8888 should be valid", validator.isValidInet6Address("1111::7777:8888"));
+        assertTrue("IPV6 ::7777:8888 should be valid", validator.isValidInet6Address("::7777:8888"));
+        assertTrue("IPV6 1111:2222:3333:4444::6666:7777:8888 should be valid", validator.isValidInet6Address("1111:2222:3333:4444::6666:7777:8888"));
+        assertTrue("IPV6 1111:2222:3333::6666:7777:8888 should be valid", validator.isValidInet6Address("1111:2222:3333::6666:7777:8888"));
+        assertTrue("IPV6 1111:2222::6666:7777:8888 should be valid", validator.isValidInet6Address("1111:2222::6666:7777:8888"));
+        assertTrue("IPV6 1111::6666:7777:8888 should be valid", validator.isValidInet6Address("1111::6666:7777:8888"));
+        assertTrue("IPV6 ::6666:7777:8888 should be valid", validator.isValidInet6Address("::6666:7777:8888"));
+        assertTrue("IPV6 1111:2222:3333::5555:6666:7777:8888 should be valid", validator.isValidInet6Address("1111:2222:3333::5555:6666:7777:8888"));
+        assertTrue("IPV6 1111:2222::5555:6666:7777:8888 should be valid", validator.isValidInet6Address("1111:2222::5555:6666:7777:8888"));
+        assertTrue("IPV6 1111::5555:6666:7777:8888 should be valid", validator.isValidInet6Address("1111::5555:6666:7777:8888"));
+        assertTrue("IPV6 ::5555:6666:7777:8888 should be valid", validator.isValidInet6Address("::5555:6666:7777:8888"));
+        assertTrue("IPV6 1111:2222::4444:5555:6666:7777:8888 should be valid", validator.isValidInet6Address("1111:2222::4444:5555:6666:7777:8888"));
+        assertTrue("IPV6 1111::4444:5555:6666:7777:8888 should be valid", validator.isValidInet6Address("1111::4444:5555:6666:7777:8888"));
+        assertTrue("IPV6 ::4444:5555:6666:7777:8888 should be valid", validator.isValidInet6Address("::4444:5555:6666:7777:8888"));
+        assertTrue("IPV6 1111::3333:4444:5555:6666:7777:8888 should be valid", validator.isValidInet6Address("1111::3333:4444:5555:6666:7777:8888"));
+        assertTrue("IPV6 ::3333:4444:5555:6666:7777:8888 should be valid", validator.isValidInet6Address("::3333:4444:5555:6666:7777:8888"));
+        assertTrue("IPV6 ::2222:3333:4444:5555:6666:7777:8888 should be valid", validator.isValidInet6Address("::2222:3333:4444:5555:6666:7777:8888"));
+        assertTrue("IPV6 1111:2222:3333:4444:5555:6666:123.123.123.123 should be valid", validator.isValidInet6Address("1111:2222:3333:4444:5555:6666:123.123.123.123"));
+        assertTrue("IPV6 1111:2222:3333:4444:5555::123.123.123.123 should be valid", validator.isValidInet6Address("1111:2222:3333:4444:5555::123.123.123.123"));
+        assertTrue("IPV6 1111:2222:3333:4444::123.123.123.123 should be valid", validator.isValidInet6Address("1111:2222:3333:4444::123.123.123.123"));
+        assertTrue("IPV6 1111:2222:3333::123.123.123.123 should be valid", validator.isValidInet6Address("1111:2222:3333::123.123.123.123"));
+        assertTrue("IPV6 1111:2222::123.123.123.123 should be valid", validator.isValidInet6Address("1111:2222::123.123.123.123"));
+        assertTrue("IPV6 1111::123.123.123.123 should be valid", validator.isValidInet6Address("1111::123.123.123.123"));
+        assertTrue("IPV6 ::123.123.123.123 should be valid", validator.isValidInet6Address("::123.123.123.123"));
+        assertTrue("IPV6 1111:2222:3333:4444::6666:123.123.123.123 should be valid", validator.isValidInet6Address("1111:2222:3333:4444::6666:123.123.123.123"));
+        assertTrue("IPV6 1111:2222:3333::6666:123.123.123.123 should be valid", validator.isValidInet6Address("1111:2222:3333::6666:123.123.123.123"));
+        assertTrue("IPV6 1111:2222::6666:123.123.123.123 should be valid", validator.isValidInet6Address("1111:2222::6666:123.123.123.123"));
+        assertTrue("IPV6 1111::6666:123.123.123.123 should be valid", validator.isValidInet6Address("1111::6666:123.123.123.123"));
+        assertTrue("IPV6 ::6666:123.123.123.123 should be valid", validator.isValidInet6Address("::6666:123.123.123.123"));
+        assertTrue("IPV6 1111:2222:3333::5555:6666:123.123.123.123 should be valid", validator.isValidInet6Address("1111:2222:3333::5555:6666:123.123.123.123"));
+        assertTrue("IPV6 1111:2222::5555:6666:123.123.123.123 should be valid", validator.isValidInet6Address("1111:2222::5555:6666:123.123.123.123"));
+        assertTrue("IPV6 1111::5555:6666:123.123.123.123 should be valid", validator.isValidInet6Address("1111::5555:6666:123.123.123.123"));
+        assertTrue("IPV6 ::5555:6666:123.123.123.123 should be valid", validator.isValidInet6Address("::5555:6666:123.123.123.123"));
+        assertTrue("IPV6 1111:2222::4444:5555:6666:123.123.123.123 should be valid", validator.isValidInet6Address("1111:2222::4444:5555:6666:123.123.123.123"));
+        assertTrue("IPV6 1111::4444:5555:6666:123.123.123.123 should be valid", validator.isValidInet6Address("1111::4444:5555:6666:123.123.123.123"));
+        assertTrue("IPV6 ::4444:5555:6666:123.123.123.123 should be valid", validator.isValidInet6Address("::4444:5555:6666:123.123.123.123"));
+        assertTrue("IPV6 1111::3333:4444:5555:6666:123.123.123.123 should be valid", validator.isValidInet6Address("1111::3333:4444:5555:6666:123.123.123.123"));
+        assertTrue("IPV6 ::2222:3333:4444:5555:6666:123.123.123.123 should be valid", validator.isValidInet6Address("::2222:3333:4444:5555:6666:123.123.123.123"));
+        // Trying combinations of "0" and "::"
+        // These are all syntactically correct, but are bad form
+        // because "0" adjacent to "::" should be combined into "::"
+        assertTrue("IPV6 ::0:0:0:0:0:0:0 should be valid", validator.isValidInet6Address("::0:0:0:0:0:0:0"));
+        assertTrue("IPV6 ::0:0:0:0:0:0 should be valid", validator.isValidInet6Address("::0:0:0:0:0:0"));
+        assertTrue("IPV6 ::0:0:0:0:0 should be valid", validator.isValidInet6Address("::0:0:0:0:0"));
+        assertTrue("IPV6 ::0:0:0:0 should be valid", validator.isValidInet6Address("::0:0:0:0"));
+        assertTrue("IPV6 ::0:0:0 should be valid", validator.isValidInet6Address("::0:0:0"));
+        assertTrue("IPV6 ::0:0 should be valid", validator.isValidInet6Address("::0:0"));
+        assertTrue("IPV6 ::0 should be valid", validator.isValidInet6Address("::0"));
+        assertTrue("IPV6 0:0:0:0:0:0:0:: should be valid", validator.isValidInet6Address("0:0:0:0:0:0:0::"));
+        assertTrue("IPV6 0:0:0:0:0:0:: should be valid", validator.isValidInet6Address("0:0:0:0:0:0::"));
+        assertTrue("IPV6 0:0:0:0:0:: should be valid", validator.isValidInet6Address("0:0:0:0:0::"));
+        assertTrue("IPV6 0:0:0:0:: should be valid", validator.isValidInet6Address("0:0:0:0::"));
+        assertTrue("IPV6 0:0:0:: should be valid", validator.isValidInet6Address("0:0:0::"));
+        assertTrue("IPV6 0:0:: should be valid", validator.isValidInet6Address("0:0::"));
+        assertTrue("IPV6 0:: should be valid", validator.isValidInet6Address("0::"));
+        // Invalid data
+        assertFalse("IPV6 XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX should be invalid", validator.isValidInet6Address("XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX"));
+        // Too many components
+        assertFalse("IPV6 1111:2222:3333:4444:5555:6666:7777:8888:9999 should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555:6666:7777:8888:9999"));
+        assertFalse("IPV6 1111:2222:3333:4444:5555:6666:7777:8888:: should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555:6666:7777:8888::"));
+        assertFalse("IPV6 ::2222:3333:4444:5555:6666:7777:8888:9999 should be invalid", validator.isValidInet6Address("::2222:3333:4444:5555:6666:7777:8888:9999"));
+        // Too few components
+        assertFalse("IPV6 1111:2222:3333:4444:5555:6666:7777 should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555:6666:7777"));
+        assertFalse("IPV6 1111:2222:3333:4444:5555:6666 should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555:6666"));
+        assertFalse("IPV6 1111:2222:3333:4444:5555 should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555"));
+        assertFalse("IPV6 1111:2222:3333:4444 should be invalid", validator.isValidInet6Address("1111:2222:3333:4444"));
+        assertFalse("IPV6 1111:2222:3333 should be invalid", validator.isValidInet6Address("1111:2222:3333"));
+        assertFalse("IPV6 1111:2222 should be invalid", validator.isValidInet6Address("1111:2222"));
+        assertFalse("IPV6 1111 should be invalid", validator.isValidInet6Address("1111"));
+        // Missing :
+        assertFalse("IPV6 11112222:3333:4444:5555:6666:7777:8888 should be invalid", validator.isValidInet6Address("11112222:3333:4444:5555:6666:7777:8888"));
+        assertFalse("IPV6 1111:22223333:4444:5555:6666:7777:8888 should be invalid", validator.isValidInet6Address("1111:22223333:4444:5555:6666:7777:8888"));
+        assertFalse("IPV6 1111:2222:33334444:5555:6666:7777:8888 should be invalid", validator.isValidInet6Address("1111:2222:33334444:5555:6666:7777:8888"));
+        assertFalse("IPV6 1111:2222:3333:44445555:6666:7777:8888 should be invalid", validator.isValidInet6Address("1111:2222:3333:44445555:6666:7777:8888"));
+        assertFalse("IPV6 1111:2222:3333:4444:55556666:7777:8888 should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:55556666:7777:8888"));
+        assertFalse("IPV6 1111:2222:3333:4444:5555:66667777:8888 should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555:66667777:8888"));
+        assertFalse("IPV6 1111:2222:3333:4444:5555:6666:77778888 should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555:6666:77778888"));
+        // Missing : intended for ::
+        assertFalse("IPV6 1111:2222:3333:4444:5555:6666:7777:8888: should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555:6666:7777:8888:"));
+        assertFalse("IPV6 1111:2222:3333:4444:5555:6666:7777: should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555:6666:7777:"));
+        assertFalse("IPV6 1111:2222:3333:4444:5555:6666: should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555:6666:"));
+        assertFalse("IPV6 1111:2222:3333:4444:5555: should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555:"));
+        assertFalse("IPV6 1111:2222:3333:4444: should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:"));
+        assertFalse("IPV6 1111:2222:3333: should be invalid", validator.isValidInet6Address("1111:2222:3333:"));
+        assertFalse("IPV6 1111:2222: should be invalid", validator.isValidInet6Address("1111:2222:"));
+        assertFalse("IPV6 :8888 should be invalid", validator.isValidInet6Address(":8888"));
+        assertFalse("IPV6 :7777:8888 should be invalid", validator.isValidInet6Address(":7777:8888"));
+        assertFalse("IPV6 :6666:7777:8888 should be invalid", validator.isValidInet6Address(":6666:7777:8888"));
+        assertFalse("IPV6 :5555:6666:7777:8888 should be invalid", validator.isValidInet6Address(":5555:6666:7777:8888"));
+        assertFalse("IPV6 :4444:5555:6666:7777:8888 should be invalid", validator.isValidInet6Address(":4444:5555:6666:7777:8888"));
+        assertFalse("IPV6 :3333:4444:5555:6666:7777:8888 should be invalid", validator.isValidInet6Address(":3333:4444:5555:6666:7777:8888"));
+        assertFalse("IPV6 :2222:3333:4444:5555:6666:7777:8888 should be invalid", validator.isValidInet6Address(":2222:3333:4444:5555:6666:7777:8888"));
+        assertFalse("IPV6 :1111:2222:3333:4444:5555:6666:7777:8888 should be invalid", validator.isValidInet6Address(":1111:2222:3333:4444:5555:6666:7777:8888"));
+        // :::
+        assertFalse("IPV6 :::2222:3333:4444:5555:6666:7777:8888 should be invalid", validator.isValidInet6Address(":::2222:3333:4444:5555:6666:7777:8888"));
+        assertFalse("IPV6 1111:::3333:4444:5555:6666:7777:8888 should be invalid", validator.isValidInet6Address("1111:::3333:4444:5555:6666:7777:8888"));
+        assertFalse("IPV6 1111:2222:::4444:5555:6666:7777:8888 should be invalid", validator.isValidInet6Address("1111:2222:::4444:5555:6666:7777:8888"));
+        assertFalse("IPV6 1111:2222:3333:::5555:6666:7777:8888 should be invalid", validator.isValidInet6Address("1111:2222:3333:::5555:6666:7777:8888"));
+        assertFalse("IPV6 1111:2222:3333:4444:::6666:7777:8888 should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:::6666:7777:8888"));
+        assertFalse("IPV6 1111:2222:3333:4444:5555:::7777:8888 should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555:::7777:8888"));
+        assertFalse("IPV6 1111:2222:3333:4444:5555:6666:::8888 should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555:6666:::8888"));
+        assertFalse("IPV6 1111:2222:3333:4444:5555:6666:7777::: should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555:6666:7777:::"));
+        // Double ::
+        assertFalse("IPV6 ::2222::4444:5555:6666:7777:8888 should be invalid", validator.isValidInet6Address("::2222::4444:5555:6666:7777:8888"));
+        assertFalse("IPV6 ::2222:3333::5555:6666:7777:8888 should be invalid", validator.isValidInet6Address("::2222:3333::5555:6666:7777:8888"));
+        assertFalse("IPV6 ::2222:3333:4444::6666:7777:8888 should be invalid", validator.isValidInet6Address("::2222:3333:4444::6666:7777:8888"));
+        assertFalse("IPV6 ::2222:3333:4444:5555::7777:8888 should be invalid", validator.isValidInet6Address("::2222:3333:4444:5555::7777:8888"));
+        assertFalse("IPV6 ::2222:3333:4444:5555:7777::8888 should be invalid", validator.isValidInet6Address("::2222:3333:4444:5555:7777::8888"));
+        assertFalse("IPV6 ::2222:3333:4444:5555:7777:8888:: should be invalid", validator.isValidInet6Address("::2222:3333:4444:5555:7777:8888::"));
+        assertFalse("IPV6 1111::3333::5555:6666:7777:8888 should be invalid", validator.isValidInet6Address("1111::3333::5555:6666:7777:8888"));
+        assertFalse("IPV6 1111::3333:4444::6666:7777:8888 should be invalid", validator.isValidInet6Address("1111::3333:4444::6666:7777:8888"));
+        assertFalse("IPV6 1111::3333:4444:5555::7777:8888 should be invalid", validator.isValidInet6Address("1111::3333:4444:5555::7777:8888"));
+        assertFalse("IPV6 1111::3333:4444:5555:6666::8888 should be invalid", validator.isValidInet6Address("1111::3333:4444:5555:6666::8888"));
+        assertFalse("IPV6 1111::3333:4444:5555:6666:7777:: should be invalid", validator.isValidInet6Address("1111::3333:4444:5555:6666:7777::"));
+        assertFalse("IPV6 1111:2222::4444::6666:7777:8888 should be invalid", validator.isValidInet6Address("1111:2222::4444::6666:7777:8888"));
+        assertFalse("IPV6 1111:2222::4444:5555::7777:8888 should be invalid", validator.isValidInet6Address("1111:2222::4444:5555::7777:8888"));
+        assertFalse("IPV6 1111:2222::4444:5555:6666::8888 should be invalid", validator.isValidInet6Address("1111:2222::4444:5555:6666::8888"));
+        assertFalse("IPV6 1111:2222::4444:5555:6666:7777:: should be invalid", validator.isValidInet6Address("1111:2222::4444:5555:6666:7777::"));
+        assertFalse("IPV6 1111:2222:3333::5555::7777:8888 should be invalid", validator.isValidInet6Address("1111:2222:3333::5555::7777:8888"));
+        assertFalse("IPV6 1111:2222:3333::5555:6666::8888 should be invalid", validator.isValidInet6Address("1111:2222:3333::5555:6666::8888"));
+        assertFalse("IPV6 1111:2222:3333::5555:6666:7777:: should be invalid", validator.isValidInet6Address("1111:2222:3333::5555:6666:7777::"));
+        assertFalse("IPV6 1111:2222:3333:4444::6666::8888 should be invalid", validator.isValidInet6Address("1111:2222:3333:4444::6666::8888"));
+        assertFalse("IPV6 1111:2222:3333:4444::6666:7777:: should be invalid", validator.isValidInet6Address("1111:2222:3333:4444::6666:7777::"));
+        assertFalse("IPV6 1111:2222:3333:4444:5555::7777:: should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555::7777::"));
+        // Too many components"
+        assertFalse("IPV6 1111:2222:3333:4444:5555:6666:7777:8888:1.2.3.4 should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555:6666:7777:8888:1.2.3.4"));
+        assertFalse("IPV6 1111:2222:3333:4444:5555:6666:7777:1.2.3.4 should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555:6666:7777:1.2.3.4"));
+        assertFalse("IPV6 1111:2222:3333:4444:5555:6666::1.2.3.4 should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555:6666::1.2.3.4"));
+        assertFalse("IPV6 ::2222:3333:4444:5555:6666:7777:1.2.3.4 should be invalid", validator.isValidInet6Address("::2222:3333:4444:5555:6666:7777:1.2.3.4"));
+        assertFalse("IPV6 1111:2222:3333:4444:5555:6666:1.2.3.4.5 should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555:6666:1.2.3.4.5"));
+        // Too few components
+        assertFalse("IPV6 1111:2222:3333:4444:5555:1.2.3.4 should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555:1.2.3.4"));
+        assertFalse("IPV6 1111:2222:3333:4444:1.2.3.4 should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:1.2.3.4"));
+        assertFalse("IPV6 1111:2222:3333:1.2.3.4 should be invalid", validator.isValidInet6Address("1111:2222:3333:1.2.3.4"));
+        assertFalse("IPV6 1111:2222:1.2.3.4 should be invalid", validator.isValidInet6Address("1111:2222:1.2.3.4"));
+        assertFalse("IPV6 1111:1.2.3.4 should be invalid", validator.isValidInet6Address("1111:1.2.3.4"));
+        assertFalse("IPV6 1.2.3.4 should be invalid", validator.isValidInet6Address("1.2.3.4"));
+        // Missing :
+        assertFalse("IPV6 11112222:3333:4444:5555:6666:1.2.3.4 should be invalid", validator.isValidInet6Address("11112222:3333:4444:5555:6666:1.2.3.4"));
+        assertFalse("IPV6 1111:22223333:4444:5555:6666:1.2.3.4 should be invalid", validator.isValidInet6Address("1111:22223333:4444:5555:6666:1.2.3.4"));
+        assertFalse("IPV6 1111:2222:33334444:5555:6666:1.2.3.4 should be invalid", validator.isValidInet6Address("1111:2222:33334444:5555:6666:1.2.3.4"));
+        assertFalse("IPV6 1111:2222:3333:44445555:6666:1.2.3.4 should be invalid", validator.isValidInet6Address("1111:2222:3333:44445555:6666:1.2.3.4"));
+        assertFalse("IPV6 1111:2222:3333:4444:55556666:1.2.3.4 should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:55556666:1.2.3.4"));
+        assertFalse("IPV6 1111:2222:3333:4444:5555:66661.2.3.4 should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555:66661.2.3.4"));
+        // Missing .
+        assertFalse("IPV6 1111:2222:3333:4444:5555:6666:255255.255.255 should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555:6666:255255.255.255"));
+        assertFalse("IPV6 1111:2222:3333:4444:5555:6666:255.255255.255 should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555:6666:255.255255.255"));
+        assertFalse("IPV6 1111:2222:3333:4444:5555:6666:255.255.255255 should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555:6666:255.255.255255"));
+        // Missing : intended for ::
+        assertFalse("IPV6 :1.2.3.4 should be invalid", validator.isValidInet6Address(":1.2.3.4"));
+        assertFalse("IPV6 :6666:1.2.3.4 should be invalid", validator.isValidInet6Address(":6666:1.2.3.4"));
+        assertFalse("IPV6 :5555:6666:1.2.3.4 should be invalid", validator.isValidInet6Address(":5555:6666:1.2.3.4"));
+        assertFalse("IPV6 :4444:5555:6666:1.2.3.4 should be invalid", validator.isValidInet6Address(":4444:5555:6666:1.2.3.4"));
+        assertFalse("IPV6 :3333:4444:5555:6666:1.2.3.4 should be invalid", validator.isValidInet6Address(":3333:4444:5555:6666:1.2.3.4"));
+        assertFalse("IPV6 :2222:3333:4444:5555:6666:1.2.3.4 should be invalid", validator.isValidInet6Address(":2222:3333:4444:5555:6666:1.2.3.4"));
+        assertFalse("IPV6 :1111:2222:3333:4444:5555:6666:1.2.3.4 should be invalid", validator.isValidInet6Address(":1111:2222:3333:4444:5555:6666:1.2.3.4"));
+        // :::
+        assertFalse("IPV6 :::2222:3333:4444:5555:6666:1.2.3.4 should be invalid", validator.isValidInet6Address(":::2222:3333:4444:5555:6666:1.2.3.4"));
+        assertFalse("IPV6 1111:::3333:4444:5555:6666:1.2.3.4 should be invalid", validator.isValidInet6Address("1111:::3333:4444:5555:6666:1.2.3.4"));
+        assertFalse("IPV6 1111:2222:::4444:5555:6666:1.2.3.4 should be invalid", validator.isValidInet6Address("1111:2222:::4444:5555:6666:1.2.3.4"));
+        assertFalse("IPV6 1111:2222:3333:::5555:6666:1.2.3.4 should be invalid", validator.isValidInet6Address("1111:2222:3333:::5555:6666:1.2.3.4"));
+        assertFalse("IPV6 1111:2222:3333:4444:::6666:1.2.3.4 should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:::6666:1.2.3.4"));
+        assertFalse("IPV6 1111:2222:3333:4444:5555:::1.2.3.4 should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555:::1.2.3.4"));
+        // Double ::
+        assertFalse("IPV6 ::2222::4444:5555:6666:1.2.3.4 should be invalid", validator.isValidInet6Address("::2222::4444:5555:6666:1.2.3.4"));
+        assertFalse("IPV6 ::2222:3333::5555:6666:1.2.3.4 should be invalid", validator.isValidInet6Address("::2222:3333::5555:6666:1.2.3.4"));
+        assertFalse("IPV6 ::2222:3333:4444::6666:1.2.3.4 should be invalid", validator.isValidInet6Address("::2222:3333:4444::6666:1.2.3.4"));
+        assertFalse("IPV6 ::2222:3333:4444:5555::1.2.3.4 should be invalid", validator.isValidInet6Address("::2222:3333:4444:5555::1.2.3.4"));
+        assertFalse("IPV6 1111::3333::5555:6666:1.2.3.4 should be invalid", validator.isValidInet6Address("1111::3333::5555:6666:1.2.3.4"));
+        assertFalse("IPV6 1111::3333:4444::6666:1.2.3.4 should be invalid", validator.isValidInet6Address("1111::3333:4444::6666:1.2.3.4"));
+        assertFalse("IPV6 1111::3333:4444:5555::1.2.3.4 should be invalid", validator.isValidInet6Address("1111::3333:4444:5555::1.2.3.4"));
+        assertFalse("IPV6 1111:2222::4444::6666:1.2.3.4 should be invalid", validator.isValidInet6Address("1111:2222::4444::6666:1.2.3.4"));
+        assertFalse("IPV6 1111:2222::4444:5555::1.2.3.4 should be invalid", validator.isValidInet6Address("1111:2222::4444:5555::1.2.3.4"));
+        assertFalse("IPV6 1111:2222:3333::5555::1.2.3.4 should be invalid", validator.isValidInet6Address("1111:2222:3333::5555::1.2.3.4"));
+        // Missing parts
+        assertFalse("IPV6 ::. should be invalid", validator.isValidInet6Address("::."));
+        assertFalse("IPV6 ::.. should be invalid", validator.isValidInet6Address("::.."));
+        assertFalse("IPV6 ::... should be invalid", validator.isValidInet6Address("::..."));
+        assertFalse("IPV6 ::1... should be invalid", validator.isValidInet6Address("::1..."));
+        assertFalse("IPV6 ::1.2.. should be invalid", validator.isValidInet6Address("::1.2.."));
+        assertFalse("IPV6 ::1.2.3. should be invalid", validator.isValidInet6Address("::1.2.3."));
+        assertFalse("IPV6 ::.2.. should be invalid", validator.isValidInet6Address("::.2.."));
+        assertFalse("IPV6 ::.2.3. should be invalid", validator.isValidInet6Address("::.2.3."));
+        assertFalse("IPV6 ::.2.3.4 should be invalid", validator.isValidInet6Address("::.2.3.4"));
+        assertFalse("IPV6 ::..3. should be invalid", validator.isValidInet6Address("::..3."));
+        assertFalse("IPV6 ::..3.4 should be invalid", validator.isValidInet6Address("::..3.4"));
+        assertFalse("IPV6 ::...4 should be invalid", validator.isValidInet6Address("::...4"));
+        // Extra : in front
+        assertFalse("IPV6 :1111:2222:3333:4444:5555:6666:7777:: should be invalid", validator.isValidInet6Address(":1111:2222:3333:4444:5555:6666:7777::"));
+        assertFalse("IPV6 :1111:2222:3333:4444:5555:6666:: should be invalid", validator.isValidInet6Address(":1111:2222:3333:4444:5555:6666::"));
+        assertFalse("IPV6 :1111:2222:3333:4444:5555:: should be invalid", validator.isValidInet6Address(":1111:2222:3333:4444:5555::"));
+        assertFalse("IPV6 :1111:2222:3333:4444:: should be invalid", validator.isValidInet6Address(":1111:2222:3333:4444::"));
+        assertFalse("IPV6 :1111:2222:3333:: should be invalid", validator.isValidInet6Address(":1111:2222:3333::"));
+        assertFalse("IPV6 :1111:2222:: should be invalid", validator.isValidInet6Address(":1111:2222::"));
+        assertFalse("IPV6 :1111:: should be invalid", validator.isValidInet6Address(":1111::"));
+        assertFalse("IPV6 :1111:2222:3333:4444:5555:6666::8888 should be invalid", validator.isValidInet6Address(":1111:2222:3333:4444:5555:6666::8888"));
+        assertFalse("IPV6 :1111:2222:3333:4444:5555::8888 should be invalid", validator.isValidInet6Address(":1111:2222:3333:4444:5555::8888"));
+        assertFalse("IPV6 :1111:2222:3333:4444::8888 should be invalid", validator.isValidInet6Address(":1111:2222:3333:4444::8888"));
+        assertFalse("IPV6 :1111:2222:3333::8888 should be invalid", validator.isValidInet6Address(":1111:2222:3333::8888"));
+        assertFalse("IPV6 :1111:2222::8888 should be invalid", validator.isValidInet6Address(":1111:2222::8888"));
+        assertFalse("IPV6 :1111::8888 should be invalid", validator.isValidInet6Address(":1111::8888"));
+        assertFalse("IPV6 :::8888 should be invalid", validator.isValidInet6Address(":::8888"));
+        assertFalse("IPV6 :1111:2222:3333:4444:5555::7777:8888 should be invalid", validator.isValidInet6Address(":1111:2222:3333:4444:5555::7777:8888"));
+        assertFalse("IPV6 :1111:2222:3333:4444::7777:8888 should be invalid", validator.isValidInet6Address(":1111:2222:3333:4444::7777:8888"));
+        assertFalse("IPV6 :1111:2222:3333::7777:8888 should be invalid", validator.isValidInet6Address(":1111:2222:3333::7777:8888"));
+        assertFalse("IPV6 :1111:2222::7777:8888 should be invalid", validator.isValidInet6Address(":1111:2222::7777:8888"));
+        assertFalse("IPV6 :1111::7777:8888 should be invalid", validator.isValidInet6Address(":1111::7777:8888"));
+        assertFalse("IPV6 :::7777:8888 should be invalid", validator.isValidInet6Address(":::7777:8888"));
+        assertFalse("IPV6 :1111:2222:3333:4444::6666:7777:8888 should be invalid", validator.isValidInet6Address(":1111:2222:3333:4444::6666:7777:8888"));
+        assertFalse("IPV6 :1111:2222:3333::6666:7777:8888 should be invalid", validator.isValidInet6Address(":1111:2222:3333::6666:7777:8888"));
+        assertFalse("IPV6 :1111:2222::6666:7777:8888 should be invalid", validator.isValidInet6Address(":1111:2222::6666:7777:8888"));
+        assertFalse("IPV6 :1111::6666:7777:8888 should be invalid", validator.isValidInet6Address(":1111::6666:7777:8888"));
+        assertFalse("IPV6 :::6666:7777:8888 should be invalid", validator.isValidInet6Address(":::6666:7777:8888"));
+        assertFalse("IPV6 :1111:2222:3333::5555:6666:7777:8888 should be invalid", validator.isValidInet6Address(":1111:2222:3333::5555:6666:7777:8888"));
+        assertFalse("IPV6 :1111:2222::5555:6666:7777:8888 should be invalid", validator.isValidInet6Address(":1111:2222::5555:6666:7777:8888"));
+        assertFalse("IPV6 :1111::5555:6666:7777:8888 should be invalid", validator.isValidInet6Address(":1111::5555:6666:7777:8888"));
+        assertFalse("IPV6 :::5555:6666:7777:8888 should be invalid", validator.isValidInet6Address(":::5555:6666:7777:8888"));
+        assertFalse("IPV6 :1111:2222::4444:5555:6666:7777:8888 should be invalid", validator.isValidInet6Address(":1111:2222::4444:5555:6666:7777:8888"));
+        assertFalse("IPV6 :1111::4444:5555:6666:7777:8888 should be invalid", validator.isValidInet6Address(":1111::4444:5555:6666:7777:8888"));
+        assertFalse("IPV6 :::4444:5555:6666:7777:8888 should be invalid", validator.isValidInet6Address(":::4444:5555:6666:7777:8888"));
+        assertFalse("IPV6 :1111::3333:4444:5555:6666:7777:8888 should be invalid", validator.isValidInet6Address(":1111::3333:4444:5555:6666:7777:8888"));
+        assertFalse("IPV6 :::3333:4444:5555:6666:7777:8888 should be invalid", validator.isValidInet6Address(":::3333:4444:5555:6666:7777:8888"));
+        assertFalse("IPV6 :::2222:3333:4444:5555:6666:7777:8888 should be invalid", validator.isValidInet6Address(":::2222:3333:4444:5555:6666:7777:8888"));
+        assertFalse("IPV6 :1111:2222:3333:4444:5555:6666:1.2.3.4 should be invalid", validator.isValidInet6Address(":1111:2222:3333:4444:5555:6666:1.2.3.4"));
+        assertFalse("IPV6 :1111:2222:3333:4444:5555::1.2.3.4 should be invalid", validator.isValidInet6Address(":1111:2222:3333:4444:5555::1.2.3.4"));
+        assertFalse("IPV6 :1111:2222:3333:4444::1.2.3.4 should be invalid", validator.isValidInet6Address(":1111:2222:3333:4444::1.2.3.4"));
+        assertFalse("IPV6 :1111:2222:3333::1.2.3.4 should be invalid", validator.isValidInet6Address(":1111:2222:3333::1.2.3.4"));
+        assertFalse("IPV6 :1111:2222::1.2.3.4 should be invalid", validator.isValidInet6Address(":1111:2222::1.2.3.4"));
+        assertFalse("IPV6 :1111::1.2.3.4 should be invalid", validator.isValidInet6Address(":1111::1.2.3.4"));
+        assertFalse("IPV6 :::1.2.3.4 should be invalid", validator.isValidInet6Address(":::1.2.3.4"));
+        assertFalse("IPV6 :1111:2222:3333:4444::6666:1.2.3.4 should be invalid", validator.isValidInet6Address(":1111:2222:3333:4444::6666:1.2.3.4"));
+        assertFalse("IPV6 :1111:2222:3333::6666:1.2.3.4 should be invalid", validator.isValidInet6Address(":1111:2222:3333::6666:1.2.3.4"));
+        assertFalse("IPV6 :1111:2222::6666:1.2.3.4 should be invalid", validator.isValidInet6Address(":1111:2222::6666:1.2.3.4"));
+        assertFalse("IPV6 :1111::6666:1.2.3.4 should be invalid", validator.isValidInet6Address(":1111::6666:1.2.3.4"));
+        assertFalse("IPV6 :::6666:1.2.3.4 should be invalid", validator.isValidInet6Address(":::6666:1.2.3.4"));
+        assertFalse("IPV6 :1111:2222:3333::5555:6666:1.2.3.4 should be invalid", validator.isValidInet6Address(":1111:2222:3333::5555:6666:1.2.3.4"));
+        assertFalse("IPV6 :1111:2222::5555:6666:1.2.3.4 should be invalid", validator.isValidInet6Address(":1111:2222::5555:6666:1.2.3.4"));
+        assertFalse("IPV6 :1111::5555:6666:1.2.3.4 should be invalid", validator.isValidInet6Address(":1111::5555:6666:1.2.3.4"));
+        assertFalse("IPV6 :::5555:6666:1.2.3.4 should be invalid", validator.isValidInet6Address(":::5555:6666:1.2.3.4"));
+        assertFalse("IPV6 :1111:2222::4444:5555:6666:1.2.3.4 should be invalid", validator.isValidInet6Address(":1111:2222::4444:5555:6666:1.2.3.4"));
+        assertFalse("IPV6 :1111::4444:5555:6666:1.2.3.4 should be invalid", validator.isValidInet6Address(":1111::4444:5555:6666:1.2.3.4"));
+        assertFalse("IPV6 :::4444:5555:6666:1.2.3.4 should be invalid", validator.isValidInet6Address(":::4444:5555:6666:1.2.3.4"));
+        assertFalse("IPV6 :1111::3333:4444:5555:6666:1.2.3.4 should be invalid", validator.isValidInet6Address(":1111::3333:4444:5555:6666:1.2.3.4"));
+        assertFalse("IPV6 :::2222:3333:4444:5555:6666:1.2.3.4 should be invalid", validator.isValidInet6Address(":::2222:3333:4444:5555:6666:1.2.3.4"));
+        // Extra : at end
+        assertFalse("IPV6 1111:2222:3333:4444:5555:6666:7777::: should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555:6666:7777:::"));
+        assertFalse("IPV6 1111:2222:3333:4444:5555:6666::: should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555:6666:::"));
+        assertFalse("IPV6 1111:2222:3333:4444:5555::: should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555:::"));
+        assertFalse("IPV6 1111:2222:3333:4444::: should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:::"));
+        assertFalse("IPV6 1111:2222:3333::: should be invalid", validator.isValidInet6Address("1111:2222:3333:::"));
+        assertFalse("IPV6 1111:2222::: should be invalid", validator.isValidInet6Address("1111:2222:::"));
+        assertFalse("IPV6 1111::: should be invalid", validator.isValidInet6Address("1111:::"));
+        assertFalse("IPV6 1111:2222:3333:4444:5555:6666::8888: should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555:6666::8888:"));
+        assertFalse("IPV6 1111:2222:3333:4444:5555::8888: should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555::8888:"));
+        assertFalse("IPV6 1111:2222:3333:4444::8888: should be invalid", validator.isValidInet6Address("1111:2222:3333:4444::8888:"));
+        assertFalse("IPV6 1111:2222:3333::8888: should be invalid", validator.isValidInet6Address("1111:2222:3333::8888:"));
+        assertFalse("IPV6 1111:2222::8888: should be invalid", validator.isValidInet6Address("1111:2222::8888:"));
+        assertFalse("IPV6 1111::8888: should be invalid", validator.isValidInet6Address("1111::8888:"));
+        assertFalse("IPV6 ::8888: should be invalid", validator.isValidInet6Address("::8888:"));
+        assertFalse("IPV6 1111:2222:3333:4444:5555::7777:8888: should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555::7777:8888:"));
+        assertFalse("IPV6 1111:2222:3333:4444::7777:8888: should be invalid", validator.isValidInet6Address("1111:2222:3333:4444::7777:8888:"));
+        assertFalse("IPV6 1111:2222:3333::7777:8888: should be invalid", validator.isValidInet6Address("1111:2222:3333::7777:8888:"));
+        assertFalse("IPV6 1111:2222::7777:8888: should be invalid", validator.isValidInet6Address("1111:2222::7777:8888:"));
+        assertFalse("IPV6 1111::7777:8888: should be invalid", validator.isValidInet6Address("1111::7777:8888:"));
+        assertFalse("IPV6 ::7777:8888: should be invalid", validator.isValidInet6Address("::7777:8888:"));
+        assertFalse("IPV6 1111:2222:3333:4444::6666:7777:8888: should be invalid", validator.isValidInet6Address("1111:2222:3333:4444::6666:7777:8888:"));
+        assertFalse("IPV6 1111:2222:3333::6666:7777:8888: should be invalid", validator.isValidInet6Address("1111:2222:3333::6666:7777:8888:"));
+        assertFalse("IPV6 1111:2222::6666:7777:8888: should be invalid", validator.isValidInet6Address("1111:2222::6666:7777:8888:"));
+        assertFalse("IPV6 1111::6666:7777:8888: should be invalid", validator.isValidInet6Address("1111::6666:7777:8888:"));
+        assertFalse("IPV6 ::6666:7777:8888: should be invalid", validator.isValidInet6Address("::6666:7777:8888:"));
+        assertFalse("IPV6 1111:2222:3333::5555:6666:7777:8888: should be invalid", validator.isValidInet6Address("1111:2222:3333::5555:6666:7777:8888:"));
+        assertFalse("IPV6 1111:2222::5555:6666:7777:8888: should be invalid", validator.isValidInet6Address("1111:2222::5555:6666:7777:8888:"));
+        assertFalse("IPV6 1111::5555:6666:7777:8888: should be invalid", validator.isValidInet6Address("1111::5555:6666:7777:8888:"));
+        assertFalse("IPV6 ::5555:6666:7777:8888: should be invalid", validator.isValidInet6Address("::5555:6666:7777:8888:"));
+        assertFalse("IPV6 1111:2222::4444:5555:6666:7777:8888: should be invalid", validator.isValidInet6Address("1111:2222::4444:5555:6666:7777:8888:"));
+        assertFalse("IPV6 1111::4444:5555:6666:7777:8888: should be invalid", validator.isValidInet6Address("1111::4444:5555:6666:7777:8888:"));
+        assertFalse("IPV6 ::4444:5555:6666:7777:8888: should be invalid", validator.isValidInet6Address("::4444:5555:6666:7777:8888:"));
+        assertFalse("IPV6 1111::3333:4444:5555:6666:7777:8888: should be invalid", validator.isValidInet6Address("1111::3333:4444:5555:6666:7777:8888:"));
+        assertFalse("IPV6 ::3333:4444:5555:6666:7777:8888: should be invalid", validator.isValidInet6Address("::3333:4444:5555:6666:7777:8888:"));
+        assertFalse("IPV6 ::2222:3333:4444:5555:6666:7777:8888: should be invalid", validator.isValidInet6Address("::2222:3333:4444:5555:6666:7777:8888:"));
+        assertTrue("IPV6 0:a:b:c:d:e:f:: should be valid", validator.isValidInet6Address("0:a:b:c:d:e:f::"));
+        assertTrue("IPV6 ::0:a:b:c:d:e:f should be valid", validator.isValidInet6Address("::0:a:b:c:d:e:f")); // syntactically correct, but bad form (::0:... could be combined)
+        assertTrue("IPV6 a:b:c:d:e:f:0:: should be valid", validator.isValidInet6Address("a:b:c:d:e:f:0::"));
+        assertFalse("IPV6 ':10.0.0.1 should be invalid", validator.isValidInet6Address("':10.0.0.1"));
+    }
+    // CHECKSTYLE.ON: ExecutableStatementCount
+    // CHECKSTYLE.ON: MethodLengthCheck
+    // CHECKSTYLE.ON: LineLength
+}
Index: trunk/test/unit/org/openstreetmap/josm/data/validation/routines/RegexValidatorTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/data/validation/routines/RegexValidatorTest.java	(revision 9853)
+++ trunk/test/unit/org/openstreetmap/josm/data/validation/routines/RegexValidatorTest.java	(revision 9853)
@@ -0,0 +1,285 @@
+/*
+ * 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.openstreetmap.josm.data.validation.routines;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+
+import java.util.Arrays;
+import java.util.regex.PatternSyntaxException;
+
+import org.junit.Test;
+import org.openstreetmap.josm.Main;
+
+/**
+ * Test Case for RegexValidatorTest.
+ *
+ * @version $Revision: 1649191 $
+ * @since Validator 1.4
+ */
+public class RegexValidatorTest {
+
+    private static final String REGEX         = "^([abc]*)(?:\\-)([DEF]*)(?:\\-)([123]*)$";
+
+    private static final String COMPONENT_1 = "([abc]{3})";
+    private static final String COMPONENT_2 = "([DEF]{3})";
+    private static final String COMPONENT_3 = "([123]{3})";
+    private static final String SEPARATOR_1  = "(?:\\-)";
+    private static final String SEPARATOR_2  = "(?:\\s)";
+    private static final String REGEX_1 = "^" + COMPONENT_1 + SEPARATOR_1 + COMPONENT_2 + SEPARATOR_1 + COMPONENT_3 + "$";
+    private static final String REGEX_2 = "^" + COMPONENT_1 + SEPARATOR_2 + COMPONENT_2 + SEPARATOR_2 + COMPONENT_3 + "$";
+    private static final String REGEX_3 = "^" + COMPONENT_1 + COMPONENT_2 + COMPONENT_3 + "$";
+    private static final String[] MULTIPLE_REGEX = new String[] {REGEX_1, REGEX_2, REGEX_3};
+
+    /**
+     * Test instance methods with single regular expression.
+     */
+    @Test
+    public void testSingle() {
+        RegexValidator sensitive   = new RegexValidator(REGEX);
+        RegexValidator insensitive = new RegexValidator(REGEX, false);
+
+        // isValid()
+        assertEquals("Sensitive isValid() valid",     true,   sensitive.isValid("ac-DE-1"));
+        assertEquals("Sensitive isValid() invalid",   false,  sensitive.isValid("AB-de-1"));
+        assertEquals("Insensitive isValid() valid",   true,   insensitive.isValid("AB-de-1"));
+        assertEquals("Insensitive isValid() invalid", false,  insensitive.isValid("ABd-de-1"));
+
+        // validate()
+        assertEquals("Sensitive validate() valid",     "acDE1", sensitive.validate("ac-DE-1"));
+        assertEquals("Sensitive validate() invalid",   null,    sensitive.validate("AB-de-1"));
+        assertEquals("Insensitive validate() valid",   "ABde1", insensitive.validate("AB-de-1"));
+        assertEquals("Insensitive validate() invalid", null,    insensitive.validate("ABd-de-1"));
+
+        // match()
+        checkArray("Sensitive match() valid",     new String[] {"ac", "DE", "1"}, sensitive.match("ac-DE-1"));
+        checkArray("Sensitive match() invalid",   null,                           sensitive.match("AB-de-1"));
+        checkArray("Insensitive match() valid",   new String[] {"AB", "de", "1"}, insensitive.match("AB-de-1"));
+        checkArray("Insensitive match() invalid", null,                           insensitive.match("ABd-de-1"));
+        assertEquals("validate one", "ABC", (new RegexValidator("^([A-Z]*)$")).validate("ABC"));
+        checkArray("match one", new String[] {"ABC"}, (new RegexValidator("^([A-Z]*)$")).match("ABC"));
+    }
+
+    /**
+     * Test with multiple regular expressions (case sensitive).
+     */
+    @Test
+    public void testMultipleSensitive() {
+
+        // ------------ Set up Sensitive Validators
+        RegexValidator multiple   = new RegexValidator(MULTIPLE_REGEX);
+        RegexValidator single1   = new RegexValidator(REGEX_1);
+        RegexValidator single2   = new RegexValidator(REGEX_2);
+        RegexValidator single3   = new RegexValidator(REGEX_3);
+
+        // ------------ Set up test values
+        String value = "aac FDE 321";
+        String expect = "aacFDE321";
+        String[] array = new String[] {"aac", "FDE", "321"};
+
+        // isValid()
+        assertEquals("Sensitive isValid() Multiple", true,  multiple.isValid(value));
+        assertEquals("Sensitive isValid() 1st",      false, single1.isValid(value));
+        assertEquals("Sensitive isValid() 2nd",      true,  single2.isValid(value));
+        assertEquals("Sensitive isValid() 3rd",      false, single3.isValid(value));
+
+        // validate()
+        assertEquals("Sensitive validate() Multiple", expect, multiple.validate(value));
+        assertNull("Sensitive validate() 1st",      single1.validate(value));
+        assertEquals("Sensitive validate() 2nd",      expect, single2.validate(value));
+        assertNull("Sensitive validate() 3rd",      single3.validate(value));
+
+        // match()
+        checkArray("Sensitive match() Multiple", array, multiple.match(value));
+        checkArray("Sensitive match() 1st",      null,  single1.match(value));
+        checkArray("Sensitive match() 2nd",      array, single2.match(value));
+        checkArray("Sensitive match() 3rd",      null,  single3.match(value));
+
+        // All invalid
+        value = "AAC*FDE*321";
+        assertEquals("isValid() Invalid",  false, multiple.isValid(value));
+        assertNull("validate() Invalid", multiple.validate(value));
+        assertNull("match() Multiple",   multiple.match(value));
+    }
+
+    /**
+     * Test with multiple regular expressions (case in-sensitive).
+     */
+    @Test
+    public void testMultipleInsensitive() {
+
+        // ------------ Set up In-sensitive Validators
+        RegexValidator multiple = new RegexValidator(MULTIPLE_REGEX, false);
+        RegexValidator single1   = new RegexValidator(REGEX_1, false);
+        RegexValidator single2   = new RegexValidator(REGEX_2, false);
+        RegexValidator single3   = new RegexValidator(REGEX_3, false);
+
+        // ------------ Set up test values
+        String value = "AAC FDE 321";
+        String expect = "AACFDE321";
+        String[] array = new String[] {"AAC", "FDE", "321"};
+
+        // isValid()
+        assertEquals("isValid() Multiple", true,  multiple.isValid(value));
+        assertEquals("isValid() 1st",      false, single1.isValid(value));
+        assertEquals("isValid() 2nd",      true,  single2.isValid(value));
+        assertEquals("isValid() 3rd",      false, single3.isValid(value));
+
+        // validate()
+        assertEquals("validate() Multiple", expect, multiple.validate(value));
+        assertNull("validate() 1st",      single1.validate(value));
+        assertEquals("validate() 2nd",      expect, single2.validate(value));
+        assertNull("validate() 3rd",      single3.validate(value));
+
+        // match()
+        checkArray("match() Multiple", array, multiple.match(value));
+        checkArray("match() 1st",      null,  single1.match(value));
+        checkArray("match() 2nd",      array, single2.match(value));
+        checkArray("match() 3rd",      null,  single3.match(value));
+
+        // All invalid
+        value = "AAC*FDE*321";
+        assertEquals("isValid() Invalid",  false, multiple.isValid(value));
+        assertNull("validate() Invalid", multiple.validate(value));
+        assertNull("match() Multiple",   multiple.match(value));
+    }
+
+    /**
+     * Test Null value
+     */
+    @Test
+    public void testNullValue() {
+
+        RegexValidator validator = new RegexValidator(REGEX);
+        assertEquals("Instance isValid()",  false, validator.isValid(null));
+        assertNull("Instance validate()", validator.validate(null));
+        assertNull("Instance match()",    validator.match(null));
+    }
+
+    /**
+     * Test exceptions
+     */
+    @Test
+    public void testMissingRegex() {
+
+        // Single Regular Expression - null
+        try {
+            new RegexValidator((String) null);
+            fail("Single Null - expected IllegalArgumentException");
+        } catch (IllegalArgumentException e) {
+            assertEquals("Single Null", "Regular expression[0] is missing", e.getMessage());
+        }
+
+        // Single Regular Expression - Zero Length
+        try {
+            new RegexValidator("");
+            fail("Single Zero Length - expected IllegalArgumentException");
+        } catch (IllegalArgumentException e) {
+            assertEquals("Single Zero Length", "Regular expression[0] is missing", e.getMessage());
+        }
+
+        // Multiple Regular Expression - Null array
+        try {
+            new RegexValidator((String[]) null);
+            fail("Null Array - expected IllegalArgumentException");
+        } catch (IllegalArgumentException e) {
+            assertEquals("Null Array", "Regular expressions are missing", e.getMessage());
+        }
+
+        // Multiple Regular Expression - Zero Length array
+        try {
+            new RegexValidator(new String[0]);
+            fail("Zero Length Array - expected IllegalArgumentException");
+        } catch (IllegalArgumentException e) {
+            assertEquals("Zero Length Array", "Regular expressions are missing", e.getMessage());
+        }
+
+        // Multiple Regular Expression - Array has Null
+        String[] expressions = new String[] {"ABC", null};
+        try {
+            new RegexValidator(expressions);
+            fail("Array has Null - expected IllegalArgumentException");
+        } catch (IllegalArgumentException e) {
+            assertEquals("Array has Null", "Regular expression[1] is missing", e.getMessage());
+        }
+
+        // Multiple Regular Expression - Array has Zero Length
+        expressions = new String[] {"", "ABC"};
+        try {
+            new RegexValidator(expressions);
+            fail("Array has Zero Length - expected IllegalArgumentException");
+        } catch (IllegalArgumentException e) {
+            assertEquals("Array has Zero Length", "Regular expression[0] is missing", e.getMessage());
+        }
+    }
+
+    /**
+     * Test exceptions
+     */
+    @Test
+    public void testExceptions() {
+        String invalidRegex = "^([abCD12]*$";
+        try {
+            new RegexValidator(invalidRegex);
+        } catch (PatternSyntaxException e) {
+            // expected
+            Main.debug(e.getMessage());
+        }
+    }
+
+    /**
+     * Test toString() method
+     */
+    @Test
+    public void testToString() {
+        RegexValidator single = new RegexValidator(REGEX);
+        assertEquals("Single", "RegexValidator{" + REGEX + "}", single.toString());
+
+        RegexValidator multiple = new RegexValidator(new String[] {REGEX, REGEX});
+        assertEquals("Multiple", "RegexValidator{" + REGEX + "," + REGEX + "}", multiple.toString());
+    }
+
+    /**
+     * Compare two arrays
+     * @param label Label for the test
+     * @param expect Expected array
+     * @param result Actual array
+     */
+    private void checkArray(String label, String[] expect, String[] result) {
+
+        // Handle nulls
+        if (expect == null || result == null) {
+            if (expect == null && result == null) {
+                return; // valid, both null
+            } else {
+                fail(label + " Null expect=" + Arrays.toString(expect) + " result=" + Arrays.toString(result));
+            }
+            return; // not strictly necessary, but prevents possible NPE below
+        }
+
+        // Check Length
+        if (expect.length != result.length) {
+            fail(label + " Length expect=" + expect.length + " result=" + result.length);
+        }
+
+        // Check Values
+        for (int i = 0; i < expect.length; i++) {
+            assertEquals(label +" value[" + i + "]", expect[i], result[i]);
+        }
+    }
+}
Index: trunk/test/unit/org/openstreetmap/josm/data/validation/routines/ResultPair.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/data/validation/routines/ResultPair.java	(revision 9853)
+++ trunk/test/unit/org/openstreetmap/josm/data/validation/routines/ResultPair.java	(revision 9853)
@@ -0,0 +1,39 @@
+/*
+ * 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.openstreetmap.josm.data.validation.routines;
+
+/**
+ * Groups tests and expected results.
+ *
+ * @version $Revision: 1713567 $
+ */
+class ResultPair {
+    /** item */
+    public final String item;
+    /** valid */
+    public final boolean valid;
+
+    /**
+     * Constructs a new {@code ResultPair}.
+     * @param item item
+     * @param valid valid
+     */
+    ResultPair(String item, boolean valid) {
+        this.item = item;
+        this.valid = valid; //Whether the individual part of url is valid.
+    }
+}
Index: trunk/test/unit/org/openstreetmap/josm/data/validation/routines/UrlValidatorTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/data/validation/routines/UrlValidatorTest.java	(revision 9853)
+++ trunk/test/unit/org/openstreetmap/josm/data/validation/routines/UrlValidatorTest.java	(revision 9853)
@@ -0,0 +1,654 @@
+/*
+ * 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.openstreetmap.josm.data.validation.routines;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Performs Validation Test for url validations.
+ *
+ * @version $Revision: 1715076 $
+ */
+public class UrlValidatorTest {
+
+   private final boolean printStatus = false;
+   private final boolean printIndex = false; //print index that indicates current scheme,host,port,path, query test were using.
+
+   /**
+    * Setup
+    */
+   @Before
+   public void setUp() {
+      for (int index = 0; index < testPartsIndex.length - 1; index++) {
+         testPartsIndex[index] = 0;
+      }
+   }
+
+   /**
+    * Test is valid
+    */
+   @Test
+   public void testIsValid() {
+        testIsValid(testUrlParts, UrlValidator.ALLOW_ALL_SCHEMES);
+        setUp();
+        long options =
+            UrlValidator.ALLOW_2_SLASHES
+                + UrlValidator.ALLOW_ALL_SCHEMES
+                + UrlValidator.NO_FRAGMENTS;
+
+        testIsValid(testUrlPartsOptions, options);
+   }
+
+   /**
+    * Test is valid scheme
+    */
+   @Test
+   public void testIsValidScheme() {
+      if (printStatus) {
+         System.out.print("\n testIsValidScheme() ");
+      }
+      //UrlValidator urlVal = new UrlValidator(schemes,false,false,false);
+      UrlValidator urlVal = new UrlValidator(schemes, 0);
+      for (int sIndex = 0; sIndex < testScheme.length; sIndex++) {
+         ResultPair testPair = testScheme[sIndex];
+         boolean result = urlVal.isValidScheme(testPair.item);
+         assertEquals(testPair.item, testPair.valid, result);
+         if (printStatus) {
+            if (result == testPair.valid) {
+               System.out.print('.');
+            } else {
+               System.out.print('X');
+            }
+         }
+      }
+      if (printStatus) {
+         System.out.println();
+      }
+   }
+
+   /**
+    * Create set of tests by taking the testUrlXXX arrays and
+    * running through all possible permutations of their combinations.
+    *
+    * @param testObjects Used to create a url.
+    * @param options options
+    */
+   private void testIsValid(Object[] testObjects, long options) {
+      UrlValidator urlVal = new UrlValidator(null, null, options);
+      assertTrue(urlVal.isValid("http://www.google.com"));
+      assertTrue(urlVal.isValid("http://www.google.com/"));
+      int statusPerLine = 60;
+      int printed = 0;
+      if (printIndex)  {
+         statusPerLine = 6;
+      }
+      do {
+          StringBuilder testBuffer = new StringBuilder();
+         boolean expected = true;
+         for (int testPartsIndexIndex = 0; testPartsIndexIndex < testPartsIndex.length; ++testPartsIndexIndex) {
+            int index = testPartsIndex[testPartsIndexIndex];
+            ResultPair[] part = (ResultPair[]) testObjects[testPartsIndexIndex];
+            testBuffer.append(part[index].item);
+            expected &= part[index].valid;
+         }
+         String url = testBuffer.toString();
+         boolean result = urlVal.isValid(url);
+         assertEquals(url, expected, result);
+         if (printStatus) {
+            if (printIndex) {
+               System.out.print(testPartsIndextoString());
+            } else {
+               if (result == expected) {
+                  System.out.print('.');
+               } else {
+                  System.out.print('X');
+               }
+            }
+            printed++;
+            if (printed == statusPerLine) {
+               System.out.println();
+               printed = 0;
+            }
+         }
+      } while (incrementTestPartsIndex(testPartsIndex, testObjects));
+      if (printStatus) {
+         System.out.println();
+      }
+   }
+
+    /**
+     * Non-regression test for VALIDATOR-202
+     */
+    @Test
+    public void testValidator202() {
+        String[] schemes = {"http", "https"};
+        UrlValidator urlValidator = new UrlValidator(schemes, UrlValidator.NO_FRAGMENTS);
+        assertTrue(urlValidator.isValid(
+          "http://l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.org"));
+    }
+
+    /**
+     * Non-regression test for VALIDATOR-204
+     */
+    @Test
+    public void testValidator204() {
+        String[] schemes = {"http", "https"};
+        UrlValidator urlValidator = new UrlValidator(schemes);
+        assertTrue(urlValidator.isValid("http://tech.yahoo.com/rc/desktops/102;_ylt=Ao8yevQHlZ4On0O3ZJGXLEQFLZA5"));
+    }
+
+    /**
+     * Non-regression test for VALIDATOR-218
+     */
+    @Test
+    public void testValidator218() {
+        UrlValidator validator = new UrlValidator(UrlValidator.ALLOW_2_SLASHES);
+        assertTrue("parentheses should be valid in URLs",
+               validator.isValid("http://somewhere.com/pathxyz/file(1).html"));
+    }
+
+    /**
+     * Non-regression test for VALIDATOR-235
+     */
+    @Test
+    public void testValidator235() {
+        String version = System.getProperty("java.version");
+        if (version.compareTo("1.6") < 0) {
+            System.out.println("Cannot run Unicode IDN tests");
+            return; // Cannot run the test
+        }
+        UrlValidator validator = new UrlValidator();
+        assertTrue("xn--d1abbgf6aiiy.xn--p1ai should validate", validator.isValid("http://xn--d1abbgf6aiiy.xn--p1ai"));
+        assertTrue("президент.рф should validate", validator.isValid("http://президент.рф"));
+        assertTrue("www.b\u00fccher.ch should validate", validator.isValid("http://www.b\u00fccher.ch"));
+        assertFalse("www.\uFFFD.ch FFFD should fail", validator.isValid("http://www.\uFFFD.ch"));
+        assertTrue("www.b\u00fccher.ch should validate", validator.isValid("ftp://www.b\u00fccher.ch"));
+        assertFalse("www.\uFFFD.ch FFFD should fail", validator.isValid("ftp://www.\uFFFD.ch"));
+    }
+
+    /**
+     * Non-regression test for VALIDATOR-248
+     */
+    @Test
+    public void testValidator248() {
+        RegexValidator regex = new RegexValidator(new String[] {"localhost", ".*\\.my-testing"});
+        UrlValidator validator = new UrlValidator(regex, 0);
+
+        assertTrue("localhost URL should validate",
+                validator.isValid("http://localhost/test/index.html"));
+        assertTrue("first.my-testing should validate",
+                validator.isValid("http://first.my-testing/test/index.html"));
+        assertTrue("sup3r.my-testing should validate",
+                validator.isValid("http://sup3r.my-testing/test/index.html"));
+
+        assertFalse("broke.my-test should not validate",
+                validator.isValid("http://broke.my-test/test/index.html"));
+
+        assertTrue("www.apache.org should still validate",
+                validator.isValid("http://www.apache.org/test/index.html"));
+
+        // Now check using options
+        validator = new UrlValidator(UrlValidator.ALLOW_LOCAL_URLS);
+
+        assertTrue("localhost URL should validate",
+              validator.isValid("http://localhost/test/index.html"));
+
+        assertTrue("machinename URL should validate",
+              validator.isValid("http://machinename/test/index.html"));
+
+        assertTrue("www.apache.org should still validate",
+              validator.isValid("http://www.apache.org/test/index.html"));
+    }
+
+    /**
+     * Non-regression test for VALIDATOR-288
+     */
+    @Test
+    public void testValidator288() {
+        UrlValidator validator = new UrlValidator(UrlValidator.ALLOW_LOCAL_URLS);
+
+        assertTrue("hostname should validate",
+                validator.isValid("http://hostname"));
+
+        assertTrue("hostname with path should validate",
+                validator.isValid("http://hostname/test/index.html"));
+
+        assertTrue("localhost URL should validate",
+                validator.isValid("http://localhost/test/index.html"));
+
+        assertFalse("first.my-testing should not validate",
+                validator.isValid("http://first.my-testing/test/index.html"));
+
+        assertFalse("broke.hostname should not validate",
+                validator.isValid("http://broke.hostname/test/index.html"));
+
+        assertTrue("www.apache.org should still validate",
+                validator.isValid("http://www.apache.org/test/index.html"));
+
+        // Turn it off, and check
+        validator = new UrlValidator(0);
+
+        assertFalse("hostname should no longer validate",
+                validator.isValid("http://hostname"));
+
+        assertFalse("localhost URL should no longer validate",
+                validator.isValid("http://localhost/test/index.html"));
+
+        assertTrue("www.apache.org should still validate",
+                validator.isValid("http://www.apache.org/test/index.html"));
+    }
+
+    /**
+     * Non-regression test for VALIDATOR-276
+     */
+    @Test
+    public void testValidator276() {
+        // file:// isn't allowed by default
+        UrlValidator validator = new UrlValidator();
+
+        assertTrue("http://apache.org/ should be allowed by default",
+                 validator.isValid("http://www.apache.org/test/index.html"));
+
+        assertFalse("file:///c:/ shouldn't be allowed by default",
+                 validator.isValid("file:///C:/some.file"));
+
+        assertFalse("file:///c:\\ shouldn't be allowed by default",
+              validator.isValid("file:///C:\\some.file"));
+
+        assertFalse("file:///etc/ shouldn't be allowed by default",
+              validator.isValid("file:///etc/hosts"));
+
+        assertFalse("file://localhost/etc/ shouldn't be allowed by default",
+              validator.isValid("file://localhost/etc/hosts"));
+
+        assertFalse("file://localhost/c:/ shouldn't be allowed by default",
+              validator.isValid("file://localhost/c:/some.file"));
+
+        // Turn it on, and check
+        // Note - we need to enable local urls when working with file:
+        validator = new UrlValidator(new String[] {"http", "file"}, UrlValidator.ALLOW_LOCAL_URLS);
+
+        assertTrue("http://apache.org/ should be allowed by default",
+                 validator.isValid("http://www.apache.org/test/index.html"));
+
+        assertTrue("file:///c:/ should now be allowed",
+                 validator.isValid("file:///C:/some.file"));
+
+        // Currently, we don't support the c:\ form
+        assertFalse("file:///c:\\ shouldn't be allowed",
+              validator.isValid("file:///C:\\some.file"));
+
+        assertTrue("file:///etc/ should now be allowed",
+              validator.isValid("file:///etc/hosts"));
+
+        assertTrue("file://localhost/etc/ should now be allowed",
+              validator.isValid("file://localhost/etc/hosts"));
+
+        assertTrue("file://localhost/c:/ should now be allowed",
+              validator.isValid("file://localhost/c:/some.file"));
+
+        // These are never valid
+        assertFalse("file://c:/ shouldn't ever be allowed, needs file:///c:/",
+              validator.isValid("file://C:/some.file"));
+
+        assertFalse("file://c:\\ shouldn't ever be allowed, needs file:///c:/",
+              validator.isValid("file://C:\\some.file"));
+    }
+
+    /**
+     * Non-regression test for VALIDATOR-309
+     */
+    @Test
+    public void testValidator309() {
+        UrlValidator urlValidator = new UrlValidator();
+        assertTrue(urlValidator.isValid("http://sample.ondemand.com/"));
+        assertTrue(urlValidator.isValid("hTtP://sample.ondemand.CoM/"));
+        assertTrue(urlValidator.isValid("httpS://SAMPLE.ONEMAND.COM/"));
+        urlValidator = new UrlValidator(new String[] {"HTTP", "HTTPS"});
+        assertTrue(urlValidator.isValid("http://sample.ondemand.com/"));
+        assertTrue(urlValidator.isValid("hTtP://sample.ondemand.CoM/"));
+        assertTrue(urlValidator.isValid("httpS://SAMPLE.ONEMAND.COM/"));
+    }
+
+    /**
+     * Non-regression test for VALIDATOR-339
+     */
+    @Test
+    public void testValidator339() {
+        UrlValidator urlValidator = new UrlValidator();
+        assertTrue(urlValidator.isValid("http://www.cnn.com/WORLD/?hpt=sitenav")); // without
+        assertTrue(urlValidator.isValid("http://www.cnn.com./WORLD/?hpt=sitenav")); // with
+        assertFalse(urlValidator.isValid("http://www.cnn.com../")); // doubly dotty
+        assertFalse(urlValidator.isValid("http://www.cnn.invalid/"));
+        assertFalse(urlValidator.isValid("http://www.cnn.invalid./")); // check . does not affect invalid domains
+    }
+
+    /**
+     * Non-regression test for VALIDATOR-339 - IDN
+     */
+    @Test
+    public void testValidator339IDN() {
+        UrlValidator urlValidator = new UrlValidator();
+        assertTrue(urlValidator.isValid("http://президент.рф/WORLD/?hpt=sitenav")); // without
+        assertTrue(urlValidator.isValid("http://президент.рф./WORLD/?hpt=sitenav")); // with
+        assertFalse(urlValidator.isValid("http://президент.рф..../")); // very dotty
+        assertFalse(urlValidator.isValid("http://президент.рф.../")); // triply dotty
+        assertFalse(urlValidator.isValid("http://президент.рф../")); // doubly dotty
+    }
+
+    /**
+     * Non-regression test for VALIDATOR-342
+     */
+    @Test
+    public void testValidator342() {
+        UrlValidator urlValidator = new UrlValidator();
+        assertTrue(urlValidator.isValid("http://example.rocks/"));
+        assertTrue(urlValidator.isValid("http://example.rocks"));
+    }
+
+    static boolean incrementTestPartsIndex(int[] testPartsIndex, Object[] testParts) {
+      boolean carry = true;  //add 1 to lowest order part.
+      boolean maxIndex = true;
+      for (int testPartsIndexIndex = testPartsIndex.length - 1; testPartsIndexIndex >= 0; --testPartsIndexIndex) {
+         int index = testPartsIndex[testPartsIndexIndex];
+         ResultPair[] part = (ResultPair[]) testParts[testPartsIndexIndex];
+         if (carry) {
+            if (index < part.length - 1) {
+               index++;
+               testPartsIndex[testPartsIndexIndex] = index;
+               carry = false;
+            } else {
+               testPartsIndex[testPartsIndexIndex] = 0;
+               carry = true;
+            }
+         }
+         maxIndex &= (index == (part.length - 1));
+      }
+
+      return (!maxIndex);
+   }
+
+   private String testPartsIndextoString() {
+       StringBuilder carryMsg = new StringBuilder("{");
+       for (int testPartsIndexIndex = 0; testPartsIndexIndex < testPartsIndex.length; ++testPartsIndexIndex) {
+         carryMsg.append(testPartsIndex[testPartsIndexIndex]);
+         if (testPartsIndexIndex < testPartsIndex.length - 1) {
+            carryMsg.append(',');
+         } else {
+            carryMsg.append('}');
+         }
+       }
+       return carryMsg.toString();
+   }
+
+   /**
+    * Test validate URL
+    */
+   @Test
+   public void testValidateUrl() {
+      assertTrue(true);
+   }
+
+   /**
+    * Non-regression test for VALIDATOR-290
+    */
+   @Test
+   public void testValidator290() {
+        UrlValidator validator = new UrlValidator();
+        assertTrue(validator.isValid("http://xn--h1acbxfam.idn.icann.org/"));
+//        assertTrue(validator.isValid("http://xn--e1afmkfd.xn--80akhbyknj4f"));
+        // Internationalized country code top-level domains
+        assertTrue(validator.isValid("http://test.xn--lgbbat1ad8j")); //Algeria
+        assertTrue(validator.isValid("http://test.xn--fiqs8s")); // China
+        assertTrue(validator.isValid("http://test.xn--fiqz9s")); // China
+        assertTrue(validator.isValid("http://test.xn--wgbh1c")); // Egypt
+        assertTrue(validator.isValid("http://test.xn--j6w193g")); // Hong Kong
+        assertTrue(validator.isValid("http://test.xn--h2brj9c")); // India
+        assertTrue(validator.isValid("http://test.xn--mgbbh1a71e")); // India
+        assertTrue(validator.isValid("http://test.xn--fpcrj9c3d")); // India
+        assertTrue(validator.isValid("http://test.xn--gecrj9c")); // India
+        assertTrue(validator.isValid("http://test.xn--s9brj9c")); // India
+        assertTrue(validator.isValid("http://test.xn--xkc2dl3a5ee0h")); // India
+        assertTrue(validator.isValid("http://test.xn--45brj9c")); // India
+        assertTrue(validator.isValid("http://test.xn--mgba3a4f16a")); // Iran
+        assertTrue(validator.isValid("http://test.xn--mgbayh7gpa")); // Jordan
+        assertTrue(validator.isValid("http://test.xn--mgbc0a9azcg")); // Morocco
+        assertTrue(validator.isValid("http://test.xn--ygbi2ammx")); // Palestinian Territory
+        assertTrue(validator.isValid("http://test.xn--wgbl6a")); // Qatar
+        assertTrue(validator.isValid("http://test.xn--p1ai")); // Russia
+        assertTrue(validator.isValid("http://test.xn--mgberp4a5d4ar")); //  Saudi Arabia
+        assertTrue(validator.isValid("http://test.xn--90a3ac")); // Serbia
+        assertTrue(validator.isValid("http://test.xn--yfro4i67o")); // Singapore
+        assertTrue(validator.isValid("http://test.xn--clchc0ea0b2g2a9gcd")); // Singapore
+        assertTrue(validator.isValid("http://test.xn--3e0b707e")); // South Korea
+        assertTrue(validator.isValid("http://test.xn--fzc2c9e2c")); // Sri Lanka
+        assertTrue(validator.isValid("http://test.xn--xkc2al3hye2a")); // Sri Lanka
+        assertTrue(validator.isValid("http://test.xn--ogbpf8fl")); // Syria
+        assertTrue(validator.isValid("http://test.xn--kprw13d")); // Taiwan
+        assertTrue(validator.isValid("http://test.xn--kpry57d")); // Taiwan
+        assertTrue(validator.isValid("http://test.xn--o3cw4h")); // Thailand
+        assertTrue(validator.isValid("http://test.xn--pgbs0dh")); // Tunisia
+        assertTrue(validator.isValid("http://test.xn--mgbaam7a8h")); // United Arab Emirates
+        // Proposed internationalized ccTLDs
+//        assertTrue(validator.isValid("http://test.xn--54b7fta0cc")); // Bangladesh
+//        assertTrue(validator.isValid("http://test.xn--90ae")); // Bulgaria
+//        assertTrue(validator.isValid("http://test.xn--node")); // Georgia
+//        assertTrue(validator.isValid("http://test.xn--4dbrk0ce")); // Israel
+//        assertTrue(validator.isValid("http://test.xn--mgb9awbf")); // Oman
+//        assertTrue(validator.isValid("http://test.xn--j1amh")); // Ukraine
+//        assertTrue(validator.isValid("http://test.xn--mgb2ddes")); // Yemen
+        // Test TLDs
+//        assertTrue(validator.isValid("http://test.xn--kgbechtv")); // Arabic
+//        assertTrue(validator.isValid("http://test.xn--hgbk6aj7f53bba")); // Persian
+//        assertTrue(validator.isValid("http://test.xn--0zwm56d")); // Chinese
+//        assertTrue(validator.isValid("http://test.xn--g6w251d")); // Chinese
+//        assertTrue(validator.isValid("http://test.xn--80akhbyknj4f")); // Russian
+//        assertTrue(validator.isValid("http://test.xn--11b5bs3a9aj6g")); // Hindi
+//        assertTrue(validator.isValid("http://test.xn--jxalpdlp")); // Greek
+//        assertTrue(validator.isValid("http://test.xn--9t4b11yi5a")); // Korean
+//        assertTrue(validator.isValid("http://test.xn--deba0ad")); // Yiddish
+//        assertTrue(validator.isValid("http://test.xn--zckzah")); // Japanese
+//        assertTrue(validator.isValid("http://test.xn--hlcj6aya9esc7a")); // Tamil
+    }
+
+   /**
+    * Non-regression test for VALIDATOR-361
+    */
+   @Test
+   public void testValidator361() {
+       UrlValidator validator = new UrlValidator();
+       assertTrue(validator.isValid("http://hello.tokyo/"));
+    }
+
+   /**
+    * Non-regression test for VALIDATOR-363
+    */
+   @Test
+   public void testValidator363() {
+        UrlValidator urlValidator = new UrlValidator();
+        assertTrue(urlValidator.isValid("http://www.example.org/a/b/hello..world"));
+        assertTrue(urlValidator.isValid("http://www.example.org/a/hello..world"));
+        assertTrue(urlValidator.isValid("http://www.example.org/hello.world/"));
+        assertTrue(urlValidator.isValid("http://www.example.org/hello..world/"));
+        assertTrue(urlValidator.isValid("http://www.example.org/hello.world"));
+        assertTrue(urlValidator.isValid("http://www.example.org/hello..world"));
+        assertTrue(urlValidator.isValid("http://www.example.org/..world"));
+        assertTrue(urlValidator.isValid("http://www.example.org/.../world"));
+        assertFalse(urlValidator.isValid("http://www.example.org/../world"));
+        assertFalse(urlValidator.isValid("http://www.example.org/.."));
+        assertFalse(urlValidator.isValid("http://www.example.org/../"));
+        assertFalse(urlValidator.isValid("http://www.example.org/./.."));
+        assertFalse(urlValidator.isValid("http://www.example.org/././.."));
+        assertTrue(urlValidator.isValid("http://www.example.org/..."));
+        assertTrue(urlValidator.isValid("http://www.example.org/.../"));
+        assertTrue(urlValidator.isValid("http://www.example.org/.../.."));
+    }
+
+   /**
+    * Non-regression test for VALIDATOR-375
+    */
+   @Test
+   public void testValidator375() {
+       UrlValidator validator = new UrlValidator();
+       String url = "http://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:80/index.html";
+       assertTrue("IPv6 address URL should validate: " + url, validator.isValid(url));
+       url = "http://[::1]:80/index.html";
+       assertTrue("IPv6 address URL should validate: " + url, validator.isValid(url));
+       url = "http://FEDC:BA98:7654:3210:FEDC:BA98:7654:3210:80/index.html";
+       assertFalse("IPv6 address without [] should not validate: " + url, validator.isValid(url));
+    }
+
+   /**
+    * Non-regression test for VALIDATOR-353
+    */
+   @Test
+   public void testValidator353() { // userinfo
+       UrlValidator validator = new UrlValidator();
+       assertTrue(validator.isValid("http://www.apache.org:80/path"));
+       assertTrue(validator.isValid("http://user:pass@www.apache.org:80/path"));
+       assertTrue(validator.isValid("http://user:@www.apache.org:80/path"));
+       assertTrue(validator.isValid("http://us%00er:-._~!$&'()*+,;=@www.apache.org:80/path"));
+       assertFalse(validator.isValid("http://:pass@www.apache.org:80/path"));
+       assertFalse(validator.isValid("http://:@www.apache.org:80/path"));
+       assertFalse(validator.isValid("http://user:pa:ss@www.apache.org/path"));
+       assertFalse(validator.isValid("http://user:pa@ss@www.apache.org/path"));
+   }
+
+   /**
+    * Non-regression test for VALIDATOR-382
+    */
+   @Test
+   public void testValidator382() {
+       UrlValidator validator = new UrlValidator();
+       assertTrue(validator.isValid("ftp://username:password@example.com:8042/over/there/index.dtb?type=animal&name=narwhal#nose"));
+   }
+
+   /**
+    * Non-regression test for VALIDATOR-380
+    */
+   @Test
+   public void testValidator380() {
+       UrlValidator validator = new UrlValidator();
+       assertTrue(validator.isValid("http://www.apache.org:80/path"));
+       assertTrue(validator.isValid("http://www.apache.org:8/path"));
+       assertTrue(validator.isValid("http://www.apache.org:/path"));
+   }
+
+   //-------------------- Test data for creating a composite URL
+   /**
+    * The data given below approximates the 4 parts of a URL
+    * {@code <scheme>://<authority><path>?<query>} except that the port number
+    * is broken out of authority to increase the number of permutations.
+    * A complete URL is composed of a scheme+authority+port+path+query,
+    * all of which must be individually valid for the entire URL to be considered
+    * valid.
+    */
+   ResultPair[] testUrlScheme = {new ResultPair("http://", true),
+                               new ResultPair("ftp://", true),
+                               new ResultPair("h3t://", true),
+                               new ResultPair("3ht://", false),
+                               new ResultPair("http:/", false),
+                               new ResultPair("http:", false),
+                               new ResultPair("http/", false),
+                               new ResultPair("://", false),
+                               new ResultPair("", true)};
+
+   ResultPair[] testUrlAuthority = {new ResultPair("www.google.com", true),
+                                  new ResultPair("go.com", true),
+                                  new ResultPair("go.au", true),
+                                  new ResultPair("0.0.0.0", true),
+                                  new ResultPair("255.255.255.255", true),
+                                  new ResultPair("256.256.256.256", false),
+                                  new ResultPair("255.com", true),
+                                  new ResultPair("1.2.3.4.5", false),
+                                  new ResultPair("1.2.3.4.", false),
+                                  new ResultPair("1.2.3", false),
+                                  new ResultPair(".1.2.3.4", false),
+                                  new ResultPair("go.a", false),
+                                  new ResultPair("go.a1a", false),
+                                  new ResultPair("go.cc", true),
+                                  new ResultPair("go.1aa", false),
+                                  new ResultPair("aaa.", false),
+                                  new ResultPair(".aaa", false),
+                                  new ResultPair("aaa", false),
+                                  new ResultPair("", false)
+   };
+   ResultPair[] testUrlPort = {new ResultPair(":80", true),
+                             new ResultPair(":65535", true),
+                             new ResultPair(":0", true),
+                             new ResultPair("", true),
+                             new ResultPair(":-1", false),
+                             new ResultPair(":65636", true),
+                             new ResultPair(":65a", false)
+   };
+   ResultPair[] testPath = {new ResultPair("/test1", true),
+                          new ResultPair("/t123", true),
+                          new ResultPair("/$23", true),
+                          new ResultPair("/..", false),
+                          new ResultPair("/../", false),
+                          new ResultPair("/test1/", true),
+                          new ResultPair("", true),
+                          new ResultPair("/test1/file", true),
+                          new ResultPair("/..//file", false),
+                          new ResultPair("/test1//file", false)
+   };
+   //Test allow2slash, noFragment
+   ResultPair[] testUrlPathOptions = {new ResultPair("/test1", true),
+                                    new ResultPair("/t123", true),
+                                    new ResultPair("/$23", true),
+                                    new ResultPair("/..", false),
+                                    new ResultPair("/../", false),
+                                    new ResultPair("/test1/", true),
+                                    new ResultPair("/#", false),
+                                    new ResultPair("", true),
+                                    new ResultPair("/test1/file", true),
+                                    new ResultPair("/t123/file", true),
+                                    new ResultPair("/$23/file", true),
+                                    new ResultPair("/../file", false),
+                                    new ResultPair("/..//file", false),
+                                    new ResultPair("/test1//file", true),
+                                    new ResultPair("/#/file", false)
+   };
+
+   ResultPair[] testUrlQuery = {new ResultPair("?action=view", true),
+                              new ResultPair("?action=edit&mode=up", true),
+                              new ResultPair("", true)
+   };
+
+   Object[] testUrlParts = {testUrlScheme, testUrlAuthority, testUrlPort, testPath, testUrlQuery};
+   Object[] testUrlPartsOptions = {testUrlScheme, testUrlAuthority, testUrlPort, testUrlPathOptions, testUrlQuery};
+   int[] testPartsIndex = {0, 0, 0, 0, 0};
+
+   //---------------- Test data for individual url parts ----------------
+   private final String[] schemes = {"http", "gopher", "g0-To+.",
+                                      "not_valid" // TODO this will need to be dropped if the ctor validates schemes
+                                    };
+
+   ResultPair[] testScheme = {new ResultPair("http", true),
+                            new ResultPair("ftp", false),
+                            new ResultPair("httpd", false),
+                            new ResultPair("gopher", true),
+                            new ResultPair("g0-to+.", true),
+                            new ResultPair("not_valid", false), // underscore not allowed
+                            new ResultPair("HtTp", true),
+                            new ResultPair("telnet", false)};
+}
Index: trunk/test/unit/org/openstreetmap/josm/data/validation/tests/InternetTagsTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/data/validation/tests/InternetTagsTest.java	(revision 9849)
+++ trunk/test/unit/org/openstreetmap/josm/data/validation/tests/InternetTagsTest.java	(revision 9853)
@@ -31,10 +31,8 @@
 
     /**
-     * Test of "Internet Tags" validation test.
+     * Test of valid URLs.
      */
     @Test
-    public void test() {
-
-        // Valid URLs
+    public void testValidUrls() {
         testUrl("url", "www.domain.com", true);                                // No protocol
         testUrl("url", "http://josm.openstreetmap.de", true);                  // Simple HTTP
@@ -47,14 +45,30 @@
         testUrl("website", "http://золотаяцепь.рф", true);                     // see #10862: IDN URL in Unicode form
         testUrl("website", "http://золотаяцепь.рф/", true);                    // see #10862: IDN URL in Unicode form + slash
+        testUrl("website", "http://www.dasideenreich.online", true);           // see #12257: new TLD added August 19, 2015
+    }
 
-        // Invalid URLs
+    /**
+     * Test of invalid URLs.
+     */
+    @Test
+    public void testInvalidUrls() {
         testUrl("url", "something://www.domain.com", false);                   // invalid protocol
         testUrl("url", "http://www.domain.invalidtld", false);                 // invalid TLD
+    }
 
-        // Valid E-mails
+    /**
+     * Test of valid e-mails.
+     */
+    @Test
+    public void testValidEmails() {
         testEmail("email", "contact@www.domain.com", true);                    // Simple email
         testEmail("contact:email", "john.doe@other-domain.org", true);         // Key with : + dash in domain
+    }
 
-        // Invalid E-mails
+    /**
+     * Test of invalid e-mails.
+     */
+    @Test
+    public void testInvalidEmails() {
         testEmail("email", "contact at www.domain.com", false);                // No @
         testEmail("contact:email", "john.doe@other-domain.invalidtld", false); // invalid TLD
