WIP json.write

This commit is contained in:
Shautvast 2023-05-29 17:44:14 +02:00
parent d59a9e1941
commit db33d0fac8
11 changed files with 271 additions and 46 deletions

View file

@ -32,6 +32,18 @@
<artifactId>jackson-databind</artifactId> <artifactId>jackson-databind</artifactId>
<version>2.15.1</version> <version>2.15.1</version>
</dependency> </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> </dependencies>
</project> </project>

View file

@ -7,24 +7,30 @@ import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; 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) { public ListSerializer() {
super(t); super(ContiguousList.class);
} }
@Override @Override
public void serialize( public void serialize(
ContiguousList<E> value, JsonGenerator jgen, SerializerProvider provider) ContiguousList clist, JsonGenerator generator, SerializerProvider provider)
throws IOException, JsonProcessingException { throws IOException, JsonProcessingException {
// value. generator.writeStartArray();
// jgen.writeStartObject(); if (clist.isSimpleElementType()){
// jgen.writeNumberField("id", value.id); Iterator<?> iterator = clist.valueIterator();
// jgen.writeStringField("itemName", value.itemName); while (iterator.hasNext()){
// jgen.writeNumberField("owner", value.owner.id); generator.writeString(iterator.next().toString());
// jgen.writeEndObject(); }
} else {
}
generator.writeEndArray();
} }
} }

View file

@ -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;
}
}

View file

@ -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);
}
}

View file

@ -39,7 +39,6 @@
<version>5.8.2</version> <version>5.8.2</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<!-- https://mvnrepository.com/artifact/org.mockito/mockito-junit-jupiter -->
<dependency> <dependency>
<groupId>org.mockito</groupId> <groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId> <artifactId>mockito-junit-jupiter</artifactId>

View file

@ -24,6 +24,11 @@ public abstract class BuiltinTypeHandler<T> extends TypeHandler {
*/ */
public abstract void store(T value, ContiguousList<?> list); public abstract void store(T value, ContiguousList<?> list);
@Override
public boolean isBuiltin() {
return true;
}
void storePropertyValue(Object instance, ContiguousList<?> typedList) { void storePropertyValue(Object instance, ContiguousList<?> typedList) {
T propertyValue = getValue(instance); T propertyValue = getValue(instance);
store(propertyValue, typedList); store(propertyValue, typedList);

View file

@ -26,4 +26,8 @@ class CompoundTypeHandler extends TypeHandler {
} }
@Override
public boolean isBuiltin() {
return false;
}
} }

View file

@ -10,6 +10,7 @@ import java.util.*;
//notes: //notes:
// should find out growth factor of arraylist // should find out growth factor of arraylist
// investigate data array reuse (pooling, SoftReferences etc) // 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 * 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 TypeHandler rootHandler;
private final Map<String, Integer> propertyNames;
public ContiguousList(Class<E> type) { public ContiguousList(Class<E> type) {
inspectType(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. * object graph. It only knows the 'primitive' values.
*/ */
private void inspectType(Class<?> type) { private void inspectType(Class<?> type) {
if (PropertyHandlerFactory.isKnownType(type)) { if (PropertyHandlerFactory.isBuiltInType(type)) {
this.rootHandler = PropertyHandlerFactory.forType(type); this.rootHandler = PropertyHandlerFactory.forType(type);
} else { } else {
CompoundTypeHandler compoundType = new CompoundTypeHandler(type, null);//TODO revisit 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 { private void addPropertyHandlersForCompoundType(Class<?> type, CompoundTypeHandler parentCompoundType) throws IllegalAccessException {
final MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(type, MethodHandles.lookup()); final MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(type, MethodHandles.lookup());
Arrays.stream(type.getDeclaredFields()) 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 getter = lookup.findGetter(type, field.getName(), fieldType);
MethodHandle setter = lookup.findSetter(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); BuiltinTypeHandler<?> primitiveType = PropertyHandlerFactory.forType(fieldType, field.getName(), getter, setter);
parentCompoundType.addHandler(field.getName(), primitiveType); parentCompoundType.addHandler(field.getName(), primitiveType);
@ -126,7 +137,7 @@ public class ContiguousList<E> extends NotImplementedList<E> implements List<E>
if (element == null) { if (element == null) {
return false; return false;
} }
getProperties(element, rootHandler); storePropertyData(element, rootHandler);
size += 1; size += 1;
// keep track of where the objects are stored // keep track of where the objects are stored
@ -137,7 +148,13 @@ public class ContiguousList<E> extends NotImplementedList<E> implements List<E>
return true; 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<?>) { if (typeHandler instanceof BuiltinTypeHandler<?>) {
((BuiltinTypeHandler<?>) typeHandler).storePropertyValue(element, this); ((BuiltinTypeHandler<?>) typeHandler).storePropertyValue(element, this);
} else { } else {
@ -149,7 +166,7 @@ public class ContiguousList<E> extends NotImplementedList<E> implements List<E>
CompoundTypeHandler child = ((CompoundTypeHandler) property); CompoundTypeHandler child = ((CompoundTypeHandler) property);
try { try {
Object result = child.getGetter().invoke(element); Object result = child.getGetter().invoke(element);
getProperties(result, child); storePropertyData(result, child);
} catch (Throwable e) { } catch (Throwable e) {
throw new RuntimeException(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(); E newInstance = (E) rootHandler.getType().getDeclaredConstructor().newInstance();
// set the data // set the data
setProperties(newInstance, (CompoundTypeHandler) rootHandler); copyDataIntoNewObjects(newInstance, (CompoundTypeHandler) rootHandler);
return newInstance; return newInstance;
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException | } 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 -> { compoundType.getProperties().forEach(property -> {
if (property instanceof BuiltinTypeHandler) { if (property instanceof BuiltinTypeHandler) {
BuiltinTypeHandler<?> type = ((BuiltinTypeHandler<?>) property); BuiltinTypeHandler<?> type = ((BuiltinTypeHandler<?>) property);
@ -208,7 +228,7 @@ public class ContiguousList<E> extends NotImplementedList<E> implements List<E>
p.getSetter().invokeWithArguments(element, newInstance); p.getSetter().invokeWithArguments(element, newInstance);
// recurse down // recurse down
setProperties(newInstance, p); copyDataIntoNewObjects(newInstance, p);
} catch (Throwable e) { } catch (Throwable e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@ -231,16 +251,37 @@ public class ContiguousList<E> extends NotImplementedList<E> implements List<E>
return new ValueIterator(); 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 * @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<>(); final List<Class<?>> types = new ArrayList<>();
getTypes(rootHandler, types); getTypes(rootHandler, types);
return types; return types;
} }
private void getTypes(TypeHandler handler, List<Class<?>> types) { private void getTypes(TypeHandler handler, List<Class<?>> types) {
if (handler instanceof BuiltinTypeHandler<?>) { if (handler instanceof BuiltinTypeHandler<?>) {
types.add(handler.getType()); 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() { List<BuiltinTypeHandler<?>> getBuiltinTypeHandlers() {
final List<BuiltinTypeHandler<?>> types = new ArrayList<>(); final List<BuiltinTypeHandler<?>> types = new ArrayList<>();
getStoredTypes(rootHandler, types); 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 * Allows 'iterating insertion of data'. Returns an iterator of Setter
* Does not work for compound types yet * 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> { public class SetterIterator implements Iterator<Setter> {
private final List<Setter> properties = new ArrayList<>(); private final List<Setter> properties = new ArrayList<>();
private Iterator<Setter> currentSetterIterator; private Iterator<Setter> currentSetterIterator;
@ -397,12 +496,6 @@ public class ContiguousList<E> extends NotImplementedList<E> implements List<E>
public boolean isEmpty() { public boolean isEmpty() {
return size == 0; return size == 0;
} }
@Override
public Iterator<E> iterator() {
return new ElementIterator<>();
}
@Override @Override
public Object[] toArray() { public Object[] toArray() {
Object[] objects = new Object[size]; Object[] objects = new Object[size];
@ -424,16 +517,24 @@ public class ContiguousList<E> extends NotImplementedList<E> implements List<E>
return ts; 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 int curIndex = 0;
private final int originalSize;
ElementIterator() {
this.originalSize = size;
data.position(0);
}
@Override @Override
public boolean hasNext() { 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) { void storeLong(Long value) {
if (value == null) { if (value == null) {
store0(); 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() { byte[] getData() {
return Arrays.copyOfRange(data.array(), 0, currentElementValueIndex); return Arrays.copyOfRange(data.array(), 0, currentElementValueIndex);
} }

View file

@ -37,7 +37,7 @@ final class PropertyHandlerFactory {
//LocalDate/time //LocalDate/time
} }
public static boolean isKnownType(Class<?> type) { public static boolean isBuiltInType(Class<?> type) {
return TYPE_HANDLERS.containsKey(type); return TYPE_HANDLERS.containsKey(type);
} }

View file

@ -23,6 +23,8 @@ public abstract class TypeHandler {
this.setter = setter; this.setter = setter;
} }
public abstract boolean isBuiltin();
void setGetter(MethodHandle getter) { void setGetter(MethodHandle getter) {
this.getter = getter; this.getter = getter;
} }

View file

@ -217,7 +217,7 @@ public class ContiguousListTest {
@Test @Test
public void testGetStoredTypesDeepCompound() { public void testGetStoredTypesDeepCompound() {
ContiguousList<DeepBean> beans = new ContiguousList<>(DeepBean.class); final ContiguousList<DeepBean> beans = new ContiguousList<>(DeepBean.class);
Iterator<Class<?>> typeIterator = beans.getTypes().iterator(); Iterator<Class<?>> typeIterator = beans.getTypes().iterator();
assertTrue(typeIterator.hasNext()); assertTrue(typeIterator.hasNext());
assertEquals(DeepBean.class, typeIterator.next()); assertEquals(DeepBean.class, typeIterator.next());
@ -256,4 +256,27 @@ public class ContiguousListTest {
assertEquals(Integer.class, typeIterator.next()); 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));
}
} }