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