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 extends StrangeDbList { 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 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 queryUids(ListQuery query) throws IOException { ElementsArrayList uids = executeQuery(query, this, databaseTools.getObjectsIO()); return uids; } public ElementsArrayList query(ListQuery query) throws IOException { ElementsArrayList uids = queryUids(query); ElementsArrayList elements = new ElementsArrayList<>(uids.size()); uids.forEachParallelUnsorted((uid) -> elements.add(databaseTools.getObjectsIO().loadEnhancedObject(uid.objectUid))); return elements; } private ElementsArrayList 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 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 results = new ElementsArrayList<>(); for (ListQuery childQuery : children) { ElementsArrayList 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 main, ElementsList 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 toElementsArrayList(ElementsList elementsList) { if (elementsList instanceof EnhancedObjectStrangeDbList) { return new ElementsArrayList<>(((EnhancedObjectStrangeDbList) elementsList).getIndices()); } else if (elementsList instanceof ElementsArrayList) { return (ElementsArrayList) elementsList; } else { throw new UnsupportedOperationException(); } } @SuppressWarnings("unchecked") private static ElementsArrayList executeQueryElement(ListQuery.ListQueryElement query, ElementsList inputList, IObjectsIO dbio) throws IOException { ElementsArrayList 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 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 elementsUids = ((ElementsArrayList) inputList); elementsUids.forEachParallelUnsorted((elementUid) -> { List result = resolveItemFromDb(query.valuePointer, dbio, elementUid); if (result.parallelStream().anyMatch(query.valueOperation::evaluate)) { results.add(elementUid); } }); } return results; } private static List resolveItemFromDb(ValuePointer pointer, IObjectsIO objectsIO, EnhancedObjectIndices element) throws IOException { return resolveItemFromDbLoop(element, pointer, 0, pointer.size(), objectsIO, element); } @NotNull private static List 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 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); } }