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

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

There is a newer version: 5.25.1
Show newest version
/*
 * 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.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.OffsetTime;
import java.time.ZonedDateTime;

import org.neo4j.exceptions.CypherTypeException;
import org.neo4j.values.AnyValue;
import org.neo4j.values.SequenceValue;

/**
 * Enumerates the different Value types and facilitates creating arrays without resorting to reflection or
 * instance of checking at runtime.
 */
public enum ValueRepresentation
{
    UNKNOWN( ValueGroup.UNKNOWN, false )
            {
                @Override
                public ValueRepresentation coerce( ValueRepresentation other )
                {
                    return UNKNOWN;
                }
            },
    GEOMETRY_ARRAY( ValueGroup.GEOMETRY_ARRAY, false ),
    ZONED_DATE_TIME_ARRAY( ValueGroup.ZONED_DATE_TIME_ARRAY, false ),
    LOCAL_DATE_TIME_ARRAY( ValueGroup.LOCAL_DATE_TIME_ARRAY, false ),
    DATE_ARRAY( ValueGroup.DATE_ARRAY, false ),
    ZONED_TIME_ARRAY( ValueGroup.ZONED_TIME_ARRAY, false ),
    LOCAL_TIME_ARRAY( ValueGroup.LOCAL_TIME_ARRAY, false ),
    DURATION_ARRAY( ValueGroup.DURATION_ARRAY, false ),
    TEXT_ARRAY( ValueGroup.TEXT_ARRAY, false ),
    BOOLEAN_ARRAY( ValueGroup.BOOLEAN_ARRAY, false ),
    INT64_ARRAY( ValueGroup.NUMBER_ARRAY, false ),
    INT32_ARRAY( ValueGroup.NUMBER_ARRAY, false ),
    INT16_ARRAY( ValueGroup.NUMBER_ARRAY, false ),
    INT8_ARRAY( ValueGroup.NUMBER_ARRAY, false ),
    FLOAT64_ARRAY( ValueGroup.NUMBER_ARRAY, false ),
    FLOAT32_ARRAY( ValueGroup.NUMBER_ARRAY, false ),
    GEOMETRY( ValueGroup.GEOMETRY, true )
            {
                @Override
                public ArrayValue arrayOf( SequenceValue values )
                {
                    PointValue[] points = new PointValue[values.length()];
                    int i = 0;
                    PointValue first = null;
                    for ( AnyValue value : values )
                    {
                        PointValue current = getOrFail( value, PointValue.class );
                        if ( first == null )
                        {
                            first = current;
                        }
                        else
                        {
                            if ( !first.getCoordinateReferenceSystem().equals( current.getCoordinateReferenceSystem() ) )
                            {
                                throw new CypherTypeException( "Collections containing point values with different CRS can not be stored in properties." );
                            }
                            else if ( first.coordinate().length != current.coordinate().length )
                            {
                                throw new CypherTypeException(
                                        "Collections containing point values with different dimensions can not be stored in properties." );
                            }
                        }
                        points[i++] = current;
                    }
                    return Values.pointArray( points );
                }
            },
    ZONED_DATE_TIME( ValueGroup.ZONED_DATE_TIME, true )
            {
                @Override
                public ArrayValue arrayOf( SequenceValue values )
                {
                    ZonedDateTime[] temporals = new ZonedDateTime[values.length()];
                    int i = 0;
                    for ( AnyValue value : values )
                    {
                        temporals[i++] = (getOrFail( value, DateTimeValue.class )).temporal();
                    }
                    return Values.dateTimeArray( temporals );
                }
            },
    LOCAL_DATE_TIME( ValueGroup.LOCAL_DATE_TIME, true )
            {
                @Override
                public ArrayValue arrayOf( SequenceValue values )
                {
                    LocalDateTime[] temporals = new LocalDateTime[values.length()];
                    int i = 0;
                    for ( AnyValue value : values )
                    {
                        temporals[i++] = getOrFail( value, LocalDateTimeValue.class ).temporal();
                    }
                    return Values.localDateTimeArray( temporals );
                }
            },
    DATE( ValueGroup.DATE, true )
            {
                @Override
                public ArrayValue arrayOf( SequenceValue values )
                {
                    LocalDate[] temporals = new LocalDate[values.length()];
                    int i = 0;
                    for ( AnyValue value : values )
                    {
                        temporals[i++] = getOrFail( value, DateValue.class ).temporal();
                    }
                    return Values.dateArray( temporals );
                }
            },
    ZONED_TIME( ValueGroup.ZONED_TIME, true )
            {
                @Override
                public ArrayValue arrayOf( SequenceValue values )
                {
                    OffsetTime[] temporals = new OffsetTime[values.length()];
                    int i = 0;
                    for ( AnyValue value : values )
                    {
                        temporals[i++] = ((TimeValue) value).temporal();
                    }
                    return Values.timeArray( temporals );
                }
            },
    LOCAL_TIME( ValueGroup.LOCAL_TIME, true )
            {
                @Override
                public ArrayValue arrayOf( SequenceValue values )
                {
                    LocalTime[] temporals = new LocalTime[values.length()];
                    int i = 0;
                    for ( AnyValue value : values )
                    {
                        temporals[i++] = ((LocalTimeValue) value).temporal();
                    }
                    return Values.localTimeArray( temporals );
                }
            },
    DURATION( ValueGroup.DURATION, true )
            {
                @Override
                public ArrayValue arrayOf( SequenceValue values )
                {
                    DurationValue[] temporals = new DurationValue[values.length()];
                    int i = 0;
                    for ( AnyValue value : values )
                    {
                        temporals[i++] = (DurationValue) value;
                    }
                    return Values.durationArray( temporals );
                }
            },
    UTF16_TEXT( ValueGroup.TEXT, true )
            {
                @Override
                public ArrayValue arrayOf( SequenceValue values )
                {
                    String[] strings = new String[values.length()];
                    int i = 0;
                    for ( AnyValue value : values )
                    {
                        strings[i++] = ((TextValue) value).stringValue();
                    }
                    return Values.stringArray( strings );
                }

                @Override
                public ValueRepresentation coerce( ValueRepresentation other )
                {
                    switch ( other )
                    {
                    case UTF8_TEXT:
                    case UTF16_TEXT:
                        return UTF16_TEXT;
                    default:
                        return UNKNOWN;
                    }
                }
            },
    UTF8_TEXT( ValueGroup.TEXT, true )
            {
                @Override
                public ArrayValue arrayOf( SequenceValue values )
                {
                    String[] strings = new String[values.length()];
                    int i = 0;
                    for ( AnyValue value : values )
                    {
                        strings[i++] = ((TextValue) value).stringValue();
                    }
                    return Values.stringArray( strings );
                }

                @Override
                public ValueRepresentation coerce( ValueRepresentation other )
                {
                    switch ( other )
                    {
                    case UTF8_TEXT:
                        return UTF8_TEXT;
                    case UTF16_TEXT:
                        return UTF16_TEXT;
                    default:
                        return UNKNOWN;
                    }
                }
            },
    BOOLEAN( ValueGroup.BOOLEAN, true )
            {
                @Override
                public ArrayValue arrayOf( SequenceValue values )
                {
                    boolean[] bools = new boolean[values.length()];
                    int i = 0;
                    for ( AnyValue value : values )
                    {
                        bools[i++] = ((BooleanValue) value).booleanValue();
                    }
                    return Values.booleanArray( bools );
                }
            },
    INT64( ValueGroup.NUMBER, true )
            {
                @Override
                public ArrayValue arrayOf( SequenceValue values )
                {
                    long[] longs = new long[values.length()];
                    int i = 0;
                    for ( AnyValue value : values )
                    {
                        longs[i++] = getOrFail( value, NumberValue.class ).longValue();
                    }
                    return Values.longArray( longs );
                }

                @Override
                public ValueRepresentation coerce( ValueRepresentation other )
                {
                    switch ( other )
                    {
                    case INT8:
                    case INT16:
                    case INT32:
                    case INT64:
                        return this;
                    case FLOAT32:
                    case FLOAT64:
                        return FLOAT64;
                    default:
                        return ValueRepresentation.UNKNOWN;
                    }
                }
            },
    INT32( ValueGroup.NUMBER, true )
            {
                @Override
                public ArrayValue arrayOf( SequenceValue values )
                {
                    int[] ints = new int[values.length()];
                    int i = 0;
                    for ( AnyValue value : values )
                    {
                        ints[i++] = getOrFail( value, IntegralValue.class ).intValue();
                    }
                    return Values.intArray( ints );
                }

                @Override
                public ValueRepresentation coerce( ValueRepresentation other )
                {
                    switch ( other )
                    {
                    case INT8:
                    case INT16:
                    case INT32:
                        return this;
                    case INT64:
                        return INT64;
                    case FLOAT32:
                    case FLOAT64:
                        return FLOAT64;
                    default:
                        return ValueRepresentation.UNKNOWN;
                    }
                }
            },
    INT16( ValueGroup.NUMBER, true )
            {
                @Override
                public ArrayValue arrayOf( SequenceValue values )
                {
                    short[] shorts = new short[values.length()];
                    int i = 0;
                    for ( AnyValue value : values )
                    {
                        shorts[i++] = getOrFail( value, IntegralValue.class ).shortValue();
                    }
                    return Values.shortArray( shorts );
                }

                @Override
                public ValueRepresentation coerce( ValueRepresentation other )
                {
                    switch ( other )
                    {
                    case INT8:
                    case INT16:
                        return this;
                    case INT32:
                        return INT32;
                    case INT64:
                        return INT64;
                    case FLOAT32:
                        return FLOAT32;
                    case FLOAT64:
                        return FLOAT64;
                    default:
                        return ValueRepresentation.UNKNOWN;
                    }
                }
            },

    INT8( ValueGroup.NUMBER, true )
            {
                @Override
                public ArrayValue arrayOf( SequenceValue values )
                {
                    byte[] bytes = new byte[values.length()];
                    int i = 0;
                    for ( AnyValue value : values )
                    {
                        bytes[i++] = getOrFail( value, ByteValue.class ).value();
                    }
                    return Values.byteArray( bytes );
                }

                @Override
                public ValueRepresentation coerce( ValueRepresentation other )
                {
                    switch ( other )
                    {
                    case INT8:
                        return this;
                    case INT16:
                        return INT16;
                    case INT32:
                        return INT32;
                    case INT64:
                        return INT64;
                    case FLOAT32:
                        return FLOAT32;
                    case FLOAT64:
                        return FLOAT64;
                    default:
                        return ValueRepresentation.UNKNOWN;
                    }
                }
            },
    FLOAT64( ValueGroup.NUMBER, true )
            {
                @Override
                public ArrayValue arrayOf( SequenceValue values )
                {
                    double[] doubles = new double[values.length()];
                    int i = 0;
                    for ( AnyValue value : values )
                    {
                        doubles[i++] = ((NumberValue) value).doubleValue();
                    }
                    return Values.doubleArray( doubles );
                }

                @Override
                public ValueRepresentation coerce( ValueRepresentation other )
                {
                    switch ( other )
                    {
                    case INT8:
                    case INT16:
                    case INT32:
                    case INT64:
                    case FLOAT32:
                    case FLOAT64:
                        return this;
                    default:
                        return ValueRepresentation.UNKNOWN;
                    }
                }
            },
    FLOAT32( ValueGroup.NUMBER, true )
            {
                @Override
                public ArrayValue arrayOf( SequenceValue values )
                {
                    float[] floats = new float[values.length()];
                    int i = 0;
                    for ( AnyValue value : values )
                    {
                        NumberValue asNumberValue = getOrFail( value, NumberValue.class );
                        if ( asNumberValue instanceof FloatValue )
                        {
                            floats[i] = ((FloatValue) asNumberValue).value();
                        }
                        else
                        {
                            floats[i] = asNumberValue.longValue();
                        }
                        i++;
                    }
                    return Values.floatArray( floats );
                }

                @Override
                public ValueRepresentation coerce( ValueRepresentation other )
                {
                    switch ( other )
                    {
                    case INT8:
                    case INT16:
                    case FLOAT32:
                        return this;
                    case INT32:
                    case INT64:
                    case FLOAT64:
                        return FLOAT64;
                    default:
                        return ValueRepresentation.UNKNOWN;
                    }
                }
            },
    NO_VALUE( ValueGroup.NO_VALUE, false );

    private final ValueGroup group;
    private final boolean canCreateArrayOf;

    ValueRepresentation( ValueGroup group, boolean canCreateArrayOf )
    {
        this.group = group;
        this.canCreateArrayOf = canCreateArrayOf;
    }

    public boolean canCreateArrayOfValueGroup()
    {
        return canCreateArrayOf;
    }

    public ValueGroup valueGroup()
    {
        return group;
    }

    /**
     * Creates an array of the corresponding type.
     *
     * NOTE: must call {@link #canCreateArrayOf} before calling this method.
     * NOTE: it is responsibility of the caller to make sure the provided values all are of the correct type
     *       if not a ClassCastException will be thrown.
     * @param values The values (of the correct type) to create the array of.
     * @return An array of the provided values.
     */
    public ArrayValue arrayOf( SequenceValue values )
    {
        //NOTE: coming here means that we know we'll fail, just a matter of finding an appropriate error message.
        AnyValue prev = null;
        for ( AnyValue value : values )
        {
            if ( value == Values.NO_VALUE )
            {
                throw new CypherTypeException(
                        "Collections containing null values can not be stored in properties." );
            }
            else if ( value instanceof SequenceValue )
            {
                throw new CypherTypeException(
                        "Collections containing collections can not be stored in properties." );
            }
            else if ( prev != null && prev.valueRepresentation().valueGroup() != (value.valueRepresentation().valueGroup()) )
            {
                throw new CypherTypeException( "Neo4j only supports a subset of Cypher types for storage as singleton or array properties. " +
                                               "Please refer to section cypher/syntax/values of the manual for more details." );
            }
            else if ( !value.valueRepresentation().canCreateArrayOfValueGroup() )
            {
                throw new CypherTypeException( String.format( "Property values can only be of primitive types or arrays thereof. Encountered: %s.", value ) );
            }
            prev = value;
        }

        throw failure();
    }

    /**
     * Finds a representation which fits this and provided representation.
     * @param other the representation to coerce.
     * @return a representation that can handle both representations.
     */
    public ValueRepresentation coerce( ValueRepresentation other )
    {
        return valueGroup() == other.valueGroup() ? this : ValueRepresentation.UNKNOWN;
    }

    private static  T getOrFail( AnyValue value, Class type )
    {
        if ( type.isAssignableFrom( value.getClass() ) )
        {
            return type.cast( value );
        }
        else if ( value == Values.NO_VALUE )
        {
            throw new CypherTypeException(
                    "Collections containing null values can not be stored in properties." );
        }
        else if ( value instanceof SequenceValue )
        {
            throw new CypherTypeException(
                    "Collections containing collections can not be stored in properties." );
        }
        else
        {
            throw failure();
        }
    }

    private static CypherTypeException failure()
    {
        return new CypherTypeException( "Property values can only be of primitive types or arrays thereof" );
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy