1/*
2 * Copyright (C) 2008 The Guava Authors
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.google.common.base;
18
19import static com.google.common.base.CharMatcher.BREAKING_WHITESPACE;
20import static com.google.common.base.CharMatcher.WHITESPACE;
21import static com.google.common.base.CharMatcher.anyOf;
22import static com.google.common.base.CharMatcher.forPredicate;
23import static com.google.common.base.CharMatcher.inRange;
24import static com.google.common.base.CharMatcher.is;
25import static com.google.common.base.CharMatcher.isNot;
26import static com.google.common.base.CharMatcher.noneOf;
27
28import com.google.common.annotations.GwtCompatible;
29import com.google.common.collect.Sets;
30
31import junit.framework.AssertionFailedError;
32import junit.framework.TestCase;
33
34import java.util.Arrays;
35import java.util.HashSet;
36import java.util.Random;
37import java.util.Set;
38
39/**
40 * Unit test for {@link CharMatcher}.
41 *
42 * @author Kevin Bourrillion
43 */
44@GwtCompatible(emulated = true)
45public class CharMatcherTest extends TestCase {
46
47  private static final CharMatcher WHATEVER = new CharMatcher() {
48    @Override public boolean matches(char c) {
49      throw new AssertionFailedError(
50          "You weren't supposed to actually invoke me!");
51    }
52  };
53
54  public void testAnyAndNone_logicalOps() throws Exception {
55    // These are testing behavior that's never promised by the API, but since
56    // we're lucky enough that these do pass, it saves us from having to write
57    // more excruciating tests! Hooray!
58
59    assertSame(CharMatcher.ANY, CharMatcher.NONE.negate());
60    assertSame(CharMatcher.NONE, CharMatcher.ANY.negate());
61
62    assertSame(WHATEVER, CharMatcher.ANY.and(WHATEVER));
63    assertSame(CharMatcher.ANY, CharMatcher.ANY.or(WHATEVER));
64
65    assertSame(CharMatcher.NONE, CharMatcher.NONE.and(WHATEVER));
66    assertSame(WHATEVER, CharMatcher.NONE.or(WHATEVER));
67  }
68
69  // The rest of the behavior of ANY and DEFAULT will be covered in the tests for
70  // the text processing methods below.
71
72  public void testWhitespaceBreakingWhitespaceSubset() throws Exception {
73    for (int c = 0; c <= Character.MAX_VALUE; c++) {
74      if (BREAKING_WHITESPACE.apply((char) c)) {
75        assertTrue(Integer.toHexString(c), WHITESPACE.apply((char) c));
76      }
77    }
78  }
79
80  // The next tests require ICU4J and have, at least for now, been sliced out
81  // of the open-source view of the tests.
82
83  // Omitting tests for the rest of the JAVA_* constants as these are defined
84  // as extremely straightforward pass-throughs to the JDK methods.
85
86  // We're testing the is(), isNot(), anyOf(), noneOf() and inRange() methods
87  // below by testing their text-processing methods.
88
89  // The organization of this test class is unusual, as it's not done by
90  // method, but by overall "scenario". Also, the variety of actual tests we
91  // do borders on absurd overkill. Better safe than sorry, though?
92
93  public void testEmpty() throws Exception {
94    doTestEmpty(CharMatcher.ANY);
95    doTestEmpty(CharMatcher.NONE);
96    doTestEmpty(is('a'));
97    doTestEmpty(isNot('a'));
98    doTestEmpty(anyOf(""));
99    doTestEmpty(anyOf("x"));
100    doTestEmpty(anyOf("xy"));
101    doTestEmpty(anyOf("CharMatcher"));
102    doTestEmpty(noneOf("CharMatcher"));
103    doTestEmpty(inRange('n', 'q'));
104    doTestEmpty(forPredicate(Predicates.equalTo('c')));
105  }
106
107  private void doTestEmpty(CharMatcher matcher) throws Exception {
108    reallyTestEmpty(matcher);
109    reallyTestEmpty(matcher.negate());
110    reallyTestEmpty(matcher.precomputed());
111  }
112
113  private void reallyTestEmpty(CharMatcher matcher) throws Exception {
114    assertEquals(-1, matcher.indexIn(""));
115    assertEquals(-1, matcher.indexIn("", 0));
116    try {
117      matcher.indexIn("", 1);
118      fail();
119    } catch (IndexOutOfBoundsException expected) {
120    }
121    try {
122      matcher.indexIn("", -1);
123      fail();
124    } catch (IndexOutOfBoundsException expected) {
125    }
126    assertEquals(-1, matcher.lastIndexIn(""));
127    assertFalse(matcher.matchesAnyOf(""));
128    assertTrue(matcher.matchesAllOf(""));
129    assertTrue(matcher.matchesNoneOf(""));
130    assertEquals("", matcher.removeFrom(""));
131    assertEquals("", matcher.replaceFrom("", 'z'));
132    assertEquals("", matcher.replaceFrom("", "ZZ"));
133    assertEquals("", matcher.trimFrom(""));
134    assertEquals(0, matcher.countIn(""));
135  }
136
137  public void testNoMatches() {
138    doTestNoMatches(CharMatcher.NONE, "blah");
139    doTestNoMatches(is('a'), "bcde");
140    doTestNoMatches(isNot('a'), "aaaa");
141    doTestNoMatches(anyOf(""), "abcd");
142    doTestNoMatches(anyOf("x"), "abcd");
143    doTestNoMatches(anyOf("xy"), "abcd");
144    doTestNoMatches(anyOf("CharMatcher"), "zxqy");
145    doTestNoMatches(noneOf("CharMatcher"), "ChMa");
146    doTestNoMatches(inRange('p', 'x'), "mom");
147    doTestNoMatches(forPredicate(Predicates.equalTo('c')), "abe");
148    doTestNoMatches(inRange('A', 'Z').and(inRange('F', 'K').negate()), "F1a");
149    doTestNoMatches(CharMatcher.DIGIT, "\tAz()");
150    doTestNoMatches(CharMatcher.JAVA_DIGIT, "\tAz()");
151    doTestNoMatches(CharMatcher.DIGIT.and(CharMatcher.ASCII), "\tAz()");
152    doTestNoMatches(CharMatcher.SINGLE_WIDTH, "\u05bf\u3000");
153  }
154
155  private void doTestNoMatches(CharMatcher matcher, String s) {
156    reallyTestNoMatches(matcher, s);
157    reallyTestAllMatches(matcher.negate(), s);
158    reallyTestNoMatches(matcher.precomputed(), s);
159    reallyTestAllMatches(matcher.negate().precomputed(), s);
160    reallyTestAllMatches(matcher.precomputed().negate(), s);
161    reallyTestNoMatches(forPredicate(matcher), s);
162
163    reallyTestNoMatches(matcher, new StringBuilder(s));
164  }
165
166  public void testAllMatches() {
167    doTestAllMatches(CharMatcher.ANY, "blah");
168    doTestAllMatches(isNot('a'), "bcde");
169    doTestAllMatches(is('a'), "aaaa");
170    doTestAllMatches(noneOf("CharMatcher"), "zxqy");
171    doTestAllMatches(anyOf("x"), "xxxx");
172    doTestAllMatches(anyOf("xy"), "xyyx");
173    doTestAllMatches(anyOf("CharMatcher"), "ChMa");
174    doTestAllMatches(inRange('m', 'p'), "mom");
175    doTestAllMatches(forPredicate(Predicates.equalTo('c')), "ccc");
176    doTestAllMatches(CharMatcher.DIGIT, "0123456789\u0ED0\u1B59");
177    doTestAllMatches(CharMatcher.JAVA_DIGIT, "0123456789");
178    doTestAllMatches(CharMatcher.DIGIT.and(CharMatcher.ASCII), "0123456789");
179    doTestAllMatches(CharMatcher.SINGLE_WIDTH, "\t0123ABCdef~\u00A0\u2111");
180  }
181
182  private void doTestAllMatches(CharMatcher matcher, String s) {
183    reallyTestAllMatches(matcher, s);
184    reallyTestNoMatches(matcher.negate(), s);
185    reallyTestAllMatches(matcher.precomputed(), s);
186    reallyTestNoMatches(matcher.negate().precomputed(), s);
187    reallyTestNoMatches(matcher.precomputed().negate(), s);
188    reallyTestAllMatches(forPredicate(matcher), s);
189
190    reallyTestAllMatches(matcher, new StringBuilder(s));
191  }
192
193  private void reallyTestNoMatches(CharMatcher matcher, CharSequence s) {
194    assertFalse(matcher.matches(s.charAt(0)));
195    assertEquals(-1, matcher.indexIn(s));
196    assertEquals(-1, matcher.indexIn(s, 0));
197    assertEquals(-1, matcher.indexIn(s, 1));
198    assertEquals(-1, matcher.indexIn(s, s.length()));
199    try {
200      matcher.indexIn(s, s.length() + 1);
201      fail();
202    } catch (IndexOutOfBoundsException expected) {
203    }
204    try {
205      matcher.indexIn(s, -1);
206      fail();
207    } catch (IndexOutOfBoundsException expected) {
208    }
209    assertEquals(-1, matcher.lastIndexIn(s));
210    assertFalse(matcher.matchesAnyOf(s));
211    assertFalse(matcher.matchesAllOf(s));
212    assertTrue(matcher.matchesNoneOf(s));
213
214    assertEquals(s.toString(), matcher.removeFrom(s));
215    assertEquals(s.toString(), matcher.replaceFrom(s, 'z'));
216    assertEquals(s.toString(), matcher.replaceFrom(s, "ZZ"));
217    assertEquals(s.toString(), matcher.trimFrom(s));
218    assertEquals(0, matcher.countIn(s));
219  }
220
221  private void reallyTestAllMatches(CharMatcher matcher, CharSequence s) {
222    assertTrue(matcher.matches(s.charAt(0)));
223    assertEquals(0, matcher.indexIn(s));
224    assertEquals(0, matcher.indexIn(s, 0));
225    assertEquals(1, matcher.indexIn(s, 1));
226    assertEquals(-1, matcher.indexIn(s, s.length()));
227    assertEquals(s.length() - 1, matcher.lastIndexIn(s));
228    assertTrue(matcher.matchesAnyOf(s));
229    assertTrue(matcher.matchesAllOf(s));
230    assertFalse(matcher.matchesNoneOf(s));
231    assertEquals("", matcher.removeFrom(s));
232    assertEquals(Strings.repeat("z", s.length()),
233        matcher.replaceFrom(s, 'z'));
234    assertEquals(Strings.repeat("ZZ", s.length()),
235        matcher.replaceFrom(s, "ZZ"));
236    assertEquals("", matcher.trimFrom(s));
237    assertEquals(s.length(), matcher.countIn(s));
238  }
239
240  public void testGeneral() {
241    doTestGeneral(is('a'), 'a', 'b');
242    doTestGeneral(isNot('a'), 'b', 'a');
243    doTestGeneral(anyOf("x"), 'x', 'z');
244    doTestGeneral(anyOf("xy"), 'y', 'z');
245    doTestGeneral(anyOf("CharMatcher"), 'C', 'z');
246    doTestGeneral(noneOf("CharMatcher"), 'z', 'C');
247    doTestGeneral(inRange('p', 'x'), 'q', 'z');
248  }
249
250  private void doTestGeneral(CharMatcher matcher, char match, char noMatch) {
251    doTestOneCharMatch(matcher, "" + match);
252    doTestOneCharNoMatch(matcher, "" + noMatch);
253    doTestMatchThenNoMatch(matcher, "" + match + noMatch);
254    doTestNoMatchThenMatch(matcher, "" + noMatch + match);
255  }
256
257  private void doTestOneCharMatch(CharMatcher matcher, String s) {
258    reallyTestOneCharMatch(matcher, s);
259    reallyTestOneCharNoMatch(matcher.negate(), s);
260    reallyTestOneCharMatch(matcher.precomputed(), s);
261    reallyTestOneCharNoMatch(matcher.negate().precomputed(), s);
262    reallyTestOneCharNoMatch(matcher.precomputed().negate(), s);
263  }
264
265  private void doTestOneCharNoMatch(CharMatcher matcher, String s) {
266    reallyTestOneCharNoMatch(matcher, s);
267    reallyTestOneCharMatch(matcher.negate(), s);
268    reallyTestOneCharNoMatch(matcher.precomputed(), s);
269    reallyTestOneCharMatch(matcher.negate().precomputed(), s);
270    reallyTestOneCharMatch(matcher.precomputed().negate(), s);
271  }
272
273  private void doTestMatchThenNoMatch(CharMatcher matcher, String s) {
274    reallyTestMatchThenNoMatch(matcher, s);
275    reallyTestNoMatchThenMatch(matcher.negate(), s);
276    reallyTestMatchThenNoMatch(matcher.precomputed(), s);
277    reallyTestNoMatchThenMatch(matcher.negate().precomputed(), s);
278    reallyTestNoMatchThenMatch(matcher.precomputed().negate(), s);
279  }
280
281  private void doTestNoMatchThenMatch(CharMatcher matcher, String s) {
282    reallyTestNoMatchThenMatch(matcher, s);
283    reallyTestMatchThenNoMatch(matcher.negate(), s);
284    reallyTestNoMatchThenMatch(matcher.precomputed(), s);
285    reallyTestMatchThenNoMatch(matcher.negate().precomputed(), s);
286    reallyTestMatchThenNoMatch(matcher.precomputed().negate(), s);
287  }
288
289  private void reallyTestOneCharMatch(CharMatcher matcher, String s) {
290    assertTrue(matcher.matches(s.charAt(0)));
291    assertTrue(matcher.apply(s.charAt(0)));
292    assertEquals(0, matcher.indexIn(s));
293    assertEquals(0, matcher.indexIn(s, 0));
294    assertEquals(-1, matcher.indexIn(s, 1));
295    assertEquals(0, matcher.lastIndexIn(s));
296    assertTrue(matcher.matchesAnyOf(s));
297    assertTrue(matcher.matchesAllOf(s));
298    assertFalse(matcher.matchesNoneOf(s));
299    assertEquals("", matcher.removeFrom(s));
300    assertEquals("z", matcher.replaceFrom(s, 'z'));
301    assertEquals("ZZ", matcher.replaceFrom(s, "ZZ"));
302    assertEquals("", matcher.trimFrom(s));
303    assertEquals(1, matcher.countIn(s));
304  }
305
306  private void reallyTestOneCharNoMatch(CharMatcher matcher, String s) {
307    assertFalse(matcher.matches(s.charAt(0)));
308    assertFalse(matcher.apply(s.charAt(0)));
309    assertEquals(-1, matcher.indexIn(s));
310    assertEquals(-1, matcher.indexIn(s, 0));
311    assertEquals(-1, matcher.indexIn(s, 1));
312    assertEquals(-1, matcher.lastIndexIn(s));
313    assertFalse(matcher.matchesAnyOf(s));
314    assertFalse(matcher.matchesAllOf(s));
315    assertTrue(matcher.matchesNoneOf(s));
316
317    assertSame(s, matcher.removeFrom(s));
318    assertSame(s, matcher.replaceFrom(s, 'z'));
319    assertSame(s, matcher.replaceFrom(s, "ZZ"));
320    assertSame(s, matcher.trimFrom(s));
321    assertSame(0, matcher.countIn(s));
322  }
323
324  private void reallyTestMatchThenNoMatch(CharMatcher matcher, String s) {
325    assertEquals(0, matcher.indexIn(s));
326    assertEquals(0, matcher.indexIn(s, 0));
327    assertEquals(-1, matcher.indexIn(s, 1));
328    assertEquals(-1, matcher.indexIn(s, 2));
329    assertEquals(0, matcher.lastIndexIn(s));
330    assertTrue(matcher.matchesAnyOf(s));
331    assertFalse(matcher.matchesAllOf(s));
332    assertFalse(matcher.matchesNoneOf(s));
333    assertEquals(s.substring(1), matcher.removeFrom(s));
334    assertEquals("z" + s.substring(1), matcher.replaceFrom(s, 'z'));
335    assertEquals("ZZ" + s.substring(1), matcher.replaceFrom(s, "ZZ"));
336    assertEquals(s.substring(1), matcher.trimFrom(s));
337    assertEquals(1, matcher.countIn(s));
338  }
339
340  private void reallyTestNoMatchThenMatch(CharMatcher matcher, String s) {
341    assertEquals(1, matcher.indexIn(s));
342    assertEquals(1, matcher.indexIn(s, 0));
343    assertEquals(1, matcher.indexIn(s, 1));
344    assertEquals(-1, matcher.indexIn(s, 2));
345    assertEquals(1, matcher.lastIndexIn(s));
346    assertTrue(matcher.matchesAnyOf(s));
347    assertFalse(matcher.matchesAllOf(s));
348    assertFalse(matcher.matchesNoneOf(s));
349    assertEquals(s.substring(0, 1), matcher.removeFrom(s));
350    assertEquals(s.substring(0, 1) + "z", matcher.replaceFrom(s, 'z'));
351    assertEquals(s.substring(0, 1) + "ZZ", matcher.replaceFrom(s, "ZZ"));
352    assertEquals(s.substring(0, 1), matcher.trimFrom(s));
353    assertEquals(1, matcher.countIn(s));
354  }
355
356  /**
357   * Checks that expected is equals to out, and further, if in is
358   * equals to expected, then out is successfully optimized to be
359   * identical to in, i.e. that "in" is simply returned.
360   */
361  private void assertEqualsSame(String expected, String in, String out) {
362    if (expected.equals(in)) {
363      assertSame(in, out);
364    } else {
365      assertEquals(expected, out);
366    }
367  }
368
369  // Test collapse() a little differently than the rest, as we really want to
370  // cover lots of different configurations of input text
371  public void testCollapse() {
372    // collapsing groups of '-' into '_' or '-'
373    doTestCollapse("-", "_");
374    doTestCollapse("x-", "x_");
375    doTestCollapse("-x", "_x");
376    doTestCollapse("--", "_");
377    doTestCollapse("x--", "x_");
378    doTestCollapse("--x", "_x");
379    doTestCollapse("-x-", "_x_");
380    doTestCollapse("x-x", "x_x");
381    doTestCollapse("---", "_");
382    doTestCollapse("--x-", "_x_");
383    doTestCollapse("--xx", "_xx");
384    doTestCollapse("-x--", "_x_");
385    doTestCollapse("-x-x", "_x_x");
386    doTestCollapse("-xx-", "_xx_");
387    doTestCollapse("x--x", "x_x");
388    doTestCollapse("x-x-", "x_x_");
389    doTestCollapse("x-xx", "x_xx");
390    doTestCollapse("x-x--xx---x----x", "x_x_xx_x_x");
391
392    doTestCollapseWithNoChange("");
393    doTestCollapseWithNoChange("x");
394    doTestCollapseWithNoChange("xx");
395  }
396
397  private void doTestCollapse(String in, String out) {
398    // Try a few different matchers which all match '-' and not 'x'
399    // Try replacement chars that both do and do not change the value.
400    for (char replacement : new char[] { '_', '-' }) {
401      String expected = out.replace('_', replacement);
402      assertEqualsSame(expected, in, is('-').collapseFrom(in, replacement));
403      assertEqualsSame(expected, in, is('-').collapseFrom(in, replacement));
404      assertEqualsSame(expected, in, is('-').or(is('#')).collapseFrom(in, replacement));
405      assertEqualsSame(expected, in, isNot('x').collapseFrom(in, replacement));
406      assertEqualsSame(expected, in, is('x').negate().collapseFrom(in, replacement));
407      assertEqualsSame(expected, in, anyOf("-").collapseFrom(in, replacement));
408      assertEqualsSame(expected, in, anyOf("-#").collapseFrom(in, replacement));
409      assertEqualsSame(expected, in, anyOf("-#123").collapseFrom(in, replacement));
410    }
411  }
412
413  private void doTestCollapseWithNoChange(String inout) {
414    assertSame(inout, is('-').collapseFrom(inout, '_'));
415    assertSame(inout, is('-').or(is('#')).collapseFrom(inout, '_'));
416    assertSame(inout, isNot('x').collapseFrom(inout, '_'));
417    assertSame(inout, is('x').negate().collapseFrom(inout, '_'));
418    assertSame(inout, anyOf("-").collapseFrom(inout, '_'));
419    assertSame(inout, anyOf("-#").collapseFrom(inout, '_'));
420    assertSame(inout, anyOf("-#123").collapseFrom(inout, '_'));
421    assertSame(inout, CharMatcher.NONE.collapseFrom(inout, '_'));
422  }
423
424  public void testCollapse_any() {
425    assertEquals("", CharMatcher.ANY.collapseFrom("", '_'));
426    assertEquals("_", CharMatcher.ANY.collapseFrom("a", '_'));
427    assertEquals("_", CharMatcher.ANY.collapseFrom("ab", '_'));
428    assertEquals("_", CharMatcher.ANY.collapseFrom("abcd", '_'));
429  }
430
431  public void testTrimFrom() {
432    // trimming -
433    doTestTrimFrom("-", "");
434    doTestTrimFrom("x-", "x");
435    doTestTrimFrom("-x", "x");
436    doTestTrimFrom("--", "");
437    doTestTrimFrom("x--", "x");
438    doTestTrimFrom("--x", "x");
439    doTestTrimFrom("-x-", "x");
440    doTestTrimFrom("x-x", "x-x");
441    doTestTrimFrom("---", "");
442    doTestTrimFrom("--x-", "x");
443    doTestTrimFrom("--xx", "xx");
444    doTestTrimFrom("-x--", "x");
445    doTestTrimFrom("-x-x", "x-x");
446    doTestTrimFrom("-xx-", "xx");
447    doTestTrimFrom("x--x", "x--x");
448    doTestTrimFrom("x-x-", "x-x");
449    doTestTrimFrom("x-xx", "x-xx");
450    doTestTrimFrom("x-x--xx---x----x", "x-x--xx---x----x");
451    // additional testing using the doc example
452    assertEquals("cat", anyOf("ab").trimFrom("abacatbab"));
453  }
454
455  private void doTestTrimFrom(String in, String out) {
456    // Try a few different matchers which all match '-' and not 'x'
457    assertEquals(out, is('-').trimFrom(in));
458    assertEquals(out, is('-').or(is('#')).trimFrom(in));
459    assertEquals(out, isNot('x').trimFrom(in));
460    assertEquals(out, is('x').negate().trimFrom(in));
461    assertEquals(out, anyOf("-").trimFrom(in));
462    assertEquals(out, anyOf("-#").trimFrom(in));
463    assertEquals(out, anyOf("-#123").trimFrom(in));
464  }
465
466  public void testTrimLeadingFrom() {
467    // trimming -
468    doTestTrimLeadingFrom("-", "");
469    doTestTrimLeadingFrom("x-", "x-");
470    doTestTrimLeadingFrom("-x", "x");
471    doTestTrimLeadingFrom("--", "");
472    doTestTrimLeadingFrom("x--", "x--");
473    doTestTrimLeadingFrom("--x", "x");
474    doTestTrimLeadingFrom("-x-", "x-");
475    doTestTrimLeadingFrom("x-x", "x-x");
476    doTestTrimLeadingFrom("---", "");
477    doTestTrimLeadingFrom("--x-", "x-");
478    doTestTrimLeadingFrom("--xx", "xx");
479    doTestTrimLeadingFrom("-x--", "x--");
480    doTestTrimLeadingFrom("-x-x", "x-x");
481    doTestTrimLeadingFrom("-xx-", "xx-");
482    doTestTrimLeadingFrom("x--x", "x--x");
483    doTestTrimLeadingFrom("x-x-", "x-x-");
484    doTestTrimLeadingFrom("x-xx", "x-xx");
485    doTestTrimLeadingFrom("x-x--xx---x----x", "x-x--xx---x----x");
486    // additional testing using the doc example
487    assertEquals("catbab", anyOf("ab").trimLeadingFrom("abacatbab"));
488  }
489
490  private void doTestTrimLeadingFrom(String in, String out) {
491    // Try a few different matchers which all match '-' and not 'x'
492    assertEquals(out, is('-').trimLeadingFrom(in));
493    assertEquals(out, is('-').or(is('#')).trimLeadingFrom(in));
494    assertEquals(out, isNot('x').trimLeadingFrom(in));
495    assertEquals(out, is('x').negate().trimLeadingFrom(in));
496    assertEquals(out, anyOf("-#").trimLeadingFrom(in));
497    assertEquals(out, anyOf("-#123").trimLeadingFrom(in));
498  }
499
500  public void testTrimTrailingFrom() {
501    // trimming -
502    doTestTrimTrailingFrom("-", "");
503    doTestTrimTrailingFrom("x-", "x");
504    doTestTrimTrailingFrom("-x", "-x");
505    doTestTrimTrailingFrom("--", "");
506    doTestTrimTrailingFrom("x--", "x");
507    doTestTrimTrailingFrom("--x", "--x");
508    doTestTrimTrailingFrom("-x-", "-x");
509    doTestTrimTrailingFrom("x-x", "x-x");
510    doTestTrimTrailingFrom("---", "");
511    doTestTrimTrailingFrom("--x-", "--x");
512    doTestTrimTrailingFrom("--xx", "--xx");
513    doTestTrimTrailingFrom("-x--", "-x");
514    doTestTrimTrailingFrom("-x-x", "-x-x");
515    doTestTrimTrailingFrom("-xx-", "-xx");
516    doTestTrimTrailingFrom("x--x", "x--x");
517    doTestTrimTrailingFrom("x-x-", "x-x");
518    doTestTrimTrailingFrom("x-xx", "x-xx");
519    doTestTrimTrailingFrom("x-x--xx---x----x", "x-x--xx---x----x");
520    // additional testing using the doc example
521    assertEquals("abacat", anyOf("ab").trimTrailingFrom("abacatbab"));
522  }
523
524  private void doTestTrimTrailingFrom(String in, String out) {
525    // Try a few different matchers which all match '-' and not 'x'
526    assertEquals(out, is('-').trimTrailingFrom(in));
527    assertEquals(out, is('-').or(is('#')).trimTrailingFrom(in));
528    assertEquals(out, isNot('x').trimTrailingFrom(in));
529    assertEquals(out, is('x').negate().trimTrailingFrom(in));
530    assertEquals(out, anyOf("-#").trimTrailingFrom(in));
531    assertEquals(out, anyOf("-#123").trimTrailingFrom(in));
532  }
533
534  public void testTrimAndCollapse() {
535    // collapsing groups of '-' into '_' or '-'
536    doTestTrimAndCollapse("", "");
537    doTestTrimAndCollapse("x", "x");
538    doTestTrimAndCollapse("-", "");
539    doTestTrimAndCollapse("x-", "x");
540    doTestTrimAndCollapse("-x", "x");
541    doTestTrimAndCollapse("--", "");
542    doTestTrimAndCollapse("x--", "x");
543    doTestTrimAndCollapse("--x", "x");
544    doTestTrimAndCollapse("-x-", "x");
545    doTestTrimAndCollapse("x-x", "x_x");
546    doTestTrimAndCollapse("---", "");
547    doTestTrimAndCollapse("--x-", "x");
548    doTestTrimAndCollapse("--xx", "xx");
549    doTestTrimAndCollapse("-x--", "x");
550    doTestTrimAndCollapse("-x-x", "x_x");
551    doTestTrimAndCollapse("-xx-", "xx");
552    doTestTrimAndCollapse("x--x", "x_x");
553    doTestTrimAndCollapse("x-x-", "x_x");
554    doTestTrimAndCollapse("x-xx", "x_xx");
555    doTestTrimAndCollapse("x-x--xx---x----x", "x_x_xx_x_x");
556  }
557
558  private void doTestTrimAndCollapse(String in, String out) {
559    // Try a few different matchers which all match '-' and not 'x'
560    for (char replacement : new char[] { '_', '-' }) {
561      String expected = out.replace('_', replacement);
562      assertEqualsSame(expected, in, is('-').trimAndCollapseFrom(in, replacement));
563      assertEqualsSame(expected, in, is('-').or(is('#')).trimAndCollapseFrom(in, replacement));
564      assertEqualsSame(expected, in, isNot('x').trimAndCollapseFrom(in, replacement));
565      assertEqualsSame(expected, in, is('x').negate().trimAndCollapseFrom(in, replacement));
566      assertEqualsSame(expected, in, anyOf("-").trimAndCollapseFrom(in, replacement));
567      assertEqualsSame(expected, in, anyOf("-#").trimAndCollapseFrom(in, replacement));
568      assertEqualsSame(expected, in, anyOf("-#123").trimAndCollapseFrom(in, replacement));
569    }
570  }
571
572  public void testReplaceFrom() {
573    assertEquals("yoho", is('a').replaceFrom("yaha", 'o'));
574    assertEquals("yh", is('a').replaceFrom("yaha", ""));
575    assertEquals("yoho", is('a').replaceFrom("yaha", "o"));
576    assertEquals("yoohoo", is('a').replaceFrom("yaha", "oo"));
577    assertEquals("12 &gt; 5", is('>').replaceFrom("12 > 5", "&gt;"));
578  }
579
580  public void testPrecomputedOptimizations() {
581    // These are testing behavior that's never promised by the API.
582    // Some matchers are so efficient that it is a waste of effort to
583    // build a precomputed version.
584    CharMatcher m1 = is('x');
585    assertSame(m1, m1.precomputed());
586    assertSame(m1.toString(), m1.precomputed().toString());
587
588    CharMatcher m2 = anyOf("Az");
589    assertSame(m2, m2.precomputed());
590    assertSame(m2.toString(), m2.precomputed().toString());
591
592    CharMatcher m3 = inRange('A', 'Z');
593    assertSame(m3, m3.precomputed());
594    assertSame(m3.toString(), m3.precomputed().toString());
595
596    assertSame(CharMatcher.NONE, CharMatcher.NONE.precomputed());
597    assertSame(CharMatcher.ANY, CharMatcher.ANY.precomputed());
598  }
599
600  static void checkExactMatches(CharMatcher m, char[] chars) {
601    Set<Character> positive = Sets.newHashSetWithExpectedSize(chars.length);
602    for (int i = 0; i < chars.length; i++) {
603      positive.add(chars[i]);
604    }
605    for (int c = 0; c <= Character.MAX_VALUE; c++) {
606      assertFalse(positive.contains(new Character((char) c)) ^ m.matches((char) c));
607    }
608  }
609
610  static char[] randomChars(Random rand, int size) {
611    Set<Character> chars = new HashSet<Character>(size);
612    for (int i = 0; i < size; i++) {
613      char c;
614      while (true) {
615        c = (char) rand.nextInt(Character.MAX_VALUE - Character.MIN_VALUE + 1);
616        if (!chars.contains(c)) {
617          break;
618        }
619      }
620      chars.add(c);
621    }
622    char[] retValue = new char[chars.size()];
623    int i = 0;
624    for (char c : chars) {
625      retValue[i++] = c;
626    }
627    Arrays.sort(retValue);
628    return retValue;
629  }
630
631  public void testToString() {
632    assertToStringWorks("CharMatcher.NONE", CharMatcher.anyOf(""));
633    assertToStringWorks("CharMatcher.is('\\u0031')", CharMatcher.anyOf("1"));
634    assertToStringWorks("CharMatcher.isNot('\\u0031')", CharMatcher.isNot('1'));
635    assertToStringWorks("CharMatcher.anyOf(\"\\u0031\\u0032\")", CharMatcher.anyOf("12"));
636    assertToStringWorks("CharMatcher.anyOf(\"\\u0031\\u0032\\u0033\")",
637        CharMatcher.anyOf("321"));
638    assertToStringWorks("CharMatcher.inRange('\\u0031', '\\u0033')",
639        CharMatcher.inRange('1', '3'));
640  }
641
642  private static void assertToStringWorks(String expected, CharMatcher matcher) {
643    assertEquals(expected, matcher.toString());
644    assertEquals(expected, matcher.precomputed().toString());
645    assertEquals(expected, matcher.negate().negate().toString());
646    assertEquals(expected, matcher.negate().precomputed().negate().toString());
647    assertEquals(expected, matcher.negate().precomputed().negate().precomputed().toString());
648  }
649}
650
651