package org.eclipse.viatra.solver.data.map; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.viatra.solver.data.map.internal.ImmutableNode; import org.eclipse.viatra.solver.data.map.internal.MapDiffCursor; import org.eclipse.viatra.solver.data.map.internal.Node; import org.eclipse.viatra.solver.data.map.internal.VersionedMapImpl; public class VersionedMapStoreImpl implements VersionedMapStore { // Configuration private final boolean immutableWhenCommiting; // Static data protected final ContinousHashProvider hashProvider; protected final V defaultValue; // Dynamic data protected final Map> states = new HashMap<>(); protected final Map, ImmutableNode> nodeCache; protected long nextID = 0; public VersionedMapStoreImpl(ContinousHashProvider hashProvider, V defaultValue, VersionedMapStoreConfiguration config) { this.immutableWhenCommiting = config.isImmutableWhenCommiting(); this.hashProvider = hashProvider; this.defaultValue = defaultValue; if (config.isSharedNodeCacheInStore()) { nodeCache = new HashMap<>(); } else { nodeCache = null; } } private VersionedMapStoreImpl(ContinousHashProvider hashProvider, V defaultValue, Map, ImmutableNode> nodeCache, VersionedMapStoreConfiguration config) { this.immutableWhenCommiting = config.isImmutableWhenCommiting(); this.hashProvider = hashProvider; this.defaultValue = defaultValue; this.nodeCache = nodeCache; } public VersionedMapStoreImpl(ContinousHashProvider hashProvider, V defaultValue) { this(hashProvider, defaultValue, new VersionedMapStoreConfiguration()); } public static List> createSharedVersionedMapStores(int amount, ContinousHashProvider hashProvider, V defaultValue, VersionedMapStoreConfiguration config) { List> result = new ArrayList<>(amount); if (config.isSharedNodeCacheInStoreGroups()) { Map, ImmutableNode> 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 List> createSharedVersionedMapStores(int amount, ContinousHashProvider hashProvider, V defaultValue) { return createSharedVersionedMapStores(amount, hashProvider, defaultValue, new VersionedMapStoreConfiguration()); } @Override public synchronized Set getStates() { return new HashSet<>(states.keySet()); } @Override public VersionedMap createMap() { return new VersionedMapImpl<>(this, hashProvider, defaultValue); } @Override public VersionedMap createMap(long state) { ImmutableNode data = revert(state); return new VersionedMapImpl<>(this, hashProvider, defaultValue, data); } public synchronized ImmutableNode revert(long state) { if (states.containsKey(state)) { return states.get(state); } else { ArrayList existingKeys = new ArrayList<>(states.keySet()); Collections.sort(existingKeys); throw new IllegalArgumentException("Store does not contain state " + state + "! Avaliable states: " + Arrays.toString(existingKeys.toArray())); } } public synchronized long commit(Node data, VersionedMapImpl mapToUpdateRoot) { ImmutableNode 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.immutableWhenCommiting) { mapToUpdateRoot.setRoot(immutable); } return id; } @Override public DiffCursor getDiffCursor(long fromState, long toState) { VersionedMap map1 = createMap(fromState); VersionedMap map2 = createMap(toState); Cursor cursor1 = map1.getAll(); Cursor cursor2 = map2.getAll(); return new MapDiffCursor<>(this.hashProvider, this.defaultValue, cursor1, cursor2); } }