private static void traverseRecursively(
     CompileContext context,
     final JavaSourceRootDescriptor rd,
     final File file,
     Set<File> excludes,
     @NotNull final Timestamps tsStorage,
     final boolean forceDirty,
     @Nullable Set<File> currentFiles)
     throws IOException {
   final File[] children = file.listFiles();
   if (children != null) { // is directory
     if (children.length > 0 && !JpsPathUtil.isUnder(excludes, file)) {
       for (File child : children) {
         traverseRecursively(context, rd, child, excludes, tsStorage, forceDirty, currentFiles);
       }
     }
   } else { // is file
     boolean markDirty = forceDirty;
     if (!markDirty) {
       markDirty = tsStorage.getStamp(file, rd.target) != FileSystemUtil.lastModified(file);
     }
     if (markDirty) {
       // if it is full project rebuild, all storages are already completely cleared;
       // so passing null because there is no need to access the storage to clear non-existing data
       final Timestamps marker = context.isProjectRebuild() ? null : tsStorage;
       context.getProjectDescriptor().fsState.markDirty(context, file, rd, marker);
     }
     if (currentFiles != null) {
       currentFiles.add(file);
     }
   }
 }
 private static void traverseRecursively(
     CompileContext context,
     final BuildRootDescriptor rd,
     final CompilationRound round,
     final File file,
     @NotNull final Timestamps tsStorage,
     final boolean forceDirty,
     @Nullable Set<File> currentFiles,
     @Nullable FileFilter filter,
     @NotNull FSCache fsCache)
     throws IOException {
   BuildRootIndex rootIndex = context.getProjectDescriptor().getBuildRootIndex();
   final File[] children = fsCache.getChildren(file);
   if (children != null) { // is directory
     if (children.length > 0 && rootIndex.isDirectoryAccepted(file, rd)) {
       for (File child : children) {
         traverseRecursively(
             context, rd, round, child, tsStorage, forceDirty, currentFiles, filter, fsCache);
       }
     }
   } else { // is file
     if (rootIndex.isFileAccepted(file, rd) && (filter == null || filter.accept(file))) {
       boolean markDirty = forceDirty;
       if (!markDirty) {
         markDirty = tsStorage.getStamp(file, rd.getTarget()) != FileSystemUtil.lastModified(file);
       }
       if (markDirty) {
         // if it is full project rebuild, all storages are already completely cleared;
         // so passing null because there is no need to access the storage to clear non-existing
         // data
         final Timestamps marker = context.isProjectRebuild() ? null : tsStorage;
         context.getProjectDescriptor().fsState.markDirty(context, round, file, rd, marker);
       }
       if (currentFiles != null) {
         currentFiles.add(file);
       }
     }
   }
 }