<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://www.theeggeadventure.com/wikimedia/index.php?action=history&amp;feed=atom&amp;title=RegexFieldValidation</id>
	<title>RegexFieldValidation - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://www.theeggeadventure.com/wikimedia/index.php?action=history&amp;feed=atom&amp;title=RegexFieldValidation"/>
	<link rel="alternate" type="text/html" href="https://www.theeggeadventure.com/wikimedia/index.php?title=RegexFieldValidation&amp;action=history"/>
	<updated>2026-05-12T21:51:53Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.46.0-beta</generator>
	<entry>
		<id>https://www.theeggeadventure.com/wikimedia/index.php?title=RegexFieldValidation&amp;diff=1727&amp;oldid=prev</id>
		<title>Egge at 02:32, 1 October 2007</title>
		<link rel="alternate" type="text/html" href="https://www.theeggeadventure.com/wikimedia/index.php?title=RegexFieldValidation&amp;diff=1727&amp;oldid=prev"/>
		<updated>2007-10-01T02:32:30Z</updated>

		<summary type="html">&lt;p&gt;&lt;/p&gt;
&lt;table style=&quot;background-color: #fff; color: #202122;&quot; data-mw-interface=&quot;&quot;&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;en&quot;&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;← Older revision&lt;/td&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;Revision as of 02:32, 1 October 2007&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l106&quot;&gt;Line 106:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Line 106:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Including JavaDoc, the class is 57 LOC.  It was easy to get 100% code coverage, and it would be easy to add or remove rules.&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Including JavaDoc, the class is 57 LOC.  It was easy to get 100% code coverage, and it would be easy to add or remove rules.&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;[[Category:Java]]&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;

&lt;!-- diff cache key egge_wikimedia-mw_:diff:1.41:old-1622:rev-1727:php=table --&gt;
&lt;/table&gt;</summary>
		<author><name>Egge</name></author>
	</entry>
	<entry>
		<id>https://www.theeggeadventure.com/wikimedia/index.php?title=RegexFieldValidation&amp;diff=1622&amp;oldid=prev</id>
		<title>Brianegge at 23:56, 28 June 2007</title>
		<link rel="alternate" type="text/html" href="https://www.theeggeadventure.com/wikimedia/index.php?title=RegexFieldValidation&amp;diff=1622&amp;oldid=prev"/>
		<updated>2007-06-28T23:56:01Z</updated>

		<summary type="html">&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;Knowing how to use regular expressions will make you a better programmer.  Most every language has a good regex library, and you can use regex&amp;#039;s to do search/replaces in most IDEs.  Here, I present an example of translating a list of validation rules into regular expressions.  This IMHO keeps the code need and tidy.  Keeping things well laid out, can make it clear what each regular expression does.&lt;br /&gt;
&lt;br /&gt;
The business has a list of rules to validate a driver&amp;#039;s license number:&lt;br /&gt;
&lt;br /&gt;
* Maximum length of 9 characters.&lt;br /&gt;
* Alphanumeric characters only.&lt;br /&gt;
* Must have at least 4 numeric characters.&lt;br /&gt;
* Must have no more than 2 alphabetic characters.&lt;br /&gt;
* The third and fourth characters must be numeric.&lt;br /&gt;
&lt;br /&gt;
When I saw these rules, my first thought was can I take these definitions and write a grammar/interpreter for them.  I decided, although that would be fun, it probably wouldn&amp;#039;t be time effective in this case.  Next, I tried to see if each rule could be expressed as a regular expression, or if I was going to have to write Java code for each rule.  I decided to try the regex route, and if that fails, then I would code some rule logic.  &lt;br /&gt;
&lt;br /&gt;
To start my class, I used the TinyType abstract class.  You can read more about that in my [[Curiously recurring template pattern]] post.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public class DriversLicense extends TinyType&amp;lt;String&amp;gt; {&lt;br /&gt;
&lt;br /&gt;
    public DriversLicense(String s) {&lt;br /&gt;
        super(s.replaceAll(&amp;quot; &amp;quot;, &amp;quot;&amp;quot;));&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is all that&amp;#039;s needed for a TinyType.  Now, I want add the validation logic.  I could put this in the ctor, but I&amp;#039;d prefer to add it as a separate method.  This allows the class to hold dirty data, but let me later check if it&amp;#039;s valid.  For my purpose, this second method is going to be more flexable.  &lt;br /&gt;
&lt;br /&gt;
Next, I created some unit tests for the expected behavior:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    public void testMaxLength() {&lt;br /&gt;
        assertTrue(new DriversLicense(&amp;quot;012345678&amp;quot;).isValid());&lt;br /&gt;
        assertFalse(new DriversLicense(&amp;quot;0123456789&amp;quot;).isValid());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public void testAlphaNumeric() {&lt;br /&gt;
        assertTrue(new DriversLicense(&amp;quot;Az0123456&amp;quot;).isValid());&lt;br /&gt;
        assertFalse(new DriversLicense(&amp;quot;01234567_&amp;quot;).isValid());&lt;br /&gt;
        assertFalse(new DriversLicense(&amp;quot;01234567@&amp;quot;).isValid());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public void testFourOrMoreNumericCharacters() {&lt;br /&gt;
        assertTrue(new DriversLicense(&amp;quot;0123&amp;quot;).isValid());&lt;br /&gt;
        assertTrue(new DriversLicense(&amp;quot;0A12Z3&amp;quot;).isValid());&lt;br /&gt;
        assertFalse(new DriversLicense(&amp;quot;012&amp;quot;).isValid());&lt;br /&gt;
        assertFalse(new DriversLicense(&amp;quot;AU012&amp;quot;).isValid());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public void testTwoOrFewerAlphaCharacters() {&lt;br /&gt;
        assertTrue(new DriversLicense(&amp;quot;N012345Z&amp;quot;).isValid());&lt;br /&gt;
        assertTrue(new DriversLicense(&amp;quot;0A12Z3&amp;quot;).isValid());&lt;br /&gt;
        assertFalse(new DriversLicense(&amp;quot;A0B1C2345&amp;quot;).isValid());&lt;br /&gt;
        assertFalse(new DriversLicense(&amp;quot;012345ABC&amp;quot;).isValid());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public void testThirdAndFourthAreNumeric() {&lt;br /&gt;
        DriversLicense license = new DriversLicense(&amp;quot;N012345Z&amp;quot;);&lt;br /&gt;
        assertTrue(license.getReason(), license.isValid());&lt;br /&gt;
        DriversLicense license1 = new DriversLicense(&amp;quot;123456&amp;quot;);&lt;br /&gt;
        assertTrue(license1.getReason(), license1.isValid());&lt;br /&gt;
        assertFalse(new DriversLicense(&amp;quot;A0B1C2345&amp;quot;).isValid());&lt;br /&gt;
        assertFalse(new DriversLicense(&amp;quot;012A345&amp;quot;).isValid());&lt;br /&gt;
    }&lt;br /&gt;
    public void testGetReason() {&lt;br /&gt;
        assertEquals(&amp;quot;Maximum length of 9 characters.&amp;quot;, new DriversLicense(&amp;quot;0123456789&amp;quot;).getReason());&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
I decided that the class should have two methods.  One, &amp;#039;&amp;#039;&amp;#039;isValid&amp;#039;&amp;#039;&amp;#039; returns a boolean, the other, &amp;#039;&amp;#039;&amp;#039;getReason&amp;#039;&amp;#039;&amp;#039; returns a string of the failed rule.  &lt;br /&gt;
&lt;br /&gt;
With my failing unit test, I was ready to start writing the rules, and re-testing as I went.&lt;br /&gt;
&lt;br /&gt;
Unfortunetly, Java does not have a pair or tuple class, but I can get the same effect using a collection.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    private static final Map&amp;lt;Pattern, String&amp;gt; RULES = new LinkedHashMap&amp;lt;Pattern, String&amp;gt;();&lt;br /&gt;
&lt;br /&gt;
    static {&lt;br /&gt;
        RULES.put(Pattern.compile(&amp;quot;^.{0,9}$&amp;quot;), &amp;quot;Maximum length of 9 characters.&amp;quot;);&lt;br /&gt;
        RULES.put(Pattern.compile(&amp;quot;^[A-Za-z0-9]*$&amp;quot;), &amp;quot;Alphanumeric characters only.&amp;quot;);&lt;br /&gt;
        RULES.put(Pattern.compile(&amp;quot;^([A-Za-z]*[0-9][A-Za-z]*){4,}$&amp;quot;), &amp;quot;Must have at least 4 numeric characters.&amp;quot;);&lt;br /&gt;
        RULES.put(Pattern.compile(&amp;quot;^[0-9]*([0-9]*[A-Za-z][0-9]*){0,2}[0-9]*$&amp;quot;),&lt;br /&gt;
                &amp;quot;Must have no more than 2 alphabetic characters.&amp;quot;);&lt;br /&gt;
        RULES.put(Pattern.compile(&amp;quot;^.{2}[0-9]{2}.*$&amp;quot;), &amp;quot;The third and fourth characters must be numeric.&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Having the business rules as a string associated with each rule, makes the code self documenting, and allows me to write the getReason method.  I tried to have each rule match only the text on the right.  For example, for the max length, I match on any characters, so if they have illegal characters, but the length passes, this rule won&amp;#039;t fail.  Lastely, here&amp;#039;s the code which iterates over the rules.  If Java supported multiple return values, I&amp;#039;d probably combine these into a single function.  Alternatively, I could define a pair type and return that.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    boolean isValid() {&lt;br /&gt;
        for (Pattern rule : RULES.keySet()) {&lt;br /&gt;
            if (!rule.matcher(getValue()).find()) {&lt;br /&gt;
                return false;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    String getReason() {&lt;br /&gt;
        for (Map.Entry&amp;lt;Pattern, String&amp;gt; rule : RULES.entrySet()) {&lt;br /&gt;
            if (!rule.getKey().matcher(getValue()).find()) {&lt;br /&gt;
                return rule.getValue();&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return &amp;quot;valid&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Including JavaDoc, the class is 57 LOC.  It was easy to get 100% code coverage, and it would be easy to add or remove rules.&lt;/div&gt;</summary>
		<author><name>Brianegge</name></author>
	</entry>
</feed>