diff options
Diffstat (limited to 'subprojects/store-query/src')
7 files changed, 195 insertions, 10 deletions
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/Constraint.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/Constraint.java index 916fb35c..375c582a 100644 --- a/subprojects/store-query/src/main/java/tools/refinery/store/query/Constraint.java +++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/Constraint.java | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/> | 2 | * SPDX-FileCopyrightText: 2021-2024 The Refinery Authors <https://refinery.tools/> |
3 | * | 3 | * |
4 | * SPDX-License-Identifier: EPL-2.0 | 4 | * SPDX-License-Identifier: EPL-2.0 |
5 | */ | 5 | */ |
@@ -69,4 +69,14 @@ public interface Constraint { | |||
69 | Variable... arguments) { | 69 | Variable... arguments) { |
70 | return aggregateBy(inputVariable, aggregator, List.of(arguments)); | 70 | return aggregateBy(inputVariable, aggregator, List.of(arguments)); |
71 | } | 71 | } |
72 | |||
73 | default <T> AssignedValue<T> leftJoinBy(DataVariable<T> placeholderVariable, T defaultValue, | ||
74 | List<Variable> arguments) { | ||
75 | return targetVariable -> new LeftJoinLiteral<>(targetVariable, placeholderVariable, defaultValue, this, | ||
76 | arguments); | ||
77 | } | ||
78 | |||
79 | default <T> AssignedValue<T> leftJoinBy(DataVariable<T> inputVariable, T defaultValue, Variable... arguments) { | ||
80 | return leftJoinBy(inputVariable, defaultValue, List.of(arguments)); | ||
81 | } | ||
72 | } | 82 | } |
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/ClausePostProcessor.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/ClausePostProcessor.java index 8800a155..7cd05364 100644 --- a/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/ClausePostProcessor.java +++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/ClausePostProcessor.java | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/> | 2 | * SPDX-FileCopyrightText: 2021-2024 The Refinery Authors <https://refinery.tools/> |
3 | * | 3 | * |
4 | * SPDX-License-Identifier: EPL-2.0 | 4 | * SPDX-License-Identifier: EPL-2.0 |
5 | */ | 5 | */ |
@@ -112,7 +112,7 @@ class ClausePostProcessor { | |||
112 | .formatted(variable, representative)); | 112 | .formatted(variable, representative)); |
113 | } | 113 | } |
114 | return equivalencePartition.computeIfAbsent(variable, key -> { | 114 | return equivalencePartition.computeIfAbsent(variable, key -> { |
115 | var set = new HashSet<Variable>(1); | 115 | var set = HashSet.<Variable>newHashSet(1); |
116 | set.add(key); | 116 | set.add(key); |
117 | return set; | 117 | return set; |
118 | }); | 118 | }); |
@@ -193,7 +193,7 @@ class ClausePostProcessor { | |||
193 | } | 193 | } |
194 | 194 | ||
195 | private void topologicallySortLiterals() { | 195 | private void topologicallySortLiterals() { |
196 | topologicallySortedLiterals = new LinkedHashSet<>(substitutedLiterals.size()); | 196 | topologicallySortedLiterals = LinkedHashSet.newLinkedHashSet(substitutedLiterals.size()); |
197 | variableToLiteralInputMap = new HashMap<>(); | 197 | variableToLiteralInputMap = new HashMap<>(); |
198 | literalsWithAllInputsBound = new PriorityQueue<>(); | 198 | literalsWithAllInputsBound = new PriorityQueue<>(); |
199 | int size = substitutedLiterals.size(); | 199 | int size = substitutedLiterals.size(); |
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/DnfPostProcessor.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/DnfPostProcessor.java index 50236642..01344b59 100644 --- a/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/DnfPostProcessor.java +++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/DnfPostProcessor.java | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | 2 | * SPDX-FileCopyrightText: 2023-2024 The Refinery Authors <https://refinery.tools/> |
3 | * | 3 | * |
4 | * SPDX-License-Identifier: EPL-2.0 | 4 | * SPDX-License-Identifier: EPL-2.0 |
5 | */ | 5 | */ |
@@ -26,7 +26,7 @@ class DnfPostProcessor { | |||
26 | 26 | ||
27 | public List<DnfClause> postProcessClauses() { | 27 | public List<DnfClause> postProcessClauses() { |
28 | var parameterInfoMap = getParameterInfoMap(); | 28 | var parameterInfoMap = getParameterInfoMap(); |
29 | var postProcessedClauses = new LinkedHashSet<CanonicalClause>(clauses.size()); | 29 | var postProcessedClauses = LinkedHashSet.<CanonicalClause>newLinkedHashSet(clauses.size()); |
30 | int index = 0; | 30 | int index = 0; |
31 | for (var literals : clauses) { | 31 | for (var literals : clauses) { |
32 | var postProcessor = new ClausePostProcessor(parameterInfoMap, literals); | 32 | var postProcessor = new ClausePostProcessor(parameterInfoMap, literals); |
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/FunctionalQuery.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/FunctionalQuery.java index 225f6844..b0a03c7d 100644 --- a/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/FunctionalQuery.java +++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/FunctionalQuery.java | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/> | 2 | * SPDX-FileCopyrightText: 2021-2024 The Refinery Authors <https://refinery.tools/> |
3 | * | 3 | * |
4 | * SPDX-License-Identifier: EPL-2.0 | 4 | * SPDX-License-Identifier: EPL-2.0 |
5 | */ | 5 | */ |
@@ -94,6 +94,22 @@ public final class FunctionalQuery<T> extends Query<T> { | |||
94 | return aggregate(aggregator, List.of(arguments)); | 94 | return aggregate(aggregator, List.of(arguments)); |
95 | } | 95 | } |
96 | 96 | ||
97 | public AssignedValue<T> leftJoin(T defaultValue, List<NodeVariable> arguments) { | ||
98 | return targetVariable -> { | ||
99 | var placeholderVariable = Variable.of(type); | ||
100 | var argumentsWithPlaceholder = new ArrayList<Variable>(arguments.size() + 1); | ||
101 | argumentsWithPlaceholder.addAll(arguments); | ||
102 | argumentsWithPlaceholder.add(placeholderVariable); | ||
103 | return getDnf() | ||
104 | .leftJoinBy(placeholderVariable, defaultValue, argumentsWithPlaceholder) | ||
105 | .toLiteral(targetVariable); | ||
106 | }; | ||
107 | } | ||
108 | |||
109 | public AssignedValue<T> leftJoin(T defaultValue, NodeVariable... arguments) { | ||
110 | return leftJoin(defaultValue, List.of(arguments)); | ||
111 | } | ||
112 | |||
97 | @Override | 113 | @Override |
98 | public boolean equals(Object o) { | 114 | public boolean equals(Object o) { |
99 | if (this == o) return true; | 115 | if (this == o) return true; |
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/AggregationLiteral.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/AggregationLiteral.java index e3acfacc..b6861de0 100644 --- a/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/AggregationLiteral.java +++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/AggregationLiteral.java | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/> | 2 | * SPDX-FileCopyrightText: 2021-2024 The Refinery Authors <https://refinery.tools/> |
3 | * | 3 | * |
4 | * SPDX-License-Identifier: EPL-2.0 | 4 | * SPDX-License-Identifier: EPL-2.0 |
5 | */ | 5 | */ |
@@ -129,7 +129,12 @@ public class AggregationLiteral<R, T> extends AbstractCallLiteral { | |||
129 | } | 129 | } |
130 | builder.append(argument); | 130 | builder.append(argument); |
131 | while (argumentIterator.hasNext()) { | 131 | while (argumentIterator.hasNext()) { |
132 | builder.append(", ").append(argumentIterator.next()); | 132 | builder.append(", "); |
133 | argument = argumentIterator.next(); | ||
134 | if (inputVariable.equals(argument)) { | ||
135 | builder.append("@Aggregate(\"").append(aggregator).append("\") "); | ||
136 | } | ||
137 | builder.append(argument); | ||
133 | } | 138 | } |
134 | } | 139 | } |
135 | builder.append(")"); | 140 | builder.append(")"); |
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/LeftJoinLiteral.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/LeftJoinLiteral.java new file mode 100644 index 00000000..bdddf120 --- /dev/null +++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/LeftJoinLiteral.java | |||
@@ -0,0 +1,140 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.store.query.literal; | ||
7 | |||
8 | import tools.refinery.store.query.Constraint; | ||
9 | import tools.refinery.store.query.InvalidQueryException; | ||
10 | import tools.refinery.store.query.equality.LiteralEqualityHelper; | ||
11 | import tools.refinery.store.query.equality.LiteralHashCodeHelper; | ||
12 | import tools.refinery.store.query.substitution.Substitution; | ||
13 | import tools.refinery.store.query.term.ConstantTerm; | ||
14 | import tools.refinery.store.query.term.DataVariable; | ||
15 | import tools.refinery.store.query.term.ParameterDirection; | ||
16 | import tools.refinery.store.query.term.Variable; | ||
17 | |||
18 | import java.util.*; | ||
19 | |||
20 | // {@link Object#equals(Object)} is implemented by {@link AbstractLiteral}. | ||
21 | @SuppressWarnings("squid:S2160") | ||
22 | public class LeftJoinLiteral<T> extends AbstractCallLiteral { | ||
23 | private final DataVariable<T> resultVariable; | ||
24 | private final DataVariable<T> placeholderVariable; | ||
25 | private final T defaultValue; | ||
26 | |||
27 | public LeftJoinLiteral(DataVariable<T> resultVariable, DataVariable<T> placeholderVariable, | ||
28 | T defaultValue, Constraint target, List<Variable> arguments) { | ||
29 | super(target, arguments); | ||
30 | this.resultVariable = resultVariable; | ||
31 | this.placeholderVariable = placeholderVariable; | ||
32 | this.defaultValue = defaultValue; | ||
33 | if (defaultValue == null) { | ||
34 | throw new InvalidQueryException("Default value must not be null"); | ||
35 | } | ||
36 | if (!resultVariable.getType().isInstance(defaultValue)) { | ||
37 | throw new InvalidQueryException("Default value %s must be assignable to result variable %s type %s" | ||
38 | .formatted(defaultValue, resultVariable, resultVariable.getType().getName())); | ||
39 | } | ||
40 | if (!getArgumentsOfDirection(ParameterDirection.OUT).contains(placeholderVariable)) { | ||
41 | throw new InvalidQueryException( | ||
42 | "Placeholder variable %s must be bound with direction %s in the argument list" | ||
43 | .formatted(resultVariable, ParameterDirection.OUT)); | ||
44 | } | ||
45 | if (arguments.contains(resultVariable)) { | ||
46 | throw new InvalidQueryException("Result variable must not appear in the argument list"); | ||
47 | } | ||
48 | } | ||
49 | |||
50 | public DataVariable<T> getResultVariable() { | ||
51 | return resultVariable; | ||
52 | } | ||
53 | |||
54 | public DataVariable<T> getPlaceholderVariable() { | ||
55 | return placeholderVariable; | ||
56 | } | ||
57 | |||
58 | public T getDefaultValue() { | ||
59 | return defaultValue; | ||
60 | } | ||
61 | |||
62 | @Override | ||
63 | public Set<Variable> getOutputVariables() { | ||
64 | return Set.of(resultVariable); | ||
65 | } | ||
66 | |||
67 | @Override | ||
68 | public Set<Variable> getInputVariables(Set<? extends Variable> positiveVariablesInClause) { | ||
69 | var inputVariables = new LinkedHashSet<>(getArguments()); | ||
70 | inputVariables.remove(placeholderVariable); | ||
71 | return Collections.unmodifiableSet(inputVariables); | ||
72 | } | ||
73 | |||
74 | @Override | ||
75 | public Set<Variable> getPrivateVariables(Set<? extends Variable> positiveVariablesInClause) { | ||
76 | return Set.of(placeholderVariable); | ||
77 | } | ||
78 | |||
79 | @Override | ||
80 | public Literal reduce() { | ||
81 | var reduction = getTarget().getReduction(); | ||
82 | return switch (reduction) { | ||
83 | case ALWAYS_FALSE -> resultVariable.assign(new ConstantTerm<>(resultVariable.getType(), defaultValue)); | ||
84 | case ALWAYS_TRUE -> throw new InvalidQueryException("Trying to left join an infinite set"); | ||
85 | case NOT_REDUCIBLE -> this; | ||
86 | }; | ||
87 | } | ||
88 | |||
89 | @Override | ||
90 | protected Literal doSubstitute(Substitution substitution, List<Variable> substitutedArguments) { | ||
91 | return new LeftJoinLiteral<>(substitution.getTypeSafeSubstitute(resultVariable), | ||
92 | substitution.getTypeSafeSubstitute(placeholderVariable), defaultValue, getTarget(), | ||
93 | substitutedArguments); | ||
94 | } | ||
95 | |||
96 | @Override | ||
97 | public AbstractCallLiteral withArguments(Constraint newTarget, List<Variable> newArguments) { | ||
98 | return new LeftJoinLiteral<>(resultVariable, placeholderVariable, defaultValue, newTarget, newArguments); | ||
99 | } | ||
100 | |||
101 | @Override | ||
102 | public boolean equalsWithSubstitution(LiteralEqualityHelper helper, Literal other) { | ||
103 | if (!super.equalsWithSubstitution(helper, other)) { | ||
104 | return false; | ||
105 | } | ||
106 | var otherLeftJoinLiteral = (LeftJoinLiteral<?>) other; | ||
107 | return helper.variableEqual(resultVariable, otherLeftJoinLiteral.resultVariable) && | ||
108 | helper.variableEqual(placeholderVariable, otherLeftJoinLiteral.placeholderVariable) && | ||
109 | Objects.equals(defaultValue, otherLeftJoinLiteral.defaultValue); | ||
110 | } | ||
111 | |||
112 | @Override | ||
113 | public int hashCodeWithSubstitution(LiteralHashCodeHelper helper) { | ||
114 | return Objects.hash(super.hashCodeWithSubstitution(helper), helper.getVariableHashCode(resultVariable), | ||
115 | helper.getVariableHashCode(placeholderVariable), defaultValue); | ||
116 | } | ||
117 | |||
118 | @Override | ||
119 | public String toString() { | ||
120 | var builder = new StringBuilder(); | ||
121 | var argumentIterator = getArguments().iterator(); | ||
122 | if (argumentIterator.hasNext()) { | ||
123 | appendArgument(builder, argumentIterator.next()); | ||
124 | while (argumentIterator.hasNext()) { | ||
125 | builder.append(", "); | ||
126 | appendArgument(builder, argumentIterator.next()); | ||
127 | } | ||
128 | } | ||
129 | builder.append(")"); | ||
130 | return builder.toString(); | ||
131 | } | ||
132 | |||
133 | private void appendArgument(StringBuilder builder, Variable argument) { | ||
134 | if (placeholderVariable.equals(argument)) { | ||
135 | builder.append("@Default(").append(defaultValue).append(") "); | ||
136 | argument = resultVariable; | ||
137 | } | ||
138 | builder.append(argument); | ||
139 | } | ||
140 | } | ||
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/view/FunctionView.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/view/FunctionView.java index 74a5be07..3dfb6777 100644 --- a/subprojects/store-query/src/main/java/tools/refinery/store/query/view/FunctionView.java +++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/view/FunctionView.java | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/> | 2 | * SPDX-FileCopyrightText: 2021-2024 The Refinery Authors <https://refinery.tools/> |
3 | * | 3 | * |
4 | * SPDX-License-Identifier: EPL-2.0 | 4 | * SPDX-License-Identifier: EPL-2.0 |
5 | */ | 5 | */ |
@@ -33,4 +33,18 @@ public final class FunctionView<T> extends AbstractFunctionView<T> { | |||
33 | public <R> AssignedValue<R> aggregate(Aggregator<R, T> aggregator, NodeVariable... arguments) { | 33 | public <R> AssignedValue<R> aggregate(Aggregator<R, T> aggregator, NodeVariable... arguments) { |
34 | return aggregate(aggregator, List.of(arguments)); | 34 | return aggregate(aggregator, List.of(arguments)); |
35 | } | 35 | } |
36 | |||
37 | public AssignedValue<T> leftJoin(T defaultValue, List<NodeVariable> arguments) { | ||
38 | return targetVariable -> { | ||
39 | var placeholderVariable = Variable.of(getSymbol().valueType()); | ||
40 | var argumentsWithPlaceholder = new ArrayList<Variable>(arguments.size() + 1); | ||
41 | argumentsWithPlaceholder.addAll(arguments); | ||
42 | argumentsWithPlaceholder.add(placeholderVariable); | ||
43 | return leftJoinBy(placeholderVariable, defaultValue, argumentsWithPlaceholder).toLiteral(targetVariable); | ||
44 | }; | ||
45 | } | ||
46 | |||
47 | public AssignedValue<T> leftJoin(T defaultValue, NodeVariable... arguments) { | ||
48 | return leftJoin(defaultValue, List.of(arguments)); | ||
49 | } | ||
36 | } | 50 | } |