/*
* 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;
}
}
|