added iterators

This commit is contained in:
Shautvast 2023-05-26 15:36:29 +02:00
parent 90ccb1015f
commit 610ce0ce41
16 changed files with 329 additions and 147 deletions

View file

@ -3,7 +3,7 @@ package nl.sanderhautvast.contiguous;
import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandle;
import java.math.BigDecimal; import java.math.BigDecimal;
class BigDecimalHandler extends PrimitiveTypeHandler<BigDecimal> { class BigDecimalHandler extends BuiltinTypeHandler<BigDecimal> {
public BigDecimalHandler(MethodHandle getter, MethodHandle setter) { public BigDecimalHandler(MethodHandle getter, MethodHandle setter) {
super(BigDecimal.class, getter, setter); super(BigDecimal.class, getter, setter);
} }

View file

@ -3,7 +3,7 @@ package nl.sanderhautvast.contiguous;
import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandle;
import java.math.BigInteger; import java.math.BigInteger;
class BigIntegerHandler extends PrimitiveTypeHandler<BigInteger> { class BigIntegerHandler extends BuiltinTypeHandler<BigInteger> {
public BigIntegerHandler(MethodHandle getter, MethodHandle setter) { public BigIntegerHandler(MethodHandle getter, MethodHandle setter) {
super(BigInteger.class, getter, setter); super(BigInteger.class, getter, setter);
} }

View file

@ -11,8 +11,8 @@ import java.lang.invoke.MethodHandle;
* of the bean that it needs to call 'runtime' (after instantiation of the list), * of the bean that it needs to call 'runtime' (after instantiation of the list),
* ie. when a bean is added or retrieved from the list * ie. when a bean is added or retrieved from the list
*/ */
public abstract class PrimitiveTypeHandler<T> extends TypeHandler { public abstract class BuiltinTypeHandler<T> extends TypeHandler {
public PrimitiveTypeHandler(Class<?> type, MethodHandle getter, MethodHandle setter) { public BuiltinTypeHandler(Class<?> type, MethodHandle getter, MethodHandle setter) {
super(type, getter, setter); super(type, getter, setter);
} }
@ -64,7 +64,7 @@ public abstract class PrimitiveTypeHandler<T> extends TypeHandler {
* Certain types can easily be stored as another known type, for instance * Certain types can easily be stored as another known type, for instance
* a BigDecimal can be stored as a String. * a BigDecimal can be stored as a String.
* <p> * <p>
* The {@link PrimitiveTypeHandler} for BigDecimal would in that case be responsible for turning the String * The {@link BuiltinTypeHandler} for BigDecimal would in that case be responsible for turning the String
* into a BigDecimal. It can do that by overriding this method * into a BigDecimal. It can do that by overriding this method
* *
* @param value raw value to transform to the desired output type * @param value raw value to transform to the desired output type

View file

@ -5,7 +5,7 @@ import java.lang.invoke.MethodHandle;
/** /**
* Stores a byte value. * Stores a byte value.
*/ */
class ByteHandler extends PrimitiveTypeHandler<Byte> { class ByteHandler extends BuiltinTypeHandler<Byte> {
public ByteHandler(MethodHandle getter, MethodHandle setter) { public ByteHandler(MethodHandle getter, MethodHandle setter) {
super(Byte.class, getter, setter); super(Byte.class, getter, setter);
@ -20,4 +20,12 @@ class ByteHandler extends PrimitiveTypeHandler<Byte> {
public void setValue(Object instance, Object value) { public void setValue(Object instance, Object value) {
super.setValue(instance, ((Long) value).byteValue()); super.setValue(instance, ((Long) value).byteValue());
} }
@Override
public Object transform(Object value) {
if (value instanceof Long) {
return ((Long) value).byteValue();
}
return value;
}
} }

View file

@ -17,7 +17,7 @@ class CompoundTypeHandler extends TypeHandler {
return properties.values(); return properties.values();
} }
void addHandler(String propertyName, PrimitiveTypeHandler<?> primitiveType) { void addHandler(String propertyName, BuiltinTypeHandler<?> primitiveType) {
properties.put(propertyName, primitiveType); properties.put(propertyName, primitiveType);
} }

View file

@ -6,7 +6,6 @@ import java.lang.reflect.InvocationTargetException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.*; import java.util.*;
import java.util.function.UnaryOperator;
//notes: //notes:
//1. should find out growth factor of arraylist //1. should find out growth factor of arraylist
@ -25,7 +24,9 @@ import java.util.function.UnaryOperator;
* <p> * <p>
* Employs the SQLite style of data storage, most notably integer numbers are stored with variable byte length * Employs the SQLite style of data storage, most notably integer numbers are stored with variable byte length
* <p> * <p>
* The classes stored in {@link ContiguousList} MUST have a no-args constructor. * The classes stored in {@link ContiguousList} MUST have a no-args constructor. (so sorry)
* // There is (was?) a way to create instances without a no-args constructor. I have used it somewhere in the past...
* // I think {@link java.io.ObjectInputStream} uses it.
* <p> * <p>
* Like ArrayList, mutating operations are not synchronized. * Like ArrayList, mutating operations are not synchronized.
* <p> * <p>
@ -35,7 +36,7 @@ import java.util.function.UnaryOperator;
* performance-wise, like the indexed add and set methods. They mess with the memory layout. The list is meant to * performance-wise, like the indexed add and set methods. They mess with the memory layout. The list is meant to
* be appended at the tail. * be appended at the tail.
*/ */
public class ContiguousList<E> implements List<E> { public class ContiguousList<E> extends NotImplementedList<E> implements List<E> {
private static final byte[] DOUBLE_TYPE = {7}; private static final byte[] DOUBLE_TYPE = {7};
private static final byte[] FLOAT_TYPE = {10}; // not in line with SQLite anymore private static final byte[] FLOAT_TYPE = {10}; // not in line with SQLite anymore
@ -49,17 +50,17 @@ public class ContiguousList<E> implements List<E> {
*/ */
private ByteBuffer data = ByteBuffer.allocate(32); private ByteBuffer data = ByteBuffer.allocate(32);
private int currentElementIndex; private int currentElementValueIndex;
private int[] elementIndices = new int[10]; private int[] elementIndices = new int[10];
private int size; private int size;
private TypeHandler type; private TypeHandler rootHandler;
public ContiguousList(Class<E> type) { public ContiguousList(Class<E> type) {
inspectType(type); inspectType(type);
elementIndices[0] = currentElementIndex; // index of first element elementIndices[0] = currentElementValueIndex; // index of first element
} }
/* /*
@ -72,10 +73,10 @@ public class ContiguousList<E> implements List<E> {
*/ */
private void inspectType(Class<?> type) { private void inspectType(Class<?> type) {
if (PropertyHandlerFactory.isKnownType(type)) { if (PropertyHandlerFactory.isKnownType(type)) {
this.type = PropertyHandlerFactory.forType(type); this.rootHandler = PropertyHandlerFactory.forType(type);
} else { } else {
CompoundTypeHandler compoundType = new CompoundTypeHandler(type); CompoundTypeHandler compoundType = new CompoundTypeHandler(type);
this.type = compoundType; this.rootHandler = compoundType;
try { try {
addPropertyHandlersForCompoundType(type, compoundType); addPropertyHandlersForCompoundType(type, compoundType);
} catch (IllegalAccessException e) { } catch (IllegalAccessException e) {
@ -94,7 +95,7 @@ public class ContiguousList<E> implements List<E> {
MethodHandle setter = lookup.findSetter(type, field.getName(), fieldType); MethodHandle setter = lookup.findSetter(type, field.getName(), fieldType);
if (PropertyHandlerFactory.isKnownType(fieldType)) { if (PropertyHandlerFactory.isKnownType(fieldType)) {
PrimitiveTypeHandler<?> primitiveType = PropertyHandlerFactory.forType(fieldType, getter, setter); BuiltinTypeHandler<?> primitiveType = PropertyHandlerFactory.forType(fieldType, getter, setter);
parentCompoundType.addHandler(field.getName(), primitiveType); parentCompoundType.addHandler(field.getName(), primitiveType);
} else { } else {
@ -111,36 +112,31 @@ public class ContiguousList<E> implements List<E> {
}); });
} }
@Override @Override
@SuppressWarnings("Contract") @SuppressWarnings("Contract")
public boolean add(E element) { public boolean add(E element) {
if (element == null) { if (element == null) {
return false; return false;
} }
getProperties(element, type); getProperties(element, rootHandler);
size += 1; size += 1;
// keep track of where the objects are stored // keep track of where the objects are stored
if (elementIndices.length < size + 1) { if (elementIndices.length < size + 1) {
this.elementIndices = Arrays.copyOf(this.elementIndices, this.elementIndices.length * 2); this.elementIndices = Arrays.copyOf(this.elementIndices, this.elementIndices.length * 2);
} }
elementIndices[size] = currentElementIndex; elementIndices[size] = currentElementValueIndex;
return true; return true;
} }
private void getProperties(Object element, TypeHandler type) { private void getProperties(Object element, TypeHandler typeHandler) {
// passed type is primitive if (typeHandler instanceof BuiltinTypeHandler<?>) {
//TODO rename primitive to builtin ((BuiltinTypeHandler<?>) typeHandler).storePropertyValue(element, this);
if (type instanceof PrimitiveTypeHandler<?>) {
((PrimitiveTypeHandler<?>) type).storePropertyValue(element, this);
} else { } else {
// passed type is compund ie. has child properties // passed type is compound ie. has child properties
((CompoundTypeHandler)type).getProperties().forEach(property -> { ((CompoundTypeHandler) typeHandler).getProperties().forEach(property -> {
if (property instanceof PrimitiveTypeHandler<?>) { if (property instanceof BuiltinTypeHandler<?>) {
// recurse once more -> property is stored ((BuiltinTypeHandler<?>) property).storePropertyValue(element, this);
getProperties(element, property);
//could easily inline this
} else { } else {
CompoundTypeHandler child = ((CompoundTypeHandler) property); CompoundTypeHandler child = ((CompoundTypeHandler) property);
try { try {
@ -154,17 +150,16 @@ public class ContiguousList<E> implements List<E> {
} }
} }
@Override /**
public boolean remove(Object o) { * Get element at specified index.
throw new RuntimeException("Not yet implemented"); * <p>
} * Please note that this creates a new instance using the stored element data and is therefore
* not the recommended usecase because of the performance penalty. It's implemented for convenience, but
@Override * there are alternatives such as the propertyIterator and getXX...[implement]
@SuppressWarnings("NullableProblems") *
public boolean containsAll(Collection<?> collection) { * @param index The index of the element data
throw new RuntimeException("Not yet implemented"); * @return a new element instance
} */
@Override @Override
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public E get(int index) { public E get(int index) {
@ -173,14 +168,15 @@ public class ContiguousList<E> implements List<E> {
} }
data.position(elementIndices[index]); data.position(elementIndices[index]);
try { try {
if (type instanceof PrimitiveTypeHandler<?>) { if (rootHandler instanceof BuiltinTypeHandler<?>) {
return (E)((PrimitiveTypeHandler<?>)type).transform(ValueReader.read(data)); Object read = ValueReader.read(data);
return (E) ((BuiltinTypeHandler<?>) rootHandler).transform(read);
} }
// create a new instance of the list element type // create a new instance of the list element type
E newInstance = (E) type.getType().getDeclaredConstructor().newInstance(); E newInstance = (E) rootHandler.getType().getDeclaredConstructor().newInstance();
// set the data // set the data
setProperties(newInstance, (CompoundTypeHandler) type); setProperties(newInstance, (CompoundTypeHandler) rootHandler);
return newInstance; return newInstance;
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException | } catch (NoSuchMethodException | InstantiationException | IllegalAccessException |
@ -191,8 +187,8 @@ public class ContiguousList<E> implements List<E> {
private void setProperties(Object element, CompoundTypeHandler compoundType) { private void setProperties(Object element, CompoundTypeHandler compoundType) {
compoundType.getProperties().forEach(property -> { compoundType.getProperties().forEach(property -> {
if (property instanceof PrimitiveTypeHandler) { if (property instanceof BuiltinTypeHandler) {
PrimitiveTypeHandler<?> type =((PrimitiveTypeHandler<?>) property); BuiltinTypeHandler<?> type = ((BuiltinTypeHandler<?>) property);
type.setValue(element, ValueReader.read(data)); type.setValue(element, ValueReader.read(data));
} else { } else {
try { try {
@ -212,6 +208,65 @@ public class ContiguousList<E> implements List<E> {
}); });
} }
/**
* Returns an {@link Iterator} over the property values in the List. That is, it iterates all
* bean property values in a fixed order for all elements.
* <p>
* Because the values differ in type the output type is Object
*
* NB the actual type (right now) is the `raw` value: all integers are of type Long, BigInteger is String
* // That is unfortunate (or must I say: annoying!), but for something like JSON not a problem (I think).
* // So maybe keep this in (say 'rawValueIterator') and also create a typesafe iterator.
* <p>
* It detects {@link ConcurrentModificationException} if the underlying list was updated while iterating.
* <p>
* // I should probably include a type so that the caller could cast to the correct type
* // not sure yet
*
* @return an Iterator<?>
*/
public Iterator<?> valueIterator() {
return new ValueIterator();
}
class ValueIterator implements Iterator<Object> {
private final int originalSize;
private final int originalPosition;
ValueIterator() {
this.originalSize = size;
this.originalPosition = data.position();
data.position(0);
}
@Override
public boolean hasNext() {
return data.position() < originalPosition;
}
@Override
public Object next() {
if (originalSize != size) {
throw new ConcurrentModificationException("Modifications detected while iterating.");
}
/* The following depends on the bytebuffer position. Calling add(..) would mess it up
* so that's why we first check for modifications (me and the computer)
*
* I could also maintain the position here. But you main want to fail ... dunno */
return ValueReader.read(data);
}
}
/**
* Returns an {@link Iterator} over the property values of the specified element in the List.
*
* @return
*/
public Iterator<Object> valueIterator(int index) {
//TODO
return null;
}
public boolean addAll(Collection<? extends E> collection) { public boolean addAll(Collection<? extends E> collection) {
for (E element : collection) { for (E element : collection) {
add(element); add(element);
@ -219,84 +274,11 @@ public class ContiguousList<E> implements List<E> {
return true; return true;
} }
@Override
@SuppressWarnings("NullableProblems")
public boolean addAll(int i, Collection<? extends E> collection) {
throw new RuntimeException("Not yet implemented");
}
@Override
@SuppressWarnings("NullableProblems")
public boolean removeAll(Collection<?> collection) {
throw new RuntimeException("Not implemented");
}
@Override
@SuppressWarnings("NullableProblems")
public boolean retainAll(Collection<?> collection) {
throw new RuntimeException("Not implemented");
}
@Override
public void replaceAll(UnaryOperator<E> operator) {
throw new RuntimeException("Not yet implemented");
}
@Override
public void sort(Comparator<? super E> c) {
throw new RuntimeException("Not implemented");
}
public void clear() { public void clear() {
this.currentElementIndex = 0; this.currentElementValueIndex = 0;
this.size = 0; this.size = 0;
} }
@Override
public E set(int i, E e) {
throw new RuntimeException("Not implemented");
}
@Override
public void add(int i, E e) {
throw new RuntimeException("Not implemented");
}
@Override
public E remove(int i) {
throw new RuntimeException("Not implemented");
}
@Override
public int indexOf(Object o) {
throw new RuntimeException("Not implemented");
}
@Override
public int lastIndexOf(Object o) {
throw new RuntimeException("Not implemented");
}
@Override
public ListIterator<E> listIterator() {
throw new RuntimeException("Not yet implemented");
}
@Override
public ListIterator<E> listIterator(int i) {
throw new RuntimeException("Not yet implemented");
}
@Override
public List<E> subList(int i, int i1) {
throw new RuntimeException("Not yet implemented");
}
@Override
public Spliterator<E> spliterator() {
return List.super.spliterator();
}
public int size() { public int size() {
return size; return size;
} }
@ -306,14 +288,9 @@ public class ContiguousList<E> implements List<E> {
return size == 0; return size == 0;
} }
@Override
public boolean contains(Object o) {
throw new RuntimeException("Not implemented");
}
@Override @Override
public Iterator<E> iterator() { public Iterator<E> iterator() {
return new Iter<>(); return new ElementIterator<>();
} }
@Override @Override
@ -338,9 +315,15 @@ public class ContiguousList<E> implements List<E> {
} }
class Iter<F> implements Iterator<F> { class ElementIterator<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() {
@ -356,15 +339,15 @@ public class ContiguousList<E> implements List<E> {
private void store(byte[] bytes) { private void store(byte[] bytes) {
ensureFree(bytes.length); ensureFree(bytes.length);
data.position(currentElementIndex); // ensures intermittent reads/writes data.position(currentElementValueIndex); // ensures intermittent reads/writes are safe
data.put(bytes); data.put(bytes);
currentElementIndex += bytes.length; currentElementValueIndex += bytes.length;
} }
private void store0() { private void store0() {
ensureFree(1); ensureFree(1);
data.put((byte) 0); data.put((byte) 0);
currentElementIndex += 1; currentElementValueIndex += 1;
} }
void storeString(String value) { void storeString(String value) {
@ -436,7 +419,7 @@ public class ContiguousList<E> implements List<E> {
} }
byte[] getData() { byte[] getData() {
return Arrays.copyOfRange(data.array(), 0, currentElementIndex); return Arrays.copyOfRange(data.array(), 0, currentElementValueIndex);
} }
int[] getElementIndices() { int[] getElementIndices() {
@ -444,7 +427,7 @@ public class ContiguousList<E> implements List<E> {
} }
private void ensureFree(int length) { private void ensureFree(int length) {
while (currentElementIndex + length > data.capacity()) { while (currentElementValueIndex + length > data.capacity()) {
byte[] bytes = this.data.array(); byte[] bytes = this.data.array();
this.data = ByteBuffer.allocate(this.data.capacity() * 2); this.data = ByteBuffer.allocate(this.data.capacity() * 2);
this.data.put(bytes); this.data.put(bytes);

View file

@ -5,7 +5,7 @@ import java.lang.invoke.MethodHandle;
/** /**
* Stores a double value. * Stores a double value.
*/ */
class DoubleHandler extends PrimitiveTypeHandler<Double> { class DoubleHandler extends BuiltinTypeHandler<Double> {
public DoubleHandler(MethodHandle getter, MethodHandle setter) { public DoubleHandler(MethodHandle getter, MethodHandle setter) {
super(Double.class, getter, setter); super(Double.class, getter, setter);
} }

View file

@ -2,7 +2,7 @@ package nl.sanderhautvast.contiguous;
import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandle;
class FloatHandler extends PrimitiveTypeHandler<Float> { class FloatHandler extends BuiltinTypeHandler<Float> {
public FloatHandler(MethodHandle getter, MethodHandle setter) { public FloatHandler(MethodHandle getter, MethodHandle setter) {
super(Float.class, getter, setter); super(Float.class, getter, setter);
} }

View file

@ -2,7 +2,7 @@ package nl.sanderhautvast.contiguous;
import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandle;
class IntegerHandler extends PrimitiveTypeHandler<Integer> { class IntegerHandler extends BuiltinTypeHandler<Integer> {
public IntegerHandler(MethodHandle getter, MethodHandle setter) { public IntegerHandler(MethodHandle getter, MethodHandle setter) {
super(Integer.class, getter, setter); super(Integer.class, getter, setter);
} }
@ -20,4 +20,14 @@ class IntegerHandler extends PrimitiveTypeHandler<Integer> {
public void setValue(Object instance, Object value) { public void setValue(Object instance, Object value) {
super.setValue(instance, ((Long) value).intValue()); super.setValue(instance, ((Long) value).intValue());
} }
@Override
public Object transform(Object value) {
// could be Long (raw value)
// or Integer when it's a property with known type
if (value instanceof Long) {
return ((Long) value).intValue();
}
return value;
}
} }

View file

@ -2,7 +2,7 @@ package nl.sanderhautvast.contiguous;
import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandle;
class LongHandler extends PrimitiveTypeHandler<Long> { class LongHandler extends BuiltinTypeHandler<Long> {
public LongHandler(MethodHandle getter, MethodHandle setter) { public LongHandler(MethodHandle getter, MethodHandle setter) {
super(Long.class, getter, setter); super(Long.class, getter, setter);
} }

View file

@ -0,0 +1,117 @@
package nl.sanderhautvast.contiguous;
import java.util.*;
import java.util.function.UnaryOperator;
/**
* Base class that is a list of all the methods live that will likely not be implemented (pun intended)
* Only purpose: reduce linecount in the subclass.
*
* @param <E>
*/
public abstract class NotImplementedList<E> implements List<E> {
@Override
public boolean contains(Object o) {
throw new RuntimeException("Not implemented");
// is possible, but not feasible: iterate, create instance, then compare ??
}
@Override
public Spliterator<E> spliterator() {
return List.super.spliterator();
// not sure about this yet
}
@Override
public boolean remove(Object o) {
throw new RuntimeException("Not yet implemented");
// is possible, but not feasible: iterate, create instance, then compare ??
// and then removing, which requires moving a lot of bytes
}
@Override
@SuppressWarnings("NullableProblems")
public boolean containsAll(Collection<?> collection) {
throw new RuntimeException("Not yet implemented");
// is possible, but not feasible: iterate, create instance, then compare ??
}
@Override
public boolean addAll(int i, Collection<? extends E> collection) {
throw new RuntimeException("Not implemented");
// is possible, but not feasible because it would require moving a lot of bytes
}
@Override
public void replaceAll(UnaryOperator<E> operator) {
throw new RuntimeException("Not implemented");
// is possible, but not feasible because it would require moving a lot of bytes
}
@Override
public boolean removeAll(Collection<?> collection) {
throw new RuntimeException("Not implemented");
// is possible, but not feasible because it would require moving a lot of bytes
}
@Override
public boolean retainAll(Collection<?> collection) {
throw new RuntimeException("Not implemented");
// is possible, but not feasible because it would require moving a lot of bytes
}
@Override
public E set(int i, E e) {
throw new RuntimeException("Not implemented");
// is possible, but not feasible because it would require moving a lot of bytes
}
@Override
public void add(int i, E e) {
throw new RuntimeException("Not implemented");
// is possible, but not feasible because it would require moving a lot of bytes
}
@Override
public E remove(int i) {
throw new RuntimeException("Not implemented");
// is possible, but not feasible because it would require moving a lot of bytes
}
@Override
public int indexOf(Object o) {
throw new RuntimeException("Not implemented");
// is possible, but not feasible: iterate, create instance, then compare ??
}
@Override
public int lastIndexOf(Object o) {
throw new RuntimeException("Not implemented");
// is possible, but not feasible
}
@Override
public ListIterator<E> listIterator() {
throw new RuntimeException("Not implemented");
// ListIterator contains remove, so not possible
}
@Override
public ListIterator<E> listIterator(int i) {
throw new RuntimeException("Not yet implemented");
// ListIterator contains remove, so not possible
}
@Override
public void sort(Comparator<? super E> c) {
throw new RuntimeException("Not implemented");
// an immutable sort would be an option
}
@Override
public List<E> subList(int i, int i1) {
throw new RuntimeException("Not yet implemented");
// possible
}
}

View file

@ -11,7 +11,7 @@ import java.util.Map;
* Maps the propertyvalue type to a PropertyHandler * Maps the propertyvalue type to a PropertyHandler
*/ */
final class PropertyHandlerFactory { final class PropertyHandlerFactory {
private static final Map<Class<?>, Class<? extends PrimitiveTypeHandler<?>>> TYPE_HANDLERS = new HashMap<>(); private static final Map<Class<?>, Class<? extends BuiltinTypeHandler<?>>> TYPE_HANDLERS = new HashMap<>();
private PropertyHandlerFactory() { private PropertyHandlerFactory() {
} }
@ -41,13 +41,13 @@ final class PropertyHandlerFactory {
return TYPE_HANDLERS.containsKey(type); return TYPE_HANDLERS.containsKey(type);
} }
public static <T> PrimitiveTypeHandler<T> forType(Class<T> type, MethodHandle getter, MethodHandle setter) { public static <T> BuiltinTypeHandler<T> forType(Class<T> type, MethodHandle getter, MethodHandle setter) {
try { try {
Class<? extends PrimitiveTypeHandler<?>> appenderClass = TYPE_HANDLERS.get(type); Class<? extends BuiltinTypeHandler<?>> appenderClass = TYPE_HANDLERS.get(type);
if (appenderClass == null) { if (appenderClass == null) {
throw new IllegalStateException("No Handler for " + type.getName()); throw new IllegalStateException("No Handler for " + type.getName());
} }
return (PrimitiveTypeHandler<T>) appenderClass.getDeclaredConstructor(MethodHandle.class, MethodHandle.class) return (BuiltinTypeHandler<T>) appenderClass.getDeclaredConstructor(MethodHandle.class, MethodHandle.class)
.newInstance(getter, setter); .newInstance(getter, setter);
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException | } catch (NoSuchMethodException | InstantiationException | IllegalAccessException |
InvocationTargetException e) { InvocationTargetException e) {
@ -55,14 +55,14 @@ final class PropertyHandlerFactory {
} }
} }
public static <T> PrimitiveTypeHandler<T> forType(Class<T> type) { public static <T> BuiltinTypeHandler<T> forType(Class<T> type) {
return forType(type, null, null); return forType(type, null, null);
} }
/** /**
* register a new TypeHandler that cannot be derived from bean properties * register a new TypeHandler that cannot be derived from bean properties
*/ */
public static void register(Class<?> type, Class<? extends PrimitiveTypeHandler<?>> typehandler) { public static void register(Class<?> type, Class<? extends BuiltinTypeHandler<?>> typehandler) {
TYPE_HANDLERS.put(type, typehandler); TYPE_HANDLERS.put(type, typehandler);
} }
} }

View file

@ -2,7 +2,7 @@ package nl.sanderhautvast.contiguous;
import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandle;
class ShortHandler extends PrimitiveTypeHandler<Short> { class ShortHandler extends BuiltinTypeHandler<Short> {
public ShortHandler(MethodHandle getter, MethodHandle setter) { public ShortHandler(MethodHandle getter, MethodHandle setter) {
super(Short.class, getter, setter); super(Short.class, getter, setter);
} }
@ -16,4 +16,12 @@ class ShortHandler extends PrimitiveTypeHandler<Short> {
public void setValue(Object instance, Object value) { public void setValue(Object instance, Object value) {
super.setValue(instance, ((Long) value).shortValue()); super.setValue(instance, ((Long) value).shortValue());
} }
@Override
public Object transform(Object value) {
if (value instanceof Long) {
return ((Long) value).shortValue();
}
return value;
}
} }

View file

@ -2,7 +2,7 @@ package nl.sanderhautvast.contiguous;
import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandle;
class StringHandler extends PrimitiveTypeHandler<String> { class StringHandler extends BuiltinTypeHandler<String> {
public StringHandler(MethodHandle getter, MethodHandle setter) { public StringHandler(MethodHandle getter, MethodHandle setter) {
super(String.class, getter, setter); super(String.class, getter, setter);
} }

View file

@ -40,9 +40,9 @@ class ValueReader {
} else if (columnType == 7) { } else if (columnType == 7) {
return buffer.getDouble(); return buffer.getDouble();
} else if (columnType == 8) { } else if (columnType == 8) {
return 0; return 0L; //has to be long!
} else if (columnType == 9) { } else if (columnType == 9) {
return 1; return 1L;
} else if (columnType == 10) { } else if (columnType == 10) {
return buffer.getFloat(); return buffer.getFloat();
} else if (columnType >= 12 && columnType % 2 == 0) { } else if (columnType >= 12 && columnType % 2 == 0) {

View file

@ -3,6 +3,7 @@ package nl.sanderhautvast.contiguous;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.Iterator;
import java.util.List; import java.util.List;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*;
@ -141,4 +142,59 @@ public class ContiguousListTest {
bigIntegers.add(new BigInteger("1000000000")); bigIntegers.add(new BigInteger("1000000000"));
assertEquals(1_000_000_000L, bigIntegers.get(0).longValue()); assertEquals(1_000_000_000L, bigIntegers.get(0).longValue());
} }
@Test
public void testIterator() {
ContiguousList<Integer> integers = new ContiguousList<>(Integer.class);
for (int i = 0; i < 100; i++) {
integers.add(i);
}
int prevValue = -1;
for (int value : integers) {
assertEquals(prevValue, value - 1);
prevValue = value;
}
integers.add(100);
assertEquals(100, integers.get(100));
}
@Test
public void testPrimitiveIterator() {
ContiguousList<Integer> integers = new ContiguousList<>(Integer.class);
for (int i = 0; i < 100; i++) {
integers.add(i);
}
long prevValue = -1;
for (Iterator<?> intIter = integers.valueIterator(); intIter.hasNext(); ) {
long value = (long) intIter.next();
assertEquals(prevValue, value - 1);
prevValue = value;
}
integers.add(100);
assertEquals(100, integers.get(100));
}
@Test
public void testCompoundValueIterator() {
ContiguousList<IntBean> integers = new ContiguousList<>(IntBean.class);
for (int i = 0; i < 100; i++) {
integers.add(new IntBean(i));
}
long prevValue = -1;
for (Iterator<?> intIter = integers.valueIterator(); intIter.hasNext(); ) {
long value = (long) intIter.next(); // here a value (IntBean.value)
assertEquals(prevValue, value - 1);
prevValue = value;
}
integers.add(new IntBean(100));
assertEquals(new IntBean(100), integers.get(100)); // here an instance
}
} }