From dd6b9c22817f05de6f238d14bdf52c14563bbbdb Mon Sep 17 00:00:00 2001 From: Shautvast Date: Wed, 31 May 2023 09:09:53 +0200 Subject: [PATCH] json output directly from the list...may not remain there --- .../contiguous/ListSerializer.java | 21 +- .../contiguous/ListSerializerTest.java | 7 +- .../contiguous/JdbcResults.java | 3 +- .../contiguous/BuiltinTypeHandler.java | 7 +- .../contiguous/CompoundTypeHandler.java | 10 +- .../contiguous/ContiguousList.java | 199 ++++++++---------- .../contiguous/TypeHandler.java | 6 +- .../contiguous/ContiguousListTest.java | 97 ++------- 8 files changed, 127 insertions(+), 223 deletions(-) diff --git a/jackson/src/main/java/nl/sanderhautvast/contiguous/ListSerializer.java b/jackson/src/main/java/nl/sanderhautvast/contiguous/ListSerializer.java index 058cb73..92b9c8a 100644 --- a/jackson/src/main/java/nl/sanderhautvast/contiguous/ListSerializer.java +++ b/jackson/src/main/java/nl/sanderhautvast/contiguous/ListSerializer.java @@ -1,33 +1,28 @@ package nl.sanderhautvast.contiguous; import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.SerializerProvider; 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> { + @SuppressWarnings("unchecked") public ListSerializer() { - super(ContiguousList.class); + super((Class) ContiguousList.class); // ? } @Override public void serialize( - ContiguousList clist, JsonGenerator generator, SerializerProvider provider) - throws IOException, JsonProcessingException { + ContiguousList clist, JsonGenerator generator, SerializerProvider provider) + throws IOException { generator.writeStartArray(); - if (clist.isSimpleElementType()){ - Iterator iterator = clist.valueIterator(); - while (iterator.hasNext()){ - generator.writeString(iterator.next().toString()); - } - } else { - + Iterator jsons = clist.jsonIterator(); + while (jsons.hasNext()) { + generator.writeRawValue(jsons.next()); } generator.writeEndArray(); diff --git a/jackson/src/test/java/nl/sanderhautvast/contiguous/ListSerializerTest.java b/jackson/src/test/java/nl/sanderhautvast/contiguous/ListSerializerTest.java index 7f5f321..fd15b5d 100644 --- a/jackson/src/test/java/nl/sanderhautvast/contiguous/ListSerializerTest.java +++ b/jackson/src/test/java/nl/sanderhautvast/contiguous/ListSerializerTest.java @@ -3,7 +3,6 @@ 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; @@ -45,9 +44,9 @@ class ListSerializerTest { String json = mapper.writeValueAsString(strings); - assertEquals("[{\"name\":\"Vogon constructor fleet\"}," + - "{\"name\":\"Restaurant at the end of the Galaxy\"}," + - "{\"name\":\"Publishing houses of Ursa Minor\"}]", + 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/src/main/java/nl/sanderhautvast/contiguous/JdbcResults.java b/jdbc/src/main/java/nl/sanderhautvast/contiguous/JdbcResults.java index 77e2888..7f8dad7 100644 --- a/jdbc/src/main/java/nl/sanderhautvast/contiguous/JdbcResults.java +++ b/jdbc/src/main/java/nl/sanderhautvast/contiguous/JdbcResults.java @@ -57,7 +57,7 @@ public class JdbcResults { } next.set(fieldValue); } - setterIterator.nextRecord(); + setterIterator.finishObject(); } } @@ -69,7 +69,6 @@ public class JdbcResults { * @throws SQLException when db throws error.. */ public static ContiguousList toList(ResultSet result, Class elementType) throws SQLException { - ContiguousList list = new ContiguousList<>(elementType); return toList(result, elementType, Function.identity()); } diff --git a/lib/src/main/java/nl/sanderhautvast/contiguous/BuiltinTypeHandler.java b/lib/src/main/java/nl/sanderhautvast/contiguous/BuiltinTypeHandler.java index 2e8eff9..bf8e74a 100644 --- a/lib/src/main/java/nl/sanderhautvast/contiguous/BuiltinTypeHandler.java +++ b/lib/src/main/java/nl/sanderhautvast/contiguous/BuiltinTypeHandler.java @@ -24,18 +24,13 @@ 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); } void storeValue(Object value, ContiguousList contiguousList) { - store((T)value, contiguousList); + store((T) value, contiguousList); } private T getValue(Object propertyValue) { diff --git a/lib/src/main/java/nl/sanderhautvast/contiguous/CompoundTypeHandler.java b/lib/src/main/java/nl/sanderhautvast/contiguous/CompoundTypeHandler.java index 72ef743..de71cd7 100644 --- a/lib/src/main/java/nl/sanderhautvast/contiguous/CompoundTypeHandler.java +++ b/lib/src/main/java/nl/sanderhautvast/contiguous/CompoundTypeHandler.java @@ -6,7 +6,9 @@ import java.util.*; class CompoundTypeHandler extends TypeHandler { private final Map properties = new LinkedHashMap<>(); - + CompoundTypeHandler(Class type) { + super(type, null,null, null); + } CompoundTypeHandler(Class type, String propertyName) { super(type, propertyName, null,null); @@ -24,10 +26,4 @@ class CompoundTypeHandler extends TypeHandler { void addChild(Field property, CompoundTypeHandler childCompoundType) { this.properties.put(property.getName(), childCompoundType); } - - - @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 ee35c3e..763bc69 100644 --- a/lib/src/main/java/nl/sanderhautvast/contiguous/ContiguousList.java +++ b/lib/src/main/java/nl/sanderhautvast/contiguous/ContiguousList.java @@ -58,7 +58,7 @@ public class ContiguousList extends NotImplementedList implements List */ private ByteBuffer data = ByteBuffer.allocate(32); - private int currentElementValueIndex; + private int bufferPosition; private int[] elementIndices = new int[10]; // avoids autoboxing. Could also use standard ArrayList though // is there a standard lib IntList?? @@ -67,12 +67,9 @@ public class ContiguousList extends NotImplementedList implements List private TypeHandler rootHandler; - private final Map propertyNames; - public ContiguousList(Class type) { inspectType(type); elementIndices[0] = 0; // index of first element - propertyNames = findPropertyNames(); } public Class getElementType() { @@ -87,14 +84,14 @@ public class ContiguousList extends NotImplementedList implements List * The advantage of the current implementation is that the binary data is not aware of the actual * object graph. It only knows the 'primitive' values. */ - private void inspectType(Class type) { - if (PropertyHandlerFactory.isBuiltInType(type)) { - this.rootHandler = PropertyHandlerFactory.forType(type); + private void inspectType(Class elementClass) { + if (PropertyHandlerFactory.isBuiltInType(elementClass)) { + this.rootHandler = PropertyHandlerFactory.forType(elementClass); } else { - CompoundTypeHandler compoundType = new CompoundTypeHandler(type, null);//TODO revisit + CompoundTypeHandler compoundType = new CompoundTypeHandler(elementClass); this.rootHandler = compoundType; try { - addPropertyHandlersForCompoundType(type, compoundType); + addPropertyHandlersForCompoundType(elementClass, compoundType); } catch (IllegalAccessException e) { throw new IllegalStateException(e); } @@ -114,7 +111,8 @@ public class ContiguousList extends NotImplementedList implements List MethodHandle setter = lookup.findSetter(type, field.getName(), 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); } else { @@ -144,7 +142,7 @@ public class ContiguousList extends NotImplementedList implements List if (elementIndices.length < size + 1) { this.elementIndices = Arrays.copyOf(this.elementIndices, this.elementIndices.length * 2); } - elementIndices[size] = currentElementValueIndex; + elementIndices[size] = bufferPosition; return true; } @@ -201,7 +199,7 @@ public class ContiguousList extends NotImplementedList implements List E newInstance = (E) rootHandler.getType().getDeclaredConstructor().newInstance(); // set the data - copyDataIntoNewObjects(newInstance, (CompoundTypeHandler) rootHandler); + copyDataIntoNewObject(newInstance, (CompoundTypeHandler) rootHandler); return newInstance; } catch (NoSuchMethodException | InstantiationException | IllegalAccessException | @@ -213,7 +211,7 @@ public class ContiguousList extends NotImplementedList implements List /* * */ - private void copyDataIntoNewObjects(Object element, CompoundTypeHandler compoundType) { + private void copyDataIntoNewObject(Object element, CompoundTypeHandler compoundType) { compoundType.getProperties().forEach(property -> { if (property instanceof BuiltinTypeHandler) { BuiltinTypeHandler type = ((BuiltinTypeHandler) property); @@ -228,7 +226,7 @@ public class ContiguousList extends NotImplementedList implements List p.getSetter().invokeWithArguments(element, newInstance); // recurse down - copyDataIntoNewObjects(newInstance, p); + copyDataIntoNewObject(newInstance, p); } catch (Throwable e) { throw new RuntimeException(e); } @@ -251,103 +249,87 @@ public class ContiguousList extends NotImplementedList implements List return new ValueIterator(); } - public Iterator propertyIterator() { - return new PropertyIterator(); + public Iterator jsonIterator() { + return new JsonIterator(); } - static class Property { - String name; - String value; - } + class JsonIterator implements Iterator { - static class PropertyIterator implements Iterator { + private int index; @Override public boolean hasNext() { - return false; + return index < size; } @Override - public Property next() { - return null; + public String next() { + return getAsJson(index++); } } /** - * @return a list of types in the Object(graph). So the element type and all (nested) properties - */ - 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()); - } else { - types.add(handler.getType()); - ((CompoundTypeHandler) handler).getProperties() - .forEach(propertyHandler -> getTypes(propertyHandler, types)); - } - } - - 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 + * For now the simplest thing I can think of to get this to work * - * 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 + * @param index + * @return */ - 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)); + public String getAsJson(int index) { + if (index < 0 || index >= size) { + throw new IndexOutOfBoundsException("index <0 or >" + size); } + data.position(elementIndices[index]); + if (rootHandler instanceof BuiltinTypeHandler) { + + BuiltinTypeHandler handler = (BuiltinTypeHandler) rootHandler; + return getValue(handler); + } + // create a new instance of the list element type + StringBuilder s = new StringBuilder(); + s.append("{"); + copyDataIntoStringBuilder(s, (CompoundTypeHandler) rootHandler); + s.append("}"); + return s.toString(); - return Collections.unmodifiableMap(names); } + private String getValue(BuiltinTypeHandler handler) { + Object read = ValueReader.read(data); + String out = handler.cast(read).toString(); + if (handler instanceof StringHandler) { + out = quote(out); + } + return out; + } + + private static String quote(String out) { + StringBuilder s = new StringBuilder(); + out = s.append("\"").append(out).append("\"").toString(); + return out; + } /* - * 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++); + private void copyDataIntoStringBuilder(StringBuilder s, CompoundTypeHandler compoundType) { + compoundType.getProperties().forEach(property -> { + if (property instanceof BuiltinTypeHandler) { + BuiltinTypeHandler typeHandler = (BuiltinTypeHandler) property; + String name = typeHandler.getName(); + String value = getValue(typeHandler); + s.append(quote(name)).append(": ").append(value); + } else { + CompoundTypeHandler p = (CompoundTypeHandler) property; + s.append(p.getName()).append(":{"); + // recurse down + copyDataIntoStringBuilder(s, p); + s.append("}"); } - } + s.append(", "); + }); + s.setLength(s.length() - 2); } - /** - * 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); @@ -404,15 +386,13 @@ 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 + * Does not work for compound types yet // (check if still valid) * - * @return A Reusable iterator + * @return an Iterator over bean property setters. Once an object is completely written, + * call finishObject, to increase the element count and to continue with the next element. */ public SetterIterator setterIterator() { return new SetterIterator(); @@ -450,11 +430,7 @@ public class ContiguousList extends NotImplementedList implements List @Override public boolean hasNext() { - boolean hasNext = currentSetterIterator.hasNext(); - if (!hasNext) { - extend(); // marks the end of an object - } - return hasNext; + return currentSetterIterator.hasNext(); } @Override @@ -462,18 +438,15 @@ public class ContiguousList extends NotImplementedList implements List return currentSetterIterator.next(); } - public void nextRecord() { + /** + * Indicates that writing of the current object is complete, so + * that it will increment the element count and start a new property iterator + * for the next object (or none it was the last). + */ + public void finishObject() { + extend(); currentSetterIterator = properties.iterator(); } - - } - - /** - * @return an {@link Iterator} over the property values of the specified element in the List. - */ - public Iterator valueIterator(int index) { - //TODO - return null; } public boolean addAll(Collection collection) { @@ -484,7 +457,7 @@ public class ContiguousList extends NotImplementedList implements List } public void clear() { - this.currentElementValueIndex = 0; + this.bufferPosition = 0; this.size = 0; } @@ -496,6 +469,7 @@ public class ContiguousList extends NotImplementedList implements List public boolean isEmpty() { return size == 0; } + @Override public Object[] toArray() { Object[] objects = new Object[size]; @@ -522,6 +496,7 @@ public class ContiguousList extends NotImplementedList implements 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 @@ -550,15 +525,15 @@ public class ContiguousList extends NotImplementedList implements List private void store(byte[] bytes) { ensureFree(bytes.length); - data.position(currentElementValueIndex); // ensures intermittent reads/writes are safe + data.position(bufferPosition); // ensures intermittent reads/writes are safe data.put(bytes); - currentElementValueIndex += bytes.length; + bufferPosition += bytes.length; } private void store0() { ensureFree(1); data.put((byte) 0); - currentElementValueIndex += 1; + bufferPosition += 1; } void storeString(String value) { @@ -636,7 +611,7 @@ public class ContiguousList extends NotImplementedList implements List } byte[] getData() { - return Arrays.copyOfRange(data.array(), 0, currentElementValueIndex); + return Arrays.copyOfRange(data.array(), 0, bufferPosition); } int[] getElementIndices() { @@ -644,7 +619,7 @@ public class ContiguousList extends NotImplementedList implements List } private void ensureFree(int length) { - while (currentElementValueIndex + length > data.capacity()) { + while (bufferPosition + length > data.capacity()) { byte[] bytes = this.data.array(); this.data = ByteBuffer.allocate(this.data.capacity() * 2); this.data.put(bytes); diff --git a/lib/src/main/java/nl/sanderhautvast/contiguous/TypeHandler.java b/lib/src/main/java/nl/sanderhautvast/contiguous/TypeHandler.java index c2df578..5f4eeee 100644 --- a/lib/src/main/java/nl/sanderhautvast/contiguous/TypeHandler.java +++ b/lib/src/main/java/nl/sanderhautvast/contiguous/TypeHandler.java @@ -14,6 +14,10 @@ public abstract class TypeHandler { protected MethodHandle getter; // both can be null, if it's for a known ('primitive') type protected MethodHandle setter; private final Class type; + + /** + * full name, prepended by all parent property names + */ private final String name; public TypeHandler(Class type, String name, MethodHandle getter, MethodHandle setter) { @@ -23,8 +27,6 @@ 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 2f7cf4f..9878592 100644 --- a/lib/src/test/java/nl/sanderhautvast/contiguous/ContiguousListTest.java +++ b/lib/src/test/java/nl/sanderhautvast/contiguous/ContiguousListTest.java @@ -4,7 +4,6 @@ import org.junit.jupiter.api.Test; import java.math.BigInteger; import java.util.Iterator; -import java.util.List; import static org.junit.jupiter.api.Assertions.*; @@ -12,7 +11,7 @@ public class ContiguousListTest { @Test public void testAddAndGetString() { - List list = new ContiguousList<>(String.class); + ContiguousList list = new ContiguousList<>(String.class); assertTrue(list.isEmpty()); list.add("hitchhikersguide to the galaxy"); @@ -21,6 +20,9 @@ public class ContiguousListTest { String title = list.get(0); assertEquals("hitchhikersguide to the galaxy", title); + + String titleJson = list.getAsJson(0); + assertEquals("\"hitchhikersguide to the galaxy\"", titleJson); } @Test @@ -33,6 +35,9 @@ public class ContiguousListTest { StringBean douglas = beanList.get(0); assertEquals("Douglas Adams", douglas.getName()); + String douglasJson = beanList.getAsJson(0); + assertEquals("{\"name\": \"Douglas Adams\"}", douglasJson); + // now add new data to see if existing data remains intact beanList.add(new StringBean("Ford Prefect")); @@ -198,85 +203,23 @@ public class ContiguousListTest { assertEquals(new IntBean(100), integers.get(100)); // here an instance } + + @Test - public void testGetTypesWhenCompound() { + public void testSetterIterator() { ContiguousList integers = new ContiguousList<>(NestedBean.class); - Iterator> typeIterator = integers.getTypes().iterator(); - assertTrue(typeIterator.hasNext()); - assertEquals(NestedBean.class, typeIterator.next()); - assertTrue(typeIterator.hasNext()); - assertEquals(StringBean.class, typeIterator.next()); - assertTrue(typeIterator.hasNext()); - assertEquals(String.class, typeIterator.next()); - assertTrue(typeIterator.hasNext()); - assertEquals(IntBean.class, typeIterator.next()); - assertTrue(typeIterator.hasNext()); - assertEquals(Integer.class, typeIterator.next()); + ContiguousList.SetterIterator iterator = integers.setterIterator(); - } - - @Test - public void testGetStoredTypesDeepCompound() { - final ContiguousList beans = new ContiguousList<>(DeepBean.class); - Iterator> typeIterator = beans.getTypes().iterator(); - assertTrue(typeIterator.hasNext()); - assertEquals(DeepBean.class, typeIterator.next()); - - assertTrue(typeIterator.hasNext()); - assertEquals(NestedBean.class, typeIterator.next()); - - assertTrue(typeIterator.hasNext()); - assertEquals(StringBean.class, typeIterator.next()); - - assertTrue(typeIterator.hasNext()); - assertEquals(String.class, typeIterator.next()); - - assertTrue(typeIterator.hasNext()); - assertEquals(IntBean.class, typeIterator.next()); - - assertTrue(typeIterator.hasNext()); - assertEquals(Integer.class, typeIterator.next()); - - assertTrue(typeIterator.hasNext()); - assertEquals(Long.class, typeIterator.next()); - - assertTrue(typeIterator.hasNext()); - assertEquals(StringBean.class, typeIterator.next()); - - assertTrue(typeIterator.hasNext()); - assertEquals(String.class, typeIterator.next()); - - } - - @Test - public void testTypeIteratorBuiltin() { - ContiguousList integers = new ContiguousList<>(Integer.class); - Iterator> typeIterator = integers.getTypes().iterator(); - if (typeIterator.hasNext()) { - assertEquals(Integer.class, typeIterator.next()); + if (iterator.hasNext()) { + iterator.next().set("Magrathea"); } - } + if (iterator.hasNext()) { + iterator.next().set(42); + } + iterator.finishObject(); - @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)); + NestedBean nestedBean = integers.get(0); + assertEquals("Magrathea", nestedBean.getStringBean().getName()); + assertEquals(42, nestedBean.getIntBean().getValue()); } }