/* * Copyright (c) 2018 Pantheon Technologies, s.r.o. and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ package org.opendaylight.yangtools.util; import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; import com.google.common.annotations.Beta; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Iterators; import java.util.AbstractList; import java.util.AbstractSet; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.Spliterator; import java.util.stream.Stream; import org.eclipse.jdt.annotation.NonNullByDefault; import org.opendaylight.yangtools.concepts.Delegator; import org.opendaylight.yangtools.concepts.Immutable; /** * Utility class for adapting a {@link Collection}s to {@link Set}s and {@link List}s. * * @author Robert Varga */ @Beta @NonNullByDefault public final class CollectionWrappers { private static final class ListWrapper extends AbstractList implements Delegator> { private final Collection delegate; ListWrapper(final Collection delegate) { this.delegate = requireNonNull(delegate); } @Override public Collection getDelegate() { return delegate; } @Override public Iterator iterator() { return Iterators.unmodifiableIterator(delegate.iterator()); } @Override public int size() { return delegate.size(); } @Override public Spliterator spliterator() { return delegate.spliterator(); } @Override public Stream parallelStream() { return delegate.parallelStream(); } @Override public Stream stream() { return delegate.stream(); } @Override public E get(final int index) { return Iterables.get(delegate, index); } } private static final class SetWrapper extends AbstractSet implements Delegator> { private final Collection delegate; SetWrapper(final Collection delegate) { this.delegate = requireNonNull(delegate); } @Override public Collection getDelegate() { return delegate; } @Override public Iterator iterator() { return Iterators.unmodifiableIterator(delegate.iterator()); } @Override public int size() { return delegate.size(); } @Override public Spliterator spliterator() { return delegate.spliterator(); } @Override public Stream parallelStream() { return delegate.parallelStream(); } @Override public Stream stream() { return delegate.stream(); } } private CollectionWrappers() { } /** * Wrap the specified {@link Collection} as a {@link List}. If the collection is already a List, it is wrapped in * a {@link Collections#unmodifiableList(List)} to prevent mutability leaking. If the collection is determined * to be empty, an empty list is returned instead. If the collection is a known-immutable implementation of List * interface, it is returned unwrapped. Backing collection is required to be effectively immutable. If this * requirement is violated, the returned object may behave in unpredictable ways. * * @param the type of elements in the collection * @param collection Collection to be wrapped * @return An effectively-immutable wrapper of the collection. * @throws NullPointerException if collection is null */ public static List wrapAsList(final Collection collection) { if (collection.isEmpty()) { return ImmutableList.of(); } if (collection instanceof SetWrapper) { return wrapAsList(((SetWrapper) collection).getDelegate()); } if (collection instanceof List) { final List cast = (List) collection; return cast instanceof ListWrapper || cast instanceof Immutable || cast instanceof ImmutableList ? cast : Collections.unmodifiableList(cast); } return new ListWrapper<>(collection); } /** * Wrap the specified {@link Collection} as a {@link Set}. If the collection is already a Set, it is wrapped in * a {@link Collections#unmodifiableSet(Set)} to prevent mutability leaking. If the collection is determined * to be empty, an empty set is returned instead. If the collection is a known-immutable implementation of Set * interface, it is returned unwrapped. The collection is checked for duplicates at instantiation time, such that * it effectively implements the Set contract. Backing collection is required to be effectively immutable. If this * requirement is violated, the returned object may behave in unpredictable ways. * * @param the type of elements in the collection * @param collection Collection to be wrapped * @return An effectively-immutable wrapper of the collection. * @throws NullPointerException if collection is null or any of its elements is null * @throws IllegalArgumentException if the collection's contents do not conform to the Set contract */ public static Set wrapAsSet(final Collection collection) { if (collection.isEmpty()) { return ImmutableSet.of(); } if (collection instanceof ListWrapper) { return wrapAsSet(((ListWrapper) collection).getDelegate()); } if (collection instanceof Set) { final Set cast = (Set) collection; return cast instanceof SetWrapper || cast instanceof Immutable || cast instanceof SingletonSet || cast instanceof ImmutableSet ? cast : Collections.unmodifiableSet(cast); } final Set check = ImmutableSet.copyOf(collection); checkArgument(collection.size() == check.size(), "Supplied collection %s has duplicate elements", collection); return new SetWrapper<>(collection); } }