/*
* Copyright 2015 Goldman Sachs.
*
* 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.gs.collections.impl.factory;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import com.gs.collections.api.LazyIterable;
import com.gs.collections.api.block.function.Function;
import com.gs.collections.api.block.function.Function2;
import com.gs.collections.api.block.predicate.Predicate;
import com.gs.collections.api.block.procedure.Procedure2;
import com.gs.collections.api.factory.set.FixedSizeSetFactory;
import com.gs.collections.api.factory.set.ImmutableSetFactory;
import com.gs.collections.api.factory.set.MutableSetFactory;
import com.gs.collections.api.set.MutableSet;
import com.gs.collections.api.tuple.Pair;
import com.gs.collections.impl.block.factory.Comparators;
import com.gs.collections.impl.set.fixed.FixedSizeSetFactoryImpl;
import com.gs.collections.impl.set.immutable.ImmutableSetFactoryImpl;
import com.gs.collections.impl.set.mutable.MutableSetFactoryImpl;
import com.gs.collections.impl.set.mutable.SetAdapter;
import com.gs.collections.impl.set.mutable.UnifiedSet;
import com.gs.collections.impl.tuple.Tuples;
import com.gs.collections.impl.utility.ArrayIterate;
import com.gs.collections.impl.utility.Iterate;
import com.gs.collections.impl.utility.LazyIterate;
/**
* Set algebra operations are available in this class as static utility.
* <p>
* Most operations are non-destructive, i.e. no input sets are modified during execution.
* The exception is operations ending in "Into." These accept the target collection of
* the final calculation as the first parameter.
* <p>
* Some effort is made to return a <tt>SortedSet</tt> if any input set is sorted, but
* this is not guaranteed (e.g., this will not be the case for collections proxied by
* Hibernate). When in doubt, specify the target collection explicitly with the "Into"
* version.
*
* This class should be used to create instances of MutableSet, ImmutableSet and FixedSizeSet
* <p>
* Mutable Examples:
*
* <pre>
* MutableSet<String> emptySet = Sets.mutable.empty();
* MutableSet<String> setWith = Sets.mutable.with("a", "b", "c");
* MutableSet<String> setOf = Sets.mutable.of("a", "b", "c");
* </pre>
*
* Immutable Examples:
*
* <pre>
* ImmutableSet<String> emptySet = Sets.immutable.empty();
* ImmutableSet<String> setWith = Sets.immutable.with("a", "b", "c");
* ImmutableSet<String> setOf = Sets.immutable.of("a", "b", "c");
* </pre>
*
* FixedSize Examples:
*
* <pre>
* FixedSizeList<String> emptySet = Sets.fixedSize.empty();
* FixedSizeList<String> setWith = Sets.fixedSize.with("a", "b", "c");
* FixedSizeList<String> setOf = Sets.fixedSize.of("a", "b", "c");
* </pre>
*/
@SuppressWarnings("ConstantNamingConvention")
public final class Sets
{
public static final ImmutableSetFactory immutable = new ImmutableSetFactoryImpl();
public static final FixedSizeSetFactory fixedSize = new FixedSizeSetFactoryImpl();
public static final MutableSetFactory mutable = new MutableSetFactoryImpl();
private static final Predicate<Set<?>> INSTANCE_OF_SORTED_SET_PREDICATE = new Predicate<Set<?>>()
{
public boolean accept(Set<?> set)
{
return set instanceof SortedSet;
}
};
private static final Predicate<Set<?>> HAS_NON_NULL_COMPARATOR = new Predicate<Set<?>>()
{
public boolean accept(Set<?> set)
{
return ((SortedSet<?>) set).comparator() != null;
}
};
private Sets()
{
throw new AssertionError("Suppress default constructor for noninstantiability");
}
public static <E> MutableSet<E> union(
Set<? extends E> setA,
Set<? extends E> setB)
{
return unionInto(newSet(setA, setB), setA, setB);
}
public static <E, R extends Set<E>> R unionInto(
R targetSet,
Set<? extends E> setA,
Set<? extends E> setB)
{
return setA.size() > setB.size() ? fillSet(targetSet, Sets.<E, R>addAllProcedure(), setA, setB)
: fillSet(targetSet, Sets.<E, R>addAllProcedure(), setB, setA);
}
public static <E> MutableSet<E> unionAll(Set<? extends E>... sets)
{
return unionAllInto(newSet(sets), sets);
}
public static <E, R extends Set<E>> R unionAllInto(
R targetSet,
Set<? extends E>... sets)
{
Arrays.sort(sets, 0, sets.length, Comparators.descendingCollectionSizeComparator());
return fillSet(targetSet, Sets.<E, R>addAllProcedure(), sets);
}
public static <E> MutableSet<E> intersect(
Set<? extends E> setA,
Set<? extends E> setB)
{
return intersectInto(newSet(setA, setB), setA, setB);
}
public static <E, R extends Set<E>> R intersectInto(
R targetSet,
Set<? extends E> setA,
Set<? extends E> setB)
{
return setA.size() < setB.size() ? fillSet(targetSet, Sets.<E, R>retainAllProcedure(), setA, setB)
: fillSet(targetSet, Sets.<E, R>retainAllProcedure(), setB, setA);
}
public static <E> MutableSet<E> intersectAll(Set<? extends E>... sets)
{
return intersectAllInto(newSet(sets), sets);
}
public static <E, R extends Set<E>> R intersectAllInto(
R targetSet,
Set<? extends E>... sets)
{
Arrays.sort(sets, 0, sets.length, Comparators.ascendingCollectionSizeComparator());
return fillSet(targetSet, Sets.<E, R>retainAllProcedure(), sets);
}
public static <E> MutableSet<E> difference(
Set<? extends E> minuendSet,
Set<? extends E> subtrahendSet)
{
return differenceInto(newSet(minuendSet, subtrahendSet), minuendSet, subtrahendSet);
}
public static <E, R extends Set<E>> R differenceInto(
R targetSet,
Set<? extends E> minuendSet,
Set<? extends E> subtrahendSet)
{
return fillSet(targetSet, Sets.<E, R>removeAllProcedure(), minuendSet, subtrahendSet);
}
public static <E> MutableSet<E> differenceAll(Set<? extends E>... sets)
{
return differenceAllInto(newSet(sets), sets);
}
public static <E, R extends Set<E>> R differenceAllInto(
R targetSet,
Set<? extends E>... sets)
{
return fillSet(targetSet, Sets.<E, R>removeAllProcedure(), sets);
}
public static <E> MutableSet<E> symmetricDifference(
Set<? extends E> setA,
Set<? extends E> setB)
{
return symmetricDifferenceInto(newSet(setA, setB), setA, setB);
}
public static <E, R extends Set<E>> R symmetricDifferenceInto(
R targetSet,
Set<? extends E> setA,
Set<? extends E> setB)
{
return unionInto(
targetSet,
differenceInto(newSet(setA, setB), setA, setB),
differenceInto(newSet(setA, setB), setB, setA));
}
public static <E> boolean isSubsetOf(
Set<? extends E> candidateSubset,
Set<? extends E> candidateSuperset)
{
return candidateSubset.size() <= candidateSuperset.size()
&& candidateSuperset.containsAll(candidateSubset);
}
public static <E> boolean isProperSubsetOf(
Set<? extends E> candidateSubset,
Set<? extends E> candidateSuperset)
{
return candidateSubset.size() < candidateSuperset.size()
&& candidateSuperset.containsAll(candidateSubset);
}
private static <E> MutableSet<E> newSet(Set<? extends E>... sets)
{
Comparator<? super E> comparator = extractComparator(sets);
if (comparator != null)
{
// TODO: this should return a SortedSetAdapter once implemented
return SetAdapter.adapt(new TreeSet<E>(comparator));
}
return UnifiedSet.newSet();
}
private static <E> Comparator<? super E> extractComparator(Set<? extends E>... sets)
{
Collection<Set<? extends E>> sortedSetCollection = ArrayIterate.select(sets, INSTANCE_OF_SORTED_SET_PREDICATE);
if (sortedSetCollection.isEmpty())
{
return null;
}
SortedSet<E> sortedSetWithComparator = (SortedSet<E>) Iterate.detect(sortedSetCollection, HAS_NON_NULL_COMPARATOR);
if (sortedSetWithComparator != null)
{
return sortedSetWithComparator.comparator();
}
return Comparators.safeNullsLow(Comparators.naturalOrder());
}
private static <E, R extends Set<E>> R fillSet(
R targetSet,
Procedure2<Set<? extends E>, R> procedure,
Set<? extends E>... sets)
{
targetSet.addAll(sets[0]);
for (int i = 1; i < sets.length; i++)
{
procedure.value(sets[i], targetSet);
}
return targetSet;
}
private static <E, R extends Set<E>> Procedure2<Set<? extends E>, R> addAllProcedure()
{
return new Procedure2<Set<? extends E>, R>()
{
public void value(Set<? extends E> argumentSet, R targetSet)
{
targetSet.addAll(argumentSet);
}
};
}
private static <E, R extends Set<E>> Procedure2<Set<? extends E>, R> retainAllProcedure()
{
return new Procedure2<Set<? extends E>, R>()
{
public void value(Set<? extends E> argumentSet, R targetSet)
{
targetSet.retainAll(argumentSet);
}
};
}
private static <E, R extends Set<E>> Procedure2<Set<? extends E>, R> removeAllProcedure()
{
return new Procedure2<Set<? extends E>, R>()
{
public void value(Set<? extends E> argumentSet, R targetSet)
{
targetSet.removeAll(argumentSet);
}
};
}
public static <T> MutableSet<MutableSet<T>> powerSet(Set<T> set)
{
MutableSet<MutableSet<T>> seed = UnifiedSet.<MutableSet<T>>newSetWith(UnifiedSet.<T>newSet());
return Iterate.injectInto(seed, set, new Function2<MutableSet<MutableSet<T>>, T, MutableSet<MutableSet<T>>>()
{
public MutableSet<MutableSet<T>> value(MutableSet<MutableSet<T>> accumulator, final T element)
{
return Sets.union(accumulator, accumulator.collect(new Function<MutableSet<T>, MutableSet<T>>()
{
public MutableSet<T> valueOf(MutableSet<T> innerSet)
{
return innerSet.toSet().with(element);
}
}));
}
});
}
public static <A, B> LazyIterable<Pair<A, B>> cartesianProduct(Set<A> set1, final Set<B> set2)
{
return LazyIterate.flatCollect(set1, new Function<A, LazyIterable<Pair<A, B>>>()
{
public LazyIterable<Pair<A, B>> valueOf(final A first)
{
return LazyIterate.collect(set2, new Function<B, Pair<A, B>>()
{
public Pair<A, B> valueOf(B second)
{
return Tuples.pair(first, second);
}
});
}
});
}
}