From db33d0fac8600536a752d870a806e418621e2e0b Mon Sep 17 00:00:00 2001 From: Shautvast Date: Mon, 29 May 2023 17:44:14 +0200 Subject: [PATCH] WIP json.write --- jackson/pom.xml | 12 ++ .../contiguous/ListSerializer.java | 26 +-- .../contiguous/AdamsObject.java | 20 +++ .../contiguous/ListSerializerTest.java | 53 ++++++ jdbc/pom.xml | 1 - .../contiguous/BuiltinTypeHandler.java | 5 + .../contiguous/CompoundTypeHandler.java | 4 + .../contiguous/ContiguousList.java | 167 ++++++++++++++---- .../contiguous/PropertyHandlerFactory.java | 2 +- .../contiguous/TypeHandler.java | 2 + .../contiguous/ContiguousListTest.java | 25 ++- 11 files changed, 271 insertions(+), 46 deletions(-) create mode 100644 jackson/src/test/java/nl/sanderhautvast/contiguous/AdamsObject.java create mode 100644 jackson/src/test/java/nl/sanderhautvast/contiguous/ListSerializerTest.java diff --git a/jackson/pom.xml b/jackson/pom.xml index e898240..33eded5 100644 --- a/jackson/pom.xml +++ b/jackson/pom.xml @@ -32,6 +32,18 @@ jackson-databind 2.15.1 + + org.junit.jupiter + junit-jupiter-engine + 5.8.2 + test + + + org.junit.jupiter + junit-jupiter-api + 5.8.2 + test + \ No newline at end of file diff --git a/jackson/src/main/java/nl/sanderhautvast/contiguous/ListSerializer.java b/jackson/src/main/java/nl/sanderhautvast/contiguous/ListSerializer.java index 33a7407..058cb73 100644 --- a/jackson/src/main/java/nl/sanderhautvast/contiguous/ListSerializer.java +++ b/jackson/src/main/java/nl/sanderhautvast/contiguous/ListSerializer.java @@ -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 extends StdSerializer> { +public class ListSerializer extends StdSerializer { - public ListSerializer(Class> t) { - super(t); + public ListSerializer() { + super(ContiguousList.class); } @Override public void serialize( - ContiguousList 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(); } } diff --git a/jackson/src/test/java/nl/sanderhautvast/contiguous/AdamsObject.java b/jackson/src/test/java/nl/sanderhautvast/contiguous/AdamsObject.java new file mode 100644 index 0000000..48421aa --- /dev/null +++ b/jackson/src/test/java/nl/sanderhautvast/contiguous/AdamsObject.java @@ -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; + } +} diff --git a/jackson/src/test/java/nl/sanderhautvast/contiguous/ListSerializerTest.java b/jackson/src/test/java/nl/sanderhautvast/contiguous/ListSerializerTest.java new file mode 100644 index 0000000..7f5f321 --- /dev/null +++ b/jackson/src/test/java/nl/sanderhautvast/contiguous/ListSerializerTest.java @@ -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 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 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); + } +} \ No newline at end of file diff --git a/jdbc/pom.xml b/jdbc/pom.xml index c78116e..0244b46 100644 --- a/jdbc/pom.xml +++ b/jdbc/pom.xml @@ -39,7 +39,6 @@ 5.8.2 test - org.mockito mockito-junit-jupiter diff --git a/lib/src/main/java/nl/sanderhautvast/contiguous/BuiltinTypeHandler.java b/lib/src/main/java/nl/sanderhautvast/contiguous/BuiltinTypeHandler.java index a424479..2e8eff9 100644 --- a/lib/src/main/java/nl/sanderhautvast/contiguous/BuiltinTypeHandler.java +++ b/lib/src/main/java/nl/sanderhautvast/contiguous/BuiltinTypeHandler.java @@ -24,6 +24,11 @@ public abstract class BuiltinTypeHandler 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); diff --git a/lib/src/main/java/nl/sanderhautvast/contiguous/CompoundTypeHandler.java b/lib/src/main/java/nl/sanderhautvast/contiguous/CompoundTypeHandler.java index ca7ccc9..72ef743 100644 --- a/lib/src/main/java/nl/sanderhautvast/contiguous/CompoundTypeHandler.java +++ b/lib/src/main/java/nl/sanderhautvast/contiguous/CompoundTypeHandler.java @@ -26,4 +26,8 @@ class CompoundTypeHandler extends TypeHandler { } + @Override + public boolean isBuiltin() { + return false; + } } diff --git a/lib/src/main/java/nl/sanderhautvast/contiguous/ContiguousList.java b/lib/src/main/java/nl/sanderhautvast/contiguous/ContiguousList.java index bda42b1..ee35c3e 100644 --- a/lib/src/main/java/nl/sanderhautvast/contiguous/ContiguousList.java +++ b/lib/src/main/java/nl/sanderhautvast/contiguous/ContiguousList.java @@ -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 extends NotImplementedList implements List private TypeHandler rootHandler; + private final Map propertyNames; + public ContiguousList(Class 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 extends NotImplementedList implements List * 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 extends NotImplementedList implements List } } + /* + * 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 extends NotImplementedList implements List 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 extends NotImplementedList implements List 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 extends NotImplementedList implements List 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 extends NotImplementedList implements List 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 extends NotImplementedList implements List 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 extends NotImplementedList implements List } } - 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 extends NotImplementedList implements List 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 extends NotImplementedList implements List return new ValueIterator(); } + public Iterator propertyIterator() { + return new PropertyIterator(); + } + + static class Property { + String name; + String value; + } + + static class PropertyIterator implements Iterator { + + @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> getTypes() { + List> getTypes() { final List> types = new ArrayList<>(); getTypes(rootHandler, types); return types; } - private void getTypes(TypeHandler handler, List> types) { if (handler instanceof BuiltinTypeHandler) { types.add(handler.getType()); @@ -251,6 +292,62 @@ public class ContiguousList extends NotImplementedList implements List } } + public List 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 findPropertyNames() { + // no name for the root property + final Map 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 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> getBuiltinTypeHandlers() { final List> types = new ArrayList<>(); getStoredTypes(rootHandler, types); @@ -307,6 +404,10 @@ public class ContiguousList extends NotImplementedList implements List } } + 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 extends NotImplementedList implements List } } - // TODO proper naming - // BTW do we even need this as a class?? public class SetterIterator implements Iterator { private final List properties = new ArrayList<>(); private Iterator currentSetterIterator; @@ -352,7 +451,7 @@ public class ContiguousList extends NotImplementedList implements List @Override public boolean hasNext() { boolean hasNext = currentSetterIterator.hasNext(); - if (!hasNext){ + if (!hasNext) { extend(); // marks the end of an object } return hasNext; @@ -397,12 +496,6 @@ public class ContiguousList extends NotImplementedList implements List public boolean isEmpty() { return size == 0; } - - @Override - public Iterator iterator() { - return new ElementIterator<>(); - } - @Override public Object[] toArray() { Object[] objects = new Object[size]; @@ -424,16 +517,24 @@ public class ContiguousList extends NotImplementedList implements List 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. + *

. + * @return An Iterator over the elements in the List + */ + @Override + public Iterator iterator() { + return new StandardElementIterator<>(); + } - class ElementIterator implements Iterator { + /* + * Iterator used by the standard Iterator() method + */ + class StandardElementIterator implements Iterator { 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 extends NotImplementedList implements List } } - // 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 extends NotImplementedList implements List } } + // 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); } diff --git a/lib/src/main/java/nl/sanderhautvast/contiguous/PropertyHandlerFactory.java b/lib/src/main/java/nl/sanderhautvast/contiguous/PropertyHandlerFactory.java index 2fd96aa..d8cf82b 100644 --- a/lib/src/main/java/nl/sanderhautvast/contiguous/PropertyHandlerFactory.java +++ b/lib/src/main/java/nl/sanderhautvast/contiguous/PropertyHandlerFactory.java @@ -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); } diff --git a/lib/src/main/java/nl/sanderhautvast/contiguous/TypeHandler.java b/lib/src/main/java/nl/sanderhautvast/contiguous/TypeHandler.java index 70a9e34..c2df578 100644 --- a/lib/src/main/java/nl/sanderhautvast/contiguous/TypeHandler.java +++ b/lib/src/main/java/nl/sanderhautvast/contiguous/TypeHandler.java @@ -23,6 +23,8 @@ public abstract class TypeHandler { this.setter = setter; } + public abstract boolean isBuiltin(); + void setGetter(MethodHandle getter) { this.getter = getter; } diff --git a/lib/src/test/java/nl/sanderhautvast/contiguous/ContiguousListTest.java b/lib/src/test/java/nl/sanderhautvast/contiguous/ContiguousListTest.java index dbc7126..2f7cf4f 100644 --- a/lib/src/test/java/nl/sanderhautvast/contiguous/ContiguousListTest.java +++ b/lib/src/test/java/nl/sanderhautvast/contiguous/ContiguousListTest.java @@ -217,7 +217,7 @@ public class ContiguousListTest { @Test public void testGetStoredTypesDeepCompound() { - ContiguousList beans = new ContiguousList<>(DeepBean.class); + final ContiguousList beans = new ContiguousList<>(DeepBean.class); Iterator> 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 integers = new ContiguousList<>(Integer.class); + assertTrue(integers.getPropertyNames().isEmpty()); + } + + @Test + public void testGetPropertyNames(){ + ContiguousList integers = new ContiguousList<>(IntBean.class); + assertFalse(integers.getPropertyNames().isEmpty()); + assertEquals("value", integers.getPropertyNames().get(0)); + } + + @Test + public void testGetMorePropertyNames(){ + ContiguousList 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)); + } }