/*
* Copyright (c) 2002-2017 "Neo Technology,"
* Network Engine for Objects in Lund AB [http://neotechnology.com]
*
* This file is part of Neo4j.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.neo4j.driver.v1;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.neo4j.driver.internal.AsValue;
import org.neo4j.driver.internal.value.BooleanValue;
import org.neo4j.driver.internal.value.FloatValue;
import org.neo4j.driver.internal.value.IntegerValue;
import org.neo4j.driver.internal.value.ListValue;
import org.neo4j.driver.internal.value.MapValue;
import org.neo4j.driver.internal.value.NullValue;
import org.neo4j.driver.internal.value.StringValue;
import org.neo4j.driver.v1.exceptions.ClientException;
import org.neo4j.driver.v1.types.Entity;
import org.neo4j.driver.v1.types.Node;
import org.neo4j.driver.v1.types.Path;
import org.neo4j.driver.v1.types.Relationship;
import org.neo4j.driver.v1.types.TypeSystem;
import org.neo4j.driver.v1.util.Function;
/**
* Utility for wrapping regular Java types and exposing them as {@link Value}
* objects, and vice versa.
*
* The long set of {@code ofXXX} methods in this class are meant to be used as
* arguments for methods like {@link Value#asList(Function)}, {@link Value#asMap(Function)},
* {@link Record#asMap(Function)} and so on.
*
* @since 1.0
*/
public abstract class Values
{
public static final Value EmptyMap = value( Collections.emptyMap() );
public static final Value NULL = NullValue.NULL;
private Values()
{
throw new UnsupportedOperationException();
}
@SuppressWarnings("unchecked")
public static Value value( Object value )
{
if ( value == null ) { return NullValue.NULL; }
if ( value instanceof AsValue ) { return ( (AsValue) value ).asValue(); }
if ( value instanceof Boolean ) { return value( (boolean) value ); }
if ( value instanceof String ) { return value( (String) value ); }
if ( value instanceof Character ) { return value( (char) value ); }
if ( value instanceof Long ) { return value( (long) value ); }
if ( value instanceof Short ) { return value( (short) value ); }
if ( value instanceof Byte ) { return value( (byte) value ); }
if ( value instanceof Integer ) { return value( (int) value ); }
if ( value instanceof Double ) { return value( (double) value ); }
if ( value instanceof Float ) { return value( (float) value ); }
if ( value instanceof List<?> ) { return value( (List<Object>) value ); }
if ( value instanceof Map<?, ?> ) { return value( (Map<String,Object>) value ); }
if ( value instanceof Iterable<?> ) { return value( (Iterable<Object>) value ); }
if ( value instanceof Iterator<?> ) { return value( (Iterator<Object>) value ); }
if ( value instanceof boolean[] ) { return value( (boolean[]) value ); }
if ( value instanceof String[] ) { return value( (String[]) value ); }
if ( value instanceof long[] ) { return value( (long[]) value ); }
if ( value instanceof int[] ) { return value( (int[]) value ); }
if ( value instanceof double[] ) { return value( (double[]) value ); }
if ( value instanceof float[] ) { return value( (float[]) value ); }
if ( value instanceof Value[] ) { return value( (Value[]) value ); }
if ( value instanceof Object[] ) { return value( Arrays.asList( (Object[]) value )); }
throw new ClientException( "Unable to convert " + value.getClass().getName() + " to Neo4j Value." );
}
public static Value[] values( final Object... input )
{
Value[] values = new Value[input.length];
for ( int i = 0; i < input.length; i++ )
{
values[i] = value( input[i] );
}
return values;
}
public static Value value( Value... input )
{
int size = input.length;
Value[] values = new Value[size];
System.arraycopy( input, 0, values, 0, size );
return new ListValue( values );
}
public static Value value( String... input )
{
StringValue[] values = new StringValue[input.length];
for ( int i = 0; i < input.length; i++ )
{
values[i] = new StringValue( input[i] );
}
return new ListValue( values );
}
public static Value value( boolean... input )
{
Value[] values = new Value[input.length];
for ( int i = 0; i < input.length; i++ )
{
values[i] = value( input[i] );
}
return new ListValue( values );
}
public static Value value( long... input )
{
Value[] values = new Value[input.length];
for ( int i = 0; i < input.length; i++ )
{
values[i] = value( input[i] );
}
return new ListValue( values );
}
public static Value value( int... input )
{
Value[] values = new Value[input.length];
for ( int i = 0; i < input.length; i++ )
{
values[i] = value( input[i] );
}
return new ListValue( values );
}
public static Value value( double... input )
{
Value[] values = new Value[input.length];
for ( int i = 0; i < input.length; i++ )
{
values[i] = value( input[i] );
}
return new ListValue( values );
}
public static Value value( float... input )
{
Value[] values = new Value[input.length];
for ( int i = 0; i < input.length; i++ )
{
values[i] = value( input[i] );
}
return new ListValue( values );
}
public static Value value( List<Object> vals )
{
Value[] values = new Value[vals.size()];
int i = 0;
for ( Object val : vals )
{
values[i++] = value( val );
}
return new ListValue( values );
}
public static Value value( Iterable<Object> val )
{
return value( val.iterator() );
}
public static Value value( Iterator<Object> val )
{
List<Value> values = new ArrayList<>();
while ( val.hasNext() )
{
values.add( value( val.next() ) );
}
return new ListValue( values.toArray( new Value[values.size()] ) );
}
public static Value value( final String val )
{
return new StringValue( val );
}
public static Value value( final long val )
{
return new IntegerValue( val );
}
public static Value value( final int val )
{
return new IntegerValue( val );
}
public static Value value( final double val )
{
return new FloatValue( val );
}
public static Value value( final boolean val )
{
return BooleanValue.fromBoolean( val );
}
public static Value value( final Map<String,Object> val )
{
Map<String,Value> asValues = new HashMap<>( val.size() );
for ( Map.Entry<String,Object> entry : val.entrySet() )
{
asValues.put( entry.getKey(), value( entry.getValue() ) );
}
return new MapValue( asValues );
}
/**
* Helper function for creating a map of parameters, this can be used when you {@link
* StatementRunner#run(String, Value) run} statements.
* <p>
* Allowed parameter types are:
* <ul>
* <li>{@link Integer}</li>
* <li>{@link Long}</li>
* <li>{@link Boolean}</li>
* <li>{@link Double}</li>
* <li>{@link Float}</li>
* <li>{@link String}</li>
* <li>{@link Map} with String keys and values being any type in this list</li>
* <li>{@link Collection} of any type in this list</li>
* </ul>
*
* @param keysAndValues alternating sequence of keys and values
* @return Map containing all parameters specified
* @see StatementRunner#run(String, Value)
*/
public static Value parameters( Object... keysAndValues )
{
if ( keysAndValues.length % 2 != 0 )
{
throw new ClientException( "Parameters function requires an even number " +
"of arguments, " +
"alternating key and value. Arguments were: " +
Arrays.toString( keysAndValues ) + "." );
}
HashMap<String,Value> map = new HashMap<>( keysAndValues.length / 2 );
for ( int i = 0; i < keysAndValues.length; i += 2 )
{
Object value = keysAndValues[i + 1];
assertParameter( value );
map.put( keysAndValues[i].toString(), value( value ) );
}
return value(map);
}
/**
* The identity function for value conversion - returns the value untouched.
* @return a function that returns the value passed into it - the identity function
*/
public static Function<Value,Value> ofValue()
{
return VALUE;
}
/**
* Converts values to objects using {@link Value#asObject()}.
* @return a function that returns {@link Value#asObject()} of a {@link Value}
*/
public static Function<Value,Object> ofObject()
{
return OBJECT;
}
/**
* Converts values to {@link Number}.
* @return a function that returns {@link Value#asNumber()} of a {@link Value}
*/
public static Function<Value,Number> ofNumber()
{
return NUMBER;
}
/**
* Converts values to {@link String}.
*
* If you want to access a string you've retrieved from the database, this is
* the right choice. If you want to print any value for human consumption, for
* instance in a log, {@link #ofToString()} is the right choice.
*
* @return a function that returns {@link Value#asString()} of a {@link Value}
*/
public static Function<Value,String> ofString()
{
return STRING;
}
/**
* Converts values using {@link Value#toString()}, a human-readable string
* description of any value.
*
* This is different from {@link #ofString()}, which returns a java
* {@link String} value from a database {@link TypeSystem#STRING()}.
*
* If you are wanting to print any value for human consumption, this is the
* right choice. If you are wanting to access a string value stored in the
* database, you should use {@link #ofString()}.
*
* @return a function that returns {@link Value#toString()} of a {@link Value}
*/
public static Function<Value,String> ofToString()
{
return TO_STRING;
}
/**
* Converts values to {@link Integer}.
* @return a function that returns {@link Value#asInt()} of a {@link Value}
*/
public static Function<Value,Integer> ofInteger()
{
return INTEGER;
}
/**
* Converts values to {@link Long}.
* @return a function that returns {@link Value#asLong()} of a {@link Value}
*/
public static Function<Value,Long> ofLong()
{
return LONG;
}
/**
* Converts values to {@link Float}.
* @return a function that returns {@link Value#asFloat()} of a {@link Value}
*/
public static Function<Value,Float> ofFloat()
{
return FLOAT;
}
/**
* Converts values to {@link Double}.
* @return a function that returns {@link Value#asDouble()} of a {@link Value}
*/
public static Function<Value,Double> ofDouble()
{
return DOUBLE;
}
/**
* Converts values to {@link Boolean}.
* @return a function that returns {@link Value#asBoolean()} of a {@link Value}
*/
public static Function<Value,Boolean> ofBoolean()
{
return BOOLEAN;
}
/**
* Converts values to {@link Map}.
* @return a function that returns {@link Value#asMap()} of a {@link Value}
*/
public static Function<Value, Map<String, Object>> ofMap()
{
return MAP;
}
/**
* Converts values to {@link Map}, with the map values further converted using
* the provided converter.
* @param valueConverter converter to use for the values of the map
* @param <T> the type of values in the returned map
* @return a function that returns {@link Value#asMap(Function)} of a {@link Value}
*/
public static <T> Function<Value, Map<String, T>> ofMap( final Function<Value, T> valueConverter)
{
return new Function<Value,Map<String,T>>()
{
public Map<String,T> apply( Value val )
{
return val.asMap(valueConverter);
}
};
}
/**
* Converts values to {@link Entity}.
* @return a function that returns {@link Value#asEntity()} of a {@link Value}
*/
public static Function<Value,Entity> ofEntity()
{
return ENTITY;
}
/**
* Converts values to {@link Long entity id}.
* @return a function that returns the id an entity {@link Value}
*/
public static Function<Value, Long> ofEntityId()
{
return ENTITY_ID;
}
/**
* Converts values to {@link Node}.
* @return a function that returns {@link Value#asNode()} of a {@link Value}
*/
public static Function<Value,Node> ofNode()
{
return NODE;
}
/**
* Converts values to {@link Relationship}.
* @return a function that returns {@link Value#asRelationship()} of a {@link Value}
*/
public static Function<Value,Relationship> ofRelationship()
{
return RELATIONSHIP;
}
/**
* Converts values to {@link Path}.
* @return a function that returns {@link Value#asPath()} of a {@link Value}
*/
public static Function<Value,Path> ofPath()
{
return PATH;
}
/**
* Converts values to {@link List} of {@link Object}.
* @return a function that returns {@link Value#asList()} of a {@link Value}
*/
public static Function<Value,List<Object>> ofList()
{
return new Function<Value,List<Object>>()
{
@Override
public List<Object> apply( Value value )
{
return value.asList();
}
};
}
/**
* Converts values to {@link List} of <tt>T</tt>.
* @param innerMap converter for the values inside the list
* @param <T> the type of values inside the list
* @return a function that returns {@link Value#asList(Function)} of a {@link Value}
*/
public static <T> Function<Value,List<T>> ofList( final Function<Value, T> innerMap )
{
return new Function<Value,List<T>>()
{
@Override
public List<T> apply( Value value )
{
return value.asList( innerMap );
}
};
}
private static final Function<Value,Object> OBJECT = new Function<Value,Object>()
{
public Object apply( Value val )
{
return val.asObject();
}
};
private static final Function<Value,Value> VALUE = new Function<Value,Value>()
{
public Value apply( Value val )
{
return val;
}
};
private static final Function<Value,Number> NUMBER = new Function<Value,Number>()
{
public Number apply( Value val )
{
return val.asNumber();
}
};
private static final Function<Value,String> STRING = new Function<Value,String>()
{
public String apply( Value val )
{
return val.asString();
}
};
private static final Function<Value,String> TO_STRING = new Function<Value,String>()
{
public String apply( Value val )
{
return val.toString();
}
};
private static final Function<Value,Integer> INTEGER = new Function<Value,Integer>()
{
public Integer apply( Value val )
{
return val.asInt();
}
};
private static final Function<Value,Long> LONG = new Function<Value,Long>()
{
public Long apply( Value val )
{
return val.asLong();
}
};
private static final Function<Value,Float> FLOAT = new Function<Value,Float>()
{
public Float apply( Value val )
{
return val.asFloat();
}
};
private static final Function<Value,Double> DOUBLE = new Function<Value,Double>()
{
public Double apply( Value val )
{
return val.asDouble();
}
};
private static final Function<Value,Boolean> BOOLEAN = new Function<Value,Boolean>()
{
public Boolean apply( Value val )
{
return val.asBoolean();
}
};
private static final Function<Value,Map<String,Object>> MAP = new Function<Value,Map<String,Object>>()
{
public Map<String,Object> apply( Value val )
{
return val.asMap();
}
};
private static final Function<Value,Long> ENTITY_ID = new Function<Value,Long>()
{
public Long apply( Value val )
{
return val.asEntity().id();
}
};
private static final Function<Value,Entity> ENTITY = new Function<Value,Entity>()
{
public Entity apply( Value val )
{
return val.asEntity();
}
};
private static final Function<Value,Node> NODE = new Function<Value,Node>()
{
public Node apply( Value val )
{
return val.asNode();
}
};
private static final Function<Value,Relationship> RELATIONSHIP = new Function<Value,Relationship>()
{
public Relationship apply( Value val )
{
return val.asRelationship();
}
};
private static final Function<Value,Path> PATH = new Function<Value,Path>()
{
public Path apply( Value val )
{
return val.asPath();
}
};
private static void assertParameter( Object value )
{
if ( value instanceof Node )
{
throw new ClientException( "Nodes can't be used as parameters." );
}
if ( value instanceof Relationship )
{
throw new ClientException( "Relationships can't be used as parameters." );
}
if ( value instanceof Path )
{
throw new ClientException( "Paths can't be used as parameters." );
}
}
}