All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
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.
org.neo4j.bolt.v1.messaging.Neo4jPack Maven / Gradle / Ivy
/*
* 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.bolt.v1.messaging;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.neo4j.bolt.v1.messaging.infrastructure.ValueNode;
import org.neo4j.bolt.v1.messaging.infrastructure.ValueRelationship;
import org.neo4j.bolt.v1.packstream.PackInput;
import org.neo4j.bolt.v1.packstream.PackOutput;
import org.neo4j.bolt.v1.packstream.PackStream;
import org.neo4j.bolt.v1.packstream.PackType;
import org.neo4j.bolt.v1.runtime.Neo4jError;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Path;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.spatial.Point;
import org.neo4j.kernel.api.exceptions.Status;
import static org.neo4j.bolt.v1.packstream.PackStream.UNKNOWN_SIZE;
/**
* Extended PackStream packer and unpacker classes for working
* with Neo4j-specific data types, represented as structures.
*/
public class Neo4jPack
{
public static final List EMPTY_LIST = new ArrayList<>();
public static final Map EMPTY_MAP = new HashMap<>();
public static final byte NODE = 'N';
public static final byte RELATIONSHIP = 'R';
public static final byte UNBOUND_RELATIONSHIP = 'r';
public static final byte PATH = 'P';
public static class Packer extends PackStream.Packer
{
private PathPack.Packer pathPacker = new PathPack.Packer();
private Optional error = Optional.empty();
public Packer( PackOutput output )
{
super( output );
}
@SuppressWarnings( "unchecked" )
public void pack( Object obj ) throws IOException
{
// Note: below uses instanceof for quick implementation, this should be swapped over
// to a dedicated
// visitable type that the serializer can simply visit. This would create explicit
// contract for what can
// be serialized and allow performant method dispatch rather than if branching.
if ( obj == null )
{
packNull();
}
else if ( obj instanceof Boolean )
{
pack( (boolean) obj );
}
else if ( obj instanceof Byte || obj instanceof Short || obj instanceof Integer ||
obj instanceof Long )
{
pack( ((Number) obj).longValue() );
}
else if ( obj instanceof Float || obj instanceof Double )
{
pack( ((Number) obj).doubleValue() );
}
else if ( obj instanceof String )
{
pack( (String) obj );
}
else if (obj instanceof Character )
{
pack( (char) obj );
}
else if ( obj instanceof Map )
{
Map map = (Map) obj;
packMapHeader( map.size() );
for ( Map.Entry entry : map.entrySet() )
{
Object key = entry.getKey();
if ( key == null )
{
error = Optional.of( new Error( Status.Request.Invalid,
"Value `null` is not supported as key in maps, must be a non-nullable string." ) );
packNull();
pack( entry.getValue() );
}
else
{
pack( key.toString() );
pack( entry.getValue() );
}
}
}
else if ( obj instanceof Collection )
{
Collection list = (Collection) obj;
packListHeader( list.size() );
for ( Object item : list )
{
pack( item );
}
}
else if ( obj instanceof byte[] )
{
pack( (byte[]) obj );
}
else if ( obj instanceof char[] )
{
char[] array = (char[]) obj;
packListHeader( array.length );
for ( char item : array )
{
pack( item );
}
}
else if ( obj instanceof short[] )
{
short[] array = (short[]) obj;
packListHeader( array.length );
for ( short item : array )
{
pack( item );
}
}
else if ( obj instanceof int[] )
{
int[] array = (int[]) obj;
packListHeader( array.length );
for ( int item : array )
{
pack( item );
}
}
else if ( obj instanceof long[] )
{
long[] array = (long[]) obj;
packListHeader( array.length );
for ( long item : array )
{
pack( item );
}
}
else if ( obj instanceof float[] )
{
float[] array = (float[]) obj;
packListHeader( array.length );
for ( float item : array )
{
pack( item );
}
}
else if ( obj instanceof double[] )
{
double[] array = (double[]) obj;
packListHeader( array.length );
for ( double item : array )
{
pack( item );
}
}
else if ( obj instanceof boolean[] )
{
boolean[] array = (boolean[]) obj;
packListHeader( array.length );
for ( boolean item : array )
{
pack( item );
}
}
else if ( obj.getClass().isArray() )
{
Object[] array = (Object[]) obj;
packListHeader( array.length );
for ( Object item : array )
{
pack( item );
}
}
else if ( obj instanceof Node )
{
ValueNode.pack( this, (Node) obj );
}
else if ( obj instanceof Relationship )
{
ValueRelationship.pack( this, (Relationship) obj );
}
else if ( obj instanceof Path )
{
pathPacker.pack( this, (Path) obj );
}
else if ( obj instanceof Point)
{
error = Optional.of(new Error( Status.Request.Invalid,
"Point is not yet supported as a return type in Bolt"));
packNull();
}
else
{
error = Optional.of(new Error( Status.Request.Invalid,
"Unpackable value " + obj + " of type " + obj.getClass().getName() ));
packNull();
}
}
public void packRawMap( Map map ) throws IOException
{
packMapHeader( map.size() );
for ( Map.Entry entry : map.entrySet() )
{
pack( entry.getKey() );
pack( entry.getValue() );
}
}
public void consumeError( ) throws BoltIOException
{
if (error.isPresent())
{
Error e = error.get();
error = Optional.empty();
throw new BoltIOException( e.status(), e.msg() );
}
}
public boolean hasErrors()
{
return error.isPresent();
}
}
public static class Unpacker extends PackStream.Unpacker
{
private List errors = new ArrayList<>( 2 );
public Unpacker( PackInput input )
{
super( input );
}
public Object unpack() throws IOException
{
PackType valType = peekNextType();
switch ( valType )
{
case BYTES:
return unpackBytes();
case STRING:
return unpackString();
case INTEGER:
return unpackLong();
case FLOAT:
return unpackDouble();
case BOOLEAN:
return unpackBoolean();
case NULL:
// still need to move past the null value
unpackNull();
return null;
case LIST:
{
return unpackList();
}
case MAP:
{
return unpackMap();
}
case STRUCT:
{
unpackStructHeader();
char signature = unpackStructSignature();
switch ( signature )
{
case NODE:
{
throw new BoltIOException( Status.Request.Invalid, "Nodes cannot be unpacked." );
}
case RELATIONSHIP:
{
throw new BoltIOException( Status.Request.Invalid, "Relationships cannot be unpacked." );
}
case UNBOUND_RELATIONSHIP:
{
throw new BoltIOException( Status.Request.Invalid, "Relationships cannot be unpacked." );
}
case PATH:
{
throw new BoltIOException( Status.Request.Invalid, "Paths cannot be unpacked." );
}
default:
throw new BoltIOException( Status.Request.InvalidFormat,
"Unknown struct type: " + Integer.toHexString( signature ) );
}
}
case END_OF_STREAM:
{
unpackEndOfStream();
return null;
}
default:
throw new BoltIOException( Status.Request.InvalidFormat,
"Unknown value type: " + valType );
}
}
public List unpackList() throws IOException
{
int size = (int) unpackListHeader();
if ( size == 0 )
{
return EMPTY_LIST;
}
ArrayList list;
if ( size == UNKNOWN_SIZE )
{
list = new ArrayList<>();
boolean more = true;
while ( more )
{
PackType keyType = peekNextType();
switch ( keyType )
{
case END_OF_STREAM:
unpack();
more = false;
break;
default:
list.add( unpack() );
}
}
}
else
{
list = new ArrayList<>( size );
for ( int i = 0; i < size; i++ )
{
list.add( unpack() );
}
}
return list;
}
public Map unpackMap() throws IOException
{
int size = (int) unpackMapHeader();
if ( size == 0 )
{
return EMPTY_MAP;
}
Map map;
if ( size == UNKNOWN_SIZE )
{
map = new HashMap<>();
boolean more = true;
while ( more )
{
PackType keyType = peekNextType();
String key;
Object val;
switch ( keyType )
{
case END_OF_STREAM:
unpack();
more = false;
break;
case STRING:
key = unpackString();
val = unpack();
if ( map.put( key, val ) != null )
{
errors.add(
Neo4jError.from( Status.Request.Invalid, "Duplicate map key `" + key + "`." ) );
}
break;
case NULL:
errors.add( Neo4jError.from( Status.Request.Invalid,
"Value `null` is not supported as key in maps, must be a non-nullable string." ) );
unpackNull();
val = unpack();
map.put( null, val );
break;
default:
throw new PackStream.PackStreamException( "Bad key type" );
}
}
}
else
{
map = new HashMap<>( size, 1 );
for ( int i = 0; i < size; i++ )
{
PackType type = peekNextType();
String key;
switch ( type )
{
case NULL:
errors.add( Neo4jError.from( Status.Request.Invalid,
"Value `null` is not supported as key in maps, must be a non-nullable string." ) );
unpackNull();
key = null;
break;
case STRING:
key = unpackString();
break;
default:
throw new PackStream.PackStreamException( "Bad key type: " + type );
}
Object val = unpack();
if ( map.put( key, val ) != null )
{
errors.add( Neo4jError.from( Status.Request.Invalid, "Duplicate map key `" + key + "`." ) );
}
}
}
return map;
}
public Optional consumeError()
{
if ( errors.isEmpty() )
{
return Optional.empty();
}
else
{
Neo4jError combined = Neo4jError.combine( errors );
errors.clear();
return Optional.of( combined );
}
}
}
private static class Error
{
private final Status status;
private final String msg;
private Error( Status status, String msg )
{
this.status = status;
this.msg = msg;
}
Status status()
{
return status;
}
String msg()
{
return msg;
}
}
}