package org.warp.jcwdb; import java.io.IOException; import java.util.function.BiConsumer; import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.function.Function; /** * You must have only a maximum of 1 reference for each index * @param */ public class EntryReference implements Castable, Saveable { private final JCWDatabase.EntryReferenceTools db; private final long entryIndex; private final DBTypeParser parser; private T value; private long cachedHash; private volatile boolean isHashCached; private volatile boolean loaded; private volatile boolean closed; private final Object hashCacheLock = new Object(); private final Object accessLock = new Object(); private final Object closeLock = new Object(); public EntryReference(JCWDatabase.EntryReferenceTools db, long entryId, long hash, DBTypeParser parser) { this.loaded = false; this.isHashCached = false; this.db = db; this.entryIndex = entryId; this.parser = parser; this.value = null; } public EntryReference(JCWDatabase.EntryReferenceTools db, long entryId, long hash, DBTypeParser parser, T value) { this.loaded = true; this.isHashCached = true; this.db = db; this.entryIndex = entryId; this.parser = parser; this.cachedHash = hash; this.value = value; } public DBTypeParser getParser() { return parser; } public long getIndex() { return entryIndex; } public long calculateHash() { synchronized(accessLock) { load(); synchronized(hashCacheLock) { if (isHashCached) { return cachedHash; } } return parser.calculateHash(this.value); } } /** * Note that this method won't be called when closing without saving */ public void save() { synchronized(accessLock) { if (loaded && !closed) { try { if (value instanceof Saveable) { ((Saveable)value).save(); } IndexDetails returnedDetails = db.write(entryIndex, parser.getWriter(value)); synchronized(hashCacheLock) { this.cachedHash = returnedDetails.getHash(); this.isHashCached = true; } } catch (IOException e) { e.printStackTrace(); } } } } /** * Reccomended way to edit the value * @param editFunction * @throws IOException */ public void editValue(BiFunction editFunction) { synchronized(accessLock) { load(); this.value = editFunction.apply(this.value, this); this.save(); } } /** * Reccomended way to edit the value * @param editFunction * @throws IOException */ public void editValue(Function editFunction) { synchronized(accessLock) { load(); this.value = editFunction.apply(this.value); this.save(); } } /** * Reccomended way to edit the value * @param editFunction * @throws IOException */ public void editValue(BiConsumer editFunction) { synchronized(accessLock) { load(); editFunction.accept(this.value, this); this.save(); } } /** * Reccomended way to edit the value * @param editFunction * @throws IOException */ public void editValue(Consumer editFunction) { synchronized(accessLock) { load(); editFunction.accept(this.value); this.save(); } } /** * Substitute the old value with a new one * @param val * @throws IOException */ public void setValue(T val) { synchronized(accessLock) { this.loaded = true; this.value = val; synchronized(hashCacheLock) { this.isHashCached = false; } this.save(); } } /** * Use editValue instead. READ ONLY!! * @return */ @Deprecated() public T getValue() { return getValueReadOnly(); } /** * DO NOT ATTEMPT TO MODIFY THE VALUE RETURNED * @return */ public T getValueReadOnly() { synchronized(accessLock) { load(); return this.value; } } private void load() { synchronized(accessLock) { if (!loaded) { try { this.value = db.read(entryIndex, parser.getReader()); this.loaded = true; } catch (IOException e) { throw (NullPointerException) new NullPointerException(e.getLocalizedMessage()).initCause(e); } } } } @SuppressWarnings("unchecked") @Override public U cast() { return (U) this; } protected void close() throws IOException { if (closed) { return; } synchronized (closeLock) { if (closed) { return; } save(); closed = true; } } public void closeWithoutSaving() { if (closed) { return; } synchronized (closeLock) { if (closed) { return; } closed = true; } } public Object getAccessLock() { return accessLock; } }