--- /dev/null
+/*******************************************************************************\r
+ * Copyright (c) 2010- Association for Decentralized Information Management in\r
+ * Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ * \r
+ * Contributors:\r
+ * VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.databoard.accessor.file;\r
+\r
+import java.io.File;\r
+import java.io.IOException;\r
+import java.lang.ref.ReferenceQueue;\r
+import java.lang.ref.WeakReference;\r
+import java.util.HashMap;\r
+import java.util.Map;\r
+\r
+import org.simantics.databoard.Bindings;\r
+import org.simantics.databoard.Datatypes;\r
+import org.simantics.databoard.accessor.binary.BinaryObject;\r
+import org.simantics.databoard.accessor.error.AccessorConstructionException;\r
+import org.simantics.databoard.accessor.error.AccessorException;\r
+import org.simantics.databoard.accessor.impl.AccessorParams;\r
+import org.simantics.databoard.binding.Binding;\r
+import org.simantics.databoard.binding.error.BindingConstructionException;\r
+import org.simantics.databoard.binding.error.BindingException;\r
+import org.simantics.databoard.util.binary.BinaryFile;\r
+\r
+/**\r
+ * File library handles sharing of file accessors and automatic closing of files.\r
+ * <p>\r
+ * On call of getFile or createFile the file library opens a file and returns a \r
+ * file accessor. In concecutive runs the same accessor instance is returned, \r
+ * unless the file was garbage collected.\r
+ *\r
+ * The shared accessor instances are not concurrent-use-safe.\r
+ * \r
+ * <p>\r
+ * The file handles are actually closed by two ways:\r
+ * a) File accessor are garbage collected and the the user calls {@link FileLibrary#expunge()}\r
+ * b) The user invokes {@link FileLibrary#close()} in the FileLibrary.\r
+ * \r
+ * Files are flushed before closed.\r
+ *\r
+ * @author Toni Kalajainen <toni.kalajainen@vtt.fi>\r
+ */\r
+public class FileLibrary {\r
+\r
+ /** Files */\r
+ Map<File, Entry> files = new HashMap<File, Entry>(); \r
+ \r
+ /** Queue */ \r
+ ReferenceQueue<FileVariantAccessor> queue = new ReferenceQueue<FileVariantAccessor>();\r
+ \r
+ /** Acessor params */\r
+ AccessorParams params;\r
+ \r
+ /**\r
+ * Create new file library\r
+ */\r
+ public FileLibrary() {\r
+ params = AccessorParams.DEFAULT;\r
+ }\r
+\r
+ /**\r
+ * Create new file library\r
+ */\r
+ public FileLibrary(AccessorParams params) {\r
+ this.params = params;\r
+ }\r
+\r
+ /**\r
+ * Get existing open file accessor.\r
+ * \r
+ * @param file\r
+ * @return file or null\r
+ * @throws IOException \r
+ * @throws AccessorConstructionException \r
+ */\r
+ public FileVariantAccessor getExistingFile(File file) throws AccessorConstructionException {\r
+ file = file.getAbsoluteFile();\r
+ Entry ref = files.get(file);\r
+ FileVariantAccessor accessor = ref==null ? null : ref.get();\r
+ if (ref!=null && !ref.file.isOpen()) {\r
+ files.remove(file);\r
+ return null;\r
+ }\r
+ expunge();\r
+ return accessor;\r
+ }\r
+ \r
+ /**\r
+ * Open file or get an existing file accessor.\r
+ * The caller must not close the file, it is closed upon garbage collection\r
+ * or when FileLibrary is closed. \r
+ * \r
+ * @param file\r
+ * @return an accessor to the contents of a file\r
+ * @throws IOException \r
+ * @throws AccessorConstructionException \r
+ */\r
+ public FileVariantAccessor getFile(File file) throws AccessorConstructionException {\r
+ file = file.getAbsoluteFile();\r
+ Entry ref = files.get(file);\r
+ FileVariantAccessor accessor = ref==null ? null : ref.get();\r
+ expunge();\r
+ if (ref!=null && !ref.file.isOpen()) {\r
+ files.remove(file);\r
+ ref = null;\r
+ }\r
+ \r
+ \r
+ // Open file\r
+ if (accessor == null) {\r
+ BinaryFile bf = ref!=null ? ref.file : null;\r
+ if (bf==null) {\r
+ try {\r
+ bf = new BinaryFile(file);\r
+ } catch (IOException e1) {\r
+ throw new AccessorConstructionException(e1);\r
+ }\r
+ }\r
+ accessor = (FileVariantAccessor) BinaryObject.createAccessor(bf, Datatypes.VARIANT, params);\r
+ Entry e = new Entry(bf, accessor); \r
+ files.put(file, e);\r
+ }\r
+ return accessor;\r
+ }\r
+ \r
+ /**\r
+ * Create a new file and put it in the library.\r
+ * If the file exists it is overwritten. \r
+ *\r
+ * @param file\r
+ * @return accessor to contents of a file\r
+ * @throws AccessorConstructionException \r
+ */\r
+ public FileVariantAccessor createFile(File file) throws AccessorConstructionException {\r
+ file = file.getAbsoluteFile();\r
+ Entry ref = files.get(file);\r
+ FileVariantAccessor accessor = ref==null ? null : ref.get();\r
+ expunge();\r
+ if (ref!=null && !ref.file.isOpen()) {\r
+ files.remove(file);\r
+ ref = null;\r
+ }\r
+ \r
+ // Create a new file\r
+ if (accessor == null) {\r
+ BinaryFile bf = ref!=null ? ref.file : null;\r
+ if (bf==null) {\r
+ try {\r
+ file.createNewFile();\r
+ bf = new BinaryFile(file);\r
+ } catch (IOException e1) {\r
+ throw new AccessorConstructionException(e1);\r
+ }\r
+ }\r
+ accessor = (FileVariantAccessor) BinaryObject.createAccessor(bf, Datatypes.VARIANT, params);\r
+ Binding vb;\r
+ try {\r
+ vb = Bindings.getBinding(void.class);\r
+ Object vv = vb.createDefault();\r
+ accessor.setContentValue(vb, vv);\r
+ } catch (BindingConstructionException e1) {\r
+ throw new AccessorConstructionException(e1);\r
+ } catch (AccessorException e) {\r
+ throw new AccessorConstructionException(e);\r
+ } catch (BindingException e) {\r
+ throw new AccessorConstructionException(e);\r
+ }\r
+ \r
+ Entry e = new Entry(bf, accessor); \r
+ files.put(file, e); \r
+ }\r
+\r
+ return accessor;\r
+ }\r
+ \r
+ public boolean deleteFile(File file) throws AccessorException { \r
+ file = file.getAbsoluteFile();\r
+ expunge();\r
+ Entry ref = files.remove(file);\r
+ if (ref!=null) {\r
+ FileVariantAccessor accessor = ref.get();\r
+ if (accessor!=null) {\r
+ accessor.close();\r
+ accessor = null;\r
+ } else {\r
+ try {\r
+ ref.file.close();\r
+ } catch (IOException e) {\r
+ throw new AccessorException(e);\r
+ }\r
+ }\r
+ }\r
+ expunge();\r
+ \r
+ if (!file.exists()) {\r
+ return true;\r
+ }\r
+ boolean ok = file.delete();\r
+ return ok;\r
+ }\r
+ \r
+\r
+ /**\r
+ * Close unused file accessors. \r
+ */\r
+ public void expunge() {\r
+ Entry e;\r
+ while ( (e = (Entry) queue.poll()) != null) {\r
+// System.out.println("expunging "+e.file.file());\r
+ files.remove(e.file.file());\r
+ try {\r
+ e.file.close();\r
+ } catch (IOException e1) {\r
+ e1.printStackTrace();\r
+ }\r
+ } \r
+ }\r
+ \r
+ /**\r
+ * Close and free all files, this invalidates all existing FileAccessors.\r
+ * close() doesn't invalidate FileLibrary.\r
+ * \r
+ */\r
+ public void close() {\r
+ for (Entry e : files.values()) {\r
+ try {\r
+// System.out.println("closing "+e.file.file());\r
+ e.file.flush();\r
+ e.file.close();\r
+ } catch (IOException e1) {\r
+ e1.printStackTrace();\r
+ }\r
+ }\r
+ files.clear();\r
+ }\r
+\r
+ class Entry extends WeakReference<FileVariantAccessor> {\r
+ BinaryFile file; \r
+ public Entry(BinaryFile file, FileVariantAccessor accessor) {\r
+ super(accessor, FileLibrary.this.queue);\r
+ this.file = file;\r
+ }\r
+ }\r
+ \r
+}\r
+\r
+\r