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