aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/store/src/main/java/tools/refinery/store/model/internal/BaseIndexer.java
blob: 3d7f59d725bee7894f86f178f7426d5191b23c23 (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
/*
 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
 *
 * SPDX-License-Identifier: EPL-2.0
 */
package tools.refinery.store.model.internal;

import org.eclipse.collections.api.factory.Maps;
import org.eclipse.collections.api.factory.primitive.IntObjectMaps;
import org.eclipse.collections.api.map.MutableMap;
import org.eclipse.collections.api.map.primitive.MutableIntObjectMap;
import tools.refinery.store.map.*;
import tools.refinery.store.tuple.Tuple;

import java.util.Set;

class BaseIndexer<T> {
	private final MutableIntObjectMap<MutableMap<Tuple, T>>[] maps;
	private final VersionedMap<Tuple, T> versionedMap;

	public BaseIndexer(int arity, VersionedMap<Tuple, T> map) {
		if (arity < 2) {
			throw new IllegalArgumentException("Only arity >= 2 symbols need to be indexed");
		}
		// There is no way in Java to create a generic array in a checked way.
		@SuppressWarnings({"unchecked", "squid:S1905"})
		var uncheckedMaps = (MutableIntObjectMap<MutableMap<Tuple, T>>[]) new MutableIntObjectMap[arity];
		maps = uncheckedMaps;
		for (int i = 0; i < arity; i++) {
			maps[i] = IntObjectMaps.mutable.empty();
		}
		this.versionedMap = map;
		if (map != null) {
			var cursor = map.getAll();
			while (cursor.move()) {
				put(cursor.getKey(), cursor.getValue());
			}
		}
	}

	public void put(Tuple key, T value) {
		for (int i = 0; i < maps.length; i++) {
			var map = maps[i];
			int element = key.get(i);
			var adjacentTuples = map.getIfAbsentPut(element, Maps.mutable::empty);
			adjacentTuples.put(key, value);
		}
	}

	public void remove(Tuple key) {
		for (int i = 0; i < maps.length; i++) {
			var map = maps[i];
			int element = key.get(i);
			var adjacentTuples = map.get(element);
			if (adjacentTuples == null) {
				continue;
			}
			adjacentTuples.remove(key);
			if (adjacentTuples.isEmpty()) {
				map.remove(element);
			}
		}
	}

	private MutableMap<Tuple, T> getAdjacentMap(int slot, int node) {
		if (slot < 0 || slot >= maps.length) {
			throw new IllegalArgumentException("Invalid index: " + slot);
		}
		var map = maps[slot];
		return map.get(node);
	}

	public int getAdjacentSize(int slot, int node) {
		var adjacentTuples = getAdjacentMap(slot, node);
		if (adjacentTuples == null) {
			return 0;
		}
		return adjacentTuples.size();
	}

	public Cursor<Tuple, T> getAdjacent(int slot, int node) {
		var adjacentTuples = getAdjacentMap(slot, node);
		if (adjacentTuples == null) {
			return Cursors.empty();
		}
		return new IndexCursor<>(adjacentTuples, versionedMap);
	}

	private static class IndexCursor<T> extends IteratorBasedCursor<Tuple, T> {
		private final Set<AnyVersionedMap> dependingMaps;

		public IndexCursor(MutableMap<Tuple, T> map, VersionedMap<Tuple, T> versionedMap) {
			super(map.entrySet().iterator());
			dependingMaps = versionedMap == null ? Set.of() : Set.of(versionedMap);
		}

		@Override
		public Set<AnyVersionedMap> getDependingMaps() {
			return dependingMaps;
		}
	}
}