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 [http://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 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.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.neo4j.exceptions.InvalidArgumentException;
import org.neo4j.exceptions.UnsupportedTemporalUnitException;
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;
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.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;
/**
* 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 );
}
static DurationValue build( Map input )
{
StructureBuilder builder = builder();
for ( Map.Entry entry : input.entrySet() )
{
builder.add( entry.getKey(), entry.getValue() );
}
return builder.build();
}
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 )
{
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 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}))"
+ ")?(?