diff --git a/application-graph/src/main/java/io/koraframework/application/graph/internal/AllPromisesImpl.java b/application-graph/src/main/java/io/koraframework/application/graph/internal/AllPromisesImpl.java index b9e68be55..cdaf8b5c5 100644 --- a/application-graph/src/main/java/io/koraframework/application/graph/internal/AllPromisesImpl.java +++ b/application-graph/src/main/java/io/koraframework/application/graph/internal/AllPromisesImpl.java @@ -1,29 +1,28 @@ package io.koraframework.application.graph.internal; -import io.koraframework.application.graph.All; -import io.koraframework.application.graph.Graph; -import io.koraframework.application.graph.NodeWithMapper; -import io.koraframework.application.graph.PromiseOf; +import io.koraframework.application.graph.*; import java.util.ArrayList; import java.util.Iterator; import java.util.function.Function; public final class AllPromisesImpl implements All> { + private final Graph graph; private final ArrayList> values; @SafeVarargs public AllPromisesImpl(Graph graph, NodeWithMapper... nodes) { + this.graph = graph; this.values = new ArrayList<>(nodes.length); for (var node : nodes) { this.values.add(new PromiseWrapper<>(graph, node)); } } - private record PromiseWrapper(PromiseOf promise, Function mapper) { + private record PromiseWrapper(PromiseOf promise, Function mapper, Node node) { public PromiseWrapper(Graph graph, NodeWithMapper element) { var promise = graph.promiseOf(element.node()); - this(promise, element.mapper()); + this(promise, element.mapper(), element.node()); } public PromiseOf get() { @@ -33,18 +32,14 @@ public PromiseOf get() { @Override public Iterator> iterator() { - var it = values.iterator(); - return new Iterator<>() { - @Override - public boolean hasNext() { - return it.hasNext(); + var list = new ArrayList>(); + for (var value : this.values) { + var condition = value.node().condition(); + if (condition == null || condition.apply(graph) instanceof GraphCondition.ConditionResult.Matched) { + list.add(value.get()); } - - @Override - public PromiseOf next() { - return it.next().get(); - } - }; + } + return list.iterator(); } } diff --git a/application-graph/src/main/java/io/koraframework/application/graph/internal/AllSimpleImpl.java b/application-graph/src/main/java/io/koraframework/application/graph/internal/AllSimpleImpl.java index 4768b8e19..5ada65d9b 100644 --- a/application-graph/src/main/java/io/koraframework/application/graph/internal/AllSimpleImpl.java +++ b/application-graph/src/main/java/io/koraframework/application/graph/internal/AllSimpleImpl.java @@ -2,6 +2,7 @@ import io.koraframework.application.graph.All; import io.koraframework.application.graph.Graph; +import io.koraframework.application.graph.GraphCondition; import io.koraframework.application.graph.NodeWithMapper; import java.util.ArrayList; @@ -15,7 +16,10 @@ public final class AllSimpleImpl implements All { public AllSimpleImpl(Graph graph, NodeWithMapper... nodes) { this.values = new ArrayList(nodes.length); for (var node : nodes) { - this.values.add(get(graph, node)); + var condition = node.node().condition(); + if (condition == null || condition.apply(graph) instanceof GraphCondition.ConditionResult.Matched) { + this.values.add(get(graph, node)); + } } } diff --git a/application-graph/src/main/java/io/koraframework/application/graph/internal/AllValuesImpl.java b/application-graph/src/main/java/io/koraframework/application/graph/internal/AllValuesImpl.java index ad739469f..29bb609ae 100644 --- a/application-graph/src/main/java/io/koraframework/application/graph/internal/AllValuesImpl.java +++ b/application-graph/src/main/java/io/koraframework/application/graph/internal/AllValuesImpl.java @@ -1,29 +1,28 @@ package io.koraframework.application.graph.internal; -import io.koraframework.application.graph.All; -import io.koraframework.application.graph.Graph; -import io.koraframework.application.graph.NodeWithMapper; -import io.koraframework.application.graph.ValueOf; +import io.koraframework.application.graph.*; import java.util.ArrayList; import java.util.Iterator; import java.util.function.Function; public final class AllValuesImpl implements All> { + private final Graph graph; private final ArrayList> values; @SafeVarargs public AllValuesImpl(Graph graph, NodeWithMapper... nodes) { + this.graph = graph; this.values = new ArrayList<>(nodes.length); for (var node : nodes) { this.values.add(new ValueWrapper<>(graph, node)); } } - private record ValueWrapper(ValueOf value, Function mapper) { + private record ValueWrapper(ValueOf value, Function mapper, Node node) { public ValueWrapper(Graph graph, NodeWithMapper element) { var promise = graph.valueOf(element.node()); - this(promise, element.mapper()); + this(promise, element.mapper(), element.node()); } public ValueOf get() { @@ -33,17 +32,13 @@ public ValueOf get() { @Override public Iterator> iterator() { - var it = this.values.iterator(); - return new Iterator<>() { - @Override - public boolean hasNext() { - return it.hasNext(); + var list = new ArrayList>(); + for (var value : this.values) { + var condition = value.node().condition(); + if (condition == null || condition.apply(graph) instanceof GraphCondition.ConditionResult.Matched) { + list.add(value.get()); } - - @Override - public ValueOf next() { - return it.next().get(); - } - }; + } + return list.iterator(); } } diff --git a/kora-app-annotation-processor/src/main/java/io/koraframework/kora/app/annotation/processor/component/ResolvedComponent.java b/kora-app-annotation-processor/src/main/java/io/koraframework/kora/app/annotation/processor/component/ResolvedComponent.java index 56d155406..d49bec25d 100644 --- a/kora-app-annotation-processor/src/main/java/io/koraframework/kora/app/annotation/processor/component/ResolvedComponent.java +++ b/kora-app-annotation-processor/src/main/java/io/koraframework/kora/app/annotation/processor/component/ResolvedComponent.java @@ -119,6 +119,7 @@ public void processCondition() { } else { condition = new HashSet<>(this.parentConditions); condition.add(this.declaration.condition()); + condition.remove(UNCONDITIONALLY); } for (var dependency : this.dependencies) { diff --git a/kora-app-annotation-processor/src/main/java/io/koraframework/kora/app/annotation/processor/component/ResolvedComponents.java b/kora-app-annotation-processor/src/main/java/io/koraframework/kora/app/annotation/processor/component/ResolvedComponents.java index b09155854..700954836 100644 --- a/kora-app-annotation-processor/src/main/java/io/koraframework/kora/app/annotation/processor/component/ResolvedComponents.java +++ b/kora-app-annotation-processor/src/main/java/io/koraframework/kora/app/annotation/processor/component/ResolvedComponents.java @@ -94,7 +94,7 @@ static int maxDependencyIndex(ComponentDependency dependency) { .min(Comparator.naturalOrder()) .orElse(-1); assert conditionIndex > maxDependency; - System.arraycopy(resolvedComponents, maxDependency + 1, resolvedComponents, maxDependency + 2, conditionIndex - maxDependency); + System.arraycopy(resolvedComponents, maxDependency + 1, resolvedComponents, maxDependency + 2, conditionIndex - maxDependency - 1); resolvedComponents[maxDependency + 1] = condition; for (int i = maxDependency + 1; i <= conditionIndex; i++) { resolvedComponents[i].setIndex(i); diff --git a/kora-app-annotation-processor/src/test/java/io/koraframework/kora/app/annotation/processor/ConditionalComponentTest.java b/kora-app-annotation-processor/src/test/java/io/koraframework/kora/app/annotation/processor/ConditionalComponentTest.java index 40bd33826..059263c77 100644 --- a/kora-app-annotation-processor/src/test/java/io/koraframework/kora/app/annotation/processor/ConditionalComponentTest.java +++ b/kora-app-annotation-processor/src/test/java/io/koraframework/kora/app/annotation/processor/ConditionalComponentTest.java @@ -173,4 +173,55 @@ public class TestClass2 {} } } } + + @Test + public void testConditionalWithAll() { + var draw = compile(""" + import io.koraframework.application.graph.All;import io.koraframework.application.graph.PromiseOf;import io.koraframework.application.graph.ValueOf;@KoraApp + public interface ExampleApplication { + @Root + default Object root(All o1, All> o2, All> o3) { + o1.forEach(java.util.Objects::requireNonNull); + o2.forEach(java.util.Objects::requireNonNull); + o3.forEach(java.util.Objects::requireNonNull); + return o2.iterator().next(); + } + + @Tag(io.koraframework.kora.app.annotation.processor.ConditionalComponentTest.MatchesCondition.class) + default GraphCondition matches() { return new io.koraframework.kora.app.annotation.processor.ConditionalComponentTest.MatchesCondition(); } + + @Tag(io.koraframework.kora.app.annotation.processor.ConditionalComponentTest.FailedCondition.class) + default GraphCondition failed() { return new io.koraframework.kora.app.annotation.processor.ConditionalComponentTest.FailedCondition(); } + } + """, """ + @Component + @Conditional(tag = io.koraframework.kora.app.annotation.processor.ConditionalComponentTest.MatchesCondition.class) + public class TestClass1 implements TestInterface{ + } + """, """ + @Component + @Conditional(tag = io.koraframework.kora.app.annotation.processor.ConditionalComponentTest.FailedCondition.class) + public class TestClass2 implements TestInterface{ + } + """, """ + public interface TestInterface {} + """); + assertThat(draw.getNodes()).hasSize(5); + var graph = draw.init(); + var class1Node = draw.getNodes() + .stream() + .filter(n -> n.type().toString().contains("TestClass1")) + .findFirst() + .get(); + var class2Node = draw.getNodes() + .stream() + .filter(n -> n.type().toString().contains("TestClass2")) + .findFirst() + .get(); + Assertions.assertThat(graph.get(class1Node)).isNotNull(); + Assertions.assertThatThrownBy(() -> graph.get(class2Node)) + .hasMessage("Node value was not initialized: test"); + } + + } diff --git a/kora-app-symbol-processor/src/main/kotlin/io/koraframework/kora/app/ksp/component/ResolvedComponent.kt b/kora-app-symbol-processor/src/main/kotlin/io/koraframework/kora/app/ksp/component/ResolvedComponent.kt index 65f607a05..99112851c 100644 --- a/kora-app-symbol-processor/src/main/kotlin/io/koraframework/kora/app/ksp/component/ResolvedComponent.kt +++ b/kora-app-symbol-processor/src/main/kotlin/io/koraframework/kora/app/ksp/component/ResolvedComponent.kt @@ -65,6 +65,7 @@ class ResolvedComponent( else -> { val set = HashSet(this.parentConditions) set.add(this.declaration.condition) + set.remove(unconditionally) set } } diff --git a/kora-app-symbol-processor/src/main/kotlin/io/koraframework/kora/app/ksp/component/ResolvedComponents.kt b/kora-app-symbol-processor/src/main/kotlin/io/koraframework/kora/app/ksp/component/ResolvedComponents.kt index a9a3f3fa1..f14f25f56 100644 --- a/kora-app-symbol-processor/src/main/kotlin/io/koraframework/kora/app/ksp/component/ResolvedComponents.kt +++ b/kora-app-symbol-processor/src/main/kotlin/io/koraframework/kora/app/ksp/component/ResolvedComponents.kt @@ -73,7 +73,7 @@ class ResolvedComponents() { .map { maxDependencyIndex(it) } .minOrNull() ?: -1 require(conditionIndex > maxDependency) - resolvedComponents.copyInto(resolvedComponents, maxDependency + 1, maxDependency + 2, conditionIndex - maxDependency) + resolvedComponents.copyInto(resolvedComponents, maxDependency + 1, maxDependency + 2, conditionIndex - maxDependency - 1) resolvedComponents[maxDependency + 1] = condition; for (i in maxDependency + 1..conditionIndex) { resolvedComponents[i]?.setIndex(i) diff --git a/kora-app-symbol-processor/src/test/kotlin/io/koraframework/kora/app/ksp/ConditionalComponentTest.kt b/kora-app-symbol-processor/src/test/kotlin/io/koraframework/kora/app/ksp/ConditionalComponentTest.kt index 81ddc4548..50b338e50 100644 --- a/kora-app-symbol-processor/src/test/kotlin/io/koraframework/kora/app/ksp/ConditionalComponentTest.kt +++ b/kora-app-symbol-processor/src/test/kotlin/io/koraframework/kora/app/ksp/ConditionalComponentTest.kt @@ -160,4 +160,45 @@ class ConditionalComponentTest : AbstractKoraAppProcessorTest() { } } + @Test + fun testConditionalWithAll() { + val draw = compile( + """ + @KoraApp + interface ExampleApplication { + @Root + fun root(o1: All, o2: All>, o3: All>): Any { + o1.forEach(java.util.Objects::requireNonNull) + o2.forEach(java.util.Objects::requireNonNull) + o3.forEach(java.util.Objects::requireNonNull) + return o2.iterator().next() + } + + @Tag(io.koraframework.kora.app.ksp.ConditionalComponentTest.MatchesCondition::class) + fun matches(): GraphCondition { return io.koraframework.kora.app.ksp.ConditionalComponentTest.MatchesCondition() } + + @Tag(io.koraframework.kora.app.ksp.ConditionalComponentTest.FailedCondition::class) + fun failed(): GraphCondition { return io.koraframework.kora.app.ksp.ConditionalComponentTest.FailedCondition() } + } + + """.trimIndent(), """ + @Component + @Conditional(tag = io.koraframework.kora.app.ksp.ConditionalComponentTest.MatchesCondition::class) + class TestClass1: TestInterface + """.trimIndent(), """ + @Component + @Conditional(tag = io.koraframework.kora.app.ksp.ConditionalComponentTest.FailedCondition::class) + class TestClass2: TestInterface + """.trimIndent(), """ + interface TestInterface + """.trimIndent() + ) + assertThat(draw.nodes).hasSize(5) + val graph = draw.init() + val class1Node = draw.nodes.first { it.type().toString().contains("TestClass1") } + val class2Node = draw.nodes.first { it.type().toString().contains("TestClass2") } + assertThat(graph.get(class1Node)).isNotNull() + assertThatThrownBy { graph.get(class2Node) } + .hasMessage("Node value was not initialized: test") + } } diff --git a/symbol-processor-common/src/testFixtures/kotlin/io/koraframework/ksp/common/AbstractSymbolProcessorTest.kt b/symbol-processor-common/src/testFixtures/kotlin/io/koraframework/ksp/common/AbstractSymbolProcessorTest.kt index e19c02c18..bb207cc64 100644 --- a/symbol-processor-common/src/testFixtures/kotlin/io/koraframework/ksp/common/AbstractSymbolProcessorTest.kt +++ b/symbol-processor-common/src/testFixtures/kotlin/io/koraframework/ksp/common/AbstractSymbolProcessorTest.kt @@ -91,6 +91,7 @@ abstract class AbstractSymbolProcessorTest { s.indexOf("{", it + 1), s.indexOf(":", it + 1), s.indexOf("<", it + 1), + s.length - 1 ) .map { it1 -> it to it1 } }