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

316 lines
12 KiB
Java

package it.cavallium.strangedb.java.objects.lists;
import it.cavallium.strangedb.java.annotations.DbDataType;
import it.cavallium.strangedb.java.annotations.DbField;
import it.cavallium.strangedb.java.database.IDatabaseTools;
import it.cavallium.strangedb.java.database.IObjectsIO;
import it.cavallium.strangedb.java.objects.EnhancedObject;
import it.cavallium.strangedb.java.objects.EnhancedObjectIndices;
import it.unimi.dsi.fastutil.longs.LongArrayList;
import it.unimi.dsi.fastutil.objects.ObjectList;
import it.unimi.dsi.fastutil.objects.ObjectLists;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletionException;
import java.util.stream.Collectors;
public class EnhancedObjectStrangeDbList<T extends EnhancedObject> extends StrangeDbList<T> {
private static final int INDICES_REF_ID = 0;
@DbField(id = INDICES_REF_ID, type = DbDataType.REFERENCES_LIST, name = "indices")
private LongArrayList indices;
@Override
protected LongArrayList getIndices() {
return indices;
}
public EnhancedObjectStrangeDbList() {
super();
}
@Deprecated
public EnhancedObjectStrangeDbList(IDatabaseTools databaseTools, Class<T> type) throws IOException {
this(databaseTools);
}
public EnhancedObjectStrangeDbList(IDatabaseTools databaseTools) throws IOException {
super(databaseTools);
indices = new LongArrayList();
}
@Override
protected T loadItem(long uid) throws IOException {
return databaseTools.getObjectsIO().loadEnhancedObject(uid);
}
@Override
protected void writeItemToDisk(long uid, T item) throws IOException {
databaseTools.getObjectsIO().setEnhancedObject(uid, item);
}
public ElementsArrayList<EnhancedObjectIndices> queryUids(ListQuery query) throws IOException {
ElementsArrayList<EnhancedObjectIndices> uids = executeQuery(query, this, databaseTools.getObjectsIO());
return uids;
}
public ElementsArrayList<T> query(ListQuery query) throws IOException {
ElementsArrayList<EnhancedObjectIndices> uids = queryUids(query);
ElementsArrayList<T> elements = new ElementsArrayList<>(uids.size());
uids.forEachParallelUnsorted((uid) -> elements.add(databaseTools.getObjectsIO().loadEnhancedObject(uid.objectUid)));
return elements;
}
private ElementsArrayList<EnhancedObjectIndices> executeQuery(ListQuery query, ElementsList<?> inputList, IObjectsIO dbio) throws IOException {
if (query instanceof ListQuery.ListQueryElement) {
return executeQueryElement((ListQuery.ListQueryElement) query, inputList, dbio);
} else if (query instanceof ListQuery.ListQueryAnd) {
ListQuery[] children = ((ListQuery.ListQueryAnd) query).getQueryChildren();
ElementsArrayList<EnhancedObjectIndices> results = null;
for (ListQuery childQuery : children) {
results = executeQuery(childQuery, results == null ? inputList : results, dbio);
if (results.size() == 0) break;
}
return results;
} else if (query instanceof ListQuery.ListQueryOr) {
ListQuery[] children = ((ListQuery.ListQueryOr) query).getQueryChildren();
ElementsArrayList<EnhancedObjectIndices> results = new ElementsArrayList<>();
for (ListQuery childQuery : children) {
ElementsArrayList<EnhancedObjectIndices> childResults = executeQuery(childQuery, inputList, dbio);
int childResultsSize = childResults.size();
if (childResultsSize == inputList.size()) {
return childResults;
}
addMissingElements(results, childResults);
}
return results;
} else {
throw new RuntimeException("Not implemented!");
}
}
static void addMissingElements(ElementsArrayList<EnhancedObjectIndices> main, ElementsList<EnhancedObjectIndices> elementsToAdd) throws IOException {
int elementsSize = elementsToAdd.size();
for (int i = 0; i < elementsSize; i++) {
EnhancedObjectIndices childResultElement = elementsToAdd.get(i);
if (!main.contains(childResultElement)) {
main.add(childResultElement);
}
}
}
/**
* This method is slow
*
* @param elementsList
* @return
*/
@Deprecated
@SuppressWarnings("unchecked")
static ElementsArrayList<Long> toElementsArrayList(ElementsList<?> elementsList) {
if (elementsList instanceof EnhancedObjectStrangeDbList<?>) {
return new ElementsArrayList<>(((EnhancedObjectStrangeDbList) elementsList).getIndices());
} else if (elementsList instanceof ElementsArrayList<?>) {
return (ElementsArrayList<Long>) elementsList;
} else {
throw new UnsupportedOperationException();
}
}
@SuppressWarnings("unchecked")
private static ElementsArrayList<EnhancedObjectIndices> executeQueryElement(ListQuery.ListQueryElement query, ElementsList<?> inputList, IObjectsIO dbio) throws IOException {
ElementsArrayList<EnhancedObjectIndices> results = new ElementsArrayList<>();
if (inputList instanceof EnhancedObjectStrangeDbList<?>) {
EnhancedObjectStrangeDbList<?> dbList = ((EnhancedObjectStrangeDbList<?>) inputList);
dbList.forEachIndexParallelUnsorted((Long elementUid) -> {
EnhancedObjectIndices elementUids = dbio.loadEnhancedObjectUids(elementUid); // check if the parent object is the declared type
if (elementUids != null) {
Class<?> declaredRootType = query.valuePointer.getRootType();
Class<?> obtainedRootType = elementUids.type;
if (isInstanceOf(obtainedRootType, declaredRootType)) {
List<Object> result = resolveItemFromDb(query.valuePointer, dbio, elementUids);
if (result.size() == 1) {
if (query.valueOperation.evaluate(result.get(0))) {
results.add(elementUids);
}
} else if (result.size() > 1) {
if (result.parallelStream().anyMatch(query.valueOperation::evaluate)) {
results.add(elementUids);
}
}
} else {
//todo: use logging api
System.err.println(obtainedRootType.getSimpleName() + " is not instance of " + declaredRootType.getSimpleName());
}
}
});
} else if (inputList instanceof ElementsArrayList<?>) {
ElementsArrayList<EnhancedObjectIndices> elementsUids = ((ElementsArrayList<EnhancedObjectIndices>) inputList);
elementsUids.forEachParallelUnsorted((elementUid) -> {
List<Object> result = resolveItemFromDb(query.valuePointer, dbio, elementUid);
if (result.parallelStream().anyMatch(query.valueOperation::evaluate)) {
results.add(elementUid);
}
});
}
return results;
}
private static List<Object> resolveItemFromDb(ValuePointer pointer, IObjectsIO objectsIO, EnhancedObjectIndices element) throws IOException {
return resolveItemFromDbLoop(element, pointer, 0, pointer.size(), objectsIO, element);
}
@NotNull
private static List<Object> resolveItemFromDbLoop(@NotNull EnhancedObjectIndices currentElement, ValuePointer pointer, int pointerPosition, int pointerSize, IObjectsIO objectsIO, EnhancedObjectIndices element) throws IOException {
if (pointerPosition > 0) {
// check if the object that we obtained is the declared type
Class<?> declaredType = pointer.resetPointerTo(pointerPosition - 1).getAdditionalData();
Class<?> obtainedType = currentElement.type;
if (!isInstanceOf(obtainedType, declaredType)) {
return ObjectLists.emptyList();
}
if (pointerPosition >= pointerSize) {
throw new IOException("The pointer is empty");
}
}
boolean isLastElement = pointerPosition >= pointer.size() - 1;
ValuePointer currentPointer = pointer.resetPointerTo(pointerPosition);
int pathNumber = currentPointer.getPathNumber();
ClassPosition valueType = currentPointer.getPathType();
List<EnhancedObjectIndices> multipleCurrentElements = null;
switch (valueType) {
case ENHANCED_LIST: {
switch (currentPointer.getPathNumber()) {
case ClassPosition.LIST_ALL: {
if (isLastElement) {
try {
return objectsIO.loadReferencesList(currentElement.fieldUids[INDICES_REF_ID]).parallelStream().map((reference) -> {
try {
return objectsIO.loadEnhancedObject(reference);
} catch (IOException e) {
throw new CompletionException(e);
}
}).filter(Objects::nonNull).collect(Collectors.toList());
} catch (CompletionException e) {
throw new IOException(e.getCause());
}
} else {
LongArrayList refList = objectsIO.loadReferencesList(currentElement.fieldUids[INDICES_REF_ID]);
try {
multipleCurrentElements = refList.parallelStream().map((reference) -> {
try {
return objectsIO.loadEnhancedObjectUids(reference);
} catch (IOException e) {
throw new CompletionException(e);
}
}).collect(Collectors.toList());
} catch (CompletionException e) {
throw new IOException(e.getCause());
}
}
break;
}
case ClassPosition.LIST_FIRST: {
try {
Long firstElementReference = objectsIO.loadReferencesListFirstElement(currentElement.fieldUids[INDICES_REF_ID]);
if (firstElementReference == null) {
return ObjectLists.emptyList();
} else {
currentElement = objectsIO.loadEnhancedObjectUids(firstElementReference);
}
} catch (CompletionException e) {
throw new IOException(e.getCause());
}
break;
}
case ClassPosition.LIST_LAST: {
try {
Long firstElementReference = objectsIO.loadReferencesListLastElement(currentElement.fieldUids[INDICES_REF_ID]);
if (firstElementReference == null) {
return ObjectLists.emptyList();
} else {
currentElement = objectsIO.loadEnhancedObjectUids(firstElementReference);
}
} catch (CompletionException e) {
throw new IOException(e.getCause());
}
break;
}
default:
throw new IOException("Unknown class position");
}
break;
}
case FIELD: {
if (isLastElement) {
//TODO: getBlock field data type. it can be an enhancedObject or an object
Object loadedField = objectsIO.loadObject(currentElement.fieldUids[pathNumber]);
if (loadedField == null) return ObjectLists.emptyList();
return ObjectLists.singleton(loadedField);
} else {
currentElement = objectsIO.loadEnhancedObjectUids(currentElement.fieldUids[pathNumber]);
}
break;
}
case PRIMITIVE_FIELD: {
if (isLastElement) {
//TODO: getBlock primitive type
Object loadedField = objectsIO.loadPrimitiveData(currentElement.nativeDataUid).getLong(pathNumber);
return ObjectLists.singleton(loadedField);
} else {
throw new IllegalArgumentException("You can access to a type field only in the last pointer");
}
}
case PROPERTY: {
if (isLastElement) {
//TODO: getBlock field data type. it can be an enhancedObject or an object
Object loadedProperty = objectsIO.loadObject(currentElement.fieldUids[pathNumber]);
if (loadedProperty == null) return ObjectLists.emptyList();
return ObjectLists.singleton(loadedProperty);
} else {
currentElement = objectsIO.loadEnhancedObjectUids(currentElement.propertyUids[pathNumber]);
}
break;
}
default: {
throw new IllegalArgumentException("Not implemented");
}
}
if (multipleCurrentElements == null) {
if (currentElement == null) return ObjectLists.emptyList();
return resolveItemFromDbLoop(currentElement, pointer, pointerPosition + 1, pointerSize, objectsIO, element);
} else {
try {
return multipleCurrentElements
.parallelStream()
.map((elem) -> {
try {
return resolveItemFromDbLoop(elem, pointer, pointerPosition + 1, pointerSize, objectsIO, element);
} catch (IOException e) {
throw new CompletionException(e);
}
})
.flatMap(List::stream)
.collect(Collectors.toList());
} catch (CompletionException ex) {
throw new IOException(ex.getCause());
}
}
}
private static boolean isInstanceOf(Class<?> clazz, Class<?> obj) {
return obj.isAssignableFrom(clazz);
}
}