Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* 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.Double.parseDouble;
import static java.lang.Long.parseLong;
import static java.time.temporal.ChronoField.EPOCH_DAY;
import static java.time.temporal.ChronoField.NANO_OF_SECOND;
import static java.time.temporal.ChronoField.OFFSET_SECONDS;
import static java.time.temporal.ChronoField.SECOND_OF_MINUTE;
import static java.time.temporal.ChronoUnit.DAYS;
import static java.time.temporal.ChronoUnit.MONTHS;
import static java.time.temporal.ChronoUnit.NANOS;
import static java.time.temporal.ChronoUnit.SECONDS;
import static java.util.Objects.requireNonNull;
import static java.util.regex.Pattern.CASE_INSENSITIVE;
import static org.neo4j.memory.HeapEstimator.shallowSizeOfInstance;
import static org.neo4j.values.storable.NumberType.NO_NUMBER;
import static org.neo4j.values.storable.NumberValue.castToLong;
import static org.neo4j.values.storable.NumberValue.safeCastFloatingPoint;
import static org.neo4j.values.utils.TemporalUtil.AVG_NANOS_PER_MONTH;
import static org.neo4j.values.utils.TemporalUtil.AVG_SECONDS_PER_MONTH;
import static org.neo4j.values.utils.TemporalUtil.NANOS_PER_SECOND;
import static org.neo4j.values.utils.TemporalUtil.SECONDS_PER_DAY;
import static org.neo4j.values.utils.ValueMath.HASH_CONSTANT;
import java.time.DateTimeException;
import java.time.Duration;
import java.time.Period;
import java.time.temporal.ChronoUnit;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAmount;
import java.time.temporal.TemporalUnit;
import java.time.temporal.UnsupportedTemporalTypeException;
import java.util.Comparator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Stream;
import org.neo4j.exceptions.InvalidArgumentException;
import org.neo4j.exceptions.UnsupportedTemporalUnitException;
import org.neo4j.gqlstatus.GqlHelper;
import org.neo4j.hashing.HashFunction;
import org.neo4j.values.AnyValue;
import org.neo4j.values.Comparison;
import org.neo4j.values.Equality;
import org.neo4j.values.StructureBuilder;
import org.neo4j.values.ValueMapper;
import org.neo4j.values.virtual.MapValue;
/**
* We use our own implementation because neither {@link java.time.Duration} nor {@link java.time.Period} fits our needs.
* {@link java.time.Duration} only works with seconds, assumes 24H days, and is unable to handle larger units than days.
* {@link java.time.Period} only works with units from days or larger, and does not deal with time.
*/
public final class DurationValue extends ScalarValue implements TemporalAmount, Comparable {
static final long SHALLOW_SIZE = shallowSizeOfInstance(DurationValue.class);
public static final DurationValue MIN_VALUE = duration(0, 0, Long.MIN_VALUE, 0);
public static final DurationValue MAX_VALUE = duration(0, 0, Long.MAX_VALUE, 999_999_999);
public static final DurationValue ZERO = new DurationValue(0, 0, 0, 0);
private static final List UNITS = List.of(MONTHS, DAYS, SECONDS, NANOS);
// This comparator is safe until 292,271,023,045 years. After that, we have an overflow.
private static final Comparator COMPARATOR = Comparator.comparingLong(
DurationValue::getAverageLengthInSeconds)
.thenComparingLong(d -> d.nanos) // nanos are guaranteed to be smaller than NANOS_PER_SECOND
.thenComparingLong(
d -> d.months) // At this point, the durations have the same length and we compare by the individual
// fields.
.thenComparingLong(d -> d.days)
.thenComparingLong(d -> d.seconds);
private final long months;
private final long days;
private final long seconds;
private final int nanos;
private DurationValue(long months, long days, long seconds, long nanos) {
assertNoOverflow(months, days, seconds, nanos);
seconds = secondsWithNanos(seconds, nanos);
nanos %= NANOS_PER_SECOND;
// normalize nanos to be between 0 and NANOS_PER_SECOND-1
if (nanos < 0) {
seconds -= 1;
nanos += NANOS_PER_SECOND;
}
this.months = months;
this.days = days;
this.seconds = seconds;
this.nanos = (int) nanos;
}
private static DurationValue newDuration(long months, long days, long seconds, long nanos) {
return seconds == 0 && days == 0 && months == 0 && nanos == 0 // ordered by probability of non-zero
? ZERO
: new DurationValue(months, days, seconds, nanos);
}
public static DurationValue duration(Duration value) {
requireNonNull(value, "Duration");
return newDuration(0, 0, value.getSeconds(), value.getNano());
}
public static DurationValue duration(Period value) {
requireNonNull(value, "Period");
return newDuration(value.toTotalMonths(), value.getDays(), 0, 0);
}
public static DurationValue duration(long months, long days, long seconds, long nanos) {
return newDuration(months, days, seconds, nanos);
}
public static DurationValue parse(CharSequence text) {
return TemporalValue.parse(DurationValue.class, PATTERN, DurationValue::parse, text);
}
public static DurationValue parse(TextValue text) {
return TemporalValue.parse(DurationValue.class, PATTERN, DurationValue::parse, text);
}
public static DurationValue build(MapValue map) {
return StructureBuilder.build(builder(), map);
}
public static DurationValue between(TemporalUnit unit, Temporal from, Temporal to) {
if (unit == null) {
return durationBetween(from, to);
} else if (unit instanceof ChronoUnit) {
switch ((ChronoUnit) unit) {
case MONTHS:
return newDuration(assertValidUntil(from, to, unit), 0, 0, 0);
case DAYS:
return newDuration(0, assertValidUntil(from, to, unit), 0, 0);
case SECONDS:
return durationInSecondsAndNanos(from, to);
default:
throw new UnsupportedTemporalUnitException("Unsupported unit: " + unit);
}
} else {
throw new UnsupportedTemporalUnitException("Unsupported unit: " + unit);
}
}
private static StructureBuilder builder() {
return new DurationBuilder<>() {
@Override
DurationValue create(
AnyValue years,
AnyValue months,
AnyValue weeks,
AnyValue days,
AnyValue hours,
AnyValue minutes,
AnyValue seconds,
AnyValue milliseconds,
AnyValue microseconds,
AnyValue nanoseconds) {
var allIntegralValues = Stream.of(
years,
months,
weeks,
days,
hours,
minutes,
seconds,
milliseconds,
microseconds,
nanoseconds)
.allMatch(t -> t == null || t instanceof IntegralValue);
if (allIntegralValues) {
return duration(
castToLong("years", years) * 12 + castToLong("months", months),
castToLong("weeks", weeks) * 7 + castToLong("days", days),
castToLong("hours", hours) * 3600
+ castToLong("minutes", minutes) * 60
+ castToLong("seconds", seconds),
castToLong("milliseconds", milliseconds) * 1_000_000
+ castToLong("microseconds", microseconds) * 1_000
+ castToLong("nanoseconds", nanoseconds));
} else {
return approximate(
safeCastFloatingPoint("years", years, 0) * 12 + safeCastFloatingPoint("months", months, 0),
safeCastFloatingPoint("weeks", weeks, 0) * 7 + safeCastFloatingPoint("days", days, 0),
safeCastFloatingPoint("hours", hours, 0) * 3600
+ safeCastFloatingPoint("minutes", minutes, 0) * 60
+ safeCastFloatingPoint("seconds", seconds, 0),
safeCastFloatingPoint("milliseconds", milliseconds, 0) * 1_000_000
+ safeCastFloatingPoint("microseconds", microseconds, 0) * 1_000
+ safeCastFloatingPoint("nanoseconds", nanoseconds, 0));
}
}
};
}
@Override
public int compareTo(DurationValue other) {
return COMPARATOR.compare(this, other);
}
@Override
protected int unsafeCompareTo(Value otherValue) {
return compareTo((DurationValue) otherValue);
}
@Override
Comparison unsafeTernaryCompareTo(Value other) {
if (ternaryEquals(other) == Equality.TRUE) {
return Comparison.EQUAL;
} else {
return Comparison.UNDEFINED;
}
}
@Override
public boolean isIncomparableType() {
return true;
}
@Override
public long estimatedHeapUsage() {
return SHALLOW_SIZE;
}
private long getAverageLengthInSeconds() {
return calcAverageLengthInSeconds(this.months, this.days, this.seconds);
}
private static long calcAverageLengthInSeconds(long months, long days, long seconds) {
long daysInSeconds = Math.multiplyExact(days, SECONDS_PER_DAY);
long monthsInSeconds = Math.multiplyExact(months, AVG_SECONDS_PER_MONTH);
return Math.addExact(seconds, Math.addExact(daysInSeconds, monthsInSeconds));
}
private static long secondsWithNanos(long seconds, long nanos) {
return Math.addExact(seconds, nanos / NANOS_PER_SECOND);
}
private static void assertNoOverflow(long months, long days, long seconds, long nanos) {
try {
// This is a mess really..
// Since different values can have different signs (as allowed by Cypher & embedded)
// We need to check all combinations of values for overflow
// Nanos are normalized to [0, NANOS_PER_SEC-1] so first we check that seconds don't overflow
long secondsWithNanos = secondsWithNanos(seconds, nanos);
if (nanos < 0) {
secondsWithNanos = Math.subtractExact(secondsWithNanos, 1);
}
// Then we check that the days+months+seconds dont overflow, with and without the nanos included
calcAverageLengthInSeconds(months, days, seconds);
calcAverageLengthInSeconds(months, days, secondsWithNanos);
} catch (java.lang.ArithmeticException | org.neo4j.exceptions.ArithmeticException e) {
throw invalidDuration(months, days, seconds, nanos, e);
}
}
long nanosOfDay() {
return (seconds % SECONDS_PER_DAY) * NANOS_PER_SECOND + nanos;
}
long totalMonths() {
return months;
}
/**
* The number of days of this duration, as computed by the days and the whole days made up of seconds. This
* excludes the days contributed by the months.
*
* @return the total number of days of this duration.
*/
long totalDays() {
return days + (seconds / SECONDS_PER_DAY);
}
private static final String UNIT_BASED_PATTERN = "(?:(?[-+]?[0-9]+(?:[.,][0-9]+)?)Y)?"
+ "(?:(?[-+]?[0-9]+(?:[.,][0-9]+)?)M)?"
+ "(?:(?[-+]?[0-9]+(?:[.,][0-9]+)?)W)?"
+ "(?:(?[-+]?[0-9]+(?:[.,][0-9]+)?)D)?"
+ "(?T"
+ "(?:(?[-+]?[0-9]+(?:[.,][0-9]+)?)H)?"
+ "(?:(?[-+]?[0-9]+(?:[.,][0-9]+)?)M)?"
+ "(?:(?[-+]?[0-9]+)(?:[.,](?[0-9]{1,9}))?S)?)?";
private static final String DATE_BASED_PATTERN = "(?:"
+ "(?[0-9]{4})(?:"
+ "-(?[0-9]{2})-(?[0-9]{2})|"
+ "(?[0-9]{2})(?[0-9]{2}))"
+ ")?(?