316 lines
12 KiB
Java
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);
|
|
}
|
|
}
|