Files
prog-intro/java/base/Selector.java
2026-04-13 10:38:59 +03:00

203 lines
6.1 KiB
Java

package base;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
/**
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
*/
public final class Selector {
private final Class<?> owner;
private final List<String> modes;
private final Set<String> variantNames = new LinkedHashSet<>();
private final Map<String, Consumer<TestCounter>> variants =
new LinkedHashMap<>();
public Selector(final Class<?> owner, final String... modes) {
this.owner = owner;
this.modes = List.of(modes);
}
public Selector variant(
final String name,
final Consumer<TestCounter> operations
) {
Asserts.assertTrue(
"Duplicate variant " + name,
variants.put(name.toLowerCase(), operations) == null
);
variantNames.add(name);
return this;
}
private static void check(
final boolean condition,
final String format,
final Object... args
) {
if (!condition) {
throw new IllegalArgumentException(String.format(format, args));
}
}
@SuppressWarnings("UseOfSystemOutOrSystemErr")
public void main(final String... args) {
try {
final String mode;
if (modes.isEmpty()) {
check(
args.length >= 1,
"At least one argument expected, found %s",
args.length
);
mode = "";
} else {
check(
args.length >= 2,
"At least two arguments expected, found %s",
args.length
);
mode = args[0];
}
final List<String> vars = Arrays.stream(args)
.skip(modes.isEmpty() ? 0 : 1)
.flatMap(arg -> Arrays.stream(arg.split("[ +]+")))
.toList();
test(mode, vars);
} catch (final IllegalArgumentException e) {
System.err.println("ERROR: " + e.getMessage());
if (modes.isEmpty()) {
System.err.println("Usage: " + owner.getName() + " VARIANT...");
} else {
System.err.println(
"Usage: " + owner.getName() + " MODE VARIANT..."
);
System.err.println("Modes: " + String.join(", ", modes));
}
System.err.println("Variants: " + String.join(", ", variantNames));
System.exit(1);
}
}
public void test(final String mode, List<String> vars) {
final int modeNo = modes.isEmpty() ? -1 : modes.indexOf(mode);
check(modes.isEmpty() || modeNo >= 0, "Unknown mode '%s'", mode);
if (variantNames.contains("Base") && !vars.contains("Base")) {
vars = new ArrayList<>(vars);
vars.add(0, "Base");
}
vars.forEach(variant ->
check(
variants.containsKey(variant.toLowerCase()),
"Unknown variant '%s'",
variant
)
);
final Map<String, String> properties = modes.isEmpty()
? Map.of("variant", String.join("+", vars))
: Map.of("variant", String.join("+", vars), "mode", mode);
final TestCounter counter = new TestCounter(owner, modeNo, properties);
counter.printHead();
vars.forEach(variant ->
counter.scope("Testing " + variant, () ->
variants.get(variant.toLowerCase()).accept(counter)
)
);
counter.printStatus();
}
public static <V extends Tester> Composite<V> composite(
final Class<?> owner,
final Function<TestCounter, V> factory,
final String... modes
) {
return new Composite<>(
owner,
factory,
(tester, counter) -> tester.test(),
modes
);
}
public static <V> Composite<V> composite(
final Class<?> owner,
final Function<TestCounter, V> factory,
final BiConsumer<V, TestCounter> tester,
final String... modes
) {
return new Composite<>(owner, factory, tester, modes);
}
public List<String> getModes() {
return modes.isEmpty() ? List.of("~") : modes;
}
public List<String> getVariants() {
return List.copyOf(variants.keySet());
}
/**
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
*/
public static final class Composite<V> {
private final Selector selector;
private final Function<TestCounter, V> factory;
private final BiConsumer<V, TestCounter> tester;
private List<Consumer<? super V>> base;
private Composite(
final Class<?> owner,
final Function<TestCounter, V> factory,
final BiConsumer<V, TestCounter> tester,
final String... modes
) {
selector = new Selector(owner, modes);
this.factory = factory;
this.tester = tester;
}
@SafeVarargs
public final Composite<V> variant(
final String name,
final Consumer<? super V>... parts
) {
if ("Base".equalsIgnoreCase(name)) {
base = List.of(parts);
return v(name.toLowerCase());
} else {
return v(name, parts);
}
}
@SafeVarargs
private Composite<V> v(
final String name,
final Consumer<? super V>... parts
) {
selector.variant(name, counter -> {
final V variant = factory.apply(counter);
for (final Consumer<? super V> part : base) {
part.accept(variant);
}
for (final Consumer<? super V> part : parts) {
part.accept(variant);
}
tester.accept(variant, counter);
});
return this;
}
public Selector selector() {
return selector;
}
}
}