Skip to content

Commit 3e9accd

Browse files
committed
CharacterSet.negate()
1 parent e7362e0 commit 3e9accd

File tree

3 files changed

+68
-11
lines changed

3 files changed

+68
-11
lines changed

dot-parse/src/main/java/com/google/common/labs/parse/CharacterSet.java

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

33
import static com.google.common.labs.parse.Utils.checkArgument;
44
import static com.google.mu.util.CharPredicate.isNot;
5+
import static com.google.mu.util.Substring.after;
6+
import static com.google.mu.util.Substring.prefix;
7+
import static com.google.mu.util.Substring.suffix;
58
import static java.util.stream.Collectors.reducing;
69

710
import com.google.mu.util.CharPredicate;
11+
import com.google.mu.util.Substring;
812

913
/**
1014
* Represents a set of characters specified by a regex-like character set string.
@@ -61,6 +65,12 @@ public boolean contains(char ch) {
6165
return predicate.test(ch);
6266
}
6367

68+
@Override public CharacterSet not() {
69+
String content = Substring.between(prefix("["), suffix("]")).from(string).get();
70+
return new CharacterSet(
71+
"[" + after(prefix("^")).from(content).orElse("^" + content) + "]", predicate.not());
72+
}
73+
6474
@Override public boolean equals(Object obj) {
6575
return (obj instanceof CharacterSet that) && string.equals(that.string);
6676
}

dot-parse/src/main/java/com/google/common/labs/parse/Parser.java

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -849,17 +849,6 @@ public final Parser<T> notImmediatelyFollowedBy(CharPredicate predicate, String
849849
return notFollowedBy(literally(single(predicate, name)), name);
850850
}
851851

852-
/**
853-
* A form of negative lookahead such that the match is rejected if <em>immediately</em> followed
854-
* by (no skippable characters as specified by {@link #parseSkipping parseSkipping()} in between)
855-
* a character contained in {@code characterSet}.
856-
*
857-
* @since 9.4
858-
*/
859-
public final Parser<T> notImmediatelyFollowedBy(CharacterSet characterSet) {
860-
return notImmediatelyFollowedBy(characterSet, characterSet.toString());
861-
}
862-
863852
/**
864853
* Starts a fluent chain for matching the current parser optionally. {@code defaultValue} will be
865854
* the result in case the current parser doesn't match.

dot-parse/src/test/java/com/google/common/labs/parse/CharacterSetTest.java

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,64 @@ public void test_missingBrackets_throws() {
6060
assertThat(thrown).hasMessageThat().contains("Use [a-z] instead.");
6161
}
6262

63+
@Test
64+
public void not_positiveSet() {
65+
CharacterSet positive = charsIn("[ab]");
66+
CharacterSet negated = positive.not();
67+
assertThat(negated.contains('a')).isFalse();
68+
assertThat(negated.contains('b')).isFalse();
69+
assertThat(negated.contains('c')).isTrue();
70+
assertThat(negated.toString()).isEqualTo("[^ab]");
71+
}
72+
73+
@Test
74+
public void not_negativeSet() {
75+
CharacterSet negative = charsIn("[^ab]");
76+
CharacterSet negated = negative.not();
77+
assertThat(negated.contains('a')).isTrue();
78+
assertThat(negated.contains('b')).isTrue();
79+
assertThat(negated.contains('c')).isFalse();
80+
assertThat(negated.toString()).isEqualTo("[ab]");
81+
}
82+
83+
@Test
84+
public void not_rangeSet() {
85+
CharacterSet range = charsIn("[a-c]");
86+
CharacterSet negated = range.not();
87+
assertThat(negated.contains('a')).isFalse();
88+
assertThat(negated.contains('b')).isFalse();
89+
assertThat(negated.contains('c')).isFalse();
90+
assertThat(negated.contains('d')).isTrue();
91+
assertThat(negated.toString()).isEqualTo("[^a-c]");
92+
}
93+
94+
@Test
95+
public void not_negatedRangeSet() {
96+
CharacterSet negatedRange = charsIn("[^a-c]");
97+
CharacterSet negated = negatedRange.not();
98+
assertThat(negated.contains('a')).isTrue();
99+
assertThat(negated.contains('b')).isTrue();
100+
assertThat(negated.contains('c')).isTrue();
101+
assertThat(negated.contains('d')).isFalse();
102+
assertThat(negated.toString()).isEqualTo("[a-c]");
103+
}
104+
105+
@Test
106+
public void not_emptySet() {
107+
CharacterSet empty = charsIn("[]");
108+
CharacterSet negated = empty.not();
109+
assertThat(negated.contains('a')).isTrue();
110+
assertThat(negated.toString()).isEqualTo("[^]");
111+
}
112+
113+
@Test
114+
public void not_fullSet() {
115+
CharacterSet full = charsIn("[^]");
116+
CharacterSet negated = full.not();
117+
assertThat(negated.contains('a')).isFalse();
118+
assertThat(negated.toString()).isEqualTo("[]");
119+
}
120+
63121
@Test
64122
public void testEquals() {
65123
new EqualsTester()

0 commit comments

Comments
 (0)