All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.xlrit.gears.base.function.Operators Maven / Gradle / Ivy

There is a newer version: 1.17.5
Show newest version
package com.xlrit.gears.base.function;

import java.math.BigDecimal;
import java.math.MathContext;
import java.time.LocalDate;
import java.time.OffsetDateTime;
import java.time.Period;
import java.time.temporal.Temporal;
import java.time.temporal.UnsupportedTemporalTypeException;
import java.util.*;
import java.util.function.Function;
import java.util.function.Supplier;

import ch.obermuhlner.math.big.BigDecimalMath;
import com.google.common.base.Preconditions;
import com.google.common.collect.Iterables;
import com.xlrit.gears.base.meta.HasAbsentValue;
import com.xlrit.gears.base.util.NumericUtils;
import org.threeten.extra.PeriodDuration;

import static com.google.common.base.Preconditions.checkArgument;
import static com.xlrit.gears.base.function.Optional.optional;
import static com.xlrit.gears.base.util.NumericUtils.toBigDecimal;
import static com.xlrit.gears.base.util.NumericUtils.toLong;

public class Operators {
	private static final MathContext mathContext = MathContext.DECIMAL64;

	// ============ converted operators ================

	// 	case sn.Operator.EQEQ       => "=="

	public static Boolean eq(BigDecimal a, BigDecimal b) {
		return a == null || b == null ? null : a.compareTo(b) == 0; // NOTE compareTo ignores precision, so 0.0 == 0.00
	}

	public static Boolean eq(Long a, Long b) {
		return a == null || b == null ? null : a.longValue() == b.longValue();
	}

	public static Boolean eq(Number a, Number b) {
		return a == null || b == null ? null : NumericUtils.compare(a, b) == 0;
	}

	public static Boolean eq(Object a, Object b) {
		return a == null || b == null ? null : a.equals(b);
	}

	//	case sn.Operator.NEQ        => "!="

	public static Boolean ne(BigDecimal a, BigDecimal b) {
		return a == null || b == null ? null : a.compareTo(b) != 0; // NOTE compareTo ignores precision, so 0.0 == 0.00
	}

	public static Boolean ne(Long a, Long b) {
		return a == null || b == null ? null : a.longValue() != b.longValue();
	}

	public static  Boolean ne(A a, B b) {
		return a == null || b == null ? null : NumericUtils.compare(a, b) != 0;
	}

	public static Boolean ne(Object a, Object b) {
		return a == null || b == null ? null : !a.equals(b);
	}

	//	case sn.Operator.LT         => "<"

	public static Boolean lt(Number a, Number b) {
		return a == null || b == null ? null : NumericUtils.compare(a, b) < 0;
	}

	public static > Boolean lt(A a, A b) {
		return optional(a, b, (x, y) -> x.compareTo(y) < 0);
	}

	public static Boolean lt(String a, String b) {
		return optional(a, b, (x, y) -> x.compareTo(y) < 0);
	}

	//	case sn.Operator.LE         => "<="

	public static Boolean le(Number a, Number b) {
		return a == null || b == null ? null : NumericUtils.compare(a, b) <= 0;
	}

	public static > Boolean le(A a, A b) {
		return optional(a, b, (x, y) -> x.compareTo(y) <= 0);
	}

	public static Boolean le(String a, String b) {
		return optional(a, b, (x, y) -> x.compareTo(y) <= 0);
	}

	//	case sn.Operator.GT         => ">"

	public static Boolean gt(Number a, Number b) {
		return a == null || b == null ? null : NumericUtils.compare(a, b) > 0;
	}

	public static > Boolean gt(A a, A b) {
		return optional(a, b, (x, y) -> x.compareTo(y) > 0);
	}

	public static Boolean gt(String a, String b) {
		return optional(a, b, (x, y) -> x.compareTo(y) > 0);
	}

	//	case sn.Operator.GE         => ">="

	public static Boolean ge(Number a, Number b) {
		return a == null || b == null ? null : NumericUtils.compare(a, b) >= 0;
	}

	public static > Boolean ge(A a, A b) {
		return optional(a, b, (x, y) -> x.compareTo(y) >= 0);
	}

	public static Boolean ge(String a, String b) {
		return optional(a, b, (x, y) -> x.compareTo(y) >= 0);
	}

	//	case sn.Operator.PLUS       => "+"

	public static  BigDecimal sum(A a, B b) {
		return optional(a, b, (x, y) -> sum(toBigDecimal(x), toBigDecimal(y)));
	}

	public static BigDecimal sum(BigDecimal a, BigDecimal b) {
		return optional(a, b, BigDecimal::add);
	}

	public static Long sum(Integer a, Integer b) {
		return optional(a, b, (x, y) -> sum(Long.valueOf(x), Long.valueOf(y)));
	}

	public static Long sum(Long a, Integer b) {
		return optional(a, b, (x, y) -> sum(x, Long.valueOf(y)));
	}

	public static Long sum(Integer a, Long b) {
		return optional(a, b, (x, y) -> sum(Long.valueOf(x), y));
	}

	public static Long sum(Long a, Long b) {
		return optional(a, b, Long::sum);
	}

	public static  A add(A a, PeriodDuration b) {
		try {
			return optional(a, b, (x, y) -> (A) (x.plus(y)));
		} catch (UnsupportedTemporalTypeException e) {
			String msg = String.format("Could not add period '%s' to %s '%s'. " + e.getMessage(), b, a.getClass().getSimpleName(), a);
			throw new IllegalArgumentException(msg, e);
		}
	}

	public static String concat(String a, String b) {
		return optional(a, b, String::concat);
	}

	public static  List add(List collection, T element) {
		// TODO: what if this function is called in a loop?
		List result = new ArrayList<>(collection.size() + 1);
		result.addAll(collection);
		result.add(element);
		return result;
	}

	public static  List concat(List collection1, List collection2) {
		List result = new ArrayList<>(collection1.size() + collection2.size());
		result.addAll(collection1);
		result.addAll(collection2);
		return result;
	}

	//	case sn.Operator.MINUS      => "-"

	public static  BigDecimal subtract(A a, B b) {
		return subtract(toBigDecimal(a), toBigDecimal(b));
	}

	public static BigDecimal subtract(BigDecimal a, BigDecimal b) {
		return optional(a, b, BigDecimal::subtract);
	}

	public static Long subtract(Integer a, Integer b) {
		return optional(a, b, (x, y) -> subtract(Long.valueOf(x), Long.valueOf(y)));
	}

	public static Long subtract(Long a, Integer b) {
		return optional(a, b, (x, y) -> subtract(x, Long.valueOf(y)));
	}

	public static Long subtract(Integer a, Long b) {
		return optional(a, b, (x, y) -> subtract(Long.valueOf(x), y));
	}

	public static Long subtract(Long a, Long b) {
		return optional(a, b, (x, y) -> x - y);
	}

	public static  A subtract(A a, PeriodDuration b) {
		try {
			return optional(a, b, (x, y) -> (A) (x.minus(y)));
		} catch (UnsupportedTemporalTypeException e) {
			String msg = String.format("Could not subtract period '%s' from %s '%s'. " + e.getMessage(), b, a.getClass().getSimpleName(), a);
			throw new IllegalArgumentException(msg, e);
		}
	}

	public static  PeriodDuration subtract(A a, B b) {
		try {
			if (a == null || b == null) return null;
			return PeriodDuration.between(b, a); // NOTE the arguments order is reversed
		}
		catch (UnsupportedTemporalTypeException e) {
			String msg = String.format("Could not subtract period '%s' from %s '%s'. " + e.getMessage(), b, a.getClass().getSimpleName(), a);
			throw new IllegalArgumentException(msg, e);
		}
	}

	public static PeriodDuration subtract(OffsetDateTime a, LocalDate b) {
		if (a == null || b == null) return null;
		return PeriodDuration.between(b, a); // NOTE the arguments order is reversed
	}

	public static PeriodDuration subtract(LocalDate a, OffsetDateTime b) {
		if (a == null || b == null) return null;
		return PeriodDuration.between(b, a); // NOTE the arguments order is reversed
	}

	//	case sn.Operator.MULT       => "*"

	public static  BigDecimal multiply(A a, B b) {
		return multiply(toBigDecimal(a), toBigDecimal(b));
	}

	public static BigDecimal multiply(BigDecimal a, BigDecimal b) {
		return optional(a, b, (x, y) -> x.multiply(y, mathContext));
	}

	public static Long multiply(Integer a, Integer b) {
		return optional(a, b, (x, y) -> multiply(Long.valueOf(x), Long.valueOf(y)));
	}

	public static Long multiply(Long a, Integer b) {
		return optional(a, b, (x, y) -> multiply(x, Long.valueOf(y)));
	}

	public static Long multiply(Integer a, Long b) {
		return optional(a, b, (x, y) -> multiply(Long.valueOf(x), y));
	}

	public static Long multiply(Long a, Long b) {
		return optional(a, b, (x, y) -> x * y);
	}

	public static String multiply(String a, Integer b) {
		return optional(a, b, (x, y) -> multiply(x, Long.valueOf(y)));
	}

	public static String multiply(String a, Long b) {
		return optional(a, b, (x, y) -> x.repeat(y.intValue()));
	}

	public static PeriodDuration multiply(PeriodDuration a, Long b) {
		if (a == null || b == null) return null;
		checkArgument(b >= Integer.MIN_VALUE, "Number too small to multiply a PeriodDuration with: " + b);
		checkArgument(b <= Integer.MAX_VALUE, "Number too large to multiply a PeriodDuration with: " + b);
		return a.multipliedBy(b.intValue());
	}

	//	case sn.Operator.DIV        => "/"

	public static  BigDecimal div(A a, B b) {
		return div(toBigDecimal(a), toBigDecimal(b));
	}

	public static BigDecimal div(BigDecimal a, BigDecimal b) {
		return optional(a, b, (x, y) -> x.divide(y, mathContext));
	}

	public static PeriodDuration div(PeriodDuration a, Long b) {
		if (a == null || b == null) return null;
		checkArgument(a.getPeriod().isZero(), "Division on PeriodDuration is only supported if the Period component is zero: " + a);
		return PeriodDuration.of(Period.ZERO, a.getDuration().dividedBy(b));
	}

	//	case sn.Operator.EDIV       => "/"

	public static  BigDecimal ediv(A a, B b) {
		return ediv(toBigDecimal(a), toBigDecimal(b));
	}

	public static BigDecimal ediv(BigDecimal a, BigDecimal b) {
		return optional(a, b, BigDecimal::divideToIntegralValue);
	}

	public static Long ediv(Integer a, Integer b) {
		return ediv(Long.valueOf(a), Long.valueOf(b));
	}

	public static Long ediv(Long a, Integer b) {
		return ediv(a, Long.valueOf(b));
	}

	public static Long ediv(Integer a, Long b) {
		return ediv(Long.valueOf(a), b);
	}

	public static Long ediv(Long a, Long b) {
		return optional(a, b, (x, y) -> x / y);
	}

	//	case sn.Operator.MOD        => "%"

	public static  BigDecimal mod(A a, B b) {
		return mod(BigDecimal.valueOf(a.doubleValue()), BigDecimal.valueOf(b.doubleValue()));
	}

	public static BigDecimal mod(BigDecimal a, BigDecimal b) {
		return optional(a, b, BigDecimal::remainder);
	}

	public static Long mod(Integer a, Integer b) {
		return mod(Long.valueOf(a), Long.valueOf(b));
	}

	public static Long mod(Long a, Integer b) {
		return mod(a, Long.valueOf(b));
	}

	public static Long mod(Integer a, Long b) {
		return mod(Long.valueOf(a), b);
	}

	public static Long mod(Long a, Long b) {
		return optional(a, b, (x, y) -> x % y);
	}

	//	case sn.Operator.OR         => "||"

	public static Boolean or(Boolean a, Boolean b) {
		if (a == Boolean.TRUE) return Boolean.TRUE;
		if (b == Boolean.TRUE) return Boolean.TRUE;
		if (a == null || b == null) return null;
		return Boolean.FALSE;
	}

	public static Boolean or(Boolean a, Supplier bSupplier) {
		if (a == Boolean.TRUE) return Boolean.TRUE;
		Boolean b = bSupplier.get();
		if (b == Boolean.TRUE) return Boolean.TRUE;
		if (a == null || b == null) return null;
		return Boolean.FALSE;
	}

	//	case sn.Operator.AND        => "&&"

	public static Boolean and(Boolean a, Boolean b) {
		if (a == Boolean.FALSE) return Boolean.FALSE;
		if (b == Boolean.FALSE) return Boolean.FALSE;
		if (a == null || b == null) return null;
		return Boolean.TRUE;
	}

	public static Boolean and(Boolean a, Supplier bSupplier) {
		if (a == Boolean.FALSE) return Boolean.FALSE;
		Boolean b = bSupplier.get();
		if (b == Boolean.FALSE) return Boolean.FALSE;
		if (a == null || b == null) return null;
		return Boolean.TRUE;
	}

	//	case sn.Operator.LIKE       => "like"

	public static Boolean like(String str, String likeRegex) {
		Preconditions.checkNotNull(likeRegex);
		return optional(str, DefaultFunctions.likeToRegex(likeRegex), String::matches);
	}

	//	case sn.Operator.POW        => "pow"

	public static  BigDecimal pow(A a, B b) {
		return pow(BigDecimal.valueOf(a.doubleValue()), BigDecimal.valueOf(b.doubleValue()));
	}

	public static BigDecimal pow(BigDecimal a, BigDecimal b) {
		return optional(a, b, (x, y) -> BigDecimalMath.pow(x, y, mathContext));
	}

	public static Long pow(Integer a, Integer b) {
		return pow(Long.valueOf(a), Long.valueOf(b));
	}

	public static Long pow(Long a, Integer b) {
		return pow(a, Long.valueOf(b));
	}

	public static Long pow(Integer a, Long b) {
		return pow(Long.valueOf(a), b);
	}

	public static Long pow(Long a, Long b) {
		return optional(a, b, (x, y) -> toLong(pow(BigDecimal.valueOf(x), y)));
	}

	//	case sn.Operator.IN         => "in"

	public static  Boolean in(B element, Collection collection) {
		return contains(collection, element);
	}

	//	case sn.Operator.CONTAINS   => "contains"

	public static  Boolean contains(Collection collection, B element) {
		return collection == null || element == null ? null : collection.contains(element);
	}

	//	case sn.Operator.INTERSECTS => "intersects"

	public static  Boolean intersects(Collection a, Collection b) {
		try {
			return b.containsAll(a);
		} catch (NullPointerException e) {
			return null;
		}
	}

	// case sn.Operator.OTHERWISE

	public static  T otherwise(T value, Supplier alternativeSupplier) {
		Objects.requireNonNull(alternativeSupplier, "The alternative value supplier must not be null");
		return value != null ? value : Objects.requireNonNull(alternativeSupplier.get(), "The alternative value must not be null");
	}

	// ========== unary functions =========

	// case (MINUS,   NumericType()) => getNullableMethodRef("flip")

	public static BigDecimal flip(BigDecimal a) {
		return optional(a, BigDecimal::negate);
	}

	public static Long flip(Integer a) {
		return flip(Long.valueOf(a));
	}

	public static Long flip(Long a) {
		return optional(a, x -> -1 * x);
	}

	// case (NOT,     BooleanType)   => getNullableMethodRef("not")

	public static Boolean not(Boolean a) {
		return optional(a, x -> !x);
	}

	// case (EXISTS,  _)             => getNullableMethodRef("exists")

	public static  boolean exists(Collection a) {
		return a != null && !a.isEmpty();
	}

	public static  boolean exists(A a) {
		return a != null;
	}

	// case (NEXISTS, _)             => getNullableMethodRef("nexists")

	public static  boolean nexists(Collection a) {
		return a == null || a.isEmpty();
	}

	public static  boolean nexists(A a) {
		return a == null;
	}

	// ========== boolean evaluation =========

	public static boolean isTrue(Boolean b) {
		return b == Boolean.TRUE;
	}

	public static boolean isFalse(Boolean b) {
		return b == Boolean.FALSE;
	}

	public static boolean isUndefined(Boolean b) {
		return b == null;
	}

	// ========== kind of the-expression =========

	/**
	 * @return null if collection is empty.
	 * @throws IllegalArgumentException if collection has more than 1 element.
	 */
	public static  T only(Collection collection) {
		if (collection == null || collection.isEmpty()) return null;
		if (collection.size() != 1) throw new IllegalArgumentException("The collection must have exactly 1 element, but it has " + collection.size());
		return collection.iterator().next();
	}

	/**
	 * @return result of alternativeSupplier if collection is empty.
	 * @throws IllegalArgumentException if collection has more than 1 element.
	 */
	public static  T only(Collection collection, Supplier alternativeSupplier) {
		if (collection == null || collection.isEmpty()) return alternativeSupplier.get();
		if (collection.size() != 1) throw new IllegalArgumentException("The collection must have exactly 0 or 1 element, but it has " + collection.size());
		return collection.iterator().next();
	}

	/**
	 * @return null if collection is empty.
	 */
	public static  T first(Collection collection) {
		if (collection == null || collection.isEmpty()) return null;
		return collection.iterator().next();
	}

	/**
	 * @return result of alternativeSupplier if collection is empty.
	 */
	public static  T first(Collection collection, Supplier alternativeSupplier) {
		if (collection == null || collection.isEmpty()) return alternativeSupplier.get();
		return collection.iterator().next();
	}

	/**
	 * @return null if collection is empty.
	 */
	public static  T last(Collection collection) {
		if (collection == null || collection.isEmpty()) return null;
		return Iterables.getLast(collection);
	}

	/**
	 * @return result of alternativeSupplier if collection is empty.
	 */
	public static  T last(Collection collection, Supplier alternativeSupplier) {
		if (collection == null || collection.isEmpty()) return alternativeSupplier.get();
		return Iterables.getLast(collection);
	}

	// ========== sorting supoprt =========== //

	private static final Comparator ASCENDING  = Comparator.nullsLast(Comparator.naturalOrder());
	private static final Comparator DESCENDING = Comparator.nullsLast(Comparator.reverseOrder());

	@SuppressWarnings("unchecked")
	public static > Comparator ascending() {
		return (Comparator) ASCENDING;
	}

	@SuppressWarnings("unchecked")
	public static > Comparator descending() {
		return (Comparator) DESCENDING;
	}

	// ========== attribute access ========= //

	public static  T2 access(
			T1 start,
			Function access) {
		return chain(start, access);
	}

	public static  T3 access(
			T1 start,
			Function access1,
			Function access2) {
		return chain(start, access1, access2);
	}

	public static  T4 access(
			T1 start,
			Function access1,
			Function access2,
			Function access3) {
		return chain(start, access1, access2, access3);
	}

	public static  T5 access(
			T1 start,
			Function access1,
			Function access2,
			Function access3,
			Function access4) {
		return chain(start, access1, access2, access3, access4);
	}

	public static  T6 access(
			T1 start,
			Function access1,
			Function access2,
			Function access3,
			Function access4,
			Function access5) {
		return chain(start, access1, access2, access3, access4, access5);
	}

	public static  T7 access(
			T1 start,
			Function access1,
			Function access2,
			Function access3,
			Function access4,
			Function access5,
			Function access6) {
		return chain(start, access1, access2, access3, access4, access5, access6);
	}

	public static  T8 access(
			T1 start,
			Function access1,
			Function access2,
			Function access3,
			Function access4,
			Function access5,
			Function access6,
			Function access7) {
		return chain(start, access1, access2, access3, access4, access5, access6, access7);
	}

	@SuppressWarnings({"rawtypes", "unchecked"})
	private static  T chain(Object start, Function... accessors) {
		Object current = start;
		for (Function accessor : accessors) {
			if (current == null) break;
			current = accessor.apply(current);
		}
		return (T)(current == null ? absentValue(getLast(accessors)) : current);
	}

	private static  T getLast(T[] array) {
		if (array == null || array.length == 0) throw new IllegalArgumentException("The array must have at least one element");
		return array[array.length - 1];
	}

	private static  T2 absentValue(Function access) {
		if (access instanceof HasAbsentValue hasAbsentValue) {
			//noinspection unchecked
			return (T2) hasAbsentValue.absentValue();
		}
		return null;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy