strangedb/src/main/java/org/warp/jcwdb/EntryReference.java

267 lines
5.4 KiB
Java
Raw Normal View History

2018-11-19 15:16:12 +01:00
package org.warp.jcwdb;
2018-11-20 18:39:48 +01:00
import java.io.IOException;
2018-12-11 23:00:51 +01:00
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
2018-12-07 00:26:38 +01:00
import java.util.function.Function;
2018-11-20 18:39:48 +01:00
2018-11-21 01:02:25 +01:00
/**
* You must have only a maximum of 1 reference for each index
* @param <T>
*/
2019-01-09 19:05:41 +01:00
public class EntryReference<T> implements Editable<T>, Saveable, Castable {
2018-12-05 02:39:41 +01:00
private final JCWDatabase.EntryReferenceTools db;
2018-11-21 01:02:25 +01:00
private final long entryIndex;
2018-11-20 18:39:48 +01:00
private final DBTypeParser<T> parser;
2018-12-07 00:26:38 +01:00
private T value;
2018-12-11 23:00:51 +01:00
private long cachedHash;
private volatile boolean isHashCached;
private volatile boolean loaded;
2018-11-21 01:02:25 +01:00
private volatile boolean closed;
2019-01-06 00:31:52 +01:00
private volatile boolean isFlushingAllowed;
2018-12-11 23:00:51 +01:00
private final Object hashCacheLock = new Object();
private final Object accessLock = new Object();
2018-11-21 01:02:25 +01:00
private final Object closeLock = new Object();
2018-11-19 15:16:12 +01:00
2018-12-11 23:00:51 +01:00
public EntryReference(JCWDatabase.EntryReferenceTools db, long entryId, long hash, DBTypeParser<T> parser) {
this.loaded = false;
this.isHashCached = false;
2018-11-19 15:16:12 +01:00
this.db = db;
2018-11-21 01:02:25 +01:00
this.entryIndex = entryId;
2018-11-19 15:16:12 +01:00
this.parser = parser;
2018-12-11 23:00:51 +01:00
this.value = null;
2018-11-21 01:02:25 +01:00
}
2018-12-11 23:00:51 +01:00
public EntryReference(JCWDatabase.EntryReferenceTools db, long entryId, long hash, DBTypeParser<T> parser, T value) {
this.loaded = true;
this.isHashCached = true;
2018-11-22 23:31:41 +01:00
this.db = db;
this.entryIndex = entryId;
this.parser = parser;
2018-12-11 23:00:51 +01:00
this.cachedHash = hash;
2018-11-22 23:31:41 +01:00
this.value = value;
}
2018-11-21 01:02:25 +01:00
public DBTypeParser<T> getParser() {
return parser;
}
public long getIndex() {
return entryIndex;
2018-11-19 15:16:12 +01:00
}
2018-12-04 23:57:49 +01:00
public long calculateHash() {
2018-12-11 23:00:51 +01:00
synchronized(accessLock) {
load();
synchronized(hashCacheLock) {
if (isHashCached) {
return cachedHash;
}
}
return parser.calculateHash(this.value);
}
2018-12-04 23:57:49 +01:00
}
2018-11-19 15:16:12 +01:00
2019-01-09 19:05:41 +01:00
public boolean isClosed() {
return closed;
}
2018-12-04 23:57:49 +01:00
/**
* Note that this method won't be called when closing without saving
2018-12-11 23:00:51 +01:00
*/
public void save() {
2019-01-06 00:31:52 +01:00
this.save(false);
}
2019-01-09 19:05:41 +01:00
public void saveAndFlush() {
this.save(true);
}
private void save(boolean flush) {
2018-12-11 23:00:51 +01:00
synchronized(accessLock) {
if (loaded && !closed) {
try {
2019-01-09 19:05:41 +01:00
if (value instanceof Saveable) {
if (flush) {
((Saveable)value).saveAndFlush();
} else {
((Saveable)value).save();
}
2018-12-21 10:03:30 +01:00
}
2019-01-06 00:31:52 +01:00
IndexDetails returnedDetails = this.db.write(entryIndex, parser.getWriter(value));
2018-12-11 23:00:51 +01:00
synchronized(hashCacheLock) {
this.cachedHash = returnedDetails.getHash();
this.isHashCached = true;
}
2019-01-09 19:05:41 +01:00
if (flush) {
2019-01-06 00:31:52 +01:00
if (!isFlushingAllowed) {
this.db.setFlushingAllowed(entryIndex, true);
this.isFlushingAllowed = true;
}
}
2018-12-11 23:00:51 +01:00
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* Reccomended way to edit the value
* @param editFunction
* @throws IOException
*/
2018-12-20 00:16:16 +01:00
public void editValue(BiFunction<T, Saveable, T> editFunction) {
2018-12-11 23:00:51 +01:00
synchronized(accessLock) {
load();
this.value = editFunction.apply(this.value, this);
2019-01-06 00:31:52 +01:00
this.save(true);
2018-12-11 23:00:51 +01:00
}
}
/**
* Reccomended way to edit the value
* @param editFunction
2018-12-04 23:57:49 +01:00
* @throws IOException
*/
2018-12-20 00:16:16 +01:00
public void editValue(Function<T, T> editFunction) {
synchronized(accessLock) {
load();
this.value = editFunction.apply(this.value);
2019-01-06 00:31:52 +01:00
this.save(true);
2018-12-20 00:16:16 +01:00
}
}
/**
* Reccomended way to edit the value
* @param editFunction
* @throws IOException
*/
public void editValue(BiConsumer<T, Saveable> editFunction) {
2018-12-11 23:00:51 +01:00
synchronized(accessLock) {
load();
editFunction.accept(this.value, this);
2019-01-06 00:31:52 +01:00
this.save(true);
2018-11-21 01:02:25 +01:00
}
}
2018-12-20 00:16:16 +01:00
/**
* Reccomended way to edit the value
* @param editFunction
* @throws IOException
*/
public void editValue(Consumer<T> editFunction) {
synchronized(accessLock) {
load();
editFunction.accept(this.value);
2019-01-06 00:31:52 +01:00
this.save(true);
2018-12-20 00:16:16 +01:00
}
}
2019-01-09 19:05:41 +01:00
/**
* Reccomended way to edit the value
* DO NOT EDIT THE VALUE
* @param viewFunction
* @throws IOException
*/
public void viewValue(Consumer<T> viewFunction) {
synchronized(accessLock) {
load();
viewFunction.accept(this.value);
}
}
2018-12-11 23:00:51 +01:00
/**
* Substitute the old value with a new one
* @param val
* @throws IOException
*/
2018-12-20 00:16:16 +01:00
public void setValue(T val) {
2018-12-11 23:00:51 +01:00
synchronized(accessLock) {
this.loaded = true;
this.value = val;
synchronized(hashCacheLock) {
this.isHashCached = false;
}
2019-01-06 00:31:52 +01:00
this.save(true);
2018-12-11 23:00:51 +01:00
}
2018-12-07 00:26:38 +01:00
}
/**
2018-12-11 23:00:51 +01:00
* Use editValue instead. READ ONLY!!
2018-12-07 00:26:38 +01:00
* @return
*/
@Deprecated()
public T getValue() {
2019-01-09 19:05:41 +01:00
return getValueReadOnlyUnsafe();
2018-12-07 00:26:38 +01:00
}
/**
* DO NOT ATTEMPT TO MODIFY THE VALUE RETURNED
* @return
*/
2019-01-09 19:05:41 +01:00
public T getValueReadOnlyUnsafe() {
2018-12-11 23:00:51 +01:00
synchronized(accessLock) {
load();
return this.value;
}
2018-12-07 00:26:38 +01:00
}
2018-12-11 23:00:51 +01:00
private void load() {
synchronized(accessLock) {
if (!loaded) {
try {
2019-01-06 00:31:52 +01:00
if (this.isFlushingAllowed) {
this.db.setFlushingAllowed(entryIndex, false);
this.isFlushingAllowed = false;
}
2018-12-11 23:00:51 +01:00
this.value = db.read(entryIndex, parser.getReader());
this.loaded = true;
} catch (IOException e) {
throw (NullPointerException) new NullPointerException(e.getLocalizedMessage()).initCause(e);
}
}
}
}
@SuppressWarnings("unchecked")
2018-11-21 01:02:25 +01:00
@Override
2018-12-11 23:00:51 +01:00
public <U> U cast() {
return (U) this;
2018-11-21 01:02:25 +01:00
}
2018-12-05 02:39:41 +01:00
protected void close() throws IOException {
2018-11-21 01:02:25 +01:00
if (closed) {
return;
}
synchronized (closeLock) {
if (closed) {
return;
}
2019-01-06 00:31:52 +01:00
save(true);
2018-11-21 01:02:25 +01:00
closed = true;
}
2018-11-19 15:16:12 +01:00
}
2018-12-04 23:57:49 +01:00
public void closeWithoutSaving() {
if (closed) {
return;
}
synchronized (closeLock) {
if (closed) {
return;
}
2018-11-21 01:02:25 +01:00
closed = true;
}
2018-11-19 15:16:12 +01:00
}
2018-12-11 23:00:51 +01:00
public Object getAccessLock() {
return accessLock;
}
2018-11-19 15:16:12 +01:00
}