001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.functor.range;
018
019import static org.junit.Assert.assertEquals;
020import static org.junit.Assert.assertFalse;
021import static org.junit.Assert.assertTrue;
022import static org.junit.Assert.fail;
023
024import java.util.ArrayList;
025import java.util.Arrays;
026import java.util.Collection;
027import java.util.Collections;
028import java.util.List;
029
030import org.apache.commons.functor.BaseFunctorTest;
031import org.apache.commons.functor.Function;
032import org.apache.commons.functor.generator.Generator;
033import org.apache.commons.functor.generator.loop.IteratorToGeneratorAdapter;
034import org.junit.After;
035import org.junit.Before;
036import org.junit.Test;
037
038/**
039 * @version $Revision: $ $Date: $
040 */
041public class TestCharacterRange extends BaseFunctorTest {
042    // A base range with all chars between a and m
043    private final List<Character> fullRange = Collections.unmodifiableList(Arrays.asList('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm'));
044
045    // Attributes
046    // ------------------------------------------------------------------------
047
048    private CharacterRange ascCharRange = null;
049    private CharacterRange descCharRange = null;
050    private Collection<Character> expectedAsc = null;
051    private Collection<Character> expectedDesc = null;
052    
053    // Test set up
054    // ------------------------------------------------------------------------
055    @Before
056    public void setUp() {
057        ascCharRange = Ranges.characterRange('b', 'k');
058        descCharRange = Ranges.characterRange('k', 'b');
059        expectedAsc = Arrays.asList('b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k');
060        expectedDesc = Arrays.asList('k', 'j', 'i', 'h', 'g', 'f', 'e', 'd', 'c', 'b');
061    }
062    
063    @After
064    public void tearDown() {
065        ascCharRange = null;
066        descCharRange = null;
067    }
068    
069    @Override
070    protected Object makeFunctor()
071        throws Exception {
072        return Ranges.characterRange('b', 'k');
073    }
074
075    // Generator tests
076    // ---------------------------------------------------------------
077
078    @Test
079    public void testStepChecking() {
080        {
081            Ranges.characterRange('b', 'b', 0); // step of 0 is ok when range is
082                                             // empty
083        }
084        {
085            Ranges.characterRange('b', 'b', 1); // positive step is ok when range
086                                             // is empty
087        }
088        {
089            Ranges.characterRange('b', 'b', -1); // negative step is ok when range
090                                              // is empty
091        }
092        {
093            Ranges.characterRange('a', 'b', 10); // big steps are ok
094        }
095        {
096            Ranges.characterRange('b', 'a', -10); // big steps are ok
097        }
098        try {
099            Ranges.characterRange('a', 'b', 0);
100            fail("Expected IllegalArgumentException");
101        } catch (IllegalArgumentException e) {
102            // expected
103        }
104        try {
105            Ranges.characterRange('a', 'b', -1);
106            fail("Expected IllegalArgumentException");
107        } catch (IllegalArgumentException e) {
108            // expected
109        }
110        try {
111            Ranges.characterRange('b', 'a', 1);
112            fail("Expected IllegalArgumentException");
113        } catch (IllegalArgumentException e) {
114            // expected
115        }
116    }
117
118    @Test
119    public void testObjectConstructor() {
120        CharacterRange range = Ranges.characterRange(Character.valueOf('a'),
121                                                     Character.valueOf('e'));
122        assertEquals("[a, b, c, d, e]", IteratorToGeneratorAdapter.adapt(range).toCollection().toString());
123        range = Ranges.characterRange(Character.valueOf('a'), Character.valueOf('e'),
124                                      Integer.valueOf(1));
125        assertEquals("[a, b, c, d, e]", IteratorToGeneratorAdapter.adapt(range).toCollection().toString());
126    }
127
128    @Test
129    public void testReverseStep() {
130        CharacterRange range = Ranges.characterRange('k', 'a', -2);
131        assertEquals("[k, i, g, e, c, a]", IteratorToGeneratorAdapter.adapt(range).toCollection().toString());
132    }
133
134    @Test
135    public void testStep() {
136        CharacterRange range = Ranges.characterRange('a', 'k', 2);
137        assertEquals("[a, c, e, g, i, k]", IteratorToGeneratorAdapter.adapt(range).toCollection().toString());
138    }
139
140    @Test
141    public void testForwardRange() {
142        CharacterRange range = Ranges.characterRange('a', 'e');
143        assertEquals("[a, b, c, d, e]", IteratorToGeneratorAdapter.adapt(range).toCollection().toString());
144    }
145
146    @Test
147    public void testReverseRange() {
148        CharacterRange range = Ranges.characterRange('e', 'a');
149        assertEquals("[e, d, c, b, a]", IteratorToGeneratorAdapter.adapt(range).toCollection().toString());
150    }
151
152    // @Test
153    // public void testEdgeCase() {
154    // CharacterRange range = Ranges.characterRange(Character.MAX_VALUE-3,
155    // Character.MAX_VALUE);
156    // assertEquals("[2147483644, 2147483645, 2147483646]",
157    // IteratorToGeneratorAdapter.adapt(range).toCollection().toString());
158    // assertEquals("[2147483644, 2147483645, 2147483646]",
159    // IteratorToGeneratorAdapter.adapt(range).toCollection().toString());
160    // }
161
162    @Test
163    public void testBoundaries() {
164        CharacterRange range = Ranges.characterRange('b', 'l');
165        assertEquals(new Endpoint<Comparable<?>>('b', BoundType.CLOSED),
166                     range.getLeftEndpoint());
167        assertEquals(new Endpoint<Comparable<?>>('l', BoundType.CLOSED),
168                     range.getRightEndpoint());
169    }
170
171    @Test
172    public void testClosedClosedAscending() {
173        // [b, l], 3 = b, e, h, k
174        CharacterRange range =  Ranges.characterRange('b', BoundType.CLOSED, 'l', BoundType.CLOSED, 3);
175        // [b, l], 3 = b, e, h, k
176        List<Character> expected = Arrays.asList('b', 'e', 'h', 'k');
177        Collection<Character> elements = IteratorToGeneratorAdapter.adapt(range).toCollection();
178        assertEquals(expected, elements);
179    }
180
181    @Test
182    public void testOpenClosedAscending() {
183        // (b, l], 3 = e, h, k
184        CharacterRange range =  Ranges.characterRange('b', BoundType.OPEN, 'l', BoundType.CLOSED, 3);
185        // (b, l], 3 = e, h, k
186        List<Character> expected = Arrays.asList('e', 'h', 'k');
187        Collection<Character> elements = IteratorToGeneratorAdapter.adapt(range).toCollection();
188        assertEquals(expected, elements);
189    }
190
191    @Test
192    public void testClosedOpenAscending() {
193        // [b, l), 3 = b, e, h, k
194        CharacterRange range =  Ranges.characterRange('b', BoundType.CLOSED, 'l', BoundType.OPEN, 3);
195        // [b, l), 3 = b, e, h, k
196        List<Character> expected = Arrays.asList('b', 'e', 'h', 'k');
197        Collection<Character> elements = IteratorToGeneratorAdapter.adapt(range).toCollection();
198        assertEquals(expected, elements);
199    }
200
201    @Test
202    public void testOpenOpenAscending() {
203        // (b, l), 3 = e, h, k
204        CharacterRange range =  Ranges.characterRange('b', BoundType.OPEN, 'l', BoundType.OPEN, 3);
205        // (b, l), 3 = e, h, k
206        List<Character> expected = Arrays.asList('e', 'h', 'k');
207        Collection<Character> elements = IteratorToGeneratorAdapter.adapt(range).toCollection();
208        assertEquals(expected, elements);
209    }
210
211    @Test
212    public void testSingleStepAscending() {
213        // (d, h], 1 = e, f, g, h
214        CharacterRange range =  Ranges.characterRange('d', BoundType.OPEN, 'h', BoundType.CLOSED, 1);
215        // (d, h], 1 = e, f, g, h
216        List<Character> expected = Arrays.asList('e', 'f', 'g', 'h');
217        Collection<Character> elements = IteratorToGeneratorAdapter.adapt(range).toCollection();
218        assertEquals(expected, elements);
219    }
220
221    @Test
222    public void testClosedClosedDescending() {
223        // [l, b], -3 = l, i, f, c
224        CharacterRange range = Ranges.characterRange('l', BoundType.CLOSED, 'b',
225                                                  BoundType.CLOSED, -3);
226        // [l, b], -3 = l, i, f, c
227        List<Character> expected = Arrays.asList('l', 'i', 'f', 'c');
228        Collection<Character> elements = IteratorToGeneratorAdapter.adapt(range).toCollection();
229        assertEquals(expected, elements);
230    }
231
232    @Test
233    public void testOpenClosedDescending() {
234        // (l, b], -3 = i, f, c
235        CharacterRange range = Ranges.characterRange('l', BoundType.OPEN, 'b',
236                                                  BoundType.CLOSED, -3);
237        // (l, b], -3 = i, f, c
238        List<Character> expected = Arrays.asList('i', 'f', 'c');
239        Collection<Character> elements = IteratorToGeneratorAdapter.adapt(range).toCollection();
240        assertEquals(expected, elements);
241    }
242
243    @Test
244    public void testClosedOpenDescending() {
245        // [l, b), -3 = l, i, f, c
246        CharacterRange range = Ranges.characterRange('l', BoundType.CLOSED, 'b',
247                                                  BoundType.OPEN, -3);
248        // [l, b), -3 = l, i, f, c
249        List<Character> expected = Arrays.asList('l', 'i', 'f', 'c');
250        Collection<Character> elements = IteratorToGeneratorAdapter.adapt(range).toCollection();
251        assertEquals(expected, elements);
252    }
253
254    @Test
255    public void testOpenOpenDescending() {
256        // (l, b), -3 = i, f, c
257        CharacterRange range = Ranges.characterRange('l', BoundType.OPEN, 'b',
258                                                  BoundType.OPEN, -3);
259        // (l, b), -3 = i, f, c
260        List<Character> expected = Arrays.asList('i', 'f', 'c');
261        Collection<Character> elements = IteratorToGeneratorAdapter.adapt(range).toCollection();
262        assertEquals(expected, elements);
263    }
264
265    @Test
266    public void testSingleStepDescending() {
267        // [h, d), -1 = h, g, f
268        CharacterRange range = Ranges.characterRange('h', BoundType.CLOSED, 'e',
269                                                  BoundType.OPEN, -1);
270        // [h, d), -1 = h, g, f
271        List<Character> expected = Arrays.asList('h', 'g', 'f');
272        Collection<Character> elements = IteratorToGeneratorAdapter.adapt(range).toCollection();
273        assertEquals(expected, elements);
274    }
275
276    @Test
277    public void testAscending() {
278        final List<Character> list = new ArrayList<Character>();
279        for (char c : ascCharRange) {
280            list.add(c);
281        }
282        assertTrue(expectedAsc.containsAll(list));
283    }
284
285    @Test
286    public void testDescending() {
287        final List<Character> list = new ArrayList<Character>();
288        for (char c : ascCharRange) {
289            list.add(c);
290        }
291        assertTrue(expectedDesc.containsAll(list));
292    }
293
294    @Test
295    public void testToCollection() {
296        Collection<Character> ascCol = IteratorToGeneratorAdapter.adapt(ascCharRange).toCollection();
297        assertEquals("Different collections", expectedAsc, ascCol);
298        Collection<Character> descCol = IteratorToGeneratorAdapter.adapt(descCharRange).toCollection();
299        assertEquals("Different collections", expectedDesc, descCol);
300    }
301
302    @Test
303    public void testTransformedGenerator() {
304        int expected = 10;
305        List<Character> list = IteratorToGeneratorAdapter.adapt(ascCharRange)
306            .to(new Function<Generator<? extends Character>, List<Character>>() {
307
308                public List<Character> evaluate(Generator<? extends Character> obj) {
309                    List<Character> chars = new ArrayList<Character>();
310                    for (Object element : obj.toCollection()) {
311                        chars.add((Character) element);
312                    }
313                    return chars;
314                }
315            });
316        assertEquals(expected, list.size());
317        expected = 10;
318        list = IteratorToGeneratorAdapter.adapt(descCharRange)
319            .to(new Function<Generator<? extends Character>, List<Character>>() {
320
321                public List<Character> evaluate(Generator<? extends Character> obj) {
322                    List<Character> chars = new ArrayList<Character>();
323                    for (Object element : obj.toCollection()) {
324                        chars.add((Character) element);
325                    }
326                    return chars;
327                }
328            });
329        assertEquals(expected, list.size());
330    }
331
332    // Range tests
333    // ---------------------------------------------------------------
334
335    @Test
336    public void testEmptyRanges() {
337        CharacterRange empty1 = Ranges.characterRange('a', BoundType.OPEN, 'b',
338                                                   BoundType.OPEN, 2);
339        assertTrue("The range was expected to be empty.", empty1.isEmpty());
340        CharacterRange empty2 = Ranges.characterRange('c', BoundType.OPEN, 'a',
341                                                   BoundType.OPEN, -2);
342        assertTrue("The range was expected to be empty.", empty2.isEmpty());
343        CharacterRange empty3 = Ranges.characterRange('a', BoundType.OPEN, 'b',
344                                                   BoundType.CLOSED, 2);
345        assertTrue("The range was expected to be empty.", empty3.isEmpty());
346        CharacterRange empty4 = Ranges.characterRange('a', BoundType.OPEN, 'a',
347                                                   BoundType.OPEN, 1);
348        assertTrue("The range was expected to be empty.", empty4.isEmpty());
349        CharacterRange empty5 = Ranges.characterRange('b', BoundType.CLOSED, 'b',
350                                                   BoundType.OPEN, 1);
351        assertTrue("The range was expected to be empty.", empty5.isEmpty());
352        CharacterRange empty6 = Ranges.characterRange('d', BoundType.OPEN, 'c',
353                                                   BoundType.CLOSED, -2);
354        assertTrue("The range was expected to be empty.", empty6.isEmpty());
355        CharacterRange notEmpty1 = Ranges.characterRange('a', BoundType.CLOSED,
356                                                      'a', BoundType.CLOSED, 1);
357        assertFalse("The range was not expected to be empty.",
358                    notEmpty1.isEmpty());
359        CharacterRange notEmpty2 = Ranges.characterRange('a', BoundType.OPEN, 'b',
360                                                      BoundType.CLOSED, 1);
361        assertFalse("The range was not expected to be empty.",
362                    notEmpty2.isEmpty());
363        CharacterRange notEmpty3 = Ranges.characterRange('b', BoundType.OPEN, 'a',
364                                                      BoundType.CLOSED, -1);
365        assertFalse("The range was not expected to be empty.",
366                    notEmpty3.isEmpty());
367        CharacterRange notEmpty4 = Ranges.characterRange('b', BoundType.CLOSED,
368                                                      'a', BoundType.OPEN, -1);
369        assertFalse("The range was not expected to be empty.",
370                    notEmpty4.isEmpty());
371        CharacterRange notEmpty5 = Ranges.characterRange('a', BoundType.CLOSED,
372                                                      'b', BoundType.OPEN, 1);
373        assertFalse("The range was not expected to be empty.",
374                    notEmpty5.isEmpty());
375    }
376
377    @Test
378    public void testClosedClosedAscendingContains() {
379        // [b, l], 3 = 'b', 'e', 'h', 'k'
380        CharacterRange range = Ranges.characterRange('b', BoundType.CLOSED, 'l',
381                                                  BoundType.CLOSED, 3);
382        // [b, l], 3 = 'b', 'e', 'h', 'k'
383        List<Character> arr = Arrays.asList('b', 'e', 'h', 'k');
384        for (Character element : arr) {
385            assertTrue("Expected element [" + element +
386                               "] is missing in range [" + range + "]",
387                       range.contains(element));
388        }
389        List<Character> elementsNotPresent = new ArrayList<Character>(fullRange);
390        elementsNotPresent.removeAll(arr);
391        for (Character element : elementsNotPresent) {
392            assertFalse("Unexpected element [" + element +
393                                "] is present in range [" + range + "]",
394                        range.contains(element));
395        }
396    }
397
398    @Test
399    public void testOpenClosedAscendingContains() {
400        // (b, l], 3 = 'e', 'h', 'k'
401        CharacterRange range = Ranges.characterRange('b', BoundType.OPEN, 'l',
402                                                  BoundType.CLOSED, 3);
403        // (b, l], 3 = 'e', 'h', 'k'
404        List<Character> arr = Arrays.asList('e', 'h', 'k');
405        for (Character element : arr) {
406            assertTrue("Expected element [" + element +
407                               "] is missing in range [" + range + "]",
408                       range.contains(element));
409        }
410        List<Character> elementsNotPresent = new ArrayList<Character>(fullRange);
411        elementsNotPresent.removeAll(arr);
412        for (Character element : elementsNotPresent) {
413            assertFalse("Unexpected element [" + element +
414                                "] is present in range [" + range + "]",
415                        range.contains(element));
416        }
417    }
418
419    @Test
420    public void testClosedOpenAscendingContains() {
421        // [b, l), 3 = 'b', 'e', 'h', 'k'
422        CharacterRange range = Ranges.characterRange('b', BoundType.CLOSED, 'l',
423                                                  BoundType.OPEN, 3);
424        // [b, l), 3 = 'b', 'e', 'h', 'k'
425        List<Character> arr = Arrays.asList('b', 'e', 'h', 'k');
426        for (Character element : arr) {
427            assertTrue("Expected element [" + element +
428                               "] is missing in range [" + range + "]",
429                       range.contains(element));
430        }
431        List<Character> elementsNotPresent = new ArrayList<Character>(fullRange);
432        elementsNotPresent.removeAll(arr);
433        for (Character element : elementsNotPresent) {
434            assertFalse("Unexpected element [" + element +
435                                "] is present in range [" + range + "]",
436                        range.contains(element));
437        }
438    }
439
440    @Test
441    public void testOpenOpenAscendingContains() {
442        // (b, l), 3 = 'e', 'h', 'k'
443        CharacterRange range = Ranges.characterRange('b', BoundType.OPEN, 'l',
444                                                  BoundType.OPEN, 3);
445        // (b, l), 3 = 'e', 'h', 'k'
446        List<Character> arr = Arrays.asList('e', 'h', 'k');
447        for (Character element : arr) {
448            assertTrue("Expected element [" + element +
449                               "] is missing in range [" + range + "]",
450                       range.contains(element));
451        }
452        List<Character> elementsNotPresent = new ArrayList<Character>(fullRange);
453        elementsNotPresent.removeAll(arr);
454        for (Character element : elementsNotPresent) {
455            assertFalse("Unexpected element [" + element +
456                                "] is present in range [" + range + "]",
457                        range.contains(element));
458        }
459    }
460
461    @Test
462    public void testContainsSingleStepAscending() {
463        // (b, l], 1 = 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l'
464        CharacterRange ascendingRange = Ranges.characterRange('b', BoundType.OPEN,
465                                                           'l',
466                                                           BoundType.CLOSED, 1);
467        // (b, l], 1 = 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l'
468        List<Character> arr = Arrays.asList('c', 'd', 'e', 'f', 'g', 'h', 'i',
469                                            'j', 'k', 'l');
470        for (Character element : arr) {
471            assertTrue("Expected element [" + element +
472                               "] is missing in range [" + ascendingRange + "]",
473                       ascendingRange.contains(element));
474        }
475        List<Character> elementsNotPresent = new ArrayList<Character>(fullRange);
476        elementsNotPresent.removeAll(arr);
477        for (Character element : elementsNotPresent) {
478            assertFalse("Unexpected element [" + element +
479                                "] is present in range [" + ascendingRange +
480                                "]",
481                        ascendingRange.contains(element));
482        }
483    }
484
485    @Test
486    public void testClosedClosedDescendingContains() {
487        // [l, b], -3 = 'l', 'i', 'f', 'c'
488        CharacterRange range = Ranges.characterRange('l', BoundType.CLOSED, 'b',
489                                                  BoundType.CLOSED, -3);
490        // [l, b], -3 = 'l', 'i', 'f', 'c'
491        List<Character> arr = Arrays.asList('l', 'i', 'f', 'c');
492        for (Character element : arr) {
493            assertTrue("Expected element [" + element +
494                               "] is missing in range [" + range + "]",
495                       range.contains(element));
496        }
497        List<Character> elementsNotPresent = new ArrayList<Character>(fullRange);
498        elementsNotPresent.removeAll(arr);
499        for (Character element : elementsNotPresent) {
500            assertFalse("Unexpected element [" + element +
501                                "] is present in range [" + range + "]",
502                        range.contains(element));
503        }
504    }
505
506    @Test
507    public void testOpenClosedDescendingContains() {
508        // (l, b], -3 = 'i', 'f', 'c'
509        CharacterRange range = Ranges.characterRange('l', BoundType.OPEN, 'b',
510                                                  BoundType.CLOSED, -3);
511        // (l, b], -3 = 'i', 'f', 'c'
512        List<Character> arr = Arrays.asList('i', 'f', 'c');
513        for (Character element : arr) {
514            assertTrue("Expected element [" + element +
515                               "] is missing in range [" + range + "]",
516                       range.contains(element));
517        }
518        List<Character> elementsNotPresent = new ArrayList<Character>(fullRange);
519        elementsNotPresent.removeAll(arr);
520        for (Character element : elementsNotPresent) {
521            assertFalse("Unexpected element [" + element +
522                                "] is present in range [" + range + "]",
523                        range.contains(element));
524        }
525    }
526
527    @Test
528    public void testClosedOpenDescendingContains() {
529        // [l, b), -3 = 'l', 'i', 'f', 'c'
530        CharacterRange range = Ranges.characterRange('l', BoundType.CLOSED, 'b',
531                                                  BoundType.OPEN, -3);
532        // [l, b), -3 = 'l', 'i', 'f', 'c'
533        List<Character> arr = Arrays.asList('l', 'i', 'f', 'c');
534        for (Character element : arr) {
535            assertTrue("Expected element [" + element +
536                               "] is missing in range [" + range + "]",
537                       range.contains(element));
538        }
539        List<Character> elementsNotPresent = new ArrayList<Character>(fullRange);
540        elementsNotPresent.removeAll(arr);
541        for (Character element : elementsNotPresent) {
542            assertFalse("Unexpected element [" + element +
543                                "] is present in range [" + range + "]",
544                        range.contains(element));
545        }
546    }
547
548    @Test
549    public void testOpenOpenDescendingContains() {
550        // (l, b), -3 = 'i', 'f', 'c'
551        CharacterRange range = Ranges.characterRange('l', BoundType.OPEN, 'b',
552                                                  BoundType.OPEN, -3);
553        // (l, b), -3 = 'i', 'f', 'c'
554        List<Character> arr = Arrays.asList('i', 'f', 'c');
555        for (Character element : arr) {
556            assertTrue("Expected element [" + element +
557                               "] is missing in range [" + range + "]",
558                       range.contains(element));
559        }
560        List<Character> elementsNotPresent = new ArrayList<Character>(fullRange);
561        elementsNotPresent.removeAll(arr);
562        for (Character element : elementsNotPresent) {
563            assertFalse("Unexpected element [" + element +
564                                "] is present in range [" + range + "]",
565                        range.contains(element));
566        }
567    }
568
569    @Test
570    public void testContainsSingleStepDescending() {
571        // [l, b), -1 = 'l', 'k', 'j', 'i', 'h', 'g', 'f', 'e', 'd', 'c'
572        CharacterRange descendingRange = Ranges.characterRange('l',
573                                                            BoundType.CLOSED,
574                                                            'b',
575                                                            BoundType.OPEN, -1);
576        // [l, b), -1 = 'l', 'k', 'j', 'i', 'h', 'g', 'f', 'e', 'd', 'c'
577        List<Character> arr = Arrays.asList('l', 'k', 'j', 'i', 'h', 'g', 'f',
578                                            'e', 'd', 'c');
579        for (Character element : arr) {
580            assertTrue("Expected element [" + element +
581                               "] is missing in range [" + descendingRange +
582                               "]",
583                       descendingRange.contains(element));
584        }
585        List<Character> elementsNotPresent = new ArrayList<Character>(fullRange);
586        elementsNotPresent.removeAll(arr);
587        for (Character element : elementsNotPresent) {
588            assertFalse("Unexpected element [" + element +
589                                "] is present in range [" + descendingRange +
590                                "]",
591                        descendingRange.contains(element));
592        }
593    }
594
595    @Test
596    public void testContainsNullOrEmpty() {
597        CharacterRange range = Ranges.characterRange('a', BoundType.OPEN, 'r',
598                                                  BoundType.CLOSED, 1);
599        assertFalse(range.contains(null));
600    }
601
602    @SuppressWarnings("unchecked")
603    @Test
604    public void testContainsAll() {
605        // (c, g], 1 = d, e, f, g
606        CharacterRange range = Ranges.characterRange('c', BoundType.OPEN, 'g',
607                                                  BoundType.CLOSED, 1);
608        List<Character> list = Arrays.asList('d', 'e', 'f', 'g');
609        assertTrue("Range [" + range +
610                   "] was expected to contain all elements from list [" + list +
611                   "]", range.containsAll(list));
612        List<Character> listWithExtraElements = Arrays.asList('a', 'd', 'e',
613                                                              'f', 'g', 'z');
614        assertFalse("Range [" + range + "] has more elements than expected",
615                    range.containsAll(listWithExtraElements));
616        assertFalse(range.containsAll(null));
617        assertFalse(range.containsAll(Collections.EMPTY_LIST));
618    }
619
620    @Test
621    public void testEquals()
622        throws Exception {
623        // equals basic properties
624        CharacterRange range = Ranges.characterRange('a', BoundType.CLOSED, 'e',
625                                                  BoundType.OPEN, 1);
626        assertEquals("equals must be reflexive", range, range);
627        assertEquals("hashCode must be reflexive", range.hashCode(),
628                     range.hashCode());
629        assertTrue(!range.equals(null)); // should be able to compare to null
630
631        Object range2 = Ranges.characterRange('a', BoundType.CLOSED, 'e',
632                                           BoundType.OPEN, 1);
633        if (range.equals(range2)) {
634            assertEquals("equals implies hash equals", range.hashCode(),
635                         range2.hashCode());
636            assertEquals("equals must be symmetric", range2, range);
637        } else {
638            assertTrue("equals must be symmetric", !range2.equals(range));
639        }
640
641        // Changing attributes
642        Object range3 = Ranges.characterRange('c', BoundType.CLOSED, 'e',
643                                           BoundType.OPEN, 1);
644        assertFalse("Invalid equals after changing attributes",
645                    range.equals(range3));
646
647        Object range4 = Ranges.characterRange('a', BoundType.OPEN, 'e',
648                                           BoundType.OPEN, 1);
649        assertFalse("Invalid equals after changing attributes",
650                    range.equals(range4));
651
652        Object range5 = Ranges.characterRange('a', BoundType.CLOSED, 'r',
653                                           BoundType.OPEN, 1);
654        assertFalse("Invalid equals after changing attributes",
655                    range.equals(range5));
656
657        Object range6 = Ranges.characterRange('a', BoundType.CLOSED, 'e',
658                                           BoundType.CLOSED, 1);
659        assertFalse("Invalid equals after changing attributes",
660                    range.equals(range6));
661
662        Object range7 = Ranges.characterRange('a', BoundType.CLOSED, 'e',
663                                           BoundType.OPEN, 2);
664        assertFalse("Invalid equals after changing attributes",
665                    range.equals(range7));
666
667        // Using different constructors
668        Endpoint<Character> leftEndpoint = new Endpoint<Character>(
669                                                                   'a',
670                                                                   BoundType.CLOSED);
671        Endpoint<Character> rightEndpoint = new Endpoint<Character>(
672                                                                    'e',
673                                                                    BoundType.OPEN);
674        CharacterRange range8 = Ranges.characterRange(leftEndpoint, rightEndpoint,
675                                                   1);
676        assertEquals("Invalid equals using different constructor", range,
677                     range8);
678    }
679
680    @Test
681    public void testToString() {
682        CharacterRange range = Ranges.characterRange('a', BoundType.OPEN, 'b',
683                                                  BoundType.CLOSED, 1);
684        assertEquals("Wrong string value", "CharacterRange<(a, b], 1>",
685                     range.toString());
686    }
687
688    @Test
689    public void testConstructorUsingSameEndpoint() {
690        Endpoint<Character> uniqueEndpoint = new Endpoint<Character>(
691                                                                     'a',
692                                                                     BoundType.CLOSED);
693        try {
694            Ranges.characterRange(uniqueEndpoint, uniqueEndpoint, 1);
695        } catch (IllegalArgumentException e) {
696            fail("Not expected to get here");
697        }
698    }
699
700    @Test
701    public void testInvalidRange() {
702        try {
703            Ranges.characterRange('a', BoundType.OPEN, 'z', BoundType.CLOSED, -100);
704            fail("Not expected to get here");
705        } catch (IllegalArgumentException e) {
706            // Do nothing
707        }
708        Endpoint<Character> leftEndpoint = new Endpoint<Character>(
709                                                                   'a',
710                                                                   BoundType.CLOSED);
711        Endpoint<Character> rightEndpoint = new Endpoint<Character>(
712                                                                    'z',
713                                                                    BoundType.OPEN);
714        try {
715            Ranges.characterRange(leftEndpoint, rightEndpoint, -100);
716            fail("Not expected to get here");
717        } catch (IllegalArgumentException e) {
718            // Do nothing
719        }
720    }
721
722    @Test
723    public void testDefaultStep() {
724        assertEquals("Invalid default step", Integer.valueOf(-1),
725                     CharacterRange.DEFAULT_STEP.evaluate('c', 'a'));
726        assertEquals("Invalid default step", Integer.valueOf(1),
727                     CharacterRange.DEFAULT_STEP.evaluate('a', 'c'));
728    }
729
730}