##// END OF EJS Templates
common: extract replayable queue
cin -
r53:07d0d84bc0a2 default
parent child
Show More
@@ -0,0 +1,89
1 package org.implab.gradle.common.core.lang;
2
3 import java.util.LinkedList;
4 import java.util.List;
5 import java.util.function.Consumer;
6
7 import org.eclipse.jdt.annotation.NonNullByDefault;
8
9 /**
10 * Replayable append-only notification queue.
11 *
12 * <p>
13 * The queue stores values in insertion order and supports replaying all values
14 * that were already added when a consumer is registered. The same consumer is
15 * also retained and invoked for every value added later.
16 *
17 * <p>
18 * Values are committed before dispatch. If a consumer throws while a value is
19 * being added, the value remains recorded in the queue and will be visible to
20 * later consumers through {@link #forEach(Consumer)} and {@link #values()}.
21 *
22 * <p>
23 * Reentrant replay is intentionally not supported. Consumers must not call
24 * {@link #add(Object)} or {@link #forEach(Consumer)} on the same queue while
25 * they are being invoked by that queue. Such nested calls fail fast with
26 * {@link IllegalStateException}. This keeps ordering predictable and avoids
27 * recursive event-loop semantics.
28 *
29 * @param <T> value type
30 */
31 @NonNullByDefault
32 public class ReplayableQueue<T> {
33 private final List<Consumer<? super T>> consumers = new LinkedList<>();
34 private final List<T> values = new LinkedList<>();
35 private boolean replaying = false;
36
37 /**
38 * Adds a value and dispatches it to all registered consumers.
39 *
40 * <p>
41 * The value is recorded before consumers are invoked. If dispatch fails, the
42 * value still belongs to the queue.
43 *
44 * @param value value to add
45 */
46 public void add(T value) {
47 safeInvoke(value, v -> {
48 values.add(v);
49 consumers.forEach(consumer -> consumer.accept(v));
50 });
51 }
52
53 /**
54 * Returns an immutable snapshot of values recorded so far.
55 *
56 * @return current values in insertion order
57 */
58 public List<T> values() {
59 return List.copyOf(values);
60 }
61
62 /**
63 * Replays all recorded values to the consumer and registers it for future
64 * values.
65 *
66 * <p>
67 * The consumer is registered only after replaying existing values succeeds.
68 *
69 * @param consumer consumer to replay and retain
70 */
71 public void forEach(Consumer<? super T> consumer) {
72 safeInvoke(values, v -> {
73 v.forEach(consumer);
74 consumers.add(consumer);
75 });
76 }
77
78 private <X> void safeInvoke(X value, Consumer<? super X> consumer) {
79 if (replaying)
80 throw new IllegalStateException("Reentrant replay is not supported: replay is in progress");
81 try {
82 replaying = true;
83 consumer.accept(value);
84 } finally {
85 replaying = false;
86 }
87 }
88
89 }
@@ -0,0 +1,59
1 package org.implab.gradle.common.core.lang;
2
3 import static org.junit.jupiter.api.Assertions.assertEquals;
4 import static org.junit.jupiter.api.Assertions.assertThrows;
5 import static org.junit.jupiter.api.Assertions.assertTrue;
6
7 import java.util.ArrayList;
8 import java.util.List;
9
10 import org.junit.jupiter.api.Test;
11
12 class ReplayableQueueTest {
13 @Test
14 void replaysExistingValuesAndReceivesFutureValues() {
15 var queue = new ReplayableQueue<String>();
16 var seen = new ArrayList<String>();
17
18 queue.add("one");
19 queue.add("two");
20 queue.forEach(seen::add);
21 queue.add("three");
22
23 assertEquals(List.of("one", "two", "three"), seen);
24 assertEquals(List.of("one", "two", "three"), queue.values());
25 }
26
27 @Test
28 void commitsValueBeforeDispatchingConsumers() {
29 var queue = new ReplayableQueue<String>();
30
31 queue.forEach(value -> {
32 throw new IllegalStateException("boom");
33 });
34
35 assertThrows(IllegalStateException.class, () -> queue.add("one"));
36 assertEquals(List.of("one"), queue.values());
37 }
38
39 @Test
40 void rejectsReentrantAdd() {
41 var queue = new ReplayableQueue<String>();
42
43 queue.forEach(value -> queue.add("nested"));
44
45 var ex = assertThrows(IllegalStateException.class, () -> queue.add("one"));
46 assertTrue(ex.getMessage().contains("Reentrant replay is not supported"));
47 }
48
49 @Test
50 void rejectsReentrantForEach() {
51 var queue = new ReplayableQueue<String>();
52 queue.add("one");
53
54 var ex = assertThrows(IllegalStateException.class, () -> queue.forEach(value -> queue.forEach(nested -> {
55 })));
56
57 assertTrue(ex.getMessage().contains("Reentrant replay is not supported"));
58 }
59 }
@@ -1,90 +1,90
1 package org.implab.gradle.variants.artifacts.internal;
1 package org.implab.gradle.variants.artifacts.internal;
2
2
3 import java.util.LinkedHashMap;
3 import java.util.LinkedHashMap;
4 import java.util.Map;
4 import java.util.Map;
5 import java.util.Optional;
5 import java.util.Optional;
6 import java.util.function.Function;
6 import java.util.function.Function;
7
7
8 import org.eclipse.jdt.annotation.NonNullByDefault;
8 import org.eclipse.jdt.annotation.NonNullByDefault;
9 import org.gradle.api.Action;
9 import org.gradle.api.Action;
10 import org.gradle.api.InvalidUserDataException;
10 import org.gradle.api.InvalidUserDataException;
11 import org.gradle.api.Task;
11 import org.gradle.api.Task;
12 import org.gradle.api.file.FileSystemLocation;
12 import org.gradle.api.file.FileSystemLocation;
13 import org.gradle.api.provider.Provider;
13 import org.gradle.api.provider.Provider;
14 import org.gradle.api.tasks.TaskProvider;
14 import org.gradle.api.tasks.TaskProvider;
15 import org.implab.gradle.common.core.lang.Deferred;
15 import org.implab.gradle.common.core.lang.Deferred;
16 import org.implab.gradle.internal.ReplayableQueue;
16 import org.implab.gradle.common.core.lang.ReplayableQueue;
17 import org.implab.gradle.variants.artifacts.ArtifactAssemblies;
17 import org.implab.gradle.variants.artifacts.ArtifactAssemblies;
18 import org.implab.gradle.variants.artifacts.ArtifactAssembly;
18 import org.implab.gradle.variants.artifacts.ArtifactAssembly;
19 import org.implab.gradle.variants.artifacts.ArtifactSlot;
19 import org.implab.gradle.variants.artifacts.ArtifactSlot;
20
20
21 @NonNullByDefault
21 @NonNullByDefault
22 public class ArtifactAssemblyRegistry implements ArtifactAssemblies {
22 public class ArtifactAssemblyRegistry implements ArtifactAssemblies {
23 private final Map<ArtifactSlot, Deferred<ArtifactAssembly>> assembliesBySlots = new LinkedHashMap<>();
23 private final Map<ArtifactSlot, Deferred<ArtifactAssembly>> assembliesBySlots = new LinkedHashMap<>();
24 private final ReplayableQueue<ArtifactAssembly> assemblies = new ReplayableQueue<>();
24 private final ReplayableQueue<ArtifactAssembly> assemblies = new ReplayableQueue<>();
25
25
26 public ArtifactAssemblyRegistry() {
26 public ArtifactAssemblyRegistry() {
27 }
27 }
28
28
29 public <T extends Task> ArtifactAssembly register(
29 public <T extends Task> ArtifactAssembly register(
30 ArtifactSlot slot,
30 ArtifactSlot slot,
31 TaskProvider<T> task,
31 TaskProvider<T> task,
32 Function<? super T, ? extends Provider<? extends FileSystemLocation>> mapOutputArtifact) {
32 Function<? super T, ? extends Provider<? extends FileSystemLocation>> mapOutputArtifact) {
33
33
34 var deferred = getDeferred(slot);
34 var deferred = getDeferred(slot);
35 if (deferred.resolved()) {
35 if (deferred.resolved()) {
36 throw new InvalidUserDataException("Artifact assembly '" + slot + "' is already registered");
36 throw new InvalidUserDataException("Artifact assembly '" + slot + "' is already registered");
37 }
37 }
38 var outputArtifact = task.flatMap(mapOutputArtifact::apply);
38 var outputArtifact = task.flatMap(mapOutputArtifact::apply);
39
39
40 var assembly = new Assembly(outputArtifact, task);
40 var assembly = new Assembly(outputArtifact, task);
41 deferred.resolve(assembly);
41 deferred.resolve(assembly);
42 assemblies.add(assembly);
42 assemblies.add(assembly);
43 return assembly;
43 return assembly;
44 }
44 }
45
45
46 @Override
46 @Override
47 public Optional<ArtifactAssembly> find(ArtifactSlot slot) {
47 public Optional<ArtifactAssembly> find(ArtifactSlot slot) {
48 // to prevent creation of map entries on lookup use the map directly
48 // to prevent creation of map entries on lookup use the map directly
49 var deferred = assembliesBySlots.get(slot);
49 var deferred = assembliesBySlots.get(slot);
50 return deferred != null && deferred.resolved()
50 return deferred != null && deferred.resolved()
51 ? Optional.of(deferred.value())
51 ? Optional.of(deferred.value())
52 : Optional.empty();
52 : Optional.empty();
53 }
53 }
54
54
55 @Override
55 @Override
56 public void when(ArtifactSlot slot, Action<? super ArtifactAssembly> action) {
56 public void when(ArtifactSlot slot, Action<? super ArtifactAssembly> action) {
57 getDeferred(slot).whenResolved(action::execute);
57 getDeferred(slot).whenResolved(action::execute);
58 }
58 }
59
59
60 @Override
60 @Override
61 public void configureEach(Action<? super ArtifactAssembly> action) {
61 public void configureEach(Action<? super ArtifactAssembly> action) {
62 assemblies.forEach(action::execute);
62 assemblies.forEach(action::execute);
63 }
63 }
64
64
65 private Deferred<ArtifactAssembly> getDeferred(ArtifactSlot slot) {
65 private Deferred<ArtifactAssembly> getDeferred(ArtifactSlot slot) {
66 return assembliesBySlots.computeIfAbsent(slot, k -> new Deferred<>());
66 return assembliesBySlots.computeIfAbsent(slot, k -> new Deferred<>());
67 }
67 }
68
68
69 static class Assembly implements ArtifactAssembly {
69 static class Assembly implements ArtifactAssembly {
70
70
71 private final Provider<? extends FileSystemLocation> artifact;
71 private final Provider<? extends FileSystemLocation> artifact;
72 private final TaskProvider<? extends Task> task;
72 private final TaskProvider<? extends Task> task;
73
73
74 Assembly(Provider<? extends FileSystemLocation> artifact, TaskProvider<? extends Task> task) {
74 Assembly(Provider<? extends FileSystemLocation> artifact, TaskProvider<? extends Task> task) {
75 this.artifact = artifact;
75 this.artifact = artifact;
76 this.task = task;
76 this.task = task;
77 }
77 }
78
78
79 @Override
79 @Override
80 public Provider<? extends FileSystemLocation> getArtifact() {
80 public Provider<? extends FileSystemLocation> getArtifact() {
81 return artifact;
81 return artifact;
82 }
82 }
83
83
84 @Override
84 @Override
85 public TaskProvider<? extends Task> getAssemblyTask() {
85 public TaskProvider<? extends Task> getAssemblyTask() {
86 return task;
86 return task;
87 }
87 }
88
88
89 }
89 }
90 }
90 }
@@ -1,122 +1,124
1 package org.implab.gradle.variants.artifacts.internal;
1 package org.implab.gradle.variants.artifacts.internal;
2
2
3 import java.util.LinkedHashMap;
3 import java.util.LinkedHashMap;
4 import java.util.Map;
4 import java.util.Map;
5 import java.util.Optional;
5 import java.util.Optional;
6 import java.util.Set;
6 import java.util.Set;
7 import java.util.function.Consumer;
7 import java.util.function.Consumer;
8
8
9 import org.eclipse.jdt.annotation.NonNullByDefault;
9 import org.eclipse.jdt.annotation.NonNullByDefault;
10 import org.gradle.api.InvalidUserDataException;
10 import org.gradle.api.InvalidUserDataException;
11 import org.gradle.api.NamedDomainObjectContainer;
11 import org.gradle.api.NamedDomainObjectContainer;
12 import org.gradle.api.NamedDomainObjectProvider;
12 import org.gradle.api.NamedDomainObjectProvider;
13 import org.gradle.api.artifacts.Configuration;
13 import org.gradle.api.artifacts.Configuration;
14 import org.gradle.api.artifacts.ConfigurationContainer;
14 import org.gradle.api.artifacts.ConfigurationContainer;
15 import org.gradle.api.model.ObjectFactory;
15 import org.gradle.api.model.ObjectFactory;
16 import org.gradle.api.provider.Property;
16 import org.gradle.api.provider.Property;
17 import org.implab.gradle.internal.IdentityContainerFactory;
17 import org.implab.gradle.internal.IdentityContainerFactory;
18 import org.implab.gradle.internal.ReplayableQueue;
18 import org.implab.gradle.common.core.lang.ReplayableQueue;
19 import org.implab.gradle.variants.artifacts.OutgoingVariant;
19 import org.implab.gradle.variants.artifacts.OutgoingVariant;
20 import org.implab.gradle.variants.artifacts.Slot;
20 import org.implab.gradle.variants.artifacts.Slot;
21 import org.implab.gradle.variants.core.Variant;
21 import org.implab.gradle.variants.core.Variant;
22
22
23 /**
23 /**
24 * Реестр исходящих вариантов. Связывает исходящие конфигурации с вариантами
24 * Registry of variant-level outgoing models.
25 * сборки. Связь устанавливается 1:1.
25 *
26 * <p>Each declared outgoing model owns one lazy Gradle consumable configuration
27 * and one live slot identity container.
26 */
28 */
27 @NonNullByDefault
29 @NonNullByDefault
28 public class OutgoingRegistry {
30 public class OutgoingRegistry {
29 private final Map<Variant, OutgoingVariant> outgoingByVariant = new LinkedHashMap<>();
31 private final Map<Variant, OutgoingVariant> outgoingByVariant = new LinkedHashMap<>();
30 private final ReplayableQueue<OutgoingVariant> outgoingVariants = new ReplayableQueue<>();
32 private final ReplayableQueue<OutgoingVariant> outgoingVariants = new ReplayableQueue<>();
31 private final ConfigurationContainer configurations;
33 private final ConfigurationContainer configurations;
32 private final ObjectFactory objects;
34 private final ObjectFactory objects;
33 private final Set<Variant> declaredVariants;
35 private final Set<Variant> declaredVariants;
34
36
35 public OutgoingRegistry(
37 public OutgoingRegistry(
36 ConfigurationContainer configurations,
38 ConfigurationContainer configurations,
37 ObjectFactory objects,
39 ObjectFactory objects,
38 Set<Variant> declaredVariants) {
40 Set<Variant> declaredVariants) {
39 this.configurations = configurations;
41 this.configurations = configurations;
40 this.objects = objects;
42 this.objects = objects;
41 this.declaredVariants = declaredVariants;
43 this.declaredVariants = declaredVariants;
42 }
44 }
43
45
44 public Optional<OutgoingVariant> find(Variant variant) {
46 public Optional<OutgoingVariant> find(Variant variant) {
45 return Optional.ofNullable(outgoingByVariant.get(variant));
47 return Optional.ofNullable(outgoingByVariant.get(variant));
46 }
48 }
47
49
48 public OutgoingVariant maybeCreate(Variant variant) {
50 public OutgoingVariant maybeCreate(Variant variant) {
49 return find(variant).orElseGet(() -> create(variant));
51 return find(variant).orElseGet(() -> create(variant));
50 }
52 }
51
53
52 public OutgoingVariant create(Variant variant) {
54 public OutgoingVariant create(Variant variant) {
53 if (!declaredVariants.contains(variant))
55 if (!declaredVariants.contains(variant))
54 throw new InvalidUserDataException("Variant " + variant + " isn't declared");
56 throw new InvalidUserDataException("Variant " + variant + " isn't declared");
55 if (outgoingByVariant.containsKey(variant))
57 if (outgoingByVariant.containsKey(variant))
56 throw new InvalidUserDataException("Outgoing variant " + variant + " already exists");
58 throw new InvalidUserDataException("Outgoing variant " + variant + " already exists");
57
59
58 var configuration = configurations.consumable(outgoingConfigurationName(variant));
60 var configuration = configurations.consumable(outgoingConfigurationName(variant));
59 var outgoing = new Outgoing(variant, configuration);
61 var outgoing = new Outgoing(variant, configuration);
60
62
61 outgoingByVariant.put(variant, outgoing);
63 outgoingByVariant.put(variant, outgoing);
62
64
63 outgoingVariants.add(outgoing);
65 outgoingVariants.add(outgoing);
64
66
65 return outgoing;
67 return outgoing;
66 }
68 }
67
69
68 /**
70 /**
69 * Replayable hook which is applied when an outgoing variant is defined
71 * Registers a replayable hook for outgoing variant declarations.
70 *
72 *
71 * @param action
73 * @param action outgoing variant action
72 */
74 */
73 public void configureEach(Consumer<? super OutgoingVariant> action) {
75 public void configureEach(Consumer<? super OutgoingVariant> action) {
74 outgoingVariants.forEach(action);
76 outgoingVariants.forEach(action);
75 }
77 }
76
78
77 private String outgoingConfigurationName(Variant variant) {
79 private String outgoingConfigurationName(Variant variant) {
78 return variant.getName() + "Elements";
80 return variant.getName() + "Elements";
79 }
81 }
80
82
81 private class Outgoing implements OutgoingVariant {
83 private class Outgoing implements OutgoingVariant {
82
84
83 private final Variant variant;
85 private final Variant variant;
84
86
85 private final NamedDomainObjectProvider<? extends Configuration> configurationProvider;
87 private final NamedDomainObjectProvider<? extends Configuration> configurationProvider;
86
88
87 private final NamedDomainObjectContainer<Slot> slots;
89 private final NamedDomainObjectContainer<Slot> slots;
88
90
89 private final Property<Slot> primarySlot;
91 private final Property<Slot> primarySlot;
90
92
91 public Outgoing(
93 public Outgoing(
92 Variant variant,
94 Variant variant,
93 NamedDomainObjectProvider<? extends Configuration> configurationProvider) {
95 NamedDomainObjectProvider<? extends Configuration> configurationProvider) {
94 this.variant = variant;
96 this.variant = variant;
95 this.configurationProvider = configurationProvider;
97 this.configurationProvider = configurationProvider;
96 this.slots = IdentityContainerFactory.create(objects, Slot.class);
98 this.slots = IdentityContainerFactory.create(objects, Slot.class);
97 this.primarySlot = objects.property(Slot.class);
99 this.primarySlot = objects.property(Slot.class);
98 primarySlot.finalizeValueOnRead();
100 primarySlot.finalizeValueOnRead();
99 }
101 }
100
102
101 @Override
103 @Override
102 public Property<Slot> getPrimarySlot() {
104 public Property<Slot> getPrimarySlot() {
103 return primarySlot;
105 return primarySlot;
104 }
106 }
105
107
106 @Override
108 @Override
107 public Variant getVariant() {
109 public Variant getVariant() {
108 return variant;
110 return variant;
109 }
111 }
110
112
111 @Override
113 @Override
112 public NamedDomainObjectProvider<? extends Configuration> getConfiguration() {
114 public NamedDomainObjectProvider<? extends Configuration> getConfiguration() {
113 return configurationProvider;
115 return configurationProvider;
114 }
116 }
115
117
116 @Override
118 @Override
117 public NamedDomainObjectContainer<Slot> getSlots() {
119 public NamedDomainObjectContainer<Slot> getSlots() {
118 return slots;
120 return slots;
119 }
121 }
120 }
122 }
121
123
122 }
124 }
@@ -1,114 +1,94
1 package org.implab.gradle.variants.sources.internal;
1 package org.implab.gradle.variants.sources.internal;
2
2
3 import java.text.MessageFormat;
3 import java.text.MessageFormat;
4 import java.util.LinkedHashMap;
4 import java.util.LinkedHashMap;
5 import java.util.LinkedList;
6 import java.util.List;
5 import java.util.List;
7 import java.util.Map;
6 import java.util.Map;
8 import java.util.function.Consumer;
9 import java.util.function.Supplier;
7 import java.util.function.Supplier;
10 import java.util.stream.Collectors;
8 import java.util.stream.Collectors;
11
9
12 import org.eclipse.jdt.annotation.NonNullByDefault;
10 import org.eclipse.jdt.annotation.NonNullByDefault;
13 import org.gradle.api.Action;
11 import org.gradle.api.Action;
14 import org.gradle.api.Named;
12 import org.gradle.api.Named;
15 import org.gradle.api.logging.Logger;
13 import org.gradle.api.logging.Logger;
16 import org.gradle.api.logging.Logging;
14 import org.gradle.api.logging.Logging;
15 import org.implab.gradle.common.core.lang.ReplayableQueue;
17 import org.implab.gradle.variants.core.Layer;
16 import org.implab.gradle.variants.core.Layer;
18 import org.implab.gradle.variants.core.Variant;
17 import org.implab.gradle.variants.core.Variant;
19 import org.implab.gradle.variants.sources.CompileUnit;
18 import org.implab.gradle.variants.sources.CompileUnit;
20 import org.implab.gradle.variants.sources.GenericSourceSet;
19 import org.implab.gradle.variants.sources.GenericSourceSet;
21
20
22 @NonNullByDefault
21 @NonNullByDefault
23 public class SourceSetConfigurationRegistry {
22 public class SourceSetConfigurationRegistry {
24 private static final Logger logger = Logging.getLogger(SourceSetConfigurationRegistry.class);
23 private static final Logger logger = Logging.getLogger(SourceSetConfigurationRegistry.class);
25
24
26 private final Map<Layer, ReplayableQueue<GenericSourceSet>> sourcesByLayer = new LinkedHashMap<>();
25 private final Map<Layer, ReplayableQueue<GenericSourceSet>> sourcesByLayer = new LinkedHashMap<>();
27 private final Map<Variant, ReplayableQueue<GenericSourceSet>> sourcesByVariant = new LinkedHashMap<>();
26 private final Map<Variant, ReplayableQueue<GenericSourceSet>> sourcesByVariant = new LinkedHashMap<>();
28 private final Map<CompileUnit, ReplayableQueue<GenericSourceSet>> sourcesByUnit = new LinkedHashMap<>();
27 private final Map<CompileUnit, ReplayableQueue<GenericSourceSet>> sourcesByUnit = new LinkedHashMap<>();
29
28
30 private final Supplier<LateConfigurationMode> lateConfigurationMode;
29 private final Supplier<LateConfigurationMode> lateConfigurationMode;
31
30
32 public SourceSetConfigurationRegistry(Supplier<LateConfigurationMode> lateConfigurationMode) {
31 public SourceSetConfigurationRegistry(Supplier<LateConfigurationMode> lateConfigurationMode) {
33 this.lateConfigurationMode = lateConfigurationMode;
32 this.lateConfigurationMode = lateConfigurationMode;
34 }
33 }
35
34
36 public void addLayerAction(Layer layer, Action<? super GenericSourceSet> action) {
35 public void addLayerAction(Layer layer, Action<? super GenericSourceSet> action) {
37 addToActions(
36 addToActions(
38 sourcesByLayer.computeIfAbsent(layer, key -> new ReplayableQueue<>()),
37 sourcesByLayer.computeIfAbsent(layer, key -> new ReplayableQueue<>()),
39 action,
38 action,
40 MessageFormat.format(
39 MessageFormat.format(
41 "Source sets for [layer={0}] layer already materialized",
40 "Source sets for [layer={0}] layer already materialized",
42 layer.getName()));
41 layer.getName()));
43 }
42 }
44
43
45 public void addVariantAction(Variant variant, Action<? super GenericSourceSet> action) {
44 public void addVariantAction(Variant variant, Action<? super GenericSourceSet> action) {
46 addToActions(
45 addToActions(
47 sourcesByVariant.computeIfAbsent(variant, key -> new ReplayableQueue<>()),
46 sourcesByVariant.computeIfAbsent(variant, key -> new ReplayableQueue<>()),
48 action,
47 action,
49 MessageFormat.format(
48 MessageFormat.format(
50 "Source sets for [variant={0}] variant already materialized",
49 "Source sets for [variant={0}] variant already materialized",
51 variant.getName()));
50 variant.getName()));
52
51
53 }
52 }
54
53
55 public void addCompileUnitAction(CompileUnit unit, Action<? super GenericSourceSet> action) {
54 public void addCompileUnitAction(CompileUnit unit, Action<? super GenericSourceSet> action) {
56 addToActions(
55 addToActions(
57 sourcesByUnit.computeIfAbsent(unit, key -> new ReplayableQueue<>()),
56 sourcesByUnit.computeIfAbsent(unit, key -> new ReplayableQueue<>()),
58 action,
57 action,
59 MessageFormat.format(
58 MessageFormat.format(
60 "Source set for [variant={0}, layer={1}] already materialed",
59 "Source set for [variant={0}, layer={1}] already materialized",
61 unit.variant().getName(),
60 unit.variant().getName(),
62 unit.layer().getName()));
61 unit.layer().getName()));
63 }
62 }
64
63
65 private void addToActions(
64 private void addToActions(
66 ReplayableQueue<GenericSourceSet> actions,
65 ReplayableQueue<GenericSourceSet> actions,
67 Action<? super GenericSourceSet> action,
66 Action<? super GenericSourceSet> action,
68 String assertMessage) {
67 String assertMessage) {
69 assertLazyConfiguration(actions.values(), assertMessage);
68 assertLazyConfiguration(actions.values(), assertMessage);
70 actions.forEach(action::execute);
69 actions.forEach(action::execute);
71 }
70 }
72
71
73 void assertLazyConfiguration(List<GenericSourceSet> sets, String message) {
72 void assertLazyConfiguration(List<GenericSourceSet> sets, String message) {
74 if (sets.size() == 0)
73 if (sets.size() == 0)
75 return;
74 return;
76
75
77 var names = sets.stream().map(Named::getName).collect(Collectors.joining(", "));
76 var names = sets.stream().map(Named::getName).collect(Collectors.joining(", "));
78
77
79 switch (lateConfigurationMode.get()) {
78 switch (lateConfigurationMode.get()) {
80 case FAIL:
79 case FAIL:
81 throw new IllegalStateException(message + " [" + names + "]");
80 throw new IllegalStateException(message + " [" + names + "]");
82 case WARN:
81 case WARN:
83 logger.warn(message + "\n\t" + names);
82 logger.warn(message + "\n\t" + names);
84 break;
83 break;
85 default:
84 default:
86 break;
85 break;
87 }
86 }
88 }
87 }
89
88
90 public void applyConfiguration(CompileUnit unit, GenericSourceSet sourceSet) {
89 public void applyConfiguration(CompileUnit unit, GenericSourceSet sourceSet) {
91 sourcesByVariant.computeIfAbsent(unit.variant(), key -> new ReplayableQueue<>()).add(sourceSet);
90 sourcesByVariant.computeIfAbsent(unit.variant(), key -> new ReplayableQueue<>()).add(sourceSet);
92 sourcesByLayer.computeIfAbsent(unit.layer(), key -> new ReplayableQueue<>()).add(sourceSet);
91 sourcesByLayer.computeIfAbsent(unit.layer(), key -> new ReplayableQueue<>()).add(sourceSet);
93 sourcesByUnit.computeIfAbsent(unit, key -> new ReplayableQueue<>()).add(sourceSet);
92 sourcesByUnit.computeIfAbsent(unit, key -> new ReplayableQueue<>()).add(sourceSet);
94 }
93 }
95
96 class ReplayableQueue<T> {
97 private final List<Consumer<? super T>> consumers = new LinkedList<>();
98 private final List<T> values = new LinkedList<>();
99
100 public void add(T value) {
101 consumers.forEach(consumer -> consumer.accept(value));
102 values.add(value);
103 }
94 }
104
105 List<T> values() {
106 return List.copyOf(values);
107 }
108
109 public void forEach(Consumer<? super T> consumer) {
110 values.forEach(consumer);
111 consumers.add(consumer);
112 }
113 }
114 }
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now