1   /**
2    * Copyright 2008 Atlassian Pty Ltd 
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  
17  package com.atlassian.util.concurrent;
18  
19  import static org.junit.Assert.assertEquals;
20  import static org.junit.Assert.assertNotNull;
21  import static org.junit.Assert.assertTrue;
22  import static org.junit.Assert.fail;
23  
24  import org.junit.Test;
25  
26  import java.io.Serializable;
27  import java.util.Collection;
28  import java.util.Collections;
29  import java.util.Comparator;
30  import java.util.Iterator;
31  import java.util.Map;
32  import java.util.SortedMap;
33  
34  public class CopyOnWriteSortedMapTest {
35  
36      private static final class ReverseComparator<T> implements Comparator<T> {
37          private final Comparator<T> comparator;
38  
39          ReverseComparator(final Comparator<T> comparator) {
40              this.comparator = comparator;
41          }
42  
43          public int compare(final T o1, final T o2) {
44              return 0 - comparator.compare(o1, o2);
45          };
46      }
47  
48      @SuppressWarnings("serial")
49      private static final class StringComparator implements Comparator<String>, Serializable {
50          public int compare(final String o1, final String o2) {
51              return o1.compareTo(o2);
52          };
53      }
54  
55      @Test
56      public void mapConstructor() {
57          final MapBuilder<String, String> builder = MapBuilder.builder();
58          builder.add("one", "value").add("two", "value").add("three", "value");
59          final SortedMap<String, String> map = CopyOnWriteSortedMap.<String, String> builder().addAll(builder.toMap()).newTreeMap();
60  
61          assertEquals(3, map.size());
62      }
63  
64      @Test
65      public void mapComparatorConstructor() {
66          final MapBuilder<String, String> builder = MapBuilder.builder();
67          builder.add("one", "value").add("two", "value").add("three", "value");
68          final SortedMap<String, String> map = CopyOnWriteSortedMap.<String, String> builder().ordering(
69              new ReverseComparator<String>(new StringComparator())).addAll(builder.toMap()).newTreeMap();
70  
71          assertEquals(3, map.size());
72      }
73  
74      @SuppressWarnings("unchecked")
75      @Test(expected = IllegalArgumentException.class)
76      public void mapNullConstructor() {
77          CopyOnWriteSortedMap.newTreeMap((Map) null);
78      }
79  
80      @SuppressWarnings("unchecked")
81      @Test(expected = IllegalArgumentException.class)
82      public void comparatorNullConstructor() {
83          CopyOnWriteSortedMap.newTreeMap((Comparator) null);
84      }
85  
86      @Test(expected = IllegalArgumentException.class)
87      public void mapNullComparatorConstructor() {
88          CopyOnWriteSortedMap.newTreeMap(null, new StringComparator());
89      }
90  
91      @Test(expected = IllegalArgumentException.class)
92      public void mapComparatorNullConstructor() {
93          CopyOnWriteSortedMap.newTreeMap(Collections.emptyMap(), null);
94      }
95  
96      @Test
97      public void comparator() {
98          final Comparator<String> comparator = new StringComparator();
99          final SortedMap<String, String> map = CopyOnWriteSortedMap.<String, String> builder().ordering(comparator).newTreeMap();
100         assertNotNull(map.comparator());
101         assertEquals(comparator, map.comparator());
102         map.put("one", "two");
103         assertNotNull(map.comparator());
104         assertEquals(1, map.size());
105         assertEquals(comparator, map.comparator());
106         map.put("two", "value");
107         assertEquals(2, map.size());
108         map.put("three", "value");
109         assertEquals(3, map.size());
110     }
111 
112     @Test
113     public void firstKey() {
114         final SortedMap<String, String> map = CopyOnWriteSortedMap.<String, String> builder().newTreeMap();
115         map.put("one", "value");
116         map.put("two", "value");
117         map.put("three", "value");
118         assertEquals("one", map.firstKey());
119     }
120 
121     @Test
122     public void firstKeyWithReverse() {
123         final MapBuilder<String, String> builder = MapBuilder.builder();
124         builder.add("one", "value").add("two", "value").add("three", "value");
125         final SortedMap<String, String> map = CopyOnWriteSortedMap.<String, String> builder().ordering(
126             new ReverseComparator<String>(new StringComparator())).addAll(builder.toMap()).newTreeMap();
127         map.put("one", "value");
128         map.put("two", "value");
129         map.put("three", "value");
130 
131         assertEquals(3, map.size());
132         assertEquals("two", map.firstKey());
133     }
134 
135     @Test
136     public void lastKey() {
137         final SortedMap<String, String> map = CopyOnWriteSortedMap.<String, String> builder().newTreeMap();
138         map.put("one", "value");
139         map.put("two", "value");
140         map.put("three", "value");
141         assertEquals("two", map.lastKey());
142     }
143 
144     @Test
145     public void lastKeyWithReverse() {
146         final MapBuilder<String, String> builder = MapBuilder.builder();
147         builder.add("one", "value").add("two", "value").add("three", "value");
148         final SortedMap<String, String> map = CopyOnWriteSortedMap.<String, String> builder().addAll(builder.toMap()).ordering(
149             new ReverseComparator<String>(new StringComparator())).newTreeMap();
150 
151         assertEquals(3, map.size());
152         assertEquals("one", map.lastKey());
153     }
154 
155     @Test
156     public void headMap() {
157         final SortedMap<String, String> map = CopyOnWriteSortedMap.<String, String> builder().newTreeMap();
158         map.put("1", "one");
159         map.put("2", "two");
160         map.put("3", "three");
161         final SortedMap<String, String> headMap = map.headMap("3");
162         assertEquals(2, headMap.size());
163         assertTrue(headMap.containsKey("1"));
164         assertTrue(headMap.containsKey("2"));
165         assertTrue(headMap.containsValue("one"));
166         assertTrue(headMap.containsValue("two"));
167 
168         assertUnmodifiableMap(headMap, "3", "three");
169     }
170 
171     @Test
172     public void tailMap() {
173         final SortedMap<String, String> map = CopyOnWriteSortedMap.<String, String> builder().newTreeMap();
174         map.put("1", "one");
175         map.put("2", "two");
176         map.put("3", "three");
177         final SortedMap<String, String> tailMap = map.tailMap("2");
178         assertEquals(2, tailMap.size());
179         assertTrue(tailMap.containsKey("2"));
180         assertTrue(tailMap.containsKey("3"));
181         assertTrue(tailMap.containsValue("two"));
182         assertTrue(tailMap.containsValue("three"));
183 
184         assertUnmodifiableMap(tailMap, "1", "one");
185     }
186 
187     @Test
188     public void subMap() {
189         final CopyOnWriteSortedMap<String, String> map = CopyOnWriteSortedMap.<String, String> builder().newTreeMap();
190         map.put("1", "one");
191         map.put("2", "two");
192         map.put("3", "three");
193         map.put("4", "four");
194         final SortedMap<String, String> subMap = map.subMap("2", "4");
195         assertEquals(2, subMap.size());
196         assertTrue(subMap.containsKey("2"));
197         assertTrue(subMap.containsKey("3"));
198         assertTrue(subMap.containsValue("two"));
199         assertTrue(subMap.containsValue("three"));
200 
201         assertUnmodifiableMap(subMap, "1", "one");
202     }
203 
204     @Test
205     public void serializableTreeMap() {
206         final CopyOnWriteSortedMap<Object, Object> map = CopyOnWriteSortedMap.builder().newTreeMap();
207         CopyOnWriteMapTest.assertMutableMapSerializable(map);
208     }
209 
210     static <K, V> void assertUnmodifiableMap(final Map<K, V> map, final K key, final V value) {
211         assertThrowsUnsupportedOp(new Runnable() {
212             public void run() {
213                 map.put(key, value);
214             }
215         });
216         assertThrowsUnsupportedOp(new Runnable() {
217             public void run() {
218                 map.remove(key);
219             }
220         });
221         assertThrowsUnsupportedOp(new Runnable() {
222             public void run() {
223                 map.clear();
224             }
225         });
226 
227         assertUnmodifiableCollection(map.keySet(), key);
228         assertUnmodifiableCollection(map.values(), value);
229     }
230 
231     static <T> void assertUnmodifiableCollection(final Collection<T> coll, final T element) {
232         assertThrowsUnsupportedOp(new Runnable() {
233             public void run() {
234                 coll.clear();
235             }
236         });
237 
238         assertThrowsUnsupportedOp(new Runnable() {
239             public void run() {
240                 coll.remove(element);
241             }
242         });
243 
244         assertThrowsUnsupportedOp(new Runnable() {
245             public void run() {
246                 coll.removeAll(Collections.EMPTY_LIST);
247             }
248         });
249 
250         assertThrowsUnsupportedOp(new Runnable() {
251             public void run() {
252                 coll.add(element);
253             }
254         });
255 
256         final Collection<T> empty = Collections.emptyList();
257         assertThrowsUnsupportedOp(new Runnable() {
258             public void run() {
259                 coll.addAll(empty);
260             }
261         });
262 
263         assertThrowsUnsupportedOp(new Runnable() {
264             public void run() {
265                 coll.retainAll(empty);
266             }
267         });
268 
269         assertThrowsUnsupportedOp(new Runnable() {
270             public void run() {
271                 final Iterator<?> it = coll.iterator();
272                 it.next();
273                 it.remove();
274             }
275         });
276     }
277 
278     static void assertThrowsUnsupportedOp(final Runnable runnable) {
279         try {
280             runnable.run();
281             fail("should have thrown UnsupportedOperationException");
282         } catch (final UnsupportedOperationException ignore) {}
283     }
284 }