Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions src/main/java/org/apache/commons/lang3/RegExUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,13 @@
*/
package org.apache.commons.lang3;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

/**
* Helpers to process Strings using regular expressions.
Expand Down Expand Up @@ -751,4 +755,67 @@ private static String toStringOrNull(final CharSequence text) {
public RegExUtils() {
// empty
}

/**
* Finds all matches in the given text according to the specified pattern.
*
* @param text the text to search for matches
* @param pattern the pattern to search for
* @return an unmodifiable list of found matches; returns an empty List if text or pattern is null
*/
public List<String> findMatches(CharSequence text, Pattern pattern) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public List<String> findMatches(CharSequence text, Pattern pattern) {
public static List<String> findAll(CharSequence text, Pattern pattern) {

This is an utility class, so all the methods must be static.

Nitpick: Naming is certainly on of the hardest tasks. Since all the methods in this class deal with matching and have names like replaceAll, removeAll, I would call this method findAll.

if (text == null || pattern == null){
return Collections.emptyList();
}

List<String> matches = new ArrayList<>();
Matcher matcher = pattern.matcher(text);

while (matcher.find()) {
matches.add(matcher.group());
}

return Collections.unmodifiableList(matches);
}

/**
* Finds all matches in the given text according to the specified pattern
* and returns them as an array of strings.
*
* @param text the text to search for matches
* @param regex the regular expression pattern
* @return an array of found matches; returns an empty array if text or regex is null
*/
public String[] findMatchesAsArray(CharSequence text, String regex) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at the remaining methods of this class, each method with signature (CharSequence, Pattern) has a corresponding method with signature (String, String). Therefore I would define a method:

public static List<String> findAll(String text, String regex)

I don't think it is useful to have both a method returning List<String> and String[]. The return type should be coherent.

if (text == null ||regex == null){
return new String[0];
}
try {
Pattern pattern = Pattern.compile(regex);
List<String> matches = findMatches(text, pattern);
return matches.toArray(new String[0]);
}catch (PatternSyntaxException e){
return new String[0];
}
}

/**
* Finds all matches in the given text according to the specified pattern
* and returns them as a modifiable ArrayList.
*
* @param text the text to search for matches
* @param pattern the pattern to search for
* @return a modifiable ArrayList of found matches; returns an empty List if text or pattern is null
*/
public List<String> findMatchesModifiable(CharSequence text, Pattern pattern){
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-1

We should not start multiplying methods.

if (text == null || pattern == null){
return new ArrayList<>();
}
Matcher matcher = pattern.matcher(text);
List<String> matches = new ArrayList<>();
while (matcher.find()){
matches.add(matcher.group());
}
return matches;
}
}
157 changes: 152 additions & 5 deletions src/test/java/org/apache/commons/lang3/RegExUtilsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,18 @@
*/
package org.apache.commons.lang3;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.util.Arrays;
import java.util.List;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import javax.annotation.RegEx;

import static org.junit.jupiter.api.Assertions.*;

/**
* Tests {@link RegExUtils}.
*/
Expand Down Expand Up @@ -379,4 +381,149 @@ public void testReplacePatternDeprecated() {
assertEquals("Lorem_ipsum_dolor_sit", RegExUtils.replacePattern("Lorem ipsum dolor sit", "( +)([a-z]+)", "_$2"));
}

private RegExUtils utils;

@BeforeEach
void setUp() {
utils = new RegExUtils();
}
Comment on lines +386 to +389
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Utility classes are never instantiated.


@Test
public void testFindMultipleMatches() {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you use parameterized logging instead of multiple methods with the same logic?

Pattern pattern = Pattern.compile("\\d+");
List<String> result = utils.findMatches("asd 123 fgd 456 zxc", pattern);
assertEquals(Arrays.asList("123", "456"), result);
}

@Test
public void testFindNoMatches() {
Pattern pattern = Pattern.compile("\\d+");
List<String> result = utils.findMatches("asd zxc", pattern);
assertTrue(result.isEmpty());
}

@Test
void testFindMatchesWhenTextIsEmpty() {
String[] result = utils.findMatchesAsArray("", "\\w+");
assertArrayEquals(new String[0], result);
}
// @Test
// public void testShouldReturnArrayList() {
// Pattern pattern = Pattern.compile("\\w+");
// List<String> result = utils.findMatches("test", pattern);
// assertEquals(Arrays.asList("test"), result);
// assertTrue(result instanceof ArrayList);
// }

@Test
public void testFindMatchesWhenTextIsNull() {
Pattern pattern = Pattern.compile("test");
List<String> result = utils.findMatches(null, pattern);
assert (result).isEmpty();
}

@Test
public void testFindMatchesWhenPatternIsNull() {
List<String> result = utils.findMatches("test", null);
assertTrue(result.isEmpty());
}

@Test
public void testFindMatchesWithEmptyText() {
Pattern pattern = Pattern.compile("\\d+");
List<String> result = utils.findMatches("", pattern);
assertTrue(result.isEmpty());
}

@Test
public void testFindMultipleMatchesAsArray() {
String[] result = utils.findMatchesAsArray("test 123 test", "\\w+");
assertArrayEquals(new String[]{"test", "123", "test"}, result);
}

@Test
void testFindMatchesAsArrayWhenNoMatches() {
String[] result = utils.findMatchesAsArray("abc", "\\d");
assertArrayEquals(new String[0], result);
}

@Test
void testFindMatchesAsArrayWhenTextIsNull() {
String[] result = utils.findMatchesAsArray(null, "\\w+");
assertArrayEquals(new String[0], result);
}

@Test
void testFindMatchesAsArrayWhenTextIsEmpty() {
String[] result = utils.findMatchesAsArray("", "\\w+");
assertArrayEquals(new String[0], result);
}

@Test
void testFindMatchesAsArrayWithInvalidRegex() {
String[] result = utils.findMatchesAsArray("text", "[a-z");
assertArrayEquals(new String[0], result);
}

@Test
public void testReturnedListIsUnmodifiable() {
Pattern pattern = Pattern.compile("\\w+");
List<String> result = utils.findMatches("test", pattern);

try {
result.add("should fail");
fail("Expected UnsupportedOperationException");
} catch (UnsupportedOperationException e) {}
}

@Test
public void testFindMultipleMatchesAsArrayList(){
Pattern pattern = Pattern.compile("\\d+");
List<String> result = utils.findMatchesModifiable("asd 123 fgd 456 zxc", pattern);
assertEquals(Arrays.asList("123", "456"), result);
}

@Test
public void testShouldAddElementInFindMatchesModifiable(){
Pattern pattern = Pattern.compile("\\w+");
List<String> result = utils.findMatchesModifiable("asd test 123", pattern);
result.add("text");
assertEquals(Arrays.asList("asd", "test", "123", "text"), result);
}

@Test
public void testShouldRemoveElementInFindMatchesModifiable(){
Pattern pattern = Pattern.compile("\\w+");
List<String> result = utils.findMatchesModifiable("asd test 123", pattern);
result.remove("123");
assertEquals(Arrays.asList("asd","test"), result);
}

@Test
public void testFindNoMatchesAsArrayList() {
Pattern pattern = Pattern.compile("\\d+");
List<String> result = utils.findMatchesModifiable("asd zxc", pattern);
assertTrue(result.isEmpty());
}

@Test
void testFindMatchesAsArrayListWhenTextIsEmpty() {
Pattern pattern = Pattern.compile("\\d+");
List<String>result = utils.findMatchesModifiable("", pattern);
assertTrue(result.isEmpty());
}

@Test
void testFindMatchesAsArrayListWhenTextIsNull() {
Pattern pattern = Pattern.compile("\\d+");
List<String>result = utils.findMatchesModifiable(null, pattern);
assertTrue(result.isEmpty());
}

@Test
void testFindMatchesAsArrayListWhenPatternIsNull(){
List<String>result = utils.findMatchesModifiable("asd 123", null);
assertTrue(result.isEmpty());
}

}
Loading