org.neo4j.values.utils.PrettyPrinter Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of neo4j-values Show documentation
Show all versions of neo4j-values Show documentation
Neo4j property value system.
/*
* 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.utils;
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.Arrays;
import java.util.Deque;
import org.neo4j.values.AnyValueWriter;
import org.neo4j.values.storable.CoordinateReferenceSystem;
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;
/**
* Pretty printer for AnyValues.
*
* Used to format AnyValue as a json-like string, as following:
*
* - nodes:
(id=42 :LABEL {prop1: ["a", 13]})
* - edges:
-[id=42 :TYPE {prop1: ["a", 13]}]-
* - paths:
(id=1 :L)-[id=42 :T {k: "v"}]->(id=2)-...
* - points are serialized to geojson
* - maps:
{foo: 42, bar: "baz"}
* - lists and arrays:
["aa", "bb", "cc"]
* - Numbers:
2.7182818285
* - Strings:
"this is a string"
*
*/
public class PrettyPrinter implements AnyValueWriter
{
private final Deque stack = new ArrayDeque<>();
private final String quoteMark;
private final StringBuilder builder;
private final EntityMode entityMode;
private final Counter counter;
public PrettyPrinter()
{
this( EntityMode.FULL );
}
public PrettyPrinter( EntityMode entityMode )
{
this( "\"", entityMode, Integer.MAX_VALUE );
}
public PrettyPrinter( String quoteMark, EntityMode entityMode )
{
this( quoteMark, entityMode, Integer.MAX_VALUE );
}
public PrettyPrinter( String quoteMark, EntityMode entityMode, int maxLength )
{
this.quoteMark = quoteMark;
this.entityMode = entityMode;
this.counter = new Counter( maxLength );
builder = new StringBuilder( 64 );
stack.push( new ValueWriter( builder, quoteMark, counter ) );
}
@Override
public EntityMode entityMode()
{
return entityMode;
}
@Override
public void writeNodeReference( long nodeId )
{
append( "(id=" );
append( String.valueOf( nodeId ) );
append( ")" );
}
@Override
public void writeNode( long nodeId, TextArray labels, MapValue properties, boolean ignored )
{
append( "(id=" );
append( String.valueOf( nodeId ) );
String sep = " ";
for ( int i = 0; i < labels.length(); i++ )
{
append( sep );
append( ":" );
append( labels.stringValue( i ) );
sep = "";
}
if ( properties.size() > 0 )
{
append( " " );
properties.writeTo( this );
}
append( ")" );
}
@Override
public void writeRelationshipReference( long relId )
{
append( "-[id=" );
append( String.valueOf( relId ) );
append( "]-" );
}
@Override
public void writeRelationship( long relId, long startNodeId, long endNodeId, TextValue type, MapValue properties, boolean ignored )
{
append( "-[id=" );
append( String.valueOf( relId ) );
append( " :" );
append( type.stringValue() );
if ( properties.size() > 0 )
{
append( " " );
properties.writeTo( this );
}
append( "]-" );
}
@Override
public void beginMap( int size )
{
assert !stack.isEmpty();
stack.peek().nest();
stack.push( new MapWriter( builder, quoteMark, counter ) );
}
@Override
public void endMap()
{
assert !stack.isEmpty();
stack.pop().done();
if ( !stack.isEmpty() )
{
stack.peek().next();
}
}
@Override
public void beginList( int size )
{
assert !stack.isEmpty();
stack.peek().nest();
stack.push( new ListWriter( builder, quoteMark, counter ) );
}
@Override
public void endList()
{
assert !stack.isEmpty();
stack.pop().done();
if ( !stack.isEmpty() )
{
stack.peek().next();
}
}
@Override
public void writePathReference( long[] nodes, long[] relationships )
{
if ( nodes.length == 0 )
{
return;
}
//Path guarantees that nodes.length = edges.length = 1
writeNodeReference( nodes[0] );
for ( int i = 0; i < relationships.length; i++ )
{
writeRelationshipReference( relationships[i] );
append( ">" );
writeNodeReference( nodes[i + 1] );
}
}
@Override
public void writePath( NodeValue[] nodes, RelationshipValue[] relationships )
{
if ( nodes.length == 0 )
{
return;
}
//Path guarantees that nodes.length = edges.length = 1
nodes[0].writeTo( this );
for ( int i = 0; i < relationships.length; i++ )
{
relationships[i].writeTo( this );
append( ">" );
nodes[i + 1].writeTo( this );
}
}
@Override
public void writePoint( CoordinateReferenceSystem crs, double[] coordinate ) throws RuntimeException
{
append( "{geometry: {type: \"Point\", coordinates: " );
append( Arrays.toString( coordinate ) );
append( ", crs: {type: link, properties: {href: \"" );
append( crs.getHref() );
append( "\", code: " );
append( Integer.toString( crs.getCode() ) );
append( "}}}}" );
}
@Override
public void writeDuration( long months, long days, long seconds, int nanos ) throws RuntimeException
{
append( "{duration: {months: " );
append( Long.toString( months ) );
append( ", days: " );
append( Long.toString( days ) );
append( ", seconds: " );
append( Long.toString( seconds ) );
append( ", nanos: " );
append( Long.toString( nanos ) );
append( "}}" );
}
@Override
public void writeDate( LocalDate localDate ) throws RuntimeException
{
append( "{date: " );
appendQuoted( localDate.toString() );
append( "}" );
}
@Override
public void writeLocalTime( LocalTime localTime ) throws RuntimeException
{
append( "{localTime: " );
appendQuoted( localTime.toString() );
append( "}" );
}
@Override
public void writeTime( OffsetTime offsetTime ) throws RuntimeException
{
append( "{time: " );
appendQuoted( offsetTime.toString() );
append( "}" );
}
@Override
public void writeLocalDateTime( LocalDateTime localDateTime ) throws RuntimeException
{
append( "{localDateTime: " );
appendQuoted( localDateTime.toString() );
append( "}" );
}
@Override
public void writeDateTime( ZonedDateTime zonedDateTime ) throws RuntimeException
{
append( "{datetime: " );
appendQuoted( zonedDateTime.toString() );
append( "}" );
}
@Override
public void writeNull()
{
append( "" );
}
@Override
public void writeBoolean( boolean value )
{
append( Boolean.toString( value ) );
}
@Override
public void writeInteger( byte value )
{
append( Byte.toString( value ) );
}
@Override
public void writeInteger( short value )
{
append( Short.toString( value ) );
}
@Override
public void writeInteger( int value )
{
append( Integer.toString( value ) );
}
@Override
public void writeInteger( long value )
{
append( Long.toString( value ) );
}
@Override
public void writeFloatingPoint( float value )
{
append( Float.toString( value ) );
}
@Override
public void writeFloatingPoint( double value )
{
append( Double.toString( value ) );
}
@Override
public void writeString( String value )
{
appendQuoted( value );
}
@Override
public void writeString( char value )
{
writeString( Character.toString( value ) );
}
@Override
public void beginArray( int size, ArrayType arrayType )
{
assert !stack.isEmpty();
stack.peek().nest();
stack.push( new ListWriter( builder, quoteMark, counter ) );
}
@Override
public void endArray()
{
assert !stack.isEmpty();
stack.pop().done();
if ( !stack.isEmpty() )
{
stack.peek().next();
}
}
@Override
public void writeByteArray( byte[] value )
{
String sep = "";
append( "[" );
for ( byte b : value )
{
append( sep );
append( Byte.toString( b ) );
sep = ", ";
}
append( "]" );
}
public String value()
{
assert stack.size() == 1;
stack.getLast().done();
return builder.toString();
}
public void valueInto( StringBuilder target )
{
assert stack.size() == 1;
stack.getLast().done();
target.append( builder );
}
private void append( String value )
{
if ( counter.isDone() )
{
return;
}
assert !stack.isEmpty();
stack.peek().append( value );
}
private void appendQuoted( String value )
{
if ( counter.isDone() )
{
return;
}
assert !stack.isEmpty();
stack.peek().appendQuoted( value );
}
private interface Writer
{
void append( String value );
void appendQuoted( String value );
void next();
void done();
void nest();
}
private abstract static class BaseWriter implements Writer
{
protected final StringBuilder builder;
protected Counter counter;
private final String quoteMark;
protected BaseWriter( StringBuilder builder, String quoteMark, Counter counter )
{
this.builder = builder;
this.quoteMark = quoteMark;
this.counter = counter;
}
@Override
public void appendQuoted( String value )
{
write( quoteMark );
this.append( value );
write( quoteMark );
}
@Override
public void next()
{
}
@Override
public void done()
{
}
@Override
public void nest()
{
}
protected void write( String value )
{
int remaining = counter.remaining();
if ( remaining <= 0 )
{
return;
}
if ( remaining < value.length() )
{
builder.append( value, 0, remaining ).append( "..." );
counter.decrement( remaining );
}
else
{
builder.append( value );
counter.decrement( value.length() );
}
}
}
private static class ValueWriter extends BaseWriter
{
private ValueWriter( StringBuilder builder, String quoteMark, Counter counter )
{
super( builder, quoteMark, counter );
}
@Override
public void append( String value )
{
write( value );
}
}
private static class MapWriter extends BaseWriter
{
private boolean writeKey = true;
private String sep = "";
MapWriter( StringBuilder builder, String quoteMark, Counter counter )
{
super( builder, quoteMark, counter );
write( "{" );
}
@Override
public void append( String value )
{
if ( writeKey )
{
write( sep );
write( value );
write( ": " );
}
else
{
write( value );
}
writeKey = !writeKey;
sep = ", ";
}
@Override
public void appendQuoted( String value )
{
if ( writeKey )
{
append( value );
}
else
{
super.appendQuoted( value );
}
}
@Override
public void next()
{
writeKey = true;
}
@Override
public void done()
{
write( "}" );
}
}
private class ListWriter extends BaseWriter
{
private String sep = "";
ListWriter( StringBuilder builder, String quoteMark, Counter counter )
{
super( builder, quoteMark, counter );
write( "[" );
}
@Override
public void append( String value )
{
write( sep );
write( value );
sep = ", ";
}
@Override
public void appendQuoted( String value )
{
write( sep );
write( quoteMark );
write( value );
write( quoteMark );
sep = ", ";
}
@Override
public void done()
{
write( "]" );
}
@Override
public void nest()
{
write( sep );
}
}
private static final class Counter
{
private int count;
Counter( int count )
{
this.count = count;
}
void decrement( int n )
{
count -= n;
}
int remaining()
{
return count;
}
public boolean isDone()
{
return count <= 0;
}
}
}