package org.warp.jcwdb; import it.unimi.dsi.fastutil.longs.LongArrayList; import java.io.IOException; import java.nio.file.Path; public class JCWDatabase implements AutoCloseable, Cleanable { public final static long MAX_LOADED_INDICES = 10000; private final TypesManager typesManager; private final MixedIndexDatabase indices; private final Cleaner databaseCleaner; private final EntryReferenceTools entryReferenceTools = new EntryReferenceTools(); private volatile boolean closed; private final Object closeLock = new Object(); private final Object indicesAccessLock = new Object(); public JCWDatabase(Path dataFile, Path metadataFile) throws IOException { this.typesManager = new TypesManager(this); this.indices = new MixedIndexDatabase(dataFile, metadataFile); Runtime.getRuntime().addShutdownHook(new Thread(() -> { try { JCWDatabase.this.close(); } catch (Exception e) { e.printStackTrace(); } })); this.databaseCleaner = new Cleaner(this); this.databaseCleaner.start(); } public EntryReference> getRoot() throws IOException { return getRoot(Object.class).cast(); } public EntryReference> getRoot(Class clazz) throws IOException { checkClosed(); if (exists(0)) { return get(0); } else { LightList newRoot = new LightList(this, new LongArrayList()); return set(0, newRoot); } } public EntryReference get(long index) throws IOException { checkClosed(); int type; long hash; synchronized (indicesAccessLock) { type = this.indices.getType(index); hash = this.indices.getHash(index); } DBTypeParser typeParser = this.typesManager.get(type); return new EntryReference<>(entryReferenceTools, index, hash, typeParser); } protected EntryReference add(T value) throws IOException { checkClosed(); DBTypeParser typeParser = this.typesManager.get((Class) value.getClass()); long index; long hash; synchronized (indicesAccessLock) { index = indices.add(typeParser.getWriter(value)); hash = indices.getHash(index); } return new EntryReference<>(entryReferenceTools, index, hash, typeParser, value); } protected boolean exists(long index) { checkClosed(); synchronized (indicesAccessLock) { return this.indices.has(index); } } protected EntryReference set(long index, T value) throws IOException { checkClosed(); EntryReference ref; if (exists(index)) { ref = get(index); ref.setValue(value); return ref; } else { @SuppressWarnings("unchecked") DBTypeParser typeParser = this.typesManager.get((Class) value.getClass()); long hash; synchronized (indicesAccessLock) { IndexDetails returnedDetails = indices.set(index, typeParser.getWriter(value)); hash = returnedDetails.getHash(); } return new EntryReference<>(entryReferenceTools, index, hash, typeParser); } } public void registerType(Class clazz, short type, DBTypeParser parser) { final int addition = 0xEFFF8000; int extendedType = addition | (type & 0x7FFF); typesManager.registerType(clazz, extendedType, parser); } public void registerClass(Class clazz, int type) { typesManager.registerGenericClass(clazz, type); } public boolean isOpen() { return !closed; } @Override public void close() throws IOException { if (closed) { return; } synchronized (closeLock) { if (closed) { return; } closed = true; } this.databaseCleaner.stop(); synchronized (indicesAccessLock) { this.indices.close(); } System.out.println("Database closed."); } private void checkClosed() { if (closed) { throw new RuntimeException("Index Manager is closed."); } } @Override public long clean() { long removedItems = indices.clean(); return removedItems; } public class EntryReferenceTools { private EntryReferenceTools() { } public T read(long index, DBReader reader) throws IOException { return indices.get(index, reader); } public IndexDetails write(long index, DBDataOutput writer) throws IOException { return indices.set(index, writer); } } @SuppressWarnings("unchecked") protected long calculateHash(T o) { return ((DBTypeParser) typesManager.get(o.getClass())).calculateHash(o); } }