| @@ -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 | 1 | package org.implab.gradle.variants.artifacts.internal; |
|
|
2 | 2 | |
|
|
3 | 3 | import java.util.LinkedHashMap; |
|
|
4 | 4 | import java.util.Map; |
|
|
5 | 5 | import java.util.Optional; |
|
|
6 | 6 | import java.util.function.Function; |
|
|
7 | 7 | |
|
|
8 | 8 | import org.eclipse.jdt.annotation.NonNullByDefault; |
|
|
9 | 9 | import org.gradle.api.Action; |
|
|
10 | 10 | import org.gradle.api.InvalidUserDataException; |
|
|
11 | 11 | import org.gradle.api.Task; |
|
|
12 | 12 | import org.gradle.api.file.FileSystemLocation; |
|
|
13 | 13 | import org.gradle.api.provider.Provider; |
|
|
14 | 14 | import org.gradle.api.tasks.TaskProvider; |
|
|
15 | 15 | import org.implab.gradle.common.core.lang.Deferred; |
|
|
16 |
import org.implab.gradle. |
|
|
|
16 | import org.implab.gradle.common.core.lang.ReplayableQueue; | |
|
|
17 | 17 | import org.implab.gradle.variants.artifacts.ArtifactAssemblies; |
|
|
18 | 18 | import org.implab.gradle.variants.artifacts.ArtifactAssembly; |
|
|
19 | 19 | import org.implab.gradle.variants.artifacts.ArtifactSlot; |
|
|
20 | 20 | |
|
|
21 | 21 | @NonNullByDefault |
|
|
22 | 22 | public class ArtifactAssemblyRegistry implements ArtifactAssemblies { |
|
|
23 | 23 | private final Map<ArtifactSlot, Deferred<ArtifactAssembly>> assembliesBySlots = new LinkedHashMap<>(); |
|
|
24 | 24 | private final ReplayableQueue<ArtifactAssembly> assemblies = new ReplayableQueue<>(); |
|
|
25 | 25 | |
|
|
26 | 26 | public ArtifactAssemblyRegistry() { |
|
|
27 | 27 | } |
|
|
28 | 28 | |
|
|
29 | 29 | public <T extends Task> ArtifactAssembly register( |
|
|
30 | 30 | ArtifactSlot slot, |
|
|
31 | 31 | TaskProvider<T> task, |
|
|
32 | 32 | Function<? super T, ? extends Provider<? extends FileSystemLocation>> mapOutputArtifact) { |
|
|
33 | 33 | |
|
|
34 | 34 | var deferred = getDeferred(slot); |
|
|
35 | 35 | if (deferred.resolved()) { |
|
|
36 | 36 | throw new InvalidUserDataException("Artifact assembly '" + slot + "' is already registered"); |
|
|
37 | 37 | } |
|
|
38 | 38 | var outputArtifact = task.flatMap(mapOutputArtifact::apply); |
|
|
39 | 39 | |
|
|
40 | 40 | var assembly = new Assembly(outputArtifact, task); |
|
|
41 | 41 | deferred.resolve(assembly); |
|
|
42 | 42 | assemblies.add(assembly); |
|
|
43 | 43 | return assembly; |
|
|
44 | 44 | } |
|
|
45 | 45 | |
|
|
46 | 46 | @Override |
|
|
47 | 47 | public Optional<ArtifactAssembly> find(ArtifactSlot slot) { |
|
|
48 | 48 | // to prevent creation of map entries on lookup use the map directly |
|
|
49 | 49 | var deferred = assembliesBySlots.get(slot); |
|
|
50 | 50 | return deferred != null && deferred.resolved() |
|
|
51 | 51 | ? Optional.of(deferred.value()) |
|
|
52 | 52 | : Optional.empty(); |
|
|
53 | 53 | } |
|
|
54 | 54 | |
|
|
55 | 55 | @Override |
|
|
56 | 56 | public void when(ArtifactSlot slot, Action<? super ArtifactAssembly> action) { |
|
|
57 | 57 | getDeferred(slot).whenResolved(action::execute); |
|
|
58 | 58 | } |
|
|
59 | 59 | |
|
|
60 | 60 | @Override |
|
|
61 | 61 | public void configureEach(Action<? super ArtifactAssembly> action) { |
|
|
62 | 62 | assemblies.forEach(action::execute); |
|
|
63 | 63 | } |
|
|
64 | 64 | |
|
|
65 | 65 | private Deferred<ArtifactAssembly> getDeferred(ArtifactSlot slot) { |
|
|
66 | 66 | return assembliesBySlots.computeIfAbsent(slot, k -> new Deferred<>()); |
|
|
67 | 67 | } |
|
|
68 | 68 | |
|
|
69 | 69 | static class Assembly implements ArtifactAssembly { |
|
|
70 | 70 | |
|
|
71 | 71 | private final Provider<? extends FileSystemLocation> artifact; |
|
|
72 | 72 | private final TaskProvider<? extends Task> task; |
|
|
73 | 73 | |
|
|
74 | 74 | Assembly(Provider<? extends FileSystemLocation> artifact, TaskProvider<? extends Task> task) { |
|
|
75 | 75 | this.artifact = artifact; |
|
|
76 | 76 | this.task = task; |
|
|
77 | 77 | } |
|
|
78 | 78 | |
|
|
79 | 79 | @Override |
|
|
80 | 80 | public Provider<? extends FileSystemLocation> getArtifact() { |
|
|
81 | 81 | return artifact; |
|
|
82 | 82 | } |
|
|
83 | 83 | |
|
|
84 | 84 | @Override |
|
|
85 | 85 | public TaskProvider<? extends Task> getAssemblyTask() { |
|
|
86 | 86 | return task; |
|
|
87 | 87 | } |
|
|
88 | 88 | |
|
|
89 | 89 | } |
|
|
90 | 90 | } |
| @@ -1,122 +1,124 | |||
|
|
1 | 1 | package org.implab.gradle.variants.artifacts.internal; |
|
|
2 | 2 | |
|
|
3 | 3 | import java.util.LinkedHashMap; |
|
|
4 | 4 | import java.util.Map; |
|
|
5 | 5 | import java.util.Optional; |
|
|
6 | 6 | import java.util.Set; |
|
|
7 | 7 | import java.util.function.Consumer; |
|
|
8 | 8 | |
|
|
9 | 9 | import org.eclipse.jdt.annotation.NonNullByDefault; |
|
|
10 | 10 | import org.gradle.api.InvalidUserDataException; |
|
|
11 | 11 | import org.gradle.api.NamedDomainObjectContainer; |
|
|
12 | 12 | import org.gradle.api.NamedDomainObjectProvider; |
|
|
13 | 13 | import org.gradle.api.artifacts.Configuration; |
|
|
14 | 14 | import org.gradle.api.artifacts.ConfigurationContainer; |
|
|
15 | 15 | import org.gradle.api.model.ObjectFactory; |
|
|
16 | 16 | import org.gradle.api.provider.Property; |
|
|
17 | 17 | import org.implab.gradle.internal.IdentityContainerFactory; |
|
|
18 |
import org.implab.gradle. |
|
|
|
18 | import org.implab.gradle.common.core.lang.ReplayableQueue; | |
|
|
19 | 19 | import org.implab.gradle.variants.artifacts.OutgoingVariant; |
|
|
20 | 20 | import org.implab.gradle.variants.artifacts.Slot; |
|
|
21 | 21 | import org.implab.gradle.variants.core.Variant; |
|
|
22 | 22 | |
|
|
23 | 23 | /** |
|
|
24 | * Реестр исходящих вариантов. Связывает исходящие конфигурации с вариантами | |
|
|
25 | * сборки. Связь устанавливается 1:1. | |
|
|
24 | * Registry of variant-level outgoing models. | |
|
|
25 | * | |
|
|
26 | * <p>Each declared outgoing model owns one lazy Gradle consumable configuration | |
|
|
27 | * and one live slot identity container. | |
|
|
26 | 28 | */ |
|
|
27 | 29 | @NonNullByDefault |
|
|
28 | 30 | public class OutgoingRegistry { |
|
|
29 | 31 | private final Map<Variant, OutgoingVariant> outgoingByVariant = new LinkedHashMap<>(); |
|
|
30 | 32 | private final ReplayableQueue<OutgoingVariant> outgoingVariants = new ReplayableQueue<>(); |
|
|
31 | 33 | private final ConfigurationContainer configurations; |
|
|
32 | 34 | private final ObjectFactory objects; |
|
|
33 | 35 | private final Set<Variant> declaredVariants; |
|
|
34 | 36 | |
|
|
35 | 37 | public OutgoingRegistry( |
|
|
36 | 38 | ConfigurationContainer configurations, |
|
|
37 | 39 | ObjectFactory objects, |
|
|
38 | 40 | Set<Variant> declaredVariants) { |
|
|
39 | 41 | this.configurations = configurations; |
|
|
40 | 42 | this.objects = objects; |
|
|
41 | 43 | this.declaredVariants = declaredVariants; |
|
|
42 | 44 | } |
|
|
43 | 45 | |
|
|
44 | 46 | public Optional<OutgoingVariant> find(Variant variant) { |
|
|
45 | 47 | return Optional.ofNullable(outgoingByVariant.get(variant)); |
|
|
46 | 48 | } |
|
|
47 | 49 | |
|
|
48 | 50 | public OutgoingVariant maybeCreate(Variant variant) { |
|
|
49 | 51 | return find(variant).orElseGet(() -> create(variant)); |
|
|
50 | 52 | } |
|
|
51 | 53 | |
|
|
52 | 54 | public OutgoingVariant create(Variant variant) { |
|
|
53 | 55 | if (!declaredVariants.contains(variant)) |
|
|
54 | 56 | throw new InvalidUserDataException("Variant " + variant + " isn't declared"); |
|
|
55 | 57 | if (outgoingByVariant.containsKey(variant)) |
|
|
56 | 58 | throw new InvalidUserDataException("Outgoing variant " + variant + " already exists"); |
|
|
57 | 59 | |
|
|
58 | 60 | var configuration = configurations.consumable(outgoingConfigurationName(variant)); |
|
|
59 | 61 | var outgoing = new Outgoing(variant, configuration); |
|
|
60 | 62 | |
|
|
61 | 63 | outgoingByVariant.put(variant, outgoing); |
|
|
62 | 64 | |
|
|
63 | 65 | outgoingVariants.add(outgoing); |
|
|
64 | 66 | |
|
|
65 | 67 | return outgoing; |
|
|
66 | 68 | } |
|
|
67 | 69 | |
|
|
68 | 70 | /** |
|
|
69 |
* Replayable hook |
|
|
|
71 | * Registers a replayable hook for outgoing variant declarations. | |
|
|
70 | 72 | * |
|
|
71 | * @param action | |
|
|
73 | * @param action outgoing variant action | |
|
|
72 | 74 | */ |
|
|
73 | 75 | public void configureEach(Consumer<? super OutgoingVariant> action) { |
|
|
74 | 76 | outgoingVariants.forEach(action); |
|
|
75 | 77 | } |
|
|
76 | 78 | |
|
|
77 | 79 | private String outgoingConfigurationName(Variant variant) { |
|
|
78 | 80 | return variant.getName() + "Elements"; |
|
|
79 | 81 | } |
|
|
80 | 82 | |
|
|
81 | 83 | private class Outgoing implements OutgoingVariant { |
|
|
82 | 84 | |
|
|
83 | 85 | private final Variant variant; |
|
|
84 | 86 | |
|
|
85 | 87 | private final NamedDomainObjectProvider<? extends Configuration> configurationProvider; |
|
|
86 | 88 | |
|
|
87 | 89 | private final NamedDomainObjectContainer<Slot> slots; |
|
|
88 | 90 | |
|
|
89 | 91 | private final Property<Slot> primarySlot; |
|
|
90 | 92 | |
|
|
91 | 93 | public Outgoing( |
|
|
92 | 94 | Variant variant, |
|
|
93 | 95 | NamedDomainObjectProvider<? extends Configuration> configurationProvider) { |
|
|
94 | 96 | this.variant = variant; |
|
|
95 | 97 | this.configurationProvider = configurationProvider; |
|
|
96 | 98 | this.slots = IdentityContainerFactory.create(objects, Slot.class); |
|
|
97 | 99 | this.primarySlot = objects.property(Slot.class); |
|
|
98 | 100 | primarySlot.finalizeValueOnRead(); |
|
|
99 | 101 | } |
|
|
100 | 102 | |
|
|
101 | 103 | @Override |
|
|
102 | 104 | public Property<Slot> getPrimarySlot() { |
|
|
103 | 105 | return primarySlot; |
|
|
104 | 106 | } |
|
|
105 | 107 | |
|
|
106 | 108 | @Override |
|
|
107 | 109 | public Variant getVariant() { |
|
|
108 | 110 | return variant; |
|
|
109 | 111 | } |
|
|
110 | 112 | |
|
|
111 | 113 | @Override |
|
|
112 | 114 | public NamedDomainObjectProvider<? extends Configuration> getConfiguration() { |
|
|
113 | 115 | return configurationProvider; |
|
|
114 | 116 | } |
|
|
115 | 117 | |
|
|
116 | 118 | @Override |
|
|
117 | 119 | public NamedDomainObjectContainer<Slot> getSlots() { |
|
|
118 | 120 | return slots; |
|
|
119 | 121 | } |
|
|
120 | 122 | } |
|
|
121 | 123 | |
|
|
122 | 124 | } |
| @@ -1,114 +1,94 | |||
|
|
1 | 1 | package org.implab.gradle.variants.sources.internal; |
|
|
2 | 2 | |
|
|
3 | 3 | import java.text.MessageFormat; |
|
|
4 | 4 | import java.util.LinkedHashMap; |
|
|
5 | import java.util.LinkedList; | |
|
|
6 | 5 | import java.util.List; |
|
|
7 | 6 | import java.util.Map; |
|
|
8 | import java.util.function.Consumer; | |
|
|
9 | 7 | import java.util.function.Supplier; |
|
|
10 | 8 | import java.util.stream.Collectors; |
|
|
11 | 9 | |
|
|
12 | 10 | import org.eclipse.jdt.annotation.NonNullByDefault; |
|
|
13 | 11 | import org.gradle.api.Action; |
|
|
14 | 12 | import org.gradle.api.Named; |
|
|
15 | 13 | import org.gradle.api.logging.Logger; |
|
|
16 | 14 | import org.gradle.api.logging.Logging; |
|
|
15 | import org.implab.gradle.common.core.lang.ReplayableQueue; | |
|
|
17 | 16 | import org.implab.gradle.variants.core.Layer; |
|
|
18 | 17 | import org.implab.gradle.variants.core.Variant; |
|
|
19 | 18 | import org.implab.gradle.variants.sources.CompileUnit; |
|
|
20 | 19 | import org.implab.gradle.variants.sources.GenericSourceSet; |
|
|
21 | 20 | |
|
|
22 | 21 | @NonNullByDefault |
|
|
23 | 22 | public class SourceSetConfigurationRegistry { |
|
|
24 | 23 | private static final Logger logger = Logging.getLogger(SourceSetConfigurationRegistry.class); |
|
|
25 | 24 | |
|
|
26 | 25 | private final Map<Layer, ReplayableQueue<GenericSourceSet>> sourcesByLayer = new LinkedHashMap<>(); |
|
|
27 | 26 | private final Map<Variant, ReplayableQueue<GenericSourceSet>> sourcesByVariant = new LinkedHashMap<>(); |
|
|
28 | 27 | private final Map<CompileUnit, ReplayableQueue<GenericSourceSet>> sourcesByUnit = new LinkedHashMap<>(); |
|
|
29 | 28 | |
|
|
30 | 29 | private final Supplier<LateConfigurationMode> lateConfigurationMode; |
|
|
31 | 30 | |
|
|
32 | 31 | public SourceSetConfigurationRegistry(Supplier<LateConfigurationMode> lateConfigurationMode) { |
|
|
33 | 32 | this.lateConfigurationMode = lateConfigurationMode; |
|
|
34 | 33 | } |
|
|
35 | 34 | |
|
|
36 | 35 | public void addLayerAction(Layer layer, Action<? super GenericSourceSet> action) { |
|
|
37 | 36 | addToActions( |
|
|
38 | 37 | sourcesByLayer.computeIfAbsent(layer, key -> new ReplayableQueue<>()), |
|
|
39 | 38 | action, |
|
|
40 | 39 | MessageFormat.format( |
|
|
41 | 40 | "Source sets for [layer={0}] layer already materialized", |
|
|
42 | 41 | layer.getName())); |
|
|
43 | 42 | } |
|
|
44 | 43 | |
|
|
45 | 44 | public void addVariantAction(Variant variant, Action<? super GenericSourceSet> action) { |
|
|
46 | 45 | addToActions( |
|
|
47 | 46 | sourcesByVariant.computeIfAbsent(variant, key -> new ReplayableQueue<>()), |
|
|
48 | 47 | action, |
|
|
49 | 48 | MessageFormat.format( |
|
|
50 | 49 | "Source sets for [variant={0}] variant already materialized", |
|
|
51 | 50 | variant.getName())); |
|
|
52 | 51 | |
|
|
53 | 52 | } |
|
|
54 | 53 | |
|
|
55 | 54 | public void addCompileUnitAction(CompileUnit unit, Action<? super GenericSourceSet> action) { |
|
|
56 | 55 | addToActions( |
|
|
57 | 56 | sourcesByUnit.computeIfAbsent(unit, key -> new ReplayableQueue<>()), |
|
|
58 | 57 | action, |
|
|
59 | 58 | MessageFormat.format( |
|
|
60 | "Source set for [variant={0}, layer={1}] already materialed", | |
|
|
59 | "Source set for [variant={0}, layer={1}] already materialized", | |
|
|
61 | 60 | unit.variant().getName(), |
|
|
62 | 61 | unit.layer().getName())); |
|
|
63 | 62 | } |
|
|
64 | 63 | |
|
|
65 | 64 | private void addToActions( |
|
|
66 | 65 | ReplayableQueue<GenericSourceSet> actions, |
|
|
67 | 66 | Action<? super GenericSourceSet> action, |
|
|
68 | 67 | String assertMessage) { |
|
|
69 | 68 | assertLazyConfiguration(actions.values(), assertMessage); |
|
|
70 | 69 | actions.forEach(action::execute); |
|
|
71 | 70 | } |
|
|
72 | 71 | |
|
|
73 | 72 | void assertLazyConfiguration(List<GenericSourceSet> sets, String message) { |
|
|
74 | 73 | if (sets.size() == 0) |
|
|
75 | 74 | return; |
|
|
76 | 75 | |
|
|
77 | 76 | var names = sets.stream().map(Named::getName).collect(Collectors.joining(", ")); |
|
|
78 | 77 | |
|
|
79 | 78 | switch (lateConfigurationMode.get()) { |
|
|
80 | 79 | case FAIL: |
|
|
81 | 80 | throw new IllegalStateException(message + " [" + names + "]"); |
|
|
82 | 81 | case WARN: |
|
|
83 | 82 | logger.warn(message + "\n\t" + names); |
|
|
84 | 83 | break; |
|
|
85 | 84 | default: |
|
|
86 | 85 | break; |
|
|
87 | 86 | } |
|
|
88 | 87 | } |
|
|
89 | 88 | |
|
|
90 | 89 | public void applyConfiguration(CompileUnit unit, GenericSourceSet sourceSet) { |
|
|
91 | 90 | sourcesByVariant.computeIfAbsent(unit.variant(), key -> new ReplayableQueue<>()).add(sourceSet); |
|
|
92 | 91 | sourcesByLayer.computeIfAbsent(unit.layer(), key -> new ReplayableQueue<>()).add(sourceSet); |
|
|
93 | 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 |
General Comments 0
You need to be logged in to leave comments.
Login now
