private List<IncludeAllPage> flattenAndGroupRecursively(Directory dir, int maxDepth, int depth) {

    List<FileOrDir> fileOrDirs = new ArrayList<>();

    Set<String> pairedDirectories = new HashSet<>();

    Map<String, Directory> childDirs = dir.getDirectories();
    Map<String, FileResource> childFiles = dir.getFiles();

    for (FileResource fr : childFiles.values()) {
      String fileNameWithoutExt = fr.getFileNameWithoutExtensions();
      Optional<Directory> pairedDir = childDirs.containsKey(fileNameWithoutExt) ? of(childDirs.get(fileNameWithoutExt)) : empty();
      fileOrDirs.add(new FileOrDir(of(fr), pairedDir));
      
      pairedDir.ifPresent(d -> {
        pairedDirectories.add(d.getName());
      });
    }

    fileOrDirs.addAll(childDirs.values().stream()
        .filter(d -> !pairedDirectories.contains(d.getName()))
        .map(d -> new FileOrDir(empty(), of(d))).collect(Collectors.toList()));

    fileOrDirs.sort(FILE_OR_DIR_COMPARATOR);


    List<IncludeAllPage> frs = new ArrayList<>();
    for (FileOrDir fd : fileOrDirs) {
      if (depth > maxDepth) {

        if (frs.isEmpty()) {
          frs.add(new IncludeAllPage(depth, new ArrayList<>()));
        }
        IncludeAllPage singleFile = frs.get(0);
        fd.file.ifPresent(singleFile.files::add);
        fd.dir.ifPresent(d -> flattenAndGroupRecursively(d, maxDepth, depth + 1).forEach(iap -> singleFile.files.addAll(iap.files)));
        
      } else {
        IncludeAllPage fileRes = new IncludeAllPage(depth, new ArrayList<>());
        frs.add(fileRes);
        fd.file.ifPresent(fileRes.files::add);

        List<IncludeAllPage> pairedFiles =
            fd.dir.map(d -> flattenAndGroupRecursively(d, maxDepth, depth + 1)).orElse(Collections.emptyList());
        if (depth >= maxDepth) {
          pairedFiles.forEach(iap -> fileRes.files.addAll(iap.files));
        } else {
          pairedFiles.forEach(frs::add);
        }
      }
    }
    return frs;
  }
private List < IncludeAllPage > flattenAndGroupRecursively ( Directory dir , int maxDepth , int depth ) { List < FileOrDir > fileOrDirs = new ArrayList < > ( ) ; Set < String > pairedDirectories = new HashSet < > ( ) ; Map < String , Directory > childDirs = dir . getDirectories ( ) ; Map < String , FileResource > childFiles = dir . getFiles ( ) ; for ( FileResource fr : childFiles . values ( ) ) { String fileNameWithoutExt = fr . getFileNameWithoutExtensions ( ) ; Optional < Directory > pairedDir = childDirs . containsKey ( fileNameWithoutExt ) ? of ( childDirs . get ( fileNameWithoutExt ) ) : empty ( ) ; fileOrDirs . add ( new FileOrDir ( of ( fr ) , pairedDir ) ) ; pairedDir . ifPresent ( d -> { pairedDirectories . add ( d . getName ( ) ) ; } ) ; } fileOrDirs . addAll ( childDirs . values ( ) . stream ( ) . filter ( d -> ! pairedDirectories . contains ( d . getName ( ) ) ) . map ( d -> new FileOrDir ( empty ( ) , of ( d ) ) ) . collect ( Collectors . toList ( ) ) ) ; fileOrDirs . sort ( FILE_OR_DIR_COMPARATOR ) ; List < IncludeAllPage > frs = new ArrayList < > ( ) ; for ( FileOrDir fd : fileOrDirs ) { if ( depth > maxDepth ) { if ( frs . isEmpty ( ) ) { frs . add ( new IncludeAllPage ( depth , new ArrayList < > ( ) ) ) ; } IncludeAllPage singleFile = frs . get ( 0 ) ; fd . file . ifPresent ( singleFile . files :: add ) ; fd . dir . ifPresent ( d -> flattenAndGroupRecursively ( d , maxDepth , depth + 1 ) . forEach ( iap -> singleFile . files . addAll ( iap . files ) ) ) ; } else { IncludeAllPage fileRes = new IncludeAllPage ( depth , new ArrayList < > ( ) ) ; frs . add ( fileRes ) ; fd . file . ifPresent ( fileRes . files :: add ) ; List < IncludeAllPage > pairedFiles = fd . dir . map ( d -> flattenAndGroupRecursively ( d , maxDepth , depth + 1 ) ) . orElse ( Collections . emptyList ( ) ) ; if ( depth >= maxDepth ) { pairedFiles . forEach ( iap -> fileRes . files . addAll ( iap . files ) ) ; } else { pairedFiles . forEach ( frs :: add ) ; } } } return frs ; }
/*
From a given directory, it will flatten the file hierarchy in a list of "pages" (That can contain more than one FileResource)