package org.eclipse.viatra.solver.data.map; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; 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 final private boolean immutableWhenCommiting; // Static data protected final ContinousHashProvider hashProvider; protected final VALUE defaultValue; // Dynamic data final protected Map> states; final protected Map, ImmutableNode> nodeCache; protected long nextID; public VersionedMapStoreImpl( ContinousHashProvider hashProvider, VALUE defaultValue, VersionedMapStoreConfiguration config) { this.immutableWhenCommiting = config.immutableWhenCommiting; this.hashProvider = hashProvider; this.defaultValue = defaultValue; states = new HashMap<>(); nextID = 0; if(config.sharedNodeCacheInStore) { nodeCache = new HashMap<>(); } else { nodeCache = null; } } private VersionedMapStoreImpl( ContinousHashProvider hashProvider, VALUE defaultValue, Map, ImmutableNode> nodeCache, VersionedMapStoreConfiguration config) { this.immutableWhenCommiting = config.immutableWhenCommiting; this.hashProvider = hashProvider; this.defaultValue = defaultValue; states = new HashMap<>(); nextID = 0; this.nodeCache = nodeCache; } public VersionedMapStoreImpl(ContinousHashProvider hashProvider, VALUE defaultValue) { this(hashProvider,defaultValue,new VersionedMapStoreConfiguration()); } public static List> createSharedVersionedMapStores( int amount, ContinousHashProvider hashProvider, VALUE defaultValue, VersionedMapStoreConfiguration config) { List> result = new ArrayList<>(amount); if(config.sharedNodeCacheInStoreGroups) { Map, ImmutableNode> nodeCache; if(config.sharedNodeCacheInStore) { 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, VALUE defaultValue) { return createSharedVersionedMapStores(amount,hashProvider,defaultValue,new VersionedMapStoreConfiguration()); } synchronized Set getStates() { return 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); } synchronized public 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: "+existingKeys.toArray().toString()); } } synchronized public long commit(Node data, VersionedMapImpl mapToUpdateRoot) { ImmutableNode immutable; if(data != null) { if(this.nodeCache != null) { immutable = data.toImmutable(this.nodeCache); } else { immutable = data.toImmutable(); } } 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; } // public Map, ImmutableNode> getStore() { // return this.nodeCache; // } // public long addState(ImmutableNode data) { // // states.put(id,data); // return id; // } @Override public DiffCursor getDiffCursor(long fromState, long toState) { VersionedMap map1 = createMap(fromState); VersionedMap map2 = createMap(toState); Cursor cursor1 = map1.getCursor(); Cursor cursor2 = map2.getCursor(); return new MapDiffCursor(this.hashProvider,this.defaultValue,cursor1,cursor2); } }