tech.ydb.yoj.databind.expression.values.FieldValue Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of yoj-databind Show documentation
Show all versions of yoj-databind Show documentation
Core data-binding logic used by YOJ (YDB ORM for Java) to convert
between Java objects and database rows (or anything representable by
a Java Map, really).
The newest version!
package tech.ydb.yoj.databind.expression.values;
import com.google.common.base.Preconditions;
import lombok.NonNull;
import tech.ydb.yoj.databind.ByteArray;
import tech.ydb.yoj.databind.CustomValueTypes;
import tech.ydb.yoj.databind.FieldValueType;
import tech.ydb.yoj.databind.expression.IllegalExpressionException;
import tech.ydb.yoj.databind.schema.ObjectSchema;
import tech.ydb.yoj.databind.schema.Schema.JavaField;
import javax.annotation.Nullable;
import java.lang.reflect.Type;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.function.Function;
import static java.util.stream.Collectors.collectingAndThen;
import static java.util.stream.Collectors.toCollection;
public sealed interface FieldValue extends tech.ydb.yoj.databind.expression.FieldValue
permits BooleanFieldValue, ByteArrayFieldValue, IntegerFieldValue,
RealFieldValue, StringFieldValue, TimestampFieldValue, TupleFieldValue, UuidFieldValue {
Optional> getComparableByType(Type fieldType, FieldValueType valueType);
ValidationResult isValidValueOfType(Type fieldType, FieldValueType valueType);
@Override
default Object getRaw(@NonNull JavaField field) {
Comparable> cmp = getComparable(field);
return CustomValueTypes.postconvert(field, cmp);
}
@Override
default Comparable> getComparable(@NonNull JavaField field) {
field = field.isFlat() ? field.toFlatField() : field;
FieldValueType valueType = FieldValueType.forSchemaField(field);
Optional> comparableOpt = getComparableByType(field.getType(), valueType);
return comparableOpt.orElseThrow(() -> new IllegalStateException(
"Field of type " + valueType + " is not compatible with the value " + this
));
}
static FieldValue ofObj(@NonNull Object obj, @NonNull JavaField schemaField) {
FieldValueType fvt = FieldValueType.forJavaType(obj.getClass(), schemaField.getField());
obj = CustomValueTypes.preconvert(schemaField, obj);
return switch (fvt) {
case STRING -> new StringFieldValue((String) obj);
case ENUM -> new StringFieldValue(((Enum>) obj).name());
case INTEGER -> new IntegerFieldValue(((Number) obj).longValue());
case REAL -> new RealFieldValue(((Number) obj).doubleValue());
case BOOLEAN -> new BooleanFieldValue((Boolean) obj);
case BYTE_ARRAY -> new ByteArrayFieldValue((ByteArray) obj);
case TIMESTAMP -> new TimestampFieldValue((Instant) obj);
case UUID -> new UuidFieldValue((UUID) obj);
case COMPOSITE -> {
ObjectSchema> schema = ObjectSchema.of(obj.getClass());
List flatFields = schema.flattenFields();
@SuppressWarnings({"rawtypes", "unchecked"})
Map flattenedObj = ((ObjectSchema) schema).flatten(obj);
List allFieldValues = tupleValues(flatFields, flattenedObj);
if (allFieldValues.size() == 1) {
FieldValue singleValue = allFieldValues.iterator().next().value();
Preconditions.checkArgument(singleValue != null, "Wrappers must have a non-null value inside them");
yield singleValue;
}
yield new TupleFieldValue(new Tuple(obj, allFieldValues));
}
default -> throw new UnsupportedOperationException("Unsupported value type: not a string, integer, timestamp, UUID, enum, "
+ "floating-point number, byte array, tuple or wrapper of the above");
};
}
private static @NonNull List tupleValues(List flatFields, Map flattenedObj) {
return flatFields.stream()
.map(jf -> new Tuple.FieldAndValue(jf, flattenedObj))
// Tuple field values are allowed to be null, so we explicitly use ArrayList, just make it unmodifiable
.collect(collectingAndThen(toCollection(ArrayList::new), Collections::unmodifiableList));
}
@Nullable
static Comparable> getComparable(@NonNull Map values, @NonNull JavaField field) {
if (field.isFlat()) {
Object rawValue = values.get(field.getName());
return rawValue == null ? null : ofObj(rawValue, field.toFlatField()).getComparable(field);
} else {
return new Tuple(null, tupleValues(field.flatten().toList(), values));
}
}
record ValidationResult(
boolean valid,
Function userException,
Function internalErrorMessage
) {
private static final ValidationResult VALID = new ValidationResult(true, null, null);
public static ValidationResult validFieldValue() {
return VALID;
}
public static ValidationResult invalidFieldValue(Function userException,
Function internalError) {
return new ValidationResult(false, userException, internalError);
}
public IllegalExpressionException throwUserException(String userFieldPath) throws IllegalExpressionException {
Preconditions.checkState(invalid(), "Cannot call ValidationResult.throwUserException() on a valid ValidationResult");
throw userException.apply(userFieldPath);
}
public RuntimeException throwInternalError(String fieldPath) throws RuntimeException {
Preconditions.checkState(invalid(), "Cannot call ValidationResult.throwInternalError() on a valid ValidationResult");
throw new IllegalArgumentException(internalErrorMessage.apply(fieldPath));
}
public boolean invalid() {
return !valid;
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy