aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/CallLiteral.java
blob: 2d0e4e97ff73f5bae609e9233c0a943acb9115f2 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
/*
 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
 *
 * SPDX-License-Identifier: EPL-2.0
 */
package tools.refinery.store.query.literal;

import tools.refinery.store.query.Constraint;
import tools.refinery.store.query.InvalidQueryException;
import tools.refinery.store.query.equality.LiteralEqualityHelper;
import tools.refinery.store.query.equality.LiteralHashCodeHelper;
import tools.refinery.store.query.substitution.Substitution;
import tools.refinery.store.query.term.ParameterDirection;
import tools.refinery.store.query.term.Variable;

import java.util.*;

// {@link Object#equals(Object)} is implemented by {@link AbstractLiteral}.
@SuppressWarnings("squid:S2160")
public final class CallLiteral extends AbstractCallLiteral implements CanNegate<CallLiteral> {
	private final CallPolarity polarity;

	public CallLiteral(CallPolarity polarity, Constraint target, List<Variable> arguments) {
		super(target, arguments);
		var parameters = target.getParameters();
		int arity = target.arity();
		if (polarity.isTransitive()) {
			if (arity != 2) {
				throw new InvalidQueryException("Transitive closures can only take binary relations");
			}
			if (parameters.get(0).isDataVariable() || parameters.get(1).isDataVariable()) {
				throw new InvalidQueryException("Transitive closures can only be computed over nodes");
			}
			if (parameters.get(0).getDirection() != ParameterDirection.OUT ||
					parameters.get(1).getDirection() != ParameterDirection.OUT) {
				throw new InvalidQueryException("Transitive closures cannot take input parameters");
			}
		}
		this.polarity = polarity;
	}

	public CallPolarity getPolarity() {
		return polarity;
	}

	@Override
	protected Literal doSubstitute(Substitution substitution, List<Variable> substitutedArguments) {
		return new CallLiteral(polarity, getTarget(), substitutedArguments);
	}

	@Override
	public Set<Variable> getOutputVariables() {
		if (polarity.isPositive()) {
			return getArgumentsOfDirection(ParameterDirection.OUT);
		}
		return Set.of();
	}

	@Override
	public Set<Variable> getInputVariables(Set<? extends Variable> positiveVariablesInClause) {
		if (polarity.isPositive()) {
			return getArgumentsOfDirection(ParameterDirection.IN);
		}
		return super.getInputVariables(positiveVariablesInClause);
	}

	@Override
	public Set<Variable> getPrivateVariables(Set<? extends Variable> positiveVariablesInClause) {
		if (polarity.isPositive()) {
			return Set.of();
		}
		return super.getPrivateVariables(positiveVariablesInClause);
	}

	@Override
	public Literal reduce() {
		var reduction = getTarget().getReduction();
		var negatedReduction = polarity.isPositive() ? reduction : reduction.negate();
		return switch (negatedReduction) {
			case ALWAYS_TRUE -> BooleanLiteral.TRUE;
			case ALWAYS_FALSE -> BooleanLiteral.FALSE;
			default -> this;
		};
	}

	@Override
	public boolean equalsWithSubstitution(LiteralEqualityHelper helper, Literal other) {
		if (!super.equalsWithSubstitution(helper, other)) {
			return false;
		}
		var otherCallLiteral = (CallLiteral) other;
		return polarity.equals(otherCallLiteral.polarity);
	}

	@Override
	public int hashCodeWithSubstitution(LiteralHashCodeHelper helper) {
		return Objects.hash(super.hashCodeWithSubstitution(helper), polarity);
	}

	@Override
	public CallLiteral negate() {
		return new CallLiteral(polarity.negate(), getTarget(), getArguments());
	}

	@Override
	public AbstractCallLiteral withArguments(Constraint newTarget, List<Variable> newArguments) {
		return new CallLiteral(polarity, newTarget, newArguments);
	}

	@Override
	public String toString() {
		var builder = new StringBuilder();
		if (!polarity.isPositive()) {
			builder.append("!(");
		}
		builder.append(getTarget().toReferenceString());
		if (polarity.isTransitive()) {
			builder.append("+");
		}
		builder.append("(");
		var argumentIterator = getArguments().iterator();
		if (argumentIterator.hasNext()) {
			builder.append(argumentIterator.next());
			while (argumentIterator.hasNext()) {
				builder.append(", ").append(argumentIterator.next());
			}
		}
		builder.append(")");
		if (!polarity.isPositive()) {
			builder.append(")");
		}
		return builder.toString();
	}
}