strangedb/src/main/java/it/cavallium/strangedb/java/objects/lists/ValuePointer.java

212 lines
7.3 KiB
Java

package it.cavallium.strangedb.java.objects.lists;
import it.cavallium.strangedb.java.annotations.DbField;
import it.cavallium.strangedb.java.annotations.DbPrimitiveField;
import it.cavallium.strangedb.java.annotations.DbProperty;
import it.cavallium.strangedb.java.objects.EnhancedObject;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.apache.commons.lang3.reflect.MethodUtils;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
public class ValuePointer {
private static ValuePointer base = new ValuePointer(new int[0], new ClassPosition[0], new Object[0], null);
private final int[] pathNumbers;
private final ClassPosition[] pathTypes;
/**
* PropertyType
*/
private final Object[] additionalData;
/**
* ParentType
*/
private final Class<?> rootType;
private ValuePointer(int[] pathNumbers, ClassPosition[] pathTypes, Object[] additionalData, Class<?> rootType) {
this.pathNumbers = pathNumbers;
this.pathTypes = pathTypes;
this.additionalData = additionalData;
this.rootType = rootType;
}
public static <T extends EnhancedObject> ValuePointer ofField(Class<T> parentType, String field) {
return base.field(parentType, field);
}
public static <T extends EnhancedObject> ValuePointer ofProp(Class<T> parentType, String property) {
return base.prop(parentType, property);
}
public static <T extends EnhancedObject> ValuePointer ofPrimitiveField(Class<T> parentType, String primitiveFieldName) {
return base.primitiveField(parentType, primitiveFieldName);
}
public <T extends EnhancedObject> ValuePointer prop(Class<T> parentType, String propertyName) {
ClassPosition[] newPathTypes = append(this.pathTypes, ClassPosition.PROPERTY);
Method[] methods = MethodUtils.getMethodsWithAnnotation(parentType, DbProperty.class);
DbProperty dbProperty = null;
Class<?> propType = null;
for (Method method : methods) {
DbProperty annotation = method.getAnnotation(DbProperty.class);
if (annotation.name().equals(propertyName)) {
propType = method.getReturnType();
dbProperty = annotation;
break;
}
}
if (dbProperty == null) {
throw new IllegalArgumentException("Property '" + propertyName + "' not found in class " + parentType.getSimpleName());
}
int[] newPathNumbers = append(this.pathNumbers, dbProperty.id());
Object[] newAdditionalData = append(this.additionalData, propType);
Class<?> rootType = this.rootType == null ? parentType : this.rootType;
if (newAdditionalData.length > 1) {
newAdditionalData[newAdditionalData.length - 2] = parentType;
}
return new ValuePointer(newPathNumbers, newPathTypes, newAdditionalData, rootType);
}
public <T extends EnhancedObject> ValuePointer field(Class<T> parentType, String fieldName) {
ClassPosition[] newPathTypes = append(this.pathTypes, ClassPosition.FIELD);
Field[] fields = FieldUtils.getFieldsWithAnnotation(parentType, DbField.class);
DbField dbField = null;
Class<?> fieldType = null;
for (Field field : fields) {
DbField annotation = field.getAnnotation(DbField.class);
if (annotation.name().equals(fieldName)) {
fieldType = field.getType();
dbField = annotation;
break;
}
}
if (dbField == null) {
throw new IllegalArgumentException("Field '" + fieldName + "' not found in class " + parentType.getSimpleName());
}
int[] newPathNumbers = append(this.pathNumbers, dbField.id());
Object[] newAdditionalData = append(this.additionalData, fieldType);
if (newAdditionalData.length > 1) {
newAdditionalData[newAdditionalData.length - 2] = parentType;
}
Class<?> rootType = this.rootType == null ? parentType : this.rootType;
return new ValuePointer(newPathNumbers, newPathTypes, newAdditionalData, rootType);
}
public <T extends EnhancedObjectStrangeDbList<?>> ValuePointer allInList(Class<T> parentType) {
return inList(parentType, ClassPosition.LIST_ALL, -1);
}
public <T extends EnhancedObjectStrangeDbList<?>> ValuePointer firstInList(Class<T> parentType) {
return inList(parentType, ClassPosition.LIST_FIRST, -1);
}
public <T extends EnhancedObjectStrangeDbList<?>> ValuePointer lastInList(Class<T> parentType) {
return inList(parentType, ClassPosition.LIST_LAST, -1);
}
public <T extends EnhancedObjectStrangeDbList<?>> ValuePointer indexInList(Class<T> parentType, int position) {
return inList(parentType, ClassPosition.LIST_POSITION, position);
}
private <T extends EnhancedObjectStrangeDbList<?>> ValuePointer inList(Class<T> parentType, int listPosition, int itemIndex) {
ClassPosition[] newPathTypes = append(this.pathTypes, ClassPosition.ENHANCED_LIST);
int[] newPathNumbers = append(this.pathNumbers, listPosition);
Object[] newAdditionalData = append(this.additionalData, itemIndex);
if (newAdditionalData.length > 1) {
newAdditionalData[newAdditionalData.length - 2] = parentType;
}
Class<?> rootType = this.rootType == null ? parentType : this.rootType;
return new ValuePointer(newPathNumbers, newPathTypes, newAdditionalData, rootType);
}
public <T extends EnhancedObject> ValuePointer primitiveField(Class<T> parentType, String primitiveFieldName) {
ClassPosition[] newPathTypes = append(this.pathTypes, ClassPosition.PRIMITIVE_FIELD);
Field[] fields = FieldUtils.getFieldsWithAnnotation(parentType, DbPrimitiveField.class);
DbPrimitiveField dbField = null;
Class<?> fieldType = null;
for (Field field : fields) {
DbPrimitiveField annotation = field.getAnnotation(DbPrimitiveField.class);
if (annotation.name().equals(primitiveFieldName)) {
fieldType = field.getType();
dbField = annotation;
break;
}
}
if (dbField == null) {
throw new IllegalArgumentException("Field '" + primitiveFieldName + "' not found in class " + parentType.getSimpleName());
}
int[] newPathNumbers = append(this.pathNumbers, dbField.id());
Object[] newAdditionalData = append(this.additionalData, fieldType);
if (newAdditionalData.length > 1) {
newAdditionalData[newAdditionalData.length - 2] = parentType;
}
Class<?> rootType = this.rootType == null ? parentType : this.rootType;
return new ValuePointer(newPathNumbers, newPathTypes, newAdditionalData, rootType);
}
public ValuePointer resetPointerTo(int index) {
if (index >= pathNumbers.length) {
throw new ArrayIndexOutOfBoundsException();
}
if (index == pathNumbers.length - 1) {
return this;
}
return new ValuePointer(Arrays.copyOf(this.pathNumbers, index + 1),
Arrays.copyOf(this.pathTypes, index + 1),
Arrays.copyOf(this.additionalData, index + 1),
rootType);
}
public int size() {
return this.pathNumbers.length;
}
public boolean hasPrevious() {
return pathNumbers.length > 0;
}
public ValuePointer previous() {
return resetPointerTo(pathNumbers.length - 2);
}
private static int[] append(int[] array, int value) {
int[] newArray = Arrays.copyOf(array, array.length + 1);
newArray[array.length] = value;
return newArray;
}
private static <T> T[] append(T[] array, T value) {
T[] newArray = Arrays.copyOf(array, array.length + 1);
newArray[array.length] = value;
return newArray;
}
public int getPathNumber() {
return pathNumbers[pathNumbers.length - 1];
}
public ClassPosition getPathType() {
return pathTypes[pathTypes.length - 1];
}
@SuppressWarnings("unchecked")
public <T> T getAdditionalData() {
return (T) additionalData[additionalData.length - 1];
}
public Class<?> getRootType() {
return rootType;
}
}