PropertyValueUtils.java
/*
* Copyright © 2014 - 2021 Leipzig University (Database Research Group)
*
* 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 org.gradoop.common.model.impl.properties;
import org.gradoop.common.exceptions.UnsupportedTypeException;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Arrays;
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 {
/**
* Short type.
*/
private static final int SHORT = 0;
/**
* Integer type.
*/
private static final int INT = 1;
/**
* Long type.
*/
private static final int LONG = 2;
/**
* Float type.
*/
private static final int FLOAT = 3;
/**
* Double type.
*/
private static final int DOUBLE = 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 = maxType(aType, bType);
if (returnType == INT) {
int a = aType == INT ? aValue.getInt() : aValue.getShort();
int b = bType == INT ? bValue.getInt() : bValue.getShort();
aValue.setInt(a + b);
} else if (returnType == FLOAT) {
float a;
float b;
if (sameType) {
a = aValue.getFloat();
b = bValue.getFloat();
} else {
a = aType == FLOAT ? aValue.getFloat() : floatValue(aValue, aType);
b = bType == FLOAT ? bValue.getFloat() : floatValue(bValue, bType);
}
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() : longValue(aValue, aType);
b = bType == LONG ? bValue.getLong() : longValue(bValue, bType);
}
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, at least Integer.
*
* @param aType first type
* @param bType second type
*
* @return larger compatible type
*/
private static int maxType(int aType, int bType) {
return Math.max(Math.max(aType, bType), INT);
}
/**
* 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 = maxType(aType, bType);
if (returnType == INT) {
int a = aType == INT ? aValue.getInt() : aValue.getShort();
int b = bType == INT ? bValue.getInt() : bValue.getShort();
aValue.setInt(a * b);
} else if (returnType == FLOAT) {
float a;
float b;
if (sameType) {
a = aValue.getFloat();
b = bValue.getFloat();
} else {
a = aType == FLOAT ? aValue.getFloat() : floatValue(aValue, aType);
b = bType == FLOAT ? bValue.getFloat() : floatValue(bValue, bType);
}
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() : longValue(aValue, aType);
b = bType == LONG ? bValue.getLong() : longValue(bValue, bType);
}
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
*
* @param aValue first value
* @param bValue second value
*
* @return 0 if a equal to b, {@code < 0} if {@code a < b} and {@code > 0} if {@code a > b}
*/
public static int compare(PropertyValue aValue, PropertyValue bValue) {
int aType = checkNumericalAndGetType(aValue);
int bType = checkNumericalAndGetType(bValue);
boolean sameType = aType == bType;
int maxType = Math.max(aType, bType);
int result;
if (maxType == SHORT) {
result = Short.compare(aValue.getShort(), bValue.getShort());
} else if (maxType == INT) {
int a;
int b;
if (sameType) {
a = aValue.getInt();
b = bValue.getInt();
} else {
a = aType == INT ? aValue.getInt() : aValue.getShort();
b = bType == INT ? bValue.getInt() : bValue.getShort();
}
result = Integer.compare(a, b);
} else if (maxType == FLOAT) {
float a;
float b;
if (sameType) {
a = aValue.getFloat();
b = bValue.getFloat();
} else {
a = aType == FLOAT ? aValue.getFloat() : floatValue(aValue, aType);
b = bType == FLOAT ? bValue.getFloat() : floatValue(bValue, bType);
}
result = Float.compare(a, b);
} else if (maxType == LONG) {
long a;
long b;
if (sameType) {
a = aValue.getLong();
b = bValue.getLong();
} else {
a = aType == LONG ? aValue.getLong() : longValue(aValue, aType);
b = bType == LONG ? bValue.getLong() : longValue(bValue, bType);
}
result = Long.compare(a, b);
} else if (maxType == 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);
}
result = Double.compare(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);
}
result = a.compareTo(b);
}
return result;
}
/**
* 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 = maxType(aType, bType);
boolean aIsLessOrEqual;
if (returnType == INT) {
int a = aType == INT ? aValue.getInt() : aValue.getShort();
int b = bType == INT ? bValue.getInt() : bValue.getShort();
aIsLessOrEqual = a <= b;
} else if (returnType == FLOAT) {
float a;
float b;
if (sameType) {
a = aValue.getFloat();
b = bValue.getFloat();
} else {
a = aType == FLOAT ? aValue.getFloat() : floatValue(aValue, aType);
b = bType == FLOAT ? bValue.getFloat() : floatValue(bValue, bType);
}
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() : longValue(aValue, aType);
b = bType == LONG ? bValue.getLong() : longValue(bValue, bType);
}
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.isShort()) {
type = SHORT;
} else 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 SHORT:
return BigDecimal.valueOf(value.getShort());
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 SHORT:
return value.getShort();
case INT:
return value.getInt();
case LONG:
return value.getLong();
default:
return value.getFloat();
}
}
/**
* Converts a value of a lower domain numerical type to Long.
*
* @param value value
* @param type type
*
* @return converted value
*/
private static long longValue(PropertyValue value, int type) {
switch (type) {
case SHORT:
return value.getShort();
default:
return value.getInt();
}
}
/**
* Converts a value of a lower domain numerical type to Float.
*
* @param value value
* @param type type
*
* @return converted value
*/
private static float floatValue(PropertyValue value, int type) {
switch (type) {
case SHORT:
return value.getShort();
case INT:
return value.getInt();
default:
return value.getLong();
}
}
}
/**
* Date and DateTime utilities.
*/
public static class Date {
/**
* Compares two time property values and returns the chronological first one.
* <p>
* Note that the comparison of a {@link java.time.LocalDate} with a {@link LocalDateTime}
* will cause casting the {@link java.time.LocalDate} to a {@link java.time.LocalDateTime}
* instance, but just for the comparison. The return value is not casted.
*
* @param a first value
* @param b second value
*
* @return the time property value that is the chronological first
*/
public static PropertyValue min(PropertyValue a, PropertyValue b) {
return compare(a, b) <= 0 ? a : b;
}
/**
* Compares two time property values and returns the chronological last one.
* <p>
* Note that the comparison of a {@link java.time.LocalDate} with a {@link LocalDateTime}
* will cause casting the {@link java.time.LocalDate} to a {@link java.time.LocalDateTime}
* instance, but just for the comparison. The return value is not casted.
*
* @param a first value
* @param b second value
*
* @return the time property value that is the chronological last
*/
public static PropertyValue max(PropertyValue a, PropertyValue b) {
return compare(a, b) <= 0 ? b : a;
}
/**
* Compares two date or datetime property values and returns {@code -1}, if the first one is earlier,
* {@code 0} equal or {@code 1} later than the second one.
* Note that the comparison of a {@link java.time.LocalDate} with a {@link LocalDateTime}
* will cause casting the {@link java.time.LocalDate} to a {@link java.time.LocalDateTime} instance.
*
* @param aValue first value
* @param bValue second value
*
* @return returns {@code -1}, if the first one is earlier, {@code 0} equal or {@code 1} later than the
* second one
* @throws IllegalArgumentException if arguments are not of type Date or DateTime
*/
private static int compare(PropertyValue aValue, PropertyValue bValue) {
if (aValue.isDateTime() && bValue.isDateTime()) {
return aValue.getDateTime().compareTo(bValue.getDateTime());
} else if (aValue.isDate() && bValue.isDate()) {
return aValue.getDate().compareTo(bValue.getDate());
} else if (aValue.isDate() && bValue.isDateTime()) {
// cast a to DateTime
return LocalDateTime.of(aValue.getDate(), LocalTime.MIN).compareTo(bValue.getDateTime());
} else if (aValue.isDateTime() && bValue.isDate()) {
// cast b to DateTime
return aValue.getDateTime().compareTo(LocalDateTime.of(bValue.getDate(), LocalTime.MIN));
} else {
throw new IllegalArgumentException("Arguments to compare are not of type Date or DateTime.");
}
}
/**
* Checks the property value type for Date or DateTime.
*
* @param value the property value to check
* @return true, iff the value is a Date or DateTime type
*/
public static boolean isDateOrDateTime(PropertyValue value) {
return value.isDate() || value.isDateTime();
}
}
/**
* Byte utilities.
*/
public static class BytesUtils {
/**
* Get the raw byte representation of a {@link PropertyValue} instance without the type byte as prefix.
*
* @param value the {@link PropertyValue} to extract the bytes
* @return a byte array containing the value without type information
*/
public static byte[] getRawBytesWithoutType(PropertyValue value) {
return Arrays.copyOfRange(value.getRawBytes(), 1, value.getRawBytes().length);
}
/**
* Get the type byte of a {@link PropertyValue} instance. It's the first one of the
* raw representation of a PropertyValue.
*
* @param value the {@link PropertyValue} to extract the type byte
* @return the type byte as array
*/
public static byte[] getTypeByte(PropertyValue value) {
byte[] typeByte = new byte[1];
typeByte[0] = value.getRawBytes()[0];
return typeByte;
}
/**
* Creates a {@link PropertyValue} instance by concatenating the byte representations
* of the type and the value.
*
* @param typeByte a byte array containing only one byte representing the value type
* @param valueBytes a byte array representing the property value
* @return the resulting {@link PropertyValue}
*/
public static PropertyValue createFromTypeValueBytes(byte[] typeByte, byte[] valueBytes) {
byte[] validValue = new byte[valueBytes.length + 1];
validValue[0] = typeByte[0];
System.arraycopy(valueBytes, 0, validValue, 1, valueBytes.length);
return PropertyValue.fromRawBytes(validValue);
}
}
}