895 lines
32 KiB
Java
895 lines
32 KiB
Java
package it.cavallium.strangedb.java.database;
|
|
|
|
import it.cavallium.strangedb.database.references.DatabaseReferencesIO;
|
|
import it.cavallium.strangedb.java.annotations.*;
|
|
import it.cavallium.strangedb.java.objects.EnhancedObject;
|
|
import it.cavallium.strangedb.java.objects.EnhancedObjectFullInfo;
|
|
import it.cavallium.strangedb.java.objects.EnhancedObjectIndices;
|
|
import it.cavallium.strangedb.java.objects.lists.EnhancedObjectStrangeDbList;
|
|
import it.cavallium.strangedb.java.objects.lists.ObjectStrangeDbList;
|
|
import it.cavallium.strangedb.java.objects.lists.StrangeDbList;
|
|
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.longs.LongList;
|
|
import it.unimi.dsi.fastutil.shorts.ShortArrayList;
|
|
import org.apache.commons.lang3.reflect.FieldUtils;
|
|
|
|
import java.io.IOException;
|
|
import java.lang.reflect.Field;
|
|
import java.lang.reflect.InvocationTargetException;
|
|
import java.lang.reflect.Method;
|
|
import java.nio.ByteBuffer;
|
|
import java.util.*;
|
|
|
|
import static it.cavallium.strangedb.database.references.DatabaseReferencesMetadata.BLANK_DATA_CLEANER;
|
|
|
|
public class DatabaseObjectsIO implements IObjectsIO {
|
|
|
|
public static final byte ENHANCED_OBJECT_METADATA_CLEANER = 1;
|
|
public static final byte REFERENCES_LIST_CLEANER = 2;
|
|
|
|
private final IDatabaseTools databaseTools;
|
|
private final DatabaseReferencesIO referencesIO;
|
|
|
|
private final DatabaseDataInitializer dataInitializer;
|
|
private final KryoSerializer serializer;
|
|
|
|
public DatabaseObjectsIO(IDatabaseTools databaseTools, DatabaseReferencesIO referencesIO) {
|
|
this.databaseTools = databaseTools;
|
|
this.referencesIO = referencesIO;
|
|
this.serializer = new KryoSerializer();
|
|
this.dataInitializer = new DatabaseDataInitializer();
|
|
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++);
|
|
|
|
registerClass(EnhancedObject.class, id++);
|
|
registerClass(EnhancedObjectStrangeDbList.class, id++);
|
|
registerClass(ObjectStrangeDbList.class, id++);
|
|
registerClass(StrangeDbList.class, id++);
|
|
}
|
|
|
|
@Override
|
|
public <T extends EnhancedObject> T loadEnhancedObject(long reference) throws IOException {
|
|
return loadEnhancedObject_(reference, null);
|
|
}
|
|
|
|
@Override
|
|
public <T extends EnhancedObject> T loadEnhancedObject(long reference, Class<?> forcedType) throws IOException {
|
|
if (forcedType == null) {
|
|
throw new NullPointerException("The class is null!");
|
|
}
|
|
return loadEnhancedObject_(reference, forcedType);
|
|
}
|
|
|
|
@SuppressWarnings("unchecked")
|
|
private <T extends EnhancedObject> T loadEnhancedObject_(long reference, Class<?> forcedType) throws IOException {
|
|
ByteBuffer buffer = referencesIO.readFromReference(reference);
|
|
if (buffer.limit() == 0) {
|
|
return null;
|
|
}
|
|
int serializedVersion = Byte.toUnsignedInt(buffer.get()) - 5;
|
|
if (serializedVersion < 0) {
|
|
System.err.println("PLEASE UPGRADE THE DATABASE");
|
|
throw new IOException("PLEASE UPGRADE THE DATABASE");
|
|
}
|
|
int serializedClassLength = Byte.toUnsignedInt(buffer.get());
|
|
byte[] serializedClassData = new byte[serializedClassLength];
|
|
buffer.get(serializedClassData);
|
|
Class<?> objectType;
|
|
try {
|
|
objectType = forcedType != null ? forcedType : serializer.readClassBytes(serializedClassData);
|
|
} catch (Exception | Error ex) {
|
|
throw new IOException(ex);
|
|
}
|
|
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();
|
|
}
|
|
long nativeFieldsDataReference = buffer.getLong();
|
|
return preloadEnhancedObject(objectType, serializedVersion, nativeFieldsDataReference, fieldRefs,
|
|
methodRefs);
|
|
}
|
|
|
|
@SuppressWarnings("unchecked")
|
|
@Override
|
|
public EnhancedObjectIndices loadEnhancedObjectUids(long reference) throws IOException {
|
|
ByteBuffer buffer = referencesIO.readFromReference(reference);
|
|
if (buffer.limit() == 0) {
|
|
return null;
|
|
}
|
|
//noinspection unused
|
|
int serializedVersion = Byte.toUnsignedInt(buffer.get()) - 5;
|
|
int serializedClassLength = Byte.toUnsignedInt(buffer.get());
|
|
byte[] serializedClassData = new byte[serializedClassLength];
|
|
buffer.get(serializedClassData);
|
|
Class<? extends EnhancedObject> objectType;
|
|
try {
|
|
objectType = serializer.readClassBytes(serializedClassData);
|
|
} catch (Exception | Error ex) {
|
|
throw new IOException(ex);
|
|
}
|
|
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();
|
|
}
|
|
long nativeFieldsDataReference = buffer.getLong();
|
|
return new EnhancedObjectIndices(objectType, reference, fieldRefs, methodRefs, nativeFieldsDataReference);
|
|
}
|
|
|
|
public Object loadData(DbDataType propertyType, long dataReference) throws IOException {
|
|
return loadData_(propertyType, dataReference);
|
|
}
|
|
|
|
private Object loadData_(DbDataType propertyType, long dataReference) throws IOException {
|
|
|
|
switch (propertyType) {
|
|
case ENHANCED_OBJECT:
|
|
return loadEnhancedObject_(dataReference, null);
|
|
case OBJECT:
|
|
return loadObject_(dataReference);
|
|
case REFERENCES_LIST:
|
|
return loadReferencesList_(dataReference);
|
|
default:
|
|
throw new NullPointerException("Unknown data type");
|
|
}
|
|
}
|
|
|
|
private <T> void setData(long reference, DbDataType propertyType, T loadedPropertyValue) throws IOException {
|
|
switch (propertyType) {
|
|
case OBJECT:
|
|
setObject_(reference, loadedPropertyValue);
|
|
break;
|
|
case REFERENCES_LIST:
|
|
setReferencesList_(reference, (LongArrayList) loadedPropertyValue);
|
|
break;
|
|
case ENHANCED_OBJECT:
|
|
setEnhancedObject_(reference, (EnhancedObject) loadedPropertyValue);
|
|
break;
|
|
}
|
|
}
|
|
|
|
private <T extends EnhancedObject> void setPrimitives(T enhancedObject, long reference, DbPrimitiveType[] types, Field[] fields)
|
|
throws IOException {
|
|
ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES * fields.length);
|
|
try {
|
|
for (int i = 0; i < fields.length; i++) {
|
|
Field field = fields[i];
|
|
DbPrimitiveType type = types[i];
|
|
if (field == null) {
|
|
buffer.putLong(0);
|
|
} else {
|
|
switch (type) {
|
|
case BOOLEAN:
|
|
buffer.putLong(field.getBoolean(enhancedObject) ? 1 : 0);
|
|
break;
|
|
case BYTE:
|
|
buffer.putLong(field.getByte(enhancedObject));
|
|
break;
|
|
case SHORT:
|
|
buffer.putLong(field.getShort(enhancedObject));
|
|
break;
|
|
case CHAR:
|
|
buffer.putLong(field.getChar(enhancedObject));
|
|
break;
|
|
case INTEGER:
|
|
buffer.putLong(field.getInt(enhancedObject));
|
|
break;
|
|
case LONG:
|
|
buffer.putLong(field.getLong(enhancedObject));
|
|
break;
|
|
case FLOAT:
|
|
buffer.putLong(Float.floatToRawIntBits(field.getFloat(enhancedObject)));
|
|
break;
|
|
case DOUBLE:
|
|
buffer.putLong(Double.doubleToRawLongBits(field.getDouble(enhancedObject)));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} catch (IllegalArgumentException | IllegalAccessException e) {
|
|
throw new IOException(e);
|
|
}
|
|
buffer.flip();
|
|
referencesIO.writeToReference(reference, BLANK_DATA_CLEANER, buffer.limit(), buffer);
|
|
}
|
|
|
|
@Override
|
|
public <T extends EnhancedObject> void setEnhancedObject(long reference, T value) throws IOException {
|
|
setEnhancedObject_(reference, value);
|
|
}
|
|
|
|
private <T extends EnhancedObject> void setEnhancedObject_(long reference, T value) throws IOException {
|
|
if (value != null) {
|
|
EnhancedObjectFullInfo objectFullInfo = value.getAllInfo();
|
|
if (objectFullInfo == null || objectFullInfo.getFieldReferences() == null
|
|
|| objectFullInfo.getPropertyReferences() == null) {
|
|
throw new NullPointerException(
|
|
"An EnhancedObject has been initialized using the empty constructor!");
|
|
}
|
|
|
|
final long[] fieldReferences = objectFullInfo.getFieldReferences();
|
|
final DbDataType[] fieldTypes = objectFullInfo.getFieldTypes();
|
|
final Field[] fields = objectFullInfo.getFields();
|
|
final long nativeFieldDataReference = objectFullInfo.getPrimitiveFieldDataReference();
|
|
final DbPrimitiveType[] nativeFieldTypes = objectFullInfo.getPrimitiveFieldTypes();
|
|
final Field[] nativeFields = objectFullInfo.getPrimitiveFields();
|
|
final long[] propertyReferences = objectFullInfo.getPropertyReferences();
|
|
final DbDataType[] propertyTypes = objectFullInfo.getPropertyTypes();
|
|
final Object[] propertyValues = objectFullInfo.getLoadedPropertyValues();
|
|
byte[] serializedClassData = serializer.writeClassBytes(value.getClass());
|
|
final int totalSize = Byte.BYTES + serializedClassData.length + Byte.BYTES + Integer.BYTES * 2 + fieldReferences.length * Long.BYTES
|
|
+ propertyReferences.length * Long.BYTES + Long.BYTES;
|
|
ByteBuffer buffer = ByteBuffer.allocate(totalSize);
|
|
if (serializedClassData.length > 255) {
|
|
throw new IndexOutOfBoundsException("The class name is too long!");
|
|
}
|
|
if (objectFullInfo.getVersion() > 250) {
|
|
throw new IndexOutOfBoundsException("The class version is too long!");
|
|
}
|
|
buffer.put((byte) (objectFullInfo.getVersion() + 5));
|
|
buffer.put((byte) serializedClassData.length);
|
|
buffer.put(serializedClassData);
|
|
buffer.putInt(fieldReferences.length);
|
|
buffer.putInt(propertyReferences.length);
|
|
for (int i = 0; i < fieldReferences.length; i++) {
|
|
buffer.putLong(fieldReferences[i]);
|
|
}
|
|
for (int i = 0; i < propertyReferences.length; i++) {
|
|
buffer.putLong(propertyReferences[i]);
|
|
}
|
|
buffer.putLong(nativeFieldDataReference);
|
|
buffer.flip();
|
|
|
|
for (int i = 0; i < fieldReferences.length; i++) {
|
|
if (fields[i] != null) {
|
|
try {
|
|
setData(fieldReferences[i], fieldTypes[i], fields[i].get(value));
|
|
} catch (IllegalAccessException e) {
|
|
throw new IOException(e);
|
|
}
|
|
}
|
|
}
|
|
for (int i = 0; i < propertyReferences.length; i++) {
|
|
if (propertyValues[i] != null) {
|
|
setData(propertyReferences[i], propertyTypes[i], propertyValues[i]);
|
|
}
|
|
}
|
|
setPrimitives(value, nativeFieldDataReference, nativeFieldTypes, nativeFields);
|
|
referencesIO.writeToReference(reference, ENHANCED_OBJECT_METADATA_CLEANER, totalSize, buffer);
|
|
} else {
|
|
referencesIO.writeToReference(reference, BLANK_DATA_CLEANER, 0, null);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public <T> T loadObject(long reference) throws IOException {
|
|
return loadObject_(reference);
|
|
}
|
|
|
|
@SuppressWarnings("unchecked")
|
|
private <T> T loadObject_(long reference) throws IOException {
|
|
ByteBuffer buffer = referencesIO.readFromReference(reference);
|
|
if (buffer.limit() == 0) {
|
|
return null;
|
|
}
|
|
buffer.rewind();
|
|
byte[] data = buffer.array();
|
|
try {
|
|
return serializer.readClassAndObjectBytes(data);
|
|
} catch (Exception | Error ex) {
|
|
throw new IOException(ex);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public LongArrayList loadReferencesList(long reference) throws IOException {
|
|
return loadReferencesList_(reference);
|
|
}
|
|
|
|
private 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 LongList loadPrimitiveData(long reference) throws IOException {
|
|
return loadPrimitiveData_(reference);
|
|
}
|
|
|
|
private LongList loadPrimitiveData_(long reference) throws IOException {
|
|
ByteBuffer buffer = referencesIO.readFromReference(reference);
|
|
if (buffer.limit() == 0) {
|
|
return null;
|
|
}
|
|
int size = buffer.limit() / Long.BYTES;
|
|
LongArrayList result = new LongArrayList(size);
|
|
for (int i = 0; i < size; i++) {
|
|
result.add(buffer.getLong());
|
|
}
|
|
return result;
|
|
}
|
|
|
|
@Override
|
|
public <T> void setObject(long reference, T value) throws IOException {
|
|
setObject_(reference, value);
|
|
}
|
|
|
|
private <T> void setObject_(long reference, T value) throws IOException {
|
|
if (value != null) {
|
|
byte[] data = serializer.writeClassAndObjectBytes(value);
|
|
ByteBuffer dataByteBuffer = ByteBuffer.wrap(data);
|
|
referencesIO.writeToReference(reference, BLANK_DATA_CLEANER, data.length, dataByteBuffer);
|
|
} else {
|
|
referencesIO.writeToReference(reference, BLANK_DATA_CLEANER, 0, null);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void setReferencesList(long reference, LongArrayList value) throws IOException {
|
|
setReferencesList_(reference, value);
|
|
}
|
|
|
|
private 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, REFERENCES_LIST_CLEANER, buffer.limit(), buffer);
|
|
} else {
|
|
referencesIO.writeToReference(reference, REFERENCES_LIST_CLEANER, 0, null);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public long newNullObject() throws IOException {
|
|
return newNullObject_();
|
|
}
|
|
|
|
private long newNullObject_() throws IOException {
|
|
return referencesIO.allocateReference();
|
|
}
|
|
|
|
@Override
|
|
public void loadProperty(EnhancedObject obj, int propertyId, DbDataType propertyType,
|
|
long propertyUID) throws IOException {
|
|
obj.setProperty(propertyId, loadData_(propertyType, propertyUID));
|
|
obj.setProperty(propertyId, loadData_(propertyType, propertyUID));
|
|
}
|
|
|
|
@Override
|
|
public void registerClass(Class<?> type, int id) {
|
|
if (id < -100) {
|
|
throw new IllegalArgumentException();
|
|
}
|
|
final int realId = id + 100;
|
|
|
|
serializer.registerClass(type, realId);
|
|
}
|
|
|
|
protected Map<Class<?>, Integer> getRegisteredClasses() {
|
|
Map<Class<?>, Integer> registeredClasses = serializer.getRegisteredClasses();
|
|
registeredClasses.replaceAll((clazz,id) -> id - 100);
|
|
registeredClasses.entrySet().removeIf((entry) -> entry.getValue() < 0);
|
|
return registeredClasses;
|
|
}
|
|
|
|
private <T extends EnhancedObject> void preloadEnhancedObjectProperties(T obj, long[] propertyReferences) {
|
|
// Declare the variables needed to getBlock 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) {
|
|
DbProperty fieldAnnotation = property.getAnnotation(DbProperty.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, DbProperty> setterMethods = new LinkedHashMap<>();
|
|
Map<String, DbProperty> getterMethods = new LinkedHashMap<>();
|
|
|
|
// Load the properties metadata
|
|
for (Method property : unorderedPropertyGetters) {
|
|
DbProperty propertyAnnotation = property.getAnnotation(DbProperty.class);
|
|
int propertyId = propertyAnnotation.id();
|
|
DbDataType propertyType = propertyAnnotation.type();
|
|
propertyTypes[propertyId] = propertyType;
|
|
propertyGetters[propertyId] = property;
|
|
getterMethods.put(property.getName(), propertyAnnotation);
|
|
}
|
|
for (Method property : unorderedPropertySetters) {
|
|
DbProperty propertyAnnotation = property.getAnnotation(DbProperty.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);
|
|
}
|
|
|
|
public int getBiggestPropertyGetterId(Method[] unorderedPropertyGetters) {
|
|
return getBiggestPropertyGetterId_(unorderedPropertyGetters);
|
|
}
|
|
|
|
private int getBiggestPropertyGetterId_(Method[] unorderedPropertyGetters) {
|
|
int biggestPropertyId = -1;
|
|
for (Method property : unorderedPropertyGetters) {
|
|
DbProperty fieldAnnotation = property.getAnnotation(DbProperty.class);
|
|
int propertyId = fieldAnnotation.id();
|
|
if (propertyId > biggestPropertyId) {
|
|
biggestPropertyId = propertyId;
|
|
}
|
|
}
|
|
return biggestPropertyId;
|
|
}
|
|
|
|
public int getBiggestPropertySetterId(Method[] unorderedPropertySetters) {
|
|
return getBiggestPropertySetterId_(unorderedPropertySetters);
|
|
}
|
|
|
|
private int getBiggestPropertySetterId_(Method[] unorderedPropertySetters) {
|
|
int biggestPropertyId = -1;
|
|
for (Method property : unorderedPropertySetters) {
|
|
DbProperty fieldAnnotation = property.getAnnotation(DbProperty.class);
|
|
int propertyId = fieldAnnotation.id();
|
|
if (propertyId > biggestPropertyId) {
|
|
biggestPropertyId = propertyId;
|
|
}
|
|
}
|
|
return biggestPropertyId;
|
|
}
|
|
|
|
private <T extends EnhancedObject> void preloadEnhancedObjectPrimitiveFields(T obj, long nativeFieldsDataReference)
|
|
throws IOException {
|
|
// Declare the variables needed to getBlock the biggest field Id
|
|
Field[] unorderedFields = getPrimitiveFields_(obj);
|
|
// Find the biggest field Id
|
|
int biggestFieldId = getBiggestPrimitiveFieldId_(unorderedFields);
|
|
|
|
// Declare the other variables
|
|
Field[] fields = new Field[biggestFieldId + 1];
|
|
DbPrimitiveType[] orderedFieldTypes = new DbPrimitiveType[biggestFieldId + 1];
|
|
|
|
// Load all fields metadata
|
|
for (Field field : unorderedFields) {
|
|
DbPrimitiveField fieldAnnotation = field.getAnnotation(DbPrimitiveField.class);
|
|
int fieldId = fieldAnnotation.id();
|
|
DbPrimitiveType fieldType = fieldAnnotation.type();
|
|
fields[fieldId] = field;
|
|
orderedFieldTypes[fieldId] = fieldType;
|
|
}
|
|
|
|
// Load fields data
|
|
ByteBuffer buffer = referencesIO.readFromReference(nativeFieldsDataReference);
|
|
|
|
// Load fields
|
|
try {
|
|
for (int id = 0; id < fields.length; id++) {
|
|
Field field = fields[id];
|
|
DbPrimitiveType type = orderedFieldTypes[id];
|
|
|
|
if (field != null) {
|
|
switch (type) {
|
|
case BOOLEAN:
|
|
FieldUtils.writeField(field, obj, (boolean) (buffer.getLong() % 2 == 1), true);
|
|
break;
|
|
case BYTE:
|
|
FieldUtils.writeField(field, obj, (byte) buffer.getLong(), true);
|
|
break;
|
|
case SHORT:
|
|
FieldUtils.writeField(field, obj, (short) buffer.getLong(), true);
|
|
break;
|
|
case CHAR:
|
|
FieldUtils.writeField(field, obj, (char) buffer.getLong(), true);
|
|
break;
|
|
case INTEGER:
|
|
FieldUtils.writeField(field, obj, (int) buffer.getLong(), true);
|
|
break;
|
|
case LONG:
|
|
FieldUtils.writeField(field, obj, (long) buffer.getLong(), true);
|
|
break;
|
|
case FLOAT:
|
|
FieldUtils.writeField(field, obj, Float.intBitsToFloat((int) buffer.getLong()), true);
|
|
break;
|
|
case DOUBLE:
|
|
FieldUtils.writeField(field, obj, Double.longBitsToDouble(buffer.getLong()), true);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} catch (IllegalArgumentException | IllegalAccessException e) {
|
|
throw new IOException(e);
|
|
}
|
|
|
|
// Set fields metadata
|
|
obj.setPrimitiveFields(fields, orderedFieldTypes, nativeFieldsDataReference);
|
|
}
|
|
|
|
private <T extends EnhancedObject> void preloadEnhancedObjectFields(T obj, long[] fieldReferences)
|
|
throws IOException {
|
|
// Declare the variables needed to getBlock 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);
|
|
}
|
|
|
|
public <T extends EnhancedObject> void loadField(T obj, Field field, DbDataType fieldType, long fieldReference)
|
|
throws IOException {
|
|
loadField_(obj, field, fieldType, fieldReference);
|
|
}
|
|
|
|
private <T extends EnhancedObject> void loadField_(T obj, Field field, DbDataType fieldType, long fieldReference)
|
|
throws IOException {
|
|
Object data = loadData_(fieldType, fieldReference);
|
|
try {
|
|
if (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 IOException(e);
|
|
}
|
|
}
|
|
|
|
public <T extends EnhancedObject> Field[] getFields(T obj) {
|
|
return getFields_(obj);
|
|
}
|
|
|
|
private <T extends EnhancedObject> Field[] getFields_(T obj) {
|
|
return FieldUtils.getFieldsWithAnnotation(obj.getClass(), DbField.class);
|
|
}
|
|
|
|
public <T extends EnhancedObject> Field[] getPrimitiveFields(T obj) {
|
|
return getPrimitiveFields_(obj);
|
|
}
|
|
|
|
private <T extends EnhancedObject> Field[] getPrimitiveFields_(T obj) {
|
|
return FieldUtils.getFieldsWithAnnotation(obj.getClass(), DbPrimitiveField.class);
|
|
}
|
|
|
|
public int getBiggestFieldId(Field[] unorderedFields) {
|
|
return getBiggestFieldId_(unorderedFields);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
public int getBiggestPrimitiveFieldId(Field[] unorderedFields) {
|
|
return getBiggestPrimitiveFieldId_(unorderedFields);
|
|
}
|
|
|
|
private int getBiggestPrimitiveFieldId_(Field[] unorderedFields) {
|
|
int biggestFieldId = -1;
|
|
for (Field field : unorderedFields) {
|
|
DbPrimitiveField fieldAnnotation = field.getAnnotation(DbPrimitiveField.class);
|
|
int propertyId = fieldAnnotation.id();
|
|
if (propertyId > biggestFieldId) {
|
|
biggestFieldId = propertyId;
|
|
}
|
|
}
|
|
return biggestFieldId;
|
|
}
|
|
|
|
@SuppressWarnings("unchecked")
|
|
private <T extends EnhancedObject> T toInstance(Class<?> type) throws IOException {
|
|
try {
|
|
T obj = (T) type.getConstructor().newInstance();
|
|
obj.setDatabaseTools(databaseTools);
|
|
return obj;
|
|
} catch (NoSuchMethodException e) {
|
|
throw new IOException("You must declare a public empty constructor in class " + type + ": public "
|
|
+ type.getSimpleName() + "()", e);
|
|
} catch (IllegalAccessException | InvocationTargetException | InstantiationException e) {
|
|
throw new IOException(e);
|
|
}
|
|
}
|
|
|
|
private <T extends EnhancedObject> T preloadEnhancedObject(Class<?> objectType, int serializedVersion, long nativeFieldsRef, long[] fieldRefs, long[] methodRefs) throws IOException {
|
|
// Instantiate the class to an object
|
|
T obj = toInstance(objectType);
|
|
|
|
// Check the serialized version
|
|
DbClass dbClass = objectType.getAnnotation(DbClass.class);
|
|
int classVersion = 0;
|
|
if (dbClass != null) {
|
|
classVersion = dbClass.version();
|
|
}
|
|
if (classVersion == serializedVersion) {
|
|
preloadEnhancedObjectPrimitiveFields(obj, nativeFieldsRef);
|
|
preloadEnhancedObjectFields(obj, fieldRefs);
|
|
preloadEnhancedObjectProperties(obj, methodRefs);
|
|
} else if (classVersion > serializedVersion) {
|
|
DatabaseEnhancedObjectUpgrader enhancedObjectUpgrader = new DatabaseEnhancedObjectUpgrader(this, fieldRefs, methodRefs, nativeFieldsRef);
|
|
dataInitializer.initializeDbObject_(obj);
|
|
obj.onUpgrade(serializedVersion, enhancedObjectUpgrader);
|
|
} else {
|
|
throw new IllegalStateException(
|
|
"The serialized class is more recent than the current version of that class!");
|
|
}
|
|
return obj;
|
|
}
|
|
|
|
public long[] allocateNewUIDs(int quantity) throws IOException {
|
|
return allocateNewUIDs_(quantity);
|
|
}
|
|
|
|
private long[] allocateNewUIDs_(int quantity) throws IOException {
|
|
long[] ids = new long[quantity];
|
|
for (int i = 0; i < quantity; i++) {
|
|
ids[i] = newNullObject_();
|
|
}
|
|
return ids;
|
|
}
|
|
|
|
@Override
|
|
public IDataInitializer getDataInitializer() {
|
|
return dataInitializer;
|
|
}
|
|
|
|
private class DatabaseDataInitializer implements IDataInitializer {
|
|
|
|
public DatabaseDataInitializer() {
|
|
}
|
|
|
|
@Override
|
|
public void initializeDbObject(EnhancedObject obj) throws IOException {
|
|
initializeDbObject_(obj);
|
|
}
|
|
|
|
private void initializeDbObject_(EnhancedObject obj) throws IOException {
|
|
initializeDbObjectFields(obj);
|
|
initializeDbObjectPrimitiveFields(obj);
|
|
initializeDbObjectProperties(obj);
|
|
}
|
|
|
|
private void initializeDbObjectFields(EnhancedObject obj) throws IOException {
|
|
// Declare the variables needed to getBlock the biggest field Id
|
|
Field[] unorderedFields = getFields_(obj);
|
|
// Find the biggest field Id
|
|
int biggestFieldId = getBiggestFieldId_(unorderedFields);
|
|
|
|
// Allocate new UIDs
|
|
long[] fieldUIDs = 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();
|
|
loadField_(obj, field, fieldType, fieldUIDs[fieldId]);
|
|
fields[fieldId] = field;
|
|
orderedFieldTypes[fieldId] = fieldType;
|
|
}
|
|
// Set fields metadata
|
|
obj.setFields(fields, orderedFieldTypes, fieldUIDs);
|
|
}
|
|
|
|
private void initializeDbObjectPrimitiveFields(EnhancedObject obj) throws IOException {
|
|
// Declare the variables needed to getBlock the biggest field Id
|
|
Field[] unorderedFields = getPrimitiveFields_(obj);
|
|
// Find the biggest field Id
|
|
int biggestFieldId = getBiggestPrimitiveFieldId_(unorderedFields);
|
|
|
|
// Allocate new UID
|
|
long fieldDataUID = newNullObject_();
|
|
|
|
// Declare the other variables
|
|
Field[] fields = new Field[biggestFieldId + 1];
|
|
DbPrimitiveType[] orderedFieldTypes = new DbPrimitiveType[biggestFieldId + 1];
|
|
|
|
// Load all fields metadata and load them
|
|
try {
|
|
for (Field field : unorderedFields) {
|
|
DbPrimitiveField fieldAnnotation = field.getAnnotation(DbPrimitiveField.class);
|
|
int fieldId = fieldAnnotation.id();
|
|
DbPrimitiveType fieldType = fieldAnnotation.type();
|
|
switch (fieldType) {
|
|
case BOOLEAN:
|
|
FieldUtils.writeField(field, obj, false, true);
|
|
break;
|
|
case BYTE:
|
|
FieldUtils.writeField(field, obj, (byte) 0, true);
|
|
break;
|
|
case CHAR:
|
|
FieldUtils.writeField(field, obj, (char) 0, true);
|
|
break;
|
|
case SHORT:
|
|
FieldUtils.writeField(field, obj, (short) 0, true);
|
|
break;
|
|
case INTEGER:
|
|
FieldUtils.writeField(field, obj, 0, true);
|
|
break;
|
|
case LONG:
|
|
FieldUtils.writeField(field, obj, (long) 0, true);
|
|
break;
|
|
case FLOAT:
|
|
FieldUtils.writeField(field, obj, (float) 0, true);
|
|
break;
|
|
case DOUBLE:
|
|
FieldUtils.writeField(field, obj, (double) 0, true);
|
|
break;
|
|
}
|
|
fields[fieldId] = field;
|
|
orderedFieldTypes[fieldId] = fieldType;
|
|
}
|
|
} catch (IllegalArgumentException | IllegalAccessException e) {
|
|
throw new IOException(e);
|
|
}
|
|
// Set fields metadata
|
|
obj.setPrimitiveFields(fields, orderedFieldTypes, fieldDataUID);
|
|
}
|
|
|
|
private void initializeDbObjectProperties(EnhancedObject obj) throws IOException {
|
|
// Declare the variables needed to getBlock 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;
|
|
|
|
// Allocate new UIDs
|
|
long[] propertyUIDs = allocateNewUIDs_(biggestPropertyId + 1);
|
|
|
|
for (Method property : unorderedPropertySetters) {
|
|
DbProperty fieldAnnotation = property.getAnnotation(DbProperty.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, DbProperty> setterMethods = new LinkedHashMap<>();
|
|
Map<String, DbProperty> getterMethods = new LinkedHashMap<>();
|
|
|
|
// Load the properties metadata
|
|
for (Method property : unorderedPropertyGetters) {
|
|
DbProperty propertyAnnotation = property.getAnnotation(DbProperty.class);
|
|
int propertyId = propertyAnnotation.id();
|
|
DbDataType propertyType = propertyAnnotation.type();
|
|
propertyTypes[propertyId] = propertyType;
|
|
propertyGetters[propertyId] = property;
|
|
getterMethods.put(property.getName(), propertyAnnotation);
|
|
}
|
|
for (Method property : unorderedPropertySetters) {
|
|
DbProperty propertyAnnotation = property.getAnnotation(DbProperty.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);
|
|
}
|
|
}
|
|
|
|
}
|