org.neo4j.kernel.impl.util.BaseToObjectValueWriter Maven / Gradle / Ivy
Show all versions of neo4j-kernel Show documentation
/*
* 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.kernel.impl.util;
import java.lang.reflect.Array;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.OffsetTime;
import java.time.ZonedDateTime;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Deque;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.neo4j.graphdb.Entity;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Path;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.spatial.Point;
import org.neo4j.graphdb.traversal.Paths;
import org.neo4j.internal.helpers.collection.ReverseArrayIterator;
import org.neo4j.values.AnyValueWriter;
import org.neo4j.values.storable.CoordinateReferenceSystem;
import org.neo4j.values.storable.DurationValue;
import org.neo4j.values.storable.TextArray;
import org.neo4j.values.storable.TextValue;
import org.neo4j.values.virtual.MapValue;
import org.neo4j.values.virtual.NodeValue;
import org.neo4j.values.virtual.RelationshipValue;
import static org.neo4j.internal.helpers.collection.Iterators.iteratorsEqual;
/**
* Base class for converting AnyValue to normal java objects.
*
* This base class takes care of converting all "normal" java types such as
* number types, booleans, strings, arrays and lists. It leaves to the extending
* class to handle neo4j specific types such as nodes, edges and points.
*
* @param the exception thrown on error.
*/
public abstract class BaseToObjectValueWriter implements AnyValueWriter
{
private final Deque stack = new ArrayDeque<>();
public BaseToObjectValueWriter()
{
stack.push( new ObjectWriter() );
}
protected abstract Node newNodeEntityById( long id );
protected abstract Relationship newRelationshipEntityById( long id );
protected abstract Point newPoint( CoordinateReferenceSystem crs, double[] coordinate );
public Object value()
{
assert stack.size() == 1;
return stack.getLast().value();
}
private void writeValue( Object value )
{
assert !stack.isEmpty();
Writer head = stack.peek();
head.write( value );
}
@Override
public EntityMode entityMode()
{
return EntityMode.FULL;
}
@Override
public void writeNodeReference( long nodeId )
{
throw new UnsupportedOperationException( "Cannot write a raw node reference" );
}
@Override
public void writeNode( long nodeId, TextArray ignore, MapValue properties, boolean ignored )
{
if ( nodeId >= 0 )
{
writeValue( newNodeEntityById( nodeId ) );
}
}
@Override
public void writeVirtualNodeHack( Object node )
{
writeValue( node );
}
@Override
public void writeRelationshipReference( long relId )
{
throw new UnsupportedOperationException( "Cannot write a raw relationship reference" );
}
@Override
public void writeRelationship( long relId, long startNodeId, long endNodeId, TextValue type, MapValue properties, boolean ignored )
{
if ( relId >= 0 )
{
writeValue( newRelationshipEntityById( relId ) );
}
}
@Override
public void writeVirtualRelationshipHack( Object relationship )
{
writeValue( relationship );
}
@Override
public void beginMap( int size )
{
stack.push( new MapWriter( size ) );
}
@Override
public void endMap()
{
assert !stack.isEmpty();
writeValue( stack.pop().value() );
}
@Override
public void beginList( int size )
{
stack.push( new ListWriter( size ) );
}
@Override
public void endList()
{
assert !stack.isEmpty();
writeValue( stack.pop().value() );
}
@Override
public void writePathReference( long[] nodes, long[] relationships ) throws E
{
assert nodes != null;
assert nodes.length > 0;
assert relationships != null;
assert nodes.length == relationships.length + 1;
Node[] nodeProxies = new Node[nodes.length];
for ( int i = 0; i < nodes.length; i++ )
{
nodeProxies[i] = newNodeEntityById( nodes[i] );
}
Relationship[] relProxies = new Relationship[relationships.length];
for ( int i = 0; i < relationships.length; i++ )
{
relProxies[i] = newRelationshipEntityById( relationships[i] );
}
writeValue( new PathProxy( nodeProxies, relProxies ) );
}
@Override
public void writePath( NodeValue[] nodes, RelationshipValue[] relationships )
{
assert nodes != null;
assert nodes.length > 0;
assert relationships != null;
assert nodes.length == relationships.length + 1;
Node[] nodeProxies = new Node[nodes.length];
for ( int i = 0; i < nodes.length; i++ )
{
nodeProxies[i] = newNodeEntityById( nodes[i].id() );
}
Relationship[] relProxies = new Relationship[relationships.length];
for ( int i = 0; i < relationships.length; i++ )
{
relProxies[i] = newRelationshipEntityById( relationships[i].id() );
}
writeValue( new PathProxy( nodeProxies, relProxies ) );
}
@Override
public final void writePoint( CoordinateReferenceSystem crs, double[] coordinate )
{
writeValue( newPoint( crs, coordinate ) );
}
@Override
public void writeNull()
{
writeValue( null );
}
@Override
public void writeBoolean( boolean value )
{
writeValue( value );
}
@Override
public void writeInteger( byte value )
{
writeValue( value );
}
@Override
public void writeInteger( short value )
{
writeValue( value );
}
@Override
public void writeInteger( int value )
{
writeValue( value );
}
@Override
public void writeInteger( long value )
{
writeValue( value );
}
@Override
public void writeFloatingPoint( float value )
{
writeValue( value );
}
@Override
public void writeFloatingPoint( double value )
{
writeValue( value );
}
@Override
public void writeString( String value )
{
writeValue( value );
}
@Override
public void writeString( char value )
{
writeValue( value );
}
@Override
public void beginArray( int size, ArrayType arrayType )
{
stack.push( new ArrayWriter( size, arrayType ) );
}
@Override
public void endArray()
{
assert !stack.isEmpty();
writeValue( stack.pop().value() );
}
@Override
public void writeByteArray( byte[] value )
{
writeValue( value );
}
@Override
public void writeDuration( long months, long days, long seconds, int nanos )
{
writeValue( DurationValue.duration( months, days, seconds, nanos ) );
}
@Override
public void writeDate( LocalDate localDate )
{
writeValue( localDate );
}
@Override
public void writeLocalTime( LocalTime localTime )
{
writeValue( localTime );
}
@Override
public void writeTime( OffsetTime offsetTime )
{
writeValue( offsetTime );
}
@Override
public void writeLocalDateTime( LocalDateTime localDateTime )
{
writeValue( localDateTime );
}
@Override
public void writeDateTime( ZonedDateTime zonedDateTime )
{
writeValue( zonedDateTime );
}
private static class PathProxy implements Path
{
private final Node[] nodes;
private final Relationship[] relationships;
private PathProxy( Node[] nodes, Relationship[] relationships )
{
this.nodes = nodes;
this.relationships = relationships;
}
@Override
public Node startNode()
{
return nodes[0];
}
@Override
public Node endNode()
{
return nodes[nodes.length - 1];
}
@Override
public Relationship lastRelationship()
{
return relationships[relationships.length - 1];
}
@Override
public Iterable relationships()
{
return Arrays.asList( relationships );
}
@Override
public Iterable reverseRelationships()
{
return () -> new ReverseArrayIterator<>( relationships );
}
@Override
public Iterable nodes()
{
return Arrays.asList( nodes );
}
@Override
public Iterable reverseNodes()
{
return () -> new ReverseArrayIterator<>( nodes );
}
@Override
public int length()
{
return relationships.length;
}
@Override
public int hashCode()
{
if ( relationships.length == 0 )
{
return startNode().hashCode();
}
else
{
return Arrays.hashCode( relationships );
}
}
@Override
public boolean equals( Object obj )
{
if ( this == obj )
{
return true;
}
else if ( obj instanceof Path )
{
Path other = (Path) obj;
return startNode().equals( other.startNode() ) &&
iteratorsEqual( this.relationships().iterator(), other.relationships().iterator() );
}
else
{
return false;
}
}
@Override
public Iterator iterator()
{
return new Iterator<>()
{
Iterator extends Entity> current = nodes().iterator();
Iterator extends Entity> next = relationships().iterator();
@Override
public boolean hasNext()
{
return current.hasNext();
}
@Override
public Entity next()
{
try
{
return current.next();
}
finally
{
Iterator extends Entity> temp = current;
current = next;
next = temp;
}
}
@Override
public void remove()
{
next.remove();
}
};
}
@Override
public String toString()
{
return Paths.defaultPathToStringWithNotInTransactionFallback( this );
}
}
private interface Writer
{
void write( Object value );
Object value();
}
private static class ObjectWriter implements Writer
{
private Object value;
@Override
public void write( Object value )
{
this.value = value;
}
@Override
public Object value()
{
return value;
}
}
private static class MapWriter implements Writer
{
private String key;
private boolean isKey = true;
private final Map map;
MapWriter( int size )
{
this.map = new HashMap<>( size );
}
@Override
public void write( Object value )
{
if ( isKey )
{
key = (String) value;
isKey = false;
}
else
{
map.put( key, value );
isKey = true;
}
}
@Override
public Object value()
{
return map;
}
}
private static class ArrayWriter implements Writer
{
protected final Object array;
private int index;
ArrayWriter( int size, ArrayType arrayType )
{
switch ( arrayType )
{
case SHORT:
this.array = Array.newInstance( short.class, size );
break;
case INT:
this.array = Array.newInstance( int.class, size );
break;
case BYTE:
this.array = Array.newInstance( byte.class, size );
break;
case LONG:
this.array = Array.newInstance( long.class, size );
break;
case FLOAT:
this.array = Array.newInstance( float.class, size );
break;
case DOUBLE:
this.array = Array.newInstance( double.class, size );
break;
case BOOLEAN:
this.array = Array.newInstance( boolean.class, size );
break;
case STRING:
this.array = Array.newInstance( String.class, size );
break;
case CHAR:
this.array = Array.newInstance( char.class, size );
break;
default:
this.array = new Object[size];
}
}
@Override
public void write( Object value )
{
Array.set( array, index++, value );
}
@Override
public Object value()
{
return array;
}
}
private static class ListWriter implements Writer
{
private final List