T getProperty(int propertyId) throws IOException {
if (!loadedProperties[propertyId]) {
long propertyUID = propertyReferences[propertyId];
- database.getObjectsIO().loadProperty(this, propertyId, propertyGetters[propertyId], propertyTypes[propertyId], propertyUID);
+ databaseTools.getObjectsIO().loadProperty(this, propertyId, propertyGetters[propertyId], propertyTypes[propertyId], propertyUID);
}
return (T) loadedPropertyValues[propertyId];
}
@@ -113,4 +114,12 @@ public abstract class EnhancedObject {
public void onUpgrade(int oldObjectVersion, EnhancedObjectUpgrader enhancedObjectUpgrader) throws IOException {
throw new NotImplementedException("Method onUpgrade() is not implemented for class " + this.getClass().getSimpleName());
}
+
+ public IDatabaseTools getDatabaseTools() {
+ return databaseTools;
+ }
+
+ public void setDatabaseTools(IDatabaseTools databaseTools) {
+ this.databaseTools = databaseTools;
+ }
}
diff --git a/src/main/java/org/warp/cowdb/FunctionWithIO.java b/src/main/java/org/warp/cowdb/FunctionWithIO.java
new file mode 100644
index 0000000..fb15117
--- /dev/null
+++ b/src/main/java/org/warp/cowdb/FunctionWithIO.java
@@ -0,0 +1,78 @@
+package org.warp.cowdb;
+
+import java.io.IOException;
+import java.util.Objects;
+
+/**
+ * Represents a function that accepts one argument and produces a result.
+ *
+ * This is a functional interface
+ * whose functional method is {@link #apply(Object)}.
+ *
+ * @param the type of the input to the function
+ * @param the type of the result of the function
+ *
+ * @since 1.8
+ */
+@FunctionalInterface
+public interface FunctionWithIO {
+
+ /**
+ * Applies this function to the given argument.
+ *
+ * @param t the function argument
+ * @return the function result
+ */
+ R apply(T t) throws IOException;
+
+ /**
+ * Returns a composed function that first applies the {@code before}
+ * function to its input, and then applies this function to the result.
+ * If evaluation of either function throws an exception, it is relayed to
+ * the caller of the composed function.
+ *
+ * @param the type of input to the {@code before} function, and to the
+ * composed function
+ * @param before the function to apply before this function is applied
+ * @return a composed function that first applies the {@code before}
+ * function and then applies this function
+ * @throws NullPointerException if before is null
+ *
+ * @see #andThen(FunctionWithIO)
+ */
+ default FunctionWithIO compose(FunctionWithIO super V, ? extends T> before) {
+ Objects.requireNonNull(before);
+ return (V v) -> apply(before.apply(v));
+ }
+
+ /**
+ * Returns a composed function that first applies this function to
+ * its input, and then applies the {@code after} function to the result.
+ * If evaluation of either function throws an exception, it is relayed to
+ * the caller of the composed function.
+ *
+ * @param the type of output of the {@code after} function, and of the
+ * composed function
+ * @param after the function to apply after this function is applied
+ * @return a composed function that first applies this function and then
+ * applies the {@code after} function
+ * @throws NullPointerException if after is null
+ *
+ * @see #compose(FunctionWithIO)
+ */
+ default FunctionWithIO andThen(FunctionWithIO super R, ? extends V> after) {
+ Objects.requireNonNull(after);
+ return (T t) -> after.apply(apply(t));
+ }
+
+ /**
+ * Returns a function that always returns its input argument.
+ *
+ * @param the type of the input and output objects to the function
+ * @return a function that always returns its input argument
+ */
+ static FunctionWithIO identity() {
+ return t -> t;
+ }
+}
+
diff --git a/src/main/java/org/warp/cowdb/IDatabase.java b/src/main/java/org/warp/cowdb/IDatabase.java
index 2e87d39..0ffeec8 100644
--- a/src/main/java/org/warp/cowdb/IDatabase.java
+++ b/src/main/java/org/warp/cowdb/IDatabase.java
@@ -5,6 +5,4 @@ import java.io.IOException;
public interface IDatabase {
void close() throws IOException;
- IDataInitializer getDataInitializer();
- IObjectsIO getObjectsIO();
}
diff --git a/src/main/java/org/warp/cowdb/IDatabaseTools.java b/src/main/java/org/warp/cowdb/IDatabaseTools.java
new file mode 100644
index 0000000..0876f01
--- /dev/null
+++ b/src/main/java/org/warp/cowdb/IDatabaseTools.java
@@ -0,0 +1,12 @@
+package org.warp.cowdb;
+
+import org.warp.jcwdb.ann.DBDataType;
+
+import java.io.IOException;
+import java.lang.reflect.Method;
+
+public interface IDatabaseTools {
+ void initializeEnhancedObject(EnhancedObject enhancedObject) throws IOException;
+
+ IObjectsIO getObjectsIO();
+}
diff --git a/src/main/java/org/warp/cowdb/IObjectsIO.java b/src/main/java/org/warp/cowdb/IObjectsIO.java
index 39212a6..8e18994 100644
--- a/src/main/java/org/warp/cowdb/IObjectsIO.java
+++ b/src/main/java/org/warp/cowdb/IObjectsIO.java
@@ -108,4 +108,6 @@ public interface IObjectsIO {
void loadProperty(EnhancedObject enhancedObject, int propertyId, Method propertyGetter, DBDataType propertyType, long propertyUID) throws IOException;
void registerClass(Class> type, int id);
+
+ IDataInitializer getDataInitializer();
}
diff --git a/src/main/java/org/warp/cowdb/IReferencesMetadata.java b/src/main/java/org/warp/cowdb/IReferencesMetadata.java
index 8b3d248..2a40a52 100644
--- a/src/main/java/org/warp/cowdb/IReferencesMetadata.java
+++ b/src/main/java/org/warp/cowdb/IReferencesMetadata.java
@@ -28,4 +28,6 @@ public interface IReferencesMetadata {
* Close file
*/
void close() throws IOException;
+
+ long getFirstFreeReference();
}
diff --git a/src/main/java/org/warp/cowdb/database/DatabaseBlocksIO.java b/src/main/java/org/warp/cowdb/database/DatabaseBlocksIO.java
new file mode 100644
index 0000000..0abca87
--- /dev/null
+++ b/src/main/java/org/warp/cowdb/database/DatabaseBlocksIO.java
@@ -0,0 +1,41 @@
+package org.warp.cowdb.database;
+
+import org.warp.cowdb.BlockInfo;
+import org.warp.cowdb.IBlocksIO;
+import org.warp.cowdb.IBlocksMetadata;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+import static org.warp.cowdb.IBlocksMetadata.EMPTY_BLOCK_ID;
+
+public class DatabaseBlocksIO implements IBlocksIO {
+
+ private final DatabaseFileIO fileIO;
+ private final IBlocksMetadata blocksMetadata;
+
+ public DatabaseBlocksIO(DatabaseFileIO fileIO, IBlocksMetadata blocksMetadata) {
+ this.fileIO = fileIO;
+ this.blocksMetadata = blocksMetadata;
+ }
+
+ @Override
+ public long newBlock(int size, ByteBuffer data) throws IOException {
+ long index = fileIO.writeAtEnd(size, data);
+ return blocksMetadata.newBlock(index, size);
+ }
+
+ @Override
+ public ByteBuffer readBlock(long blockId) throws IOException {
+ if (blockId == EMPTY_BLOCK_ID) {
+ return ByteBuffer.wrap(new byte[0]);
+ }
+ BlockInfo blockInfo = blocksMetadata.getBlockInfo(blockId);
+ return fileIO.readAt(blockInfo.getIndex(), blockInfo.getSize());
+ }
+
+ @Override
+ public void close() {
+
+ }
+}
diff --git a/src/main/java/org/warp/cowdb/database/DatabaseBlocksMetadata.java b/src/main/java/org/warp/cowdb/database/DatabaseBlocksMetadata.java
new file mode 100644
index 0000000..7981dac
--- /dev/null
+++ b/src/main/java/org/warp/cowdb/database/DatabaseBlocksMetadata.java
@@ -0,0 +1,52 @@
+package org.warp.cowdb.database;
+
+
+import org.warp.cowdb.BlockInfo;
+import org.warp.cowdb.IBlocksMetadata;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.SeekableByteChannel;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+
+public class DatabaseBlocksMetadata implements IBlocksMetadata {
+ private final SeekableByteChannel metaFileChannel;
+ private final int BLOCK_META_BYTES_COUNT = Long.BYTES + Integer.BYTES;
+ private long firstFreeBlock;
+
+ public DatabaseBlocksMetadata(Path metaFile) throws IOException {
+ metaFileChannel = Files.newByteChannel(metaFile, StandardOpenOption.READ, StandardOpenOption.WRITE);
+ firstFreeBlock = metaFileChannel.size() / BLOCK_META_BYTES_COUNT;
+ }
+
+ @Override
+ public BlockInfo getBlockInfo(long blockId) throws IOException {
+ if (blockId == EMPTY_BLOCK_ID) {
+ return EMPTY_BLOCK_INFO;
+ }
+ ByteBuffer buffer = ByteBuffer.allocate(BLOCK_META_BYTES_COUNT);
+ metaFileChannel.position(blockId * BLOCK_META_BYTES_COUNT).read(buffer);
+ buffer.flip();
+ long index = buffer.getLong();
+ int size = buffer.getInt();
+ return new BlockInfo(index, size);
+ }
+
+ @Override
+ public long newBlock(long index, int size) throws IOException {
+ long newBlockId = firstFreeBlock++;
+ ByteBuffer data = ByteBuffer.allocate(BLOCK_META_BYTES_COUNT);
+ data.putLong(index);
+ data.putInt(size);
+ data.flip();
+ metaFileChannel.position(newBlockId * BLOCK_META_BYTES_COUNT).write(data);
+ return newBlockId;
+ }
+
+ @Override
+ public void close() throws IOException {
+ metaFileChannel.close();
+ }
+}
diff --git a/src/main/java/org/warp/cowdb/database/DatabaseDataInitializer.java b/src/main/java/org/warp/cowdb/database/DatabaseDataInitializer.java
new file mode 100644
index 0000000..1fb0fdf
--- /dev/null
+++ b/src/main/java/org/warp/cowdb/database/DatabaseDataInitializer.java
@@ -0,0 +1,104 @@
+package org.warp.cowdb.database;
+
+import org.warp.cowdb.EnhancedObject;
+import org.warp.cowdb.IDataInitializer;
+import org.warp.jcwdb.ann.DBDataType;
+import org.warp.jcwdb.ann.DBField;
+import org.warp.jcwdb.ann.DBPropertyGetter;
+import org.warp.jcwdb.ann.DBPropertySetter;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+public class DatabaseDataInitializer implements IDataInitializer {
+
+ private final DatabaseObjectsIO objectsIO;
+
+ public DatabaseDataInitializer(DatabaseObjectsIO objectsIO) {
+ this.objectsIO = objectsIO;
+ }
+
+ @Override
+ public void initializeDBObject(EnhancedObject obj) throws IOException {
+ initializeDBObjectFields(obj);
+ initializeDBObjectProperties(obj);
+ }
+
+ private void initializeDBObjectFields(EnhancedObject obj) throws IOException {
+ // Declare the variables needed to get the biggest field Id
+ Field[] unorderedFields = objectsIO.getFields(obj);
+ // Find the biggest field Id
+ int biggestFieldId = objectsIO.getBiggestFieldId(unorderedFields);
+
+ // Allocate new UIDs
+ long[] fieldUIDs = objectsIO.allocateNewUIDs(biggestFieldId + 1);
+
+ // Declare the other variables
+ Field[] fields = new Field[biggestFieldId + 1];
+ DBDataType[] orderedFieldTypes = new DBDataType[biggestFieldId + 1];
+
+ // Load all fields metadata and load them
+ for (Field field : unorderedFields) {
+ DBField fieldAnnotation = field.getAnnotation(DBField.class);
+ int fieldId = fieldAnnotation.id();
+ DBDataType fieldType = fieldAnnotation.type();
+ objectsIO.loadField(obj, field, fieldType, fieldUIDs[fieldId]);
+ fields[fieldId] = field;
+ orderedFieldTypes[fieldId] = fieldType;
+ }
+ // Set fields metadata
+ obj.setFields(fields, orderedFieldTypes, fieldUIDs);
+ }
+
+ private void initializeDBObjectProperties(EnhancedObject obj) throws IOException {
+ // Declare the variables needed to get the biggest property Id
+ Method[] unorderedPropertyGetters = obj.getPropertyGetters();
+ Method[] unorderedPropertySetters = obj.getPropertySetters();
+
+ // Find the biggest property Id
+ int biggestGetter = objectsIO.getBiggestPropertyGetterId(unorderedPropertyGetters);
+ int biggestSetter = objectsIO.getBiggestPropertySetterId(unorderedPropertySetters);
+ int biggestPropertyId = biggestGetter > biggestSetter ? biggestGetter : biggestSetter;
+
+ // Allocate new UIDs
+ long[] propertyUIDs = objectsIO.allocateNewUIDs(biggestPropertyId + 1);
+
+ for (Method property : unorderedPropertySetters) {
+ DBPropertySetter fieldAnnotation = property.getAnnotation(DBPropertySetter.class);
+ int propertyId = fieldAnnotation.id();
+ if (propertyId > biggestPropertyId) {
+ biggestPropertyId = propertyId;
+ }
+ }
+
+ // Declare the other variables
+ DBDataType[] propertyTypes = new DBDataType[biggestPropertyId + 1];
+ Method[] propertyGetters = new Method[biggestPropertyId + 1];
+ Method[] propertySetters = new Method[biggestPropertyId + 1];
+ Map setterMethods = new LinkedHashMap<>();
+ Map getterMethods = new LinkedHashMap<>();
+
+ // Load the properties metadata
+ for (Method property : unorderedPropertyGetters) {
+ DBPropertyGetter propertyAnnotation = property.getAnnotation(DBPropertyGetter.class);
+ int propertyId = propertyAnnotation.id();
+ DBDataType propertyType = propertyAnnotation.type();
+ propertyTypes[propertyId] = propertyType;
+ propertyGetters[propertyId] = property;
+ getterMethods.put(property.getName(), propertyAnnotation);
+ }
+ for (Method property : unorderedPropertySetters) {
+ DBPropertySetter propertyAnnotation = property.getAnnotation(DBPropertySetter.class);
+ int propertyId = propertyAnnotation.id();
+ DBDataType propertyType = propertyAnnotation.type();
+ propertyTypes[propertyId] = propertyType;
+ propertySetters[propertyId] = property;
+ setterMethods.put(property.getName(), propertyAnnotation);
+ }
+ // Set properties metadata
+ obj.setProperties(propertyGetters, propertySetters, propertyTypes, propertyUIDs, setterMethods, getterMethods);
+ }
+}
diff --git a/src/main/java/org/warp/cowdb/database/DatabaseEnhancedObjectUpgrader.java b/src/main/java/org/warp/cowdb/database/DatabaseEnhancedObjectUpgrader.java
new file mode 100644
index 0000000..63121b0
--- /dev/null
+++ b/src/main/java/org/warp/cowdb/database/DatabaseEnhancedObjectUpgrader.java
@@ -0,0 +1,31 @@
+package org.warp.cowdb.database;
+
+import org.warp.cowdb.EnhancedObjectUpgrader;
+import org.warp.jcwdb.ann.DBDataType;
+
+import java.io.IOException;
+import java.util.function.Supplier;
+
+public class DatabaseEnhancedObjectUpgrader implements EnhancedObjectUpgrader {
+ private final DatabaseObjectsIO objectsIO;
+ private final long[] fieldRefs;
+ private final long[] methodRefs;
+
+ public DatabaseEnhancedObjectUpgrader(DatabaseObjectsIO objectsIO, long[] fieldRefs, long[] methodRefs) {
+ this.objectsIO = objectsIO;
+ this.fieldRefs = fieldRefs;
+ this.methodRefs = methodRefs;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public Object getField(int id, DBDataType type, Supplier> enhancedClassType) throws IOException {
+ return objectsIO.loadData(type, fieldRefs[id], enhancedClassType);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public Object getMethod(int id, DBDataType type, Supplier> enhancedClassType) throws IOException {
+ return objectsIO.loadData(type, methodRefs[id], enhancedClassType);
+ }
+}
diff --git a/src/main/java/org/warp/cowdb/database/DatabaseFileIO.java b/src/main/java/org/warp/cowdb/database/DatabaseFileIO.java
new file mode 100644
index 0000000..7304b12
--- /dev/null
+++ b/src/main/java/org/warp/cowdb/database/DatabaseFileIO.java
@@ -0,0 +1,64 @@
+package org.warp.cowdb.database;
+
+
+import org.warp.cowdb.IFileIO;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.SeekableByteChannel;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+import java.util.concurrent.ExecutionException;
+
+public class DatabaseFileIO implements IFileIO {
+
+ private final SeekableByteChannel dataFileChannel;
+ private final Object dataAccessLock = new Object();
+ private long firstFreeIndex;
+
+ public DatabaseFileIO(Path dataFile) throws IOException {
+ synchronized (dataAccessLock) {
+ dataFileChannel = Files.newByteChannel(dataFile, StandardOpenOption.READ, StandardOpenOption.WRITE);
+ firstFreeIndex = dataFileChannel.size();
+ }
+ }
+
+ @Override
+ public ByteBuffer readAt(long index, int length) throws IOException {
+ ByteBuffer dataBuffer = ByteBuffer.allocate(length);
+ dataFileChannel.position(index).read(dataBuffer);
+ dataBuffer.flip();
+ return dataBuffer;
+ }
+
+ @Override
+ public void writeAt(long index, int length, ByteBuffer data) throws IOException {
+ synchronized (dataAccessLock) {
+ if (data.position() != 0) {
+ throw new IOException("You didn't flip the ByteBuffer!");
+ }
+ if (firstFreeIndex < index + length) {
+ firstFreeIndex = index + length;
+ }
+ dataFileChannel.position(index).write(data);
+ }
+ }
+
+ @Override
+ public long writeAtEnd(int length, ByteBuffer data) throws IOException {
+ synchronized (dataAccessLock) {
+ long index = firstFreeIndex;
+ firstFreeIndex += length;
+ writeAt(index, length, data);
+ return index;
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ synchronized (dataAccessLock) {
+ dataFileChannel.close();
+ }
+ }
+}
diff --git a/src/main/java/org/warp/cowdb/database/DatabaseObjectsIO.java b/src/main/java/org/warp/cowdb/database/DatabaseObjectsIO.java
new file mode 100644
index 0000000..00fd440
--- /dev/null
+++ b/src/main/java/org/warp/cowdb/database/DatabaseObjectsIO.java
@@ -0,0 +1,605 @@
+package org.warp.cowdb.database;
+
+import com.esotericsoftware.kryo.Kryo;
+import com.esotericsoftware.kryo.io.Input;
+import com.esotericsoftware.kryo.io.Output;
+import it.unimi.dsi.fastutil.booleans.BooleanArrayList;
+import it.unimi.dsi.fastutil.bytes.ByteArrayList;
+import it.unimi.dsi.fastutil.chars.CharArrayList;
+import it.unimi.dsi.fastutil.ints.IntArrayList;
+import it.unimi.dsi.fastutil.longs.LongArrayList;
+import it.unimi.dsi.fastutil.shorts.ShortArrayList;
+import org.apache.commons.lang3.reflect.FieldUtils;
+import org.warp.cowdb.*;
+import org.warp.jcwdb.ann.*;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.nio.ByteBuffer;
+import java.util.*;
+import java.util.function.Supplier;
+
+public class DatabaseObjectsIO implements IObjectsIO {
+
+ private final IDatabaseTools databaseTools;
+ private final DatabaseReferencesIO referencesIO;
+
+ private final Object accessLock = new Object();
+ private final DatabaseDataInitializer dataInitializer;
+
+ private Kryo kryo = new Kryo();
+
+ public DatabaseObjectsIO(IDatabaseTools databaseTools, DatabaseReferencesIO referencesIO) {
+ this.databaseTools = databaseTools;
+ this.referencesIO = referencesIO;
+ this.dataInitializer = new DatabaseDataInitializer(this);
+ kryo.setRegistrationRequired(false);
+ int id = -90;
+ registerClass(boolean[].class, id++);
+ registerClass(byte[].class, id++);
+ registerClass(short[].class, id++);
+ registerClass(char[].class, id++);
+ registerClass(int[].class, id++);
+ registerClass(long[].class, id++);
+ registerClass(Boolean[].class, id++);
+ registerClass(Byte[].class, id++);
+ registerClass(Short[].class, id++);
+ registerClass(Character[].class, id++);
+ registerClass(Integer[].class, id++);
+ registerClass(Long[].class, id++);
+ registerClass(String.class, id++);
+ registerClass(String[].class, id++);
+ registerClass(Boolean.class, id++);
+ registerClass(Byte.class, id++);
+ registerClass(Short.class, id++);
+ registerClass(Character.class, id++);
+ registerClass(Integer.class, id++);
+ registerClass(Class.class, id++);
+ registerClass(Object.class, id++);
+ registerClass(Object[].class, id++);
+ registerClass(Long.class, id++);
+ registerClass(String.class, id++);
+ registerClass(String[].class, id++);
+ registerClass(boolean[][].class, id++);
+ registerClass(byte[][].class, id++);
+ registerClass(short[][].class, id++);
+ registerClass(char[][].class, id++);
+ registerClass(int[][].class, id++);
+ registerClass(long[][].class, id++);
+ registerClass(String[][].class, id++);
+ registerClass(List.class, id++);
+ registerClass(ArrayList.class, id++);
+ registerClass(LinkedList.class, id++);
+ registerClass(Set.class, id++);
+ registerClass(HashSet.class, id++);
+ registerClass(LinkedHashSet.class, id++);
+ registerClass(Map.class, id++);
+ registerClass(HashMap.class, id++);
+ registerClass(LinkedHashMap.class, id++);
+ registerClass(TreeMap.class, id++);
+ registerClass(BooleanArrayList.class, id++);
+ registerClass(ByteArrayList.class, id++);
+ registerClass(ShortArrayList.class, id++);
+ registerClass(CharArrayList.class, id++);
+ registerClass(IntArrayList.class, id++);
+ registerClass(LongArrayList.class, id++);
+ registerClass(TreeSet.class, id++);
+ registerClass(SortedSet.class, id++);
+ registerClass(SortedMap.class, id++);
+ }
+
+ @Override
+ public T loadEnhancedObject(long reference, Class objectType) throws IOException {
+ synchronized (accessLock) {
+ ByteBuffer buffer = referencesIO.readFromReference(reference);
+ if (buffer.limit() == 0) {
+ return null;
+ }
+ int serializedVersion = Byte.toUnsignedInt(buffer.get());
+ int fieldsCount = buffer.getInt();
+ int methodsCount = buffer.getInt();
+ long[] fieldRefs = new long[fieldsCount];
+ long[] methodRefs = new long[methodsCount];
+ for (int i = 0; i < fieldsCount; i++) {
+ fieldRefs[i] = buffer.getLong();
+ }
+ for (int i = 0; i < methodsCount; i++) {
+ methodRefs[i] = buffer.getLong();
+ }
+ return preloadEnhancedObject(objectType, serializedVersion, fieldRefs, methodRefs);
+ }
+ }
+
+
+ @SuppressWarnings("unchecked")
+ Object loadData(DBDataType propertyType, long dataReference, Supplier> returnType) throws IOException {
+ switch (propertyType) {
+ case ENHANCED_OBJECT:
+ return loadEnhancedObject(dataReference, (Class extends EnhancedObject>) returnType.get());
+ case OBJECT:
+ return loadObject(dataReference);
+ case REFERENCES_LIST:
+ return loadReferencesList(dataReference);
+ case BOOLEAN:
+ return loadBoolean(dataReference);
+ case BYTE:
+ return loadByte(dataReference);
+ case SHORT:
+ return loadShort(dataReference);
+ case CHAR:
+ return loadChar(dataReference);
+ case INTEGER:
+ return loadInt(dataReference);
+ case LONG:
+ return loadLong(dataReference);
+ default:
+ throw new NullPointerException("Unknown data type");
+ }
+ }
+
+ void setData(long reference, DBDataType propertyType, T loadedPropertyValue) throws IOException {
+ switch (propertyType) {
+ case BOOLEAN:
+ setBoolean(reference, loadedPropertyValue != null && (boolean) loadedPropertyValue);
+ break;
+ case BYTE:
+ setByte(reference, loadedPropertyValue == null ? 0 : (byte) loadedPropertyValue);
+ break;
+ case SHORT:
+ setShort(reference, loadedPropertyValue == null ? 0 : (short) loadedPropertyValue);
+ break;
+ case CHAR:
+ setChar(reference, loadedPropertyValue == null ? 0 : (char) loadedPropertyValue);
+ break;
+ case INTEGER:
+ setInt(reference, loadedPropertyValue == null ? 0 : (int) loadedPropertyValue);
+ break;
+ case LONG:
+ setLong(reference, loadedPropertyValue == null ? 0 : (long) loadedPropertyValue);
+ break;
+ case OBJECT:
+ setObject(reference, loadedPropertyValue);
+ break;
+ case REFERENCES_LIST:
+ setReferencesList(reference, (LongArrayList) loadedPropertyValue);
+ break;
+ case ENHANCED_OBJECT:
+ setEnhancedObject(reference, (EnhancedObject) loadedPropertyValue);
+ break;
+ }
+ }
+
+ @Override
+ public void setEnhancedObject(long reference, T value) throws IOException {
+ synchronized (accessLock) {
+ if (value != null) {
+ EnhancedObjectFullInfo objectFullInfo = value.getAllInfo();
+ if (objectFullInfo == null || objectFullInfo.getFieldReferences() == null || objectFullInfo.getPropertyReferences() == null) {
+ throw new NullPointerException("An EnhancedObject has been initialized using the empty constructor!");
+ }
+ int totalSize = Byte.BYTES + Integer.BYTES * 2 + objectFullInfo.getFieldReferences().length * Long.BYTES + objectFullInfo.getPropertyReferences().length * Long.BYTES;
+ ByteBuffer buffer = ByteBuffer.allocate(totalSize);
+ buffer.put((byte) objectFullInfo.getVersion());
+ buffer.putInt(objectFullInfo.getFieldReferences().length);
+ buffer.putInt(objectFullInfo.getPropertyReferences().length);
+ for (int i = 0; i < objectFullInfo.getFieldReferences().length; i++) {
+ buffer.putLong(objectFullInfo.getFieldReferences()[i]);
+ }
+ for (int i = 0; i < objectFullInfo.getPropertyReferences().length; i++) {
+ buffer.putLong(objectFullInfo.getPropertyReferences()[i]);
+ }
+ long[] fieldReferences = objectFullInfo.getFieldReferences();
+ DBDataType[] fieldTypes = objectFullInfo.getFieldTypes();
+ Field[] fields = objectFullInfo.getFields();
+ long[] propertyReferences = objectFullInfo.getPropertyReferences();
+ DBDataType[] propertyTypes = objectFullInfo.getPropertyTypes();
+ Object[] propertyValues = objectFullInfo.getLoadedPropertyValues();
+ for (int i = 0; i < objectFullInfo.getFieldReferences().length; i++) {
+ if (fields[i] != null) {
+ try {
+ setData(fieldReferences[i], fieldTypes[i], fields[i].get(value));
+ } catch (IllegalAccessException e) {
+ throw new IOException(e);
+ }
+ }
+ }
+ for (int i = 0; i < objectFullInfo.getPropertyReferences().length; i++) {
+ if (propertyValues[i] != null) {
+ setData(propertyReferences[i], propertyTypes[i], propertyValues[i]);
+ }
+ }
+ buffer.flip();
+ referencesIO.writeToReference(reference, totalSize, buffer);
+ } else {
+ referencesIO.writeToReference(reference, 0, null);
+ }
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public T loadObject(long reference) throws IOException {
+ synchronized (accessLock) {
+ ByteBuffer buffer = referencesIO.readFromReference(reference);
+ if (buffer.limit() == 0) {
+ return null;
+ }
+ buffer.rewind();
+ byte[] data = buffer.array();
+ return (T) kryo.readClassAndObject(new Input(data));
+ }
+ }
+
+ @Override
+ public LongArrayList loadReferencesList(long reference) throws IOException {
+ synchronized (accessLock) {
+ ByteBuffer buffer = referencesIO.readFromReference(reference);
+ if (buffer.limit() == 0) {
+ return null;
+ }
+ int itemsCount = buffer.getInt();
+ LongArrayList arrayList = new LongArrayList();
+ for (int i = 0; i < itemsCount; i++) {
+ arrayList.add(buffer.getLong());
+ }
+ return arrayList;
+ }
+ }
+
+ @Override
+ public boolean loadBoolean(long reference) throws IOException {
+ synchronized (accessLock) {
+ ByteBuffer buffer = referencesIO.readFromReference(reference);
+ if (buffer.limit() == 0) {
+ return false;
+ }
+ return buffer.get() == 1;
+ }
+ }
+
+ @Override
+ public byte loadByte(long reference) throws IOException {
+ synchronized (accessLock) {
+ ByteBuffer buffer = referencesIO.readFromReference(reference);
+ if (buffer.limit() == 0) {
+ return 0;
+ }
+ return buffer.get();
+ }
+ }
+
+ @Override
+ public short loadShort(long reference) throws IOException {
+ synchronized (accessLock) {
+ ByteBuffer buffer = referencesIO.readFromReference(reference);
+ if (buffer.limit() == 0) {
+ return 0;
+ }
+ return buffer.getShort();
+ }
+ }
+
+ @Override
+ public char loadChar(long reference) throws IOException {
+ synchronized (accessLock) {
+ ByteBuffer buffer = referencesIO.readFromReference(reference);
+ if (buffer.limit() == 0) {
+ return 0;
+ }
+ return buffer.getChar();
+ }
+ }
+
+ @Override
+ public int loadInt(long reference) throws IOException {
+ synchronized (accessLock) {
+ ByteBuffer buffer = referencesIO.readFromReference(reference);
+ if (buffer.limit() == 0) {
+ return 0;
+ }
+ return buffer.getInt();
+ }
+ }
+
+ @Override
+ public long loadLong(long reference) throws IOException {
+ synchronized (accessLock) {
+ ByteBuffer buffer = referencesIO.readFromReference(reference);
+ if (buffer.limit() == 0) {
+ return 0;
+ }
+ return buffer.getLong();
+ }
+ }
+
+ @Override
+ public void setObject(long reference, T value) throws IOException {
+ synchronized (accessLock) {
+ if (value != null) {
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ Output output = new Output(outputStream);
+ kryo.writeClassAndObject(output, value);
+ output.flush();
+ byte[] data = outputStream.toByteArray();
+ ByteBuffer dataByteBuffer = ByteBuffer.wrap(data);
+ referencesIO.writeToReference(reference, data.length, dataByteBuffer);
+ } else {
+ referencesIO.writeToReference(reference, 0, null);
+ }
+ }
+ }
+
+ @Override
+ public void setReferencesList(long reference, LongArrayList value) throws IOException {
+ synchronized (accessLock) {
+ if (value != null) {
+ int items = value.size();
+ ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES * items + Integer.BYTES);
+ buffer.putInt(items);
+ for (int i = 0; i < items; i++) {
+ buffer.putLong(value.getLong(i));
+ }
+ buffer.flip();
+ referencesIO.writeToReference(reference, buffer.limit(), buffer);
+ } else {
+ referencesIO.writeToReference(reference, 0, null);
+ }
+ }
+ }
+
+ @Override
+ public void setBoolean(long reference, boolean value) throws IOException {
+ synchronized (accessLock) {
+ ByteBuffer buffer = ByteBuffer.allocate(Byte.BYTES);
+ buffer.put(value ? (byte) 1 : (byte) 0);
+ buffer.flip();
+ referencesIO.writeToReference(reference, Byte.BYTES, buffer);
+ }
+ }
+
+ @Override
+ public void setByte(long reference, byte value) throws IOException {
+ synchronized (accessLock) {
+ ByteBuffer buffer = ByteBuffer.allocate(Byte.BYTES);
+ buffer.put(value);
+ buffer.flip();
+ referencesIO.writeToReference(reference, Byte.BYTES, buffer);
+ }
+ }
+
+ @Override
+ public void setShort(long reference, short value) throws IOException {
+ synchronized (accessLock) {
+ ByteBuffer buffer = ByteBuffer.allocate(Short.BYTES);
+ buffer.putShort(value);
+ buffer.flip();
+ referencesIO.writeToReference(reference, Short.BYTES, buffer);
+ }
+ }
+
+ @Override
+ public void setChar(long reference, char value) throws IOException {
+ synchronized (accessLock) {
+ ByteBuffer buffer = ByteBuffer.allocate(Character.BYTES);
+ buffer.putChar(value);
+ buffer.flip();
+ referencesIO.writeToReference(reference, Character.BYTES, buffer);
+ }
+ }
+
+ @Override
+ public void setInt(long reference, int value) throws IOException {
+ synchronized (accessLock) {
+ ByteBuffer buffer = ByteBuffer.allocate(Integer.BYTES);
+ buffer.putInt(value);
+ buffer.flip();
+ referencesIO.writeToReference(reference, Integer.BYTES, buffer);
+ }
+ }
+
+ @Override
+ public void setLong(long reference, long value) throws IOException {
+ synchronized (accessLock) {
+ ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES);
+ buffer.putLong(value);
+ buffer.flip();
+ referencesIO.writeToReference(reference, Long.BYTES, buffer);
+ }
+ }
+
+ @Override
+ public long newNullObject() throws IOException {
+ synchronized (accessLock) {
+ return referencesIO.allocateReference();
+ }
+ }
+
+ @Override
+ public void loadProperty(EnhancedObject obj, int propertyId, Method property, DBDataType propertyType, long propertyUID) throws IOException {
+ synchronized (accessLock) {
+ obj.setProperty(propertyId, loadData(propertyType, propertyUID, property::getReturnType));
+ }
+ }
+
+ @Override
+ public void registerClass(Class> type, int id) {
+ if (id < -100) {
+ throw new IllegalArgumentException();
+ }
+ kryo.register(type, 100 + id);
+ }
+
+ private void preloadEnhancedObjectProperties(T obj, long[] propertyReferences) {
+ // Declare the variables needed to get the biggest property Id
+ Method[] unorderedPropertyGetters = obj.getPropertyGetters();
+ Method[] unorderedPropertySetters = obj.getPropertySetters();
+
+ // Find the biggest property Id
+ int biggestGetter = getBiggestPropertyGetterId(unorderedPropertyGetters);
+ int biggestSetter = getBiggestPropertySetterId(unorderedPropertySetters);
+ int biggestPropertyId = biggestGetter > biggestSetter ? biggestGetter : biggestSetter;
+
+ for (Method property : unorderedPropertySetters) {
+ DBPropertySetter fieldAnnotation = property.getAnnotation(DBPropertySetter.class);
+ int propertyId = fieldAnnotation.id();
+ if (propertyId > biggestPropertyId) {
+ biggestPropertyId = propertyId;
+ }
+ }
+
+ // Declare the other variables
+ DBDataType[] propertyTypes = new DBDataType[biggestPropertyId + 1];
+ Method[] propertyGetters = new Method[biggestPropertyId + 1];
+ Method[] propertySetters = new Method[biggestPropertyId + 1];
+ Map setterMethods = new LinkedHashMap<>();
+ Map getterMethods = new LinkedHashMap<>();
+
+ // Load the properties metadata
+ for (Method property : unorderedPropertyGetters) {
+ DBPropertyGetter propertyAnnotation = property.getAnnotation(DBPropertyGetter.class);
+ int propertyId = propertyAnnotation.id();
+ DBDataType propertyType = propertyAnnotation.type();
+ propertyTypes[propertyId] = propertyType;
+ propertyGetters[propertyId] = property;
+ getterMethods.put(property.getName(), propertyAnnotation);
+ }
+ for (Method property : unorderedPropertySetters) {
+ DBPropertySetter propertyAnnotation = property.getAnnotation(DBPropertySetter.class);
+ int propertyId = propertyAnnotation.id();
+ DBDataType propertyType = propertyAnnotation.type();
+ propertyTypes[propertyId] = propertyType;
+ propertySetters[propertyId] = property;
+ setterMethods.put(property.getName(), propertyAnnotation);
+ }
+ // Set properties metadata
+ obj.setProperties(propertyGetters, propertySetters, propertyTypes, propertyReferences, setterMethods, getterMethods);
+ }
+
+ int getBiggestPropertyGetterId(Method[] unorderedPropertyGetters) {
+ int biggestPropertyId = -1;
+ for (Method property : unorderedPropertyGetters) {
+ DBPropertyGetter fieldAnnotation = property.getAnnotation(DBPropertyGetter.class);
+ int propertyId = fieldAnnotation.id();
+ if (propertyId > biggestPropertyId) {
+ biggestPropertyId = propertyId;
+ }
+ }
+ return biggestPropertyId;
+ }
+
+ int getBiggestPropertySetterId(Method[] unorderedPropertySetters) {
+ int biggestPropertyId = -1;
+ for (Method property : unorderedPropertySetters) {
+ DBPropertySetter fieldAnnotation = property.getAnnotation(DBPropertySetter.class);
+ int propertyId = fieldAnnotation.id();
+ if (propertyId > biggestPropertyId) {
+ biggestPropertyId = propertyId;
+ }
+ }
+ return biggestPropertyId;
+ }
+
+ private void preloadEnhancedObjectFields(T obj, long[] fieldReferences) throws IOException {
+ // Declare the variables needed to get the biggest field Id
+ Field[] unorderedFields = getFields(obj);
+ // Find the biggest field Id
+ int biggestFieldId = getBiggestFieldId(unorderedFields);
+
+ // Declare the other variables
+ Field[] fields = new Field[biggestFieldId + 1];
+ DBDataType[] orderedFieldTypes = new DBDataType[biggestFieldId + 1];
+
+ // Load all fields metadata and load them
+ for (Field field : unorderedFields) {
+ DBField fieldAnnotation = field.getAnnotation(DBField.class);
+ int fieldId = fieldAnnotation.id();
+ DBDataType fieldType = fieldAnnotation.type();
+ loadField(obj, field, fieldType, fieldReferences[fieldId]);
+ fields[fieldId] = field;
+ orderedFieldTypes[fieldId] = fieldType;
+ }
+ // Set fields metadata
+ obj.setFields(fields, orderedFieldTypes, fieldReferences);
+ }
+
+ void loadField(T obj, Field field, DBDataType fieldType, long fieldReference) throws IOException {
+ Object data = loadData(fieldType, fieldReference, field::getType);
+ try {
+ if (fieldType == DBDataType.OBJECT && data != null) {
+ if (!field.getType().isInstance(data)) {
+ throw new IOException("There is an attempt to load an object of type " + data.getClass() + " into a field of type " + field.getType());
+ }
+ }
+ FieldUtils.writeField(field, obj, data, true);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ Field[] getFields(T obj) {
+ return FieldUtils.getFieldsWithAnnotation(obj.getClass(), DBField.class);
+ }
+
+ int getBiggestFieldId(Field[] unorderedFields) {
+ int biggestFieldId = -1;
+ for (Field field : unorderedFields) {
+ DBField fieldAnnotation = field.getAnnotation(DBField.class);
+ int propertyId = fieldAnnotation.id();
+ if (propertyId > biggestFieldId) {
+ biggestFieldId = propertyId;
+ }
+ }
+ return biggestFieldId;
+ }
+
+ private T toInstance(Class type) throws IOException {
+ try {
+ T obj = type.getConstructor().newInstance();
+ obj.setDatabaseTools(databaseTools);
+ return obj;
+ } catch (NoSuchMethodException e) {
+ throw new IOException("You must declare a public empty constructor in class " + type + ": public " + type.getSimpleName() + "()", e);
+ } catch (IllegalAccessException | InvocationTargetException | InstantiationException e) {
+ throw new IOException(e);
+ }
+ }
+
+ private T preloadEnhancedObject(Class objectType, int serializedVersion, long[] fieldRefs, long[] methodRefs) throws IOException {
+ // Instantiate the class to an object
+ T obj = toInstance(objectType);
+
+ // Check the serialized version
+ DBClass dbClass = objectType.getAnnotation(DBClass.class);
+ int classVersion = 0;
+ if (dbClass != null) {
+ classVersion = dbClass.version();
+ }
+ if (classVersion > serializedVersion) {
+ DatabaseEnhancedObjectUpgrader enhancedObjectUpgrader = new DatabaseEnhancedObjectUpgrader(this, fieldRefs, methodRefs);
+ dataInitializer.initializeDBObject(obj);
+ obj.onUpgrade(serializedVersion, enhancedObjectUpgrader);
+ } else if (classVersion < serializedVersion) {
+ throw new IllegalStateException("The serialized class is more recent than the current version of that class!");
+ } else {
+ preloadEnhancedObjectFields(obj, fieldRefs);
+ preloadEnhancedObjectProperties(obj, methodRefs);
+ }
+ return obj;
+ }
+
+ public long[] allocateNewUIDs(int quantity) throws IOException {
+ long[] ids = new long[quantity];
+ for (int i = 0; i < quantity; i++) {
+ ids[i] = newNullObject();
+ }
+ return ids;
+ }
+
+ @Override
+ public IDataInitializer getDataInitializer() {
+ return dataInitializer;
+ }
+}
diff --git a/src/main/java/org/warp/cowdb/database/DatabaseReferencesIO.java b/src/main/java/org/warp/cowdb/database/DatabaseReferencesIO.java
new file mode 100644
index 0000000..24cbd77
--- /dev/null
+++ b/src/main/java/org/warp/cowdb/database/DatabaseReferencesIO.java
@@ -0,0 +1,42 @@
+package org.warp.cowdb.database;
+
+import org.warp.cowdb.IReferencesIO;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+import static org.warp.cowdb.IBlocksMetadata.EMPTY_BLOCK_ID;
+
+public class DatabaseReferencesIO implements IReferencesIO {
+
+ private final DatabaseBlocksIO blocksIO;
+ private final DatabaseReferencesMetadata referencesMetadata;
+
+ public DatabaseReferencesIO(DatabaseBlocksIO blocksIO, DatabaseReferencesMetadata referencesMetadata) {
+ this.blocksIO = blocksIO;
+ this.referencesMetadata = referencesMetadata;
+ }
+
+ @Override
+ public long allocateReference() throws IOException {
+ return referencesMetadata.newReference(EMPTY_BLOCK_ID);
+ }
+
+ @Override
+ public long allocateReference(int size, ByteBuffer data) throws IOException {
+ long blockId = (size == 0) ? EMPTY_BLOCK_ID : blocksIO.newBlock(size, data);
+ return referencesMetadata.newReference(blockId);
+ }
+
+ @Override
+ public void writeToReference(long reference, int size, ByteBuffer data) throws IOException {
+ long blockId = (size == 0) ? EMPTY_BLOCK_ID : blocksIO.newBlock(size, data);
+ referencesMetadata.editReference(reference, blockId);
+ }
+
+ @Override
+ public ByteBuffer readFromReference(long reference) throws IOException {
+ long blockId = referencesMetadata.getReference(reference);
+ return blocksIO.readBlock(blockId);
+ }
+}
diff --git a/src/main/java/org/warp/cowdb/database/DatabaseReferencesMetadata.java b/src/main/java/org/warp/cowdb/database/DatabaseReferencesMetadata.java
new file mode 100644
index 0000000..09ee1d1
--- /dev/null
+++ b/src/main/java/org/warp/cowdb/database/DatabaseReferencesMetadata.java
@@ -0,0 +1,66 @@
+package org.warp.cowdb.database;
+
+import org.warp.cowdb.IReferencesMetadata;
+import org.warp.jcwdb.ann.DBClass;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.SeekableByteChannel;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+
+import static org.warp.cowdb.IBlocksMetadata.EMPTY_BLOCK_ID;
+
+public class DatabaseReferencesMetadata implements IReferencesMetadata {
+ private final SeekableByteChannel metaFileChannel;
+ private final int REF_META_BYTES_COUNT = Long.BYTES;
+ private long firstFreeReference;
+
+ public DatabaseReferencesMetadata(Path refMetaFile) throws IOException {
+ metaFileChannel = Files.newByteChannel(refMetaFile, StandardOpenOption.READ, StandardOpenOption.WRITE);
+ firstFreeReference = metaFileChannel.size() / REF_META_BYTES_COUNT;
+ }
+
+ @Override
+ public long getReference(long reference) throws IOException {
+ ByteBuffer buffer = ByteBuffer.allocate(REF_META_BYTES_COUNT);
+ if (reference >= firstFreeReference) {
+ return EMPTY_BLOCK_ID;
+ }
+ SeekableByteChannel currentFileChannel = metaFileChannel.position(reference * REF_META_BYTES_COUNT);
+ currentFileChannel.read(buffer);
+ buffer.flip();
+ long block = buffer.getLong();
+ if (buffer.limit() == 0 || block == 0xFFFFFFFFFFFFFFFFL) {
+ return EMPTY_BLOCK_ID;
+ }
+ return block;
+ }
+
+ @Override
+ public long newReference(long blockId) throws IOException {
+ long newReference = firstFreeReference++;
+ editReference(newReference, blockId);
+ return newReference;
+ }
+
+ @Override
+ public void editReference(long reference, long blockId) throws IOException {
+ ByteBuffer data = ByteBuffer.allocate(REF_META_BYTES_COUNT);
+ data.putLong(blockId);
+ SeekableByteChannel currentFileChannel = metaFileChannel.position(reference * REF_META_BYTES_COUNT);
+ data.flip();
+ currentFileChannel.write(data);
+ }
+
+ @Override
+ public void close() throws IOException {
+ metaFileChannel.close();
+ }
+
+ @Override
+ public long getFirstFreeReference() {
+ return firstFreeReference;
+ }
+}
diff --git a/src/main/java/org/warp/cowdb/EnhancedObjectFullInfo.java b/src/main/java/org/warp/cowdb/database/EnhancedObjectFullInfo.java
similarity index 83%
rename from src/main/java/org/warp/cowdb/EnhancedObjectFullInfo.java
rename to src/main/java/org/warp/cowdb/database/EnhancedObjectFullInfo.java
index 6cac504..fc2aa67 100644
--- a/src/main/java/org/warp/cowdb/EnhancedObjectFullInfo.java
+++ b/src/main/java/org/warp/cowdb/database/EnhancedObjectFullInfo.java
@@ -1,4 +1,4 @@
-package org.warp.cowdb;
+package org.warp.cowdb.database;
import org.warp.jcwdb.ann.DBDataType;
@@ -13,7 +13,7 @@ public class EnhancedObjectFullInfo {
private final DBDataType[] propertyTypes;
private final Object[] loadedPropertyValues;
- EnhancedObjectFullInfo(int version, long[] fieldReferences, DBDataType[] fieldTypes, Field[] fields, long[] propertyReferences, DBDataType[] propertyTypes, Object[] loadedPropertyValues) {
+ public EnhancedObjectFullInfo(int version, long[] fieldReferences, DBDataType[] fieldTypes, Field[] fields, long[] propertyReferences, DBDataType[] propertyTypes, Object[] loadedPropertyValues) {
this.version = version;
if (version > 255) {
throw new IllegalArgumentException();
diff --git a/src/main/java/org/warp/cowdb/lists/CowList.java b/src/main/java/org/warp/cowdb/lists/CowList.java
index b5a3d64..9cf6bc3 100644
--- a/src/main/java/org/warp/cowdb/lists/CowList.java
+++ b/src/main/java/org/warp/cowdb/lists/CowList.java
@@ -3,8 +3,7 @@ package org.warp.cowdb.lists;
import it.unimi.dsi.fastutil.longs.LongArrayList;
import org.warp.cowdb.EnhancedObject;
import org.warp.cowdb.IDatabase;
-import org.warp.jcwdb.ann.DBDataType;
-import org.warp.jcwdb.ann.DBField;
+import org.warp.cowdb.IDatabaseTools;
import java.io.IOException;
import java.util.StringJoiner;
@@ -19,8 +18,8 @@ public abstract class CowList extends EnhancedObject {
}
- public CowList(IDatabase database) throws IOException {
- super(database);
+ public CowList(IDatabaseTools databaseTools) throws IOException {
+ super(databaseTools);
}
public T get(int index) throws IOException {
@@ -31,7 +30,7 @@ public abstract class CowList extends EnhancedObject {
}
public void add(T value) throws IOException {
- long uid = database.getObjectsIO().newNullObject();
+ long uid = databaseTools.getObjectsIO().newNullObject();
synchronized (indicesAccessLock) {
getIndices().add(uid);
writeItemToDisk(uid, value);
@@ -46,7 +45,7 @@ public abstract class CowList extends EnhancedObject {
}
public void set(int index, T value) throws IOException {
- long uid = database.getObjectsIO().newNullObject();
+ long uid = databaseTools.getObjectsIO().newNullObject();
synchronized (indicesAccessLock) {
getIndices().set(index, uid);
writeItemToDisk(uid, value);
@@ -54,7 +53,7 @@ public abstract class CowList extends EnhancedObject {
}
public void add(int index, T value) throws IOException {
- long uid = database.getObjectsIO().newNullObject();
+ long uid = databaseTools.getObjectsIO().newNullObject();
synchronized (indicesAccessLock) {
getIndices().add(index, uid);
writeItemToDisk(uid, value);
diff --git a/src/main/java/org/warp/cowdb/lists/EnhancedObjectCowList.java b/src/main/java/org/warp/cowdb/lists/EnhancedObjectCowList.java
index 9fb879c..8fc4fa5 100644
--- a/src/main/java/org/warp/cowdb/lists/EnhancedObjectCowList.java
+++ b/src/main/java/org/warp/cowdb/lists/EnhancedObjectCowList.java
@@ -3,6 +3,7 @@ package org.warp.cowdb.lists;
import it.unimi.dsi.fastutil.longs.LongArrayList;
import org.warp.cowdb.EnhancedObject;
import org.warp.cowdb.IDatabase;
+import org.warp.cowdb.IDatabaseTools;
import org.warp.jcwdb.ann.DBDataType;
import org.warp.jcwdb.ann.DBField;
@@ -25,19 +26,19 @@ public class EnhancedObjectCowList extends CowList
super();
}
- public EnhancedObjectCowList(IDatabase database, Class type) throws IOException {
- super(database);
+ public EnhancedObjectCowList(IDatabaseTools databaseTools, Class type) throws IOException {
+ super(databaseTools);
this.type = type;
indices = new LongArrayList();
}
@Override
protected T loadItem(long uid) throws IOException {
- return database.getObjectsIO().loadEnhancedObject(uid, type);
+ return databaseTools.getObjectsIO().loadEnhancedObject(uid, type);
}
@Override
protected void writeItemToDisk(long uid, T item) throws IOException {
- database.getObjectsIO().setEnhancedObject(uid, item);
+ databaseTools.getObjectsIO().setEnhancedObject(uid, item);
}
}
diff --git a/src/main/java/org/warp/cowdb/lists/ObjectCowList.java b/src/main/java/org/warp/cowdb/lists/ObjectCowList.java
index ebdc58e..697b0ed 100644
--- a/src/main/java/org/warp/cowdb/lists/ObjectCowList.java
+++ b/src/main/java/org/warp/cowdb/lists/ObjectCowList.java
@@ -1,8 +1,8 @@
package org.warp.cowdb.lists;
import it.unimi.dsi.fastutil.longs.LongArrayList;
-import org.warp.cowdb.EnhancedObject;
import org.warp.cowdb.IDatabase;
+import org.warp.cowdb.IDatabaseTools;
import org.warp.jcwdb.ann.DBDataType;
import org.warp.jcwdb.ann.DBField;
@@ -22,18 +22,18 @@ public class ObjectCowList extends CowList {
super();
}
- public ObjectCowList(IDatabase database) throws IOException {
- super(database);
+ public ObjectCowList(IDatabaseTools databaseTools) throws IOException {
+ super(databaseTools);
indices = new LongArrayList();
}
@Override
protected T loadItem(long uid) throws IOException {
- return database.getObjectsIO().loadObject(uid);
+ return databaseTools.getObjectsIO().loadObject(uid);
}
@Override
protected void writeItemToDisk(long uid, T item) throws IOException {
- database.getObjectsIO().setObject(uid, item);
+ databaseTools.getObjectsIO().setObject(uid, item);
}
}
diff --git a/src/test/java/org/warp/jcwdb/tests/EnhancedClassUpdate.java b/src/test/java/org/warp/jcwdb/tests/EnhancedClassUpdate.java
index 2b2c344..42b988e 100644
--- a/src/test/java/org/warp/jcwdb/tests/EnhancedClassUpdate.java
+++ b/src/test/java/org/warp/jcwdb/tests/EnhancedClassUpdate.java
@@ -32,7 +32,7 @@ public class EnhancedClassUpdate {
path2 = Files.createTempFile("db-tests-", ".db");
path3 = Files.createTempFile("db-tests-", ".db");
db = new Database(path1, path2, path3);
- OldClass root = db.loadRoot(OldClass.class);
+ OldClass root = db.loadRoot(OldClass.class, OldClass::new);
root.field1 = "Abc";
root.field2 = 12;
root.field4 = 13;
@@ -42,7 +42,7 @@ public class EnhancedClassUpdate {
@Test
public void shouldUpdateClass() throws IOException {
db = new Database(path1, path2, path3);
- V2Class root = db.loadRoot(V2Class.class);
+ V2Class root = db.loadRoot(V2Class.class, V2Class::new);
assertEquals(root.field4, "Abc");
assertEquals(root.field2, 12);
assertEquals(root.field1, 13L);
diff --git a/src/test/java/org/warp/jcwdb/tests/MultipleEnhancedObjects.java b/src/test/java/org/warp/jcwdb/tests/MultipleEnhancedObjects.java
index 438132e..7fe7b50 100644
--- a/src/test/java/org/warp/jcwdb/tests/MultipleEnhancedObjects.java
+++ b/src/test/java/org/warp/jcwdb/tests/MultipleEnhancedObjects.java
@@ -5,6 +5,7 @@ import org.junit.Before;
import org.junit.Test;
import org.warp.cowdb.EnhancedObject;
import org.warp.cowdb.IDatabase;
+import org.warp.cowdb.IDatabaseTools;
import org.warp.jcwdb.ann.DBDataType;
import org.warp.jcwdb.ann.DBField;
import org.warp.jcwdb.ann.DBPropertyGetter;
@@ -20,7 +21,7 @@ public class MultipleEnhancedObjects {
@Before
public void setUp() throws Exception {
db = NTestUtils.wrapDb().create((db) -> {
- root = db.get().loadRoot(RootTwoClasses.class);
+ root = db.get().loadRoot(RootTwoClasses.class, RootTwoClasses::new);
});
root.class1 = new NTestUtils.RootClass(db.get());
db.setRootClassValues(root.class1);
@@ -58,8 +59,8 @@ public class MultipleEnhancedObjects {
super();
}
- public RootTwoClasses(IDatabase database) throws IOException {
- super(database);
+ public RootTwoClasses(IDatabaseTools databaseTools) throws IOException {
+ super(databaseTools);
}
@DBPropertyGetter(id = 0, type = DBDataType.ENHANCED_OBJECT)
diff --git a/src/test/java/org/warp/jcwdb/tests/OldClass.java b/src/test/java/org/warp/jcwdb/tests/OldClass.java
index 1254909..2f7d943 100644
--- a/src/test/java/org/warp/jcwdb/tests/OldClass.java
+++ b/src/test/java/org/warp/jcwdb/tests/OldClass.java
@@ -1,6 +1,8 @@
package org.warp.jcwdb.tests;
import org.warp.cowdb.EnhancedObject;
+import org.warp.cowdb.IDatabase;
+import org.warp.cowdb.IDatabaseTools;
import org.warp.jcwdb.ann.DBClass;
import org.warp.jcwdb.ann.DBDataType;
import org.warp.jcwdb.ann.DBField;
@@ -8,6 +10,7 @@ import org.warp.jcwdb.ann.DBField;
import java.io.IOException;
public class OldClass extends EnhancedObject {
+
@DBField(id = 0, type = DBDataType.OBJECT)
public String field1;
@@ -16,4 +19,12 @@ public class OldClass extends EnhancedObject {
@DBField(id = 3, type = DBDataType.INTEGER)
public int field4;
+
+ public OldClass() {
+
+ }
+
+ public OldClass(IDatabaseTools databaseTools) throws IOException {
+ super(databaseTools);
+ }
}
diff --git a/src/test/java/org/warp/jcwdb/tests/Performance.java b/src/test/java/org/warp/jcwdb/tests/Performance.java
new file mode 100644
index 0000000..a2aa29c
--- /dev/null
+++ b/src/test/java/org/warp/jcwdb/tests/Performance.java
@@ -0,0 +1,333 @@
+package org.warp.jcwdb.tests;
+
+import it.unimi.dsi.fastutil.longs.LongArrayList;
+import org.warp.cowdb.Database;
+import org.warp.cowdb.EnhancedObject;
+import org.warp.cowdb.IDatabaseTools;
+import org.warp.cowdb.lists.EnhancedObjectCowList;
+import org.warp.cowdb.lists.ObjectCowList;
+import org.warp.jcwdb.VariableWrapper;
+import org.warp.jcwdb.ann.*;
+
+import java.io.IOException;
+import java.lang.reflect.Array;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Vector;
+
+public class Performance {
+ private static boolean FAST_TESTS;
+
+ private static Path rootDirectory;
+ private static Path dbDataFile;
+ private static Path dbBlocksFile;
+ private static Path dbReferencesFile;
+ private static Database db;
+
+ /**
+ *
+ * @param args args[0] = true for fast tests
+ * @throws IOException
+ * @throws InterruptedException
+ */
+ public static void main(String[] args) throws IOException, InterruptedException {
+ FAST_TESTS = args.length > 0 && args[0].equalsIgnoreCase("true");
+ rootDirectory = Files.createTempDirectory("performance-tests");
+ generateDb();
+ System.out.println("Performance test started.");
+ System.out.println("-------------------------------------------------------+-----------------------------------------------------------------");
+ System.out.println("Test name Total Time | Time at 1 Time at 10 Time at 100 Time at 1K Time at 10K");
+ System.out.println("-------------------------------------------------------+-----------------------------------------------------------------");
+ testS("Database creation", 3000, Performance::deleteDb, Performance::generateDb, () -> {});
+ testS("Database root creation", 3000, Performance::regenDb, () -> db.loadRoot(PreloadedListContainer.class, PreloadedListContainer::new), () -> {});
+ final VariableWrapper preloadedListContainer = new VariableWrapper<>(null);
+ final VariableWrapper simpleEnhancedObjectContainer = new VariableWrapper<>(null);
+ testS("ObjectCowList creation", 3000, () -> {
+ regenDb();
+ preloadedListContainer.var = db.loadRoot(PreloadedListContainer.class, PreloadedListContainer::new);
+ }, () -> preloadedListContainer.var.list = new ObjectCowList<>(db), () -> {});
+ testS("ObjectCowList: Filling with 1000 items", 100, () -> {
+ regenDb();
+ preloadedListContainer.var = db.loadRoot(PreloadedListContainer.class, PreloadedListContainer::new);
+ preloadedListContainer.var.list = new ObjectCowList<>(db);
+ }, () -> {
+ for (int i = 0; i < 1000; i++) {
+ preloadedListContainer.var.list.add(1000);
+ }
+ }, () -> {});
+ testS("ObjectCowList: Filling with 1000 items", 100, () -> {
+ regenDb();
+ preloadedListContainer.var = db.loadRoot(PreloadedListContainer.class, PreloadedListContainer::new);
+ preloadedListContainer.var.listOfEnhancedObj = new EnhancedObjectCowList<>(db, SimpleEnhancedObject.class);
+ simpleEnhancedObjectContainer.var = new SimpleEnhancedObject(db);
+ simpleEnhancedObjectContainer.var.integerNumber = 10;
+ simpleEnhancedObjectContainer.var.longNumber = 10L;
+ simpleEnhancedObjectContainer.var.object = new ArrayList<>();
+ simpleEnhancedObjectContainer.var.object.add("XHIghicuiHUCB UIVY");
+ simpleEnhancedObjectContainer.var.object.add("ioZ>UIHZXGHXYGY");
+ simpleEnhancedObjectContainer.var.object.add("XJIOUIhcgGuigscwvyv");
+ }, () -> {
+ for (int i = 0; i < 1000; i++) {
+ preloadedListContainer.var.listOfEnhancedObj.add(simpleEnhancedObjectContainer.var);
+ }
+ }, () -> {});
+ testS("ObjectCowList: Filling with 10000 items", 10, () -> {
+ regenDb();
+ preloadedListContainer.var = db.loadRoot(PreloadedListContainer.class, PreloadedListContainer::new);
+ preloadedListContainer.var.list = new ObjectCowList<>(db);
+ }, () -> {
+ for (int i = 0; i < 10000; i++) {
+ preloadedListContainer.var.list.add(1000);
+ }
+ }, () -> {});
+ testS("ObjectCowList: Filling with 100000 items", 1, () -> {
+ regenDb();
+ preloadedListContainer.var = db.loadRoot(PreloadedListContainer.class, PreloadedListContainer::new);
+ preloadedListContainer.var.list = new ObjectCowList<>(db);
+ }, () -> {
+ for (int i = 0; i < 100000; i++) {
+ preloadedListContainer.var.list.add(1000);
+ }
+ }, () -> {});
+ testS("ObjectCowList: Loading 1000 items", 100, () -> {
+ regenDb();
+ preloadedListContainer.var = db.loadRoot(PreloadedListContainer.class, PreloadedListContainer::new);
+ preloadedListContainer.var.list = new ObjectCowList<>(db);
+ for (int i = 0; i < 1000; i++) {
+ preloadedListContainer.var.list.add(1000);
+ }
+ }, () -> {
+ for (int i = 0; i < 1000; i++) {
+ preloadedListContainer.var.list.get(i);
+ }
+ }, () -> {});
+ testS("ObjectCowList: Loading with 1000 items", 100, () -> {
+ regenDb();
+ preloadedListContainer.var = db.loadRoot(PreloadedListContainer.class, PreloadedListContainer::new);
+ preloadedListContainer.var.listOfEnhancedObj = new EnhancedObjectCowList<>(db, SimpleEnhancedObject.class);
+ simpleEnhancedObjectContainer.var = new SimpleEnhancedObject(db);
+ simpleEnhancedObjectContainer.var.integerNumber = 10;
+ simpleEnhancedObjectContainer.var.longNumber = 10L;
+ simpleEnhancedObjectContainer.var.object = new ArrayList<>();
+ simpleEnhancedObjectContainer.var.object.add("XHIghicuiHUCB UIVY");
+ simpleEnhancedObjectContainer.var.object.add("ioZ>UIHZXGHXYGY");
+ simpleEnhancedObjectContainer.var.object.add("XJIOUIhcgGuigscwvyv");
+ for (int i = 0; i < 1000; i++) {
+ preloadedListContainer.var.listOfEnhancedObj.add(simpleEnhancedObjectContainer.var);
+ }
+ }, () -> {
+ for (int i = 0; i < 1000; i++) {
+ preloadedListContainer.var.listOfEnhancedObj.get(i);
+ }
+ }, () -> {});
+ testS("ObjectCowList: Loading 10000 items", 10, () -> {
+ regenDb();
+ preloadedListContainer.var = db.loadRoot(PreloadedListContainer.class, PreloadedListContainer::new);
+ preloadedListContainer.var.list = new ObjectCowList<>(db);
+ for (int i = 0; i < 10000; i++) {
+ preloadedListContainer.var.list.add(1000);
+ }
+ }, () -> {
+ for (int i = 0; i < 10000; i++) {
+ preloadedListContainer.var.list.get(i);
+ }
+ }, () -> {});
+ testS("ObjectCowList: getLast() with 1000 items", 100, () -> {
+ regenDb();
+ preloadedListContainer.var = db.loadRoot(PreloadedListContainer.class, PreloadedListContainer::new);
+ preloadedListContainer.var.list = new ObjectCowList<>(db);
+ for (int i = 0; i < 1000; i++) {
+ preloadedListContainer.var.list.add(1000);
+ }
+ }, () -> {
+ preloadedListContainer.var.list.getLast();
+ }, () -> {});
+ testS("ObjectCowList: getLast() with 1000 items", 100, () -> {
+ regenDb();
+ preloadedListContainer.var = db.loadRoot(PreloadedListContainer.class, PreloadedListContainer::new);
+ preloadedListContainer.var.listOfEnhancedObj = new EnhancedObjectCowList<>(db, SimpleEnhancedObject.class);
+ simpleEnhancedObjectContainer.var = new SimpleEnhancedObject(db);
+ simpleEnhancedObjectContainer.var.integerNumber = 10;
+ simpleEnhancedObjectContainer.var.longNumber = 10L;
+ simpleEnhancedObjectContainer.var.object = new ArrayList<>();
+ simpleEnhancedObjectContainer.var.object.add("XHIghicuiHUCB UIVY");
+ simpleEnhancedObjectContainer.var.object.add("ioZ>UIHZXGHXYGY");
+ simpleEnhancedObjectContainer.var.object.add("XJIOUIhcgGuigscwvyv");
+ for (int i = 0; i < 1000; i++) {
+ preloadedListContainer.var.listOfEnhancedObj.add(simpleEnhancedObjectContainer.var);
+ }
+ }, () -> {
+ preloadedListContainer.var.listOfEnhancedObj.getLast();
+ }, () -> {});
+ testS("ObjectCowList: size() with 1000 items", 100, () -> {
+ regenDb();
+ preloadedListContainer.var = db.loadRoot(PreloadedListContainer.class, PreloadedListContainer::new);
+ preloadedListContainer.var.list = new ObjectCowList<>(db);
+ for (int i = 0; i < 1000; i++) {
+ preloadedListContainer.var.list.add(1000);
+ }
+ }, () -> {
+ preloadedListContainer.var.list.size();
+ }, () -> {});
+ System.out.println("-------------------------------------------------------+-----------------------------------------------------------------");
+ System.out.println("Performance test finished.");
+ deleteDb();
+ Files.deleteIfExists(rootDirectory);
+ }
+
+ private static void NtestS(String description, int times, RunnableWithIO beforeAction, RunnableWithIO action, RunnableWithIO afterAction) throws IOException, InterruptedException {
+ }
+
+
+ private static void testS(String description, int times, RunnableWithIO beforeAction, RunnableWithIO action, RunnableWithIO afterAction) throws IOException, InterruptedException {
+ if (FAST_TESTS) {
+ if (times >= 5) {
+ times /= 5;
+ } else if (times >= 2) {
+ times /= 2;
+ }
+ }
+ description = description + " time is";
+ int spacesCount = 40 - description.length();
+ int cutAt = 0;
+ if (spacesCount < 0) {
+ spacesCount = 40 - (description.length() - 40);
+ cutAt = 40;
+ }
+ StringBuilder spaces = new StringBuilder();
+ for (int i = 0; i < spacesCount; i++) {
+ spaces.append(' ');
+ }
+ double[] results = test(times, beforeAction, action, afterAction);
+ if (cutAt > 0) {
+ System.out.println(description.substring(0, cutAt) + " |");
+ }
+ System.out.printf("%s:%s%s%s%n", description.substring(cutAt), spaces, format(results[0]) + " |", results.length > 1 ? (format(results[1]) + (results.length > 2 ? (format(results[2]) + (results.length > 3 ? (format(results[3]) + (results.length > 4 ? (format(results[4]) + (results.length > 5 ? format(results[5]) : "")) : "")) : "")) : "")) : "");
+ }
+
+ private static String format(double result) {
+ String spaces;
+ if (result < 10) {
+ spaces = " ";
+ } else if (result < 100) {
+ spaces = " ";
+ } else if (result < 1000) {
+ spaces = " ";
+ } else {
+ spaces = " ";
+ }
+ return spaces + String.format("%.2fms", result);
+ }
+
+ private static double[] test(int times, RunnableWithIO beforeAction, RunnableWithIO action, RunnableWithIO afterAction) throws IOException, InterruptedException {
+ LongArrayList results = new LongArrayList(times);
+ Thread.sleep(100);
+ System.gc();
+ Thread.sleep(100);
+ for (int i = 0; i < times; i++) {
+ beforeAction.run();
+ long startTime = System.nanoTime();
+ action.run();
+ long elapsedTime = System.nanoTime() - startTime;
+ afterAction.run();
+ results.add(elapsedTime);
+ }
+ double result1 = results.stream().limit(1).mapToLong(val -> val).average().orElse(0.0) / 1000000d;
+ double result10 = results.stream().limit(10).mapToLong(val -> val).average().orElse(0.0) / 1000000d;
+ double result100 = results.stream().limit(100).mapToLong(val -> val).average().orElse(0.0) / 1000000d;
+ double result1000 = results.stream().limit(1000).mapToLong(val -> val).average().orElse(0.0) / 1000000d;
+ double result10000 = results.stream().limit(10000).mapToLong(val -> val).average().orElse(0.0) / 1000000d;
+ double resultMax = results.stream().mapToLong(val -> val).average().orElse(0.0) / 1000000d;
+ if (times <= 1) {
+ return new double[]{resultMax};
+ } else if (times <= 10) {
+ return new double[]{resultMax, result1};
+ } else if (times <= 100) {
+ return new double[]{resultMax, result1, result10};
+ } else if (times <= 1000) {
+ return new double[]{resultMax, result1, result10, result100};
+ } else if (times <= 10000) {
+ return new double[]{resultMax, result1, result10, result100, result1000};
+ } else {
+ return new double[]{resultMax, result1, result10, result100, result1000, result10000};
+ }
+ }
+
+ public static void generateDb() throws IOException {
+ dbDataFile = Files.createFile(rootDirectory.resolve("db_data.dat"));
+ dbBlocksFile = Files.createFile(rootDirectory.resolve("db_blocks.dat"));
+ dbReferencesFile = Files.createFile(rootDirectory.resolve("db_references.dat"));
+ db = new Database(dbDataFile, dbBlocksFile, dbReferencesFile);
+ }
+
+ public static void deleteDb() throws IOException {
+ db.close();
+ Files.deleteIfExists(dbDataFile);
+ Files.deleteIfExists(dbBlocksFile);
+ Files.deleteIfExists(dbReferencesFile);
+ }
+
+ public static void regenDb() throws IOException {
+ deleteDb();
+ generateDb();
+ }
+
+ public static class PreloadedListContainer extends EnhancedObject {
+
+ @DBField(id = 0, type = DBDataType.ENHANCED_OBJECT)
+ public ObjectCowList list;
+
+ @DBField(id = 1, type = DBDataType.ENHANCED_OBJECT)
+ public EnhancedObjectCowList listOfEnhancedObj;
+
+ public PreloadedListContainer() {
+
+ }
+
+ public PreloadedListContainer(IDatabaseTools databaseTools) throws IOException {
+ super(databaseTools);
+ }
+ }
+
+ public static class DynamicListContainer extends EnhancedObject {
+
+ public DynamicListContainer() {
+
+ }
+
+ public DynamicListContainer(IDatabaseTools databaseTools) throws IOException {
+ super(databaseTools);
+ }
+
+
+ @DBPropertyGetter(id = 0, type = DBDataType.ENHANCED_OBJECT)
+ public ObjectCowList getList() {
+ return getProperty();
+ }
+
+ @DBPropertySetter(id = 1, type = DBDataType.ENHANCED_OBJECT)
+ public void setList(ObjectCowList list) {
+ setProperty(list);
+ }
+ }
+
+ public static class SimpleEnhancedObject extends EnhancedObject {
+ public SimpleEnhancedObject() {
+
+ }
+
+ public SimpleEnhancedObject(IDatabaseTools databaseTools) throws IOException {
+ super(databaseTools);
+ }
+
+ @DBField(id = 0, type = DBDataType.OBJECT)
+ public ArrayList object;
+
+ @DBField(id = 1, type = DBDataType.INTEGER)
+ public int integerNumber;
+
+ @DBField(id = 2, type = DBDataType.LONG)
+ public long longNumber;
+ }
+}
diff --git a/src/test/java/org/warp/jcwdb/tests/V2Class.java b/src/test/java/org/warp/jcwdb/tests/V2Class.java
index ce6ceb0..de51c79 100644
--- a/src/test/java/org/warp/jcwdb/tests/V2Class.java
+++ b/src/test/java/org/warp/jcwdb/tests/V2Class.java
@@ -2,6 +2,8 @@ package org.warp.jcwdb.tests;
import org.warp.cowdb.EnhancedObject;
import org.warp.cowdb.EnhancedObjectUpgrader;
+import org.warp.cowdb.IDatabase;
+import org.warp.cowdb.IDatabaseTools;
import org.warp.jcwdb.ann.DBClass;
import org.warp.jcwdb.ann.DBDataType;
import org.warp.jcwdb.ann.DBField;
@@ -19,6 +21,14 @@ public class V2Class extends EnhancedObject {
@DBField(id = 3, type = DBDataType.OBJECT)
public String field4;
+ public V2Class() {
+
+ }
+
+ public V2Class(IDatabaseTools databaseTools) throws IOException {
+ super(databaseTools);
+ }
+
@Override
public void onUpgrade(int oldObjectVersion, EnhancedObjectUpgrader enhancedObjectUpgrader) throws IOException {
switch (oldObjectVersion) {
diff --git a/src/test/java/org/warp/jcwdb/utils/NTestUtils.java b/src/test/java/org/warp/jcwdb/utils/NTestUtils.java
index 07baa75..e920403 100644
--- a/src/test/java/org/warp/jcwdb/utils/NTestUtils.java
+++ b/src/test/java/org/warp/jcwdb/utils/NTestUtils.java
@@ -4,6 +4,7 @@ import it.unimi.dsi.fastutil.longs.LongArrayList;
import org.warp.cowdb.Database;
import org.warp.cowdb.EnhancedObject;
import org.warp.cowdb.IDatabase;
+import org.warp.cowdb.IDatabaseTools;
import org.warp.jcwdb.ann.*;
import java.io.File;
@@ -277,8 +278,8 @@ public class NTestUtils {
}
- public RootClass(IDatabase database) throws IOException {
- super(database);
+ public RootClass(IDatabaseTools databaseTools) throws IOException {
+ super(databaseTools);
}
@DBPropertyGetter(id = 0, type = DBDataType.BOOLEAN)