aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalQueryMetaContext.java
blob: cba3fa08080eecb9e47fd00e0702feb3a6d668b6 (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
package tools.refinery.store.query.viatra.internal.context;

import org.eclipse.viatra.query.runtime.matchers.context.AbstractQueryMetaContext;
import org.eclipse.viatra.query.runtime.matchers.context.IInputKey;
import org.eclipse.viatra.query.runtime.matchers.context.InputKeyImplication;
import tools.refinery.store.query.viatra.internal.pquery.RelationViewWrapper;
import tools.refinery.store.query.view.AnyRelationView;

import java.util.*;

/**
 * The meta context information for String scopes.
 */
public class RelationalQueryMetaContext extends AbstractQueryMetaContext {
	private final Map<AnyRelationView, IInputKey> inputKeys;

	RelationalQueryMetaContext(Map<AnyRelationView, IInputKey> inputKeys) {
		this.inputKeys = inputKeys;
	}

	@Override
	public boolean isEnumerable(IInputKey key) {
		checkKey(key);
		return key.isEnumerable();
	}

	@Override
	public boolean isStateless(IInputKey key) {
		checkKey(key);
		return true;
	}

	@Override
	public boolean canLeadOutOfScope(IInputKey key) {
		return false;
	}

	@Override
	public Collection<InputKeyImplication> getImplications(IInputKey implyingKey) {
		var relationView = checkKey(implyingKey);
		var relationViewImplications = relationView.getImpliedRelationViews();
		var inputKeyImplications = new HashSet<InputKeyImplication>(relationViewImplications.size());
		for (var relationViewImplication : relationViewImplications) {
			if (!relationView.equals(relationViewImplication.implyingRelationView())) {
				throw new IllegalArgumentException("Relation view %s returned unrelated implication %s".formatted(
						relationView, relationViewImplication));
			}
			var impliedInputKey = inputKeys.get(relationViewImplication.impliedRelationView());
			// Ignore implications not relevant for any queries included in the model.
			if (impliedInputKey != null) {
				inputKeyImplications.add(new InputKeyImplication(implyingKey, impliedInputKey,
						relationViewImplication.impliedIndices()));
			}
		}
		return inputKeyImplications;
	}

	@Override
	public Map<Set<Integer>, Set<Integer>> getFunctionalDependencies(IInputKey key) {
		var relationView = checkKey(key);
		var functionalDependencies = relationView.getFunctionalDependencies();
		var flattened = new HashMap<Set<Integer>, Set<Integer>>(functionalDependencies.size());
		for (var functionalDependency : functionalDependencies) {
			var forEach = functionalDependency.forEach();
			checkValidIndices(relationView, forEach);
			var unique = functionalDependency.unique();
			checkValidIndices(relationView, unique);
			var existing = flattened.get(forEach);
			if (existing == null) {
				flattened.put(forEach, new HashSet<>(unique));
			} else {
				existing.addAll(unique);
			}
		}
		return flattened;
	}

	private static void checkValidIndices(AnyRelationView relationView, Collection<Integer> indices) {
		indices.stream().filter(relationView::invalidIndex).findAny().ifPresent(i -> {
			throw new IllegalArgumentException("Index %d is invalid for %s".formatted(i, relationView));
		});
	}

	public AnyRelationView checkKey(IInputKey key) {
		if (!(key instanceof RelationViewWrapper wrapper)) {
			throw new IllegalArgumentException("The input key %s is not a valid input key".formatted(key));
		}
		var relationView = wrapper.getWrappedKey();
		if (!inputKeys.containsKey(relationView)) {
			throw new IllegalArgumentException("The input key %s is not present in the model".formatted(key));
		}
		return relationView;
	}
}