/* * Data Hub Service (DHuS) - For Space data distribution. * Copyright (C) 2016 GAEL Systems * * This file is part of DHuS software sources. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package fr.gael.dhus.olingo.v1.visitor.functors; import fr.gael.dhus.util.functional.FunctionalTools; import fr.gael.dhus.util.functional.tuple.Duo; import fr.gael.dhus.util.functional.tuple.Trio; import java.util.Calendar; import java.util.Date; import org.apache.commons.collections4.Transformer; /** * OData operators and functions. * * See http://www.odata.org/documentation/odata-version-2-0/uri-conventions/#FilterSystemQueryOption * * @see fr.gael.dhus.util.functional */ public class Transformers { // Logical Operators /** eq, equals. */ public static Transformer<Duo<Object, Object>, Boolean> eq() { return new Transformer<Duo<Object, Object>, Boolean>() { @Override public Boolean transform(Duo<Object, Object> d) { if (d.getA() == null || d.getB() == null) { return d.getA() == d.getB(); } // subclasses of Number reimplement #equals(o) to compare instance types if (involvesNumbers(d)) { return Number.class.cast(d.getA()).doubleValue() == Number.class.cast(d.getB()).doubleValue(); } // dates can be instances of Date or Calendar if (involvesDates(d)) { Duo<Calendar, Calendar> duo = asDuoOfCalendar(d); return duo.getA().equals(duo.getB()); } return d.getA().equals(d.getB()); } }; } /** ne, not equals. */ public static Transformer<Duo<Object, Object>, Boolean> ne() { return FunctionalTools.compose(eq(), not()); } /** gt, greater than. */ public static Transformer<Duo<Object, Object>, Boolean> gt() { return new Transformer<Duo<Object, Object>, Boolean>() { @Override public Boolean transform(Duo<Object, Object> d) { if (involvesNumbers(d)) { return Number.class.cast(d.getA()).doubleValue() > Number.class.cast(d.getB()).doubleValue(); } if (involvesDates(d)) { Duo<Calendar, Calendar> duo = asDuoOfCalendar(d); return duo.getA().after(duo.getB()); } throw new IllegalStateException(notComparableMsg(d)); } }; } /** ge, greater or equals than. */ public static Transformer<Duo<Object, Object>, Boolean> ge() { return new Transformer<Duo<Object, Object>, Boolean>() { @Override public Boolean transform(Duo<Object, Object> u) { if (involvesNumbers(u)) { return Number.class.cast(u.getA()).doubleValue() >= Number.class.cast(u.getB()).doubleValue(); } if (involvesDates(u)) { Duo<Calendar, Calendar> duo = asDuoOfCalendar(u); return duo.getA().after(duo.getB()) || duo.getA().equals(duo.getB()); } throw new IllegalStateException(notComparableMsg(u)); } }; } /** lt, lower than. */ public static <T> Transformer<Duo<Object, Object>, Boolean> lt() { return new Transformer<Duo<Object, Object>, Boolean>() { @Override public Boolean transform(Duo<Object, Object> u) { if (involvesNumbers(u)) { return Number.class.cast(u.getA()).doubleValue() < Number.class.cast(u.getB()).doubleValue(); } if (involvesDates(u)) { Duo<Calendar, Calendar> duo = asDuoOfCalendar(u); return duo.getA().before(duo.getB()); } throw new IllegalStateException(notComparableMsg(u)); } }; } /** le, lower or equals than. */ public static <T> Transformer<Duo<Object, Object>, Boolean> le() { return new Transformer<Duo<Object, Object>, Boolean>() { @Override public Boolean transform(Duo<Object, Object> u) { if (involvesNumbers(u)) { return Number.class.cast(u.getA()).doubleValue() <= Number.class.cast(u.getB()).doubleValue(); } if (involvesDates(u)) { Duo<Calendar, Calendar> duo = asDuoOfCalendar(u); return duo.getA().before(duo.getB()) || duo.getA().equals(duo.getB()); } throw new IllegalStateException(notComparableMsg(u)); } }; } /** and. */ public static Transformer<Duo<Boolean, Boolean>, Boolean> and() { return new Transformer<Duo<Boolean, Boolean>, Boolean>() { @Override public Boolean transform(Duo<Boolean, Boolean> u) { return u.getA() && u.getB(); } }; } /** or. */ public static Transformer<Duo<Boolean, Boolean>, Boolean> or() { return new Transformer<Duo<Boolean, Boolean>, Boolean>() { @Override public Boolean transform(Duo<Boolean, Boolean> u) { return u.getA() || u.getB(); } }; } /** not. */ public static Transformer<Boolean, Boolean> not() { return new Transformer<Boolean, Boolean>() { @Override public Boolean transform(Boolean u) { return !u; } }; } // Arithmetic operators /** unary minus. */ public static Transformer<Number, Number> minus() { return new Transformer<Number, Number>() { @Override public Number transform(Number u) { return -(u.doubleValue()); } }; } /** add. */ public static Transformer<Duo<Number, Number>, Number> add() { return new Transformer<Duo<Number, Number>, Number>() { @Override public Number transform(Duo<Number, Number> u) { return u.getA().doubleValue() + u.getB().doubleValue(); } }; } /** sub. */ public static Transformer<Duo<Number, Number>, Number> sub() { return new Transformer<Duo<Number, Number>, Number>() { @Override public Number transform(Duo<Number, Number> u) { return u.getA().doubleValue() - u.getB().doubleValue(); } }; } /** mul. */ public static Transformer<Duo<Number, Number>, Number> mul() { return new Transformer<Duo<Number, Number>, Number>() { @Override public Number transform(Duo<Number, Number> u) { return u.getA().doubleValue() * u.getB().doubleValue(); } }; } /** div. */ public static Transformer<Duo<Number, Number>, Number> div() { return new Transformer<Duo<Number, Number>, Number>() { @Override public Number transform(Duo<Number, Number> u) { return u.getA().doubleValue() / u.getB().doubleValue(); } }; } /** mod. */ public static Transformer<Duo<Long, Long>, Long> mod() { return new Transformer<Duo<Long, Long>, Long>() { @Override public Long transform(Duo<Long, Long> u) { return u.getA() % u.getB(); } }; } // String Functions /** substringof. */ public static Transformer<Duo<String, String>, Boolean> substringof() { return new Transformer<Duo<String, String>, Boolean>() { @Override public Boolean transform(Duo<String, String> u) { if (u == null || u.getA() == null) { return false; } return u.getA().contains(u.getB()); } }; } /** endswith. */ public static Transformer<Duo<String, String>, Boolean> endswith() { return new Transformer<Duo<String, String>, Boolean>() { @Override public Boolean transform(Duo<String, String> u) { if (u == null || u.getA() == null) { return false; } return u.getA().endsWith(u.getB()); } }; } /** startswith. */ public static Transformer<Duo<String, String>, Boolean> startswith() { return new Transformer<Duo<String, String>, Boolean>() { @Override public Boolean transform(Duo<String, String> u) { if (u == null || u.getA() == null) { return false; } return u.getA().startsWith(u.getB()); } }; } /** length. */ public static Transformer<String, Integer> length() { return new Transformer<String, Integer>() { @Override public Integer transform(String u) { if (u == null) { return 0; } return u.length(); } }; } /** indexof. */ public static Transformer<Duo<String, String>, Integer> indexof() { return new Transformer<Duo<String, String>, Integer>() { @Override public Integer transform(Duo<String, String> u) { if (u == null || u.getA() == null) { return -1; } return u.getA().indexOf(u.getB()); } }; } /** replace. */ public static Transformer<Trio<String, String, String>, String> replace() { return new Transformer<Trio<String, String, String>, String>() { @Override public String transform(Trio<String, String, String> u) { if (u == null || u.getA() == null) { return null; } return u.getA().replaceFirst(u.getB(), u.getC()); } }; } /** substring(string, int). */ public static Transformer<Duo<String, Integer>, String> substring() { return new Transformer<Duo<String, Integer>, String>() { @Override public String transform(Duo<String, Integer> u) { if (u == null || u.getA() == null) { return null; } return u.getA().substring(u.getB()); } }; } /** substring(string, int, int). */ public static Transformer<Trio<String, Integer, Integer>, String> substring2() { return new Transformer<Trio<String, Integer, Integer>, String>() { @Override public String transform(Trio<String, Integer, Integer> u) { if (u == null || u.getA() == null) { return null; } return u.getA().substring(u.getB(), u.getC() - u.getB()); } }; } /** tolower. */ public static Transformer<String, String> tolower() { return new Transformer<String, String>() { @Override public String transform(String u) { if (u == null) { return null; } return u.toLowerCase(); } }; } /** toupper. */ public static Transformer<String, String> toupper() { return new Transformer<String, String>() { @Override public String transform(String u) { if (u == null) { return null; } return u.toUpperCase(); } }; } /** trim. */ public static Transformer<String, String> trim() { return new Transformer<String, String>() { @Override public String transform(String u) { if (u == null) { return null; } return u.trim(); } }; } /** concat. */ public static Transformer<Duo<String, String>, String> concat() { return new Transformer<Duo<String, String>, String>() { @Override public String transform(Duo<String , String> v) { return v.getA()+v.getB(); } }; } // Date Functions /** day. */ public static Transformer<Date, Integer> day() { return new Transformer<Date, Integer>() { @Override public Integer transform(Date u) { Calendar c = Calendar.getInstance(); c.setTime(u); return c.get(Calendar.DAY_OF_MONTH); } }; } /** hour. */ public static Transformer<Date, Integer> hour() { return new Transformer<Date, Integer>() { @Override public Integer transform(Date u) { Calendar c = Calendar.getInstance(); c.setTime(u); return c.get(Calendar.HOUR_OF_DAY); } }; } /** minute. */ public static Transformer<Date, Integer> minute() { return new Transformer<Date, Integer>() { @Override public Integer transform(Date u) { Calendar c = Calendar.getInstance(); c.setTime(u); return c.get(Calendar.MINUTE); } }; } /** month. */ public static Transformer<Date, Integer> month() { return new Transformer<Date, Integer>() { @Override public Integer transform(Date u) { Calendar c = Calendar.getInstance(); c.setTime(u); return c.get(Calendar.MONTH); } }; } /** second. */ public static Transformer<Date, Integer> second() { return new Transformer<Date, Integer>() { @Override public Integer transform(Date u) { Calendar c = Calendar.getInstance(); c.setTime(u); return c.get(Calendar.SECOND); } }; } /** year. */ public static Transformer<Date, Integer> year() { return new Transformer<Date, Integer>() { @Override public Integer transform(Date u) { Calendar c = Calendar.getInstance(); c.setTime(u); return c.get(Calendar.YEAR); } }; } // Math functions /** round. */ public static Transformer<Double, Double> round() { return new Transformer<Double, Double>() { @Override public Double transform(Double u) { return (double)Math.round(u); } }; } /** floor. */ public static Transformer<Double, Double> floor() { return new Transformer<Double, Double>() { @Override public Double transform(Double u) { return (double)Math.floor(u); } }; } /** ceiling. */ public static Transformer<Double, Double> ceiling() { return new Transformer<Double, Double>() { @Override public Double transform(Double u) { return (double)Math.ceil(u); } }; } // IsOf functions are not yet implemented in Olingo2 /** * Return true if <strong>BOTH</strong> A and B are instances of {@link Number}. * @param d to test. * @return true if A and B can be cast to Number. */ private static boolean involvesNumbers(Duo<?, ?> d) { return Number.class.isAssignableFrom(d.getA().getClass()) && Number.class.isAssignableFrom(d.getB().getClass()); } /** * Returns true if A or B is a {@link Calendar} or a {@link Date}. * @param d to test. * @return true if the given Duo involves a date type. */ private static boolean involvesDates(Duo<?, ?> d) { return Calendar.class.isAssignableFrom(d.getA().getClass()) || Calendar.class.isAssignableFrom(d.getB().getClass()) || Date.class.isAssignableFrom(d.getA().getClass()) || Date.class.isAssignableFrom(d.getB().getClass()); } /** * Converts the given object to a {@link Calendar}. * @param o to convert to Calendar. * @throws IllegalArgumentException parameter `o` cannot be converted into a Calendar. */ private static Calendar castToCalendar(Object o) throws IllegalArgumentException { Calendar res; Class<?> ocls = o.getClass(); if (Calendar.class.isAssignableFrom(ocls)) { res = Calendar.class.cast(o); } else if (Date.class.isAssignableFrom(ocls)) { res = Calendar.getInstance(); res.setTime(Date.class.cast(o)); } else if (Long.class.isAnnotation()) { res = Calendar.getInstance(); res.setTimeInMillis(Long.class.cast(o)); } else { throw new IllegalArgumentException("Cannot create a Calendar from an instance of " + ocls); } return res; } /** * Returns a {@link Duo} of {@link Calendar}s from the given Duo. * @param d to convert. * @return a Duo of Calendars. */ private static Duo<Calendar, Calendar> asDuoOfCalendar(Duo<?, ?> d) { return new Duo<>(castToCalendar(d.getA()), castToCalendar(d.getB())); } private static String notComparableMsg(Duo<?, ?> d) { return String.format("Instances of %s and %s are not comparable", d.getA().getClass(), d.getB().getClass()); } }