All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.neo4j.values.storable.Values Maven / Gradle / Ivy

/*
 * Copyright (c) "Neo4j"
 * Neo4j Sweden AB [https://neo4j.com]
 *
 * This file is part of Neo4j.
 *
 * Neo4j 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.
 *
 * 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see .
 */
package org.neo4j.values.storable;

import static java.lang.String.format;
import static org.neo4j.values.storable.DateTimeValue.datetime;
import static org.neo4j.values.storable.DateValue.date;
import static org.neo4j.values.storable.DurationValue.duration;
import static org.neo4j.values.storable.LocalDateTimeValue.localDateTime;
import static org.neo4j.values.storable.LocalTimeValue.localTime;
import static org.neo4j.values.storable.TimeValue.time;

import java.lang.reflect.Array;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.time.OffsetTime;
import java.time.Period;
import java.time.ZonedDateTime;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAmount;
import java.time.temporal.TemporalUnit;
import java.util.Arrays;
import java.util.Objects;
import org.apache.commons.lang3.ArrayUtils;
import org.neo4j.graphdb.spatial.CRS;
import org.neo4j.graphdb.spatial.Point;

/**
 * Entry point to the values library.
 * 

* The values library centers around the Value class, which represents a value in Neo4j. Values can be correctly * checked for equality over different primitive representations, including consistent hashCodes and sorting. *

* To create Values use the factory methods in the Values class. *

* Values come in two major categories: Storable and Virtual. Storable values are valid values for * node, relationship and graph properties. Virtual values are not supported as property values, but might be created * and returned as part of cypher execution. These include Node, Relationship and Path. */ @SuppressWarnings("WeakerAccess") public final class Values { public static final Value NO_VALUE = NoValue.NO_VALUE; public static final Value MIN_GLOBAL = DateTimeValue.MIN_VALUE; public static final Value MAX_GLOBAL = Values.NO_VALUE; public static final Value MIN_NUMBER = Values.doubleValue(Double.NEGATIVE_INFINITY); public static final Value MAX_NUMBER = Values.doubleValue(Double.NaN); public static final Value ZERO_FLOAT = Values.doubleValue(0.0); public static final IntegralValue ZERO_INT = Values.longValue(0); public static final Value MIN_STRING = StringValue.EMPTY; public static final Value MAX_STRING = Values.booleanValue(false); public static final BooleanValue TRUE = Values.booleanValue(true); public static final BooleanValue FALSE = Values.booleanValue(false); public static final TextValue EMPTY_STRING = StringValue.EMPTY; public static final DoubleValue E = Values.doubleValue(Math.E); public static final DoubleValue PI = Values.doubleValue(Math.PI); public static final DoubleValue NaN = Values.doubleValue(Double.NaN); public static final DoubleValue Infinity = Values.doubleValue(Double.POSITIVE_INFINITY); public static final DoubleValue NegInfinity = Values.doubleValue(Double.NEGATIVE_INFINITY); public static final ArrayValue EMPTY_SHORT_ARRAY = Values.shortArray(ArrayUtils.EMPTY_SHORT_ARRAY); public static final ArrayValue EMPTY_BOOLEAN_ARRAY = Values.booleanArray(ArrayUtils.EMPTY_BOOLEAN_ARRAY); public static final ArrayValue EMPTY_BYTE_ARRAY = Values.byteArray(ArrayUtils.EMPTY_BYTE_ARRAY); public static final ArrayValue EMPTY_CHAR_ARRAY = Values.charArray(ArrayUtils.EMPTY_CHAR_ARRAY); public static final ArrayValue EMPTY_INT_ARRAY = Values.intArray(ArrayUtils.EMPTY_INT_ARRAY); public static final ArrayValue EMPTY_LONG_ARRAY = Values.longArray(ArrayUtils.EMPTY_LONG_ARRAY); public static final ArrayValue EMPTY_FLOAT_ARRAY = Values.floatArray(ArrayUtils.EMPTY_FLOAT_ARRAY); public static final ArrayValue EMPTY_DOUBLE_ARRAY = Values.doubleArray(ArrayUtils.EMPTY_DOUBLE_ARRAY); public static final TextArray EMPTY_TEXT_ARRAY = Values.stringArray(); private Values() {} /** * Default value comparator. Will correctly compare all storable values and order the value groups according the * to orderability group. * * To get Comparability semantics, use .ternaryCompare */ public static final ValueComparator COMPARATOR = new ValueComparator(ValueGroup::compareTo); public static boolean isNumberValue(Object value) { return value instanceof NumberValue; } public static boolean isBooleanValue(Object value) { return value instanceof BooleanValue; } public static boolean isTextValue(Object value) { return value instanceof TextValue; } public static boolean isArrayValue(Value value) { return value instanceof ArrayValue; } public static boolean isGeometryValue(Value value) { return value instanceof PointValue; } public static boolean isGeometryArray(Value value) { return value instanceof PointArray; } public static boolean isTemporalValue(Value value) { return value instanceof TemporalValue || value instanceof DurationValue; } public static boolean isTemporalArray(Value value) { return value instanceof TemporalArray || value instanceof DurationArray; } public static double coerceToDouble(Value value) { if (value instanceof IntegralValue integralValue) { return integralValue.longValue(); } if (value instanceof FloatingPointValue floatingPointValue) { return floatingPointValue.doubleValue(); } throw new UnsupportedOperationException(format("Cannot coerce %s to double", value)); } // DIRECT FACTORY METHODS public static TextValue utf8Value(String value) { return utf8Value(value.getBytes(StandardCharsets.UTF_8)); } public static Value ut8fOrNoValue(String value) { if (value == null) { return NO_VALUE; } else { return utf8Value(value); } } public static TextValue utf8Value(byte[] bytes) { if (bytes.length == 0) { return EMPTY_STRING; } return utf8Value(bytes, 0, bytes.length); } public static TextValue utf8Value(byte[] bytes, int offset, int length) { if (length == 0) { return EMPTY_STRING; } return new UTF8StringValue(bytes, offset, length); } public static TextValue stringValue(String value) { if (value.isEmpty()) { return EMPTY_STRING; } return new StringWrappingStringValue(value); } public static Value stringOrNoValue(String value) { if (value == null) { return NO_VALUE; } else { return stringValue(value); } } public static NumberValue numberValue(Number number) { if (number instanceof Long longNumber) { return longValue(longNumber); } if (number instanceof Integer intNumber) { return intValue(intNumber); } if (number instanceof Double doubleNumber) { return doubleValue(doubleNumber); } if (number instanceof Byte byteNumber) { return byteValue(byteNumber); } if (number instanceof Float floatNumber) { return floatValue(floatNumber); } if (number instanceof Short shortNumber) { return shortValue(shortNumber); } throw new UnsupportedOperationException("Unsupported type of Number " + number); } public static LongValue longValue(long value) { return new LongValue(value); } public static IntValue intValue(int value) { return new IntValue(value); } public static ShortValue shortValue(short value) { return new ShortValue(value); } public static ByteValue byteValue(byte value) { return new ByteValue(value); } public static BooleanValue booleanValue(boolean value) { return value ? BooleanValue.TRUE : BooleanValue.FALSE; } public static CharValue charValue(char value) { return new CharValue(value); } public static DoubleValue doubleValue(double value) { return new DoubleValue(value); } public static FloatValue floatValue(float value) { return new FloatValue(value); } public static TextArray stringArray(String... value) { return new StringArray(value); } public static ByteArray byteArray(byte[] value) { return new ByteArray(value); } public static LongArray longArray(long[] value) { return new LongArray(value); } public static IntArray intArray(int[] value) { return new IntArray(value); } public static DoubleArray doubleArray(double[] value) { return new DoubleArray(value); } public static FloatArray floatArray(float[] value) { return new FloatArray(value); } public static BooleanArray booleanArray(boolean[] value) { return new BooleanArray(value); } public static CharArray charArray(char[] value) { return new CharArray(value); } public static ShortArray shortArray(short[] value) { return new ShortArray(value); } /** * Creates a PointValue, and enforces consistency between the CRS and coordinate dimensions. */ public static PointValue pointValue(CoordinateReferenceSystem crs, double... coordinate) { return new PointValue(crs, coordinate); } public static PointValue point(Point point) { // An optimization could be to do an instanceof PointValue check here // and in that case just return the casted argument. double[] coords = point.getCoordinate().getCoordinateCopy(); return new PointValue(crs(point.getCRS()), coords); } public static PointValue minPointValue(PointValue reference) { return PointValue.minPointValueOf(reference.getCoordinateReferenceSystem()); } public static PointValue maxPointValue(PointValue reference) { return PointValue.maxPointValueOf(reference.getCoordinateReferenceSystem()); } public static PointArray pointArray(Point[] points) { PointValue[] values = new PointValue[points.length]; for (int i = 0; i < points.length; i++) { values[i] = Values.point(points[i]); } return new PointArray(values); } public static PointArray pointArray(Value[] maybePoints) { PointValue[] values = new PointValue[maybePoints.length]; for (int i = 0; i < maybePoints.length; i++) { Value maybePoint = maybePoints[i]; if (!(maybePoint instanceof PointValue)) { throw new IllegalArgumentException(format( "[%s:%s] is not a supported point value", maybePoint, maybePoint.getClass().getName())); } values[i] = Values.point((PointValue) maybePoint); } return pointArray(values); } public static PointArray pointArray(PointValue[] points) { return new PointArray(points); } public static CoordinateReferenceSystem crs(CRS crs) { return CoordinateReferenceSystem.get(crs); } public static Value temporalValue(Temporal value) { if (value instanceof ZonedDateTime zonedDateTime) { return datetime(zonedDateTime); } if (value instanceof OffsetDateTime offsetDateTime) { return datetime(offsetDateTime); } if (value instanceof LocalDateTime localDateTime) { return localDateTime(localDateTime); } if (value instanceof OffsetTime offsetTime) { return time(offsetTime); } if (value instanceof LocalDate localDate) { return date(localDate); } if (value instanceof LocalTime localTime) { return localTime(localTime); } if (value instanceof TemporalValue temporalValue) { return temporalValue; } if (value == null) { return NO_VALUE; } throw new UnsupportedOperationException("Unsupported type of Temporal " + value); } public static DurationValue durationValue(TemporalAmount value) { if (value instanceof Duration duration) { return duration(duration); } if (value instanceof Period period) { return duration(period); } if (value instanceof DurationValue durationValue) { return durationValue; } DurationValue duration = duration(0, 0, 0, 0); for (TemporalUnit unit : value.getUnits()) { duration = duration.plus(value.get(unit), unit); } return duration; } public static DateTimeArray dateTimeArray(ZonedDateTime[] values) { return new DateTimeArray(values); } public static LocalDateTimeArray localDateTimeArray(LocalDateTime[] values) { return new LocalDateTimeArray(values); } public static LocalTimeArray localTimeArray(LocalTime[] values) { return new LocalTimeArray(values); } public static TimeArray timeArray(OffsetTime[] values) { return new TimeArray(values); } public static DateArray dateArray(LocalDate[] values) { return new DateArray(values); } public static DurationArray durationArray(DurationValue[] values) { return new DurationArray(values); } public static DurationArray durationArray(TemporalAmount[] values) { DurationValue[] durations = new DurationValue[values.length]; for (int i = 0; i < values.length; i++) { durations[i] = durationValue(values[i]); } return new DurationArray(durations); } // BOXED FACTORY METHODS /** * Generic value factory method. *

* Beware, this method is intended for converting externally supplied values to the internal Value type, and to * make testing convenient. Passing a Value as in parameter should never be needed, and will throw an * UnsupportedOperationException. *

* This method does defensive copying of arrays, while the explicit *Array() factory methods do not. * * @param value Object to convert to Value * @return the created Value */ public static Value of(Object value) { return of(value, true); } public static Value of(Object value, boolean allowNull) { Value of = unsafeOf(value, allowNull); if (of != null) { return of; } Objects.requireNonNull(value); throw new IllegalArgumentException(format( "[%s:%s] is not a supported property value", value, value.getClass().getName())); } public static Value unsafeOf(Object value, boolean allowNull) { if (value == null) { if (allowNull) { return NO_VALUE; } throw new IllegalArgumentException("[null] is not a supported property value"); } if (value instanceof String string) { return utf8Value(string.getBytes(StandardCharsets.UTF_8)); } if (value instanceof Object[] array) { return arrayValue(array, true); } if (value instanceof Boolean bool) { return booleanValue(bool); } if (value instanceof Number number) { return numberValue(number); } if (value instanceof Character character) { return charValue(character); } if (value instanceof Temporal temporal) { return temporalValue(temporal); } if (value instanceof TemporalAmount temporalAmount) { return durationValue(temporalAmount); } if (value instanceof byte[] byteArray) { return byteArray(Arrays.copyOf(byteArray, byteArray.length)); } if (value instanceof long[] longArray) { return longArray(Arrays.copyOf(longArray, longArray.length)); } if (value instanceof int[] intArray) { return intArray(Arrays.copyOf(intArray, intArray.length)); } if (value instanceof double[] doubleArray) { return doubleArray(Arrays.copyOf(doubleArray, doubleArray.length)); } if (value instanceof float[] floatArray) { return floatArray(Arrays.copyOf(floatArray, floatArray.length)); } if (value instanceof boolean[] boolArray) { return booleanArray(Arrays.copyOf(boolArray, boolArray.length)); } if (value instanceof char[] charArray) { return charArray(Arrays.copyOf(charArray, charArray.length)); } if (value instanceof short[] shortArray) { return shortArray(Arrays.copyOf(shortArray, shortArray.length)); } if (value instanceof Point point) { return Values.point(point); } if (value instanceof Value) { throw new UnsupportedOperationException( "Converting a Value to a Value using Values.of() is not supported."); } // otherwise fail return null; } /** * Generic value factory method. *

* Converts an array of object values to the internal Value type. See {@link Values#of}. */ public static Value[] values(Object... objects) { return Arrays.stream(objects).map(Values::of).toArray(Value[]::new); } public static Object[] asObjects(Value[] propertyValues) { Object[] legacy = new Object[propertyValues.length]; for (int i = 0; i < propertyValues.length; i++) { legacy[i] = propertyValues[i].asObjectCopy(); } return legacy; } public static ArrayValue arrayValue(Object[] value, boolean copyDefensively) { if (value instanceof String[] array) { return stringArray(copyDefensively ? copy(value, new String[value.length]) : array); } if (value instanceof Byte[]) { return byteArray(copy(value, new byte[value.length])); } if (value instanceof Long[]) { return longArray(copy(value, new long[value.length])); } if (value instanceof Integer[]) { return intArray(copy(value, new int[value.length])); } if (value instanceof Double[]) { return doubleArray(copy(value, new double[value.length])); } if (value instanceof Float[]) { return floatArray(copy(value, new float[value.length])); } if (value instanceof Boolean[]) { return booleanArray(copy(value, new boolean[value.length])); } if (value instanceof Character[]) { return charArray(copy(value, new char[value.length])); } if (value instanceof Short[]) { return shortArray(copy(value, new short[value.length])); } if (value instanceof PointValue[] array) { return pointArray(copyDefensively ? copy(value, new PointValue[value.length]) : array); } if (value instanceof Point[] array) { // no need to copy here, since the pointArray(...) method will copy into a PointValue[] return pointArray(array); } if (value instanceof ZonedDateTime[] array) { return dateTimeArray(copyDefensively ? copy(value, new ZonedDateTime[value.length]) : array); } if (value instanceof LocalDateTime[] array) { return localDateTimeArray(copyDefensively ? copy(value, new LocalDateTime[value.length]) : array); } if (value instanceof LocalTime[] array) { return localTimeArray(copyDefensively ? copy(value, new LocalTime[value.length]) : array); } if (value instanceof OffsetTime[] array) { return timeArray(copyDefensively ? copy(value, new OffsetTime[value.length]) : array); } if (value instanceof LocalDate[] array) { return dateArray(copyDefensively ? copy(value, new LocalDate[value.length]) : array); } if (value instanceof TemporalAmount[] array) { // no need to copy here, since the durationArray(...) method will perform copying as appropriate return durationArray(array); } return null; } private static T copy(Object[] value, T target) { for (int i = 0; i < value.length; i++) { if (value[i] == null) { throw new IllegalArgumentException("Property array value elements may not be null."); } Array.set(target, i, value[i]); } return target; } public static Value minValue(ValueGroup valueGroup, Value value) { return switch (valueGroup) { case TEXT -> MIN_STRING; case NUMBER -> MIN_NUMBER; case GEOMETRY -> minPointValue((PointValue) value); case DATE -> DateValue.MIN_VALUE; case LOCAL_DATE_TIME -> LocalDateTimeValue.MIN_VALUE; case ZONED_DATE_TIME -> DateTimeValue.MIN_VALUE; case LOCAL_TIME -> LocalTimeValue.MIN_VALUE; case ZONED_TIME -> TimeValue.MIN_VALUE; default -> throw new IllegalStateException( format("The minValue for valueGroup %s is not defined yet", valueGroup)); }; } public static Value maxValue(ValueGroup valueGroup, Value value) { return switch (valueGroup) { case TEXT -> MAX_STRING; case NUMBER -> MAX_NUMBER; case GEOMETRY -> maxPointValue((PointValue) value); case DATE -> DateValue.MAX_VALUE; case LOCAL_DATE_TIME -> LocalDateTimeValue.MAX_VALUE; case ZONED_DATE_TIME -> DateTimeValue.MAX_VALUE; case LOCAL_TIME -> LocalTimeValue.MAX_VALUE; case ZONED_TIME -> TimeValue.MAX_VALUE; default -> throw new IllegalStateException( format("The maxValue for valueGroup %s is not defined yet", valueGroup)); }; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy