This is an automated email from the ASF dual-hosted git repository. jochen pushed a commit to branch Features/Email198 in repository https://gitbox.apache.org/repos/asf/commons-email.git
commit 1992a9d751fabcb4cd566b6ca37d127fce13c620 Author: Jochen Wiedmann <jochen.wiedm...@gmail.com> AuthorDate: Thu Aug 12 12:24:17 2021 +0200 Initial work on EMAIL-198 --- .../apache/commons/mail/re/AbstractMatcher.java | 51 +++++++++++++ .../apache/commons/mail/re/AttributeSkipper.java | 62 ++++++++++++++++ .../commons/mail/re/AttributeValueMatcher.java | 45 ++++++++++++ .../org/apache/commons/mail/re/HtmlTagMatcher.java | 57 +++++++++++++++ .../java/org/apache/commons/mail/re/IMatcher.java | 69 ++++++++++++++++++ .../apache/commons/mail/re/ImageTagHandler.java | 84 ++++++++++++++++++++++ .../org/apache/commons/mail/re/package-info.java | 31 ++++++++ .../org/apache/commons/mail/AbstractEmailTest.java | 10 ++- .../apache/commons/mail/ImageHtmlEmailTest.java | 22 +++++- .../commons/mail/re/AttributeSkipperTest.java | 59 +++++++++++++++ .../apache/commons/mail/re/HtmlTagMatcherTest.java | 36 ++++++++++ .../org/apache/commons/mail/re/MatcherTests.java | 49 +++++++++++++ 12 files changed, 572 insertions(+), 3 deletions(-) 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*[^>]*?\\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><img</pre>, + * or <pre><src</pre>. Ignores attribute definitions, and the closing + * > 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> + * (<[Ii][Mm][Gg]\\s*[^>]*?\\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> + * (<[Ii][Mm][Gg]\\s*[^>]*?\\s+[Ss][Rr][Cc]\\s*=\\s*[\"'])([^\"']+?)([\"']) + * </pre> + * , and, + * <pre> + * (<[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> + * (<[Ii][Mm][Gg]\\s*[^>]*?\\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.BufferedOutputStream; 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 @@ public abstract class AbstractEmailTest 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 @@ public class ImageHtmlEmailTest extends HtmlEmailTest { email.getCcAddresses(), email.getBccAddresses(), true); } + @Test + public void testSlowRegularExpressions() throws Exception { + ImageHtmlEmail mail = new ImageHtmlEmail(); + mail.setHostName("example.com"); + mail.setFrom("f...@example.com"); + mail.addTo("t...@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 @@ public class ImageHtmlEmailTest extends HtmlEmailTest { } private static final class MockDataSourceClassPathResolver extends DataSourceClassPathResolver { - public MockDataSourceClassPathResolver(final String classPathBase, final boolean lenient) { super(classPathBase, lenient); } @@ -504,6 +523,5 @@ public class ImageHtmlEmailTest extends HtmlEmailTest { 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"); + }); + } + +}