/** * diqube: Distributed Query Base. * * Copyright (C) 2015 Bastian Gloeckle * * This file is part of diqube. * * diqube 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 org.diqube.function.projection; import java.lang.reflect.Array; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.function.BiFunction; import org.diqube.data.column.ColumnType; import org.diqube.function.FunctionException; import org.diqube.function.ProjectionFunction; /** * Abstract implementation for projection functions with a two params. * * @author Bastian Gloeckle */ public abstract class AbstractTwoParamProjectionFunction<I, O> implements ProjectionFunction<I, O> { private String nameLowercase; private BiFunction<I, I, O> fn; private ColumnType inputType; private ColumnType outputType; private boolean[] isArray = new boolean[2]; private I[][] arrayValues; private I[] values; private boolean paramsAreExchangeable; @SuppressWarnings("unchecked") protected AbstractTwoParamProjectionFunction(String nameLowercase, ColumnType inputType, ColumnType outputType, boolean paramsAreExchangeable, BiFunction<I, I, O> fn) { this.nameLowercase = nameLowercase; this.inputType = inputType; this.outputType = outputType; this.fn = fn; this.paramsAreExchangeable = paramsAreExchangeable; values = this.createEmptyInputArray(2); switch (inputType) { case LONG: arrayValues = (I[][]) Array.newInstance(Long.class, 2, 0); break; case DOUBLE: arrayValues = (I[][]) Array.newInstance(Double.class, 2, 0); break; default: arrayValues = (I[][]) Array.newInstance(String.class, 2, 0); } } @Override public String getNameLowerCase() { return nameLowercase; } @Override public I[] createEmptyInputArray(int length) { return createArray(inputType, length); } @SuppressWarnings("unchecked") private <T> T[] createArray(ColumnType type, int length) { if (type.equals(ColumnType.LONG)) return (T[]) new Long[length]; if (type.equals(ColumnType.DOUBLE)) return (T[]) new Double[length]; return (T[]) new String[length]; } @Override public void provideParameter(int parameterIdx, I[] value) { arrayValues[parameterIdx] = value; isArray[parameterIdx] = true; } @Override public void provideConstantParameter(int parameterIdx, I value) { values[parameterIdx] = value; isArray[parameterIdx] = false; } @Override public O[] execute() throws FunctionException { if (!isArray[0] && !isArray[1]) { O[] res = createArray(outputType, 1); res[0] = fn.apply(values[0], values[1]); return res; } if (isArray[0] ^ isArray[1]) { I[] array = (isArray[0]) ? arrayValues[0] : arrayValues[1]; O[] res = createArray(outputType, array.length); if (isArray[0]) { for (int i = 0; i < res.length; i++) res[i] = fn.apply(array[i], values[1]); } else { for (int i = 0; i < res.length; i++) res[i] = fn.apply(values[0], array[i]); } return res; } if (arrayValues[0].length != arrayValues[1].length) throw new FunctionException("Arrays have to be of same length for " + getNameLowerCase() + "!"); O[] res = createArray(outputType, arrayValues[0].length); for (int i = 0; i < res.length; i++) res[i] = fn.apply(arrayValues[0][i], arrayValues[1][i]); return res; } @Override public int numberOfParameters() { return 2; } @Override public List<Set<Integer>> exchangeableParameterIndices() { if (paramsAreExchangeable) { Set<Integer> exchangeable = new HashSet<Integer>(); exchangeable.add(0); exchangeable.add(1); List<Set<Integer>> res = new ArrayList<>(); res.add(exchangeable); return res; } return new ArrayList<>(); } @Override public ColumnType getOutputType() { return inputType; } @Override public ColumnType getInputType() { return outputType; } }