diff options
Diffstat (limited to 'subprojects/logic/src/main/java/tools/refinery/logic/literal/LeftJoinLiteral.java')
-rw-r--r-- | subprojects/logic/src/main/java/tools/refinery/logic/literal/LeftJoinLiteral.java | 140 |
1 files changed, 140 insertions, 0 deletions
diff --git a/subprojects/logic/src/main/java/tools/refinery/logic/literal/LeftJoinLiteral.java b/subprojects/logic/src/main/java/tools/refinery/logic/literal/LeftJoinLiteral.java new file mode 100644 index 00000000..593904c5 --- /dev/null +++ b/subprojects/logic/src/main/java/tools/refinery/logic/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.logic.literal; | ||
7 | |||
8 | import tools.refinery.logic.Constraint; | ||
9 | import tools.refinery.logic.InvalidQueryException; | ||
10 | import tools.refinery.logic.equality.LiteralEqualityHelper; | ||
11 | import tools.refinery.logic.equality.LiteralHashCodeHelper; | ||
12 | import tools.refinery.logic.substitution.Substitution; | ||
13 | import tools.refinery.logic.term.ConstantTerm; | ||
14 | import tools.refinery.logic.term.DataVariable; | ||
15 | import tools.refinery.logic.term.ParameterDirection; | ||
16 | import tools.refinery.logic.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 | } | ||