/*
* This file is part of Gradoop.
*
* Gradoop is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Gradoop 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Gradoop. If not, see <http://www.gnu.org/licenses/>.
*/
package org.gradoop.common.model.impl.properties;
import org.gradoop.common.storage.exceptions.UnsupportedTypeException;
import java.math.BigDecimal;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Utilities for operations on multiple property values.
*/
public class PropertyValueUtils {
/**
* Boolean utilities.
*/
public static class Boolean {
/**
* Logical or of two boolean properties.
*
* @param a first value
* @param b second value
* @return a OR b
*/
public static PropertyValue or(PropertyValue a, PropertyValue b) {
checkNotNull(a, b);
checkBoolean(a);
checkBoolean(b);
a.setBoolean(a.getBoolean() || b.getBoolean());
return a;
}
/**
* Checks if a property value is boolean.
*
* @param value property value
*/
private static void checkBoolean(PropertyValue value) {
if (!value.isBoolean()) {
throw new UnsupportedTypeException(value.getObject().getClass());
}
}
}
/**
* Numeric utilities.
*/
public static class Numeric {
/**
* Float type.
*/
private static final int FLOAT = 1;
/**
* Integer type.
*/
private static final int INT = 2;
/**
* Double type.
*/
private static final int DOUBLE = 3;
/**
* Long type.
*/
private static final int LONG = 4;
/**
* Big decimal type.
*/
private static final int BIG_DECIMAL = 5;
/**
* Adds two numerical property values.
*
* @param aValue first value
* @param bValue second value
*
* @return first value + second value
*/
public static PropertyValue add(
PropertyValue aValue, PropertyValue bValue) {
int aType = checkNumericalAndGetType(aValue);
int bType = checkNumericalAndGetType(bValue);
boolean sameType = aType == bType;
int returnType = sameType ? aType : maxType(aType, bType);
if (returnType == INT) {
aValue.setInt(aValue.getInt() + bValue.getInt());
} else if (returnType == FLOAT) {
float a;
float b;
if (sameType) {
a = aValue.getFloat();
b = bValue.getFloat();
} else {
a = aType == FLOAT ? aValue.getFloat() : aValue.getInt();
b = bType == FLOAT ? bValue.getFloat() : bValue.getInt();
}
aValue.setFloat(a + b);
} else if (returnType == LONG) {
long a;
long b;
if (sameType) {
a = aValue.getLong();
b = bValue.getLong();
} else {
a = aType == LONG ? aValue.getLong() : aValue.getInt();
b = bType == LONG ? bValue.getLong() : bValue.getInt();
}
aValue.setLong(a + b);
} else if (returnType == DOUBLE) {
double a;
double b;
if (sameType) {
a = aValue.getDouble();
b = bValue.getDouble();
} else {
a = aType == DOUBLE ? aValue.getDouble() : doubleValue(aValue, aType);
b = bType == DOUBLE ? bValue.getDouble() : doubleValue(bValue, bType);
}
aValue.setDouble(a + b);
} else {
BigDecimal a;
BigDecimal b;
if (sameType) {
a = aValue.getBigDecimal();
b = bValue.getBigDecimal();
} else {
a = aType == BIG_DECIMAL ?
aValue.getBigDecimal() : bigDecimalValue(aValue, aType);
b = bType == BIG_DECIMAL ?
bValue.getBigDecimal() : bigDecimalValue(bValue, bType);
}
aValue.setBigDecimal(a.add(b));
}
return aValue;
}
/**
* returns the maximum of two types
*
* @param aType first type
* @param bType second type
*
* @return larger compatible type
*/
private static int maxType(int aType, int bType) {
int maxType = Math.max(aType, bType);
if (maxType != BIG_DECIMAL && // not big decimal
aType % 2 != bType % 2 && // different supertype
maxType % 2 == 0) { // high supertype is integer
// next decimal type
maxType++;
}
return maxType;
}
/**
* Multiplies two numerical property values.
*
* @param aValue first value
* @param bValue second value
*
* @return first value * second value
*/
public static PropertyValue multiply(
PropertyValue aValue, PropertyValue bValue) {
int aType = checkNumericalAndGetType(aValue);
int bType = checkNumericalAndGetType(bValue);
boolean sameType = aType == bType;
int returnType = sameType ? aType : maxType(aType, bType);
if (returnType == INT) {
aValue.setInt(aValue.getInt() * bValue.getInt());
} else if (returnType == FLOAT) {
float a;
float b;
if (sameType) {
a = aValue.getFloat();
b = bValue.getFloat();
} else {
a = aType == FLOAT ? aValue.getFloat() : aValue.getInt();
b = bType == FLOAT ? bValue.getFloat() : bValue.getInt();
}
aValue.setFloat(a * b);
} else if (returnType == LONG) {
long a;
long b;
if (sameType) {
a = aValue.getLong();
b = bValue.getLong();
} else {
a = aType == LONG ? aValue.getLong() : aValue.getInt();
b = bType == LONG ? bValue.getLong() : bValue.getInt();
}
aValue.setLong(a * b);
} else if (returnType == DOUBLE) {
double a;
double b;
if (sameType) {
a = aValue.getDouble();
b = bValue.getDouble();
} else {
a = aType == DOUBLE ? aValue.getDouble() : doubleValue(aValue, aType);
b = bType == DOUBLE ? bValue.getDouble() : doubleValue(bValue, bType);
}
aValue.setDouble(a * b);
} else {
BigDecimal a;
BigDecimal b;
if (sameType) {
a = aValue.getBigDecimal();
b = bValue.getBigDecimal();
} else {
a = aType == BIG_DECIMAL ?
aValue.getBigDecimal() : bigDecimalValue(aValue, aType);
b = bType == BIG_DECIMAL ?
bValue.getBigDecimal() : bigDecimalValue(bValue, bType);
}
aValue.setBigDecimal(a.multiply(b));
}
return aValue;
}
/**
* Compares two numerical property values and returns the smaller one.
*
* @param a first value
* @param b second value
*
* @return smaller value
*/
public static PropertyValue min(PropertyValue a, PropertyValue b) {
return isLessOrEqualThan(a, b) ? a : b;
}
/**
* Compares two numerical property values and returns the bigger one.
*
* @param a first value
* @param b second value
*
* @return bigger value
*/
public static PropertyValue max(PropertyValue a, PropertyValue b) {
return isLessOrEqualThan(a, b) ? b : a;
}
/**
* Compares two numerical property values and returns true,
* if the first one is smaller.
*
* @param aValue first value
* @param bValue second value
*
* @return a < b
*/
private static boolean isLessOrEqualThan(
PropertyValue aValue, PropertyValue bValue) {
int aType = checkNumericalAndGetType(aValue);
int bType = checkNumericalAndGetType(bValue);
boolean sameType = aType == bType;
int returnType = sameType ? aType : maxType(aType, bType);
boolean aIsLessOrEqual;
if (returnType == INT) {
aIsLessOrEqual = aValue.getInt() <= bValue.getInt();
} else if (returnType == FLOAT) {
float a;
float b;
if (sameType) {
a = aValue.getFloat();
b = bValue.getFloat();
} else {
a = aType == FLOAT ? aValue.getFloat() : aValue.getInt();
b = bType == FLOAT ? bValue.getFloat() : bValue.getInt();
}
aIsLessOrEqual = a <= b;
} else if (returnType == LONG) {
long a;
long b;
if (sameType) {
a = aValue.getLong();
b = bValue.getLong();
} else {
a = aType == LONG ? aValue.getLong() : aValue.getInt();
b = bType == LONG ? bValue.getLong() : bValue.getInt();
}
aIsLessOrEqual = a <= b;
} else if (returnType == DOUBLE) {
double a;
double b;
if (sameType) {
a = aValue.getDouble();
b = bValue.getDouble();
} else {
a = aType == DOUBLE ? aValue.getDouble() : doubleValue(aValue, aType);
b = bType == DOUBLE ? bValue.getDouble() : doubleValue(bValue, bType);
}
aIsLessOrEqual = a <= b;
} else {
BigDecimal a;
BigDecimal b;
if (sameType) {
a = aValue.getBigDecimal();
b = bValue.getBigDecimal();
} else {
a = aType == BIG_DECIMAL ? aValue.getBigDecimal() :
bigDecimalValue(aValue, aType);
b = bType == BIG_DECIMAL ? bValue.getBigDecimal() :
bigDecimalValue(bValue, bType);
}
aIsLessOrEqual = a.compareTo(b) <= 0;
}
return aIsLessOrEqual;
}
/**
* Checks a property value for numerical type and returns its type.
*
* @param value property value
*
* @return numerical type
*/
private static int checkNumericalAndGetType(PropertyValue value) {
checkNotNull(value);
int type;
if (value.isInt()) {
type = INT;
} else if (value.isLong()) {
type = LONG;
} else if (value.isFloat()) {
type = FLOAT;
} else if (value.isDouble()) {
type = DOUBLE;
} else if (value.isBigDecimal()) {
type = BIG_DECIMAL;
} else {
throw new UnsupportedTypeException(value.getObject().getClass());
}
return type;
}
/**
* Converts a value of a lower domain numerical type to BigDecimal.
*
* @param value value
* @param type type
*
* @return converted value
*/
private static BigDecimal bigDecimalValue(PropertyValue value, int type) {
switch (type) {
case INT:
return BigDecimal.valueOf(value.getInt());
case LONG:
return BigDecimal.valueOf(value.getLong());
case FLOAT:
return BigDecimal.valueOf(value.getFloat());
default:
return BigDecimal.valueOf(value.getDouble());
}
}
/**
* Converts a value of a lower domain numerical type to Double.
*
* @param value value
* @param type type
*
* @return converted value
*/
private static double doubleValue(PropertyValue value, int type) {
switch (type) {
case INT:
return value.getInt();
case LONG:
return value.getLong();
default:
return value.getFloat();
}
}
}
}