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