package com.github.sormuras.bach.internal;

import com.github.sormuras.bach.ModuleLookup;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Optional;
import java.util.ServiceLoader;
import java.util.StringJoiner;

/** A module lookup that delegates to providers loaded from within the given directory. */
public final class DynamicModuleLookup implements ModuleLookup {

  private final Path directory;
  private ModuleLookup directoryLookup;
  private String directoryNames;

  public DynamicModuleLookup(Path directory) {
    this.directory = directory;
  }

  @Override
  public Optional<String> lookupModule(String module) {
    var computedNames = computeNames(directory);
    if (directoryLookup == null || !computedNames.equals(directoryNames)) {
      directoryLookup = computeModuleLookup(directory);
      directoryNames = computedNames;
    }
    return directoryLookup.lookupModule(module);
  }

  private static String computeNames(Path directory) {
    if (!Files.isDirectory(directory)) return "";
    var joiner = new StringJoiner("\n");
    try (var stream = Files.newDirectoryStream(directory, "*.jar")) {
      stream.forEach(path -> joiner.add(path.toString()));
      return joiner.toString();
    } catch (Exception exception) {
      throw new RuntimeException("Stream directory failed: " + directory, exception);
    }
  }

  private static ModuleLookup computeModuleLookup(Path directory) {
    if (!Files.isDirectory(directory)) return ModuleLookup.ofEmpty();
    var layer = ModuleLayerBuilder.build(directory);
    var loader = ServiceLoader.load(layer, ModuleLookup.class);
    var lookups = loader.stream().map(ServiceLoader.Provider::get).toList();
    return new ComposedModuleLookup(lookups);
  }
}

@@ -571,20 +571,107 @@
 ectory;%0A
+    this.directoryLookup = ModuleLookup.ofEmpty();%0A    this.directoryNames = %22%3Cinit%3E%22;%0A
   %7D%0A
-
 %0A  @Over
@@ -792,35 +792,8 @@
 if (
-directoryLookup == null %7C%7C 
 !com
@@ -929,16 +929,16 @@
 ;%0A    %7D%0A
-
     retu
@@ -979,24 +979,126 @@
 dule);%0A  %7D%0A%0A
+  @Override%0A  public String toString() %7B%0A    return %22DynamicModuleLookup -%3E %22 + directoryLookup;%0A  %7D%0A%0A
   private st
@@ -1867,16 +1867,61 @@
   return
+ lookups.isEmpty() ? ModuleLookup.ofEmpty() :
  new Com

Initialize all fields to non-null values