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