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

import tools.refinery.store.map.internal.*;

import java.util.*;

public class VersionedMapStoreImpl<K, V> implements VersionedMapStore<K, V> {
	// Configuration
	private final boolean immutableWhenCommitting;

	// Static data
	protected final ContinousHashProvider<K> hashProvider;
	protected final V defaultValue;

	// Dynamic data
	protected final Map<Long, ImmutableNode<K, V>> states = new HashMap<>();
	protected final Map<Node<K, V>, ImmutableNode<K, V>> nodeCache;
	protected long nextID = 0;

	public VersionedMapStoreImpl(ContinousHashProvider<K> hashProvider, V defaultValue,
			VersionedMapStoreConfiguration config) {
		this.immutableWhenCommitting = config.isImmutableWhenCommitting();
		this.hashProvider = hashProvider;
		this.defaultValue = defaultValue;
		if (config.isSharedNodeCacheInStore()) {
			nodeCache = new HashMap<>();
		} else {
			nodeCache = null;
		}
	}

	private VersionedMapStoreImpl(ContinousHashProvider<K> hashProvider, V defaultValue,
			Map<Node<K, V>, ImmutableNode<K, V>> nodeCache, VersionedMapStoreConfiguration config) {
		this.immutableWhenCommitting = config.isImmutableWhenCommitting();
		this.hashProvider = hashProvider;
		this.defaultValue = defaultValue;
		this.nodeCache = nodeCache;
	}

	public VersionedMapStoreImpl(ContinousHashProvider<K> hashProvider, V defaultValue) {
		this(hashProvider, defaultValue, new VersionedMapStoreConfiguration());
	}

	public static <K, V> List<VersionedMapStore<K, V>> createSharedVersionedMapStores(int amount,
			ContinousHashProvider<K> hashProvider, V defaultValue,
			VersionedMapStoreConfiguration config) {
		List<VersionedMapStore<K, V>> result = new ArrayList<>(amount);
		if (config.isSharedNodeCacheInStoreGroups()) {
			Map<Node<K, V>, ImmutableNode<K, V>> nodeCache;
			if (config.isSharedNodeCacheInStore()) {
				nodeCache = new HashMap<>();
			} else {
				nodeCache = null;
			}
			for (int i = 0; i < amount; i++) {
				result.add(new VersionedMapStoreImpl<>(hashProvider, defaultValue, nodeCache, config));
			}
		} else {
			for (int i = 0; i < amount; i++) {
				result.add(new VersionedMapStoreImpl<>(hashProvider, defaultValue, config));
			}
		}
		return result;
	}

	public static <K, V> List<VersionedMapStore<K, V>> createSharedVersionedMapStores(int amount,
			ContinousHashProvider<K> hashProvider, V defaultValue) {
		return createSharedVersionedMapStores(amount, hashProvider, defaultValue, new VersionedMapStoreConfiguration());
	}

	@Override
	public synchronized Set<Long> getStates() {
		return new HashSet<>(states.keySet());
	}

	@Override
	public VersionedMap<K, V> createMap() {
		return new VersionedMapImpl<>(this, hashProvider, defaultValue);
	}

	@Override
	public VersionedMap<K, V> createMap(long state) {
		ImmutableNode<K, V> data = revert(state);
		return new VersionedMapImpl<>(this, hashProvider, defaultValue, data);
	}

	public synchronized ImmutableNode<K, V> revert(long state) {
		if (states.containsKey(state)) {
			return states.get(state);
		} else {
			ArrayList<Long> existingKeys = new ArrayList<>(states.keySet());
			Collections.sort(existingKeys);
			throw new IllegalArgumentException("Store does not contain state " + state + "! Available states: "
					+ Arrays.toString(existingKeys.toArray()));
		}
	}

	public synchronized long commit(Node<K, V> data, VersionedMapImpl<K, V> mapToUpdateRoot) {
		ImmutableNode<K, V> immutable;
		if (data != null) {
			immutable = data.toImmutable(this.nodeCache);
		} else {
			immutable = null;
		}

		if (nextID == Long.MAX_VALUE)
			throw new IllegalStateException("Map store run out of Id-s");
		long id = nextID++;
		this.states.put(id, immutable);
		if (this.immutableWhenCommitting) {
			mapToUpdateRoot.setRoot(immutable);
		}
		return id;
	}

	@Override
	public DiffCursor<K, V> getDiffCursor(long fromState, long toState) {
		VersionedMapImpl<K, V> map1 = (VersionedMapImpl<K, V>) createMap(fromState);
		VersionedMapImpl<K, V> map2 = (VersionedMapImpl<K, V>) createMap(toState);
		InOrderMapCursor<K, V> cursor1 = new InOrderMapCursor<>(map1);
		InOrderMapCursor<K, V> cursor2 = new InOrderMapCursor<>(map2);
		return new MapDiffCursor<>(this.defaultValue, cursor1, cursor2);
	}
}