First real copy-on-write implementation
This commit is contained in:
parent
740db59b62
commit
a772ac5431
2
pom.xml
2
pom.xml
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
<groupId>org.warp</groupId>
|
<groupId>org.warp</groupId>
|
||||||
<artifactId>jcwdb</artifactId>
|
<artifactId>jcwdb</artifactId>
|
||||||
<version>1.2-SNAPSHOT</version>
|
<version>1.3</version>
|
||||||
|
|
||||||
<name>jcwdb</name>
|
<name>jcwdb</name>
|
||||||
<!-- FIXME change it to the project's website -->
|
<!-- FIXME change it to the project's website -->
|
||||||
|
44
src/main/java/org/warp/cowdb/BlockInfo.java
Normal file
44
src/main/java/org/warp/cowdb/BlockInfo.java
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
package org.warp.cowdb;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.StringJoiner;
|
||||||
|
|
||||||
|
public class BlockInfo {
|
||||||
|
private final long index;
|
||||||
|
private final int size;
|
||||||
|
|
||||||
|
public BlockInfo(long index, int size) {
|
||||||
|
this.index = index;
|
||||||
|
this.size = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getIndex() {
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSize() {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
BlockInfo blockInfo = (BlockInfo) o;
|
||||||
|
return index == blockInfo.index &&
|
||||||
|
size == blockInfo.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(index, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return new StringJoiner(", ", BlockInfo.class.getSimpleName() + "[", "]")
|
||||||
|
.add("index=" + index)
|
||||||
|
.add("size=" + size)
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
package org.warp.cowdb;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
public class ByteBufferBackedInputStream extends InputStream {
|
||||||
|
|
||||||
|
ByteBuffer buf;
|
||||||
|
|
||||||
|
public ByteBufferBackedInputStream(ByteBuffer buf) {
|
||||||
|
this.buf = buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int read() throws IOException {
|
||||||
|
if (!buf.hasRemaining()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return buf.get() & 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int read(byte[] bytes, int off, int len)
|
||||||
|
throws IOException {
|
||||||
|
if (!buf.hasRemaining()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
len = Math.min(len, buf.remaining());
|
||||||
|
buf.get(bytes, off, len);
|
||||||
|
return len;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
839
src/main/java/org/warp/cowdb/Database.java
Normal file
839
src/main/java/org/warp/cowdb/Database.java
Normal file
@ -0,0 +1,839 @@
|
|||||||
|
package org.warp.cowdb;
|
||||||
|
|
||||||
|
import com.esotericsoftware.kryo.Kryo;
|
||||||
|
import com.esotericsoftware.kryo.io.ByteBufferInput;
|
||||||
|
import com.esotericsoftware.kryo.io.ByteBufferInputStream;
|
||||||
|
import com.esotericsoftware.kryo.io.Input;
|
||||||
|
import com.esotericsoftware.kryo.io.Output;
|
||||||
|
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
||||||
|
import org.apache.commons.lang3.reflect.FieldUtils;
|
||||||
|
import org.warp.jcwdb.ann.*;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOError;
|
||||||
|
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.nio.channels.SeekableByteChannel;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.StandardOpenOption;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import static org.warp.cowdb.IBlocksMetadata.EMPTY_BLOCK_ID;
|
||||||
|
import static org.warp.cowdb.IBlocksMetadata.EMPTY_BLOCK_INFO;
|
||||||
|
|
||||||
|
public class Database implements IDatabase {
|
||||||
|
|
||||||
|
private final DatabaseFileIO fileIO;
|
||||||
|
private final DatabaseBlocksIO blocksIO;
|
||||||
|
private final DatabaseBlocksMetadata blocksMetadata;
|
||||||
|
private final DatabaseReferencesIO referencesIO;
|
||||||
|
private final DatabaseReferencesMetadata referencesMetadata;
|
||||||
|
private final DatabaseObjectsIO objectsIO;
|
||||||
|
private final DatabaseDataInitializer dataInitializer;
|
||||||
|
private EnhancedObject loadedRootObject;
|
||||||
|
|
||||||
|
public Database(Path dataFile, Path blocksMetaFile, Path referencesMetaFile) throws IOException {
|
||||||
|
if (Files.notExists(dataFile)) {
|
||||||
|
Files.createFile(dataFile);
|
||||||
|
}
|
||||||
|
if (Files.notExists(blocksMetaFile)) {
|
||||||
|
Files.createFile(blocksMetaFile);
|
||||||
|
}
|
||||||
|
if (Files.notExists(referencesMetaFile)) {
|
||||||
|
Files.createFile(referencesMetaFile);
|
||||||
|
}
|
||||||
|
this.fileIO = new DatabaseFileIO(dataFile);
|
||||||
|
this.blocksMetadata = new DatabaseBlocksMetadata(blocksMetaFile);
|
||||||
|
this.blocksIO = new DatabaseBlocksIO(fileIO, blocksMetadata);
|
||||||
|
this.referencesMetadata = new DatabaseReferencesMetadata(referencesMetaFile);
|
||||||
|
this.referencesIO = new DatabaseReferencesIO(blocksIO, referencesMetadata);
|
||||||
|
this.objectsIO = new DatabaseObjectsIO(this, referencesIO);
|
||||||
|
this.dataInitializer = new DatabaseDataInitializer(objectsIO);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IDataInitializer getDataInitializer() {
|
||||||
|
return dataInitializer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IObjectsIO getObjectsIO() {
|
||||||
|
return objectsIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
this.objectsIO.setEnhancedObject(0, loadedRootObject);
|
||||||
|
this.referencesMetadata.close();
|
||||||
|
this.blocksMetadata.close();
|
||||||
|
this.fileIO.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T extends EnhancedObject> T loadRoot(Class<T> type) throws IOException {
|
||||||
|
return loadRoot(type, () -> {
|
||||||
|
T obj = objectsIO.instantiateEnhancedObject(type);
|
||||||
|
dataInitializer.initializeDBObject(obj);
|
||||||
|
return obj;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T extends EnhancedObject> T loadRoot(Class<T> type, SupplierWithIO<T> ifAbsent) throws IOException {
|
||||||
|
if (loadedRootObject != null) {
|
||||||
|
throw new RuntimeException("Root already set!");
|
||||||
|
}
|
||||||
|
T root;
|
||||||
|
if (referencesMetadata.firstFreeReference > 0) {
|
||||||
|
root = objectsIO.loadEnhancedObject(0, type);
|
||||||
|
} else {
|
||||||
|
if (objectsIO.newNullObject() != 0) {
|
||||||
|
throw new IOException("Can't allocate root!");
|
||||||
|
} else {
|
||||||
|
root = ifAbsent.getWithIO();
|
||||||
|
objectsIO.setEnhancedObject(0, root);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
loadedRootObject = root;
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static 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);
|
||||||
|
obj.initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
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<String, DBPropertySetter> setterMethods = new LinkedHashMap<>();
|
||||||
|
Map<String, DBPropertyGetter> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class DatabaseObjectsIO implements IObjectsIO {
|
||||||
|
|
||||||
|
private final IDatabase database;
|
||||||
|
private final DatabaseReferencesIO referencesIO;
|
||||||
|
|
||||||
|
private Kryo kryo = new Kryo();
|
||||||
|
|
||||||
|
private DatabaseObjectsIO(IDatabase database, DatabaseReferencesIO referencesIO) {
|
||||||
|
this.database = database;
|
||||||
|
this.referencesIO = referencesIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T extends EnhancedObject> T loadEnhancedObject(long reference, Class<T> objectType) throws IOException {
|
||||||
|
ByteBuffer buffer = referencesIO.readFromReference(reference);
|
||||||
|
if (buffer.limit() == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
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, fieldRefs, methodRefs);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private Object loadData(DBDataType propertyType, long dataReference, Supplier<Class<?>> returnType) throws IOException {
|
||||||
|
switch (propertyType) {
|
||||||
|
case DATABASE_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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> 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 DATABASE_OBJECT:
|
||||||
|
setEnhancedObject(reference, (EnhancedObject) loadedPropertyValue);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T extends EnhancedObject> void setEnhancedObject(long reference, T value) throws IOException {
|
||||||
|
if (value != null) {
|
||||||
|
EnhancedObjectFullInfo objectFullInfo = value.getAllInfo();
|
||||||
|
int totalSize = Integer.BYTES * 2 + objectFullInfo.getFieldReferences().length * Long.BYTES + objectFullInfo.getPropertyReferences().length * Long.BYTES;
|
||||||
|
ByteBuffer buffer = ByteBuffer.allocate(totalSize);
|
||||||
|
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]);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < objectFullInfo.getFieldReferences().length; i++) {
|
||||||
|
try {
|
||||||
|
setData(objectFullInfo.getFieldReferences()[i], objectFullInfo.getFieldTypes()[i], objectFullInfo.getFields()[i].get(value));
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new IOError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int i = 0; i < objectFullInfo.getPropertyReferences().length; i++) {
|
||||||
|
setData(objectFullInfo.getPropertyReferences()[i], objectFullInfo.getPropertyTypes()[i], objectFullInfo.getLoadedPropertyValues()[i]);
|
||||||
|
}
|
||||||
|
buffer.flip();
|
||||||
|
referencesIO.writeToReference(reference, totalSize, buffer);
|
||||||
|
} else {
|
||||||
|
referencesIO.writeToReference(reference, 0, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public <T> T loadObject(long reference) throws IOException {
|
||||||
|
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 {
|
||||||
|
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 {
|
||||||
|
ByteBuffer buffer = referencesIO.readFromReference(reference);
|
||||||
|
if (buffer.limit() == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return buffer.get() == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte loadByte(long reference) throws IOException {
|
||||||
|
ByteBuffer buffer = referencesIO.readFromReference(reference);
|
||||||
|
if (buffer.limit() == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return buffer.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public short loadShort(long reference) throws IOException {
|
||||||
|
ByteBuffer buffer = referencesIO.readFromReference(reference);
|
||||||
|
if (buffer.limit() == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return buffer.getShort();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public char loadChar(long reference) throws IOException {
|
||||||
|
ByteBuffer buffer = referencesIO.readFromReference(reference);
|
||||||
|
if (buffer.limit() == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return buffer.getChar();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int loadInt(long reference) throws IOException {
|
||||||
|
ByteBuffer buffer = referencesIO.readFromReference(reference);
|
||||||
|
if (buffer.limit() == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return buffer.getInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long loadLong(long reference) throws IOException {
|
||||||
|
ByteBuffer buffer = referencesIO.readFromReference(reference);
|
||||||
|
if (buffer.limit() == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return buffer.getLong();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> void setObject(long reference, T value) throws IOException {
|
||||||
|
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 {
|
||||||
|
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 {
|
||||||
|
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 {
|
||||||
|
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 {
|
||||||
|
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 {
|
||||||
|
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 {
|
||||||
|
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 {
|
||||||
|
ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES);
|
||||||
|
buffer.putLong(value);
|
||||||
|
buffer.flip();
|
||||||
|
referencesIO.writeToReference(reference, Long.BYTES, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long newNullObject() throws IOException {
|
||||||
|
return referencesIO.allocateReference();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void loadProperty(EnhancedObject obj, int propertyId, Method property, DBDataType propertyType, long propertyUID) throws IOException {
|
||||||
|
obj.setProperty(propertyId, loadData(propertyType, propertyUID, property::getReturnType));
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T extends EnhancedObject> 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<String, DBPropertySetter> setterMethods = new LinkedHashMap<>();
|
||||||
|
Map<String, DBPropertyGetter> 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
private 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
private 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 <T extends EnhancedObject> 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T extends EnhancedObject> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T extends EnhancedObject> Field[] getFields(T obj) {
|
||||||
|
return FieldUtils.getFieldsWithAnnotation(obj.getClass(), DBField.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private 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 extends EnhancedObject> T instantiateEnhancedObject(Class<T> type) throws IOException {
|
||||||
|
try {
|
||||||
|
T obj = type.getConstructor().newInstance();
|
||||||
|
obj.database = database;
|
||||||
|
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 extends EnhancedObject> T preloadEnhancedObject(Class<T> objectType, long[] fieldRefs, long[] methodRefs) throws IOException {
|
||||||
|
T obj = instantiateEnhancedObject(objectType);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class DatabaseReferencesMetadata implements IReferencesMetadata {
|
||||||
|
private final SeekableByteChannel metaFileChannel;
|
||||||
|
private final int REF_META_BYTES_COUNT = Long.BYTES;
|
||||||
|
private long metaFileChannelSize;
|
||||||
|
private long firstFreeReference;
|
||||||
|
|
||||||
|
private DatabaseReferencesMetadata(Path refMetaFile) throws IOException {
|
||||||
|
metaFileChannel = Files.newByteChannel(refMetaFile, StandardOpenOption.READ, StandardOpenOption.WRITE);
|
||||||
|
metaFileChannelSize = metaFileChannel.size();
|
||||||
|
firstFreeReference = metaFileChannelSize / REF_META_BYTES_COUNT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getReference(long reference) throws IOException {
|
||||||
|
ByteBuffer buffer = ByteBuffer.allocate(REF_META_BYTES_COUNT);
|
||||||
|
if (reference >= firstFreeReference || reference * REF_META_BYTES_COUNT > metaFileChannelSize) {
|
||||||
|
return EMPTY_BLOCK_ID;
|
||||||
|
}
|
||||||
|
SeekableByteChannel currentFileChannel = metaFileChannel.position(reference * REF_META_BYTES_COUNT);
|
||||||
|
currentFileChannel.read(buffer);
|
||||||
|
buffer.flip();
|
||||||
|
return buffer.getLong();
|
||||||
|
}
|
||||||
|
|
||||||
|
@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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class DatabaseBlocksIO implements IBlocksIO {
|
||||||
|
|
||||||
|
private final DatabaseFileIO fileIO;
|
||||||
|
private final IBlocksMetadata blocksMetadata;
|
||||||
|
|
||||||
|
private 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() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class DatabaseBlocksMetadata implements IBlocksMetadata {
|
||||||
|
private final SeekableByteChannel metaFileChannel;
|
||||||
|
private final int BLOCK_META_BYTES_COUNT = Long.BYTES + Integer.BYTES;
|
||||||
|
private long firstFreeBlock;
|
||||||
|
|
||||||
|
private 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class DatabaseFileIO implements IFileIO {
|
||||||
|
|
||||||
|
private final SeekableByteChannel dataFileChannel;
|
||||||
|
private final Object dataAccessLock = new Object();
|
||||||
|
private long firstFreeIndex;
|
||||||
|
|
||||||
|
private 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
101
src/main/java/org/warp/cowdb/EnhancedObject.java
Normal file
101
src/main/java/org/warp/cowdb/EnhancedObject.java
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
package org.warp.cowdb;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.reflect.MethodUtils;
|
||||||
|
import org.warp.jcwdb.ann.DBDataType;
|
||||||
|
import org.warp.jcwdb.ann.DBPropertyGetter;
|
||||||
|
import org.warp.jcwdb.ann.DBPropertySetter;
|
||||||
|
|
||||||
|
import java.io.IOError;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public abstract class EnhancedObject {
|
||||||
|
protected IDatabase database;
|
||||||
|
private Field[] fields;
|
||||||
|
private DBDataType[] fieldTypes;
|
||||||
|
private long[] fieldReferences;
|
||||||
|
private Method[] propertyGetters;
|
||||||
|
private Method[] propertySetters;
|
||||||
|
private DBDataType[] propertyTypes;
|
||||||
|
private long[] propertyReferences;
|
||||||
|
private boolean[] loadedProperties;
|
||||||
|
private Object[] loadedPropertyValues;
|
||||||
|
private Map<String, DBPropertySetter> setterMethods;
|
||||||
|
private Map<String, DBPropertyGetter> getterMethods;
|
||||||
|
|
||||||
|
public EnhancedObject() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public EnhancedObject(IDatabase database) throws IOException {
|
||||||
|
this.database = database;
|
||||||
|
database.getDataInitializer().initializeDBObject(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void initialize() throws IOException;
|
||||||
|
|
||||||
|
|
||||||
|
public void setFields(Field[] fields, DBDataType[] fieldTypes, long[] fieldReferences) {
|
||||||
|
this.fields = fields;
|
||||||
|
this.fieldTypes = fieldTypes;
|
||||||
|
this.fieldReferences = fieldReferences;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Method[] getPropertyGetters() {
|
||||||
|
return MethodUtils.getMethodsWithAnnotation(this.getClass(), DBPropertyGetter.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Method[] getPropertySetters() {
|
||||||
|
return MethodUtils.getMethodsWithAnnotation(this.getClass(), DBPropertySetter.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProperties(Method[] propertyGetters, Method[] propertySetters, DBDataType[] propertyTypes, long[] propertyReferences, Map<String, DBPropertySetter> setterMethods, Map<String, DBPropertyGetter> getterMethods) {
|
||||||
|
this.propertyGetters = propertyGetters;
|
||||||
|
this.propertySetters = propertySetters;
|
||||||
|
this.propertyTypes = propertyTypes;
|
||||||
|
this.propertyReferences = propertyReferences;
|
||||||
|
this.loadedProperties = new boolean[this.propertyReferences.length];
|
||||||
|
this.loadedPropertyValues = new Object[this.propertyReferences.length];
|
||||||
|
this.setterMethods = setterMethods;
|
||||||
|
this.getterMethods = getterMethods;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EnhancedObjectFullInfo getAllInfo() {
|
||||||
|
return new EnhancedObjectFullInfo(fieldReferences, fieldTypes, fields, propertyReferences, propertyTypes, loadedPropertyValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public <T> T getProperty() {
|
||||||
|
StackWalker walker = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
|
||||||
|
StackWalker.StackFrame stackFrame = walker.walk(f -> f.skip(1).findFirst().orElse(null));
|
||||||
|
try {
|
||||||
|
int propertyId = stackFrame.getDeclaringClass().getDeclaredMethod(stackFrame.getMethodName()).getAnnotation(DBPropertyGetter.class).id();
|
||||||
|
return getProperty(propertyId);
|
||||||
|
} catch (IOException | NoSuchMethodException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private <T> T getProperty(int propertyId) throws IOException {
|
||||||
|
if (!loadedProperties[propertyId]) {
|
||||||
|
long propertyUID = propertyReferences[propertyId];
|
||||||
|
database.getObjectsIO().loadProperty(this, propertyId, propertyGetters[propertyId], propertyTypes[propertyId], propertyUID);
|
||||||
|
}
|
||||||
|
return (T) loadedPropertyValues[propertyId];
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> void setProperty(T value) {
|
||||||
|
StackWalker walker = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
|
||||||
|
StackWalker.StackFrame stackFrame = walker.walk(f -> f.skip(1).findFirst().orElse(null));
|
||||||
|
DBPropertySetter propertyAnnotation = setterMethods.get(stackFrame.getMethodName());
|
||||||
|
setProperty(propertyAnnotation.id(), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> void setProperty(int propertyId, T value) {
|
||||||
|
loadedPropertyValues[propertyId] = value;
|
||||||
|
loadedProperties[propertyId] = true;
|
||||||
|
}
|
||||||
|
}
|
47
src/main/java/org/warp/cowdb/EnhancedObjectFullInfo.java
Normal file
47
src/main/java/org/warp/cowdb/EnhancedObjectFullInfo.java
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
package org.warp.cowdb;
|
||||||
|
|
||||||
|
import org.warp.jcwdb.ann.DBDataType;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
|
||||||
|
public class EnhancedObjectFullInfo {
|
||||||
|
private final long[] fieldReferences;
|
||||||
|
private final DBDataType[] fieldTypes;
|
||||||
|
private final Field[] fields;
|
||||||
|
private final long[] propertyReferences;
|
||||||
|
private final DBDataType[] propertyTypes;
|
||||||
|
private final Object[] loadedPropertyValues;
|
||||||
|
|
||||||
|
public EnhancedObjectFullInfo(long[] fieldReferences, DBDataType[] fieldTypes, Field[] fields, long[] propertyReferences, DBDataType[] propertyTypes, Object[] loadedPropertyValues) {
|
||||||
|
this.fieldReferences = fieldReferences;
|
||||||
|
this.fieldTypes = fieldTypes;
|
||||||
|
this.fields = fields;
|
||||||
|
this.propertyReferences = propertyReferences;
|
||||||
|
this.propertyTypes = propertyTypes;
|
||||||
|
this.loadedPropertyValues = loadedPropertyValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long[] getFieldReferences() {
|
||||||
|
return fieldReferences;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DBDataType[] getFieldTypes() {
|
||||||
|
return fieldTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Field[] getFields() {
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long[] getPropertyReferences() {
|
||||||
|
return propertyReferences;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DBDataType[] getPropertyTypes() {
|
||||||
|
return propertyTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object[] getLoadedPropertyValues() {
|
||||||
|
return loadedPropertyValues;
|
||||||
|
}
|
||||||
|
}
|
28
src/main/java/org/warp/cowdb/IBlocksIO.java
Normal file
28
src/main/java/org/warp/cowdb/IBlocksIO.java
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package org.warp.cowdb;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
public interface IBlocksIO {
|
||||||
|
/**
|
||||||
|
* Allocate a block
|
||||||
|
* @param size block size
|
||||||
|
* @param data block data
|
||||||
|
* @return the block id
|
||||||
|
*/
|
||||||
|
long newBlock(int size, ByteBuffer data) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read a block
|
||||||
|
* @param blockId block id
|
||||||
|
* @return block data
|
||||||
|
*/
|
||||||
|
ByteBuffer readBlock(long blockId) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close file
|
||||||
|
*/
|
||||||
|
void close();
|
||||||
|
}
|
44
src/main/java/org/warp/cowdb/IBlocksMetadata.java
Normal file
44
src/main/java/org/warp/cowdb/IBlocksMetadata.java
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
package org.warp.cowdb;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public interface IBlocksMetadata {
|
||||||
|
long EMPTY_BLOCK_ID = -1;
|
||||||
|
BlockInfo EMPTY_BLOCK_INFO = new BlockInfo(0, 0);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get block info
|
||||||
|
* @param blockId block id
|
||||||
|
* @return block metadata
|
||||||
|
*/
|
||||||
|
BlockInfo getBlockInfo(long blockId) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* New empty block info
|
||||||
|
* @return block id
|
||||||
|
*/
|
||||||
|
default long newBlock() {
|
||||||
|
return EMPTY_BLOCK_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set block info
|
||||||
|
* @param index block index
|
||||||
|
* @param size block size
|
||||||
|
* @return block id
|
||||||
|
*/
|
||||||
|
long newBlock(long index, int size) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set block info
|
||||||
|
* @param blockInfo block info
|
||||||
|
* @return block id
|
||||||
|
*/
|
||||||
|
default long newBlock(BlockInfo blockInfo) throws IOException {
|
||||||
|
return this.newBlock(blockInfo.getIndex(), blockInfo.getSize());
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Close file
|
||||||
|
*/
|
||||||
|
void close() throws IOException;
|
||||||
|
}
|
7
src/main/java/org/warp/cowdb/IDataInitializer.java
Normal file
7
src/main/java/org/warp/cowdb/IDataInitializer.java
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package org.warp.cowdb;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public interface IDataInitializer {
|
||||||
|
void initializeDBObject(EnhancedObject obj) throws IOException;
|
||||||
|
}
|
10
src/main/java/org/warp/cowdb/IDatabase.java
Normal file
10
src/main/java/org/warp/cowdb/IDatabase.java
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package org.warp.cowdb;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public interface IDatabase {
|
||||||
|
|
||||||
|
void close() throws IOException;
|
||||||
|
IDataInitializer getDataInitializer();
|
||||||
|
IObjectsIO getObjectsIO();
|
||||||
|
}
|
35
src/main/java/org/warp/cowdb/IFileIO.java
Normal file
35
src/main/java/org/warp/cowdb/IFileIO.java
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
package org.warp.cowdb;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
public interface IFileIO {
|
||||||
|
/**
|
||||||
|
* Read *length* bytes in position *index*
|
||||||
|
* @param index index
|
||||||
|
* @param length length
|
||||||
|
* @return bytes
|
||||||
|
*/
|
||||||
|
ByteBuffer readAt(long index, int length) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write *length* bytes in position *index*
|
||||||
|
* @param index index
|
||||||
|
* @param length length
|
||||||
|
* @param data bytes
|
||||||
|
*/
|
||||||
|
void writeAt(long index, int length, ByteBuffer data) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write *length* bytes in position *index*
|
||||||
|
* @param length length
|
||||||
|
* @param data bytes
|
||||||
|
* @return index
|
||||||
|
*/
|
||||||
|
long writeAtEnd(int length, ByteBuffer data) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close the file
|
||||||
|
*/
|
||||||
|
void close() throws IOException;
|
||||||
|
}
|
109
src/main/java/org/warp/cowdb/IObjectsIO.java
Normal file
109
src/main/java/org/warp/cowdb/IObjectsIO.java
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
package org.warp.cowdb;
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
||||||
|
import org.warp.jcwdb.ann.DBDataType;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
interface IObjectsIO {
|
||||||
|
<T extends EnhancedObject> T loadEnhancedObject(long reference, Class<T> objectType) throws IOException;
|
||||||
|
|
||||||
|
<T> T loadObject(long reference) throws IOException;
|
||||||
|
|
||||||
|
LongArrayList loadReferencesList(long reference) throws IOException;
|
||||||
|
|
||||||
|
boolean loadBoolean(long reference) throws IOException;
|
||||||
|
|
||||||
|
byte loadByte(long reference) throws IOException;
|
||||||
|
|
||||||
|
short loadShort(long reference) throws IOException;
|
||||||
|
|
||||||
|
char loadChar(long reference) throws IOException;
|
||||||
|
|
||||||
|
int loadInt(long reference) throws IOException;
|
||||||
|
|
||||||
|
long loadLong(long reference) throws IOException;
|
||||||
|
|
||||||
|
<T extends EnhancedObject> void setEnhancedObject(long reference, T value) throws IOException;
|
||||||
|
|
||||||
|
<T> void setObject(long reference, T value) throws IOException;
|
||||||
|
|
||||||
|
void setReferencesList(long reference, LongArrayList value) throws IOException;
|
||||||
|
|
||||||
|
void setBoolean(long reference, boolean value) throws IOException;
|
||||||
|
|
||||||
|
void setByte(long reference, byte value) throws IOException;
|
||||||
|
|
||||||
|
void setShort(long reference, short value) throws IOException;
|
||||||
|
|
||||||
|
void setChar(long reference, char value) throws IOException;
|
||||||
|
|
||||||
|
void setInt(long reference, int value) throws IOException;
|
||||||
|
|
||||||
|
void setLong(long reference, long value) throws IOException;
|
||||||
|
|
||||||
|
long newNullObject() throws IOException;
|
||||||
|
|
||||||
|
default <T extends EnhancedObject> long newEnhancedObject(T value) throws IOException {
|
||||||
|
long reference = newNullObject();
|
||||||
|
if (value != null) {
|
||||||
|
setEnhancedObject(reference, value);
|
||||||
|
}
|
||||||
|
return reference;
|
||||||
|
}
|
||||||
|
|
||||||
|
default <T> long newObject(T value) throws IOException {
|
||||||
|
long reference = newNullObject();
|
||||||
|
if (value != null) {
|
||||||
|
setObject(reference, value);
|
||||||
|
}
|
||||||
|
return reference;
|
||||||
|
}
|
||||||
|
|
||||||
|
default long newReferencesList(LongArrayList value) throws IOException {
|
||||||
|
long reference = newNullObject();
|
||||||
|
if (value != null) {
|
||||||
|
setReferencesList(reference, value);
|
||||||
|
}
|
||||||
|
return reference;
|
||||||
|
}
|
||||||
|
|
||||||
|
default long newBoolean(boolean value) throws IOException {
|
||||||
|
long reference = newNullObject();
|
||||||
|
setBoolean(reference, value);
|
||||||
|
return reference;
|
||||||
|
}
|
||||||
|
|
||||||
|
default long newByte(byte value) throws IOException {
|
||||||
|
long reference = newNullObject();
|
||||||
|
setByte(reference, value);
|
||||||
|
return reference;
|
||||||
|
}
|
||||||
|
|
||||||
|
default long newShort(short value) throws IOException {
|
||||||
|
long reference = newNullObject();
|
||||||
|
setShort(reference, value);
|
||||||
|
return reference;
|
||||||
|
}
|
||||||
|
|
||||||
|
default long newChar(char value) throws IOException {
|
||||||
|
long reference = newNullObject();
|
||||||
|
setChar(reference, value);
|
||||||
|
return reference;
|
||||||
|
}
|
||||||
|
|
||||||
|
default long newInt(int value) throws IOException {
|
||||||
|
long reference = newNullObject();
|
||||||
|
setInt(reference, value);
|
||||||
|
return reference;
|
||||||
|
}
|
||||||
|
|
||||||
|
default long newLong(long value) throws IOException {
|
||||||
|
long reference = newNullObject();
|
||||||
|
setLong(reference, value);
|
||||||
|
return reference;
|
||||||
|
}
|
||||||
|
|
||||||
|
void loadProperty(EnhancedObject enhancedObject, int propertyId, Method propertyGetter, DBDataType propertyType, long propertyUID) throws IOException;
|
||||||
|
}
|
37
src/main/java/org/warp/cowdb/IReferencesIO.java
Normal file
37
src/main/java/org/warp/cowdb/IReferencesIO.java
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
package org.warp.cowdb;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
public interface IReferencesIO {
|
||||||
|
/**
|
||||||
|
* Allocate a new empty reference
|
||||||
|
* @return the new reference
|
||||||
|
*/
|
||||||
|
long allocateReference() throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocate a new reference with that data
|
||||||
|
* @param size data size
|
||||||
|
* @param data bytes
|
||||||
|
* @return the new reference
|
||||||
|
*/
|
||||||
|
long allocateReference(int size, ByteBuffer data) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write some data to the reference
|
||||||
|
* @param reference reference
|
||||||
|
* @param size data size
|
||||||
|
* @param data bytes
|
||||||
|
*/
|
||||||
|
void writeToReference(long reference, int size, ByteBuffer data) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read data from the reference
|
||||||
|
* @param reference reference
|
||||||
|
* @return bytes
|
||||||
|
*/
|
||||||
|
ByteBuffer readFromReference(long reference) throws IOException;
|
||||||
|
}
|
31
src/main/java/org/warp/cowdb/IReferencesMetadata.java
Normal file
31
src/main/java/org/warp/cowdb/IReferencesMetadata.java
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
package org.warp.cowdb;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public interface IReferencesMetadata {
|
||||||
|
/**
|
||||||
|
* Get block of reference
|
||||||
|
* @param reference reference
|
||||||
|
* @return block id
|
||||||
|
*/
|
||||||
|
long getReference(long reference) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocate a block for a new reference
|
||||||
|
* @param blockId block id
|
||||||
|
* @return reference
|
||||||
|
*/
|
||||||
|
long newReference(long blockId) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change reference size
|
||||||
|
* @param reference reference
|
||||||
|
* @param blockId block id
|
||||||
|
*/
|
||||||
|
void editReference(long reference, long blockId) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close file
|
||||||
|
*/
|
||||||
|
void close() throws IOException;
|
||||||
|
}
|
@ -1,11 +1,5 @@
|
|||||||
package org.warp.jcwdb;
|
package org.warp.jcwdb;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.lang.ref.WeakReference;
|
|
||||||
import java.nio.channels.ClosedChannelException;
|
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMap.Entry;
|
|
||||||
|
|
||||||
public class Cleaner {
|
public class Cleaner {
|
||||||
|
|
||||||
public static final boolean DISABLE_CLEANER = false;
|
public static final boolean DISABLE_CLEANER = false;
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
package org.warp.jcwdb;
|
package org.warp.jcwdb;
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.longs.*;
|
import it.unimi.dsi.fastutil.longs.Long2IntLinkedOpenHashMap;
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectBidirectionalIterator;
|
import it.unimi.dsi.fastutil.longs.Long2IntMap;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.channels.SeekableByteChannel;
|
import java.nio.channels.SeekableByteChannel;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
@ -7,6 +7,7 @@ import it.unimi.dsi.fastutil.objects.ObjectIterator;
|
|||||||
import org.warp.jcwdb.ann.DatabaseManager;
|
import org.warp.jcwdb.ann.DatabaseManager;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.channels.Channels;
|
import java.nio.channels.Channels;
|
||||||
import java.nio.channels.SeekableByteChannel;
|
import java.nio.channels.SeekableByteChannel;
|
||||||
@ -15,7 +16,7 @@ import java.nio.file.Path;
|
|||||||
import java.nio.file.StandardOpenOption;
|
import java.nio.file.StandardOpenOption;
|
||||||
|
|
||||||
public class FileIndexManager implements IndexManager {
|
public class FileIndexManager implements IndexManager {
|
||||||
public static final boolean ALWAYS_ALLOCATE_NEW = false;
|
public static final boolean ALWAYS_ALLOCATE_NEW = true;
|
||||||
private final SeekableByteChannel dataFileChannel, metadataFileChannel;
|
private final SeekableByteChannel dataFileChannel, metadataFileChannel;
|
||||||
private volatile long metadataFileChannelSize;
|
private volatile long metadataFileChannelSize;
|
||||||
private final FileAllocator fileAllocator;
|
private final FileAllocator fileAllocator;
|
||||||
@ -114,7 +115,7 @@ public class FileIndexManager implements IndexManager {
|
|||||||
public <T> T get(long index, DBReader<T> reader) throws IOException {
|
public <T> T get(long index, DBReader<T> reader) throws IOException {
|
||||||
checkClosed();
|
checkClosed();
|
||||||
IndexDetails details = getIndexMetadata(index);
|
IndexDetails details = getIndexMetadata(index);
|
||||||
Input i = new Input(Channels.newInputStream(dataFileChannel.position(details.getOffset())));
|
Input i = new Input(Channels.newInputStream(dataFileChannel.position(details.getOffset())), details.getSize());
|
||||||
T result = reader.read(i, details.getSize());
|
T result = reader.read(i, details.getSize());
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -184,8 +185,10 @@ public class FileIndexManager implements IndexManager {
|
|||||||
throw new IOException("Unable to write " + size + " in a space of " + indexDetails.getSize());
|
throw new IOException("Unable to write " + size + " in a space of " + indexDetails.getSize());
|
||||||
}
|
}
|
||||||
final long offset = indexDetails.getOffset();
|
final long offset = indexDetails.getOffset();
|
||||||
final Output o = new Output(Channels.newOutputStream(dataFileChannel.position(offset)), size);
|
OutputStream os = Channels.newOutputStream(dataFileChannel.position(offset));
|
||||||
|
final Output o = new Output(os, size);
|
||||||
data.write(o);
|
data.write(o);
|
||||||
|
os.flush();
|
||||||
o.flush();
|
o.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
package org.warp.jcwdb;
|
package org.warp.jcwdb;
|
||||||
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
public class IndexDetails {
|
public class IndexDetails {
|
||||||
/**
|
/**
|
||||||
* The bitmask is used to determine if an index has been deleted
|
* The bitmask is used to determine if an index has been deleted
|
||||||
|
@ -1,11 +1,6 @@
|
|||||||
package org.warp.jcwdb;
|
package org.warp.jcwdb;
|
||||||
|
|
||||||
import com.esotericsoftware.kryo.io.Output;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.function.BiConsumer;
|
|
||||||
import java.util.function.BiPredicate;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
public interface IndexManager extends Cleanable {
|
public interface IndexManager extends Cleanable {
|
||||||
<T> T get(long index, DBReader<T> reader) throws IOException;
|
<T> T get(long index, DBReader<T> reader) throws IOException;
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
package org.warp.jcwdb;
|
package org.warp.jcwdb;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
public interface Saveable {
|
public interface Saveable {
|
||||||
void save();
|
void save();
|
||||||
void saveAndFlush();
|
void saveAndFlush();
|
||||||
|
15
src/main/java/org/warp/jcwdb/ann/ConsumerWithIO.java
Normal file
15
src/main/java/org/warp/jcwdb/ann/ConsumerWithIO.java
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package org.warp.jcwdb.ann;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface ConsumerWithIO<T> {
|
||||||
|
|
||||||
|
void accept(T t) throws IOException;
|
||||||
|
|
||||||
|
default ConsumerWithIO<T> andThen(ConsumerWithIO<? super T> after) {
|
||||||
|
Objects.requireNonNull(after);
|
||||||
|
return (T t) -> { accept(t); after.accept(t); };
|
||||||
|
}
|
||||||
|
}
|
@ -13,13 +13,17 @@ public abstract class DBArrayList<T> extends DBObject {
|
|||||||
@DBField(id = 0, type = DBDataType.UID_LIST)
|
@DBField(id = 0, type = DBDataType.UID_LIST)
|
||||||
private LongArrayList indices;
|
private LongArrayList indices;
|
||||||
|
|
||||||
public DBArrayList(JCWDatabase database) {
|
public DBArrayList() {
|
||||||
super(database);
|
super();
|
||||||
indices = new LongArrayList();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public DBArrayList(JCWDatabase database, DBObjectIndicesManager.DBObjectInfo objectInfo) throws IOException {
|
public DBArrayList(JCWDatabase database) throws IOException {
|
||||||
super(database, objectInfo);
|
super(database);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize() {
|
||||||
|
indices = new LongArrayList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public T get(int index) {
|
public T get(int index) {
|
||||||
@ -34,7 +38,7 @@ public abstract class DBArrayList<T> extends DBObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void add(T value) {
|
public void add(T value) {
|
||||||
long uid = databaseManager.allocateNullValue();
|
long uid = database.getDataLoader().allocateNullValue();
|
||||||
synchronized (indicesAccessLock) {
|
synchronized (indicesAccessLock) {
|
||||||
indices.add(uid);
|
indices.add(uid);
|
||||||
try {
|
try {
|
||||||
@ -52,7 +56,7 @@ public abstract class DBArrayList<T> extends DBObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void set(int index, T value) {
|
public void set(int index, T value) {
|
||||||
long uid = databaseManager.allocateNullValue();
|
long uid = database.getDataLoader().allocateNullValue();
|
||||||
synchronized (indicesAccessLock) {
|
synchronized (indicesAccessLock) {
|
||||||
indices.set(index, uid);
|
indices.set(index, uid);
|
||||||
try {
|
try {
|
||||||
@ -64,7 +68,7 @@ public abstract class DBArrayList<T> extends DBObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void add(int index, T value) {
|
public void add(int index, T value) {
|
||||||
long uid = databaseManager.allocateNullValue();
|
long uid = database.getDataLoader().allocateNullValue();
|
||||||
synchronized (indicesAccessLock) {
|
synchronized (indicesAccessLock) {
|
||||||
indices.add(index, uid);
|
indices.add(index, uid);
|
||||||
try {
|
try {
|
||||||
|
@ -7,22 +7,22 @@ public class DBDBObjectList<T extends DBObject> extends DBArrayList<T> {
|
|||||||
@DBField(id = 1, type = DBDataType.OBJECT)
|
@DBField(id = 1, type = DBDataType.OBJECT)
|
||||||
private Class<T> type;
|
private Class<T> type;
|
||||||
|
|
||||||
public DBDBObjectList(JCWDatabase database, Class<T> type) {
|
public DBDBObjectList() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public DBDBObjectList(JCWDatabase database, Class<T> type) throws IOException {
|
||||||
super(database);
|
super(database);
|
||||||
this.type = type;
|
this.type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DBDBObjectList(JCWDatabase database, DBObjectIndicesManager.DBObjectInfo objectInfo) throws IOException {
|
|
||||||
super(database, objectInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected T loadItem(long uid) throws IOException {
|
protected T loadItem(long uid) throws IOException {
|
||||||
return databaseManager.loadDBObject(type, uid);
|
return database.getDataLoader().loadDBObject(type, uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void writeItemToDisk(long uid, T item) throws IOException {
|
protected void writeItemToDisk(long uid, T item) throws IOException {
|
||||||
databaseManager.writeObjectProperty(uid, DBDataType.DATABASE_OBJECT, item);
|
database.getDataLoader().writeObjectProperty(uid, DBDataType.DATABASE_OBJECT, item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,5 +9,6 @@ public enum DBDataType {
|
|||||||
CHAR,
|
CHAR,
|
||||||
INTEGER,
|
INTEGER,
|
||||||
LONG,
|
LONG,
|
||||||
UID_LIST
|
UID_LIST,
|
||||||
|
REFERENCES_LIST
|
||||||
}
|
}
|
@ -1,6 +1,6 @@
|
|||||||
package org.warp.jcwdb.ann;
|
package org.warp.jcwdb.ann;
|
||||||
|
|
||||||
import org.apache.commons.lang3.reflect.FieldUtils;
|
import org.apache.commons.lang3.reflect.MethodUtils;
|
||||||
|
|
||||||
import java.io.IOError;
|
import java.io.IOError;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -10,8 +10,7 @@ import java.util.LinkedHashMap;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public abstract class DBObject {
|
public abstract class DBObject {
|
||||||
protected final JCWDatabase database;
|
protected JCWDatabase database;
|
||||||
protected final DatabaseManager databaseManager;
|
|
||||||
private Field[] fields;
|
private Field[] fields;
|
||||||
private DBDataType[] fieldTypes;
|
private DBDataType[] fieldTypes;
|
||||||
private long[] fieldUIDs;
|
private long[] fieldUIDs;
|
||||||
@ -27,101 +26,17 @@ public abstract class DBObject {
|
|||||||
private final Object fieldsAccessLock = new Object();
|
private final Object fieldsAccessLock = new Object();
|
||||||
private final Object propertiesAccessLock = new Object();
|
private final Object propertiesAccessLock = new Object();
|
||||||
|
|
||||||
public DBObject(JCWDatabase database) {
|
public DBObject() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public DBObject(JCWDatabase database) throws IOException {
|
||||||
this.database = database;
|
this.database = database;
|
||||||
this.databaseManager = database.getDatabaseManager();
|
database.initializeDBObject(this);
|
||||||
try {
|
|
||||||
initializeDBObject();
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public DBObject(JCWDatabase database, DBObjectIndicesManager.DBObjectInfo objectInfo) throws IOException {
|
public abstract void initialize() throws IOException;
|
||||||
this.database = database;
|
|
||||||
this.databaseManager = database.getDatabaseManager();
|
|
||||||
this.databaseManager.preloadDBObject(this, objectInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initializeDBObject() throws IOException {
|
|
||||||
initializeDBObjectFields();
|
|
||||||
initializeDBObjectProperties();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initializeDBObjectFields() throws IOException {
|
|
||||||
// Declare the variables needed to get the biggest field Id
|
|
||||||
Field[] unorderedFields = databaseManager.getFields(this);
|
|
||||||
// Find the biggest field Id
|
|
||||||
int biggestFieldId = databaseManager.getBiggestFieldId(unorderedFields);
|
|
||||||
|
|
||||||
// Allocate new UIDs
|
|
||||||
fieldUIDs = databaseManager.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();
|
|
||||||
databaseManager.loadField(this, field, fieldType, fieldUIDs[fieldId]);
|
|
||||||
fields[fieldId] = field;
|
|
||||||
orderedFieldTypes[fieldId] = fieldType;
|
|
||||||
}
|
|
||||||
// Set fields metadata
|
|
||||||
setFields(fields, orderedFieldTypes, fieldUIDs);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initializeDBObjectProperties() {
|
|
||||||
// Declare the variables needed to get the biggest property Id
|
|
||||||
Method[] unorderedPropertyGetters = databaseManager.getPropertyGetters(this);
|
|
||||||
Method[] unorderedPropertySetters = databaseManager.getPropertySetters(this);
|
|
||||||
|
|
||||||
// Find the biggest property Id
|
|
||||||
int biggestGetter = databaseManager.getBiggestPropertyGetterId(unorderedPropertyGetters);
|
|
||||||
int biggestSetter = databaseManager.getBiggestPropertySetterId(unorderedPropertySetters);
|
|
||||||
int biggestPropertyId = biggestGetter > biggestSetter ? biggestGetter : biggestSetter;
|
|
||||||
|
|
||||||
// Allocate new UIDs
|
|
||||||
propertyUIDs = databaseManager.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<String, DBPropertySetter> setterMethods = new LinkedHashMap<>();
|
|
||||||
Map<String, DBPropertyGetter> 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
|
|
||||||
setProperties(propertyGetters, propertySetters, propertyTypes, propertyUIDs, setterMethods, getterMethods);
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T> T getProperty() {
|
public <T> T getProperty() {
|
||||||
StackWalker walker = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
|
StackWalker walker = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
|
||||||
@ -144,7 +59,7 @@ public abstract class DBObject {
|
|||||||
synchronized (propertiesAccessLock) {
|
synchronized (propertiesAccessLock) {
|
||||||
if (!loadedProperties[propertyId]) {
|
if (!loadedProperties[propertyId]) {
|
||||||
long propertyUID = propertyUIDs[propertyId];
|
long propertyUID = propertyUIDs[propertyId];
|
||||||
databaseManager.loadProperty(this, propertyId, propertyGetters[propertyId], propertyTypes[propertyId], propertyUID);
|
database.getDataLoader().loadProperty(this, propertyId, propertyGetters[propertyId], propertyTypes[propertyId], propertyUID);
|
||||||
}
|
}
|
||||||
return (T) loadedPropertyValues[propertyId];
|
return (T) loadedPropertyValues[propertyId];
|
||||||
}
|
}
|
||||||
@ -169,13 +84,13 @@ public abstract class DBObject {
|
|||||||
try {
|
try {
|
||||||
synchronized (propertiesAccessLock) {
|
synchronized (propertiesAccessLock) {
|
||||||
synchronized (fieldsAccessLock) {
|
synchronized (fieldsAccessLock) {
|
||||||
databaseManager.writeObjectInfo(uid, fieldUIDs, propertyUIDs);
|
database.getDataLoader().writeObjectInfo(uid, fieldUIDs, propertyUIDs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
synchronized (fieldsAccessLock) {
|
synchronized (fieldsAccessLock) {
|
||||||
for (int i = 0; i < fieldUIDs.length; i++) {
|
for (int i = 0; i < fieldUIDs.length; i++) {
|
||||||
try {
|
try {
|
||||||
databaseManager.writeObjectProperty(fieldUIDs[i], fieldTypes[i], fields[i].get(this));
|
database.getDataLoader().writeObjectProperty(fieldUIDs[i], fieldTypes[i], fields[i].get(this));
|
||||||
} catch (IllegalAccessException e) {
|
} catch (IllegalAccessException e) {
|
||||||
throw new IOError(e);
|
throw new IOError(e);
|
||||||
}
|
}
|
||||||
@ -183,7 +98,7 @@ public abstract class DBObject {
|
|||||||
}
|
}
|
||||||
synchronized (propertiesAccessLock) {
|
synchronized (propertiesAccessLock) {
|
||||||
for (int i = 0; i < propertyUIDs.length; i++) {
|
for (int i = 0; i < propertyUIDs.length; i++) {
|
||||||
databaseManager.writeObjectProperty(propertyUIDs[i], propertyTypes[i], loadedPropertyValues[i]);
|
database.getDataLoader().writeObjectProperty(propertyUIDs[i], propertyTypes[i], loadedPropertyValues[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
@ -211,4 +126,12 @@ public abstract class DBObject {
|
|||||||
this.getterMethods = getterMethods;
|
this.getterMethods = getterMethods;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Method[] getPropertyGetters() {
|
||||||
|
return MethodUtils.getMethodsWithAnnotation(this.getClass(), DBPropertyGetter.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Method[] getPropertySetters() {
|
||||||
|
return MethodUtils.getMethodsWithAnnotation(this.getClass(), DBPropertySetter.class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,21 +3,22 @@ package org.warp.jcwdb.ann;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
public class DBObjectList<T> extends DBArrayList<T> {
|
public class DBObjectList<T> extends DBArrayList<T> {
|
||||||
public DBObjectList(JCWDatabase database) {
|
|
||||||
super(database);
|
public DBObjectList() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public DBObjectList(JCWDatabase database, DBObjectIndicesManager.DBObjectInfo objectInfo) throws IOException {
|
public DBObjectList(JCWDatabase database) throws IOException {
|
||||||
super(database, objectInfo);
|
super(database);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public T loadItem(long uid) throws IOException {
|
public T loadItem(long uid) throws IOException {
|
||||||
return databaseManager.loadObject(uid);
|
return database.getDataLoader().loadObject(uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeItemToDisk(long uid, T item) throws IOException {
|
public void writeItemToDisk(long uid, T item) throws IOException {
|
||||||
databaseManager.writeObjectProperty(uid, DBDataType.OBJECT, item);
|
database.getDataLoader().writeObjectProperty(uid, DBDataType.OBJECT, item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
96
src/main/java/org/warp/jcwdb/ann/DataInitializer.java
Normal file
96
src/main/java/org/warp/jcwdb/ann/DataInitializer.java
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
package org.warp.jcwdb.ann;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class DataInitializer {
|
||||||
|
private final DataLoader dataLoader;
|
||||||
|
|
||||||
|
public DataInitializer(DataLoader dataLoader) {
|
||||||
|
this.dataLoader = dataLoader;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void initializeDBObject(DBObject obj) throws IOException {
|
||||||
|
initializeDBObjectFields(obj);
|
||||||
|
initializeDBObjectProperties(obj);
|
||||||
|
obj.initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeDBObjectFields(DBObject obj) throws IOException {
|
||||||
|
// Declare the variables needed to get the biggest field Id
|
||||||
|
Field[] unorderedFields = dataLoader.getFields(obj);
|
||||||
|
// Find the biggest field Id
|
||||||
|
int biggestFieldId = dataLoader.getBiggestFieldId(unorderedFields);
|
||||||
|
|
||||||
|
// Allocate new UIDs
|
||||||
|
long[] fieldUIDs = dataLoader.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();
|
||||||
|
dataLoader.loadField(obj, field, fieldType, fieldUIDs[fieldId]);
|
||||||
|
fields[fieldId] = field;
|
||||||
|
orderedFieldTypes[fieldId] = fieldType;
|
||||||
|
}
|
||||||
|
// Set fields metadata
|
||||||
|
obj.setFields(fields, orderedFieldTypes, fieldUIDs);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeDBObjectProperties(DBObject obj) {
|
||||||
|
// 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 = dataLoader.getBiggestPropertyGetterId(unorderedPropertyGetters);
|
||||||
|
int biggestSetter = dataLoader.getBiggestPropertySetterId(unorderedPropertySetters);
|
||||||
|
int biggestPropertyId = biggestGetter > biggestSetter ? biggestGetter : biggestSetter;
|
||||||
|
|
||||||
|
// Allocate new UIDs
|
||||||
|
long[] propertyUIDs = dataLoader.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<String, DBPropertySetter> setterMethods = new LinkedHashMap<>();
|
||||||
|
Map<String, DBPropertyGetter> 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);
|
||||||
|
}
|
||||||
|
}
|
645
src/main/java/org/warp/jcwdb/ann/DataLoader.java
Normal file
645
src/main/java/org/warp/jcwdb/ann/DataLoader.java
Normal file
@ -0,0 +1,645 @@
|
|||||||
|
package org.warp.jcwdb.ann;
|
||||||
|
|
||||||
|
import com.esotericsoftware.kryo.Kryo;
|
||||||
|
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.apache.commons.lang3.reflect.MethodUtils;
|
||||||
|
import org.warp.jcwdb.FileIndexManager;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOError;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
public class DataLoader {
|
||||||
|
|
||||||
|
private final Kryo kryo = new Kryo();
|
||||||
|
private final DBObjectIndicesManager objectIndicesManager;
|
||||||
|
private final FileIndexManager indices;
|
||||||
|
private final Object indicesAccessLock = new Object();
|
||||||
|
private volatile boolean closed;
|
||||||
|
/**
|
||||||
|
* DO NOT USE
|
||||||
|
*/
|
||||||
|
private JCWDatabase databaseInstance;
|
||||||
|
|
||||||
|
public DataLoader(JCWDatabase databaseInstance, Path dataFile, Path metadataFile, boolean registrationRequired) throws IOException {
|
||||||
|
synchronized (indicesAccessLock) {
|
||||||
|
this.databaseInstance = databaseInstance;
|
||||||
|
this.indices = new FileIndexManager(dataFile, metadataFile);
|
||||||
|
if (!indices.has(0)) {
|
||||||
|
allocateNullValue();
|
||||||
|
}
|
||||||
|
this.objectIndicesManager = new DBObjectIndicesManager(this.indices);
|
||||||
|
kryo.setRegistrationRequired(registrationRequired);
|
||||||
|
registerDefaultClasses();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void registerDefaultClasses() {
|
||||||
|
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++);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close() throws IOException {
|
||||||
|
synchronized (indicesAccessLock) {
|
||||||
|
if (!closed) {
|
||||||
|
closed = true;
|
||||||
|
indices.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void preloadDBObject(DBObject obj, DBObjectIndicesManager.DBObjectInfo objectInfo) throws IOException {
|
||||||
|
synchronized (indicesAccessLock) {
|
||||||
|
preloadDBObjectFields(obj, objectInfo.getFields());
|
||||||
|
preloadDBObjectProperties(obj, objectInfo.getProperties());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
<T extends DBObject> T loadRoot(Class<T> rootType, SupplierWithIO<T> ifAbsent) throws IOException {
|
||||||
|
synchronized (indicesAccessLock) {
|
||||||
|
if (isDBObjectNull(0)) {
|
||||||
|
return ifAbsent.getWithIO();
|
||||||
|
} else {
|
||||||
|
return loadDBObject(rootType, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T extends DBObject> T instantiateDBObject(Class<T> type) throws IOException {
|
||||||
|
synchronized (indicesAccessLock) {
|
||||||
|
try {
|
||||||
|
T obj = type.getConstructor().newInstance();
|
||||||
|
obj.database = databaseInstance;
|
||||||
|
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 void preloadDBObjectFields(DBObject obj, long[] fieldUIDs) throws IOException {
|
||||||
|
synchronized (indicesAccessLock) {
|
||||||
|
// 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, fieldUIDs[fieldId]);
|
||||||
|
fields[fieldId] = field;
|
||||||
|
orderedFieldTypes[fieldId] = fieldType;
|
||||||
|
}
|
||||||
|
// Set fields metadata
|
||||||
|
obj.setFields(fields, orderedFieldTypes, fieldUIDs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void preloadDBObjectProperties(DBObject obj, long[] propertyUIDs) {
|
||||||
|
synchronized (indicesAccessLock) {
|
||||||
|
// 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<String, DBPropertySetter> setterMethods = new LinkedHashMap<>();
|
||||||
|
Map<String, DBPropertyGetter> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected Field[] getFields(DBObject obj) {
|
||||||
|
synchronized (indicesAccessLock) {
|
||||||
|
return FieldUtils.getFieldsWithAnnotation(obj.getClass(), DBField.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int getBiggestPropertyGetterId(Method[] unorderedPropertyGetters) {
|
||||||
|
synchronized (indicesAccessLock) {
|
||||||
|
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) {
|
||||||
|
synchronized (indicesAccessLock) {
|
||||||
|
int biggestPropertyId = -1;
|
||||||
|
for (Method property : unorderedPropertySetters) {
|
||||||
|
DBPropertySetter fieldAnnotation = property.getAnnotation(DBPropertySetter.class);
|
||||||
|
int propertyId = fieldAnnotation.id();
|
||||||
|
if (propertyId > biggestPropertyId) {
|
||||||
|
biggestPropertyId = propertyId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return biggestPropertyId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected int getBiggestFieldId(Field[] unorderedFields) {
|
||||||
|
synchronized (indicesAccessLock) {
|
||||||
|
int biggestFieldId = -1;
|
||||||
|
for (Field field : unorderedFields) {
|
||||||
|
DBField fieldAnnotation = field.getAnnotation(DBField.class);
|
||||||
|
int propertyId = fieldAnnotation.id();
|
||||||
|
if (propertyId > biggestFieldId) {
|
||||||
|
biggestFieldId = propertyId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return biggestFieldId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loadProperty(DBObject obj, int propertyId, Method property, DBDataType propertyType, long propertyUID) throws IOException {
|
||||||
|
synchronized (indicesAccessLock) {
|
||||||
|
loadData(propertyType, propertyUID, property::getReturnType, (data) -> {
|
||||||
|
synchronized (indicesAccessLock) {
|
||||||
|
obj.setLoadedProperty(propertyId, data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loadField(DBObject obj, Field field, DBDataType fieldType, long fieldUID) throws IOException {
|
||||||
|
synchronized (indicesAccessLock) {
|
||||||
|
loadData(fieldType, fieldUID, field::getType, (data) -> {
|
||||||
|
synchronized (indicesAccessLock) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private void loadData(DBDataType propertyType, long dataUID, Supplier<Class<?>> returnType, ConsumerWithIO result) throws IOException {
|
||||||
|
synchronized (indicesAccessLock) {
|
||||||
|
switch (propertyType) {
|
||||||
|
case DATABASE_OBJECT:
|
||||||
|
DBObject fieldDBObjectValue = loadDBObject((Class<? extends DBObject>) returnType.get(), dataUID);
|
||||||
|
//System.err.println("Loading data DBObj " + dataUID + ":" + fieldDBObjectValue);
|
||||||
|
result.accept(fieldDBObjectValue);
|
||||||
|
return;
|
||||||
|
case OBJECT:
|
||||||
|
Object fieldObjectValue = loadObject(dataUID);
|
||||||
|
//System.err.println("Loading data Obj " + dataUID + ":" + fieldObjectValue);
|
||||||
|
result.accept(fieldObjectValue);
|
||||||
|
return;
|
||||||
|
case UID_LIST:
|
||||||
|
LongArrayList fieldListObjectValue = loadListObject(dataUID);
|
||||||
|
//System.err.println("Loading data LOb " + dataUID + ":" + fieldListObjectValue);
|
||||||
|
result.accept(fieldListObjectValue);
|
||||||
|
return;
|
||||||
|
case BOOLEAN:
|
||||||
|
boolean fieldBooleanValue = loadBoolean(dataUID);
|
||||||
|
//System.err.println("Loading data Boo " + dataUID + ":" + fieldBooleanValue);
|
||||||
|
result.accept(fieldBooleanValue);
|
||||||
|
return;
|
||||||
|
case BYTE:
|
||||||
|
byte fieldByteValue = loadByte(dataUID);
|
||||||
|
//System.err.println("Loading data Byt " + dataUID + ":" + fieldByteValue);
|
||||||
|
result.accept(fieldByteValue);
|
||||||
|
return;
|
||||||
|
case SHORT:
|
||||||
|
short fieldShortValue = loadShort(dataUID);
|
||||||
|
//System.err.println("Loading data Shr " + dataUID + ":" + fieldShortValue);
|
||||||
|
result.accept(fieldShortValue);
|
||||||
|
return;
|
||||||
|
case CHAR:
|
||||||
|
char fieldCharValue = loadChar(dataUID);
|
||||||
|
//System.err.println("Loading data Chr " + dataUID + ":" + fieldCharValue);
|
||||||
|
result.accept(fieldCharValue);
|
||||||
|
return;
|
||||||
|
case INTEGER:
|
||||||
|
int fieldIntValue = loadInt(dataUID);
|
||||||
|
//System.err.println("Loading data Int " + dataUID + ":" + fieldIntValue);
|
||||||
|
result.accept(fieldIntValue);
|
||||||
|
return;
|
||||||
|
case LONG:
|
||||||
|
long fieldLongValue = loadLong(dataUID);
|
||||||
|
//System.err.println("Loading data Lng " + dataUID + ":" + fieldLongValue);
|
||||||
|
result.accept(fieldLongValue);
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
throw new NullPointerException("Unknown data type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public <T extends DBObject> T loadDBObject(Class<T> type, long propertyUID) throws IOException {
|
||||||
|
synchronized (indicesAccessLock) {
|
||||||
|
DBObjectIndicesManager.DBObjectInfo objectInfo = readUIDs(propertyUID);
|
||||||
|
if (objectInfo == null) return null;
|
||||||
|
T obj = instantiateDBObject(type);
|
||||||
|
preloadDBObject(obj, objectInfo);
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isDBObjectNull(long uid) {
|
||||||
|
synchronized (indicesAccessLock) {
|
||||||
|
try {
|
||||||
|
return !objectIndicesManager.has(uid) || objectIndicesManager.get(uid) == null;
|
||||||
|
} catch (IOException ex) {
|
||||||
|
throw new IOError(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T> T loadObject(long uid) throws IOException {
|
||||||
|
synchronized (indicesAccessLock) {
|
||||||
|
return indices.get(uid, (i, size) -> {
|
||||||
|
synchronized (indicesAccessLock) {
|
||||||
|
if (size != 0) {
|
||||||
|
return (T) kryo.readClassAndObject(i);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private LongArrayList loadListObject(long uid) throws IOException {
|
||||||
|
synchronized (indicesAccessLock) {
|
||||||
|
return indices.get(uid, (i, size) -> {
|
||||||
|
synchronized (indicesAccessLock) {
|
||||||
|
if (size != 0) {
|
||||||
|
LongArrayList list = new LongArrayList();
|
||||||
|
int listSize = i.readVarInt(true);
|
||||||
|
for (int li = 0; li < listSize; li++) {
|
||||||
|
list.add(i.readVarLong(true));
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean loadBoolean(long uid) throws IOException {
|
||||||
|
synchronized (indicesAccessLock) {
|
||||||
|
return indices.get(uid, (i, size) -> {
|
||||||
|
synchronized (indicesAccessLock) {
|
||||||
|
if (size != 0) {
|
||||||
|
return i.readBoolean();
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte loadByte(long uid) throws IOException {
|
||||||
|
synchronized (indicesAccessLock) {
|
||||||
|
return indices.get(uid, (i, size) -> {
|
||||||
|
synchronized (indicesAccessLock) {
|
||||||
|
if (size != 0) {
|
||||||
|
return i.readByte();
|
||||||
|
} else {
|
||||||
|
return (byte) 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public short loadShort(long uid) throws IOException {
|
||||||
|
synchronized (indicesAccessLock) {
|
||||||
|
return indices.get(uid, (i, size) -> {
|
||||||
|
synchronized (indicesAccessLock) {
|
||||||
|
if (size != 0) {
|
||||||
|
return i.readShort();
|
||||||
|
} else {
|
||||||
|
return (short) 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public char loadChar(long uid) throws IOException {
|
||||||
|
synchronized (indicesAccessLock) {
|
||||||
|
return indices.get(uid, (i, size) -> {
|
||||||
|
synchronized (indicesAccessLock) {
|
||||||
|
if (size != 0) {
|
||||||
|
return i.readChar();
|
||||||
|
} else {
|
||||||
|
return (char) 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public int loadInt(long uid) throws IOException {
|
||||||
|
synchronized (indicesAccessLock) {
|
||||||
|
return indices.get(uid, (i, size) -> {
|
||||||
|
if (size != 0) {
|
||||||
|
return i.readInt();
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public long loadLong(long uid) throws IOException {
|
||||||
|
synchronized (indicesAccessLock) {
|
||||||
|
return indices.get(uid, (i, size) -> {
|
||||||
|
synchronized (indicesAccessLock) {
|
||||||
|
if (size != 0) {
|
||||||
|
return i.readLong();
|
||||||
|
} else {
|
||||||
|
return 0L;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public boolean exists(long uid) {
|
||||||
|
synchronized (indicesAccessLock) {
|
||||||
|
return objectIndicesManager.has(uid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void writeObjectInfo(long uid, long[] fieldUIDs, long[] propertyUIDs) throws IOException {
|
||||||
|
synchronized (indicesAccessLock) {
|
||||||
|
//System.err.println("Saving obj. " + uid);
|
||||||
|
this.objectIndicesManager.set(uid, fieldUIDs, propertyUIDs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeObjectInfoNull(long uid) throws IOException {
|
||||||
|
synchronized (indicesAccessLock) {
|
||||||
|
this.objectIndicesManager.setNull(uid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param uid
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public DBObjectIndicesManager.DBObjectInfo readUIDs(long uid) {
|
||||||
|
synchronized (indicesAccessLock) {
|
||||||
|
try {
|
||||||
|
return objectIndicesManager.get(uid);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new IOError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public <T> void writeObjectProperty(long uid, DBDataType propertyType, T loadedPropertyValue) throws IOException {
|
||||||
|
synchronized (indicesAccessLock) {
|
||||||
|
switch (propertyType) {
|
||||||
|
case BOOLEAN:
|
||||||
|
indices.set(uid, 1, (o) -> {
|
||||||
|
synchronized (indicesAccessLock) {
|
||||||
|
o.writeBoolean(loadedPropertyValue == null ? false : (boolean) loadedPropertyValue);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
//System.err.println("Saving data Boo " + uid + ":" + loadedPropertyValue);
|
||||||
|
break;
|
||||||
|
case BYTE:
|
||||||
|
indices.set(uid, Byte.BYTES, (o) -> {
|
||||||
|
synchronized (indicesAccessLock) {
|
||||||
|
o.writeByte(loadedPropertyValue == null ? 0 : (byte) loadedPropertyValue);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
//System.err.println("Saving data Byt " + uid + ":" + loadedPropertyValue);
|
||||||
|
break;
|
||||||
|
case SHORT:
|
||||||
|
indices.set(uid, Short.BYTES, (o) -> {
|
||||||
|
synchronized (indicesAccessLock) {
|
||||||
|
o.writeShort(loadedPropertyValue == null ? 0 : (short) loadedPropertyValue);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
//System.err.println("Saving data Shr " + uid + ":" + loadedPropertyValue);
|
||||||
|
break;
|
||||||
|
case CHAR:
|
||||||
|
indices.set(uid, Character.BYTES, (o) -> {
|
||||||
|
synchronized (indicesAccessLock) {
|
||||||
|
o.writeChar(loadedPropertyValue == null ? 0 : (char) loadedPropertyValue);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
//System.err.println("Saving data Chr " + uid + ":" + loadedPropertyValue);
|
||||||
|
break;
|
||||||
|
case INTEGER:
|
||||||
|
indices.set(uid, Integer.BYTES, (o) -> {
|
||||||
|
synchronized (indicesAccessLock) {
|
||||||
|
o.writeInt(loadedPropertyValue == null ? 0 : (int) loadedPropertyValue);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
//System.err.println("Saving data Int " + uid + ":" + loadedPropertyValue);
|
||||||
|
break;
|
||||||
|
case LONG:
|
||||||
|
indices.set(uid, Long.BYTES, (o) -> {
|
||||||
|
synchronized (indicesAccessLock) {
|
||||||
|
o.writeLong(loadedPropertyValue == null ? 0 : (long) loadedPropertyValue);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
//System.err.println("Saving data Lng " + uid + ":" + loadedPropertyValue);
|
||||||
|
break;
|
||||||
|
case OBJECT:
|
||||||
|
Output baosOutput = new Output(new ByteArrayOutputStream());
|
||||||
|
kryo.writeClassAndObject(baosOutput, loadedPropertyValue);
|
||||||
|
//System.err.println("Saving data Obj " + uid + ":" + loadedPropertyValue);
|
||||||
|
if (loadedPropertyValue instanceof Class) {
|
||||||
|
System.out.println();
|
||||||
|
}
|
||||||
|
byte[] out = baosOutput.toBytes();
|
||||||
|
indices.set(uid, out.length, o -> {
|
||||||
|
synchronized (indicesAccessLock) {
|
||||||
|
o.write(out, 0, out.length);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case UID_LIST:
|
||||||
|
if (loadedPropertyValue == null) {
|
||||||
|
indices.set(uid, 0, (o) -> {
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
LongArrayList list = (LongArrayList) loadedPropertyValue;
|
||||||
|
final int listSize = list.size();
|
||||||
|
Output baosListOutput = new Output(Long.BYTES * 100, Long.BYTES * (listSize > 100 ? listSize : 100));
|
||||||
|
baosListOutput.writeVarInt(listSize, true);
|
||||||
|
for (int i = 0; i < listSize; i++) {
|
||||||
|
baosListOutput.writeVarLong(list.getLong(i), true);
|
||||||
|
}
|
||||||
|
//System.err.println("Saving data LOb " + uid + ":" + loadedPropertyValue);
|
||||||
|
byte[] outList = baosListOutput.toBytes();
|
||||||
|
indices.set(uid, outList.length, o -> {
|
||||||
|
synchronized (indicesAccessLock) {
|
||||||
|
o.write(outList, 0, outList.length);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DATABASE_OBJECT:
|
||||||
|
//System.err.println("Saving data DBObj " + uid + ":" + loadedPropertyValue);
|
||||||
|
if (loadedPropertyValue == null) {
|
||||||
|
writeObjectInfoNull(uid);
|
||||||
|
} else {
|
||||||
|
((DBObject) loadedPropertyValue).writeToDisk(uid);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerClass(Class<?> clazz, int id) {
|
||||||
|
synchronized (indicesAccessLock) {
|
||||||
|
kryo.register(clazz, 100 + id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public long allocateNullValue() {
|
||||||
|
synchronized (indicesAccessLock) {
|
||||||
|
return indices.add(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public long[] allocateNewUIDs(int quantity) {
|
||||||
|
synchronized (indicesAccessLock) {
|
||||||
|
long[] ids = new long[quantity];
|
||||||
|
for (int i = 0; i < quantity; i++) {
|
||||||
|
ids[i] = allocateNullValue();
|
||||||
|
}
|
||||||
|
return ids;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,50 +1,21 @@
|
|||||||
package org.warp.jcwdb.ann;
|
package org.warp.jcwdb.ann;
|
||||||
|
|
||||||
import com.esotericsoftware.kryo.Kryo;
|
|
||||||
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.apache.commons.lang3.reflect.MethodUtils;
|
|
||||||
import org.warp.jcwdb.Cleanable;
|
import org.warp.jcwdb.Cleanable;
|
||||||
import org.warp.jcwdb.Cleaner;
|
import org.warp.jcwdb.Cleaner;
|
||||||
import org.warp.jcwdb.FileIndexManager;
|
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.IOError;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
public class DatabaseManager implements Cleanable {
|
public class DatabaseManager implements Cleanable {
|
||||||
|
|
||||||
public static final long MAX_LOADED_INDICES = 100000;
|
public static final long MAX_LOADED_INDICES = 100000;
|
||||||
private final DBObjectIndicesManager objectIndicesManager;
|
|
||||||
private final FileIndexManager indices;
|
|
||||||
private final Cleaner cleaner;
|
private final Cleaner cleaner;
|
||||||
private final JCWDatabase jcwDatabase;
|
private final DataLoader dataLoader;
|
||||||
private DBObject loadedRootObject = null;
|
private DBObject loadedRootObject = null;
|
||||||
private final Kryo kryo = new Kryo();
|
|
||||||
private volatile boolean closed;
|
private volatile boolean closed;
|
||||||
|
|
||||||
DatabaseManager(JCWDatabase jcwDatabase, Path dataFile, Path metadataFile, boolean registrationRequired) throws IOException {
|
DatabaseManager(DataLoader dataLoader) {
|
||||||
this.jcwDatabase = jcwDatabase;
|
this.dataLoader = dataLoader;
|
||||||
kryo.setRegistrationRequired(registrationRequired);
|
|
||||||
registerDefaultClasses();
|
|
||||||
this.indices = new FileIndexManager(dataFile, metadataFile);
|
|
||||||
if (!indices.has(0)) {
|
|
||||||
allocateNullValue();
|
|
||||||
}
|
|
||||||
this.objectIndicesManager = new DBObjectIndicesManager(this.indices);
|
|
||||||
this.cleaner = new Cleaner(this);
|
this.cleaner = new Cleaner(this);
|
||||||
this.cleaner.start();
|
this.cleaner.start();
|
||||||
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
||||||
@ -56,79 +27,13 @@ public class DatabaseManager implements Cleanable {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void registerDefaultClasses() {
|
public <T extends DBObject> T loadRoot(Class<T> rootType, SupplierWithIO<T> ifAbsent) throws IOException {
|
||||||
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++);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public <T extends DBObject> T loadRoot(Class<T> rootType) throws IOException {
|
|
||||||
if (loadedRootObject != null) {
|
if (loadedRootObject != null) {
|
||||||
throw new RuntimeException("Root already set!");
|
throw new RuntimeException("Root already set!");
|
||||||
}
|
}
|
||||||
if (isDBObjectNull(0)) {
|
T root = dataLoader.loadRoot(rootType, ifAbsent);
|
||||||
try {
|
|
||||||
T root = rootType.getConstructor(JCWDatabase.class).newInstance(this.jcwDatabase);
|
|
||||||
loadedRootObject = root;
|
loadedRootObject = root;
|
||||||
return root;
|
return root;
|
||||||
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
|
|
||||||
throw new IOError(e);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
T root = (T) loadDBObject(rootType, 0);
|
|
||||||
loadedRootObject = root;
|
|
||||||
return root;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
@ -138,367 +43,12 @@ public class DatabaseManager implements Cleanable {
|
|||||||
if (loadedRootObject != null) {
|
if (loadedRootObject != null) {
|
||||||
loadedRootObject.writeToDisk(0);
|
loadedRootObject.writeToDisk(0);
|
||||||
}
|
}
|
||||||
indices.close();
|
dataLoader.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void preloadDBObject(DBObject obj, DBObjectIndicesManager.DBObjectInfo objectInfo) throws IOException {
|
|
||||||
preloadDBObjectFields(obj, objectInfo.getFields());
|
|
||||||
preloadDBObjectProperties(obj, objectInfo.getProperties());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void preloadDBObjectFields(DBObject obj, long[] fieldUIDs) 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, fieldUIDs[fieldId]);
|
|
||||||
fields[fieldId] = field;
|
|
||||||
orderedFieldTypes[fieldId] = fieldType;
|
|
||||||
}
|
|
||||||
// Set fields metadata
|
|
||||||
obj.setFields(fields, orderedFieldTypes, fieldUIDs);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void preloadDBObjectProperties(DBObject obj, long[] propertyUIDs) {
|
|
||||||
// Declare the variables needed to get the biggest property Id
|
|
||||||
Method[] unorderedPropertyGetters = getPropertyGetters(obj);
|
|
||||||
Method[] unorderedPropertySetters = getPropertySetters(obj);
|
|
||||||
|
|
||||||
// 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<String, DBPropertySetter> setterMethods = new LinkedHashMap<>();
|
|
||||||
Map<String, DBPropertyGetter> 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
Method[] getPropertyGetters(DBObject obj) {
|
|
||||||
return MethodUtils.getMethodsWithAnnotation(obj.getClass(), DBPropertyGetter.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
Method[] getPropertySetters(DBObject obj) {
|
|
||||||
return MethodUtils.getMethodsWithAnnotation(obj.getClass(), DBPropertySetter.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Field[] getFields(DBObject obj) {
|
|
||||||
return FieldUtils.getFieldsWithAnnotation(obj.getClass(), DBField.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
protected 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void loadProperty(DBObject obj, int propertyId, Method property, DBDataType propertyType, long propertyUID) throws IOException {
|
|
||||||
loadData(propertyType, propertyUID, property::getReturnType, (data) -> obj.setLoadedProperty(propertyId, data));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void loadField(DBObject obj, Field field, DBDataType fieldType, long fieldUID) throws IOException {
|
|
||||||
loadData(fieldType, fieldUID, field::getType, (data) -> {
|
|
||||||
try {
|
|
||||||
FieldUtils.writeField(field, obj, data, true);
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
private void loadData(DBDataType propertyType, long dataUID, Supplier<Class<?>> returnType, Consumer result) throws IOException {
|
|
||||||
switch (propertyType) {
|
|
||||||
case DATABASE_OBJECT:
|
|
||||||
DBObject fieldDBObjectValue = loadDBObject((Class<? extends DBObject>) returnType.get(), dataUID);
|
|
||||||
//System.err.println("Loading data DBObj " + dataUID + ":" + fieldDBObjectValue);
|
|
||||||
result.accept(fieldDBObjectValue);
|
|
||||||
return;
|
|
||||||
case OBJECT:
|
|
||||||
Object fieldObjectValue = loadObject(dataUID);
|
|
||||||
//System.err.println("Loading data Obj " + dataUID + ":" + fieldObjectValue);
|
|
||||||
result.accept(fieldObjectValue);
|
|
||||||
return;
|
|
||||||
case UID_LIST:
|
|
||||||
LongArrayList fieldListObjectValue = loadListObject(dataUID);
|
|
||||||
//System.err.println("Loading data LOb " + dataUID + ":" + fieldListObjectValue);
|
|
||||||
result.accept(fieldListObjectValue);
|
|
||||||
return;
|
|
||||||
case BOOLEAN:
|
|
||||||
boolean fieldBooleanValue = loadBoolean(dataUID);
|
|
||||||
//System.err.println("Loading data Boo " + dataUID + ":" + fieldBooleanValue);
|
|
||||||
result.accept(fieldBooleanValue);
|
|
||||||
return;
|
|
||||||
case BYTE:
|
|
||||||
byte fieldByteValue = loadByte(dataUID);
|
|
||||||
//System.err.println("Loading data Byt " + dataUID + ":" + fieldByteValue);
|
|
||||||
result.accept(fieldByteValue);
|
|
||||||
return;
|
|
||||||
case SHORT:
|
|
||||||
short fieldShortValue = loadShort(dataUID);
|
|
||||||
//System.err.println("Loading data Shr " + dataUID + ":" + fieldShortValue);
|
|
||||||
result.accept(fieldShortValue);
|
|
||||||
return;
|
|
||||||
case CHAR:
|
|
||||||
char fieldCharValue = loadChar(dataUID);
|
|
||||||
//System.err.println("Loading data Chr " + dataUID + ":" + fieldCharValue);
|
|
||||||
result.accept(fieldCharValue);
|
|
||||||
return;
|
|
||||||
case INTEGER:
|
|
||||||
int fieldIntValue = loadInt(dataUID);
|
|
||||||
//System.err.println("Loading data Int " + dataUID + ":" + fieldIntValue);
|
|
||||||
result.accept(fieldIntValue);
|
|
||||||
return;
|
|
||||||
case LONG:
|
|
||||||
long fieldLongValue = loadLong(dataUID);
|
|
||||||
//System.err.println("Loading data Lng " + dataUID + ":" + fieldLongValue);
|
|
||||||
result.accept(fieldLongValue);
|
|
||||||
return;
|
|
||||||
default:
|
|
||||||
throw new NullPointerException("Unknown data type");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T extends DBObject> T loadDBObject(Class<T> type, long propertyUID) throws IOException {
|
|
||||||
try {
|
|
||||||
DBObjectIndicesManager.DBObjectInfo objectInfo = readUIDs(propertyUID);
|
|
||||||
if (objectInfo == null) return null;
|
|
||||||
return type.getDeclaredConstructor(JCWDatabase.class, DBObjectIndicesManager.DBObjectInfo.class).newInstance(jcwDatabase, objectInfo);
|
|
||||||
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
|
|
||||||
throw new IOException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isDBObjectNull(long uid) {
|
|
||||||
try {
|
|
||||||
return !objectIndicesManager.has(uid) || objectIndicesManager.get(uid) == null;
|
|
||||||
} catch (IOException ex) {
|
|
||||||
throw new IOError(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public <T> T loadObject(long uid) throws IOException {
|
|
||||||
return indices.get(uid, (i, size) -> size == 0 ? null : (T) kryo.readClassAndObject(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
private LongArrayList loadListObject(long uid) throws IOException {
|
|
||||||
return indices.get(uid, (i, size) -> {
|
|
||||||
if (size == 0) return null;
|
|
||||||
LongArrayList list = new LongArrayList();
|
|
||||||
int listSize = i.readVarInt(true);
|
|
||||||
for (int li = 0; li < listSize; li++) {
|
|
||||||
list.add(i.readVarLong(true));
|
|
||||||
}
|
|
||||||
return list;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean loadBoolean(long uid) throws IOException {
|
|
||||||
return indices.get(uid, (i, size) -> size != 0 && i.readBoolean());
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte loadByte(long uid) throws IOException {
|
|
||||||
return indices.get(uid, (i, size) -> size == 0 ? 0 : i.readByte());
|
|
||||||
}
|
|
||||||
|
|
||||||
public short loadShort(long uid) throws IOException {
|
|
||||||
return indices.get(uid, (i, size) -> size == 0 ? 0 : i.readShort());
|
|
||||||
}
|
|
||||||
|
|
||||||
public char loadChar(long uid) throws IOException {
|
|
||||||
return indices.get(uid, (i, size) -> size == 0 ? 0 : i.readChar());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public int loadInt(long uid) throws IOException {
|
|
||||||
return indices.get(uid, (i, size) -> size == 0 ? 0 : i.readInt());
|
|
||||||
}
|
|
||||||
|
|
||||||
public long loadLong(long uid) throws IOException {
|
|
||||||
return indices.get(uid, (i, size) -> size == 0 ? 0 : i.readLong());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param uid
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public DBObjectIndicesManager.DBObjectInfo readUIDs(long uid) {
|
|
||||||
try {
|
|
||||||
return objectIndicesManager.get(uid);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new IOError(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean exists(long uid) {
|
|
||||||
return objectIndicesManager.has(uid);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void writeObjectInfo(long uid, long[] fieldUIDs, long[] propertyUIDs) throws IOException {
|
|
||||||
//System.err.println("Saving obj. " + uid);
|
|
||||||
this.objectIndicesManager.set(uid, fieldUIDs, propertyUIDs);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void writeObjectInfoNull(long uid) throws IOException {
|
|
||||||
this.objectIndicesManager.setNull(uid);
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T> void writeObjectProperty(long uid, DBDataType propertyType, T loadedPropertyValue) throws IOException {
|
|
||||||
switch (propertyType) {
|
|
||||||
case BOOLEAN:
|
|
||||||
indices.set(uid, 1, (o) -> o.writeBoolean(loadedPropertyValue == null ? false : (boolean) loadedPropertyValue));
|
|
||||||
//System.err.println("Saving data Boo " + uid + ":" + loadedPropertyValue);
|
|
||||||
break;
|
|
||||||
case BYTE:
|
|
||||||
indices.set(uid, Byte.BYTES, (o) -> o.writeByte(loadedPropertyValue == null ? 0 : (byte) loadedPropertyValue));
|
|
||||||
//System.err.println("Saving data Byt " + uid + ":" + loadedPropertyValue);
|
|
||||||
break;
|
|
||||||
case SHORT:
|
|
||||||
indices.set(uid, Short.BYTES, (o) -> o.writeShort(loadedPropertyValue == null ? 0 : (short) loadedPropertyValue));
|
|
||||||
//System.err.println("Saving data Shr " + uid + ":" + loadedPropertyValue);
|
|
||||||
break;
|
|
||||||
case CHAR:
|
|
||||||
indices.set(uid, Character.BYTES, (o) -> o.writeChar(loadedPropertyValue == null ? 0 : (char) loadedPropertyValue));
|
|
||||||
//System.err.println("Saving data Chr " + uid + ":" + loadedPropertyValue);
|
|
||||||
break;
|
|
||||||
case INTEGER:
|
|
||||||
indices.set(uid, Integer.BYTES, (o) -> o.writeInt(loadedPropertyValue == null ? 0 : (int) loadedPropertyValue));
|
|
||||||
//System.err.println("Saving data Int " + uid + ":" + loadedPropertyValue);
|
|
||||||
break;
|
|
||||||
case LONG:
|
|
||||||
indices.set(uid, Long.BYTES, (o) -> o.writeLong(loadedPropertyValue == null ? 0 : (long) loadedPropertyValue));
|
|
||||||
//System.err.println("Saving data Lng " + uid + ":" + loadedPropertyValue);
|
|
||||||
break;
|
|
||||||
case OBJECT:
|
|
||||||
Output baosOutput = new Output(new ByteArrayOutputStream());
|
|
||||||
kryo.writeClassAndObject(baosOutput, loadedPropertyValue);
|
|
||||||
//System.err.println("Saving data Obj " + uid + ":" + loadedPropertyValue);
|
|
||||||
if (loadedPropertyValue instanceof Class) {
|
|
||||||
System.out.println();
|
|
||||||
}
|
|
||||||
byte[] out = baosOutput.toBytes();
|
|
||||||
indices.set(uid, out.length, o -> o.write(out, 0, out.length));
|
|
||||||
break;
|
|
||||||
case UID_LIST:
|
|
||||||
if (loadedPropertyValue == null) {
|
|
||||||
indices.set(uid, 0, (o) -> {});
|
|
||||||
} else {
|
|
||||||
LongArrayList list = (LongArrayList) loadedPropertyValue;
|
|
||||||
final int listSize = list.size();
|
|
||||||
Output baosListOutput = new Output(Long.BYTES * 100, Long.BYTES * (listSize > 100 ? listSize : 100));
|
|
||||||
baosListOutput.writeVarInt(listSize, true);
|
|
||||||
for (int i = 0; i < listSize; i++) {
|
|
||||||
baosListOutput.writeVarLong(list.getLong(i), true);
|
|
||||||
}
|
|
||||||
//System.err.println("Saving data LOb " + uid + ":" + loadedPropertyValue);
|
|
||||||
byte[] outList = baosListOutput.toBytes();
|
|
||||||
indices.set(uid, outList.length, o -> o.write(outList, 0, outList.length));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case DATABASE_OBJECT:
|
|
||||||
//System.err.println("Saving data DBObj " + uid + ":" + loadedPropertyValue);
|
|
||||||
if (loadedPropertyValue == null) {
|
|
||||||
writeObjectInfoNull(uid);
|
|
||||||
} else {
|
|
||||||
((DBObject) loadedPropertyValue).writeToDisk(uid);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void registerClass(Class<?> clazz, int id) {
|
|
||||||
kryo.register(clazz, 100 + id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public long allocateNullValue() {
|
|
||||||
return indices.add(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long clean() {
|
public long clean() {
|
||||||
return 0;//indices.clean();
|
return 0;//indices.clean();
|
||||||
}
|
}
|
||||||
|
|
||||||
public long[] allocateNewUIDs(int quantity) {
|
|
||||||
long[] ids = new long[quantity];
|
|
||||||
for (int i = 0; i < quantity; i++) {
|
|
||||||
ids[i] = allocateNullValue();
|
|
||||||
}
|
|
||||||
return ids;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,35 +1,55 @@
|
|||||||
package org.warp.jcwdb.ann;
|
package org.warp.jcwdb.ann;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
public class JCWDatabase {
|
public class JCWDatabase {
|
||||||
private final DatabaseManager database;
|
private final DatabaseManager database;
|
||||||
|
private final DataLoader dataLoader;
|
||||||
|
private final DataInitializer dataInitializer;
|
||||||
|
|
||||||
public JCWDatabase(Path dataFile, Path metadataFile, boolean registrationRequired) throws IOException {
|
public JCWDatabase(Path dataFile, Path metadataFile, boolean registrationRequired) throws IOException {
|
||||||
this.database = new DatabaseManager(this, dataFile, metadataFile, registrationRequired);
|
this.dataLoader = new DataLoader(this, dataFile, metadataFile, registrationRequired);
|
||||||
|
this.dataInitializer = new DataInitializer(dataLoader);
|
||||||
|
this.database = new DatabaseManager(dataLoader);
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T extends DBObject> T loadRoot(Class<T> rootClass) {
|
public <T extends DBObject> T loadRoot(Class<T> rootClass) throws IOException {
|
||||||
|
return loadRoot(rootClass, () -> {
|
||||||
try {
|
try {
|
||||||
return database.loadRoot(rootClass);
|
return rootClass.getConstructor(JCWDatabase.class).newInstance(this);
|
||||||
} catch (IOException e) {
|
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
|
||||||
throw new RuntimeException(e);
|
throw new IOException(e);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
DatabaseManager getDatabaseManager() {
|
public <T extends DBObject> T loadRoot(Class<T> rootClass, SupplierWithIO<T> ifAbsent) throws IOException {
|
||||||
return database;
|
return database.loadRoot(rootClass, ifAbsent);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void registerClass(Class<?> clazz, int id) {
|
public void registerClass(Class<?> clazz, int id) {
|
||||||
if (id < 0) {
|
if (id < 0) {
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
}
|
}
|
||||||
database.registerClass(clazz, id);
|
dataLoader.registerClass(clazz, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
database.close();
|
database.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DataLoader getDataLoader() {
|
||||||
|
return dataLoader;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DataInitializer getDataInitializer() {
|
||||||
|
return dataInitializer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void initializeDBObject(DBObject dbObject) throws IOException {
|
||||||
|
dataInitializer.initializeDBObject(dbObject);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
19
src/main/java/org/warp/jcwdb/ann/RunnableWithIO.java
Normal file
19
src/main/java/org/warp/jcwdb/ann/RunnableWithIO.java
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package org.warp.jcwdb.ann;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface RunnableWithIO {
|
||||||
|
/**
|
||||||
|
* When an object implementing interface <code>Runnable</code> is used
|
||||||
|
* to create a thread, starting the thread causes the object's
|
||||||
|
* <code>run</code> method to be called in that separately executing
|
||||||
|
* thread.
|
||||||
|
* <p>
|
||||||
|
* The general contract of the method <code>run</code> is that it may
|
||||||
|
* take any action whatsoever.
|
||||||
|
*
|
||||||
|
* @see java.lang.Thread#run()
|
||||||
|
*/
|
||||||
|
public abstract void run() throws IOException;
|
||||||
|
}
|
8
src/main/java/org/warp/jcwdb/ann/SupplierWithIO.java
Normal file
8
src/main/java/org/warp/jcwdb/ann/SupplierWithIO.java
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package org.warp.jcwdb.ann;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface SupplierWithIO<T> {
|
||||||
|
public T getWithIO() throws IOException;
|
||||||
|
}
|
@ -20,6 +20,7 @@ public class DBDBObjectListTests {
|
|||||||
db.get().registerClass(TestUtils.class, 0);
|
db.get().registerClass(TestUtils.class, 0);
|
||||||
db.get().registerClass(TestUtils.RootClass.class, 1);
|
db.get().registerClass(TestUtils.RootClass.class, 1);
|
||||||
db.get().registerClass(Class.class, 2);
|
db.get().registerClass(Class.class, 2);
|
||||||
|
System.out.println("Loading root");
|
||||||
root = db.get().loadRoot(RootWithList.class);
|
root = db.get().loadRoot(RootWithList.class);
|
||||||
});
|
});
|
||||||
root.list = new DBDBObjectList<>(db.get(), TestUtils.RootClass.class);
|
root.list = new DBDBObjectList<>(db.get(), TestUtils.RootClass.class);
|
||||||
@ -37,7 +38,7 @@ public class DBDBObjectListTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void shouldMatchList() {
|
public void shouldMatchList() throws IOException {
|
||||||
checkEmptyList();
|
checkEmptyList();
|
||||||
assertEquals(200, root.list.size());
|
assertEquals(200, root.list.size());
|
||||||
for (int i = 0; i < 200; i++) {
|
for (int i = 0; i < 200; i++) {
|
||||||
@ -45,7 +46,7 @@ public class DBDBObjectListTests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkEmptyList() {
|
private void checkEmptyList() throws IOException {
|
||||||
DBObjectList<String> list = new DBObjectList<>(db.get());
|
DBObjectList<String> list = new DBObjectList<>(db.get());
|
||||||
assertEquals(null, list.getLast());
|
assertEquals(null, list.getLast());
|
||||||
assertEquals(0, list.size());
|
assertEquals(0, list.size());
|
||||||
@ -63,15 +64,20 @@ public class DBDBObjectListTests {
|
|||||||
|
|
||||||
public static class RootWithList extends DBObject {
|
public static class RootWithList extends DBObject {
|
||||||
|
|
||||||
public RootWithList(JCWDatabase database) {
|
@DBField(id = 0, type = DBDataType.DATABASE_OBJECT)
|
||||||
|
public DBDBObjectList<TestUtils.RootClass> list;
|
||||||
|
|
||||||
|
public RootWithList() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public RootWithList(JCWDatabase database) throws IOException {
|
||||||
super(database);
|
super(database);
|
||||||
}
|
}
|
||||||
|
|
||||||
public RootWithList(JCWDatabase database, DBObjectIndicesManager.DBObjectInfo objectInfo) throws IOException {
|
@Override
|
||||||
super(database, objectInfo);
|
public void initialize() throws IOException {
|
||||||
}
|
list = new DBDBObjectList<>(database, TestUtils.RootClass.class);
|
||||||
|
}
|
||||||
@DBField(id = 0, type = DBDataType.DATABASE_OBJECT)
|
|
||||||
public DBDBObjectList<TestUtils.RootClass> list;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,20 +43,20 @@ public class DBMultipleDBObjects {
|
|||||||
|
|
||||||
public static class RootTwoClasses extends DBObject {
|
public static class RootTwoClasses extends DBObject {
|
||||||
|
|
||||||
public RootTwoClasses(JCWDatabase database) {
|
|
||||||
super(database);
|
|
||||||
}
|
|
||||||
|
|
||||||
public RootTwoClasses(JCWDatabase database, DBObjectIndicesManager.DBObjectInfo objectInfo) throws IOException {
|
|
||||||
super(database, objectInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
@DBField(id = 0, type = DBDataType.DATABASE_OBJECT)
|
@DBField(id = 0, type = DBDataType.DATABASE_OBJECT)
|
||||||
public TestUtils.RootClass class1;
|
public TestUtils.RootClass class1;
|
||||||
|
|
||||||
@DBField(id = 1, type = DBDataType.DATABASE_OBJECT)
|
@DBField(id = 1, type = DBDataType.DATABASE_OBJECT)
|
||||||
public TestUtils.RootClass class2;
|
public TestUtils.RootClass class2;
|
||||||
|
|
||||||
|
public RootTwoClasses() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public RootTwoClasses(JCWDatabase database) throws IOException {
|
||||||
|
super(database);
|
||||||
|
}
|
||||||
|
|
||||||
@DBPropertyGetter(id = 0, type = DBDataType.DATABASE_OBJECT)
|
@DBPropertyGetter(id = 0, type = DBDataType.DATABASE_OBJECT)
|
||||||
public TestUtils.RootClass getClass3() {
|
public TestUtils.RootClass getClass3() {
|
||||||
return getProperty();
|
return getProperty();
|
||||||
@ -76,5 +76,10 @@ public class DBMultipleDBObjects {
|
|||||||
public void setClass4(TestUtils.RootClass value) {
|
public void setClass4(TestUtils.RootClass value) {
|
||||||
setProperty(value);
|
setProperty(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize() throws IOException {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,12 +3,9 @@ package org.warp.jcwdb.tests;
|
|||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.warp.jcwdb.ann.*;
|
|
||||||
import org.warp.jcwdb.utils.NestedClass;
|
import org.warp.jcwdb.utils.NestedClass;
|
||||||
import org.warp.jcwdb.utils.TestUtils;
|
import org.warp.jcwdb.utils.TestUtils;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
public class DBNestedDBObjects {
|
public class DBNestedDBObjects {
|
||||||
|
@ -10,7 +10,7 @@ import org.warp.jcwdb.utils.TestUtils;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
public class DBRootCreation {
|
public class DBRootCreation {
|
||||||
private TestUtils.WrappedDb db;
|
private TestUtils.WrappedDb db;
|
||||||
@ -21,7 +21,7 @@ public class DBRootCreation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void shouldCreateRoot() {
|
public void shouldCreateRoot() throws IOException {
|
||||||
RootClass root = db.get().loadRoot(RootClass.class);
|
RootClass root = db.get().loadRoot(RootClass.class);
|
||||||
assertTrue(root.test());
|
assertTrue(root.test());
|
||||||
}
|
}
|
||||||
@ -33,16 +33,17 @@ public class DBRootCreation {
|
|||||||
|
|
||||||
public static class RootClass extends DBObject {
|
public static class RootClass extends DBObject {
|
||||||
|
|
||||||
public RootClass(JCWDatabase database) {
|
public RootClass(JCWDatabase database) throws IOException {
|
||||||
super(database);
|
super(database);
|
||||||
}
|
}
|
||||||
|
|
||||||
public RootClass(JCWDatabase database, DBObjectIndicesManager.DBObjectInfo objectInfo) throws IOException {
|
|
||||||
super(database, objectInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean test() {
|
public boolean test() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize() throws IOException {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,90 @@
|
|||||||
|
package org.warp.jcwdb.tests;
|
||||||
|
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
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.jcwdb.ann.DBPropertyGetter;
|
||||||
|
import org.warp.jcwdb.ann.DBPropertySetter;
|
||||||
|
import org.warp.jcwdb.utils.NTestUtils;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class NDBMultipleEnhancedObjects {
|
||||||
|
private NTestUtils.WrappedDb db;
|
||||||
|
private RootTwoClasses root;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
db = NTestUtils.wrapDb().create((db) -> {
|
||||||
|
root = db.get().loadRoot(RootTwoClasses.class);
|
||||||
|
});
|
||||||
|
root.class1 = new NTestUtils.RootClass(db.get());
|
||||||
|
db.setRootClassValues(root.class1);
|
||||||
|
root.class2 = new NTestUtils.RootClass(db.get());
|
||||||
|
db.setRootClassValues(root.class2);
|
||||||
|
root.setClass3(new NTestUtils.RootClass(db.get()));
|
||||||
|
db.setRootClassValues(root.getClass3());
|
||||||
|
root.setClass4(new NTestUtils.RootClass(db.get()));
|
||||||
|
db.setRootClassValues(root.getClass4());
|
||||||
|
db.closeAndReopen();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldMatchMultipleEnhancedObjects() {
|
||||||
|
db.testRootClassValues(root.class1);
|
||||||
|
db.testRootClassValues(root.class2);
|
||||||
|
db.testRootClassValues(root.getClass3());
|
||||||
|
db.testRootClassValues(root.getClass4());
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() throws Exception {
|
||||||
|
db.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class RootTwoClasses extends EnhancedObject {
|
||||||
|
|
||||||
|
@DBField(id = 0, type = DBDataType.DATABASE_OBJECT)
|
||||||
|
public NTestUtils.RootClass class1;
|
||||||
|
|
||||||
|
@DBField(id = 1, type = DBDataType.DATABASE_OBJECT)
|
||||||
|
public NTestUtils.RootClass class2;
|
||||||
|
|
||||||
|
public RootTwoClasses() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public RootTwoClasses(IDatabase database) throws IOException {
|
||||||
|
super(database);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DBPropertyGetter(id = 0, type = DBDataType.DATABASE_OBJECT)
|
||||||
|
public NTestUtils.RootClass getClass3() {
|
||||||
|
return getProperty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@DBPropertySetter(id = 0, type = DBDataType.DATABASE_OBJECT)
|
||||||
|
public void setClass3(NTestUtils.RootClass value) {
|
||||||
|
setProperty(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DBPropertyGetter(id = 1, type = DBDataType.DATABASE_OBJECT)
|
||||||
|
public NTestUtils.RootClass getClass4() {
|
||||||
|
return getProperty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@DBPropertySetter(id = 1, type = DBDataType.DATABASE_OBJECT)
|
||||||
|
public void setClass4(NTestUtils.RootClass value) {
|
||||||
|
setProperty(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize() throws IOException {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
32
src/test/java/org/warp/jcwdb/utils/ConsumerWithIO.java
Normal file
32
src/test/java/org/warp/jcwdb/utils/ConsumerWithIO.java
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package org.warp.jcwdb.utils;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface ConsumerWithIO<T> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs this operation on the given argument.
|
||||||
|
*
|
||||||
|
* @param t the input argument
|
||||||
|
*/
|
||||||
|
void accept(T t) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a composed {@code Consumer} that performs, in sequence, this
|
||||||
|
* operation followed by the {@code after} operation. If performing either
|
||||||
|
* operation throws an exception, it is relayed to the caller of the
|
||||||
|
* composed operation. If performing this operation throws an exception,
|
||||||
|
* the {@code after} operation will not be performed.
|
||||||
|
*
|
||||||
|
* @param after the operation to perform after this operation
|
||||||
|
* @return a composed {@code Consumer} that performs in sequence this
|
||||||
|
* operation followed by the {@code after} operation
|
||||||
|
* @throws NullPointerException if {@code after} is null
|
||||||
|
*/
|
||||||
|
default ConsumerWithIO<T> andThen(ConsumerWithIO<? super T> after) {
|
||||||
|
Objects.requireNonNull(after);
|
||||||
|
return (T t) -> { accept(t); after.accept(t); };
|
||||||
|
}
|
||||||
|
}
|
29
src/test/java/org/warp/jcwdb/utils/NSimplestClass.java
Normal file
29
src/test/java/org/warp/jcwdb/utils/NSimplestClass.java
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package org.warp.jcwdb.utils;
|
||||||
|
|
||||||
|
import org.warp.cowdb.Database;
|
||||||
|
import org.warp.cowdb.EnhancedObject;
|
||||||
|
import org.warp.jcwdb.ann.DBDataType;
|
||||||
|
import org.warp.jcwdb.ann.DBField;
|
||||||
|
import org.warp.jcwdb.ann.DBObject;
|
||||||
|
import org.warp.jcwdb.ann.JCWDatabase;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class NSimplestClass extends EnhancedObject {
|
||||||
|
|
||||||
|
@DBField(id = 0, type = DBDataType.BOOLEAN)
|
||||||
|
public boolean field1;
|
||||||
|
|
||||||
|
public NSimplestClass() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public NSimplestClass(Database database) throws IOException {
|
||||||
|
super(database);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize() throws IOException {
|
||||||
|
field1 = true;
|
||||||
|
}
|
||||||
|
}
|
383
src/test/java/org/warp/jcwdb/utils/NTestUtils.java
Normal file
383
src/test/java/org/warp/jcwdb/utils/NTestUtils.java
Normal file
@ -0,0 +1,383 @@
|
|||||||
|
package org.warp.jcwdb.utils;
|
||||||
|
|
||||||
|
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.jcwdb.ann.*;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.Comparator;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
public class NTestUtils {
|
||||||
|
public static WrappedDb wrapDb() {
|
||||||
|
return new WrappedDb();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class WrappedDb {
|
||||||
|
|
||||||
|
private Database db;
|
||||||
|
private Path tempDir;
|
||||||
|
private RunnableWithIO r;
|
||||||
|
|
||||||
|
private WrappedDb() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public WrappedDb create() throws IOException {
|
||||||
|
tempDir = Files.createTempDirectory("tests-");
|
||||||
|
db = openDatabase();
|
||||||
|
if (r != null) {
|
||||||
|
r.run();
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public WrappedDb create(ConsumerWithIO<NTestUtils.WrappedDb> r) throws IOException {
|
||||||
|
this.r = () -> r.accept(WrappedDb.this);
|
||||||
|
this.create();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Database openDatabase() throws IOException {
|
||||||
|
return new Database(tempDir.resolve(Paths.get("data.db")), tempDir.resolve(Paths.get("blocks.dat")), tempDir.resolve(Paths.get("references.dat")));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void delete() throws IOException {
|
||||||
|
db.close();
|
||||||
|
deleteDir(tempDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Database get() {
|
||||||
|
return db;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deleteDir(Path p) throws IOException {
|
||||||
|
Files.walk(p)
|
||||||
|
.sorted(Comparator.reverseOrder())
|
||||||
|
.map(Path::toFile)
|
||||||
|
.forEach(File::delete);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void closeAndReopen() throws IOException {
|
||||||
|
db.close();
|
||||||
|
db = openDatabase();
|
||||||
|
r.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRootClassValues(RootClass root) throws IOException {
|
||||||
|
setRootClassFields(root);
|
||||||
|
setRootClassProperties(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRootClassFields(RootClass root) throws IOException {
|
||||||
|
root.field1 = true;
|
||||||
|
root.field2 = 2;
|
||||||
|
root.field3 = 3;
|
||||||
|
root.field4 = 4;
|
||||||
|
root.field5 = 5;
|
||||||
|
root.field6 = 6;
|
||||||
|
root.field7 = "Test";
|
||||||
|
root.field8 = new LongArrayList();
|
||||||
|
root.field8.add(0);
|
||||||
|
root.field8.add(1);
|
||||||
|
root.field8.add(2);
|
||||||
|
root.field8.add(Long.MAX_VALUE/2);
|
||||||
|
root.field8.add(Long.MIN_VALUE/2);
|
||||||
|
root.field8.add(Long.MAX_VALUE);
|
||||||
|
root.field8.add(Long.MIN_VALUE);
|
||||||
|
root.field9 = new NSimplestClass(db);
|
||||||
|
root.field9.field1 = true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRootClassProperties(RootClass root) throws IOException {
|
||||||
|
root.set1(true);
|
||||||
|
root.set2((byte)2);
|
||||||
|
root.set3((short)3);
|
||||||
|
root.set4((char)4);
|
||||||
|
root.set5(5);
|
||||||
|
root.set6(6);
|
||||||
|
root.set7("Test");
|
||||||
|
LongArrayList lArrayList = new LongArrayList();
|
||||||
|
lArrayList.add(0);
|
||||||
|
lArrayList.add(1);
|
||||||
|
lArrayList.add(2);
|
||||||
|
lArrayList.add(Long.MAX_VALUE/2);
|
||||||
|
lArrayList.add(Long.MIN_VALUE/2);
|
||||||
|
lArrayList.add(Long.MAX_VALUE);
|
||||||
|
lArrayList.add(Long.MIN_VALUE);
|
||||||
|
root.set8(lArrayList);
|
||||||
|
NSimplestClass simplestClass9 = new NSimplestClass(db);
|
||||||
|
simplestClass9.field1 = true;
|
||||||
|
root.set9(simplestClass9);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testRootClassValues(RootClass root) {
|
||||||
|
testRootClassFields(root);
|
||||||
|
testRootClassProperties(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testRootClassFields(RootClass root) {
|
||||||
|
shouldGetFieldBoolean(root);
|
||||||
|
shouldGetFieldByte(root);
|
||||||
|
shouldGetFieldShort(root);
|
||||||
|
shouldGetFieldCharacter(root);
|
||||||
|
shouldGetFieldInteger(root);
|
||||||
|
shouldGetFieldLong(root);
|
||||||
|
shouldGetFieldObject(root);
|
||||||
|
shouldGetFieldUID(root);
|
||||||
|
shouldGetFieldDBObject(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testRootClassProperties(RootClass root) {
|
||||||
|
shouldGetPropertyBoolean(root);
|
||||||
|
shouldGetPropertyByte(root);
|
||||||
|
shouldGetPropertyShort(root);
|
||||||
|
shouldGetPropertyCharacter(root);
|
||||||
|
shouldGetPropertyInteger(root);
|
||||||
|
shouldGetPropertyLong(root);
|
||||||
|
shouldGetPropertyObject(root);
|
||||||
|
shouldGetPropertyUID(root);
|
||||||
|
shouldGetPropertyDBObject(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void shouldGetFieldBoolean(RootClass root) {
|
||||||
|
assertTrue(root.field1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void shouldGetPropertyBoolean(RootClass root) {
|
||||||
|
assertTrue(root.get1());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void shouldGetFieldByte(RootClass root) {
|
||||||
|
assertEquals(2, root.field2);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void shouldGetPropertyByte(RootClass root) {
|
||||||
|
assertEquals(2, root.get2());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void shouldGetFieldShort(RootClass root) {
|
||||||
|
assertEquals(3, root.field3);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void shouldGetPropertyShort(RootClass root) {
|
||||||
|
assertEquals(3, root.get3());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void shouldGetFieldCharacter(RootClass root) {
|
||||||
|
assertEquals(4, root.field4);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void shouldGetPropertyCharacter(RootClass root) {
|
||||||
|
assertEquals(4, root.get4());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void shouldGetFieldInteger(RootClass root) {
|
||||||
|
assertEquals(5, root.field5);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void shouldGetPropertyInteger(RootClass root) {
|
||||||
|
assertEquals(5, root.get5());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void shouldGetFieldLong(RootClass root) {
|
||||||
|
assertEquals(6, root.field6);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void shouldGetPropertyLong(RootClass root) {
|
||||||
|
assertEquals(6, root.get6());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void shouldGetFieldObject(RootClass root) {
|
||||||
|
shouldGetObject(root.field7);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void shouldGetPropertyObject(RootClass root) {
|
||||||
|
shouldGetObject(root.get7());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void shouldGetFieldDBObject(RootClass root) {
|
||||||
|
assertTrue(root.field9.field1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void shouldGetPropertyDBObject(RootClass root) {
|
||||||
|
assertTrue(root.get9().field1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void shouldGetObject(String val) {
|
||||||
|
assertNotNull(val);
|
||||||
|
assertEquals("Test", val);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void shouldGetDBObject(SimplestClass val) {
|
||||||
|
assertNotNull(val);
|
||||||
|
assertTrue(val.field1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void shouldGetFieldUID(RootClass root) {
|
||||||
|
shouldGetUID(root.field8);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void shouldGetPropertyUID(RootClass root) {
|
||||||
|
shouldGetUID(root.get8());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void shouldGetUID(LongArrayList val) {
|
||||||
|
assertNotNull(val);
|
||||||
|
assertEquals(7, val.size());
|
||||||
|
assertEquals(0, val.getLong(0));
|
||||||
|
assertEquals(val.getLong(5), Long.MAX_VALUE);
|
||||||
|
assertEquals(val.getLong(6), Long.MIN_VALUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onLoad(RunnableWithIO r) {
|
||||||
|
this.r = r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class RootClass extends EnhancedObject {
|
||||||
|
|
||||||
|
@DBField(id = 0, type = DBDataType.BOOLEAN)
|
||||||
|
public boolean field1;
|
||||||
|
|
||||||
|
@DBField(id = 1, type = DBDataType.BYTE)
|
||||||
|
public byte field2;
|
||||||
|
|
||||||
|
@DBField(id = 2, type = DBDataType.SHORT)
|
||||||
|
public short field3;
|
||||||
|
|
||||||
|
@DBField(id = 3, type = DBDataType.CHAR)
|
||||||
|
public char field4;
|
||||||
|
|
||||||
|
@DBField(id = 4, type = DBDataType.INTEGER)
|
||||||
|
public int field5;
|
||||||
|
|
||||||
|
@DBField(id = 5, type = DBDataType.LONG)
|
||||||
|
public long field6;
|
||||||
|
|
||||||
|
@DBField(id = 6, type = DBDataType.OBJECT)
|
||||||
|
public String field7;
|
||||||
|
|
||||||
|
@DBField(id = 7, type = DBDataType.REFERENCES_LIST)
|
||||||
|
public LongArrayList field8;
|
||||||
|
|
||||||
|
@DBField(id = 8, type = DBDataType.DATABASE_OBJECT)
|
||||||
|
public NSimplestClass field9;
|
||||||
|
|
||||||
|
public RootClass() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public RootClass(IDatabase database) throws IOException {
|
||||||
|
super(database);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DBPropertyGetter(id = 0, type = DBDataType.BOOLEAN)
|
||||||
|
public boolean get1() {
|
||||||
|
return getProperty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@DBPropertyGetter(id = 1, type = DBDataType.BYTE)
|
||||||
|
public byte get2() {
|
||||||
|
return getProperty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@DBPropertyGetter(id = 2, type = DBDataType.SHORT)
|
||||||
|
public short get3() {
|
||||||
|
return getProperty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@DBPropertyGetter(id = 3, type = DBDataType.CHAR)
|
||||||
|
public char get4() {
|
||||||
|
return getProperty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@DBPropertyGetter(id = 4, type = DBDataType.INTEGER)
|
||||||
|
public int get5() {
|
||||||
|
return getProperty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@DBPropertyGetter(id = 5, type = DBDataType.LONG)
|
||||||
|
public long get6() {
|
||||||
|
return getProperty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@DBPropertyGetter(id = 6, type = DBDataType.OBJECT)
|
||||||
|
public String get7() {
|
||||||
|
return getProperty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@DBPropertyGetter(id = 7, type = DBDataType.REFERENCES_LIST)
|
||||||
|
public LongArrayList get8() {
|
||||||
|
return getProperty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@DBPropertyGetter(id = 8, type = DBDataType.DATABASE_OBJECT)
|
||||||
|
public NSimplestClass get9() {
|
||||||
|
return getProperty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@DBPropertySetter(id = 0, type = DBDataType.BOOLEAN)
|
||||||
|
public void set1(boolean val) {
|
||||||
|
setProperty(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DBPropertySetter(id = 1, type = DBDataType.BYTE)
|
||||||
|
public void set2(byte val) {
|
||||||
|
setProperty(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DBPropertySetter(id = 2, type = DBDataType.SHORT)
|
||||||
|
public void set3(short val) {
|
||||||
|
setProperty(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DBPropertySetter(id = 3, type = DBDataType.CHAR)
|
||||||
|
public void set4(char val) {
|
||||||
|
setProperty(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DBPropertySetter(id = 4, type = DBDataType.INTEGER)
|
||||||
|
public void set5(int val) {
|
||||||
|
setProperty(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DBPropertySetter(id = 5, type = DBDataType.LONG)
|
||||||
|
public void set6(long val) {
|
||||||
|
setProperty(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DBPropertySetter(id = 6, type = DBDataType.OBJECT)
|
||||||
|
public void set7(String val) {
|
||||||
|
setProperty(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DBPropertySetter(id = 7, type = DBDataType.REFERENCES_LIST)
|
||||||
|
public void set8(LongArrayList val) {
|
||||||
|
setProperty(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DBPropertySetter(id = 8, type = DBDataType.DATABASE_OBJECT)
|
||||||
|
public void set9(NSimplestClass val) {
|
||||||
|
setProperty(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean test() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize() throws IOException {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -12,6 +12,14 @@ public class NestedClass extends DBObject {
|
|||||||
@DBField(id = 1, type = DBDataType.DATABASE_OBJECT)
|
@DBField(id = 1, type = DBDataType.DATABASE_OBJECT)
|
||||||
public NestedClass child;
|
public NestedClass child;
|
||||||
|
|
||||||
|
public NestedClass() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public NestedClass(JCWDatabase database) throws IOException {
|
||||||
|
super(database);
|
||||||
|
}
|
||||||
|
|
||||||
@DBPropertySetter(id = 0, type = DBDataType.DATABASE_OBJECT)
|
@DBPropertySetter(id = 0, type = DBDataType.DATABASE_OBJECT)
|
||||||
public void setValue(NestedClass value) {
|
public void setValue(NestedClass value) {
|
||||||
setProperty(value);
|
setProperty(value);
|
||||||
@ -22,11 +30,8 @@ public class NestedClass extends DBObject {
|
|||||||
return getProperty();
|
return getProperty();
|
||||||
}
|
}
|
||||||
|
|
||||||
public NestedClass(JCWDatabase database) {
|
@Override
|
||||||
super(database);
|
public void initialize() throws IOException {
|
||||||
}
|
|
||||||
|
|
||||||
public NestedClass(JCWDatabase database, DBObjectIndicesManager.DBObjectInfo objectInfo) throws IOException {
|
|
||||||
super(database, objectInfo);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,12 +9,16 @@ public class SimplestClass extends DBObject {
|
|||||||
@DBField(id = 0, type = DBDataType.BOOLEAN)
|
@DBField(id = 0, type = DBDataType.BOOLEAN)
|
||||||
public boolean field1;
|
public boolean field1;
|
||||||
|
|
||||||
public SimplestClass(JCWDatabase database) {
|
public SimplestClass() {
|
||||||
super(database);
|
|
||||||
field1 = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public SimplestClass(JCWDatabase database, DBObjectIndicesManager.DBObjectInfo objectInfo) throws IOException {
|
public SimplestClass(JCWDatabase database) throws IOException {
|
||||||
super(database, objectInfo);
|
super(database);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize() throws IOException {
|
||||||
|
field1 = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,6 @@ import java.util.Comparator;
|
|||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
|
|
||||||
public class TestUtils {
|
public class TestUtils {
|
||||||
public static WrappedDb wrapDb() {
|
public static WrappedDb wrapDb() {
|
||||||
@ -23,7 +22,7 @@ public class TestUtils {
|
|||||||
|
|
||||||
private JCWDatabase db;
|
private JCWDatabase db;
|
||||||
private Path tempDir;
|
private Path tempDir;
|
||||||
private Runnable r;
|
private RunnableWithIO r;
|
||||||
|
|
||||||
private WrappedDb() {
|
private WrappedDb() {
|
||||||
|
|
||||||
@ -38,7 +37,7 @@ public class TestUtils {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public WrappedDb create(Consumer<TestUtils.WrappedDb> r) throws IOException {
|
public WrappedDb create(ConsumerWithIO<WrappedDb> r) throws IOException {
|
||||||
this.r = () -> r.accept(WrappedDb.this);
|
this.r = () -> r.accept(WrappedDb.this);
|
||||||
this.create();
|
this.create();
|
||||||
return this;
|
return this;
|
||||||
@ -70,12 +69,12 @@ public class TestUtils {
|
|||||||
r.run();
|
r.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setRootClassValues(RootClass root) {
|
public void setRootClassValues(RootClass root) throws IOException {
|
||||||
setRootClassFields(root);
|
setRootClassFields(root);
|
||||||
setRootClassProperties(root);
|
setRootClassProperties(root);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setRootClassFields(RootClass root) {
|
public void setRootClassFields(RootClass root) throws IOException {
|
||||||
root.field1 = true;
|
root.field1 = true;
|
||||||
root.field2 = 2;
|
root.field2 = 2;
|
||||||
root.field3 = 3;
|
root.field3 = 3;
|
||||||
@ -96,7 +95,7 @@ public class TestUtils {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setRootClassProperties(RootClass root) {
|
public void setRootClassProperties(RootClass root) throws IOException {
|
||||||
root.set1(true);
|
root.set1(true);
|
||||||
root.set2((byte)2);
|
root.set2((byte)2);
|
||||||
root.set3((short)3);
|
root.set3((short)3);
|
||||||
@ -238,7 +237,7 @@ public class TestUtils {
|
|||||||
assertEquals(val.getLong(6), Long.MIN_VALUE);
|
assertEquals(val.getLong(6), Long.MIN_VALUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onLoad(Runnable r) {
|
public void onLoad(RunnableWithIO r) {
|
||||||
this.r = r;
|
this.r = r;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -272,6 +271,14 @@ public class TestUtils {
|
|||||||
@DBField(id = 8, type = DBDataType.DATABASE_OBJECT)
|
@DBField(id = 8, type = DBDataType.DATABASE_OBJECT)
|
||||||
public SimplestClass field9;
|
public SimplestClass field9;
|
||||||
|
|
||||||
|
public RootClass() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public RootClass(JCWDatabase database) throws IOException {
|
||||||
|
super(database);
|
||||||
|
}
|
||||||
|
|
||||||
@DBPropertyGetter(id = 0, type = DBDataType.BOOLEAN)
|
@DBPropertyGetter(id = 0, type = DBDataType.BOOLEAN)
|
||||||
public boolean get1() {
|
public boolean get1() {
|
||||||
return getProperty();
|
return getProperty();
|
||||||
@ -362,16 +369,13 @@ public class TestUtils {
|
|||||||
setProperty(val);
|
setProperty(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
public RootClass(JCWDatabase database) {
|
|
||||||
super(database);
|
|
||||||
}
|
|
||||||
|
|
||||||
public RootClass(JCWDatabase database, DBObjectIndicesManager.DBObjectInfo objectInfo) throws IOException {
|
|
||||||
super(database, objectInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean test() {
|
public boolean test() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize() throws IOException {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user