blob: 64d9166e8d2c2db650f7eb9a112d04fc86ea3d33 [file] [log] [blame]
/*
// Licensed to Julian Hyde under one or more contributor license
// agreements. See the NOTICE file distributed with this work for
// additional information regarding copyright ownership.
//
// Julian Hyde 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 net.hydromatic.linq4j.function;
import net.hydromatic.linq4j.Linq4j;
import java.io.Serializable;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.util.*;
/**
* Utilities relating to functions.
*/
public abstract class Functions {
private Functions() {}
public static final Map<Class<? extends Function>, Class>
FUNCTION_RESULT_TYPES =
Collections.<Class<? extends Function>, Class>unmodifiableMap(map(
Function0.class, Object.class,
Function1.class, Object.class,
Function2.class, Object.class,
BigDecimalFunction1.class, BigDecimal.class,
DoubleFunction1.class, Double.TYPE,
FloatFunction1.class, Float.TYPE,
IntegerFunction1.class, Integer.TYPE,
LongFunction1.class, Long.TYPE,
NullableBigDecimalFunction1.class, BigDecimal.class,
NullableDoubleFunction1.class, Double.class,
NullableFloatFunction1.class, Float.class,
NullableIntegerFunction1.class, Integer.class,
NullableLongFunction1.class, Long.class));
private static final Map<Class, Class<? extends Function>> FUNCTION1_CLASSES =
Collections.unmodifiableMap(
new HashMap<Class, Class<? extends Function>>(
inverse(FUNCTION_RESULT_TYPES)));
private static final Comparator NULLS_FIRST_COMPARATOR =
new NullsFirstComparator();
private static final Comparator NULLS_LAST_COMPARATOR =
new NullsLastComparator();
private static final Comparator NULLS_LAST_REVERSE_COMPARATOR =
new NullsLastReverseComparator();
private static final Comparator NULLS_FIRST_REVERSE_COMPARATOR =
new NullsFirstReverseComparator();
private static final EqualityComparer<Object> IDENTITY_COMPARER =
new IdentityEqualityComparer();
private static final EqualityComparer<Object[]> ARRAY_COMPARER =
new ArrayEqualityComparer();
private static final Function1 CONSTANT_NULL_FUNCTION1 =
new Function1() {
public Object apply(Object s) {
return null;
}
};
@SuppressWarnings("unchecked")
private static <K, V> Map<K, V> map(K k, V v, Object... rest) {
final Map<K, V> map = new HashMap<K, V>();
map.put(k, v);
for (int i = 0; i < rest.length; i++) {
map.put((K) rest[i++], (V) rest[i++]);
}
return map;
}
private static <K, V> Map<V, K> inverse(Map<K, V> map) {
HashMap<V, K> inverseMap = new HashMap<V, K>();
for (Map.Entry<K, V> entry : map.entrySet()) {
inverseMap.put(entry.getValue(), entry.getKey());
}
return inverseMap;
}
/** Returns a 1-parameter function that always returns the same value. */
public static <T, R> Function1<T, R> constant(final R r) {
return new Function1<T, R>() {
public R apply(T s) {
return r;
}
};
}
/** Returns a 1-parameter function that always returns null. */
@SuppressWarnings("unchecked")
public static <T, R> Function1<T, R> constantNull() {
return CONSTANT_NULL_FUNCTION1;
}
/**
* A predicate with one parameter that always returns {@code true}.
*
* @param <T> First parameter type
*
* @return Predicate that always returns true
*/
public static <T> Predicate1<T> truePredicate1() {
//noinspection unchecked
return (Predicate1<T>) Predicate1.TRUE;
}
/**
* A predicate with one parameter that always returns {@code true}.
*
* @param <T> First parameter type
*
* @return Predicate that always returns true
*/
public static <T> Predicate1<T> falsePredicate1() {
//noinspection unchecked
return (Predicate1<T>) Predicate1.FALSE;
}
/**
* A predicate with two parameters that always returns {@code true}.
*
* @param <T1> First parameter type
* @param <T2> Second parameter type
*
* @return Predicate that always returns true
*/
public static <T1, T2> Predicate2<T1, T2> truePredicate2() {
//noinspection unchecked
return (Predicate2<T1, T2>) Predicate2.TRUE;
}
/**
* A predicate with two parameters that always returns {@code false}.
*
* @param <T1> First parameter type
* @param <T2> Second parameter type
*
* @return Predicate that always returns false
*/
public static <T1, T2> Predicate2<T1, T2> falsePredicate2() {
//noinspection unchecked
return (Predicate2<T1, T2>) Predicate2.FALSE;
}
public static <TSource> Function1<TSource, TSource> identitySelector() {
//noinspection unchecked
return (Function1) Function1.IDENTITY;
}
/**
* Creates a predicate that returns whether an object is an instance of a
* particular type or is null.
*
* @param clazz Desired type
* @param <T> Type of objects to test
* @param <T2> Desired type
*
* @return Predicate that tests for desired type
*/
public static <T, T2> Predicate1<T> ofTypePredicate(final Class<T2> clazz) {
return new Predicate1<T>() {
public boolean apply(T v1) {
return v1 == null || clazz.isInstance(v1);
}
};
}
public static <T1, T2> Predicate2<T1, T2> toPredicate2(
final Predicate1<T1> p1) {
return new Predicate2<T1, T2>() {
public boolean apply(T1 v1, T2 v2) {
return p1.apply(v1);
}
};
}
/**
* Converts a 2-parameter function to a predicate.
*/
public static <T1, T2> Predicate2<T1, T2> toPredicate(
final Function2<T1, T2, Boolean> function) {
return new Predicate2<T1, T2>() {
public boolean apply(T1 v1, T2 v2) {
return function.apply(v1, v2);
}
};
}
/**
* Converts a 1-parameter function to a predicate.
*/
private static <T> Predicate1<T> toPredicate(
final Function1<T, Boolean> function) {
return new Predicate1<T>() {
public boolean apply(T v1) {
return function.apply(v1);
}
};
}
/**
* Returns the appropriate interface for a lambda function with
* 1 argument and the given return type.
*
* <p>For example:</p>
* functionClass(Integer.TYPE) returns IntegerFunction1.class
* functionClass(String.class) returns Function1.class
*
* @param aClass Return type
*
* @return Function class
*/
public static Class<? extends Function> functionClass(Type aClass) {
Class<? extends Function> c = FUNCTION1_CLASSES.get(aClass);
if (c != null) {
return c;
}
return Function1.class;
}
/**
* Adapts an {@link IntegerFunction1} (that returns an {@code int}) to
* an {@link Function1} returning an {@link Integer}.
*/
public static <T1> Function1<T1, Integer> adapt(
final IntegerFunction1<T1> f) {
return new Function1<T1, Integer>() {
public Integer apply(T1 a0) {
return f.apply(a0);
}
};
}
/**
* Adapts a {@link DoubleFunction1} (that returns a {@code double}) to
* an {@link Function1} returning a {@link Double}.
*/
public static <T1> Function1<T1, Double> adapt(final DoubleFunction1<T1> f) {
return new Function1<T1, Double>() {
public Double apply(T1 a0) {
return f.apply(a0);
}
};
}
/**
* Adapts a {@link LongFunction1} (that returns a {@code long}) to
* an {@link Function1} returning a {@link Long}.
*/
public static <T1> Function1<T1, Long> adapt(final LongFunction1<T1> f) {
return new Function1<T1, Long>() {
public Long apply(T1 a0) {
return f.apply(a0);
}
};
}
/**
* Adapts a {@link FloatFunction1} (that returns a {@code float}) to
* an {@link Function1} returning a {@link Float}.
*/
public static <T1> Function1<T1, Float> adapt(final FloatFunction1<T1> f) {
return new Function1<T1, Float>() {
public Float apply(T1 a0) {
return f.apply(a0);
}
};
}
/**
* Creates a view of a list that applies a function to each element.
*/
public static <T1, R> List<R> adapt(final List<T1> list,
final Function1<T1, R> f) {
return new AbstractList<R>() {
public R get(int index) {
return f.apply(list.get(index));
}
public int size() {
return list.size();
}
};
}
/**
* Creates a view of an array that applies a function to each element.
*/
public static <T, R> List<R> adapt(final T[] ts,
final Function1<T, R> f) {
return new AbstractList<R>() {
public R get(int index) {
return f.apply(ts[index]);
}
public int size() {
return ts.length;
}
};
}
/**
* Creates a copy of a list, applying a function to each element.
*/
public static <T1, R> List<R> apply(final List<T1> list,
final Function1<T1, R> f) {
final ArrayList<R> list2 = new ArrayList<R>(list.size());
for (T1 t : list) {
list2.add(f.apply(t));
}
return list2;
}
/** Returns a list that contains only elements of {@code list} that match
* {@code predicate}. Avoids allocating a list if all elements match or no
* elements match. */
public static <E> List<E> filter(List<E> list, Predicate1<E> predicate) {
sniff:
{
int hitCount = 0;
int missCount = 0;
for (E e : list) {
if (predicate.apply(e)) {
if (missCount > 0) {
break sniff;
}
++hitCount;
} else {
if (hitCount > 0) {
break sniff;
}
++missCount;
}
}
if (hitCount == 0) {
return Collections.emptyList();
}
if (missCount == 0) {
return list;
}
}
final List<E> list2 = new ArrayList<E>(list.size());
for (E e : list) {
if (predicate.apply(e)) {
list2.add(e);
}
}
return list2;
}
/** Returns whether there is an element in {@code list} for which
* {@code predicate} is true. */
public static <E> boolean exists(List<? extends E> list,
Predicate1<E> predicate) {
for (E e : list) {
if (predicate.apply(e)) {
return true;
}
}
return false;
}
/** Returns whether {@code predicate} is true for all elements of
* {@code list}. */
public static <E> boolean all(List<? extends E> list,
Predicate1<E> predicate) {
for (E e : list) {
if (!predicate.apply(e)) {
return false;
}
}
return true;
}
/** Returns a list generated by applying a function to each index between
* 0 and {@code size} - 1. */
public static <E> List<E> generate(final int size,
final Function1<Integer, E> fn) {
if (size < 0) {
throw new IllegalArgumentException();
}
return new AbstractList<E>() {
public int size() {
return size;
}
public E get(int index) {
return fn.apply(index);
}
};
}
/**
* Returns a function of arity 0 that does nothing.
*
* @param <R> Return type
* @return Function that does nothing.
*/
public static <R> Function0<R> ignore0() {
//noinspection unchecked
return Ignore.INSTANCE;
}
/**
* Returns a function of arity 1 that does nothing.
*
* @param <R> Return type
* @param <T0> Type of parameter 0
* @return Function that does nothing.
*/
public static <R, T0> Function1<R, T0> ignore1() {
//noinspection unchecked
return Ignore.INSTANCE;
}
/**
* Returns a function of arity 2 that does nothing.
*
* @param <R> Return type
* @param <T0> Type of parameter 0
* @param <T1> Type of parameter 1
* @return Function that does nothing.
*/
public static <R, T0, T1> Function2<R, T0, T1> ignore2() {
//noinspection unchecked
return Ignore.INSTANCE;
}
/**
* Returns a {@link Comparator} that handles null values.
*
* @param nullsFirst Whether nulls come before all other values
* @param reverse Whether to reverse the usual order of {@link Comparable}s
*/
@SuppressWarnings("unchecked")
public static <T extends Comparable<T>> Comparator<T> nullsComparator(
boolean nullsFirst,
boolean reverse) {
return (Comparator<T>)
(reverse
? (nullsFirst
? NULLS_FIRST_REVERSE_COMPARATOR
: NULLS_LAST_REVERSE_COMPARATOR)
: (nullsFirst
? NULLS_FIRST_COMPARATOR
: NULLS_LAST_COMPARATOR));
}
/**
* Returns an {@link EqualityComparer} that uses object identity and hash
* code.
*/
@SuppressWarnings("unchecked")
public static <T> EqualityComparer<T> identityComparer() {
return (EqualityComparer) IDENTITY_COMPARER;
}
/**
* Returns an {@link EqualityComparer} that works on arrays of objects.
*/
@SuppressWarnings("unchecked")
public static <T> EqualityComparer<T[]> arrayComparer() {
return (EqualityComparer) ARRAY_COMPARER;
}
/**
* Returns an {@link EqualityComparer} that uses a selector function.
*/
public static <T, T2> EqualityComparer<T> selectorComparer(
Function1<T, T2> selector) {
return new SelectorEqualityComparer<T, T2>(selector);
}
private static class ArrayEqualityComparer
implements EqualityComparer<Object[]> {
public boolean equal(Object[] v1, Object[] v2) {
return Arrays.equals(v1, v2);
}
public int hashCode(Object[] t) {
return Arrays.hashCode(t);
}
}
private static class IdentityEqualityComparer
implements EqualityComparer<Object> {
public boolean equal(Object v1, Object v2) {
return Linq4j.equals(v1, v2);
}
public int hashCode(Object t) {
return t == null ? 0x789d : t.hashCode();
}
}
private static final class SelectorEqualityComparer<T, T2>
implements EqualityComparer<T> {
private final Function1<T, T2> selector;
SelectorEqualityComparer(Function1<T, T2> selector) {
this.selector = selector;
}
public boolean equal(T v1, T v2) {
return v1 == v2
|| v1 != null
&& v2 != null
&& Linq4j.equals(selector.apply(v1), selector.apply(v2));
}
public int hashCode(T t) {
return t == null ? 0x789d : selector.apply(t).hashCode();
}
}
private static class NullsFirstComparator
implements Comparator<Comparable>, Serializable {
public int compare(Comparable o1, Comparable o2) {
if (o1 == o2) {
return 0;
}
if (o1 == null) {
return -1;
}
if (o2 == null) {
return 1;
}
//noinspection unchecked
return o1.compareTo(o2);
}
}
private static class NullsLastComparator
implements Comparator<Comparable>, Serializable {
public int compare(Comparable o1, Comparable o2) {
if (o1 == o2) {
return 0;
}
if (o1 == null) {
return 1;
}
if (o2 == null) {
return -1;
}
//noinspection unchecked
return o1.compareTo(o2);
}
}
private static class NullsFirstReverseComparator
implements Comparator<Comparable>, Serializable {
public int compare(Comparable o1, Comparable o2) {
if (o1 == o2) {
return 0;
}
if (o1 == null) {
return -1;
}
if (o2 == null) {
return 1;
}
//noinspection unchecked
return -o1.compareTo(o2);
}
}
private static class NullsLastReverseComparator
implements Comparator<Comparable>, Serializable {
public int compare(Comparable o1, Comparable o2) {
if (o1 == o2) {
return 0;
}
if (o1 == null) {
return 1;
}
if (o2 == null) {
return -1;
}
//noinspection unchecked
return -o1.compareTo(o2);
}
}
private static final class Ignore<R, T0, T1>
implements Function0<R>, Function1<T0, R>, Function2<T0, T1, R> {
public R apply() {
return null;
}
public R apply(T0 p0) {
return null;
}
public R apply(T0 p0, T1 p1) {
return null;
}
static final Ignore INSTANCE = new Ignore();
}
}
// End Functions.java