WIP json.write
This commit is contained in:
parent
d59a9e1941
commit
db33d0fac8
11 changed files with 271 additions and 46 deletions
|
|
@ -32,6 +32,18 @@
|
|||
<artifactId>jackson-databind</artifactId>
|
||||
<version>2.15.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
<version>5.8.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-api</artifactId>
|
||||
<version>5.8.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
|
@ -7,24 +7,30 @@ import com.fasterxml.jackson.databind.ser.std.StdSerializer;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
|
||||
public class ListSerializer<E> extends StdSerializer<ContiguousList<E>> {
|
||||
public class ListSerializer<E> extends StdSerializer<ContiguousList> {
|
||||
|
||||
public ListSerializer(Class<ContiguousList<E>> t) {
|
||||
super(t);
|
||||
public ListSerializer() {
|
||||
super(ContiguousList.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(
|
||||
ContiguousList<E> value, JsonGenerator jgen, SerializerProvider provider)
|
||||
ContiguousList clist, JsonGenerator generator, SerializerProvider provider)
|
||||
throws IOException, JsonProcessingException {
|
||||
// value.
|
||||
generator.writeStartArray();
|
||||
|
||||
// jgen.writeStartObject();
|
||||
// jgen.writeNumberField("id", value.id);
|
||||
// jgen.writeStringField("itemName", value.itemName);
|
||||
// jgen.writeNumberField("owner", value.owner.id);
|
||||
// jgen.writeEndObject();
|
||||
if (clist.isSimpleElementType()){
|
||||
Iterator<?> iterator = clist.valueIterator();
|
||||
while (iterator.hasNext()){
|
||||
generator.writeString(iterator.next().toString());
|
||||
}
|
||||
} else {
|
||||
|
||||
}
|
||||
|
||||
generator.writeEndArray();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
package nl.sanderhautvast.contiguous;
|
||||
|
||||
public class AdamsObject {
|
||||
private String name;
|
||||
|
||||
public AdamsObject() {
|
||||
}
|
||||
|
||||
public AdamsObject(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
package nl.sanderhautvast.contiguous;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class ListSerializerTest {
|
||||
|
||||
|
||||
private ObjectMapper mapper;
|
||||
|
||||
@BeforeEach
|
||||
public void setup(){
|
||||
mapper = new ObjectMapper();
|
||||
final SimpleModule module = new SimpleModule("mySerializers");
|
||||
module.addSerializer(new ListSerializer<>());
|
||||
mapper.registerModule(module);
|
||||
}
|
||||
@Test
|
||||
public void testStringList() throws JsonProcessingException {
|
||||
ContiguousList<String> strings = new ContiguousList<>(String.class);
|
||||
strings.add("Vogon constructor fleet");
|
||||
strings.add("Restaurant at the end of the Galaxy");
|
||||
strings.add("Publishing houses of Ursa Minor");
|
||||
|
||||
String json = mapper.writeValueAsString(strings);
|
||||
assertEquals("[\"Vogon constructor fleet\"," +
|
||||
"\"Restaurant at the end of the Galaxy\"," +
|
||||
"\"Publishing houses of Ursa Minor\"]",
|
||||
json);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testObjectList() throws JsonProcessingException {
|
||||
ContiguousList<AdamsObject> strings = new ContiguousList<>(AdamsObject.class);
|
||||
strings.add(new AdamsObject("Vogon constructor fleet"));
|
||||
strings.add(new AdamsObject("Restaurant at the end of the Galaxy"));
|
||||
strings.add(new AdamsObject("Publishing houses of Ursa Minor"));
|
||||
|
||||
|
||||
|
||||
String json = mapper.writeValueAsString(strings);
|
||||
assertEquals("[{\"name\":\"Vogon constructor fleet\"}," +
|
||||
"{\"name\":\"Restaurant at the end of the Galaxy\"}," +
|
||||
"{\"name\":\"Publishing houses of Ursa Minor\"}]",
|
||||
json);
|
||||
}
|
||||
}
|
||||
|
|
@ -39,7 +39,6 @@
|
|||
<version>5.8.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/org.mockito/mockito-junit-jupiter -->
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-junit-jupiter</artifactId>
|
||||
|
|
|
|||
|
|
@ -24,6 +24,11 @@ public abstract class BuiltinTypeHandler<T> extends TypeHandler {
|
|||
*/
|
||||
public abstract void store(T value, ContiguousList<?> list);
|
||||
|
||||
@Override
|
||||
public boolean isBuiltin() {
|
||||
return true;
|
||||
}
|
||||
|
||||
void storePropertyValue(Object instance, ContiguousList<?> typedList) {
|
||||
T propertyValue = getValue(instance);
|
||||
store(propertyValue, typedList);
|
||||
|
|
|
|||
|
|
@ -26,4 +26,8 @@ class CompoundTypeHandler extends TypeHandler {
|
|||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isBuiltin() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import java.util.*;
|
|||
//notes:
|
||||
// should find out growth factor of arraylist
|
||||
// investigate data array reuse (pooling, SoftReferences etc)
|
||||
// is all this inner class usage not wasteful? (references to this etc)
|
||||
|
||||
/**
|
||||
* Short for Contiguous Layout List, an Experimental List implementation
|
||||
|
|
@ -66,9 +67,16 @@ public class ContiguousList<E> extends NotImplementedList<E> implements List<E>
|
|||
|
||||
private TypeHandler rootHandler;
|
||||
|
||||
private final Map<String, Integer> propertyNames;
|
||||
|
||||
public ContiguousList(Class<E> type) {
|
||||
inspectType(type);
|
||||
elementIndices[0] = currentElementValueIndex; // index of first element
|
||||
elementIndices[0] = 0; // index of first element
|
||||
propertyNames = findPropertyNames();
|
||||
}
|
||||
|
||||
public Class<?> getElementType() {
|
||||
return rootHandler.getType();
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -80,7 +88,7 @@ public class ContiguousList<E> extends NotImplementedList<E> implements List<E>
|
|||
* object graph. It only knows the 'primitive' values.
|
||||
*/
|
||||
private void inspectType(Class<?> type) {
|
||||
if (PropertyHandlerFactory.isKnownType(type)) {
|
||||
if (PropertyHandlerFactory.isBuiltInType(type)) {
|
||||
this.rootHandler = PropertyHandlerFactory.forType(type);
|
||||
} else {
|
||||
CompoundTypeHandler compoundType = new CompoundTypeHandler(type, null);//TODO revisit
|
||||
|
|
@ -93,6 +101,9 @@ public class ContiguousList<E> extends NotImplementedList<E> implements List<E>
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* using reflection find all properties in the element, recursing down when the property is compound
|
||||
*/
|
||||
private void addPropertyHandlersForCompoundType(Class<?> type, CompoundTypeHandler parentCompoundType) throws IllegalAccessException {
|
||||
final MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(type, MethodHandles.lookup());
|
||||
Arrays.stream(type.getDeclaredFields())
|
||||
|
|
@ -102,7 +113,7 @@ public class ContiguousList<E> extends NotImplementedList<E> implements List<E>
|
|||
MethodHandle getter = lookup.findGetter(type, field.getName(), fieldType);
|
||||
MethodHandle setter = lookup.findSetter(type, field.getName(), fieldType);
|
||||
|
||||
if (PropertyHandlerFactory.isKnownType(fieldType)) {
|
||||
if (PropertyHandlerFactory.isBuiltInType(fieldType)) {
|
||||
BuiltinTypeHandler<?> primitiveType = PropertyHandlerFactory.forType(fieldType, field.getName(), getter, setter);
|
||||
|
||||
parentCompoundType.addHandler(field.getName(), primitiveType);
|
||||
|
|
@ -126,7 +137,7 @@ public class ContiguousList<E> extends NotImplementedList<E> implements List<E>
|
|||
if (element == null) {
|
||||
return false;
|
||||
}
|
||||
getProperties(element, rootHandler);
|
||||
storePropertyData(element, rootHandler);
|
||||
size += 1;
|
||||
|
||||
// keep track of where the objects are stored
|
||||
|
|
@ -137,7 +148,13 @@ public class ContiguousList<E> extends NotImplementedList<E> implements List<E>
|
|||
return true;
|
||||
}
|
||||
|
||||
private void getProperties(Object element, TypeHandler typeHandler) {
|
||||
/*
|
||||
* Stores the properties
|
||||
*
|
||||
* walks the object graph depth-first. The order of the stored data is crucial, because only
|
||||
* leaf elements are stored and all information on what object owns what is implicit.
|
||||
*/
|
||||
private void storePropertyData(Object element, TypeHandler typeHandler) {
|
||||
if (typeHandler instanceof BuiltinTypeHandler<?>) {
|
||||
((BuiltinTypeHandler<?>) typeHandler).storePropertyValue(element, this);
|
||||
} else {
|
||||
|
|
@ -149,7 +166,7 @@ public class ContiguousList<E> extends NotImplementedList<E> implements List<E>
|
|||
CompoundTypeHandler child = ((CompoundTypeHandler) property);
|
||||
try {
|
||||
Object result = child.getGetter().invoke(element);
|
||||
getProperties(result, child);
|
||||
storePropertyData(result, child);
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
|
@ -184,7 +201,7 @@ public class ContiguousList<E> extends NotImplementedList<E> implements List<E>
|
|||
E newInstance = (E) rootHandler.getType().getDeclaredConstructor().newInstance();
|
||||
|
||||
// set the data
|
||||
setProperties(newInstance, (CompoundTypeHandler) rootHandler);
|
||||
copyDataIntoNewObjects(newInstance, (CompoundTypeHandler) rootHandler);
|
||||
|
||||
return newInstance;
|
||||
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException |
|
||||
|
|
@ -193,7 +210,10 @@ public class ContiguousList<E> extends NotImplementedList<E> implements List<E>
|
|||
}
|
||||
}
|
||||
|
||||
private void setProperties(Object element, CompoundTypeHandler compoundType) {
|
||||
/*
|
||||
*
|
||||
*/
|
||||
private void copyDataIntoNewObjects(Object element, CompoundTypeHandler compoundType) {
|
||||
compoundType.getProperties().forEach(property -> {
|
||||
if (property instanceof BuiltinTypeHandler) {
|
||||
BuiltinTypeHandler<?> type = ((BuiltinTypeHandler<?>) property);
|
||||
|
|
@ -208,7 +228,7 @@ public class ContiguousList<E> extends NotImplementedList<E> implements List<E>
|
|||
p.getSetter().invokeWithArguments(element, newInstance);
|
||||
|
||||
// recurse down
|
||||
setProperties(newInstance, p);
|
||||
copyDataIntoNewObjects(newInstance, p);
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
|
@ -231,16 +251,37 @@ public class ContiguousList<E> extends NotImplementedList<E> implements List<E>
|
|||
return new ValueIterator();
|
||||
}
|
||||
|
||||
public Iterator<Property> propertyIterator() {
|
||||
return new PropertyIterator();
|
||||
}
|
||||
|
||||
static class Property {
|
||||
String name;
|
||||
String value;
|
||||
}
|
||||
|
||||
static class PropertyIterator implements Iterator<Property> {
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Property next() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a list of types in the Object(graph). So the element type and all (nested) properties
|
||||
*/
|
||||
public List<Class<?>> getTypes() {
|
||||
List<Class<?>> getTypes() {
|
||||
final List<Class<?>> types = new ArrayList<>();
|
||||
getTypes(rootHandler, types);
|
||||
return types;
|
||||
}
|
||||
|
||||
|
||||
private void getTypes(TypeHandler handler, List<Class<?>> types) {
|
||||
if (handler instanceof BuiltinTypeHandler<?>) {
|
||||
types.add(handler.getType());
|
||||
|
|
@ -251,6 +292,62 @@ public class ContiguousList<E> extends NotImplementedList<E> implements List<E>
|
|||
}
|
||||
}
|
||||
|
||||
public List<String> getPropertyNames() {
|
||||
return new ArrayList<>(this.propertyNames.keySet());//TODO should be SET!
|
||||
}
|
||||
|
||||
/*
|
||||
* walk the tree of typehandlers to find the names
|
||||
* adds an index to know what index a property has
|
||||
* this in turn is needed to find where the data of the property is in the byte array
|
||||
*
|
||||
* could also store property data indices, but that would incur more memory overhead
|
||||
* the way it is now, we have to iterate all properties in an element. ie. a tradeoff
|
||||
*/
|
||||
private Map<String, Integer> findPropertyNames() {
|
||||
// no name for the root property
|
||||
final Map<String, Integer> names = new HashMap<>();
|
||||
if (rootHandler instanceof CompoundTypeHandler) {
|
||||
((CompoundTypeHandler) rootHandler).getProperties()
|
||||
.forEach(propertyHandler -> findPropertyNames(propertyHandler, names, 0));
|
||||
}
|
||||
|
||||
return Collections.unmodifiableMap(names);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* TODO
|
||||
* // oopsie: the properties are not guaranteed to be unique
|
||||
*/
|
||||
private void findPropertyNames(TypeHandler handler, Map<String, Integer> names, int index) {
|
||||
if (handler instanceof BuiltinTypeHandler<?>) {
|
||||
names.put(handler.getName(), index);
|
||||
} else {
|
||||
names.put(handler.getName(), index);
|
||||
for (TypeHandler propertyHandler : ((CompoundTypeHandler) handler).getProperties()) {
|
||||
findPropertyNames(propertyHandler, names, index++);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* gets a named property of element at index
|
||||
*
|
||||
* @param index elementIndex
|
||||
* @param propertyName the name of the property
|
||||
* @return the property value
|
||||
*/
|
||||
public Object getValue(int index, String propertyName) {
|
||||
if (rootHandler.isBuiltin() || propertyName == null) {
|
||||
data.position(elementIndices[index]);
|
||||
return ValueReader.read(data);
|
||||
} else {
|
||||
return null; //TODO implement
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
List<BuiltinTypeHandler<?>> getBuiltinTypeHandlers() {
|
||||
final List<BuiltinTypeHandler<?>> types = new ArrayList<>();
|
||||
getStoredTypes(rootHandler, types);
|
||||
|
|
@ -307,6 +404,10 @@ public class ContiguousList<E> extends NotImplementedList<E> implements List<E>
|
|||
}
|
||||
}
|
||||
|
||||
public boolean isSimpleElementType() {
|
||||
return PropertyHandlerFactory.isBuiltInType(rootHandler.getType());
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows 'iterating insertion of data'. Returns an iterator of Setter
|
||||
* Does not work for compound types yet
|
||||
|
|
@ -334,8 +435,6 @@ public class ContiguousList<E> extends NotImplementedList<E> implements List<E>
|
|||
}
|
||||
}
|
||||
|
||||
// TODO proper naming
|
||||
// BTW do we even need this as a class??
|
||||
public class SetterIterator implements Iterator<Setter> {
|
||||
private final List<Setter> properties = new ArrayList<>();
|
||||
private Iterator<Setter> currentSetterIterator;
|
||||
|
|
@ -397,12 +496,6 @@ public class ContiguousList<E> extends NotImplementedList<E> implements List<E>
|
|||
public boolean isEmpty() {
|
||||
return size == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<E> iterator() {
|
||||
return new ElementIterator<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] toArray() {
|
||||
Object[] objects = new Object[size];
|
||||
|
|
@ -424,16 +517,24 @@ public class ContiguousList<E> extends NotImplementedList<E> implements List<E>
|
|||
return ts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Standard Iterator that conforms to the protocol of java.util.List.
|
||||
* Although it's convenient to use, it's not the best way to read from the list because it
|
||||
* needs the Reflection API to instantiate new objects of the element type.
|
||||
* <p/>.
|
||||
* @return An Iterator over the elements in the List
|
||||
*/
|
||||
@Override
|
||||
public Iterator<E> iterator() {
|
||||
return new StandardElementIterator<>();
|
||||
}
|
||||
|
||||
class ElementIterator<F> implements Iterator<F> {
|
||||
/*
|
||||
* Iterator used by the standard Iterator() method
|
||||
*/
|
||||
class StandardElementIterator<F> implements Iterator<F> {
|
||||
|
||||
private int curIndex = 0;
|
||||
private final int originalSize;
|
||||
|
||||
ElementIterator() {
|
||||
this.originalSize = size;
|
||||
data.position(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
|
|
@ -470,12 +571,6 @@ public class ContiguousList<E> extends NotImplementedList<E> implements List<E>
|
|||
}
|
||||
}
|
||||
|
||||
// to be called by framework to force element count
|
||||
// used by SetterIterator
|
||||
void extend() {
|
||||
size += 1;
|
||||
}
|
||||
|
||||
void storeLong(Long value) {
|
||||
if (value == null) {
|
||||
store0();
|
||||
|
|
@ -534,6 +629,12 @@ public class ContiguousList<E> extends NotImplementedList<E> implements List<E>
|
|||
}
|
||||
}
|
||||
|
||||
// to be called by framework to force element count
|
||||
// used by SetterIterator
|
||||
void extend() {
|
||||
size += 1;
|
||||
}
|
||||
|
||||
byte[] getData() {
|
||||
return Arrays.copyOfRange(data.array(), 0, currentElementValueIndex);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ final class PropertyHandlerFactory {
|
|||
//LocalDate/time
|
||||
}
|
||||
|
||||
public static boolean isKnownType(Class<?> type) {
|
||||
public static boolean isBuiltInType(Class<?> type) {
|
||||
return TYPE_HANDLERS.containsKey(type);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@ public abstract class TypeHandler {
|
|||
this.setter = setter;
|
||||
}
|
||||
|
||||
public abstract boolean isBuiltin();
|
||||
|
||||
void setGetter(MethodHandle getter) {
|
||||
this.getter = getter;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -217,7 +217,7 @@ public class ContiguousListTest {
|
|||
|
||||
@Test
|
||||
public void testGetStoredTypesDeepCompound() {
|
||||
ContiguousList<DeepBean> beans = new ContiguousList<>(DeepBean.class);
|
||||
final ContiguousList<DeepBean> beans = new ContiguousList<>(DeepBean.class);
|
||||
Iterator<Class<?>> typeIterator = beans.getTypes().iterator();
|
||||
assertTrue(typeIterator.hasNext());
|
||||
assertEquals(DeepBean.class, typeIterator.next());
|
||||
|
|
@ -256,4 +256,27 @@ public class ContiguousListTest {
|
|||
assertEquals(Integer.class, typeIterator.next());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetPropertyNamesForBuiltinElementType(){
|
||||
ContiguousList<Integer> integers = new ContiguousList<>(Integer.class);
|
||||
assertTrue(integers.getPropertyNames().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetPropertyNames(){
|
||||
ContiguousList<IntBean> integers = new ContiguousList<>(IntBean.class);
|
||||
assertFalse(integers.getPropertyNames().isEmpty());
|
||||
assertEquals("value", integers.getPropertyNames().get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetMorePropertyNames(){
|
||||
ContiguousList<NestedBean> integers = new ContiguousList<>(NestedBean.class);
|
||||
assertFalse(integers.getPropertyNames().isEmpty());
|
||||
assertEquals("stringBean", integers.getPropertyNames().get(0));
|
||||
assertEquals("name", integers.getPropertyNames().get(1));
|
||||
assertEquals("intBean", integers.getPropertyNames().get(2));
|
||||
assertEquals("value", integers.getPropertyNames().get(3));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue