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 17 package com.google.common.collect.testing; 18 19 import static com.google.common.collect.testing.Helpers.castOrCopyToList; 20 import static com.google.common.collect.testing.Helpers.equal; 21 import static com.google.common.collect.testing.Helpers.mapEntry; 22 import static java.util.Collections.sort; 23 24 import com.google.common.annotations.GwtCompatible; 25 26 import java.util.ArrayList; 27 import java.util.Arrays; 28 import java.util.Collection; 29 import java.util.Collections; 30 import java.util.Comparator; 31 import java.util.List; 32 import java.util.Map; 33 import java.util.Map.Entry; 34 import java.util.Set; 35 import java.util.SortedMap; 36 import java.util.SortedSet; 37 38 /** 39 * Derived suite generators, split out of the suite builders so that they are available to GWT. 40 * 41 * @author George van den Driessche 42 */ 43 @GwtCompatible 44 public final class DerivedCollectionGenerators { 45 public static class MapEntrySetGenerator<K, V> 46 implements TestSetGenerator<Map.Entry<K, V>>, DerivedGenerator { 47 private final OneSizeTestContainerGenerator<Map<K, V>, Map.Entry<K, V>> 48 mapGenerator; 49 50 public MapEntrySetGenerator( 51 OneSizeTestContainerGenerator< 52 Map<K, V>, Map.Entry<K, V>> mapGenerator) { 53 this.mapGenerator = mapGenerator; 54 } 55 56 @Override 57 public SampleElements<Map.Entry<K, V>> samples() { 58 return mapGenerator.samples(); 59 } 60 61 @Override 62 public Set<Map.Entry<K, V>> create(Object... elements) { 63 return mapGenerator.create(elements).entrySet(); 64 } 65 66 @Override 67 public Map.Entry<K, V>[] createArray(int length) { 68 return mapGenerator.createArray(length); 69 } 70 71 @Override 72 public Iterable<Map.Entry<K, V>> order( 73 List<Map.Entry<K, V>> insertionOrder) { 74 return mapGenerator.order(insertionOrder); 75 } 76 77 @Override 78 public OneSizeTestContainerGenerator<Map<K, V>, Map.Entry<K, V>> getInnerGenerator() { 79 return mapGenerator; 80 } 81 } 82 83 // TODO: investigate some API changes to SampleElements that would tidy up 84 // parts of the following classes. 85 86 static <K, V> TestSetGenerator<K> keySetGenerator( 87 OneSizeTestContainerGenerator<Map<K, V>, Map.Entry<K, V>> mapGenerator) { 88 TestContainerGenerator<Map<K, V>, Entry<K, V>> generator = mapGenerator.getInnerGenerator(); 89 if (generator instanceof TestSortedMapGenerator 90 && ((TestSortedMapGenerator<K, V>) generator).create().keySet() instanceof SortedSet) { 91 return new MapSortedKeySetGenerator<K, V>(mapGenerator); 92 } else { 93 return new MapKeySetGenerator<K, V>(mapGenerator); 94 } 95 } 96 97 public static class MapKeySetGenerator<K, V> 98 implements TestSetGenerator<K>, DerivedGenerator { 99 private final OneSizeTestContainerGenerator<Map<K, V>, Map.Entry<K, V>> 100 mapGenerator; 101 private final SampleElements<K> samples; 102 103 public MapKeySetGenerator( 104 OneSizeTestContainerGenerator<Map<K, V>, Map.Entry<K, V>> 105 mapGenerator) { 106 this.mapGenerator = mapGenerator; 107 final SampleElements<Map.Entry<K, V>> mapSamples = 108 this.mapGenerator.samples(); 109 this.samples = new SampleElements<K>( 110 mapSamples.e0.getKey(), 111 mapSamples.e1.getKey(), 112 mapSamples.e2.getKey(), 113 mapSamples.e3.getKey(), 114 mapSamples.e4.getKey()); 115 } 116 117 @Override 118 public SampleElements<K> samples() { 119 return samples; 120 } 121 122 @Override 123 public Set<K> create(Object... elements) { 124 @SuppressWarnings("unchecked") 125 K[] keysArray = (K[]) elements; 126 127 // Start with a suitably shaped collection of entries 128 Collection<Map.Entry<K, V>> originalEntries = 129 mapGenerator.getSampleElements(elements.length); 130 131 // Create a copy of that, with the desired value for each key 132 Collection<Map.Entry<K, V>> entries = 133 new ArrayList<Entry<K, V>>(elements.length); 134 int i = 0; 135 for (Map.Entry<K, V> entry : originalEntries) { 136 entries.add(Helpers.mapEntry(keysArray[i++], entry.getValue())); 137 } 138 139 return mapGenerator.create(entries.toArray()).keySet(); 140 } 141 142 @Override 143 public K[] createArray(int length) { 144 // TODO: with appropriate refactoring of OneSizeGenerator, we can perhaps 145 // tidy this up and get rid of the casts here and in 146 // MapValueCollectionGenerator. 147 148 return ((TestMapGenerator<K, V>) mapGenerator.getInnerGenerator()) 149 .createKeyArray(length); 150 } 151 152 @Override 153 public Iterable<K> order(List<K> insertionOrder) { 154 V v = ((TestMapGenerator<K, V>) mapGenerator.getInnerGenerator()).samples().e0.getValue(); 155 List<Entry<K, V>> entries = new ArrayList<Entry<K, V>>(); 156 for (K element : insertionOrder) { 157 entries.add(mapEntry(element, v)); 158 } 159 160 List<K> keys = new ArrayList<K>(); 161 for (Entry<K, V> entry : mapGenerator.order(entries)) { 162 keys.add(entry.getKey()); 163 } 164 return keys; 165 } 166 167 @Override 168 public OneSizeTestContainerGenerator<Map<K, V>, Map.Entry<K, V>> getInnerGenerator() { 169 return mapGenerator; 170 } 171 } 172 173 public static class MapSortedKeySetGenerator<K, V> extends MapKeySetGenerator<K, V> 174 implements TestSortedSetGenerator<K>, DerivedGenerator { 175 private final TestSortedMapGenerator<K, V> delegate; 176 177 public MapSortedKeySetGenerator( 178 OneSizeTestContainerGenerator<Map<K, V>, Entry<K, V>> mapGenerator) { 179 super(mapGenerator); 180 this.delegate = (TestSortedMapGenerator<K, V>) mapGenerator.getInnerGenerator(); 181 } 182 183 @Override 184 public SortedSet<K> create(Object... elements) { 185 return (SortedSet<K>) super.create(elements); 186 } 187 188 @Override 189 public K belowSamplesLesser() { 190 return delegate.belowSamplesLesser().getKey(); 191 } 192 193 @Override 194 public K belowSamplesGreater() { 195 return delegate.belowSamplesGreater().getKey(); 196 } 197 198 @Override 199 public K aboveSamplesLesser() { 200 return delegate.aboveSamplesLesser().getKey(); 201 } 202 203 @Override 204 public K aboveSamplesGreater() { 205 return delegate.aboveSamplesGreater().getKey(); 206 } 207 208 } 209 210 public static class MapValueCollectionGenerator<K, V> 211 implements TestCollectionGenerator<V>, DerivedGenerator { 212 private final OneSizeTestContainerGenerator<Map<K, V>, Map.Entry<K, V>> 213 mapGenerator; 214 private final SampleElements<V> samples; 215 216 public MapValueCollectionGenerator( 217 OneSizeTestContainerGenerator< 218 Map<K, V>, Map.Entry<K, V>> mapGenerator) { 219 this.mapGenerator = mapGenerator; 220 final SampleElements<Map.Entry<K, V>> mapSamples = 221 this.mapGenerator.samples(); 222 this.samples = new SampleElements<V>( 223 mapSamples.e0.getValue(), 224 mapSamples.e1.getValue(), 225 mapSamples.e2.getValue(), 226 mapSamples.e3.getValue(), 227 mapSamples.e4.getValue()); 228 } 229 230 @Override 231 public SampleElements<V> samples() { 232 return samples; 233 } 234 235 @Override 236 public Collection<V> create(Object... elements) { 237 @SuppressWarnings("unchecked") 238 V[] valuesArray = (V[]) elements; 239 240 // Start with a suitably shaped collection of entries 241 Collection<Map.Entry<K, V>> originalEntries = 242 mapGenerator.getSampleElements(elements.length); 243 244 // Create a copy of that, with the desired value for each value 245 Collection<Map.Entry<K, V>> entries = 246 new ArrayList<Entry<K, V>>(elements.length); 247 int i = 0; 248 for (Map.Entry<K, V> entry : originalEntries) { 249 entries.add(Helpers.mapEntry(entry.getKey(), valuesArray[i++])); 250 } 251 252 return mapGenerator.create(entries.toArray()).values(); 253 } 254 255 @Override 256 public V[] createArray(int length) { 257 //noinspection UnnecessaryLocalVariable 258 final V[] vs = ((TestMapGenerator<K, V>) mapGenerator.getInnerGenerator()) 259 .createValueArray(length); 260 return vs; 261 } 262 263 @Override 264 public Iterable<V> order(List<V> insertionOrder) { 265 final List<Entry<K, V>> orderedEntries = 266 castOrCopyToList(mapGenerator.order(castOrCopyToList( 267 mapGenerator.getSampleElements(5)))); 268 sort(insertionOrder, new Comparator<V>() { 269 @Override public int compare(V left, V right) { 270 // The indexes are small enough for the subtraction trick to be safe. 271 return indexOfEntryWithValue(left) - indexOfEntryWithValue(right); 272 } 273 274 int indexOfEntryWithValue(V value) { 275 for (int i = 0; i < orderedEntries.size(); i++) { 276 if (equal(orderedEntries.get(i).getValue(), value)) { 277 return i; 278 } 279 } 280 throw new IllegalArgumentException("Map.values generator can order only sample values"); 281 } 282 }); 283 return insertionOrder; 284 } 285 286 @Override 287 public OneSizeTestContainerGenerator<Map<K, V>, Map.Entry<K, V>> getInnerGenerator() { 288 return mapGenerator; 289 } 290 } 291 292 // TODO(cpovirk): could something like this be used elsewhere, e.g., ReserializedListGenerator? 293 static class ForwardingTestMapGenerator<K, V> implements TestMapGenerator<K, V> { 294 TestMapGenerator<K, V> delegate; 295 296 ForwardingTestMapGenerator(TestMapGenerator<K, V> delegate) { 297 this.delegate = delegate; 298 } 299 300 @Override 301 public Iterable<Entry<K, V>> order(List<Entry<K, V>> insertionOrder) { 302 return delegate.order(insertionOrder); 303 } 304 305 @Override 306 public K[] createKeyArray(int length) { 307 return delegate.createKeyArray(length); 308 } 309 310 @Override 311 public V[] createValueArray(int length) { 312 return delegate.createValueArray(length); 313 } 314 315 @Override 316 public SampleElements<Entry<K, V>> samples() { 317 return delegate.samples(); 318 } 319 320 @Override 321 public Map<K, V> create(Object... elements) { 322 return delegate.create(elements); 323 } 324 325 @Override 326 public Entry<K, V>[] createArray(int length) { 327 return delegate.createArray(length); 328 } 329 } 330 331 /** 332 * Two bounds (from and to) define how to build a subMap. 333 */ 334 public enum Bound { 335 INCLUSIVE, 336 EXCLUSIVE, 337 NO_BOUND; 338 } 339 340 public static class SortedSetSubsetTestSetGenerator<E> 341 implements TestSortedSetGenerator<E> { 342 final Bound to; 343 final Bound from; 344 final E firstInclusive; 345 final E lastInclusive; 346 private final Comparator<? super E> comparator; 347 private final TestSortedSetGenerator<E> delegate; 348 349 public SortedSetSubsetTestSetGenerator( 350 TestSortedSetGenerator<E> delegate, Bound to, Bound from) { 351 this.to = to; 352 this.from = from; 353 this.delegate = delegate; 354 355 SortedSet<E> emptySet = delegate.create(); 356 this.comparator = emptySet.comparator(); 357 358 SampleElements<E> samples = delegate.samples(); 359 List<E> samplesList = new ArrayList<E>(samples.asList()); 360 Collections.sort(samplesList, comparator); 361 this.firstInclusive = samplesList.get(0); 362 this.lastInclusive = samplesList.get(samplesList.size() - 1); 363 } 364 365 public final TestSortedSetGenerator<E> getInnerGenerator() { 366 return delegate; 367 } 368 369 public final Bound getTo() { 370 return to; 371 } 372 373 public final Bound getFrom() { 374 return from; 375 } 376 377 @Override 378 public SampleElements<E> samples() { 379 return delegate.samples(); 380 } 381 382 @Override 383 public E[] createArray(int length) { 384 return delegate.createArray(length); 385 } 386 387 @Override 388 public Iterable<E> order(List<E> insertionOrder) { 389 return delegate.order(insertionOrder); 390 } 391 392 @Override 393 public SortedSet<E> create(Object... elements) { 394 @SuppressWarnings("unchecked") // set generators must pass SampleElements values 395 List<E> normalValues = (List) Arrays.asList(elements); 396 List<E> extremeValues = new ArrayList<E>(); 397 398 // nulls are usually out of bounds for a subset, so ban them altogether 399 for (Object o : elements) { 400 if (o == null) { 401 throw new NullPointerException(); 402 } 403 } 404 405 // prepare extreme values to be filtered out of view 406 E firstExclusive = delegate.belowSamplesGreater(); 407 E lastExclusive = delegate.aboveSamplesLesser(); 408 if (from != Bound.NO_BOUND) { 409 extremeValues.add(delegate.belowSamplesLesser()); 410 extremeValues.add(delegate.belowSamplesGreater()); 411 } 412 if (to != Bound.NO_BOUND) { 413 extremeValues.add(delegate.aboveSamplesLesser()); 414 extremeValues.add(delegate.aboveSamplesGreater()); 415 } 416 417 // the regular values should be visible after filtering 418 List<E> allEntries = new ArrayList<E>(); 419 allEntries.addAll(extremeValues); 420 allEntries.addAll(normalValues); 421 SortedSet<E> map = delegate.create(allEntries.toArray()); 422 423 return createSubSet(map, firstExclusive, lastExclusive); 424 } 425 426 /** 427 * Calls the smallest subSet overload that filters out the extreme values. 428 */ 429 SortedSet<E> createSubSet(SortedSet<E> set, E firstExclusive, E lastExclusive) { 430 if (from == Bound.NO_BOUND && to == Bound.EXCLUSIVE) { 431 return set.headSet(lastExclusive); 432 } else if (from == Bound.INCLUSIVE && to == Bound.NO_BOUND) { 433 return set.tailSet(firstInclusive); 434 } else if (from == Bound.INCLUSIVE && to == Bound.EXCLUSIVE) { 435 return set.subSet(firstInclusive, lastExclusive); 436 } else { 437 throw new IllegalArgumentException(); 438 } 439 } 440 441 @Override 442 public E belowSamplesLesser() { 443 throw new UnsupportedOperationException(); 444 } 445 446 @Override 447 public E belowSamplesGreater() { 448 throw new UnsupportedOperationException(); 449 } 450 451 @Override 452 public E aboveSamplesLesser() { 453 throw new UnsupportedOperationException(); 454 } 455 456 @Override 457 public E aboveSamplesGreater() { 458 throw new UnsupportedOperationException(); 459 } 460 } 461 462 /* 463 * TODO(cpovirk): surely we can find a less ugly solution than a class that accepts 3 parameters, 464 * exposes as many getters, does work in the constructor, and has both a superclass and a subclass 465 */ 466 public static class SortedMapSubmapTestMapGenerator<K, V> 467 extends ForwardingTestMapGenerator<K, V> implements TestSortedMapGenerator<K, V> { 468 final Bound to; 469 final Bound from; 470 final K firstInclusive; 471 final K lastInclusive; 472 private final Comparator<Entry<K, V>> entryComparator; 473 474 public SortedMapSubmapTestMapGenerator( 475 TestSortedMapGenerator<K, V> delegate, Bound to, Bound from) { 476 super(delegate); 477 this.to = to; 478 this.from = from; 479 480 SortedMap<K, V> emptyMap = delegate.create(); 481 this.entryComparator = Helpers.entryComparator(emptyMap.comparator()); 482 483 // derive values for inclusive filtering from the input samples 484 SampleElements<Entry<K, V>> samples = delegate.samples(); 485 @SuppressWarnings("unchecked") // no elements are inserted into the array 486 List<Entry<K, V>> samplesList = Arrays.asList( 487 samples.e0, samples.e1, samples.e2, samples.e3, samples.e4); 488 Collections.sort(samplesList, entryComparator); 489 this.firstInclusive = samplesList.get(0).getKey(); 490 this.lastInclusive = samplesList.get(samplesList.size() - 1).getKey(); 491 } 492 493 @Override public SortedMap<K, V> create(Object... entries) { 494 @SuppressWarnings("unchecked") // map generators must past entry objects 495 List<Entry<K, V>> normalValues = (List) Arrays.asList(entries); 496 List<Entry<K, V>> extremeValues = new ArrayList<Entry<K, V>>(); 497 498 // prepare extreme values to be filtered out of view 499 K firstExclusive = getInnerGenerator().belowSamplesGreater().getKey(); 500 K lastExclusive = getInnerGenerator().aboveSamplesLesser().getKey(); 501 if (from != Bound.NO_BOUND) { 502 extremeValues.add(getInnerGenerator().belowSamplesLesser()); 503 extremeValues.add(getInnerGenerator().belowSamplesGreater()); 504 } 505 if (to != Bound.NO_BOUND) { 506 extremeValues.add(getInnerGenerator().aboveSamplesLesser()); 507 extremeValues.add(getInnerGenerator().aboveSamplesGreater()); 508 } 509 510 // the regular values should be visible after filtering 511 List<Entry<K, V>> allEntries = new ArrayList<Entry<K, V>>(); 512 allEntries.addAll(extremeValues); 513 allEntries.addAll(normalValues); 514 SortedMap<K, V> map = (SortedMap<K, V>) 515 delegate.create((Object[]) 516 allEntries.toArray(new Entry<?, ?>[allEntries.size()])); 517 518 return createSubMap(map, firstExclusive, lastExclusive); 519 } 520 521 /** 522 * Calls the smallest subMap overload that filters out the extreme values. This method is 523 * overridden in NavigableMapTestSuiteBuilder. 524 */ 525 SortedMap<K, V> createSubMap(SortedMap<K, V> map, K firstExclusive, K lastExclusive) { 526 if (from == Bound.NO_BOUND && to == Bound.EXCLUSIVE) { 527 return map.headMap(lastExclusive); 528 } else if (from == Bound.INCLUSIVE && to == Bound.NO_BOUND) { 529 return map.tailMap(firstInclusive); 530 } else if (from == Bound.INCLUSIVE && to == Bound.EXCLUSIVE) { 531 return map.subMap(firstInclusive, lastExclusive); 532 } else { 533 throw new IllegalArgumentException(); 534 } 535 } 536 537 public final Bound getTo() { 538 return to; 539 } 540 541 public final Bound getFrom() { 542 return from; 543 } 544 545 public final TestSortedMapGenerator<K, V> getInnerGenerator() { 546 return (TestSortedMapGenerator<K, V>) delegate; 547 } 548 549 @Override 550 public Entry<K, V> belowSamplesLesser() { 551 // should never reach here! 552 throw new UnsupportedOperationException(); 553 } 554 555 @Override 556 public Entry<K, V> belowSamplesGreater() { 557 // should never reach here! 558 throw new UnsupportedOperationException(); 559 } 560 561 @Override 562 public Entry<K, V> aboveSamplesLesser() { 563 // should never reach here! 564 throw new UnsupportedOperationException(); 565 } 566 567 @Override 568 public Entry<K, V> aboveSamplesGreater() { 569 // should never reach here! 570 throw new UnsupportedOperationException(); 571 } 572 } 573 574 private DerivedCollectionGenerators() {} 575 } 576