/* * Copyright 2015, The Querydsl Team (http://www.querydsl.com/team) * * 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.querydsl.collections; import java.lang.reflect.Field; import java.math.BigDecimal; import java.util.*; import java.util.regex.Pattern; import javax.annotation.Nullable; import com.google.common.base.Objects; import com.google.common.collect.Sets; import com.querydsl.core.types.Expression; import com.querydsl.core.types.Operator; import com.querydsl.core.types.Ops; import com.querydsl.core.util.MathUtils; import com.querydsl.core.util.ReflectionUtils; /** * {@code CollQueryFunctions} defines function implementation for use in ColQueryTemplates * * @author tiwe * */ public final class CollQueryFunctions { private interface BinaryFunction { Number apply(Number num1, Number num2); } private static final BinaryFunction SUM = new BinaryFunction() { @Override public Number apply(Number num1, Number num2) { return MathUtils.sum(num1, num2); } }; private static final BinaryFunction MAX = new BinaryFunction() { @Override public Number apply(Number num1, Number num2) { if (num1.getClass().equals(num2.getClass()) && num1 instanceof Comparable) { @SuppressWarnings("unchecked") // The types are interchangeable, guarded by previous check Comparable<Number> left = (Comparable<Number>) num1; return left.compareTo(num2) < 0 ? num2 : num1; } else { BigDecimal n1 = new BigDecimal(num1.toString()); BigDecimal n2 = new BigDecimal(num2.toString()); return n1.compareTo(n2) < 0 ? num2 : num1; } } }; private static final BinaryFunction MIN = new BinaryFunction() { @Override public Number apply(Number num1, Number num2) { if (num1.getClass().equals(num2.getClass()) && num1 instanceof Comparable) { @SuppressWarnings("unchecked") // The types are interchangeable, guarded by previous check Comparable<Number> left = (Comparable<Number>) num1; return left.compareTo(num2) < 0 ? num1 : num2; } else { BigDecimal n1 = new BigDecimal(num1.toString()); BigDecimal n2 = new BigDecimal(num2.toString()); return n1.compareTo(n2) < 0 ? num1 : num2; } } }; private static final List<Object> nullList = Arrays.asList((Object) null); public static boolean equals(Object o1, Object o2) { return Objects.equal(o1, o2); } public static <T extends Comparable<? super T>> int compareTo(T c1, T c2) { if (c1 == null) { return c2 == null ? 0 : -1; } else if (c2 == null) { return 1; } else { return c1.compareTo(c2); } } public static <A extends Comparable<? super A>> boolean between(A a, A b, A c) { return compareTo(a, b) >= 0 && compareTo(a, c) <= 0; } public static double cot(double x) { return Math.cos(x) / Math.sin(x); } public static double coth(double x) { return Math.cosh(x) / Math.sinh(x); } public static double degrees(double x) { return x * 180.0 / Math.PI; } public static double radians(double x) { return x * Math.PI / 180.0; } public static double log(double x, int y) { return Math.log(x) / Math.log(y); } @Nullable public static <T> T coalesce(T... args) { for (T arg : args) { if (arg != null) { return arg; } } return null; } public static <T> T nullif(T first, T second) { if (first.equals(second)) { return null; } else { return first; } } public static int getYearMonth(Date date) { Calendar cal = Calendar.getInstance(); cal.setTime(date); return cal.get(Calendar.YEAR) * 100 + cal.get(Calendar.MONTH) + 1; } public static int getDayOfMonth(Date date) { return getField(date, Calendar.DAY_OF_MONTH); } public static int getDayOfWeek(Date date) { return getField(date, Calendar.DAY_OF_WEEK); } public static int getDayOfYear(Date date) { return getField(date, Calendar.DAY_OF_YEAR); } private static int getField(Date date, int field) { Calendar cal = Calendar.getInstance(); cal.setTime(date); return cal.get(field); } public static int getHour(Date date) { return getField(date, Calendar.HOUR_OF_DAY); } public static int getMilliSecond(Date date) { return getField(date, Calendar.MILLISECOND); } public static int getMinute(Date date) { return getField(date, Calendar.MINUTE); } public static int getMonth(Date date) { return getField(date, Calendar.MONTH) + 1; } public static int getSecond(Date date) { return getField(date, Calendar.SECOND); } public static int getWeek(Date date) { return getField(date, Calendar.WEEK_OF_YEAR); } public static int getYear(Date date) { return getField(date, Calendar.YEAR); } public static int getYearWeek(Date date) { Calendar cal = Calendar.getInstance(); cal.setTime(date); return cal.get(Calendar.YEAR) * 100 + cal.get(Calendar.WEEK_OF_YEAR); } public static <T> Collection<T> leftJoin(Collection<T> coll) { if (coll == null || coll.isEmpty()) { @SuppressWarnings("unchecked") // List only contains null Collection<T> rv = (Collection<T>) nullList; return rv; } else { return coll; } } private static Number reduce(Iterable<Number> source, BinaryFunction f) { Iterator<Number> it = source.iterator(); Number result = it.next(); while (it.hasNext()) { result = f.apply(result, it.next()); } return result; } public static Number aggregate(Collection<Number> source, Expression<?> expr, Operator aggregator) { @SuppressWarnings("unchecked") // This is a number expression Class<Number> numberType = (Class<Number>) expr.getType(); if (aggregator == Ops.AggOps.AVG_AGG) { Number sum = reduce(source, SUM); return sum.doubleValue() / source.size(); } else if (aggregator == Ops.AggOps.COUNT_AGG) { return (long) source.size(); } else if (aggregator == Ops.AggOps.COUNT_DISTINCT_AGG) { if (!Set.class.isInstance(source)) { source = Sets.newHashSet(source); } return (long) source.size(); } else if (aggregator == Ops.AggOps.MAX_AGG) { return MathUtils.cast(reduce(source, MAX), numberType); } else if (aggregator == Ops.AggOps.MIN_AGG) { return MathUtils.cast(reduce(source, MIN), numberType); } else if (aggregator == Ops.AggOps.SUM_AGG) { return MathUtils.cast(reduce(source, SUM), numberType); } else { throw new IllegalArgumentException("Unknown operator " + aggregator); } } public static boolean like(final String str, String like) { final StringBuilder pattern = new StringBuilder(like.length() + 4); for (int i = 0; i < like.length(); i++) { final char ch = like.charAt(i); if (ch == '%') { pattern.append(".*"); continue; } else if (ch == '_') { pattern.append('.'); continue; } else if (ch == '.' || ch == '$' || ch == '^') { pattern.append('\\'); } pattern.append(ch); } if (pattern.toString().equals(like)) { return str.equals(like); } else { return str.matches(pattern.toString()); } } public static boolean like(String str, String like, char escape) { return like(str, like); } public static boolean likeIgnoreCase(String str, String like) { final StringBuilder pattern = new StringBuilder(like.length() + 4); for (int i = 0; i < like.length(); i++) { final char ch = like.charAt(i); if (ch == '%') { pattern.append(".*"); continue; } else if (ch == '_') { pattern.append('.'); continue; } else if (ch == '.' || ch == '$' || ch == '^') { pattern.append('\\'); } pattern.append(ch); } if (pattern.toString().equals(like)) { return str.equalsIgnoreCase(like); } else { return Pattern.compile(pattern.toString(), Pattern.CASE_INSENSITIVE) .matcher(str).matches(); } } public static boolean likeIgnoreCase(String str, String like, char escape) { return likeIgnoreCase(str, like); } public static <T> T get(Object parent, String f) { try { Field field = ReflectionUtils.getFieldOrNull(parent.getClass(), f); if (field != null) { field.setAccessible(true); @SuppressWarnings("unchecked") T rv = (T) field.get(parent); return rv; } else { throw new IllegalArgumentException("No field " + f + " for " + parent.getClass()); } } catch (IllegalAccessException e) { throw new IllegalStateException(e); } } private CollQueryFunctions() { } }