001/*
002 * Copyright (C) 2007 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.base.Preconditions.checkNotNull;
020import static java.lang.System.arraycopy;
021import static java.util.Arrays.asList;
022import static java.util.Objects.requireNonNull;
023
024import com.google.common.annotations.GwtCompatible;
025import java.util.AbstractCollection;
026import java.util.Collection;
027import java.util.Iterator;
028import org.jspecify.annotations.NonNull;
029import org.jspecify.annotations.NullMarked;
030import org.jspecify.annotations.Nullable;
031
032/**
033 * A simplistic collection which implements only the bare minimum allowed by the spec, and throws
034 * exceptions whenever it can.
035 *
036 * @author Kevin Bourrillion
037 */
038@GwtCompatible
039@NullMarked
040public class MinimalCollection<E extends @Nullable Object> extends AbstractCollection<E> {
041  // TODO: expose allow nulls parameter?
042
043  public static <E extends @Nullable Object> MinimalCollection<E> of(E... contents) {
044    return new MinimalCollection<>(Object.class, true, contents);
045  }
046
047  // TODO: use this
048  public static <E extends @Nullable Object> MinimalCollection<E> ofClassAndContents(
049      Class<? super @NonNull E> type, E... contents) {
050    return new MinimalCollection<>(type, true, contents);
051  }
052
053  private final E[] contents;
054  private final Class<? super @NonNull E> type;
055  private final boolean allowNulls;
056
057  // Package-private so that it can be extended.
058  MinimalCollection(Class<? super @NonNull E> type, boolean allowNulls, E... contents) {
059    // TODO: consider making it shuffle the contents to test iteration order.
060    this.contents = Platform.clone(contents);
061    this.type = type;
062    this.allowNulls = allowNulls;
063
064    if (!allowNulls) {
065      for (Object element : contents) {
066        requireNonNull(element);
067      }
068    }
069  }
070
071  @Override
072  public int size() {
073    return contents.length;
074  }
075
076  @Override
077  public boolean contains(@Nullable Object object) {
078    if (!allowNulls) {
079      // behave badly
080      checkNotNull(object);
081    }
082    Platform.checkCast(type, object); // behave badly
083    return asList(contents).contains(object);
084  }
085
086  @Override
087  public boolean containsAll(Collection<?> collection) {
088    if (!allowNulls) {
089      for (Object object : collection) {
090        // behave badly
091        requireNonNull(object);
092      }
093    }
094    return super.containsAll(collection);
095  }
096
097  @Override
098  public Iterator<E> iterator() {
099    return asList(contents).iterator();
100  }
101
102  @Override
103  public @Nullable Object[] toArray() {
104    @Nullable Object[] result = new @Nullable Object[contents.length];
105    arraycopy(contents, 0, result, 0, contents.length);
106    return result;
107  }
108
109  /*
110   * a "type A" unmodifiable collection freaks out proactively, even if there
111   * wasn't going to be any actual work to do anyway
112   */
113
114  @Override
115  public boolean addAll(Collection<? extends E> elementsToAdd) {
116    throw up();
117  }
118
119  @Override
120  public boolean removeAll(Collection<?> elementsToRemove) {
121    throw up();
122  }
123
124  @Override
125  public boolean retainAll(Collection<?> elementsToRetain) {
126    throw up();
127  }
128
129  @Override
130  public void clear() {
131    throw up();
132  }
133
134  private static UnsupportedOperationException up() {
135    throw new UnsupportedOperationException();
136  }
137}