Initial work on EMAIL-198
diff --git a/src/main/java/org/apache/commons/mail/re/AbstractMatcher.java b/src/main/java/org/apache/commons/mail/re/AbstractMatcher.java
new file mode 100644
index 0000000..985623e
--- /dev/null
+++ b/src/main/java/org/apache/commons/mail/re/AbstractMatcher.java
@@ -0,0 +1,51 @@
+/*

+ * 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.apache.commons.mail.re;

+

+

+/** Abstract base class for deriving implementations of {@link IMatcher}.

+ */

+public abstract class AbstractMatcher implements IMatcher {

+

+	/** Checks, whether a given word is in the given text at the given offset.

+	 * @param text The text, in which the word should be present.

+	 * @param offset The offset, at which to look for the word.

+	 * @param word The word to look for.

+	 * @param caseInsensitive Whether the match might be case insensitive.

+	 * @return True, if the word is founf. Otherwise false.

+	 */

+	protected boolean isWordAt(String text, int offset, String word, boolean caseInsensitive) {

+		if (offset+word.length() <= text.length()) {

+			for (int i = 0;  i < word.length();  i++) {

+				final char c1 = text.charAt(offset+i);

+				final char c2 = word.charAt(i);

+				if (caseInsensitive) {

+					if (Character.toLowerCase(c1) != Character.toLowerCase(c2)) {

+						return false;

+					}

+				} else {

+					if (c1 != c2) {

+						return false;

+					}

+				}

+			}

+			return true;

+		}

+		return false;

+	}

+

+}

diff --git a/src/main/java/org/apache/commons/mail/re/AttributeSkipper.java b/src/main/java/org/apache/commons/mail/re/AttributeSkipper.java
new file mode 100644
index 0000000..34895f5
--- /dev/null
+++ b/src/main/java/org/apache/commons/mail/re/AttributeSkipper.java
@@ -0,0 +1,62 @@
+/*

+ * 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.apache.commons.mail.re;

+

+/** An implementation of {@link IMatcher}, that implements the regular expression

+ * <pre>

+ *   \\s*[^&gt;]*?\\s+

+ * </pre>

+ */

+public class AttributeSkipper extends AbstractMatcher {

+	@Override

+	public void find(String text, int startOffset, int endOffset, IListener listener) throws TerminationRequest {

+		// Implement the initial \s*

+		int offset2 = startOffset;

+		for (int i = startOffset;  i < endOffset;  i++) {

+			final char c = text.charAt(i);

+			if (!Character.isWhitespace(c)) {

+				break;

+			} else {

+				++offset2;

+			}

+		}

+		if (offset2 > startOffset) {

+			listener.match(this, text, startOffset, offset2);

+		}

+		// Implement the [^>]*

+		int offset3 = offset2;

+		for (int i = startOffset;  i < endOffset;  i++) {

+			final char c = text.charAt(i);

+			if (Character.isWhitespace(c)) {

+				for (int j = offset3;  j < endOffset;  j++) {

+					if (Character.isWhitespace(text.charAt(j))) {

+						if (j+1 > offset2) {

+							listener.match(this, text, startOffset, j+1);

+						} else {

+							// Already notified, do nothing.

+						}

+					}

+				}

+			} else if (c == '>') {

+				return;

+			} else {

+				++offset3;

+			}

+		}

+	}

+

+}

diff --git a/src/main/java/org/apache/commons/mail/re/AttributeValueMatcher.java b/src/main/java/org/apache/commons/mail/re/AttributeValueMatcher.java
new file mode 100644
index 0000000..6777938
--- /dev/null
+++ b/src/main/java/org/apache/commons/mail/re/AttributeValueMatcher.java
@@ -0,0 +1,45 @@
+/*

+ * 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.apache.commons.mail.re;

+

+public class AttributeValueMatcher extends AbstractMatcher {

+	private final String expectedAttributeName;

+	private int attributeValueStart = -1;

+	private int attributeValueEnd = -1;

+

+	public AttributeValueMatcher(String expectedAttributeName) {

+		this.expectedAttributeName = expectedAttributeName;

+	}

+

+	@Override

+	public void find(String text, int startOffset, int endOffset, IListener listener) throws TerminationRequest {

+	}

+

+	public int getAttributeValueStart() {

+		if (attributeValueStart == -1) {

+    		throw new IllegalStateException("The attribute value start is only available inside IListener.match()");

+		}

+		return attributeValueStart;

+	}

+

+	public int getAttributeValueEnd() {

+		if (attributeValueEnd == -1) {

+    		throw new IllegalStateException("The attribute value end is only available inside IListener.match()");

+		}

+		return attributeValueEnd;

+	}

+}

diff --git a/src/main/java/org/apache/commons/mail/re/HtmlTagMatcher.java b/src/main/java/org/apache/commons/mail/re/HtmlTagMatcher.java
new file mode 100644
index 0000000..251496b
--- /dev/null
+++ b/src/main/java/org/apache/commons/mail/re/HtmlTagMatcher.java
@@ -0,0 +1,57 @@
+/*

+ * 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.apache.commons.mail.re;

+

+import java.util.Arrays;

+import java.util.List;

+

+/** A {@link IMatcher} for opening HTML tags, like <pre>&lt;img</pre>,

+ * or <pre>&lt;src</pre>. Ignores attribute definitions, and the closing

+ * &gt; character.

+ */

+public class HtmlTagMatcher extends AbstractMatcher {

+    private final List<String> elementNames;

+    private String currentElementName;

+

+    public HtmlTagMatcher(String[] elementNames) {

+        this.elementNames = Arrays.asList(elementNames);

+    }

+

+    @Override

+    public void find(String text, int startOffset, int endOffset, IMatcher.IListener listener) {

+        for (int i = startOffset;   i < endOffset;  i++) {

+            final char c = text.charAt(i);

+            if (c == '<') {

+                for (int j = 0;  j < elementNames.size();  j++) {

+					final String elementName = elementNames.get(j);

+					if (isWordAt(text, i+1, elementName, true)) {

+						currentElementName = elementName;

+						listener.match(this, text, i, i+1+elementName.length());

+						currentElementName = null;

+					}

+                }

+            }

+        }

+    }

+

+    public String getCurrentElementName() {

+    	if (currentElementName == null) {

+    		throw new IllegalStateException("The current element name is only available inside IListener.match()");

+    	}

+    	return currentElementName;

+    }

+}

diff --git a/src/main/java/org/apache/commons/mail/re/IMatcher.java b/src/main/java/org/apache/commons/mail/re/IMatcher.java
new file mode 100644
index 0000000..709371b
--- /dev/null
+++ b/src/main/java/org/apache/commons/mail/re/IMatcher.java
@@ -0,0 +1,69 @@
+/*

+ * 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.apache.commons.mail.re;

+

+

+/** An {@link IMatcher} is a helper classes, that is designed to replace

+ * regular expressions, like

+ * <pre>

+ *   (&lt;[Ii][Mm][Gg]\\s*[^&gt;]*?\\s+[Ss][Rr][Cc]\\s*=\\s*[\"'])([^\"']+?)([\"'])

+ * </pre>

+ * The reason for using the helper classes, and not a simple regular

+ * expression is a performance problem of the latter, that we wish to overcome.

+ * The main idea of the {@link IMatcher} is to divide the regular expression into a

+ * cascade of so-called matchers. A matcher replaces a comparatively simple

+ * subexpression, like <pre>\\s*</pre>. The simplicity of the replaced expression

+ * allows a manual, performace optimized implementation of the corresponding

+ * matcher.

+ */

+public interface IMatcher {

+    /** This exception is being thrown, if the search for matches should

+     * be terminated.

+     */

+    public static class TerminationRequest extends RuntimeException {

+        private static final long serialVersionUID = 5706488409254083692L;

+

+        public TerminationRequest() {

+        }

+    }

+	/** Interface of a match listener, which is being invoked, if a match

+	 * has been found.

+	 */

+	public interface IListener {

+        /** Called, if a match has been found.

+         * @param matcher The matcher, which is sending the notification.

+         * @param text The text, in which a match has been found.

+         * @param startOffset Offset of the matches first character in the text.

+         * @param endOffset Offset of the first character in the text, that

+         * follows after the match.

+         * @throws TerminationRequest The caller is supposed to stop

+         * searching for further matches.

+         */

+        void match(IMatcher matcher, String text, int startOffset, int endOffset) throws TerminationRequest;

+    }

+

+    /** Called to find matches in the given text.

+     * @param text The text, in which a match has been found.

+     * @param startOffset Offset of the matches first character in the text.

+     * @param endOffset Offset of the first character in the text, that

+     * follows after the match.

+     * @param listener The listener, which is being notified in case of matches.

+     * @throws TerminationRequest The caller is supposed to stop

+     * searching for further matches.

+     */

+    void find(String text, int startOffset, int endOffset, IListener listener) throws TerminationRequest;

+}

diff --git a/src/main/java/org/apache/commons/mail/re/ImageTagHandler.java b/src/main/java/org/apache/commons/mail/re/ImageTagHandler.java
new file mode 100644
index 0000000..d1ce128
--- /dev/null
+++ b/src/main/java/org/apache/commons/mail/re/ImageTagHandler.java
@@ -0,0 +1,84 @@
+/*

+ * 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.apache.commons.mail.re;

+

+import java.util.function.Function;

+

+import org.apache.commons.mail.ImageHtmlEmail;

+import org.apache.commons.mail.re.IMatcher.TerminationRequest;

+

+/** The {@link ImageTagHandler} implements the regular expressions

+ * <pre>

+ *   (&lt;[Ii][Mm][Gg]\\s*[^&gt;]*?\\s+[Ss][Rr][Cc]\\s*=\\s*[\"'])([^\"']+?)([\"'])

+ * </pre>

+ * , and,

+ * <pre>

+ *   (&lt;[Ss][Cc][Rr][Ii][Pp][Tt]\\s*.*?\\s+[Ss][Rr][Cc]\\s*=\\s*[\"'])([^\"']+?)([\"'])

+ * </pre>

+ * ({@link ImageHtmlEmail#REGEX_IMG_SRC}, and {@link ImageHtmlEmail#REGEX_SCRIPT_SRC})

+ * with instances of {@link IMatcher}.

+ */

+public class ImageTagHandler {

+    private static class MatchFoundException extends TerminationRequest {

+        private static final long serialVersionUID = 1206347581416053598L;

+        private final int matchStartOffset, matchEndOffset;

+

+        public MatchFoundException(int matchStartOffset, int matchEndOffset) {

+            this.matchStartOffset = matchStartOffset;

+            this.matchEndOffset = matchEndOffset;

+        }

+

+        public int getMatchStartOffset() {

+            return matchStartOffset;

+        }

+

+        public int getMatchEndOffset() {

+            return matchEndOffset;

+        }

+    }

+

+	public String findReferences(String text, Function<String,String> editor) {

+		final HtmlTagMatcher htm = new HtmlTagMatcher(new String[] {"img", "script"});

+		final AttributeSkipper as = new AttributeSkipper();

+		final AttributeValueMatcher avm = new AttributeValueMatcher("src");

+

+		String txt = text;

+		for (;;) {

+			MatchFoundException mfe;

+			try {

+				htm.find(text, 0, text.length(), (m,t,s,e) -> {

+					as.find(t, e, text.length(), (m2,t2,s2,e2) -> {

+						avm.find(t2, e2, t2.length(), (m3,t3,s3,e3) -> {

+							throw new MatchFoundException(avm.getAttributeValueStart(), avm.getAttributeValueEnd());

+						});

+					});

+				});

+				mfe = null;

+			} catch (MatchFoundException e) {

+				mfe = e;

+			}

+			if (mfe != null) {

+				txt = txt.substring(0, mfe.matchStartOffset)

+						+ editor.apply(txt.substring(mfe.matchStartOffset+1, mfe.matchEndOffset))

+						+ txt.substring(mfe.matchEndOffset);

+			} else {

+				break;

+			}

+		}

+		return txt;

+	}

+}

diff --git a/src/main/java/org/apache/commons/mail/re/package-info.java b/src/main/java/org/apache/commons/mail/re/package-info.java
new file mode 100644
index 0000000..32fe6b8
--- /dev/null
+++ b/src/main/java/org/apache/commons/mail/re/package-info.java
@@ -0,0 +1,31 @@
+/*

+ * 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.apache.commons.mail.re;

+

+/** This package provides helper classes, that are designed to replace

+ * regular expressions, like

+ * <pre>

+ *   (&lt;[Ii][Mm][Gg]\\s*[^&gt;]*?\\s+[Ss][Rr][Cc]\\s*=\\s*[\"'])([^\"']+?)([\"'])

+ * </pre>

+ * The reason for using the helper classes, and not a simple regular

+ * expression is a performance problem of the latter, that we wish to overcome.

+ * The main idea of the helper class is to divide the regular expression into a

+ * cascade of so-called matchers. A matcher replaces a comparatively simple

+ * subexpression, like <pre>\\s*</pre>. The simplicity of the replaced expression

+ * allows a manual, performace optimized implementation of the corresponding

+ * matcher.

+ */

diff --git a/src/test/java/org/apache/commons/mail/AbstractEmailTest.java b/src/test/java/org/apache/commons/mail/AbstractEmailTest.java
index 8c1324f..a9dfb06 100644
--- a/src/test/java/org/apache/commons/mail/AbstractEmailTest.java
+++ b/src/test/java/org/apache/commons/mail/AbstractEmailTest.java
@@ -26,6 +26,7 @@
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.IOException;
+import java.net.BindException;
 import java.net.URL;
 import java.util.Date;
 import java.util.Enumeration;
@@ -199,7 +200,14 @@
 
             this.fakeMailServer = new Wiser();
             this.fakeMailServer.setPort(getMailServerPort());
-            this.fakeMailServer.start();
+            try {
+            	this.fakeMailServer.start();
+            } catch (RuntimeException e) {
+            	if (e.getCause() != null  &&  e.getCause() instanceof BindException) {
+            		throw new IllegalStateException("Port " + getMailServerPort()
+            		                                + " is already in use.");
+            	}
+            }
 
             assertFalse("fake mail server didn't start", isMailServerStopped(fakeMailServer));
 
diff --git a/src/test/java/org/apache/commons/mail/ImageHtmlEmailTest.java b/src/test/java/org/apache/commons/mail/ImageHtmlEmailTest.java
index 5f52098..026264c 100644
--- a/src/test/java/org/apache/commons/mail/ImageHtmlEmailTest.java
+++ b/src/test/java/org/apache/commons/mail/ImageHtmlEmailTest.java
@@ -477,6 +477,26 @@
                      email.getCcAddresses(), email.getBccAddresses(), true);
     }
 
+    @Test
+    public void testSlowRegularExpressions() throws Exception {
+        ImageHtmlEmail mail = new ImageHtmlEmail();
+        mail.setHostName("example.com");
+        mail.setFrom("from@example.com");
+        mail.addTo("to@example.com");
+        StringBuilder text = new StringBuilder("<img");
+        for (int i = 0; i < 3000; i++) {
+            text.append(" ");
+        }
+        mail.setMsg("<html><body><pre>" + text + "</pre></body></html>");
+
+        long startTime = System.currentTimeMillis();
+        mail.buildMimeMessage();
+        long duration = System.currentTimeMillis() - startTime;
+        final int permittedDurationInSeconds = 200;
+		assertTrue("Run time exceeds permitted duration of " + permittedDurationInSeconds
+				   + " seconds: " + duration, duration < permittedDurationInSeconds*1000);
+    }
+
     private String loadUrlContent(final URL url) throws IOException {
         final InputStream stream = url.openStream();
         final StringBuilder html = new StringBuilder();
@@ -492,7 +512,6 @@
     }
 
     private static final class MockDataSourceClassPathResolver extends DataSourceClassPathResolver {
-
         public MockDataSourceClassPathResolver(final String classPathBase, final boolean lenient) {
             super(classPathBase, lenient);
         }
@@ -504,6 +523,5 @@
             ds.setName(null);
             return ds;
         }
-
     }
 }
diff --git a/src/test/java/org/apache/commons/mail/re/AttributeSkipperTest.java b/src/test/java/org/apache/commons/mail/re/AttributeSkipperTest.java
new file mode 100644
index 0000000..badfe43
--- /dev/null
+++ b/src/test/java/org/apache/commons/mail/re/AttributeSkipperTest.java
@@ -0,0 +1,59 @@
+/*

+ * 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.apache.commons.mail.re;

+

+import static org.junit.Assert.assertSame;

+

+import java.util.ArrayList;

+import java.util.List;

+

+import org.junit.Test;

+

+public class AttributeSkipperTest {

+	/** Test the {@link AttributeSkipper} alone.

+	 */

+	@Test

+	public void testStandalone() {

+		final AttributeSkipper as = new AttributeSkipper();

+		MatcherTests.assertMatch(as, "<img src='foo'>", 0, 0, 5);

+		MatcherTests.assertMatch(as, "<img src='foo'>", 4, 4, 5);

+		MatcherTests.assertNoMatch(as, "", 0);

+	}

+

+	/** Test the {@link AttributeSkipper} in combination with the {@link HtmlTagMatcher}.

+	 */

+	@Test

+	public void testChain() {

+		assertChainMatch("<img src='foo'>", 0, 4, 5);

+	}

+

+	private void assertChainMatch(String text, int startOffset, int... expectedOffsets) {

+		final HtmlTagMatcher htm = new HtmlTagMatcher(new String[] {"script", "img"});

+		final AttributeSkipper as = new AttributeSkipper();

+		final List<Integer> actualOffsets = new ArrayList<>();

+		htm.find(text, startOffset, text.length(), (m,t,s,e) -> {

+			assertSame(htm,m);

+			assertSame(text,t);

+			as.find(text, e, text.length(), (m2,t2,s2,e2) -> {

+				assertSame(as,m2);

+				assertSame(text,t2);

+				actualOffsets.add(Integer.valueOf(s));

+				actualOffsets.add(Integer.valueOf(e));

+			});

+		});

+	}

+}

diff --git a/src/test/java/org/apache/commons/mail/re/HtmlTagMatcherTest.java b/src/test/java/org/apache/commons/mail/re/HtmlTagMatcherTest.java
new file mode 100644
index 0000000..fe36e6d
--- /dev/null
+++ b/src/test/java/org/apache/commons/mail/re/HtmlTagMatcherTest.java
@@ -0,0 +1,36 @@
+/*

+ * 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.apache.commons.mail.re;

+

+import org.junit.Test;

+

+public class HtmlTagMatcherTest {

+	@Test

+	public void testMatch() {

+		final HtmlTagMatcher htm = new HtmlTagMatcher(new String[] {"img", "src"});

+		MatcherTests.assertMatch(htm, "<img", 0, 0, 4);

+		MatcherTests.assertMatch(htm, "<img>", 0, 0, 4);

+		MatcherTests.assertMatch(htm, "<iMg", 0, 0, 4);

+		MatcherTests.assertMatch(htm, "<imG>", 0, 0, 4);

+		MatcherTests.assertMatch(htm, " <img", 0, 1, 5);

+		MatcherTests.assertMatch(htm, " <img>", 0, 1, 5);

+		MatcherTests.assertMatch(htm, " <img", 1, 1, 5);

+		MatcherTests.assertMatch(htm, " <img>", 1, 1, 5);

+		MatcherTests.assertMatch(htm, " <img>", 0, 1, 5);

+		MatcherTests.assertMatch(htm, " <img> <src", 0, 1, 5, 7, 11);

+	}

+}

diff --git a/src/test/java/org/apache/commons/mail/re/MatcherTests.java b/src/test/java/org/apache/commons/mail/re/MatcherTests.java
new file mode 100644
index 0000000..ead2693
--- /dev/null
+++ b/src/test/java/org/apache/commons/mail/re/MatcherTests.java
@@ -0,0 +1,49 @@
+/*

+ * 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.apache.commons.mail.re;

+

+import static org.junit.Assert.assertEquals;

+import static org.junit.Assert.assertSame;

+

+import java.util.ArrayList;

+import java.util.List;

+

+/** Helper class for matcher tests.

+ */

+public class MatcherTests {

+

+	public static void assertMatch(IMatcher matcher, String text, int startOffset, int... expectedOffsets) {

+		final List<Integer> list = new ArrayList<>();

+		matcher.find(text, startOffset, text.length(), (m,t,s,e) -> {

+			assertSame(matcher,m);

+			assertSame(text,t);

+			list.add(Integer.valueOf(s));

+			list.add(Integer.valueOf(e));

+		});

+		assertEquals(expectedOffsets.length, list.size());

+		for (int i = 0;  i < expectedOffsets.length;  i++) {

+			assertEquals(String.valueOf(i), expectedOffsets[i], list.get(i).intValue());

+		}

+	}

+

+	public static void assertNoMatch(IMatcher matcher, String text, int startOffset) {

+		matcher.find(text, startOffset, text.length(), (m,t,s,e) -> {

+			throw new IllegalStateException("Unexpected match");

+		});

+	}

+

+}