001/* 002 * Copyright (C) 2008 The Guava Authors 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package com.google.common.collect.testing; 018 019import static com.google.common.collect.testing.Helpers.castOrCopyToList; 020import static com.google.common.collect.testing.Helpers.equal; 021import static com.google.common.collect.testing.Helpers.mapEntry; 022import static java.util.Collections.sort; 023 024import com.google.common.annotations.GwtCompatible; 025import java.util.ArrayList; 026import java.util.Arrays; 027import java.util.Collection; 028import java.util.Collections; 029import java.util.Comparator; 030import java.util.List; 031import java.util.Map; 032import java.util.Map.Entry; 033import java.util.Set; 034import java.util.SortedMap; 035import java.util.SortedSet; 036 037/** 038 * Derived suite generators, split out of the suite builders so that they are available to GWT. 039 * 040 * @author George van den Driessche 041 */ 042@GwtCompatible 043public final class DerivedCollectionGenerators { 044 public static class MapEntrySetGenerator<K, V> 045 implements TestSetGenerator<Entry<K, V>>, DerivedGenerator { 046 private final OneSizeTestContainerGenerator<Map<K, V>, Entry<K, V>> mapGenerator; 047 048 public MapEntrySetGenerator( 049 OneSizeTestContainerGenerator<Map<K, V>, Entry<K, V>> mapGenerator) { 050 this.mapGenerator = mapGenerator; 051 } 052 053 @Override 054 public SampleElements<Entry<K, V>> samples() { 055 return mapGenerator.samples(); 056 } 057 058 @Override 059 public Set<Entry<K, V>> create(Object... elements) { 060 return mapGenerator.create(elements).entrySet(); 061 } 062 063 @Override 064 public Entry<K, V>[] createArray(int length) { 065 return mapGenerator.createArray(length); 066 } 067 068 @Override 069 public Iterable<Entry<K, V>> order(List<Entry<K, V>> insertionOrder) { 070 return mapGenerator.order(insertionOrder); 071 } 072 073 @Override 074 public OneSizeTestContainerGenerator<Map<K, V>, Entry<K, V>> getInnerGenerator() { 075 return mapGenerator; 076 } 077 } 078 079 // TODO: investigate some API changes to SampleElements that would tidy up 080 // parts of the following classes. 081 082 static <K, V> TestSetGenerator<K> keySetGenerator( 083 OneSizeTestContainerGenerator<Map<K, V>, Entry<K, V>> mapGenerator) { 084 TestContainerGenerator<Map<K, V>, Entry<K, V>> generator = mapGenerator.getInnerGenerator(); 085 if (generator instanceof TestSortedMapGenerator 086 && ((TestSortedMapGenerator<K, V>) generator).create().keySet() instanceof SortedSet) { 087 return new MapSortedKeySetGenerator<>(mapGenerator); 088 } else { 089 return new MapKeySetGenerator<>(mapGenerator); 090 } 091 } 092 093 public static class MapKeySetGenerator<K, V> implements TestSetGenerator<K>, DerivedGenerator { 094 private final OneSizeTestContainerGenerator<Map<K, V>, Entry<K, V>> mapGenerator; 095 private final SampleElements<K> samples; 096 097 public MapKeySetGenerator(OneSizeTestContainerGenerator<Map<K, V>, Entry<K, V>> mapGenerator) { 098 this.mapGenerator = mapGenerator; 099 final SampleElements<Entry<K, V>> mapSamples = this.mapGenerator.samples(); 100 this.samples = 101 new SampleElements<K>( 102 mapSamples.e0().getKey(), 103 mapSamples.e1().getKey(), 104 mapSamples.e2().getKey(), 105 mapSamples.e3().getKey(), 106 mapSamples.e4().getKey()); 107 } 108 109 @Override 110 public SampleElements<K> samples() { 111 return samples; 112 } 113 114 @Override 115 public Set<K> create(Object... elements) { 116 @SuppressWarnings("unchecked") 117 K[] keysArray = (K[]) elements; 118 119 // Start with a suitably shaped collection of entries 120 Collection<Entry<K, V>> originalEntries = mapGenerator.getSampleElements(elements.length); 121 122 // Create a copy of that, with the desired value for each key 123 Collection<Entry<K, V>> entries = new ArrayList<>(elements.length); 124 int i = 0; 125 for (Entry<K, V> entry : originalEntries) { 126 entries.add(Helpers.mapEntry(keysArray[i++], entry.getValue())); 127 } 128 129 return mapGenerator.create(entries.toArray()).keySet(); 130 } 131 132 @Override 133 public K[] createArray(int length) { 134 // TODO: with appropriate refactoring of OneSizeGenerator, we can perhaps 135 // tidy this up and get rid of the casts here and in 136 // MapValueCollectionGenerator. 137 138 return ((TestMapGenerator<K, V>) mapGenerator.getInnerGenerator()).createKeyArray(length); 139 } 140 141 @Override 142 public Iterable<K> order(List<K> insertionOrder) { 143 V v = ((TestMapGenerator<K, V>) mapGenerator.getInnerGenerator()).samples().e0().getValue(); 144 List<Entry<K, V>> entries = new ArrayList<>(); 145 for (K element : insertionOrder) { 146 entries.add(mapEntry(element, v)); 147 } 148 149 List<K> keys = new ArrayList<>(); 150 for (Entry<K, V> entry : mapGenerator.order(entries)) { 151 keys.add(entry.getKey()); 152 } 153 return keys; 154 } 155 156 @Override 157 public OneSizeTestContainerGenerator<Map<K, V>, Entry<K, V>> getInnerGenerator() { 158 return mapGenerator; 159 } 160 } 161 162 public static class MapSortedKeySetGenerator<K, V> extends MapKeySetGenerator<K, V> 163 implements TestSortedSetGenerator<K>, DerivedGenerator { 164 private final TestSortedMapGenerator<K, V> delegate; 165 166 public MapSortedKeySetGenerator( 167 OneSizeTestContainerGenerator<Map<K, V>, Entry<K, V>> mapGenerator) { 168 super(mapGenerator); 169 this.delegate = (TestSortedMapGenerator<K, V>) mapGenerator.getInnerGenerator(); 170 } 171 172 @Override 173 public SortedSet<K> create(Object... elements) { 174 return (SortedSet<K>) super.create(elements); 175 } 176 177 @Override 178 public K belowSamplesLesser() { 179 return delegate.belowSamplesLesser().getKey(); 180 } 181 182 @Override 183 public K belowSamplesGreater() { 184 return delegate.belowSamplesGreater().getKey(); 185 } 186 187 @Override 188 public K aboveSamplesLesser() { 189 return delegate.aboveSamplesLesser().getKey(); 190 } 191 192 @Override 193 public K aboveSamplesGreater() { 194 return delegate.aboveSamplesGreater().getKey(); 195 } 196 } 197 198 public static class MapValueCollectionGenerator<K, V> 199 implements TestCollectionGenerator<V>, DerivedGenerator { 200 private final OneSizeTestContainerGenerator<Map<K, V>, Entry<K, V>> mapGenerator; 201 private final SampleElements<V> samples; 202 203 public MapValueCollectionGenerator( 204 OneSizeTestContainerGenerator<Map<K, V>, Entry<K, V>> mapGenerator) { 205 this.mapGenerator = mapGenerator; 206 final SampleElements<Entry<K, V>> mapSamples = this.mapGenerator.samples(); 207 this.samples = 208 new SampleElements<V>( 209 mapSamples.e0().getValue(), 210 mapSamples.e1().getValue(), 211 mapSamples.e2().getValue(), 212 mapSamples.e3().getValue(), 213 mapSamples.e4().getValue()); 214 } 215 216 @Override 217 public SampleElements<V> samples() { 218 return samples; 219 } 220 221 @Override 222 public Collection<V> create(Object... elements) { 223 @SuppressWarnings("unchecked") 224 V[] valuesArray = (V[]) elements; 225 226 // Start with a suitably shaped collection of entries 227 Collection<Entry<K, V>> originalEntries = mapGenerator.getSampleElements(elements.length); 228 229 // Create a copy of that, with the desired value for each value 230 Collection<Entry<K, V>> entries = new ArrayList<>(elements.length); 231 int i = 0; 232 for (Entry<K, V> entry : originalEntries) { 233 entries.add(Helpers.mapEntry(entry.getKey(), valuesArray[i++])); 234 } 235 236 return mapGenerator.create(entries.toArray()).values(); 237 } 238 239 @Override 240 public V[] createArray(int length) { 241 // noinspection UnnecessaryLocalVariable 242 final V[] vs = 243 ((TestMapGenerator<K, V>) mapGenerator.getInnerGenerator()).createValueArray(length); 244 return vs; 245 } 246 247 @Override 248 public Iterable<V> order(List<V> insertionOrder) { 249 final List<Entry<K, V>> orderedEntries = 250 castOrCopyToList(mapGenerator.order(castOrCopyToList(mapGenerator.getSampleElements(5)))); 251 sort( 252 insertionOrder, 253 new Comparator<V>() { 254 @Override 255 public int compare(V left, V right) { 256 // The indexes are small enough for the subtraction trick to be safe. 257 return indexOfEntryWithValue(left) - indexOfEntryWithValue(right); 258 } 259 260 int indexOfEntryWithValue(V value) { 261 for (int i = 0; i < orderedEntries.size(); i++) { 262 if (equal(orderedEntries.get(i).getValue(), value)) { 263 return i; 264 } 265 } 266 throw new IllegalArgumentException( 267 "Map.values generator can order only sample values"); 268 } 269 }); 270 return insertionOrder; 271 } 272 273 @Override 274 public OneSizeTestContainerGenerator<Map<K, V>, Entry<K, V>> getInnerGenerator() { 275 return mapGenerator; 276 } 277 } 278 279 // TODO(cpovirk): could something like this be used elsewhere, e.g., ReserializedListGenerator? 280 static class ForwardingTestMapGenerator<K, V> implements TestMapGenerator<K, V> { 281 TestMapGenerator<K, V> delegate; 282 283 ForwardingTestMapGenerator(TestMapGenerator<K, V> delegate) { 284 this.delegate = delegate; 285 } 286 287 @Override 288 public Iterable<Entry<K, V>> order(List<Entry<K, V>> insertionOrder) { 289 return delegate.order(insertionOrder); 290 } 291 292 @Override 293 public K[] createKeyArray(int length) { 294 return delegate.createKeyArray(length); 295 } 296 297 @Override 298 public V[] createValueArray(int length) { 299 return delegate.createValueArray(length); 300 } 301 302 @Override 303 public SampleElements<Entry<K, V>> samples() { 304 return delegate.samples(); 305 } 306 307 @Override 308 public Map<K, V> create(Object... elements) { 309 return delegate.create(elements); 310 } 311 312 @Override 313 public Entry<K, V>[] createArray(int length) { 314 return delegate.createArray(length); 315 } 316 } 317 318 /** Two bounds (from and to) define how to build a subMap. */ 319 public enum Bound { 320 INCLUSIVE, 321 EXCLUSIVE, 322 NO_BOUND; 323 } 324 325 public static class SortedSetSubsetTestSetGenerator<E> implements TestSortedSetGenerator<E> { 326 final Bound to; 327 final Bound from; 328 final E firstInclusive; 329 final E lastInclusive; 330 private final Comparator<? super E> comparator; 331 private final TestSortedSetGenerator<E> delegate; 332 333 public SortedSetSubsetTestSetGenerator( 334 TestSortedSetGenerator<E> delegate, Bound to, Bound from) { 335 this.to = to; 336 this.from = from; 337 this.delegate = delegate; 338 339 SortedSet<E> emptySet = delegate.create(); 340 this.comparator = emptySet.comparator(); 341 342 SampleElements<E> samples = delegate.samples(); 343 List<E> samplesList = new ArrayList<>(samples.asList()); 344 Collections.sort(samplesList, comparator); 345 this.firstInclusive = samplesList.get(0); 346 this.lastInclusive = samplesList.get(samplesList.size() - 1); 347 } 348 349 public final TestSortedSetGenerator<E> getInnerGenerator() { 350 return delegate; 351 } 352 353 public final Bound getTo() { 354 return to; 355 } 356 357 public final Bound getFrom() { 358 return from; 359 } 360 361 @Override 362 public SampleElements<E> samples() { 363 return delegate.samples(); 364 } 365 366 @Override 367 public E[] createArray(int length) { 368 return delegate.createArray(length); 369 } 370 371 @Override 372 public Iterable<E> order(List<E> insertionOrder) { 373 return delegate.order(insertionOrder); 374 } 375 376 @Override 377 public SortedSet<E> create(Object... elements) { 378 @SuppressWarnings("unchecked") // set generators must pass SampleElements values 379 List<E> normalValues = (List) Arrays.asList(elements); 380 List<E> extremeValues = new ArrayList<>(); 381 382 // nulls are usually out of bounds for a subset, so ban them altogether 383 for (Object o : elements) { 384 if (o == null) { 385 throw new NullPointerException(); 386 } 387 } 388 389 // prepare extreme values to be filtered out of view 390 E firstExclusive = delegate.belowSamplesGreater(); 391 E lastExclusive = delegate.aboveSamplesLesser(); 392 if (from != Bound.NO_BOUND) { 393 extremeValues.add(delegate.belowSamplesLesser()); 394 extremeValues.add(delegate.belowSamplesGreater()); 395 } 396 if (to != Bound.NO_BOUND) { 397 extremeValues.add(delegate.aboveSamplesLesser()); 398 extremeValues.add(delegate.aboveSamplesGreater()); 399 } 400 401 // the regular values should be visible after filtering 402 List<E> allEntries = new ArrayList<>(); 403 allEntries.addAll(extremeValues); 404 allEntries.addAll(normalValues); 405 SortedSet<E> map = delegate.create(allEntries.toArray()); 406 407 return createSubSet(map, firstExclusive, lastExclusive); 408 } 409 410 /** Calls the smallest subSet overload that filters out the extreme values. */ 411 SortedSet<E> createSubSet(SortedSet<E> set, E firstExclusive, E lastExclusive) { 412 if (from == Bound.NO_BOUND && to == Bound.EXCLUSIVE) { 413 return set.headSet(lastExclusive); 414 } else if (from == Bound.INCLUSIVE && to == Bound.NO_BOUND) { 415 return set.tailSet(firstInclusive); 416 } else if (from == Bound.INCLUSIVE && to == Bound.EXCLUSIVE) { 417 return set.subSet(firstInclusive, lastExclusive); 418 } else { 419 throw new IllegalArgumentException(); 420 } 421 } 422 423 @Override 424 public E belowSamplesLesser() { 425 throw new UnsupportedOperationException(); 426 } 427 428 @Override 429 public E belowSamplesGreater() { 430 throw new UnsupportedOperationException(); 431 } 432 433 @Override 434 public E aboveSamplesLesser() { 435 throw new UnsupportedOperationException(); 436 } 437 438 @Override 439 public E aboveSamplesGreater() { 440 throw new UnsupportedOperationException(); 441 } 442 } 443 444 /* 445 * TODO(cpovirk): surely we can find a less ugly solution than a class that accepts 3 parameters, 446 * exposes as many getters, does work in the constructor, and has both a superclass and a subclass 447 */ 448 public static class SortedMapSubmapTestMapGenerator<K, V> extends ForwardingTestMapGenerator<K, V> 449 implements TestSortedMapGenerator<K, V> { 450 final Bound to; 451 final Bound from; 452 final K firstInclusive; 453 final K lastInclusive; 454 private final Comparator<Entry<K, V>> entryComparator; 455 456 public SortedMapSubmapTestMapGenerator( 457 TestSortedMapGenerator<K, V> delegate, Bound to, Bound from) { 458 super(delegate); 459 this.to = to; 460 this.from = from; 461 462 SortedMap<K, V> emptyMap = delegate.create(); 463 this.entryComparator = Helpers.entryComparator(emptyMap.comparator()); 464 465 // derive values for inclusive filtering from the input samples 466 SampleElements<Entry<K, V>> samples = delegate.samples(); 467 @SuppressWarnings("unchecked") // no elements are inserted into the array 468 List<Entry<K, V>> samplesList = 469 Arrays.asList(samples.e0(), samples.e1(), samples.e2(), samples.e3(), samples.e4()); 470 Collections.sort(samplesList, entryComparator); 471 this.firstInclusive = samplesList.get(0).getKey(); 472 this.lastInclusive = samplesList.get(samplesList.size() - 1).getKey(); 473 } 474 475 @Override 476 public SortedMap<K, V> create(Object... entries) { 477 @SuppressWarnings("unchecked") // map generators must past entry objects 478 List<Entry<K, V>> normalValues = (List) Arrays.asList(entries); 479 List<Entry<K, V>> extremeValues = new ArrayList<>(); 480 481 // prepare extreme values to be filtered out of view 482 K firstExclusive = getInnerGenerator().belowSamplesGreater().getKey(); 483 K lastExclusive = getInnerGenerator().aboveSamplesLesser().getKey(); 484 if (from != Bound.NO_BOUND) { 485 extremeValues.add(getInnerGenerator().belowSamplesLesser()); 486 extremeValues.add(getInnerGenerator().belowSamplesGreater()); 487 } 488 if (to != Bound.NO_BOUND) { 489 extremeValues.add(getInnerGenerator().aboveSamplesLesser()); 490 extremeValues.add(getInnerGenerator().aboveSamplesGreater()); 491 } 492 493 // the regular values should be visible after filtering 494 List<Entry<K, V>> allEntries = new ArrayList<>(); 495 allEntries.addAll(extremeValues); 496 allEntries.addAll(normalValues); 497 SortedMap<K, V> map = 498 (SortedMap<K, V>) 499 delegate.create((Object[]) allEntries.toArray(new Entry<?, ?>[allEntries.size()])); 500 501 return createSubMap(map, firstExclusive, lastExclusive); 502 } 503 504 /** 505 * Calls the smallest subMap overload that filters out the extreme values. This method is 506 * overridden in NavigableMapTestSuiteBuilder. 507 */ 508 SortedMap<K, V> createSubMap(SortedMap<K, V> map, K firstExclusive, K lastExclusive) { 509 if (from == Bound.NO_BOUND && to == Bound.EXCLUSIVE) { 510 return map.headMap(lastExclusive); 511 } else if (from == Bound.INCLUSIVE && to == Bound.NO_BOUND) { 512 return map.tailMap(firstInclusive); 513 } else if (from == Bound.INCLUSIVE && to == Bound.EXCLUSIVE) { 514 return map.subMap(firstInclusive, lastExclusive); 515 } else { 516 throw new IllegalArgumentException(); 517 } 518 } 519 520 public final Bound getTo() { 521 return to; 522 } 523 524 public final Bound getFrom() { 525 return from; 526 } 527 528 public final TestSortedMapGenerator<K, V> getInnerGenerator() { 529 return (TestSortedMapGenerator<K, V>) delegate; 530 } 531 532 @Override 533 public Entry<K, V> belowSamplesLesser() { 534 // should never reach here! 535 throw new UnsupportedOperationException(); 536 } 537 538 @Override 539 public Entry<K, V> belowSamplesGreater() { 540 // should never reach here! 541 throw new UnsupportedOperationException(); 542 } 543 544 @Override 545 public Entry<K, V> aboveSamplesLesser() { 546 // should never reach here! 547 throw new UnsupportedOperationException(); 548 } 549 550 @Override 551 public Entry<K, V> aboveSamplesGreater() { 552 // should never reach here! 553 throw new UnsupportedOperationException(); 554 } 555 } 556 557 private DerivedCollectionGenerators() {} 558}