| @@ -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. |
|
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. |
|
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 |
|
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
