diff options
Diffstat (limited to 'subprojects/store-query/src/main/java/tools/refinery/store/query/literal/AbstractCountLiteral.java')
-rw-r--r-- | subprojects/store-query/src/main/java/tools/refinery/store/query/literal/AbstractCountLiteral.java | 107 |
1 files changed, 107 insertions, 0 deletions
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/AbstractCountLiteral.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/AbstractCountLiteral.java new file mode 100644 index 00000000..9bb572c0 --- /dev/null +++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/AbstractCountLiteral.java | |||
@@ -0,0 +1,107 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2023 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.term.ConstantTerm; | ||
13 | import tools.refinery.store.query.term.DataVariable; | ||
14 | import tools.refinery.store.query.term.Variable; | ||
15 | |||
16 | import java.util.List; | ||
17 | import java.util.Objects; | ||
18 | import java.util.Set; | ||
19 | |||
20 | // {@link Object#equals(Object)} is implemented by {@link AbstractLiteral}. | ||
21 | @SuppressWarnings("squid:S2160") | ||
22 | public abstract class AbstractCountLiteral<T> extends AbstractCallLiteral { | ||
23 | private final Class<T> resultType; | ||
24 | private final DataVariable<T> resultVariable; | ||
25 | |||
26 | protected AbstractCountLiteral(Class<T> resultType, DataVariable<T> resultVariable, Constraint target, | ||
27 | List<Variable> arguments) { | ||
28 | super(target, arguments); | ||
29 | if (!resultVariable.getType().equals(resultType)) { | ||
30 | throw new InvalidQueryException("Count result variable %s must be of type %s, got %s instead".formatted( | ||
31 | resultVariable, resultType, resultVariable.getType().getName())); | ||
32 | } | ||
33 | if (arguments.contains(resultVariable)) { | ||
34 | throw new InvalidQueryException("Count result variable %s must not appear in the argument list" | ||
35 | .formatted(resultVariable)); | ||
36 | } | ||
37 | this.resultType = resultType; | ||
38 | this.resultVariable = resultVariable; | ||
39 | } | ||
40 | |||
41 | public Class<T> getResultType() { | ||
42 | return resultType; | ||
43 | } | ||
44 | |||
45 | public DataVariable<T> getResultVariable() { | ||
46 | return resultVariable; | ||
47 | } | ||
48 | |||
49 | @Override | ||
50 | public Set<Variable> getOutputVariables() { | ||
51 | return Set.of(resultVariable); | ||
52 | } | ||
53 | |||
54 | protected abstract T zero(); | ||
55 | |||
56 | protected abstract T one(); | ||
57 | |||
58 | @Override | ||
59 | public Literal reduce() { | ||
60 | var reduction = getTarget().getReduction(); | ||
61 | return switch (reduction) { | ||
62 | case ALWAYS_FALSE -> getResultVariable().assign(new ConstantTerm<>(resultType, zero())); | ||
63 | // The only way a constant {@code true} predicate can be called in a negative position is to have all of | ||
64 | // its arguments bound as input variables. Thus, there will only be a single match. | ||
65 | case ALWAYS_TRUE -> getResultVariable().assign(new ConstantTerm<>(resultType, one())); | ||
66 | case NOT_REDUCIBLE -> this; | ||
67 | }; | ||
68 | } | ||
69 | |||
70 | @Override | ||
71 | public boolean equalsWithSubstitution(LiteralEqualityHelper helper, Literal other) { | ||
72 | if (!super.equalsWithSubstitution(helper, other)) { | ||
73 | return false; | ||
74 | } | ||
75 | var otherCountLiteral = (AbstractCountLiteral<?>) other; | ||
76 | return Objects.equals(resultType, otherCountLiteral.resultType) && | ||
77 | helper.variableEqual(resultVariable, otherCountLiteral.resultVariable); | ||
78 | } | ||
79 | |||
80 | @Override | ||
81 | public int hashCodeWithSubstitution(LiteralHashCodeHelper helper) { | ||
82 | return Objects.hash(super.hashCodeWithSubstitution(helper), resultType, | ||
83 | helper.getVariableHashCode(resultVariable)); | ||
84 | } | ||
85 | |||
86 | protected abstract String operatorName(); | ||
87 | |||
88 | @Override | ||
89 | public String toString() { | ||
90 | var builder = new StringBuilder(); | ||
91 | builder.append(resultVariable); | ||
92 | builder.append(" is "); | ||
93 | builder.append(operatorName()); | ||
94 | builder.append(' '); | ||
95 | builder.append(getTarget().toReferenceString()); | ||
96 | builder.append('('); | ||
97 | var argumentIterator = getArguments().iterator(); | ||
98 | if (argumentIterator.hasNext()) { | ||
99 | builder.append(argumentIterator.next()); | ||
100 | while (argumentIterator.hasNext()) { | ||
101 | builder.append(", ").append(argumentIterator.next()); | ||
102 | } | ||
103 | } | ||
104 | builder.append(')'); | ||
105 | return builder.toString(); | ||
106 | } | ||
107 | } | ||