package org.warp.cowdb; import org.apache.commons.lang3.NotImplementedException; import org.apache.commons.lang3.reflect.MethodUtils; import org.warp.cowdb.database.EnhancedObjectFullInfo; import org.warp.jcwdb.ann.DBClass; import org.warp.jcwdb.ann.DBDataType; import org.warp.jcwdb.ann.DBPropertyGetter; import org.warp.jcwdb.ann.DBPropertySetter; import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Map; public abstract class EnhancedObject { protected final int version; protected IDatabaseTools databaseTools; 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 setterMethods; private Map getterMethods; public EnhancedObject() { version = getClassVersion(); } public EnhancedObject(IDatabaseTools databaseTools) throws IOException { this.databaseTools = databaseTools; version = getClassVersion(); databaseTools.initializeEnhancedObject(this); } 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 setterMethods, Map 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(version, fieldReferences, fieldTypes, fields, propertyReferences, propertyTypes, loadedPropertyValues); } public 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 getProperty(int propertyId) throws IOException { if (!loadedProperties[propertyId]) { long propertyUID = propertyReferences[propertyId]; databaseTools.getObjectsIO().loadProperty(this, propertyId, propertyGetters[propertyId], propertyTypes[propertyId], propertyUID); } return (T) loadedPropertyValues[propertyId]; } public 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 void setProperty(int propertyId, T value) { loadedPropertyValues[propertyId] = value; loadedProperties[propertyId] = true; } private int getClassVersion() { DBClass classAnnotation = this.getClass().getAnnotation(DBClass.class); return classAnnotation == null ? 0 : classAnnotation.version(); } /** * Called when an unloaded class is older than the actual version * @param oldObjectVersion * @param enhancedObjectUpgrader * @throws IOException */ public void onUpgrade(int oldObjectVersion, EnhancedObjectUpgrader enhancedObjectUpgrader) throws IOException { throw new NotImplementedException("Method onUpgrade() is not implemented for class " + this.getClass().getSimpleName()); } public IDatabaseTools getDatabaseTools() { return databaseTools; } public void setDatabaseTools(IDatabaseTools databaseTools) { this.databaseTools = databaseTools; } }