diff --git a/src/main/java/com/github/sgreben/regex_builder/CaptureGroup.java b/src/main/java/com/github/sgreben/regex_builder/CaptureGroup.java index ec89bd8..a39a10a 100644 --- a/src/main/java/com/github/sgreben/regex_builder/CaptureGroup.java +++ b/src/main/java/com/github/sgreben/regex_builder/CaptureGroup.java @@ -3,20 +3,32 @@ import com.github.sgreben.regex_builder.expression.Unary; import com.github.sgreben.regex_builder.tokens.END_GROUP; import com.github.sgreben.regex_builder.tokens.START_GROUP; +import com.github.sgreben.regex_builder.tokens.START_GROUP_NAMED; import com.github.sgreben.regex_builder.tokens.TOKEN; /** * A regex capture group "(...)" */ public class CaptureGroup extends Unary { + private final String name; public CaptureGroup(Expression expression) { super(expression); + name = null; + } + + public CaptureGroup(Expression expression, String name) { + super(expression); + this.name = name; } @Override public void compile(CaptureGroupIndex index, java.util.List output) { - output.add(new START_GROUP()); + if (name != null) { + output.add(new START_GROUP_NAMED(name)); + } else { + output.add(new START_GROUP()); + } for (Expression child : children()) { child.compile(index, output); } diff --git a/src/main/java/com/github/sgreben/regex_builder/FluentRe.java b/src/main/java/com/github/sgreben/regex_builder/FluentRe.java index 4c037e9..ffa6b4c 100644 --- a/src/main/java/com/github/sgreben/regex_builder/FluentRe.java +++ b/src/main/java/com/github/sgreben/regex_builder/FluentRe.java @@ -86,6 +86,10 @@ public CaptureGroup capture() { return Re.capture(expression); } + public CaptureGroup captureNamed(String name) { + return Re.captureNamed(name, expression); + } + public FluentRe separatedBy1(FluentRe e) { return new FluentRe(Re.separatedBy1(e.expression, expression)); } @@ -193,4 +197,8 @@ public FluentRe optional() { public Pattern compile() { return Pattern.compile(expression); } -} \ No newline at end of file + + public Pattern compile(int flags) { + return Pattern.compile(expression, flags); + } +} diff --git a/src/main/java/com/github/sgreben/regex_builder/Re.java b/src/main/java/com/github/sgreben/regex_builder/Re.java index 3c6ab1e..bab4725 100644 --- a/src/main/java/com/github/sgreben/regex_builder/Re.java +++ b/src/main/java/com/github/sgreben/regex_builder/Re.java @@ -1,12 +1,37 @@ package com.github.sgreben.regex_builder; -import com.github.sgreben.regex_builder.expression.*; +import com.github.sgreben.regex_builder.expression.Atomic; +import com.github.sgreben.regex_builder.expression.BackReference; +import com.github.sgreben.regex_builder.expression.BeginLine; +import com.github.sgreben.regex_builder.expression.CharClassExpression; +import com.github.sgreben.regex_builder.expression.Choice; +import com.github.sgreben.regex_builder.expression.EndLine; +import com.github.sgreben.regex_builder.expression.Literal; +import com.github.sgreben.regex_builder.expression.NegativeLookahead; +import com.github.sgreben.regex_builder.expression.NegativeLookbehind; +import com.github.sgreben.regex_builder.expression.Optional; +import com.github.sgreben.regex_builder.expression.OptionalPossessive; +import com.github.sgreben.regex_builder.expression.OptionalReluctant; +import com.github.sgreben.regex_builder.expression.PositiveLookahead; +import com.github.sgreben.regex_builder.expression.PositiveLookbehind; +import com.github.sgreben.regex_builder.expression.Repeat; +import com.github.sgreben.regex_builder.expression.Repeat1; +import com.github.sgreben.regex_builder.expression.Repeat1Possessive; +import com.github.sgreben.regex_builder.expression.Repeat1Reluctant; +import com.github.sgreben.regex_builder.expression.RepeatAtLeast; +import com.github.sgreben.regex_builder.expression.RepeatAtLeastPossessive; +import com.github.sgreben.regex_builder.expression.RepeatAtLeastReluctant; +import com.github.sgreben.regex_builder.expression.RepeatPossessive; +import com.github.sgreben.regex_builder.expression.RepeatReluctant; +import com.github.sgreben.regex_builder.expression.Sequence; /** * Regular expression builder */ public class Re { - private Re() {} + private Re() { + } + /** * Match a string literal. */ @@ -388,62 +413,86 @@ public static Expression repeat1Reluctant(char e) { /** * Repeat the expression at least the given number of times. */ - public static Expression repeatAtLeast(Expression expression, int n) { return new RepeatAtLeast(expression, n); } + public static Expression repeatAtLeast(Expression expression, int n) { + return new RepeatAtLeast(expression, n); + } /** * Repeat the expression at least the given number of times. */ - public static Expression repeatAtLeast(String s, int n) { return repeatAtLeast(string(s), n); } + public static Expression repeatAtLeast(String s, int n) { + return repeatAtLeast(string(s), n); + } /** * Repeat the expression at least the given number of times. */ - public static Expression repeatAtLeast(char c, int n) { return repeatAtLeast(character(c), n); } + public static Expression repeatAtLeast(char c, int n) { + return repeatAtLeast(character(c), n); + } /** * Repeat the expression at least the given number of times. */ - public static Expression repeatAtLeast(CharClass c, int n) { return repeatAtLeast(charClass(c), n); } + public static Expression repeatAtLeast(CharClass c, int n) { + return repeatAtLeast(charClass(c), n); + } /** * Repeat the expression at least the given number of times (reluctant). */ - public static Expression repeatAtLeastReluctant(Expression expression, int n) { return new RepeatAtLeastReluctant(expression, n); } + public static Expression repeatAtLeastReluctant(Expression expression, int n) { + return new RepeatAtLeastReluctant(expression, n); + } /** * Repeat the expression at least the given number of times (reluctant). */ - public static Expression repeatAtLeastReluctant(String s, int n) { return repeatAtLeastReluctant(string(s), n); } + public static Expression repeatAtLeastReluctant(String s, int n) { + return repeatAtLeastReluctant(string(s), n); + } /** * Repeat the expression at least the given number of times (reluctant). */ - public static Expression repeatAtLeastReluctant(char c, int n) { return repeatAtLeastReluctant(character(c), n); } + public static Expression repeatAtLeastReluctant(char c, int n) { + return repeatAtLeastReluctant(character(c), n); + } /** * Repeat the expression at least the given number of times (reluctant). */ - public static Expression repeatAtLeastReluctant(CharClass c, int n) { return repeatAtLeastReluctant(charClass(c), n); } + public static Expression repeatAtLeastReluctant(CharClass c, int n) { + return repeatAtLeastReluctant(charClass(c), n); + } /** * Repeat the expression at least the given number of times (possessive). */ - public static Expression repeatAtLeastPossessive(Expression expression, int n) { return new RepeatAtLeastPossessive(expression, n); } + public static Expression repeatAtLeastPossessive(Expression expression, int n) { + return new RepeatAtLeastPossessive(expression, n); + } /** * Repeat the expression at least the given number of times (possessive). */ - public static Expression repeatAtLeastPossessive(String s, int n) { return repeatAtLeastPossessive(string(s), n); } + public static Expression repeatAtLeastPossessive(String s, int n) { + return repeatAtLeastPossessive(string(s), n); + } /** * Repeat the expression at least the given number of times (possessive). */ - public static Expression repeatAtLeastPossessive(char c, int n) { return repeatAtLeastPossessive(character(c), n); } + public static Expression repeatAtLeastPossessive(char c, int n) { + return repeatAtLeastPossessive(character(c), n); + } /** * Repeat the expression at least the given number of times (possessive). */ - public static Expression repeatAtLeastPossessive(CharClass c, int n) { return repeatAtLeastPossessive(charClass(c), n); } + public static Expression repeatAtLeastPossessive(CharClass c, int n) { + return repeatAtLeastPossessive(charClass(c), n); + } /** * Match a sequence of expessions. @@ -782,140 +831,160 @@ public static Expression separatedBy1(Expression separator, CharClass e) { } /** - * Match a (possibly empty) sequence of matches of [e], separated by matches of [separator] (possessive). + * Match a (possibly empty) sequence of matches of [e], separated by matches of [separator] + * (possessive). */ public static Expression separatedByPossessive(Expression separator, Expression e) { return optionalPossessive(separatedBy1Possessive(separator, e)); } /** - * Match a (possibly empty) sequence of matches of [e], separated by matches of [separator] (possessive). + * Match a (possibly empty) sequence of matches of [e], separated by matches of [separator] + * (possessive). */ public static Expression separatedByPossessive(char separator, Expression e) { return optionalPossessive(separatedBy1Possessive(character(separator), e)); } /** - * Match a (possibly empty) sequence of matches of [e], separated by matches of [separator] (possessive). + * Match a (possibly empty) sequence of matches of [e], separated by matches of [separator] + * (possessive). */ public static Expression separatedByPossessive(CharClass separator, Expression e) { return optionalPossessive(separatedBy1Possessive(charClass(separator), e)); } /** - * Match a (possibly empty) sequence of matches of [e], separated by matches of [separator] (possessive). + * Match a (possibly empty) sequence of matches of [e], separated by matches of [separator] + * (possessive). */ public static Expression separatedByPossessive(String separator, Expression e) { return separatedBy(string(separator), e); } /** - * Match a (possibly empty) sequence of matches of [e], separated by matches of [separator] (possessive). + * Match a (possibly empty) sequence of matches of [e], separated by matches of [separator] + * (possessive). */ public static Expression separatedByPossessive(Expression separator, String e) { return separatedBy(separator, string(e)); } /** - * Match a (possibly empty) sequence of matches of [e], separated by matches of [separator] (possessive). + * Match a (possibly empty) sequence of matches of [e], separated by matches of [separator] + * (possessive). */ public static Expression separatedByPossessive(Expression separator, char e) { return separatedBy(separator, character(e)); } /** - * Match a (possibly empty) sequence of matches of [e], separated by matches of [separator] (possessive). + * Match a (possibly empty) sequence of matches of [e], separated by matches of [separator] + * (possessive). */ public static Expression separatedByPossessive(String separator, String e) { return separatedBy(string(separator), string(e)); } /** - * Match a (possibly empty) sequence of matches of [e], separated by matches of [separator] (possessive). + * Match a (possibly empty) sequence of matches of [e], separated by matches of [separator] + * (possessive). */ public static Expression separatedByPossessive(char separator, String e) { return separatedBy(character(separator), string(e)); } /** - * Match a (possibly empty) sequence of matches of [e], separated by matches of [separator] (possessive). + * Match a (possibly empty) sequence of matches of [e], separated by matches of [separator] + * (possessive). */ public static Expression separatedByPossessive(String separator, char e) { return separatedBy(string(separator), character(e)); } /** - * Match a (possibly empty) sequence of matches of [e], separated by matches of [separator] (possessive). + * Match a (possibly empty) sequence of matches of [e], separated by matches of [separator] + * (possessive). */ public static Expression separatedByPossessive(char separator, char e) { return separatedBy(character(separator), character(e)); } /** - * Match a (possibly empty) sequence of matches of [e], separated by matches of [separator] (possessive). + * Match a (possibly empty) sequence of matches of [e], separated by matches of [separator] + * (possessive). */ public static Expression separatedBy1Possessive(Expression separator, Expression e) { return sequence(e, repeatPossessive(sequence(separator, e))); } /** - * Match a nonempty sequence of matches of [e], separated by matches of [separator] (possessive). + * Match a nonempty sequence of matches of [e], separated by matches of [separator] + * (possessive). */ public static Expression separatedBy1Possessive(String separator, Expression e) { return separatedBy1Possessive(string(separator), e); } /** - * Match a nonempty sequence of matches of [e], separated by matches of [separator] (possessive). + * Match a nonempty sequence of matches of [e], separated by matches of [separator] + * (possessive). */ public static Expression separatedBy1Possessive(char separator, Expression e) { return separatedBy1Possessive(character(separator), e); } /** - * Match a nonempty sequence of matches of [e], separated by matches of [separator] (possessive). + * Match a nonempty sequence of matches of [e], separated by matches of [separator] + * (possessive). */ public static Expression separatedBy1Possessive(CharClass separator, Expression e) { return separatedBy1Possessive(charClass(separator), e); } /** - * Match a nonempty sequence of matches of [e], separated by matches of [separator] (possessive). + * Match a nonempty sequence of matches of [e], separated by matches of [separator] + * (possessive). */ public static Expression separatedBy1Possessive(Expression separator, String e) { return separatedBy1Possessive(separator, string(e)); } /** - * Match a nonempty sequence of matches of [e], separated by matches of [separator] (possessive). + * Match a nonempty sequence of matches of [e], separated by matches of [separator] + * (possessive). */ public static Expression separatedBy1Possessive(Expression separator, char e) { return separatedBy1Possessive(separator, character(e)); } /** - * Match a nonempty sequence of matches of [e], separated by matches of [separator] (possessive). + * Match a nonempty sequence of matches of [e], separated by matches of [separator] + * (possessive). */ public static Expression separatedBy1Possessive(String separator, String e) { return separatedBy1Possessive(string(separator), string(e)); } /** - * Match a nonempty sequence of matches of [e], separated by matches of [separator] (possessive). + * Match a nonempty sequence of matches of [e], separated by matches of [separator] + * (possessive). */ public static Expression separatedBy1Possessive(char separator, String e) { return separatedBy1Possessive(character(separator), string(e)); } /** - * Match a nonempty sequence of matches of [e], separated by matches of [separator] (possessive). + * Match a nonempty sequence of matches of [e], separated by matches of [separator] + * (possessive). */ public static Expression separatedBy1Possessive(String separator, char e) { return separatedBy1Possessive(string(separator), character(e)); } /** - * Match a nonempty sequence of matches of [e], separated by matches of [separator] (possessive). + * Match a nonempty sequence of matches of [e], separated by matches of [separator] + * (possessive). */ public static Expression separatedBy1Possessive(char separator, char e) { return separatedBy1Possessive(character(separator), character(e)); @@ -972,6 +1041,13 @@ public static CaptureGroup capture(Expression expression) { return new CaptureGroup(expression); } + /** + * Return a named capture group for the given expression. + */ + public static CaptureGroup captureNamed(String name, Expression expression) { + return new CaptureGroup(expression, name); + } + /** * Create a capture group for the given sequence of expressions. */ @@ -979,6 +1055,13 @@ public static CaptureGroup capture(Object... os) { return capture(sequence(os)); } + /** + * Create a named capture group for the given sequence of expressions. + */ + public static CaptureGroup captureNamed(String name, Object... os) { + return captureNamed(name, sequence(os)); + } + /** * Create a capture group for the given expression. */ @@ -986,6 +1069,13 @@ public static CaptureGroup capture(String e) { return capture(string(e)); } + /** + * Create a named capture group for the given expression. + */ + public static CaptureGroup captureNamed(String name, String e) { + return captureNamed(name, string(e)); + } + /** * Create a capture group for the given expression. */ @@ -993,6 +1083,13 @@ public static CaptureGroup capture(CharClass e) { return capture(charClass(e)); } + /** + * Create a named capture group for the given expression. + */ + public static CaptureGroup captureNamed(String name, CharClass e) { + return captureNamed(name, charClass(e)); + } + /** * Create a capture group for the given expression. */ @@ -1000,6 +1097,13 @@ public static CaptureGroup capture(char e) { return capture(character(e)); } + /** + * Create a named capture group for the given expression. + */ + public static CaptureGroup captureNamed(String name, char e) { + return captureNamed(name, character(e)); + } + public static Expression positiveLookbehind(Expression expression) { return new PositiveLookbehind(expression); } @@ -1097,27 +1201,22 @@ public static Replacement replacement(Object... os) { Replacement replacement = new Replacement(); for (Object o : os) { if (o instanceof String) { - replacement.addPart( - new StringReplacementPart((String) o) - ); + replacement.addPart(new StringReplacementPart((String) o)); } else if (o instanceof Character) { - replacement.addPart( - new StringReplacementPart("" + (Character) o) - ); + replacement.addPart(new StringReplacementPart("" + (Character) o)); } else if (o instanceof CaptureGroup) { - replacement.addPart( - new CaptureGroupReplacementPart((CaptureGroup) o) - ); + replacement.addPart(new CaptureGroupReplacementPart((CaptureGroup) o)); } else { - throw new IllegalArgumentException("A replacement must be a string, character, or a capture group."); + throw new IllegalArgumentException( + "A replacement must be a string, character, or a capture group."); } } return replacement; } /** - * Converts vararg actuals consisting of expressions, strings, - * characters and character classes into an array of expressions. + * Converts vararg actuals consisting of expressions, strings, characters and character classes + * into an array of expressions. */ private static Expression[] convertStrings(Object[] os) { Expression[] es = new Expression[os.length]; @@ -1131,10 +1230,11 @@ private static Expression[] convertStrings(Object[] os) { } else if (os[i] instanceof Character) { es[i] = character((Character) os[i]); } else { - throw new IllegalArgumentException("An expression must be an Expression, string, character, or a character class."); + throw new IllegalArgumentException( + "An expression must be an Expression, string, character, or a character class."); } } return es; } -} \ No newline at end of file +} diff --git a/src/main/java/com/github/sgreben/regex_builder/tokens/START_GROUP_NAMED.java b/src/main/java/com/github/sgreben/regex_builder/tokens/START_GROUP_NAMED.java new file mode 100644 index 0000000..5580c3c --- /dev/null +++ b/src/main/java/com/github/sgreben/regex_builder/tokens/START_GROUP_NAMED.java @@ -0,0 +1,13 @@ +package com.github.sgreben.regex_builder.tokens; + +public class START_GROUP_NAMED implements TOKEN { + private final String name; + + public START_GROUP_NAMED(String name) { + this.name = name; + } + + public String regexString() { + return "(?<" + name + ">"; + } +} diff --git a/src/test/java/com/github/sgreben/regex_builder/FluentReTest.java b/src/test/java/com/github/sgreben/regex_builder/FluentReTest.java index 71e8839..7b4bdd3 100644 --- a/src/test/java/com/github/sgreben/regex_builder/FluentReTest.java +++ b/src/test/java/com/github/sgreben/regex_builder/FluentReTest.java @@ -1,11 +1,17 @@ package com.github.sgreben.regex_builder; -import org.junit.Test; - +import static com.github.sgreben.regex_builder.CharClass.beginInput; +import static com.github.sgreben.regex_builder.CharClass.digit; +import static com.github.sgreben.regex_builder.CharClass.endInput; +import static com.github.sgreben.regex_builder.CharClass.nonWhitespaceChar; +import static com.github.sgreben.regex_builder.CharClass.oneOf; +import static com.github.sgreben.regex_builder.CharClass.union; +import static com.github.sgreben.regex_builder.CharClass.whitespaceChar; +import static com.github.sgreben.regex_builder.CharClass.wordChar; +import static com.github.sgreben.regex_builder.Re.replacement; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import static com.github.sgreben.regex_builder.CharClass.*; -import static com.github.sgreben.regex_builder.Re.replacement; +import org.junit.Test; public class FluentReTest { @Test @@ -75,41 +81,43 @@ public void abcThen_DefOrGhiGroup() { assertEquals("(\\Qabc\\E((?:\\QDef\\E|\\QGhi\\E)))", p.toString()); } + @Test + public void namedGroup_thenUnnamedGroup() { + CaptureGroup namedGroup = FluentRe.match("foo").captureNamed("namedGroup"); + CaptureGroup unnamedGroup = FluentRe.match("bar").capture(); + Pattern p = FluentRe.match(namedGroup).then(unnamedGroup).compile(); + assertEquals("((?\\Qfoo\\E)(\\Qbar\\E))", p.toString()); + } + @Test public void apacheLogLine() { - String logLine = "127.0.0.1 - - [21/Jul/2014:9:55:27 -0800] \"GET /home.html HTTP/1.1\" 200 2048"; - // "^(\\S+) (\\S+) (\\S+) \\[([\\w:/]+\\s[+\\-]\\d{4})\\] \"(\\S+) (\\S+) (\\S+)\" (\\d{3}) (\\d+)"; + String logLine = + "127.0.0.1 - - [21/Jul/2014:9:55:27 -0800] \"GET /home.html HTTP/1.1\" 200 2048"; + // "^(\\S+) (\\S+) (\\S+) \\[([\\w:/]+\\s[+\\-]\\d{4})\\] \"(\\S+) (\\S+) (\\S+)\" (\\d{3}) + // (\\d+)"; CaptureGroup ip, client, user, dateTime, method, request, protocol, responseCode, size; FluentRe nonWhitespace = FluentRe.match(nonWhitespaceChar()).repeat1(); - ip = nonWhitespace.capture(); + ip = nonWhitespace.captureNamed("ip"); client = nonWhitespace.capture(); user = nonWhitespace.capture(); - dateTime = FluentRe - .match(union(wordChar(), oneOf(":/"))).repeat1() - .then(whitespaceChar()) - .then(oneOf("+\\-")) - .then(FluentRe.match(digit()).repeat(4)) - .capture(); + dateTime = FluentRe.match(union(wordChar(), oneOf(":/"))).repeat1().then(whitespaceChar()) + .then(oneOf("+\\-")).then(FluentRe.match(digit()).repeat(4)).capture(); method = nonWhitespace.capture(); request = nonWhitespace.capture(); protocol = nonWhitespace.capture(); - responseCode = FluentRe.match(digit()).repeat(3).capture(); + responseCode = FluentRe.match(digit()).repeat(3).captureNamed("code"); size = FluentRe.match(digit()).repeat1().capture(); - Pattern p = FluentRe.match(beginInput()) - .then(ip).then(' ') - .then(client).then(' ') - .then(user).then(" [") - .then(dateTime).then("] \"") - .then(method).then(' ') - .then(request).then(' ') - .then(protocol).then("\" ") - .then(responseCode).then(' ') - .then(size) - .then(endInput()) - .compile(); + Pattern p = FluentRe.match(beginInput()).then(ip).then(' ').then(client).then(' ') + .then(user).then(" [").then(dateTime).then("] \"").then(method).then(' ') + .then(request).then(' ').then(protocol).then("\" ").then(responseCode).then(' ') + .then(size).then(endInput()).compile(); + + assertEquals( + "(\\A(?(?:\\S)+)\\Q \\E((?:\\S)+)\\Q \\E((?:\\S)+)\\Q [\\E((?:[\\w[:/]])+\\s[+\\-](?:\\d){4})\\Q] \"\\E((?:\\S)+)\\Q \\E((?:\\S)+)\\Q \\E((?:\\S)+)\\Q\" \\E(?(?:\\d){3})\\Q \\E((?:\\d)+)\\z)", + p.toString()); Matcher m = p.matcher(logLine); assertTrue(m.matches()); @@ -122,7 +130,9 @@ public void apacheLogLine() { assertEquals("HTTP/1.1", m.group(protocol)); assertEquals("200", m.group(responseCode)); assertEquals("2048", m.group(size)); - assertEquals("127.0.0.1 - /home.html - 200", m.replaceAll(replacement(ip, " - ", request, " - ", responseCode))); - assertEquals("127.0.0.1 - /home.html - 200", m.replaceFirst(replacement(ip, " - ", request, " - ", responseCode))); + assertEquals("127.0.0.1 - /home.html - 200", + m.replaceAll(replacement(ip, " - ", request, " - ", responseCode))); + assertEquals("127.0.0.1 - /home.html - 200", + m.replaceFirst(replacement(ip, " - ", request, " - ", responseCode))); } } diff --git a/src/test/java/com/github/sgreben/regex_builder/MatcherTest.java b/src/test/java/com/github/sgreben/regex_builder/MatcherTest.java index e855508..bd75bd0 100644 --- a/src/test/java/com/github/sgreben/regex_builder/MatcherTest.java +++ b/src/test/java/com/github/sgreben/regex_builder/MatcherTest.java @@ -1,6 +1,7 @@ package com.github.sgreben.regex_builder; import static com.github.sgreben.regex_builder.Re.capture; +import static com.github.sgreben.regex_builder.Re.captureNamed; import static com.github.sgreben.regex_builder.Re.repeat; import static com.github.sgreben.regex_builder.Re.repeat1; import static com.github.sgreben.regex_builder.Re.replacement; @@ -161,6 +162,28 @@ public void matchCharTwoGroup_replaceByDoubled_caseInsensitive() { assertEquals("c C def ghi", result); } + @Test + public void matchCharTwoGroup_replaceByDoubled_caseInsensitive_namedGroup() { + String s = "abc ABC def ghi"; + CaptureGroup a = Re.captureNamed("a", Re.character('a')); + CaptureGroup b = Re.capture(Re.character('b')); + Pattern p = Pattern.compile(Re.sequence(a, b), CASE_INSENSITIVE); + Matcher m = p.matcher(s); + String result = m.replaceAll(Re.replacement("<", b, b, ">")); + assertEquals("c C def ghi", result); + } + + @Test + public void matchCharTwoGroup_replaceByDoubled_caseInsensitive_namedGroups() { + String s = "abc ABC def ghi"; + CaptureGroup a = Re.captureNamed("groupA", Re.character('a')); + CaptureGroup b = Re.captureNamed("groupB", Re.character('b')); + Pattern p = Pattern.compile(Re.sequence(a, b), CASE_INSENSITIVE); + Matcher m = p.matcher(s); + String result = m.replaceAll(Re.replacement("<", b, b, ">")); + assertEquals("c C def ghi", result); + } + @Test public void matchChars_literalSyntax_replaceByDoubled() { String s = "abc def ghi"; @@ -319,7 +342,7 @@ public void apacheLogLine() { CaptureGroup ip, client, user, dateTime, method, request, protocol, responseCode, size; Expression nonWhitespace = repeat1(CharClass.nonWhitespaceChar()); - ip = capture(nonWhitespace); + ip = captureNamed("ip", nonWhitespace); client = capture(nonWhitespace); user = capture(nonWhitespace); dateTime = capture(sequence(repeat1(CharClass.union(CharClass.wordChar(), ':', '/')), // 21/Jul/2014:9:55:27 @@ -329,7 +352,7 @@ public void apacheLogLine() { method = capture(nonWhitespace); request = capture(nonWhitespace); protocol = capture(nonWhitespace); - responseCode = capture(repeat(CharClass.digit(), 3)); + responseCode = captureNamed("code", repeat(CharClass.digit(), 3)); size = capture(repeat1(CharClass.digit())); Pattern p = Pattern.compile(sequence(CharClass.beginInput(), ip, ' ', client, ' ', user,