Skip to content

Commit

Permalink
enhancement:support more type value as parameter in session.value2Nva…
Browse files Browse the repository at this point in the history
…lue (#511)

* enhancement:support more type value as parameter in session.value2Nvalue

* code style for #511

* code style for #511
  • Loading branch information
CorvusYe committed Feb 24, 2023
1 parent 3abd962 commit 0feaf3e
Show file tree
Hide file tree
Showing 2 changed files with 204 additions and 51 deletions.
170 changes: 119 additions & 51 deletions client/src/main/java/com/vesoft/nebula/client/graph/net/Session.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,34 @@

package com.vesoft.nebula.client.graph.net;

import com.vesoft.nebula.Date;
import static com.vesoft.nebula.util.ReflectUtil.isCurrentTypeOrParentType;

import com.vesoft.nebula.DataSet;
import com.vesoft.nebula.DateTime;
import com.vesoft.nebula.Duration;
import com.vesoft.nebula.Edge;
import com.vesoft.nebula.Geography;
import com.vesoft.nebula.NList;
import com.vesoft.nebula.NMap;
import com.vesoft.nebula.NSet;
import com.vesoft.nebula.NullType;
import com.vesoft.nebula.Path;
import com.vesoft.nebula.Time;
import com.vesoft.nebula.Value;
import com.vesoft.nebula.Vertex;
import com.vesoft.nebula.client.graph.data.HostAddress;
import com.vesoft.nebula.client.graph.data.ResultSet;
import com.vesoft.nebula.client.graph.exception.IOErrorException;
import com.vesoft.nebula.graph.ExecutionResponse;
import com.vesoft.nebula.util.ReflectUtil;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
Expand Down Expand Up @@ -414,68 +425,125 @@ private static NMap map2Nmap(Map<String, Object> map) throws UnsupportedOperatio
return nmap;
}


/**
* convert java value type to nebula thrift value type
*
* @param value java obj
* @return nebula value
*/
public static Value value2Nvalue(Object value) throws UnsupportedOperationException {
Value nvalue = new Value();
if (value == null) {
nvalue.setNVal(NullType.__NULL__);
} else if (value instanceof Boolean) {
boolean bval = (Boolean) value;
nvalue.setBVal(bval);
} else if (value instanceof Integer) {
int ival = (Integer) value;
nvalue.setIVal(ival);
} else if (value instanceof Short) {
int ival = (Short) value;
nvalue.setIVal(ival);
} else if (value instanceof Byte) {
int ival = (Byte) value;
nvalue.setIVal(ival);
} else if (value instanceof Long) {
long ival = (Long) value;
nvalue.setIVal(ival);
} else if (value instanceof Float) {
float fval = (Float) value;
nvalue.setFVal(fval);
} else if (value instanceof Double) {
double dval = (Double) value;
nvalue.setFVal(dval);
} else if (value instanceof String) {
byte[] sval = ((String) value).getBytes();
nvalue.setSVal(sval);
} else if (value instanceof List) {
nvalue.setLVal(list2Nlist((List<Object>) value));
} else if (value instanceof Map) {
nvalue.setMVal(map2Nmap((Map<String, Object>) value));
} else if (value instanceof Value) {
return (Value) value;
} else if (value instanceof Date) {
nvalue.setDVal((Date) value);
} else if (value instanceof Time) {
nvalue.setTVal((Time) value);
} else if (value instanceof Duration) {
nvalue.setDuVal((Duration) value);
} else if (value instanceof DateTime) {
nvalue.setDtVal((DateTime) value);
} else if (value instanceof Geography) {
nvalue.setGgVal((Geography) value);
} else {
// unsupport other Value type, use this function carefully
throw new UnsupportedOperationException(
"Only support convert boolean/float/int/string/map/list to nebula.Value but was"
+ value.getClass().getTypeName());
try {
if (value == null) {
Value nullValue = new Value();
nullValue.setNVal(NullType.__NULL__);
return nullValue;
}
Class<?> type = value.getClass();
Setter<Object> setter = LEAF_TYPE_AND_SETTER.get(type);
if (setter != null) {
return setter.set(value);
}
for (Class<?> parentType : COMPLEX_TYPE_AND_SETTER.keySet()) {
if (isCurrentTypeOrParentType(type, parentType)) {
return COMPLEX_TYPE_AND_SETTER.get(parentType).set(value);
}
}
} catch (Exception e) {
throw new UnsupportedOperationException(e);
}
return nvalue;
throw new UnsupportedOperationException(
"Only support convert boolean/float/int/string/map/list/date/pojo to nebula.Value but was"
+ value.getClass().getTypeName());
}

@Override
public synchronized void close() {
release();
}

/**
* some value setter for java type (basic or nebula special type) that need convert to NValue
*/
public static Map<Class<?>, Setter> LEAF_TYPE_AND_SETTER =
new HashMap<Class<?>, Setter>() {{
put(Value.class, (Setter<Value>) (param) -> param);
put(Boolean.class, (Setter<Boolean>) Value::bVal);
put(Integer.class, (Setter<Integer>) Value::iVal);
put(Short.class, (Setter<Short>) Value::iVal);
put(Byte.class, (Setter<Short>) Value::iVal);
put(Long.class, (Setter<Long>) Value::iVal);
put(Float.class, (Setter<Float>) Value::fVal);
put(Double.class, (Setter<Double>) Value::fVal);
put(byte[].class, (Setter<byte[]>) Value::sVal);
put(Byte[].class, (Setter<byte[]>) Value::sVal);
put(String.class, (Setter<String>) (param) -> Value.sVal(param.getBytes()));
put(com.vesoft.nebula.Date.class, (Setter<com.vesoft.nebula.Date>) Value::dVal);
put(Time.class, (Setter<Time>) Value::tVal);
put(DateTime.class, (Setter<DateTime>) Value::dtVal);
put(Vertex.class, (Setter<Vertex>) Value::vVal);
put(Edge.class, (Setter<Edge>) Value::eVal);
put(Path.class, (Setter<Path>) Value::pVal);
put(NList.class, (Setter<NList>) Value::lVal);
put(NMap.class, (Setter<NMap>) Value::mVal);
put(NSet.class, (Setter<NSet>) Value::uVal);
put(DataSet.class, (Setter<DataSet>) Value::gVal);
put(Geography.class, (Setter<Geography>) Value::ggVal);
put(Duration.class, (Setter<Duration>) Value::duVal);
}};

/**
* some value setter for java type (complex java type include collections or date) that need
* convert to NValue
*/
public static Map<Class<?>, Setter> COMPLEX_TYPE_AND_SETTER =
new LinkedHashMap<Class<?>, Setter>() {{
put(Collection.class, (Setter<Collection>) (collection) -> {
Value value = new Value();
List<Object> list = new ArrayList<>();
collection.forEach(el -> list.add(value2Nvalue(el)));
value.setLVal(list2Nlist(list));
return value;
});

put(Map.class, (Setter<Map<String, Object>>) (map) -> {
Value value = new Value();
Map<String, Object> valueMap = new HashMap<>();
map.forEach((k, v) -> {
valueMap.put(k, value2Nvalue(v));
});
value.setMVal(map2Nmap(valueMap));
return value;
});

put(java.util.Date.class, (Setter<java.util.Date>) (date) -> {
Calendar calendar = new Calendar.Builder().setInstant(date).build();
return Value.dtVal(new DateTime(
new Short(String.valueOf(calendar.get(Calendar.YEAR))),
new Byte(String.valueOf(calendar.get(Calendar.MONTH))),
new Byte(String.valueOf(calendar.get(Calendar.DATE))),
new Byte(String.valueOf(calendar.get(Calendar.HOUR))),
new Byte(String.valueOf(calendar.get(Calendar.MINUTE))),
new Byte(String.valueOf(calendar.get(Calendar.SECOND))),
new Short(String.valueOf(calendar.get(Calendar.MILLISECOND)))
));
});

put(Object.class, (Setter<Object>) (obj) -> {
Value value = new Value();
Map<String, Object> pojoFields = new HashMap<>();
Class<?> paramType = obj.getClass();
Field[] declaredFields = paramType.getDeclaredFields();
for (Field declaredField : declaredFields) {
pojoFields.put(declaredField.getName(),
value2Nvalue(ReflectUtil.getValue(obj, declaredField)));
}
value.setMVal(map2Nmap(pojoFields));
return value;
});
}};

interface Setter<T> {

Value set(T param);
}
}
85 changes: 85 additions & 0 deletions client/src/main/java/com/vesoft/nebula/util/ReflectUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/* Copyright (c) 2023 vesoft inc. All rights reserved.
*
* This source code is licensed under Apache 2.0 License.
*/

package com.vesoft.nebula.util;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;


/**
* The util of reflect
*
* @author yeweicheng
* <br>Now is history!
*/
public class ReflectUtil {

/**
* Break through the access rights of the object
* and obtain the specified field value.
*
* @param o object of field value source
* @param field field used for get value
* @return field value in obj
*/
public static Object getValue(Object o, Field field) {
try {
boolean accessible = field.isAccessible();
if (accessible) {
return field.get(o);
} else {
field.setAccessible(true);
Object value = field.get(o);
field.setAccessible(false);
return value;
}
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}

/**
* Determine whether parentType is paramType or its parent class or interface
*
* @param paramType type to be determined - subclass
* @param parentType type to be determined - parent class or interface
* @return whether paramType is a subclass or implementation class of parentType
*/
public static boolean isCurrentTypeOrParentType(Class<?> paramType, Class<?> parentType) {
if (paramType == parentType) {
return true;
}
Set<Class<?>> parentTypes = getParentTypes(paramType);
return parentTypes.contains(parentType);
}

/**
* Get the super class and interface type collection according to paramType.
*
* @param paramType subclass type.
* @return super class and interface.
*/
public static Set<Class<?>> getParentTypes(Class<?> paramType) {
if (paramType == null) {
return Collections.EMPTY_SET;
}
List<Class<?>> interfaces = Arrays.asList(paramType.getInterfaces());
Set<Class<?>> parents = new HashSet<>(interfaces);

for (Class<?> anInterface : interfaces) {
parents.addAll(getParentTypes(anInterface));
}

Class<?> superclass = paramType.getSuperclass();
parents.add(superclass);
parents.addAll(getParentTypes(superclass));
return parents;
}
}

0 comments on commit 0feaf3e

Please sign in to comment.