diff --git a/community/values/src/main/java/org/neo4j/values/ValueComparator.java b/community/values/src/main/java/org/neo4j/values/ValueComparator.java index fc76c5a73957c..f116601aceafc 100644 --- a/community/values/src/main/java/org/neo4j/values/ValueComparator.java +++ b/community/values/src/main/java/org/neo4j/values/ValueComparator.java @@ -29,10 +29,14 @@ class ValueComparator implements Comparator { private final Comparator valueGroupComparator; + private final Comparator virtualValueComparator; - ValueComparator( Comparator valueGroupComparator ) + ValueComparator( + Comparator valueGroupComparator, + Comparator virtualValueComparator ) { this.valueGroupComparator = valueGroupComparator; + this.virtualValueComparator = virtualValueComparator; } @Override @@ -76,6 +80,9 @@ public int compare( Value v1, Value v2 ) case BOOLEAN_ARRAY: return ((BooleanArray) v1).compareTo( (BooleanArray) v2 ); + case VIRTUAL: + return virtualValueComparator.compare( (VirtualValue)v1, (VirtualValue)v2 ); + default: throw new UnsupportedOperationException( format( "Unknown ValueGroup id '%s'", id1 ) ); } diff --git a/community/values/src/main/java/org/neo4j/values/ValueGroup.java b/community/values/src/main/java/org/neo4j/values/ValueGroup.java index abfd91ce649fc..767b7fc1118ed 100644 --- a/community/values/src/main/java/org/neo4j/values/ValueGroup.java +++ b/community/values/src/main/java/org/neo4j/values/ValueGroup.java @@ -33,7 +33,8 @@ public enum ValueGroup INTEGER_ARRAY( 3 ), FLOAT_ARRAY( 3 ), TEXT_ARRAY( 4 ), - BOOLEAN_ARRAY( 5 ); + BOOLEAN_ARRAY( 5 ), + VIRTUAL( 6 ); private final int comparabilityGroup; diff --git a/community/values/src/main/java/org/neo4j/values/Values.java b/community/values/src/main/java/org/neo4j/values/Values.java index d79f6213fa333..9b7fadfa2094c 100644 --- a/community/values/src/main/java/org/neo4j/values/Values.java +++ b/community/values/src/main/java/org/neo4j/values/Values.java @@ -20,6 +20,7 @@ package org.neo4j.values; import java.lang.reflect.Array; +import java.util.Comparator; /** * Entry point to the values library. @@ -49,6 +50,16 @@ public class ValueLoadException extends RuntimeException { } + /** + * Default value comparator. Will correctly compare all storable values and order the value groups according the + * to comparability group. Virtual values are sorted in a random but deterministic fashion (by hashCode). + */ + public static final ValueComparator VALUE_COMPARATOR = + new ValueComparator( + Comparator.comparingInt( ValueGroup::comparabilityGroup ), + Comparator.comparingInt( VirtualValue::hashCode ) + ); + // DIRECT FACTORY METHODS public static final Value NO_VALUE = NoValue.NO_VALUE; diff --git a/community/values/src/main/java/org/neo4j/values/VirtualValue.java b/community/values/src/main/java/org/neo4j/values/VirtualValue.java new file mode 100644 index 0000000000000..b85758cb27743 --- /dev/null +++ b/community/values/src/main/java/org/neo4j/values/VirtualValue.java @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2002-2017 "Neo Technology," + * Network Engine for Objects in Lund AB [http://neotechnology.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; + +/** + * Value that can exist transiently during computations, but that cannot be stored as a property value. A Virtual + * Value could be a NodeReference for example. + */ +public abstract class VirtualValue extends Value +{ + @Override + public final boolean equals( byte[] x ) + { + return false; + } + + @Override + public final boolean equals( short[] x ) + { + return false; + } + + @Override + public final boolean equals( int[] x ) + { + return false; + } + + @Override + public final boolean equals( long[] x ) + { + return false; + } + + @Override + public final boolean equals( float[] x ) + { + return false; + } + + @Override + public final boolean equals( double[] x ) + { + return false; + } + + @Override + public final boolean equals( boolean x ) + { + return false; + } + + @Override + public final boolean equals( boolean[] x ) + { + return false; + } + + @Override + public final boolean equals( char x ) + { + return false; + } + + @Override + public final boolean equals( String x ) + { + return false; + } + + @Override + public final boolean equals( char[] x ) + { + return false; + } + + @Override + public final boolean equals( String[] x ) + { + return false; + } + + @Override + public final boolean equals( Object other ) + { + return other != null && other instanceof VirtualValue && equals( (VirtualValue) other ); + } + + @Override + public final boolean equals( Value other ) + { + return other != null && other instanceof VirtualValue && equals( (VirtualValue) other ); + } + + @Override + public final int hashCode() + { + return hash(); + } + + public abstract int hash(); + + public abstract boolean equals( VirtualValue other ); + + @Override + public final ValueGroup valueGroup() + { + return ValueGroup.VIRTUAL; + } +} diff --git a/community/values/src/test/java/org/neo4j/values/MyVirtualValue.java b/community/values/src/test/java/org/neo4j/values/MyVirtualValue.java new file mode 100644 index 0000000000000..a3ef912a8222e --- /dev/null +++ b/community/values/src/test/java/org/neo4j/values/MyVirtualValue.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2002-2017 "Neo Technology," + * Network Engine for Objects in Lund AB [http://neotechnology.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; + +public class MyVirtualValue extends VirtualValue +{ + private final int hashCode; + + MyVirtualValue( int hashCode ) + { + this.hashCode = hashCode; + } + + @Override + public boolean equals( VirtualValue other ) + { + return this == other; + } + + @Override + public int hash() + { + return hashCode; + } + + @Override + public void writeTo( ValueWriter writer ) + { + } + + @Override + public Object asPublic() + { + return this; + } +} diff --git a/community/values/src/test/java/org/neo4j/values/ValueComparisonTest.java b/community/values/src/test/java/org/neo4j/values/ValueComparisonTest.java index cc74660614cb9..77f368890d9bb 100644 --- a/community/values/src/test/java/org/neo4j/values/ValueComparisonTest.java +++ b/community/values/src/test/java/org/neo4j/values/ValueComparisonTest.java @@ -25,6 +25,8 @@ import java.util.Arrays; import java.util.Comparator; +import java.util.List; +import java.util.stream.Collectors; import static java.lang.String.format; @@ -33,8 +35,7 @@ public class ValueComparisonTest @Rule public ExpectedException thrown = ExpectedException.none(); - private ValueComparator comparator = new ValueComparator( - Comparator.comparingInt( ValueGroup::comparabilityGroup ) ); + private ValueComparator comparator = Values.VALUE_COMPARATOR; private Object[] objs = new Object[]{ // OTHER @@ -92,14 +93,16 @@ public class ValueComparisonTest @Test public void shouldOrderValuesCorrectly() { - Value[] values = Arrays.stream( objs ).map( Values::of ).toArray( Value[]::new ); + List values = Arrays.stream( objs ).map( Values::of ).collect( Collectors.toList() ); + values.add( new MyVirtualValue( 0 ) ); + values.add( new MyVirtualValue( 1 ) ); - for ( int i = 0; i < values.length; i++ ) + for ( int i = 0; i < values.size(); i++ ) { - for ( int j = 0; j < values.length; j++ ) + for ( int j = 0; j < values.size(); j++ ) { - Value left = values[i]; - Value right = values[j]; + Value left = values.get( i ); + Value right = values.get( j ); int cmpPos = sign( i - j ); int cmpVal = sign( compare( comparator, left, right ) ); diff --git a/community/values/src/test/java/org/neo4j/values/ValuesTest.java b/community/values/src/test/java/org/neo4j/values/ValuesTest.java index 7c0f6e7f7a4ea..0aeeeff6a4cfa 100644 --- a/community/values/src/test/java/org/neo4j/values/ValuesTest.java +++ b/community/values/src/test/java/org/neo4j/values/ValuesTest.java @@ -21,6 +21,7 @@ import org.junit.Test; +import static junit.framework.TestCase.assertFalse; import static org.junit.Assert.assertTrue; import static org.neo4j.values.Values.booleanArray; import static org.neo4j.values.Values.booleanValue; @@ -118,10 +119,31 @@ public void shouldBeEqualToItself() assertEqual( stringArray( new String[]{"hi"} ), lazyStringArray( () -> new String[]{"hi"} ) ); } + @Test + public void shouldNotEqualVirtualValue() + { + VirtualValue virtual = new MyVirtualValue( 42 ); + + assertNotEqual( booleanValue( false ), virtual ); + assertNotEqual( byteValue( (byte)0 ), virtual ); + assertNotEqual( shortValue( (short)0 ), virtual ); + assertNotEqual( intValue( 0 ), virtual ); + assertNotEqual( longValue( 0 ), virtual ); + assertNotEqual( floatValue( 0.0f ), virtual ); + assertNotEqual( doubleValue( 0.0 ), virtual ); + assertNotEqual( stringValue( "" ), virtual ); + } + private void assertEqual( Value a, Value b ) { assertTrue( "should be equal", a.equals( b ) ); assertTrue( "should be equal", b.equals( a ) ); assertTrue( "should have same has", a.hashCode() == b.hashCode() ); } + + private void assertNotEqual( Value a, Value b ) + { + assertFalse( "should not be equal", a.equals( b ) ); + assertFalse( "should not be equal", b.equals( a ) ); + } }