private CompletableFuture<Project> readSchemaMetadata(
        final Project project,
        final Dbms dbms,
        final Predicate<String> filterCriteria,
        final ProgressMeasure progress
    ) {
        requireNonNulls(project, dbms, filterCriteria, progress);

        extraInfo("readSchemaMetadata(%s, %s, ...)", project, dbms);

        final DbmsType dbmsType = dbmsTypeOf(dbmsHandlerComponent, dbms);
        final String action = actionName(dbms);

        LOGGER.info(action);
        progress.setCurrentAction(action);

        final Set<String> discardedSchemas = new HashSet<>();
        final DatabaseNamingConvention naming = dbmsType.getDatabaseNamingConvention();
        final Set<TypeInfoMetaData> preSet = dbmsType.getDataTypes();

        // Task that downloads the SQL Type Mappings from the database
        final CompletableFuture<Map<String, Class<?>>> sqlTypeMappingTask = CompletableFuture.supplyAsync(() ->
            loadSqlTypeMappings(dbms, preSet)
        );

        // Task that downloads the schemas from the database
        final CompletableFuture<Void> schemasTask = CompletableFuture.runAsync(() ->
            loadSchemas(dbms, filterCriteria, dbmsType, discardedSchemas, naming)
        );

        // Task that downloads the catalogs from the database
        final CompletableFuture<Void> catalogsTask = CompletableFuture.runAsync(() ->
            loadCatalogs(dbms, filterCriteria, discardedSchemas, naming)
        );

        // Create a new task that will execute once the schemas and the catalogs 
        // have been loaded independently of each other.
        return CompletableFuture.allOf(
            schemasTask,
            catalogsTask
        ).thenComposeAsync(v -> {
            @SuppressWarnings({"unchecked", "rawtypes"})
            final CompletableFuture<Schema>[] tablesTask
                = dbms.schemas()
                .map(schema -> tables(sqlTypeMappingTask, dbms, schema, progress))
                .toArray(s -> (CompletableFuture<Schema>[]) new CompletableFuture[s]);

            return CompletableFuture.allOf(tablesTask)
                .handleAsync((v2, ex) -> {
                    if (ex == null) {
                        if (tablesTask.length == 0) {
                            throw new SpeedmentException(
                                "Could not find any matching schema. The following schemas was considered: " + discardedSchemas + "."
                            );
                        } else {
                            return project;
                        }
                    } else {
                        throw new SpeedmentException(
                            "An exception occurred while the tables were loading.", ex
                        );
                    }
                });
        });
    }
@author  Emil Forslund
@author  Per Minborg
@since   3.0.0
runtime-parent/runtime-core/src/main/java/com/speedment/runtime/core/abstracts/AbstractDbmsMetadataHandler.java