Skip to content

Commit c7554c1

Browse files
author
MerkushevKirill
committed
add - method to add another regex builder to current regex
add - unnamed group method and shortcuts for capture* methods rft - non backward compatibility - make Builder constructor package-private
1 parent 88884ab commit c7554c1

File tree

4 files changed

+221
-21
lines changed

4 files changed

+221
-21
lines changed

src/main/java/ru/lanwen/verbalregex/VerbalExpression.java

Lines changed: 102 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,33 @@ public static class Builder {
1414
private StringBuilder suffixes = new StringBuilder();
1515
private int modifiers = Pattern.MULTILINE;
1616

17+
/**
18+
* Package private. Use {@link #regex()} to build a new one
19+
* @since 1.2
20+
*/
21+
Builder() {
22+
}
23+
24+
/**
25+
* Escapes any non-word char with two backslashes
26+
* used by any method, except {@link #add(String)}
27+
*
28+
* @param pValue - the string for char escaping
29+
* @return sanitized string value
30+
*/
1731
private String sanitize(final String pValue) {
1832
return pValue.replaceAll("[\\W]", "\\\\$0");
1933
}
2034

35+
/**
36+
* Counts occurrences of some substring in whole string
37+
* Same as org.apache.commons.lang3.StringUtils#countMatches(String, java.lang.String)
38+
* by effect. Used to count braces for {@link #or(String)} method
39+
*
40+
* @param where - where to find
41+
* @param what - what needs to count matches
42+
* @return 0 if nothing found, count of occurrences instead
43+
*/
2144
private int countOccurrencesOf(String where, String what) {
2245
return (where.length() - where.replace(what, "").length()) / what.length();
2346
}
@@ -40,11 +63,21 @@ public VerbalExpression build() {
4063
* @param pValue - literal expression, not sanitized
4164
* @return this builder
4265
*/
43-
public Builder add(String pValue) {
66+
public Builder add(final String pValue) {
4467
this.source.append(pValue);
4568
return this;
4669
}
4770

71+
/**
72+
* Append a regex from builder and wrap it with unnamed group (?: ... )
73+
* @param regex - VerbalExpression.Builder, that not changed
74+
* @return this builder
75+
* @since 1.2
76+
*/
77+
public Builder add(final Builder regex) {
78+
return this.group().add(regex.build().toString()).endGr();
79+
}
80+
4881
/**
4982
* Enable or disable the expression to start at the beginning of the line
5083
*
@@ -147,7 +180,7 @@ public Builder anything() {
147180
* Add expression that matches anything, but not passed argument
148181
*
149182
* @param pValue - the string not to match
150-
* @return
183+
* @return this builder
151184
*/
152185
public Builder anythingButNot(final String pValue) {
153186
return this.add("(?:[^" + sanitize(pValue) + "]*)");
@@ -273,6 +306,12 @@ public Builder anyOf(final String pValue) {
273306
return this;
274307
}
275308

309+
/**
310+
* Shortcut to {@link #anyOf(String)}
311+
*
312+
* @param value - CharSequence every char from can be matched
313+
* @return this builder
314+
*/
276315
public Builder any(final String value) {
277316
return this.anyOf(value);
278317
}
@@ -452,6 +491,35 @@ public Builder capture() {
452491
return this.add("(");
453492
}
454493

494+
/**
495+
* Shortcut for {@link #capture()}
496+
*
497+
* @return this builder
498+
* @since 1.2
499+
*/
500+
public Builder capt() {
501+
return this.capture();
502+
}
503+
504+
/**
505+
* Same as {@link #capture()}, but don't save result
506+
* May be used to set count of duplicated captures, without creating a new saved capture
507+
* Example:
508+
* // Without group() - count(2) applies only to second capture
509+
* regex().group()
510+
* .capt().range("0", "1").endCapt().tab()
511+
* .capt().digit().count(5).endCapt()
512+
* .endGr().count(2);
513+
*
514+
*
515+
* @return this builder
516+
* @since 1.2
517+
*/
518+
public Builder group() {
519+
this.suffixes.append(")");
520+
return this.add("(?:");
521+
}
522+
455523
/**
456524
* Close brace for previous capture and remove last closed brace from suffixes
457525
* Can be used to continue build regex after capture or to add multiply captures
@@ -463,12 +531,39 @@ public Builder endCapture() {
463531
this.suffixes.setLength(suffixes.length() - 1);
464532
return this.add(")");
465533
} else {
466-
throw new IllegalStateException("Can't end capture when it not started");
534+
throw new IllegalStateException("Can't end capture (group) when it not started");
467535
}
468536
}
469-
}
470537

538+
/**
539+
* Shortcut for {@link #endCapture()}
540+
* @return this builder
541+
* @since 1.2
542+
*/
543+
public Builder endCapt() {
544+
return this.endCapture();
545+
}
546+
547+
/**
548+
* Closes current unnamed and unsaved group
549+
* Shortcut for {@link #endCapture()}
550+
* Use it with {@link #group()} for prettify code
551+
* Example:
552+
* regex().group().maybe("word").count(2).endGr()
553+
* @return this builder
554+
* @since 1.2
555+
*/
556+
public Builder endGr() {
557+
return this.endCapture();
558+
}
559+
}
471560

561+
/**
562+
* Use builder {@link #regex()} (or {@link #regex(ru.lanwen.verbalregex.VerbalExpression.Builder)})
563+
* to create new instance of VerbalExpression
564+
*
565+
* @param pattern - {@link java.util.regex.Pattern} that constructed by builder
566+
*/
472567
private VerbalExpression(final Pattern pattern) {
473568
this.pattern = pattern;
474569
}
@@ -518,6 +613,7 @@ public String getText(final String toTest) {
518613
* @param toTest - string to extract from
519614
* @param group - group to extract
520615
* @return extracted group
616+
* @since 1.1
521617
*/
522618
public String getText(final String toTest, final int group) {
523619
Matcher m = pattern.matcher(toTest);
@@ -539,6 +635,7 @@ public String toString() {
539635
*
540636
* @param pBuilder - instance to clone
541637
* @return new VerbalExpression.Builder copied from passed
638+
* @since 1.1
542639
*/
543640
public static Builder regex(final Builder pBuilder) {
544641
Builder builder = new Builder();
@@ -555,6 +652,7 @@ public static Builder regex(final Builder pBuilder) {
555652
* Creates new instance of VerbalExpression builder
556653
*
557654
* @return new VerbalExpression.Builder
655+
* @since 1.1
558656
*/
559657
public static Builder regex() {
560658
return new Builder();

src/test/java/ru/lanwen/verbalregex/BasicFunctionalityUnitTest.java

Lines changed: 39 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22

33
import org.junit.Test;
44

5-
import static org.hamcrest.CoreMatchers.equalTo;
6-
import static org.hamcrest.CoreMatchers.is;
5+
import static org.hamcrest.CoreMatchers.*;
76
import static org.junit.Assert.*;
7+
import static ru.lanwen.verbalregex.VerbalExpression.regex;
8+
import static ru.lanwen.verbalregex.matchers.TestsExactMatcher.matchesExactly;
89

910
public class BasicFunctionalityUnitTest {
1011
@Test
@@ -69,7 +70,7 @@ public void testStartOfLine() {
6970

7071
@Test
7172
public void testStartOfLineFalse() {
72-
VerbalExpression testRegex = VerbalExpression.regex()
73+
VerbalExpression testRegex = regex()
7374
.startOfLine(false)
7475
.then("a")
7576
.build();
@@ -79,7 +80,7 @@ public void testStartOfLineFalse() {
7980

8081
@Test
8182
public void testRangeWithMultiplyRanges() throws Exception {
82-
VerbalExpression regex = VerbalExpression.regex().range("a", "z", "A", "Z").build();
83+
VerbalExpression regex = regex().range("a", "z", "A", "Z").build();
8384

8485
assertThat("Regex with multi-range differs from expected", regex.toString(), equalTo("[a-zA-Z]"));
8586
assertThat("Regex don't matches letter", regex.test("b"), is(true));
@@ -102,7 +103,7 @@ public void testEndOfLine() {
102103

103104
@Test
104105
public void testEndOfLineIsFalse() {
105-
VerbalExpression testRegex = VerbalExpression.regex()
106+
VerbalExpression testRegex = regex()
106107
.find("a")
107108
.endOfLine(false)
108109
.build();
@@ -141,8 +142,8 @@ public void testAnyOf() {
141142

142143
@Test
143144
public void testAnySameAsAnyOf() {
144-
VerbalExpression any = VerbalExpression.regex().any("abc").build();
145-
VerbalExpression anyOf = VerbalExpression.regex().anyOf("abc").build();
145+
VerbalExpression any = regex().any("abc").build();
146+
VerbalExpression anyOf = regex().anyOf("abc").build();
146147

147148
assertThat("any differs from anyOf", any.toString(), equalTo(anyOf.toString()));
148149
}
@@ -189,7 +190,7 @@ public void testBr() {
189190
.then("def")
190191
.build();
191192

192-
assertThat(".br() differs from .lineBreak()", testRegexBr.toString(), equalTo(testRegexLineBr.toString()));
193+
assertThat(".br() differs from .lineBreak()", testRegexBr.toString(), equalTo(testRegexLineBr.toString()));
193194
}
194195

195196
@Test
@@ -224,7 +225,7 @@ public void testWithAnyCase() {
224225

225226
@Test
226227
public void testWithAnyCaseIsFalse() {
227-
VerbalExpression testRegex = VerbalExpression.regex()
228+
VerbalExpression testRegex = regex()
228229
.withAnyCase()
229230
.startOfLine()
230231
.then("a")
@@ -236,7 +237,7 @@ public void testWithAnyCaseIsFalse() {
236237

237238
@Test
238239
public void testSearchOneLine() {
239-
VerbalExpression testRegex = VerbalExpression.regex()
240+
VerbalExpression testRegex = regex()
240241
.startOfLine()
241242
.then("a")
242243
.br()
@@ -274,18 +275,24 @@ public void testGetText() {
274275
@Test
275276
public void testStartCapture() {
276277
String text = "aaabcd";
277-
VerbalExpression regex = VerbalExpression.regex()
278+
VerbalExpression regex = regex()
278279
.find("a").count(3)
279280
.capture().find("b").anything().build();
280281

281282
assertThat("regex don't match string", regex.getText(text), equalTo(text));
282283
assertThat("can't get first captured group", regex.getText(text, 1), equalTo("bcd"));
283284
}
284285

286+
@Test
287+
public void captIsSameAsCapture() {
288+
assertThat("Capt produce defferent than capture regex", regex().capt().build().toString(),
289+
equalTo(regex().capture().build().toString()));
290+
}
291+
285292
@Test
286293
public void shouldReturnEmptyStringWhenNoGroupFound() {
287294
String text = "abc";
288-
VerbalExpression regex = VerbalExpression.regex().find("d").capture().find("e").build();
295+
VerbalExpression regex = regex().find("d").capture().find("e").build();
289296

290297
assertThat("regex don't match string", regex.getText(text), equalTo(""));
291298
assertThat("first captured group not empty string", regex.getText(text, 1), equalTo(""));
@@ -298,17 +305,17 @@ public void testCountWithRange() {
298305
String text2c = "abcce";
299306
String text1c = "abce";
300307

301-
VerbalExpression regex = VerbalExpression.regex().find("c").count(2, 3).build();
308+
VerbalExpression regex = regex().find("c").count(2, 3).build();
302309

303310
assertThat("regex don't match string", regex.getText(text4c), equalTo("ccc"));
304311
assertThat("regex don't match string", regex.getText(text2c), equalTo("cc"));
305312
assertThat("regex don't match string", regex.test(text1c), is(false));
306313
}
307314

308-
@Test
315+
@Test
309316
public void testEndCapture() {
310317
String text = "aaabcd";
311-
VerbalExpression regex = VerbalExpression.regex()
318+
VerbalExpression regex = regex()
312319
.find("a")
313320
.capture().find("b").anything().endCapture().then("cd").build();
314321

@@ -320,16 +327,17 @@ public void testEndCapture() {
320327
@Test
321328
public void testMultiplyCapture() {
322329
String text = "aaabcd";
323-
VerbalExpression regex = VerbalExpression.regex()
330+
VerbalExpression regex = regex()
324331
.find("a").count(1)
325332
.capture().find("b").endCapture().anything().capture().find("d").build();
326333

327334
assertThat("can't get first captured group", regex.getText(text, 1), equalTo("b"));
328335
assertThat("can't get second captured group", regex.getText(text, 2), equalTo("d"));
329336
}
337+
330338
@Test
331339
public void testOrWithCapture() {
332-
VerbalExpression testRegex = VerbalExpression.regex()
340+
VerbalExpression testRegex = regex()
333341
.capture()
334342
.find("abc")
335343
.or("def")
@@ -343,4 +351,18 @@ public void testOrWithCapture() {
343351
assertThat(testRegex.getText("xxxabcdefzzz", 2), equalTo("abcnull"));
344352
}
345353

354+
@Test
355+
public void addRegexBuilderWrapsItWithUnsavedGroup() throws Exception {
356+
VerbalExpression regex = regex()
357+
.add(regex().capt().find("string").count(2).endCapt().count(1).digit()).count(2).build();
358+
359+
assertThat("Added regex builder don't wrapped with unsaved group",
360+
regex.toString(), startsWith("(?:((?:string"));
361+
362+
String example = "stringstring1";
363+
String example2digit = "stringstring11";
364+
365+
assertThat(regex, matchesExactly(example + example));
366+
assertThat(regex, not(matchesExactly(example2digit)));
367+
}
346368
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package ru.lanwen.verbalregex.matchers;
2+
3+
import org.hamcrest.Description;
4+
import org.hamcrest.Factory;
5+
import org.hamcrest.TypeSafeMatcher;
6+
import ru.lanwen.verbalregex.VerbalExpression;
7+
8+
/**
9+
* User: lanwen
10+
* Date: 29.05.14
11+
* Time: 20:06
12+
*/
13+
public class TestMatchMatcher extends TypeSafeMatcher<VerbalExpression> {
14+
15+
private String toTest;
16+
17+
private TestMatchMatcher(String toTest) {
18+
this.toTest = toTest;
19+
}
20+
21+
@Override
22+
protected boolean matchesSafely(VerbalExpression verbalExpression) {
23+
return verbalExpression.test(toTest);
24+
}
25+
26+
@Override
27+
public void describeTo(Description description) {
28+
description.appendText("regex should match to ").appendValue(toTest);
29+
}
30+
31+
@Override
32+
protected void describeMismatchSafely(VerbalExpression item, Description mismatchDescription) {
33+
mismatchDescription.appendText(item.toString()).appendText(" don't matches this string");
34+
}
35+
36+
@Factory
37+
public static TestMatchMatcher matchesTo(String test) {
38+
return new TestMatchMatcher(test);
39+
}
40+
}

0 commit comments

Comments
 (0)