/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.geotoolkit.util.collection;
import java.util.*;
import java.io.Serializable;
import org.geotoolkit.lang.Static;
import org.apache.sis.util.collection.Containers;
import org.apache.sis.internal.util.CollectionsExt;
/**
* Static methods working on {@link Collection} objects.
* This is an extension to the Java {@link Collections} and {@link Containers} utility classes.
*
* @author Martin Desruisseaux (IRD, Geomatys)
* @version 3.22
*
* @since 3.10 (derived from 3.00)
* @module
*/
public final class XCollections extends Static {
/**
* Do not allow instantiation of this class.
*/
private XCollections() {
}
/**
* Clears the given collection, if non-null. If the collection is null, then this method does
* nothing. This is a convenience method when a null collection is a synonymous of empty.
*
* @param collection The collection to clear, or {@code null}.
*
* @since 3.18
*/
public static void clear(final Collection<?> collection) {
if (collection != null) {
collection.clear();
}
}
/**
* Clears the given map, if non-null. If the map is null, then this method does nothing.
* This is a convenience method when a null map is a synonymous of empty.
*
* @param map The map to clear, or {@code null}.
*
* @since 3.18
*/
public static void clear(final Map<?,?> map) {
if (map != null) {
map.clear();
}
}
/**
* Adds the given element to the given collection only if the element is non-null.
* If any of the given argument is null, then this method does nothing.
*
* @param <E> The type of elements in the collection.
* @param collection The collection in which to add elements, or {@code null}.
* @param element The element to add in the collection, or {@code null}.
* @return {@code true} if the given element has been added, or {@code false} otherwise.
*
* @since 3.20
*/
public static <E> boolean addIfNonNull(final Collection<E> collection, final E element) {
return (collection != null && element != null) && collection.add(element);
}
/**
* Returns the specified array as an immutable set, or {@code null} if the array is null.
* If the given array contains duplicated elements, i.e. elements that are equal in the
* sense of {@link Object#equals(Object)}, then only the last instance of the duplicated
* values will be included in the returned set.
*
* @param <E> The type of array elements.
* @param array The array to copy in a set. May be {@code null}.
* @return A set containing the array elements, or {@code null} if the given array was null.
*
* @see Collections#unmodifiableSet(Set)
*
* @since 3.17
*/
@SafeVarargs
public static <E> Set<E> immutableSet(final E... array) {
return CollectionsExt.immutableSet(false, array);
}
/**
* Returns a unmodifiable version of the given set.
* This method is different than the standard {@link Collections#unmodifiableSet(Set)}
* in that it tries to returns a more efficient object when there is zero or one element.
* Such small set occurs frequently in Apache SIS, especially for
* {@link org.apache.sis.referencing.AbstractIdentifiedObject} names or identifiers.
*
* <p><em>The set returned by this method may or may not be a view of the given set</em>.
* Consequently this method shall be used <strong>only</strong> if the given set will
* <strong>not</strong> be modified after this method call. In case of doubt, use the
* standard {@link Collections#unmodifiableSet(Set)} method instead.</p>
*
* @param <E> The type of elements in the set.
* @param set The set to make unmodifiable, or {@code null}.
* @return A unmodifiable version of the given set, or {@code null} if the given set was null.
*/
public static <E> Set<E> unmodifiableOrCopy(Set<E> set) {
return CollectionsExt.unmodifiableOrCopy(set);
}
/**
* Returns a unmodifiable version of the given map.
* This method is different than the standard {@link Collections#unmodifiableMap(Map)}
* in that it tries to returns a more efficient object when there is zero or one entry.
* Such small maps occur frequently in Apache SIS.
*
* <p><em>The map returned by this method may or may not be a view of the given map</em>.
* Consequently this method shall be used <strong>only</strong> if the given map will
* <strong>not</strong> be modified after this method call. In case of doubt, use the
* standard {@link Collections#unmodifiableMap(Map)} method instead.</p>
*
* @param <K> The type of keys in the map.
* @param <V> The type of values in the map.
* @param map The map to make unmodifiable, or {@code null}.
* @return A unmodifiable version of the given map, or {@code null} if the given map was null.
*/
public static <K,V> Map<K,V> unmodifiableOrCopy(Map<K,V> map) {
return CollectionsExt.unmodifiableOrCopy(map);
}
/**
* The comparator to be returned by {@link Collections#listComparator()} and similar methods.
*/
private static final class Compare<T extends Comparable<T>>
implements Comparator<Collection<T>>, Serializable
{
/**
* For cross-version compatibility.
*/
private static final long serialVersionUID = 7050753365408754641L;
/**
* The unique instance. Can not be public because of parameterized types: we need a method
* for casting to the expected type. This is the same trick than the one used by the JDK
* in the {@link Collections#emptySet()} method for instance.
*/
@SuppressWarnings("rawtypes")
static final Comparator INSTANCE = new Compare();
/**
* Do not allow instantiation other than the unique {@link #INSTANCE}.
*/
private Compare() {
}
/**
* Compares two collections of comparable objects.
*/
@Override
public int compare(final Collection<T> c1, final Collection<T> c2) {
final Iterator<T> i1 = c1.iterator();
final Iterator<T> i2 = c2.iterator();
int c;
do {
final boolean h1 = i1.hasNext();
final boolean h2 = i2.hasNext();
if (!h1) return h2 ? -1 : 0;
if (!h2) return +1;
final T e1 = i1.next();
final T e2 = i2.next();
c = e1.compareTo(e2);
} while (c == 0);
return c;
}
};
/**
* Returns a comparator for lists of comparable elements. The first element of each list are
* {@linkplain Comparable#compareTo(Object) compared}. If one is <cite>greater than</cite> or
* <cite>less than</cite> the other, the result of that comparison is returned. Otherwise
* the second element are compared, and so on until either non-equal elements are found,
* or end-of-list are reached. In the later case, the shortest list is considered
* <cite>less than</cite> the longest one.
*
* <p>If both lists have the same length and equal elements in the sense of
* {@link Comparable#compareTo}, then the comparator returns 0.</p>
*
* @param <T> The type of elements in both lists.
* @return The ordering between two lists.
*
* @since 3.18 (derived from 2.5)
*/
@SuppressWarnings("unchecked")
public static <T extends Comparable<T>> Comparator<List<T>> listComparator() {
return Compare.INSTANCE;
}
/**
* Returns a comparator for sorted sets of comparable elements. The first element of each set
* are {@linkplain Comparable#compareTo(Object) compared}. If one is <cite>greater than</cite>
* or <cite>less than</cite> the other, the result of that comparison is returned. Otherwise
* the second element are compared, and so on until either non-equal elements are found,
* or end-of-set are reached. In the later case, the smallest set is considered
* <cite>less than</cite> the largest one.
*
* {@note There is no method accepting an arbitrary <code>Set</code> or <code>Collection</code>
* argument because this comparator makes sense only for collections having determinist
* iteration order.}
*
* @param <T> The type of elements in both sets.
* @return The ordering between two sets.
*/
@SuppressWarnings("unchecked")
public static <T extends Comparable<T>> Comparator<SortedSet<T>> sortedSetComparator() {
return Compare.INSTANCE;
}
/**
* The comparator to be returned by {@link Collections#valueComparator()}.
*/
private static final class ValueComparator<K,V extends Comparable<V>>
implements Comparator<Map.Entry<K,V>>, Serializable
{
/**
* For cross-version compatibility.
*/
private static final long serialVersionUID = 807166038568740444L;
/**
* The unique instance. Can not be public because of parameterized types: we need a method
* for casting to the expected type. This is the same trick than the one used by the JDK
* in the {@link Collections#emptySet()} method for instance.
*/
@SuppressWarnings("rawtypes")
static final ValueComparator INSTANCE = new ValueComparator();
/**
* Do not allow instantiation other than the unique {@link #INSTANCE}.
*/
private ValueComparator() {
}
/**
* Compares the values of two entries.
*/
@Override
public int compare(final Map.Entry<K,V> e1, final Map.Entry<K,V> e2) {
return e1.getValue().compareTo(e2.getValue());
}
}
/**
* Returns a comparator for map entries having comparable {@linkplain java.util.Map.Entry#getValue() values}.
* For any pair of entries {@code e1} and {@code e2}, this method performs the comparison as below:
*
* {@preformat java
* return e1.getValue().compareTo(e2.getValue());
* }
*
* This comparator can be used as a complement to {@link SortedSet}. While {@code SortedSet}
* maintains keys ordering at all time, {@code valueComparator()} is typically used only at
* the end of a process in which the values are the numerical calculation results.
*
* @param <K> The type of keys in the map entries.
* @param <V> The type of values in the map entries.
* @return A comparator for the values of the given type.
*/
@SuppressWarnings("unchecked")
public static <K,V extends Comparable<V>> Comparator<Map.Entry<K,V>> valueComparator() {
return ValueComparator.INSTANCE;
}
}