package org.simantics.db.service; import java.io.File; import java.io.IOException; import java.util.HashMap; import org.simantics.databoard.Bindings; import org.simantics.databoard.Files; import org.simantics.databoard.binding.Binding; import org.simantics.databoard.parser.repository.DataValueRepository; import org.simantics.db.exception.RuntimeDatabaseException; public class ClusterSets { // final private static boolean DEBUG = false; private File writeDirectory; final private String databaseId; final private HashMap clusterSets; // Maps cluster set resource id to last user cluster id in the cluster set. final private HashMap reverseMap; private int refCount; // Reference counter for user of this class. private boolean modified; // True if modified since last save. public void setWriteDirectory(File writeDirectory) { this.writeDirectory = writeDirectory; } private File getFile1(File directory) { return new File(directory, "clusterSets." + this.databaseId + ".dat"); } private File getFile2(File directory) { return new File(directory, "clusterSets." + this.databaseId + ".dat.reverse"); } public ClusterSets(File readDirectory, File writeDirectory, String databaseId) { try { this.databaseId = databaseId; this.writeDirectory = writeDirectory; readDirectory.mkdirs(); File file1 = getFile1(readDirectory); File file2 = getFile2(readDirectory); this.refCount = 1; PersistentData pd, pd2; try { pd = (PersistentData)Files.readFile(file1, PersistentData.BINDING); } catch (IOException e) { // New file pd = new PersistentData(); pd.values = new HashMap(); Files.writeFile(file1, PersistentData.BINDING, pd); } try { pd2 = (PersistentData)Files.readFile(file2, PersistentData.BINDING); } catch (IOException e) { // New file pd2 = new PersistentData(); pd2.values = new HashMap(); Files.writeFile(file2, PersistentData.BINDING, pd2); } this.clusterSets = pd.values; this.reverseMap = pd2.values; this.modified = false; } catch (Exception e) { e.printStackTrace(); throw new RuntimeDatabaseException("Failed to create ClusterSets."); } } public synchronized int inc() { return ++refCount; } public synchronized int dec() { return --refCount; } // Save is thread safe. public void dispose() { try { // We still save changes even if transaction is not finished. // This is because we have no cancel mechanism for cluster (sets). save(); } catch (IOException e) { e.printStackTrace(); throw new RuntimeDatabaseException("Failed to save ClusterSets."); } } // clusterSets is not thread safe. public synchronized boolean containsKey(long resourceId) { return clusterSets.containsKey(resourceId); } public synchronized Long get(Long resourceId) { return clusterSets.get(resourceId); } public synchronized void put(long resourceId, long clusterId) { clusterSets.put(resourceId, clusterId); reverseMap.put(clusterId, resourceId); modified = true; } public synchronized Long getClusterSet(Long clusterId) { return reverseMap.get(clusterId); } public void clear() { for(Long key : clusterSets.keySet()) clusterSets.put(key, -1L); touch(); } public synchronized void touch() { modified = true; } public synchronized void save() throws IOException { if (!modified) return; writeDirectory.mkdirs(); File file1 = getFile1(writeDirectory); File file2 = getFile2(writeDirectory); PersistentData pd = new PersistentData(); pd.values = clusterSets; Files.writeFile(file1, PersistentData.BINDING, pd); pd = new PersistentData(); pd.values = reverseMap; Files.writeFile(file2, PersistentData.BINDING, pd); modified = false; } static public class PersistentData { final public static Binding BINDING = Bindings.getBindingUnchecked(PersistentData.class); // public TreeMap values; public HashMap values; public static void main(String[] args) throws Exception { System.err.println("" + BINDING.type().toSingleLineString()); PersistentData pd = new PersistentData(); // pd.values = new TreeMap(); pd.values = new HashMap(); for (long i=0; i<10; ++i) pd.values.put(i, i); BINDING.printValue(pd, System.err, new DataValueRepository(), true); } } }