aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/logic/src/main/java/tools/refinery/logic/literal/LeftJoinLiteral.java
diff options
context:
space:
mode:
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.java140
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 */
6package tools.refinery.logic.literal;
7
8import tools.refinery.logic.Constraint;
9import tools.refinery.logic.InvalidQueryException;
10import tools.refinery.logic.equality.LiteralEqualityHelper;
11import tools.refinery.logic.equality.LiteralHashCodeHelper;
12import tools.refinery.logic.substitution.Substitution;
13import tools.refinery.logic.term.ConstantTerm;
14import tools.refinery.logic.term.DataVariable;
15import tools.refinery.logic.term.ParameterDirection;
16import tools.refinery.logic.term.Variable;
17
18import java.util.*;
19
20// {@link Object#equals(Object)} is implemented by {@link AbstractLiteral}.
21@SuppressWarnings("squid:S2160")
22public 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}