212 lines
7.7 KiB
Java
212 lines
7.7 KiB
Java
package org.warp.jcwdb.ann;
|
|
|
|
import org.apache.commons.lang3.reflect.FieldUtils;
|
|
|
|
import java.io.IOError;
|
|
import java.io.IOException;
|
|
import java.lang.reflect.Field;
|
|
import java.lang.reflect.Method;
|
|
import java.util.LinkedHashMap;
|
|
import java.util.Map;
|
|
|
|
public abstract class DBObject {
|
|
protected final DatabaseManager database;
|
|
private Field[] fields;
|
|
private DBDataType[] fieldTypes;
|
|
private long[] fieldUIDs;
|
|
|
|
private Method[] propertyGetters;
|
|
private Method[] propertySetters;
|
|
private DBDataType[] propertyTypes;
|
|
private long[] propertyUIDs;
|
|
private boolean[] loadedProperties;
|
|
private Object[] loadedPropertyValues;
|
|
private Map<String, DBPropertySetter> setterMethods;
|
|
private Map<String, DBPropertyGetter> getterMethods;
|
|
private final Object fieldsAccessLock = new Object();
|
|
private final Object propertiesAccessLock = new Object();
|
|
|
|
public DBObject(JCWDatabase database) {
|
|
this.database = database.getDatabaseManager();
|
|
try {
|
|
initializeDBObject();
|
|
} catch (IOException e) {
|
|
throw new RuntimeException(e);
|
|
}
|
|
}
|
|
|
|
private void initializeDBObject() throws IOException {
|
|
initializeDBObjectFields();
|
|
initializeDBObjectProperties();
|
|
}
|
|
|
|
private void initializeDBObjectFields() throws IOException {
|
|
// Declare the variables needed to get the biggest field Id
|
|
Field[] unorderedFields = database.getFields(this);
|
|
// Find the biggest field Id
|
|
int biggestFieldId = database.getBiggestFieldId(unorderedFields);
|
|
|
|
// Allocate new UIDs
|
|
fieldUIDs = database.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();
|
|
database.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 = database.getPropertyGetters(this);
|
|
Method[] unorderedPropertySetters = database.getPropertySetters(this);
|
|
|
|
// Find the biggest property Id
|
|
int biggestGetter = database.getBiggestPropertyGetterId(unorderedPropertyGetters);
|
|
int biggestSetter = database.getBiggestPropertySetterId(unorderedPropertySetters);
|
|
int biggestPropertyId = biggestGetter > biggestSetter ? biggestGetter : biggestSetter;
|
|
|
|
// Allocate new UIDs
|
|
propertyUIDs = database.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 DBObject(JCWDatabase database, DBObjectIndicesManager.DBObjectInfo objectInfo) throws IOException {
|
|
this.database = database.getDatabaseManager();
|
|
this.database.preloadDBObject(this, objectInfo);
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
<T> void setLoadedProperty(int propertyId, T value) {
|
|
loadedPropertyValues[propertyId] = value;
|
|
loadedProperties[propertyId] = true;
|
|
}
|
|
|
|
@SuppressWarnings("unchecked")
|
|
private <T> T getProperty(int propertyId) throws IOException {
|
|
synchronized (propertiesAccessLock) {
|
|
if (!loadedProperties[propertyId]) {
|
|
long propertyUID = propertyUIDs[propertyId];
|
|
database.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(), propertyAnnotation.type(), value);
|
|
}
|
|
|
|
public <T> void setProperty(int propertyId, DBDataType propertyType, T value) {
|
|
synchronized (propertiesAccessLock) {
|
|
loadedPropertyValues[propertyId] = value;
|
|
loadedProperties[propertyId] = true;
|
|
}
|
|
}
|
|
|
|
public void writeToDisk(long uid) {
|
|
//System.err.println("Saving object " + uid + ":" + this);
|
|
try {
|
|
synchronized (propertiesAccessLock) {
|
|
synchronized (fieldsAccessLock) {
|
|
database.writeObjectInfo(uid, fieldUIDs, propertyUIDs);
|
|
}
|
|
}
|
|
synchronized (fieldsAccessLock) {
|
|
for (int i = 0; i < fieldUIDs.length; i++) {
|
|
try {
|
|
database.writeObjectProperty(fieldUIDs[i], fieldTypes[i], fields[i].get(this));
|
|
} catch (IllegalAccessException e) {
|
|
throw new IOError(e);
|
|
}
|
|
}
|
|
}
|
|
synchronized (propertiesAccessLock) {
|
|
for (int i = 0; i < propertyUIDs.length; i++) {
|
|
database.writeObjectProperty(propertyUIDs[i], propertyTypes[i], loadedPropertyValues[i]);
|
|
}
|
|
}
|
|
} catch (IOException e) {
|
|
throw new IOError(e);
|
|
}
|
|
}
|
|
|
|
public final void setFields(Field[] fields, DBDataType[] fieldTypes, long[] fieldUIDs) {
|
|
synchronized (fieldsAccessLock) {
|
|
this.fields = fields;
|
|
this.fieldTypes = fieldTypes;
|
|
this.fieldUIDs = fieldUIDs;
|
|
}
|
|
}
|
|
|
|
public final void setProperties(Method[] propertyGetters, Method[] propertySetters, DBDataType[] propertyTypes, long[] propertyUIDs, Map<String, DBPropertySetter> setterMethods, Map<String, DBPropertyGetter> getterMethods) {
|
|
synchronized (propertiesAccessLock) {
|
|
this.propertyGetters = propertyGetters;
|
|
this.propertySetters = propertySetters;
|
|
this.propertyTypes = propertyTypes;
|
|
this.propertyUIDs = propertyUIDs;
|
|
this.loadedProperties = new boolean[this.propertyUIDs.length];
|
|
this.loadedPropertyValues = new Object[this.propertyUIDs.length];
|
|
this.setterMethods = setterMethods;
|
|
this.getterMethods = getterMethods;
|
|
}
|
|
}
|
|
}
|