/* * Copyright (C) 2007 The Guava Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.common.collect; import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import java.util.Collection; import java.util.List; import java.util.ListIterator; import java.util.RandomAccess; import java.util.Set; import java.util.SortedSet; /** * Factories and utilities pertaining to the {@link Constraint} interface. * * @see MapConstraints * @author Mike Bostock * @author Jared Levy * @since 3.0 */ @Beta @GwtCompatible public final class Constraints { private Constraints() {} // enum singleton pattern private enum NotNullConstraint implements Constraint<Object> { INSTANCE; @Override public Object checkElement(Object element) { return checkNotNull(element); } @Override public String toString() { return "Not null"; } } /** * Returns a constraint that verifies that the element is not null. If the * element is null, a {@link NullPointerException} is thrown. */ // safe to narrow the type since checkElement returns its argument directly @SuppressWarnings("unchecked") public static <E> Constraint<E> notNull() { return (Constraint<E>) NotNullConstraint.INSTANCE; } /** * Returns a constrained view of the specified collection, using the specified * constraint. Any operations that add new elements to the collection will * call the provided constraint. However, this method does not verify that * existing elements satisfy the constraint. * * <p>The returned collection is not serializable. * * @param collection the collection to constrain * @param constraint the constraint that validates added elements * @return a constrained view of the collection */ public static <E> Collection<E> constrainedCollection( Collection<E> collection, Constraint<? super E> constraint) { return new ConstrainedCollection<E>(collection, constraint); } /** @see Constraints#constrainedCollection */ static class ConstrainedCollection<E> extends ForwardingCollection<E> { private final Collection<E> delegate; private final Constraint<? super E> constraint; public ConstrainedCollection( Collection<E> delegate, Constraint<? super E> constraint) { this.delegate = checkNotNull(delegate); this.constraint = checkNotNull(constraint); } @Override protected Collection<E> delegate() { return delegate; } @Override public boolean add(E element) { constraint.checkElement(element); return delegate.add(element); } @Override public boolean addAll(Collection<? extends E> elements) { return delegate.addAll(checkElements(elements, constraint)); } } /** * Returns a constrained view of the specified set, using the specified * constraint. Any operations that add new elements to the set will call the * provided constraint. However, this method does not verify that existing * elements satisfy the constraint. * * <p>The returned set is not serializable. * * @param set the set to constrain * @param constraint the constraint that validates added elements * @return a constrained view of the set */ public static <E> Set<E> constrainedSet( Set<E> set, Constraint<? super E> constraint) { return new ConstrainedSet<E>(set, constraint); } /** @see Constraints#constrainedSet */ static class ConstrainedSet<E> extends ForwardingSet<E> { private final Set<E> delegate; private final Constraint<? super E> constraint; public ConstrainedSet(Set<E> delegate, Constraint<? super E> constraint) { this.delegate = checkNotNull(delegate); this.constraint = checkNotNull(constraint); } @Override protected Set<E> delegate() { return delegate; } @Override public boolean add(E element) { constraint.checkElement(element); return delegate.add(element); } @Override public boolean addAll(Collection<? extends E> elements) { return delegate.addAll(checkElements(elements, constraint)); } } /** * Returns a constrained view of the specified sorted set, using the specified * constraint. Any operations that add new elements to the sorted set will * call the provided constraint. However, this method does not verify that * existing elements satisfy the constraint. * * <p>The returned set is not serializable. * * @param sortedSet the sorted set to constrain * @param constraint the constraint that validates added elements * @return a constrained view of the sorted set */ public static <E> SortedSet<E> constrainedSortedSet( SortedSet<E> sortedSet, Constraint<? super E> constraint) { return new ConstrainedSortedSet<E>(sortedSet, constraint); } /** @see Constraints#constrainedSortedSet */ private static class ConstrainedSortedSet<E> extends ForwardingSortedSet<E> { final SortedSet<E> delegate; final Constraint<? super E> constraint; ConstrainedSortedSet( SortedSet<E> delegate, Constraint<? super E> constraint) { this.delegate = checkNotNull(delegate); this.constraint = checkNotNull(constraint); } @Override protected SortedSet<E> delegate() { return delegate; } @Override public SortedSet<E> headSet(E toElement) { return constrainedSortedSet(delegate.headSet(toElement), constraint); } @Override public SortedSet<E> subSet(E fromElement, E toElement) { return constrainedSortedSet( delegate.subSet(fromElement, toElement), constraint); } @Override public SortedSet<E> tailSet(E fromElement) { return constrainedSortedSet(delegate.tailSet(fromElement), constraint); } @Override public boolean add(E element) { constraint.checkElement(element); return delegate.add(element); } @Override public boolean addAll(Collection<? extends E> elements) { return delegate.addAll(checkElements(elements, constraint)); } } /** * Returns a constrained view of the specified list, using the specified * constraint. Any operations that add new elements to the list will call the * provided constraint. However, this method does not verify that existing * elements satisfy the constraint. * * <p>If {@code list} implements {@link RandomAccess}, so will the returned * list. The returned list is not serializable. * * @param list the list to constrain * @param constraint the constraint that validates added elements * @return a constrained view of the list */ public static <E> List<E> constrainedList( List<E> list, Constraint<? super E> constraint) { return (list instanceof RandomAccess) ? new ConstrainedRandomAccessList<E>(list, constraint) : new ConstrainedList<E>(list, constraint); } /** @see Constraints#constrainedList */ @GwtCompatible private static class ConstrainedList<E> extends ForwardingList<E> { final List<E> delegate; final Constraint<? super E> constraint; ConstrainedList(List<E> delegate, Constraint<? super E> constraint) { this.delegate = checkNotNull(delegate); this.constraint = checkNotNull(constraint); } @Override protected List<E> delegate() { return delegate; } @Override public boolean add(E element) { constraint.checkElement(element); return delegate.add(element); } @Override public void add(int index, E element) { constraint.checkElement(element); delegate.add(index, element); } @Override public boolean addAll(Collection<? extends E> elements) { return delegate.addAll(checkElements(elements, constraint)); } @Override public boolean addAll(int index, Collection<? extends E> elements) { return delegate.addAll(index, checkElements(elements, constraint)); } @Override public ListIterator<E> listIterator() { return constrainedListIterator(delegate.listIterator(), constraint); } @Override public ListIterator<E> listIterator(int index) { return constrainedListIterator(delegate.listIterator(index), constraint); } @Override public E set(int index, E element) { constraint.checkElement(element); return delegate.set(index, element); } @Override public List<E> subList(int fromIndex, int toIndex) { return constrainedList( delegate.subList(fromIndex, toIndex), constraint); } } /** @see Constraints#constrainedList */ static class ConstrainedRandomAccessList<E> extends ConstrainedList<E> implements RandomAccess { ConstrainedRandomAccessList( List<E> delegate, Constraint<? super E> constraint) { super(delegate, constraint); } } /** * Returns a constrained view of the specified list iterator, using the * specified constraint. Any operations that would add new elements to the * underlying list will be verified by the constraint. * * @param listIterator the iterator for which to return a constrained view * @param constraint the constraint for elements in the list * @return a constrained view of the specified iterator */ private static <E> ListIterator<E> constrainedListIterator( ListIterator<E> listIterator, Constraint<? super E> constraint) { return new ConstrainedListIterator<E>(listIterator, constraint); } /** @see Constraints#constrainedListIterator */ static class ConstrainedListIterator<E> extends ForwardingListIterator<E> { private final ListIterator<E> delegate; private final Constraint<? super E> constraint; public ConstrainedListIterator( ListIterator<E> delegate, Constraint<? super E> constraint) { this.delegate = delegate; this.constraint = constraint; } @Override protected ListIterator<E> delegate() { return delegate; } @Override public void add(E element) { constraint.checkElement(element); delegate.add(element); } @Override public void set(E element) { constraint.checkElement(element); delegate.set(element); } } static <E> Collection<E> constrainedTypePreservingCollection( Collection<E> collection, Constraint<E> constraint) { if (collection instanceof SortedSet) { return constrainedSortedSet((SortedSet<E>) collection, constraint); } else if (collection instanceof Set) { return constrainedSet((Set<E>) collection, constraint); } else if (collection instanceof List) { return constrainedList((List<E>) collection, constraint); } else { return constrainedCollection(collection, constraint); } } /** * Returns a constrained view of the specified multiset, using the specified * constraint. Any operations that add new elements to the multiset will call * the provided constraint. However, this method does not verify that * existing elements satisfy the constraint. * * <p>The returned multiset is not serializable. * * @param multiset the multiset to constrain * @param constraint the constraint that validates added elements * @return a constrained view of the multiset */ public static <E> Multiset<E> constrainedMultiset( Multiset<E> multiset, Constraint<? super E> constraint) { return new ConstrainedMultiset<E>(multiset, constraint); } /** @see Constraints#constrainedMultiset */ static class ConstrainedMultiset<E> extends ForwardingMultiset<E> { private Multiset<E> delegate; private final Constraint<? super E> constraint; public ConstrainedMultiset( Multiset<E> delegate, Constraint<? super E> constraint) { this.delegate = checkNotNull(delegate); this.constraint = checkNotNull(constraint); } @Override protected Multiset<E> delegate() { return delegate; } @Override public boolean add(E element) { return standardAdd(element); } @Override public boolean addAll(Collection<? extends E> elements) { return delegate.addAll(checkElements(elements, constraint)); } @Override public int add(E element, int occurrences) { constraint.checkElement(element); return delegate.add(element, occurrences); } @Override public int setCount(E element, int count) { constraint.checkElement(element); return delegate.setCount(element, count); } @Override public boolean setCount(E element, int oldCount, int newCount) { constraint.checkElement(element); return delegate.setCount(element, oldCount, newCount); } } /* * TODO(kevinb): For better performance, avoid making a copy of the elements * by having addAll() call add() repeatedly instead. */ private static <E> Collection<E> checkElements( Collection<E> elements, Constraint<? super E> constraint) { Collection<E> copy = Lists.newArrayList(elements); for (E element : copy) { constraint.checkElement(element); } return copy; } }