Compare commits
17 Commits
Author | SHA1 | Date | |
---|---|---|---|
17ca5a2fc2 | |||
6497d2cff9 | |||
8617a1848b | |||
74e4260558 | |||
6d531ee1c4 | |||
9c3cf7b023 | |||
b34af24e33 | |||
debe1f70bb | |||
78bcb67c67 | |||
641233af3e | |||
14702d759c | |||
e0a7c2f6c0 | |||
a1624bf91e | |||
dea0eea5b7 | |||
4c07228e54 | |||
d75c00cc78 | |||
ca03ef4791 |
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[submodule "strangedb-core"]
|
||||||
|
path = strangedb-core
|
||||||
|
url = ssh://git@git.ignuranza.net:5003/andreacavalli/strangedb-core.git
|
19
pom.xml
19
pom.xml
@ -6,9 +6,9 @@
|
|||||||
|
|
||||||
<groupId>it.cavallium</groupId>
|
<groupId>it.cavallium</groupId>
|
||||||
<artifactId>strangedb</artifactId>
|
<artifactId>strangedb</artifactId>
|
||||||
<version>1.5.4</version>
|
<version>1.5.7</version>
|
||||||
|
|
||||||
<name>strangedb</name>
|
<name>strangedb-java</name>
|
||||||
<url>https://git.ignuranza.net/andreacavalli/strangedb</url>
|
<url>https://git.ignuranza.net/andreacavalli/strangedb</url>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
@ -37,15 +37,22 @@
|
|||||||
<artifactId>fastutil</artifactId>
|
<artifactId>fastutil</artifactId>
|
||||||
<version>8.2.2</version>
|
<version>8.2.2</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!--
|
||||||
|
<dependency>
|
||||||
|
<groupId>de.ruedigermoeller</groupId>
|
||||||
|
<artifactId>fst</artifactId>
|
||||||
|
<version>2.56</version>
|
||||||
|
</dependency>
|
||||||
|
-->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.esotericsoftware</groupId>
|
<groupId>com.esotericsoftware</groupId>
|
||||||
<artifactId>kryo</artifactId>
|
<artifactId>kryo</artifactId>
|
||||||
<version>5.0.0-RC1</version>
|
<version>5.0.0-RC4</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>net.openhft</groupId>
|
<groupId>it.cavallium</groupId>
|
||||||
<artifactId>zero-allocation-hashing</artifactId>
|
<artifactId>strangedb-core</artifactId>
|
||||||
<version>0.8</version>
|
<version>1.5.7</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
|
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
|
@ -1,44 +0,0 @@
|
|||||||
package it.cavallium.strangedb;
|
|
||||||
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.StringJoiner;
|
|
||||||
|
|
||||||
public class BlockInfo {
|
|
||||||
private final long index;
|
|
||||||
private final int size;
|
|
||||||
|
|
||||||
public BlockInfo(long index, int size) {
|
|
||||||
this.index = index;
|
|
||||||
this.size = size;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getIndex() {
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getSize() {
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (this == o) return true;
|
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
|
||||||
BlockInfo blockInfo = (BlockInfo) o;
|
|
||||||
return index == blockInfo.index &&
|
|
||||||
size == blockInfo.size;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return Objects.hash(index, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return new StringJoiner(", ", BlockInfo.class.getSimpleName() + "[", "]")
|
|
||||||
.add("index=" + index)
|
|
||||||
.add("size=" + size)
|
|
||||||
.toString();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
package it.cavallium.strangedb;
|
|
||||||
|
|
||||||
public class VariableWrapper<T> {
|
|
||||||
|
|
||||||
public T var;
|
|
||||||
|
|
||||||
public VariableWrapper(T value) {
|
|
||||||
this.var = value;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,140 +0,0 @@
|
|||||||
package it.cavallium.strangedb.database;
|
|
||||||
|
|
||||||
import it.cavallium.strangedb.functionalinterfaces.FunctionWithIO;
|
|
||||||
import it.cavallium.strangedb.EnhancedObject;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.nio.file.StandardCopyOption;
|
|
||||||
|
|
||||||
public class Database implements IDatabase, IDatabaseTools {
|
|
||||||
|
|
||||||
private final IDatabaseTools databaseTools;
|
|
||||||
private final DatabaseFileIO fileIO;
|
|
||||||
private final DatabaseBlocksIO blocksIO;
|
|
||||||
private final DatabaseBlocksMetadata blocksMetadata;
|
|
||||||
private final DatabaseReferencesIO referencesIO;
|
|
||||||
private final DatabaseReferencesMetadata referencesMetadata;
|
|
||||||
private final DatabaseObjectsIO objectsIO;
|
|
||||||
private final Path dataFile;
|
|
||||||
private final Path blocksMetaFile;
|
|
||||||
private final Path referencesMetaFile;
|
|
||||||
private EnhancedObject loadedRootObject;
|
|
||||||
private volatile boolean closed;
|
|
||||||
|
|
||||||
public Database(Path dataFile, Path blocksMetaFile, Path referencesMetaFile) throws IOException {
|
|
||||||
if (Files.notExists(dataFile)) {
|
|
||||||
Files.createFile(dataFile);
|
|
||||||
}
|
|
||||||
if (Files.notExists(blocksMetaFile)) {
|
|
||||||
Files.createFile(blocksMetaFile);
|
|
||||||
}
|
|
||||||
if (Files.notExists(referencesMetaFile)) {
|
|
||||||
Files.createFile(referencesMetaFile);
|
|
||||||
}
|
|
||||||
this.dataFile = dataFile;
|
|
||||||
this.blocksMetaFile = blocksMetaFile;
|
|
||||||
this.referencesMetaFile = referencesMetaFile;
|
|
||||||
this.databaseTools = this;
|
|
||||||
this.fileIO = new DatabaseFileIO(dataFile);
|
|
||||||
this.blocksMetadata = new DatabaseBlocksMetadata(blocksMetaFile);
|
|
||||||
this.blocksIO = new DatabaseBlocksIO(fileIO, blocksMetadata);
|
|
||||||
this.referencesMetadata = new DatabaseReferencesMetadata(referencesMetaFile);
|
|
||||||
this.referencesIO = new DatabaseReferencesIO(blocksIO, referencesMetadata);
|
|
||||||
this.objectsIO = new DatabaseObjectsIO(databaseTools, referencesIO);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public synchronized void close() throws IOException {
|
|
||||||
if (this.closed) {
|
|
||||||
throw new IOException("The database has been already closed!");
|
|
||||||
}
|
|
||||||
this.objectsIO.setEnhancedObject(0, loadedRootObject);
|
|
||||||
this.referencesMetadata.close();
|
|
||||||
this.blocksMetadata.close();
|
|
||||||
this.fileIO.close();
|
|
||||||
this.closed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isClosed() {
|
|
||||||
return closed;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void closeAndClean() throws IOException {
|
|
||||||
if (!this.closed) {
|
|
||||||
this.close();
|
|
||||||
}
|
|
||||||
Path newDataFile = dataFile.resolveSibling("compressed-data-file.tmp");
|
|
||||||
Path newBlocksFile = blocksMetaFile.resolveSibling("compressed-blocks-file.tmp");
|
|
||||||
Path newReferencesFile = referencesMetaFile.resolveSibling("compressed-references-file.tmp");
|
|
||||||
Path backupDataFile = dataFile.resolveSibling("backup-data.db.bak");
|
|
||||||
Path backupBlocksFile = blocksMetaFile.resolveSibling("backup-blocks.dat.bak");
|
|
||||||
Path backupReferencesFile = referencesMetaFile.resolveSibling("backup-references.dat.bak");
|
|
||||||
Files.copy(dataFile, backupDataFile, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES);
|
|
||||||
Files.copy(blocksMetaFile, backupBlocksFile, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES);
|
|
||||||
Files.copy(referencesMetaFile, backupReferencesFile, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES);
|
|
||||||
Files.move(dataFile, newDataFile, StandardCopyOption.REPLACE_EXISTING);
|
|
||||||
Files.move(blocksMetaFile, newBlocksFile, StandardCopyOption.REPLACE_EXISTING);
|
|
||||||
Files.move(referencesMetaFile, newReferencesFile, StandardCopyOption.REPLACE_EXISTING);
|
|
||||||
Database databaseToClean = new Database(newDataFile, newBlocksFile, newReferencesFile);
|
|
||||||
Database newDatabase = new Database(dataFile, blocksMetaFile, referencesMetaFile);
|
|
||||||
|
|
||||||
long referencesCount = databaseToClean.referencesMetadata.getFirstFreeReference();
|
|
||||||
long blocksCount = databaseToClean.blocksMetadata.getTotalBlocksCount();
|
|
||||||
long writtenReferences = 0;
|
|
||||||
|
|
||||||
for (int referenceID = 0; referenceID < referencesCount; referenceID++) {
|
|
||||||
try {
|
|
||||||
ByteBuffer buffer = databaseToClean.referencesIO.readFromReference(referenceID);
|
|
||||||
newDatabase.referencesIO.writeToReference(referenceID, buffer.limit(), buffer);
|
|
||||||
writtenReferences++;
|
|
||||||
} catch (IOException ex) {
|
|
||||||
System.out.println("Error while reading reference " + referenceID + ". References written: " + writtenReferences);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
System.out.println("References written: " + writtenReferences + ". Removed " + (blocksCount - writtenReferences) + " blocks. Removed " + (referencesCount - writtenReferences) + " references.");
|
|
||||||
databaseToClean.close();
|
|
||||||
newDatabase.close();
|
|
||||||
Files.deleteIfExists(newDataFile);
|
|
||||||
Files.deleteIfExists(newBlocksFile);
|
|
||||||
Files.deleteIfExists(newReferencesFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T extends EnhancedObject> T loadRoot(Class<T> type, FunctionWithIO<IDatabaseTools, T> ifAbsent) throws IOException {
|
|
||||||
if (loadedRootObject != null) {
|
|
||||||
throw new RuntimeException("Root already set!");
|
|
||||||
}
|
|
||||||
T root;
|
|
||||||
if (referencesMetadata.getFirstFreeReference() > 0) {
|
|
||||||
root = objectsIO.loadEnhancedObject(0, type);
|
|
||||||
} else {
|
|
||||||
if (objectsIO.newNullObject() != 0) {
|
|
||||||
throw new IOException("Can't allocate root!");
|
|
||||||
} else {
|
|
||||||
root = ifAbsent.apply(Database.this);
|
|
||||||
objectsIO.setEnhancedObject(0, root);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
loadedRootObject = root;
|
|
||||||
return root;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void registerClass(Class<?> type, int id) {
|
|
||||||
this.objectsIO.registerClass(type, id);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void initializeEnhancedObject(EnhancedObject enhancedObject) throws IOException {
|
|
||||||
this.objectsIO.getDataInitializer().initializeDbObject(enhancedObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IObjectsIO getObjectsIO() {
|
|
||||||
return objectsIO;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,42 +0,0 @@
|
|||||||
package it.cavallium.strangedb.database;
|
|
||||||
|
|
||||||
import it.cavallium.strangedb.BlockInfo;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
|
|
||||||
import static it.cavallium.strangedb.database.IBlocksMetadata.EMPTY_BLOCK_ID;
|
|
||||||
|
|
||||||
public class DatabaseBlocksIO implements IBlocksIO {
|
|
||||||
|
|
||||||
private final DatabaseFileIO fileIO;
|
|
||||||
private final IBlocksMetadata blocksMetadata;
|
|
||||||
|
|
||||||
public DatabaseBlocksIO(DatabaseFileIO fileIO, IBlocksMetadata blocksMetadata) {
|
|
||||||
this.fileIO = fileIO;
|
|
||||||
this.blocksMetadata = blocksMetadata;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long newBlock(int size, ByteBuffer data) throws IOException {
|
|
||||||
if (size == 0) {
|
|
||||||
return EMPTY_BLOCK_ID;
|
|
||||||
}
|
|
||||||
long index = fileIO.writeAtEnd(size, data);
|
|
||||||
return blocksMetadata.newBlock(index, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ByteBuffer readBlock(long blockId) throws IOException {
|
|
||||||
if (blockId == EMPTY_BLOCK_ID) {
|
|
||||||
return ByteBuffer.wrap(new byte[0]);
|
|
||||||
}
|
|
||||||
BlockInfo blockInfo = blocksMetadata.getBlockInfo(blockId);
|
|
||||||
return fileIO.readAt(blockInfo.getIndex(), blockInfo.getSize());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,82 +0,0 @@
|
|||||||
package it.cavallium.strangedb.database;
|
|
||||||
|
|
||||||
|
|
||||||
import it.cavallium.strangedb.BlockInfo;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.channels.AsynchronousFileChannel;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.nio.file.StandardOpenOption;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
import java.util.concurrent.Future;
|
|
||||||
|
|
||||||
public class DatabaseBlocksMetadata implements IBlocksMetadata {
|
|
||||||
public static final BlockInfo ERROR_BLOCK_INFO = new BlockInfo(-2, 0);
|
|
||||||
|
|
||||||
private final AsynchronousFileChannel metaFileChannel;
|
|
||||||
private final int BLOCK_META_BYTES_COUNT = Long.BYTES + Integer.BYTES;
|
|
||||||
private final DatabaseBlocksMetadataCache cache;
|
|
||||||
private long firstFreeBlock;
|
|
||||||
|
|
||||||
public DatabaseBlocksMetadata(Path metaFile) throws IOException {
|
|
||||||
metaFileChannel = AsynchronousFileChannel.open(metaFile, StandardOpenOption.READ, StandardOpenOption.WRITE);
|
|
||||||
firstFreeBlock = metaFileChannel.size() / BLOCK_META_BYTES_COUNT;
|
|
||||||
this.cache = new DatabaseBlocksMetadataCache(this::writeBlockToDisk);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BlockInfo getBlockInfo(long blockId) throws IOException {
|
|
||||||
if (blockId == EMPTY_BLOCK_ID) {
|
|
||||||
return EMPTY_BLOCK_INFO;
|
|
||||||
}
|
|
||||||
BlockInfo blockInfo;
|
|
||||||
if ((blockInfo = cache.get(blockId)) != ERROR_BLOCK_INFO) {
|
|
||||||
return blockInfo;
|
|
||||||
}
|
|
||||||
ByteBuffer buffer = ByteBuffer.allocate(BLOCK_META_BYTES_COUNT);
|
|
||||||
try {
|
|
||||||
metaFileChannel.read(buffer, blockId * BLOCK_META_BYTES_COUNT).get();
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
throw new IOException(e);
|
|
||||||
} catch (ExecutionException e) {
|
|
||||||
throw new IOException(e.getCause());
|
|
||||||
}
|
|
||||||
buffer.flip();
|
|
||||||
long index = buffer.getLong();
|
|
||||||
int size = buffer.getInt();
|
|
||||||
blockInfo = new BlockInfo(index, size);
|
|
||||||
cache.put(blockId, blockInfo);
|
|
||||||
return blockInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long newBlock(long index, int size) throws IOException {
|
|
||||||
if (size == 0) {
|
|
||||||
return EMPTY_BLOCK_ID;
|
|
||||||
}
|
|
||||||
long newBlockId = firstFreeBlock++;
|
|
||||||
BlockInfo blockInfo = new BlockInfo(index, size);
|
|
||||||
cache.put(newBlockId, blockInfo);
|
|
||||||
return newBlockId;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() throws IOException {
|
|
||||||
cache.close();
|
|
||||||
metaFileChannel.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
private Future<Integer> writeBlockToDisk(long blockId, long index, int size) {
|
|
||||||
ByteBuffer data = ByteBuffer.allocate(BLOCK_META_BYTES_COUNT);
|
|
||||||
data.putLong(index);
|
|
||||||
data.putInt(size);
|
|
||||||
data.flip();
|
|
||||||
return metaFileChannel.write(data, blockId * BLOCK_META_BYTES_COUNT);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getTotalBlocksCount() {
|
|
||||||
return firstFreeBlock;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,105 +0,0 @@
|
|||||||
package it.cavallium.strangedb.database;
|
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
|
|
||||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectIterator;
|
|
||||||
import it.cavallium.strangedb.BlockInfo;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
import java.util.concurrent.Future;
|
|
||||||
|
|
||||||
public class DatabaseBlocksMetadataCache {
|
|
||||||
|
|
||||||
private static final int GOOD_CACHE_SIZE = 70000;
|
|
||||||
private static final int MAX_CACHE_SIZE = 100000;
|
|
||||||
|
|
||||||
private final Long2ObjectMap<BlockInfo> blocks2Info = new Long2ObjectLinkedOpenHashMap<>(MAX_CACHE_SIZE);
|
|
||||||
private final Object readAccessLock = new Object();
|
|
||||||
private final Object writeAccessLock = new Object();
|
|
||||||
private final DatabaseBlocksMetadataCacheFlusher flusher;
|
|
||||||
private volatile boolean closed;
|
|
||||||
|
|
||||||
public DatabaseBlocksMetadataCache(DatabaseBlocksMetadataCacheFlusher flusher) {
|
|
||||||
this.flusher = flusher;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BlockInfo get(long block) throws IOException {
|
|
||||||
if (closed) throw new IOException("Cache already closed!");
|
|
||||||
synchronized (readAccessLock) {
|
|
||||||
return blocks2Info.getOrDefault(block, DatabaseBlocksMetadata.ERROR_BLOCK_INFO);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void put(long block, BlockInfo blockInfo) throws IOException {
|
|
||||||
if (closed) return;
|
|
||||||
synchronized (readAccessLock) {
|
|
||||||
synchronized (writeAccessLock) {
|
|
||||||
blocks2Info.put(block, blockInfo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void flush() throws IOException {
|
|
||||||
if (closed) return;
|
|
||||||
int blocks2InfoSize = blocks2Info.size();
|
|
||||||
if (blocks2InfoSize > MAX_CACHE_SIZE) {
|
|
||||||
synchronized (writeAccessLock) {
|
|
||||||
ObjectIterator<Long2ObjectMap.Entry<BlockInfo>> entriesIterator = blocks2Info.long2ObjectEntrySet().iterator();
|
|
||||||
List<Future<?>> entriesToFlush = new LinkedList<>();
|
|
||||||
while (blocks2InfoSize > GOOD_CACHE_SIZE) {
|
|
||||||
Long2ObjectMap.Entry<BlockInfo> entry = entriesIterator.next();
|
|
||||||
BlockInfo blockInfo = entry.getValue();
|
|
||||||
entriesToFlush.add(flusher.flush(entry.getLongKey(), blockInfo.getIndex(), blockInfo.getSize()));
|
|
||||||
entriesIterator.remove();
|
|
||||||
if (entriesToFlush.size() >= 1000) {
|
|
||||||
executeAsyncFlush(entriesToFlush);
|
|
||||||
}
|
|
||||||
blocks2InfoSize--;
|
|
||||||
}
|
|
||||||
executeAsyncFlush(entriesToFlush);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void close() throws IOException {
|
|
||||||
synchronized (readAccessLock) {
|
|
||||||
synchronized (writeAccessLock) {
|
|
||||||
if (!closed) {
|
|
||||||
closed = true;
|
|
||||||
int blocks2InfoSize = blocks2Info.size();
|
|
||||||
ObjectIterator<Long2ObjectMap.Entry<BlockInfo>> entriesIterator = blocks2Info.long2ObjectEntrySet().iterator();
|
|
||||||
List<Future<?>> entriesToFlush = new LinkedList<>();
|
|
||||||
while (blocks2InfoSize > 0) {
|
|
||||||
Long2ObjectMap.Entry<BlockInfo> entry = entriesIterator.next();
|
|
||||||
BlockInfo blockInfo = entry.getValue();
|
|
||||||
entriesToFlush.add(flusher.flush(entry.getLongKey(), blockInfo.getIndex(), blockInfo.getSize()));
|
|
||||||
entriesIterator.remove();
|
|
||||||
if (entriesToFlush.size() >= 1000) {
|
|
||||||
executeAsyncFlush(entriesToFlush);
|
|
||||||
}
|
|
||||||
blocks2InfoSize--;
|
|
||||||
}
|
|
||||||
executeAsyncFlush(entriesToFlush);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void executeAsyncFlush(List<Future<?>> entriesToFlush) throws IOException {
|
|
||||||
try {
|
|
||||||
for (Future<?> entryToFlush : entriesToFlush) {
|
|
||||||
entryToFlush.get();
|
|
||||||
}
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
throw new IOException(e);
|
|
||||||
} catch (ExecutionException e) {
|
|
||||||
throw new IOException(e.getCause());
|
|
||||||
} finally {
|
|
||||||
entriesToFlush.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
package it.cavallium.strangedb.database;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.concurrent.Future;
|
|
||||||
|
|
||||||
public interface DatabaseBlocksMetadataCacheFlusher {
|
|
||||||
Future<Integer> flush(long key, long value1, int value2) throws IOException;
|
|
||||||
}
|
|
@ -1,157 +0,0 @@
|
|||||||
package it.cavallium.strangedb.database;
|
|
||||||
|
|
||||||
import it.cavallium.strangedb.annotations.*;
|
|
||||||
import org.apache.commons.lang3.reflect.FieldUtils;
|
|
||||||
import it.cavallium.strangedb.EnhancedObject;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public class DatabaseDataInitializer implements IDataInitializer {
|
|
||||||
|
|
||||||
private final DatabaseObjectsIO objectsIO;
|
|
||||||
|
|
||||||
public DatabaseDataInitializer(DatabaseObjectsIO objectsIO) {
|
|
||||||
this.objectsIO = objectsIO;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void initializeDbObject(EnhancedObject obj) throws IOException {
|
|
||||||
initializeDbObjectFields(obj);
|
|
||||||
initializeDbObjectPrimitiveFields(obj);
|
|
||||||
initializeDbObjectProperties(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initializeDbObjectFields(EnhancedObject obj) throws IOException {
|
|
||||||
// Declare the variables needed to get the biggest field Id
|
|
||||||
Field[] unorderedFields = objectsIO.getFields(obj);
|
|
||||||
// Find the biggest field Id
|
|
||||||
int biggestFieldId = objectsIO.getBiggestFieldId(unorderedFields);
|
|
||||||
|
|
||||||
// Allocate new UIDs
|
|
||||||
long[] fieldUIDs = objectsIO.allocateNewUIDs(biggestFieldId + 1);
|
|
||||||
|
|
||||||
// Declare the other variables
|
|
||||||
Field[] fields = new Field[biggestFieldId + 1];
|
|
||||||
DbDataType[] orderedFieldTypes = new DbDataType[biggestFieldId + 1];
|
|
||||||
|
|
||||||
// Load all fields metadata and load them
|
|
||||||
for (Field field : unorderedFields) {
|
|
||||||
DbField fieldAnnotation = field.getAnnotation(DbField.class);
|
|
||||||
int fieldId = fieldAnnotation.id();
|
|
||||||
DbDataType fieldType = fieldAnnotation.type();
|
|
||||||
objectsIO.loadField(obj, field, fieldType, fieldUIDs[fieldId]);
|
|
||||||
fields[fieldId] = field;
|
|
||||||
orderedFieldTypes[fieldId] = fieldType;
|
|
||||||
}
|
|
||||||
// Set fields metadata
|
|
||||||
obj.setFields(fields, orderedFieldTypes, fieldUIDs);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initializeDbObjectPrimitiveFields(EnhancedObject obj) throws IOException {
|
|
||||||
// Declare the variables needed to get the biggest field Id
|
|
||||||
Field[] unorderedFields = objectsIO.getPrimitiveFields(obj);
|
|
||||||
// Find the biggest field Id
|
|
||||||
int biggestFieldId = objectsIO.getBiggestPrimitiveFieldId(unorderedFields);
|
|
||||||
|
|
||||||
// Allocate new UID
|
|
||||||
long fieldDataUID = objectsIO.newNullObject();
|
|
||||||
|
|
||||||
// Declare the other variables
|
|
||||||
Field[] fields = new Field[biggestFieldId + 1];
|
|
||||||
DbPrimitiveType[] orderedFieldTypes = new DbPrimitiveType[biggestFieldId + 1];
|
|
||||||
|
|
||||||
// Load all fields metadata and load them
|
|
||||||
try {
|
|
||||||
for (Field field : unorderedFields) {
|
|
||||||
DbPrimitiveField fieldAnnotation = field.getAnnotation(DbPrimitiveField.class);
|
|
||||||
int fieldId = fieldAnnotation.id();
|
|
||||||
DbPrimitiveType fieldType = fieldAnnotation.type();
|
|
||||||
switch (fieldType) {
|
|
||||||
case BOOLEAN:
|
|
||||||
FieldUtils.writeField(field, obj, false, true);
|
|
||||||
break;
|
|
||||||
case BYTE:
|
|
||||||
FieldUtils.writeField(field, obj, (byte) 0, true);
|
|
||||||
break;
|
|
||||||
case CHAR:
|
|
||||||
FieldUtils.writeField(field, obj, (char) 0, true);
|
|
||||||
break;
|
|
||||||
case SHORT:
|
|
||||||
FieldUtils.writeField(field, obj, (short) 0, true);
|
|
||||||
break;
|
|
||||||
case INTEGER:
|
|
||||||
FieldUtils.writeField(field, obj, 0, true);
|
|
||||||
break;
|
|
||||||
case LONG:
|
|
||||||
FieldUtils.writeField(field, obj, (long) 0, true);
|
|
||||||
break;
|
|
||||||
case FLOAT:
|
|
||||||
FieldUtils.writeField(field, obj, (float) 0, true);
|
|
||||||
break;
|
|
||||||
case DOUBLE:
|
|
||||||
FieldUtils.writeField(field, obj, (double) 0, true);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
fields[fieldId] = field;
|
|
||||||
orderedFieldTypes[fieldId] = fieldType;
|
|
||||||
}
|
|
||||||
} catch (IllegalArgumentException | IllegalAccessException e) {
|
|
||||||
throw new IOException(e);
|
|
||||||
}
|
|
||||||
// Set fields metadata
|
|
||||||
obj.setPrimitiveFields(fields, orderedFieldTypes, fieldDataUID);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initializeDbObjectProperties(EnhancedObject obj) throws IOException {
|
|
||||||
// Declare the variables needed to get the biggest property Id
|
|
||||||
Method[] unorderedPropertyGetters = obj.getPropertyGetters();
|
|
||||||
Method[] unorderedPropertySetters = obj.getPropertySetters();
|
|
||||||
|
|
||||||
// Find the biggest property Id
|
|
||||||
int biggestGetter = objectsIO.getBiggestPropertyGetterId(unorderedPropertyGetters);
|
|
||||||
int biggestSetter = objectsIO.getBiggestPropertySetterId(unorderedPropertySetters);
|
|
||||||
int biggestPropertyId = biggestGetter > biggestSetter ? biggestGetter : biggestSetter;
|
|
||||||
|
|
||||||
// Allocate new UIDs
|
|
||||||
long[] propertyUIDs = objectsIO.allocateNewUIDs(biggestPropertyId + 1);
|
|
||||||
|
|
||||||
for (Method property : unorderedPropertySetters) {
|
|
||||||
DbPropertySetter fieldAnnotation = property.getAnnotation(DbPropertySetter.class);
|
|
||||||
int propertyId = fieldAnnotation.id();
|
|
||||||
if (propertyId > biggestPropertyId) {
|
|
||||||
biggestPropertyId = propertyId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Declare the other variables
|
|
||||||
DbDataType[] propertyTypes = new DbDataType[biggestPropertyId + 1];
|
|
||||||
Method[] propertyGetters = new Method[biggestPropertyId + 1];
|
|
||||||
Method[] propertySetters = new Method[biggestPropertyId + 1];
|
|
||||||
Map<String, DbPropertySetter> setterMethods = new LinkedHashMap<>();
|
|
||||||
Map<String, DbPropertyGetter> getterMethods = new LinkedHashMap<>();
|
|
||||||
|
|
||||||
// Load the properties metadata
|
|
||||||
for (Method property : unorderedPropertyGetters) {
|
|
||||||
DbPropertyGetter propertyAnnotation = property.getAnnotation(DbPropertyGetter.class);
|
|
||||||
int propertyId = propertyAnnotation.id();
|
|
||||||
DbDataType propertyType = propertyAnnotation.type();
|
|
||||||
propertyTypes[propertyId] = propertyType;
|
|
||||||
propertyGetters[propertyId] = property;
|
|
||||||
getterMethods.put(property.getName(), propertyAnnotation);
|
|
||||||
}
|
|
||||||
for (Method property : unorderedPropertySetters) {
|
|
||||||
DbPropertySetter propertyAnnotation = property.getAnnotation(DbPropertySetter.class);
|
|
||||||
int propertyId = propertyAnnotation.id();
|
|
||||||
DbDataType propertyType = propertyAnnotation.type();
|
|
||||||
propertyTypes[propertyId] = propertyType;
|
|
||||||
propertySetters[propertyId] = property;
|
|
||||||
setterMethods.put(property.getName(), propertyAnnotation);
|
|
||||||
}
|
|
||||||
// Set properties metadata
|
|
||||||
obj.setProperties(propertyGetters, propertySetters, propertyTypes, propertyUIDs, setterMethods, getterMethods);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,61 +0,0 @@
|
|||||||
package it.cavallium.strangedb.database;
|
|
||||||
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.channels.SeekableByteChannel;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.nio.file.StandardOpenOption;
|
|
||||||
|
|
||||||
public class DatabaseFileIO implements IFileIO {
|
|
||||||
|
|
||||||
private final SeekableByteChannel dataFileChannel;
|
|
||||||
private final Object dataAccessLock = new Object();
|
|
||||||
private long firstFreeIndex;
|
|
||||||
|
|
||||||
public DatabaseFileIO(Path dataFile) throws IOException {
|
|
||||||
synchronized (dataAccessLock) {
|
|
||||||
dataFileChannel = Files.newByteChannel(dataFile, StandardOpenOption.READ, StandardOpenOption.WRITE);
|
|
||||||
firstFreeIndex = dataFileChannel.size();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ByteBuffer readAt(long index, int length) throws IOException {
|
|
||||||
ByteBuffer dataBuffer = ByteBuffer.allocate(length);
|
|
||||||
dataFileChannel.position(index).read(dataBuffer);
|
|
||||||
dataBuffer.flip();
|
|
||||||
return dataBuffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeAt(long index, int length, ByteBuffer data) throws IOException {
|
|
||||||
synchronized (dataAccessLock) {
|
|
||||||
if (data.position() != 0) {
|
|
||||||
throw new IOException("You didn't flip the ByteBuffer!");
|
|
||||||
}
|
|
||||||
if (firstFreeIndex < index + length) {
|
|
||||||
firstFreeIndex = index + length;
|
|
||||||
}
|
|
||||||
dataFileChannel.position(index).write(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long writeAtEnd(int length, ByteBuffer data) throws IOException {
|
|
||||||
synchronized (dataAccessLock) {
|
|
||||||
long index = firstFreeIndex;
|
|
||||||
firstFreeIndex += length;
|
|
||||||
writeAt(index, length, data);
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() throws IOException {
|
|
||||||
synchronized (dataAccessLock) {
|
|
||||||
dataFileChannel.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,612 +0,0 @@
|
|||||||
package it.cavallium.strangedb.database;
|
|
||||||
|
|
||||||
import com.esotericsoftware.kryo.Kryo;
|
|
||||||
import com.esotericsoftware.kryo.io.Input;
|
|
||||||
import com.esotericsoftware.kryo.io.Output;
|
|
||||||
import it.cavallium.strangedb.annotations.*;
|
|
||||||
import it.unimi.dsi.fastutil.booleans.BooleanArrayList;
|
|
||||||
import it.unimi.dsi.fastutil.bytes.ByteArrayList;
|
|
||||||
import it.unimi.dsi.fastutil.chars.CharArrayList;
|
|
||||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
|
||||||
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
|
||||||
import it.unimi.dsi.fastutil.longs.LongList;
|
|
||||||
import it.unimi.dsi.fastutil.shorts.ShortArrayList;
|
|
||||||
import org.apache.commons.lang3.reflect.FieldUtils;
|
|
||||||
import it.cavallium.strangedb.EnhancedObject;
|
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
public class DatabaseObjectsIO implements IObjectsIO {
|
|
||||||
|
|
||||||
private final IDatabaseTools databaseTools;
|
|
||||||
private final DatabaseReferencesIO referencesIO;
|
|
||||||
|
|
||||||
private final Object accessLock = new Object();
|
|
||||||
private final DatabaseDataInitializer dataInitializer;
|
|
||||||
|
|
||||||
private Kryo kryo = new Kryo();
|
|
||||||
|
|
||||||
public DatabaseObjectsIO(IDatabaseTools databaseTools, DatabaseReferencesIO referencesIO) {
|
|
||||||
this.databaseTools = databaseTools;
|
|
||||||
this.referencesIO = referencesIO;
|
|
||||||
this.dataInitializer = new DatabaseDataInitializer(this);
|
|
||||||
kryo.setRegistrationRequired(false);
|
|
||||||
int id = -90;
|
|
||||||
registerClass(boolean[].class, id++);
|
|
||||||
registerClass(byte[].class, id++);
|
|
||||||
registerClass(short[].class, id++);
|
|
||||||
registerClass(char[].class, id++);
|
|
||||||
registerClass(int[].class, id++);
|
|
||||||
registerClass(long[].class, id++);
|
|
||||||
registerClass(Boolean[].class, id++);
|
|
||||||
registerClass(Byte[].class, id++);
|
|
||||||
registerClass(Short[].class, id++);
|
|
||||||
registerClass(Character[].class, id++);
|
|
||||||
registerClass(Integer[].class, id++);
|
|
||||||
registerClass(Long[].class, id++);
|
|
||||||
registerClass(String.class, id++);
|
|
||||||
registerClass(String[].class, id++);
|
|
||||||
registerClass(Boolean.class, id++);
|
|
||||||
registerClass(Byte.class, id++);
|
|
||||||
registerClass(Short.class, id++);
|
|
||||||
registerClass(Character.class, id++);
|
|
||||||
registerClass(Integer.class, id++);
|
|
||||||
registerClass(Class.class, id++);
|
|
||||||
registerClass(Object.class, id++);
|
|
||||||
registerClass(Object[].class, id++);
|
|
||||||
registerClass(Long.class, id++);
|
|
||||||
registerClass(String.class, id++);
|
|
||||||
registerClass(String[].class, id++);
|
|
||||||
registerClass(boolean[][].class, id++);
|
|
||||||
registerClass(byte[][].class, id++);
|
|
||||||
registerClass(short[][].class, id++);
|
|
||||||
registerClass(char[][].class, id++);
|
|
||||||
registerClass(int[][].class, id++);
|
|
||||||
registerClass(long[][].class, id++);
|
|
||||||
registerClass(String[][].class, id++);
|
|
||||||
registerClass(List.class, id++);
|
|
||||||
registerClass(ArrayList.class, id++);
|
|
||||||
registerClass(LinkedList.class, id++);
|
|
||||||
registerClass(Set.class, id++);
|
|
||||||
registerClass(HashSet.class, id++);
|
|
||||||
registerClass(LinkedHashSet.class, id++);
|
|
||||||
registerClass(Map.class, id++);
|
|
||||||
registerClass(HashMap.class, id++);
|
|
||||||
registerClass(LinkedHashMap.class, id++);
|
|
||||||
registerClass(TreeMap.class, id++);
|
|
||||||
registerClass(BooleanArrayList.class, id++);
|
|
||||||
registerClass(ByteArrayList.class, id++);
|
|
||||||
registerClass(ShortArrayList.class, id++);
|
|
||||||
registerClass(CharArrayList.class, id++);
|
|
||||||
registerClass(IntArrayList.class, id++);
|
|
||||||
registerClass(LongArrayList.class, id++);
|
|
||||||
registerClass(TreeSet.class, id++);
|
|
||||||
registerClass(SortedSet.class, id++);
|
|
||||||
registerClass(SortedMap.class, id++);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T extends EnhancedObject> T loadEnhancedObject(long reference, Class<T> objectType) throws IOException {
|
|
||||||
synchronized (accessLock) {
|
|
||||||
ByteBuffer buffer = referencesIO.readFromReference(reference);
|
|
||||||
if (buffer.limit() == 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
int serializedVersion = Byte.toUnsignedInt(buffer.get());
|
|
||||||
int fieldsCount = buffer.getInt();
|
|
||||||
int methodsCount = buffer.getInt();
|
|
||||||
long[] fieldRefs = new long[fieldsCount];
|
|
||||||
long[] methodRefs = new long[methodsCount];
|
|
||||||
for (int i = 0; i < fieldsCount; i++) {
|
|
||||||
fieldRefs[i] = buffer.getLong();
|
|
||||||
}
|
|
||||||
for (int i = 0; i < methodsCount; i++) {
|
|
||||||
methodRefs[i] = buffer.getLong();
|
|
||||||
}
|
|
||||||
long nativeFieldsDataReference = buffer.getLong();
|
|
||||||
return preloadEnhancedObject(objectType, serializedVersion, nativeFieldsDataReference, fieldRefs,
|
|
||||||
methodRefs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
Object loadData(DbDataType propertyType, long dataReference, Supplier<Class<?>> returnType) throws IOException {
|
|
||||||
switch (propertyType) {
|
|
||||||
case ENHANCED_OBJECT:
|
|
||||||
return loadEnhancedObject(dataReference, (Class<? extends EnhancedObject>) returnType.get());
|
|
||||||
case OBJECT:
|
|
||||||
return loadObject(dataReference);
|
|
||||||
case REFERENCES_LIST:
|
|
||||||
return loadReferencesList(dataReference);
|
|
||||||
default:
|
|
||||||
throw new NullPointerException("Unknown data type");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
<T> void setData(long reference, DbDataType propertyType, T loadedPropertyValue) throws IOException {
|
|
||||||
switch (propertyType) {
|
|
||||||
case OBJECT:
|
|
||||||
setObject(reference, loadedPropertyValue);
|
|
||||||
break;
|
|
||||||
case REFERENCES_LIST:
|
|
||||||
setReferencesList(reference, (LongArrayList) loadedPropertyValue);
|
|
||||||
break;
|
|
||||||
case ENHANCED_OBJECT:
|
|
||||||
setEnhancedObject(reference, (EnhancedObject) loadedPropertyValue);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
<T extends EnhancedObject> void setPrimitives(T enhancedObject, long reference, DbPrimitiveType[] types, Field[] fields)
|
|
||||||
throws IOException {
|
|
||||||
ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES * fields.length);
|
|
||||||
try {
|
|
||||||
for (int i = 0; i < fields.length; i++) {
|
|
||||||
Field field = fields[i];
|
|
||||||
DbPrimitiveType type = types[i];
|
|
||||||
if (field == null) {
|
|
||||||
buffer.putLong(0);
|
|
||||||
} else {
|
|
||||||
switch (type) {
|
|
||||||
case BOOLEAN:
|
|
||||||
buffer.putLong(field.getBoolean(enhancedObject) ? 1 : 0);
|
|
||||||
break;
|
|
||||||
case BYTE:
|
|
||||||
buffer.putLong(field.getByte(enhancedObject));
|
|
||||||
break;
|
|
||||||
case SHORT:
|
|
||||||
buffer.putLong(field.getShort(enhancedObject));
|
|
||||||
break;
|
|
||||||
case CHAR:
|
|
||||||
buffer.putLong(field.getChar(enhancedObject));
|
|
||||||
break;
|
|
||||||
case INTEGER:
|
|
||||||
buffer.putLong(field.getInt(enhancedObject));
|
|
||||||
break;
|
|
||||||
case LONG:
|
|
||||||
buffer.putLong(field.getLong(enhancedObject));
|
|
||||||
break;
|
|
||||||
case FLOAT:
|
|
||||||
buffer.putLong(Float.floatToRawIntBits(field.getFloat(enhancedObject)));
|
|
||||||
break;
|
|
||||||
case DOUBLE:
|
|
||||||
buffer.putLong(Double.doubleToRawLongBits(field.getDouble(enhancedObject)));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (IllegalArgumentException | IllegalAccessException e) {
|
|
||||||
throw new IOException(e);
|
|
||||||
}
|
|
||||||
buffer.flip();
|
|
||||||
referencesIO.writeToReference(reference, buffer.limit(), buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T extends EnhancedObject> void setEnhancedObject(long reference, T value) throws IOException {
|
|
||||||
synchronized (accessLock) {
|
|
||||||
if (value != null) {
|
|
||||||
EnhancedObjectFullInfo objectFullInfo = value.getAllInfo();
|
|
||||||
if (objectFullInfo == null || objectFullInfo.getFieldReferences() == null
|
|
||||||
|| objectFullInfo.getPropertyReferences() == null) {
|
|
||||||
throw new NullPointerException(
|
|
||||||
"An EnhancedObject has been initialized using the empty constructor!");
|
|
||||||
}
|
|
||||||
|
|
||||||
final long[] fieldReferences = objectFullInfo.getFieldReferences();
|
|
||||||
final DbDataType[] fieldTypes = objectFullInfo.getFieldTypes();
|
|
||||||
final Field[] fields = objectFullInfo.getFields();
|
|
||||||
final long nativeFieldDataReference = objectFullInfo.getPrimitiveFieldDataReference();
|
|
||||||
final DbPrimitiveType[] nativeFieldTypes = objectFullInfo.getPrimitiveFieldTypes();
|
|
||||||
final Field[] nativeFields = objectFullInfo.getPrimitiveFields();
|
|
||||||
final long[] propertyReferences = objectFullInfo.getPropertyReferences();
|
|
||||||
final DbDataType[] propertyTypes = objectFullInfo.getPropertyTypes();
|
|
||||||
final Object[] propertyValues = objectFullInfo.getLoadedPropertyValues();
|
|
||||||
final int totalSize = Byte.BYTES + Integer.BYTES * 2 + fieldReferences.length * Long.BYTES
|
|
||||||
+ propertyReferences.length * Long.BYTES + Long.BYTES;
|
|
||||||
ByteBuffer buffer = ByteBuffer.allocate(totalSize);
|
|
||||||
buffer.put((byte) objectFullInfo.getVersion());
|
|
||||||
buffer.putInt(fieldReferences.length);
|
|
||||||
buffer.putInt(propertyReferences.length);
|
|
||||||
for (int i = 0; i < fieldReferences.length; i++) {
|
|
||||||
buffer.putLong(fieldReferences[i]);
|
|
||||||
}
|
|
||||||
for (int i = 0; i < propertyReferences.length; i++) {
|
|
||||||
buffer.putLong(propertyReferences[i]);
|
|
||||||
}
|
|
||||||
buffer.putLong(nativeFieldDataReference);
|
|
||||||
buffer.flip();
|
|
||||||
|
|
||||||
for (int i = 0; i < fieldReferences.length; i++) {
|
|
||||||
if (fields[i] != null) {
|
|
||||||
try {
|
|
||||||
setData(fieldReferences[i], fieldTypes[i], fields[i].get(value));
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
throw new IOException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (int i = 0; i < propertyReferences.length; i++) {
|
|
||||||
if (propertyValues[i] != null) {
|
|
||||||
setData(propertyReferences[i], propertyTypes[i], propertyValues[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setPrimitives(value, nativeFieldDataReference, nativeFieldTypes, nativeFields);
|
|
||||||
referencesIO.writeToReference(reference, totalSize, buffer);
|
|
||||||
} else {
|
|
||||||
referencesIO.writeToReference(reference, 0, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
@Override
|
|
||||||
public <T> T loadObject(long reference) throws IOException {
|
|
||||||
synchronized (accessLock) {
|
|
||||||
ByteBuffer buffer = referencesIO.readFromReference(reference);
|
|
||||||
if (buffer.limit() == 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
buffer.rewind();
|
|
||||||
byte[] data = buffer.array();
|
|
||||||
return (T) kryo.readClassAndObject(new Input(data));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public LongArrayList loadReferencesList(long reference) throws IOException {
|
|
||||||
synchronized (accessLock) {
|
|
||||||
ByteBuffer buffer = referencesIO.readFromReference(reference);
|
|
||||||
if (buffer.limit() == 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
int itemsCount = buffer.getInt();
|
|
||||||
LongArrayList arrayList = new LongArrayList();
|
|
||||||
for (int i = 0; i < itemsCount; i++) {
|
|
||||||
arrayList.add(buffer.getLong());
|
|
||||||
}
|
|
||||||
return arrayList;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public LongList loadPrimitiveData(long reference) throws IOException {
|
|
||||||
synchronized (accessLock) {
|
|
||||||
ByteBuffer buffer = referencesIO.readFromReference(reference);
|
|
||||||
if (buffer.limit() == 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
int size = buffer.limit() / Long.BYTES;
|
|
||||||
LongArrayList result = new LongArrayList(size);
|
|
||||||
for (int i = 0; i < size; i++) {
|
|
||||||
result.add(buffer.getLong());
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T> void setObject(long reference, T value) throws IOException {
|
|
||||||
synchronized (accessLock) {
|
|
||||||
if (value != null) {
|
|
||||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
|
||||||
Output output = new Output(outputStream);
|
|
||||||
kryo.writeClassAndObject(output, value);
|
|
||||||
output.flush();
|
|
||||||
byte[] data = outputStream.toByteArray();
|
|
||||||
ByteBuffer dataByteBuffer = ByteBuffer.wrap(data);
|
|
||||||
referencesIO.writeToReference(reference, data.length, dataByteBuffer);
|
|
||||||
} else {
|
|
||||||
referencesIO.writeToReference(reference, 0, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setReferencesList(long reference, LongArrayList value) throws IOException {
|
|
||||||
synchronized (accessLock) {
|
|
||||||
if (value != null) {
|
|
||||||
int items = value.size();
|
|
||||||
ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES * items + Integer.BYTES);
|
|
||||||
buffer.putInt(items);
|
|
||||||
for (int i = 0; i < items; i++) {
|
|
||||||
buffer.putLong(value.getLong(i));
|
|
||||||
}
|
|
||||||
buffer.flip();
|
|
||||||
referencesIO.writeToReference(reference, buffer.limit(), buffer);
|
|
||||||
} else {
|
|
||||||
referencesIO.writeToReference(reference, 0, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long newNullObject() throws IOException {
|
|
||||||
synchronized (accessLock) {
|
|
||||||
return referencesIO.allocateReference();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void loadProperty(EnhancedObject obj, int propertyId, Method property, DbDataType propertyType,
|
|
||||||
long propertyUID) throws IOException {
|
|
||||||
synchronized (accessLock) {
|
|
||||||
obj.setProperty(propertyId, loadData(propertyType, propertyUID, property::getReturnType));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void registerClass(Class<?> type, int id) {
|
|
||||||
if (id < -100) {
|
|
||||||
throw new IllegalArgumentException();
|
|
||||||
}
|
|
||||||
kryo.register(type, 100 + id);
|
|
||||||
}
|
|
||||||
|
|
||||||
private <T extends EnhancedObject> void preloadEnhancedObjectProperties(T obj, long[] propertyReferences) {
|
|
||||||
// Declare the variables needed to get the biggest property Id
|
|
||||||
Method[] unorderedPropertyGetters = obj.getPropertyGetters();
|
|
||||||
Method[] unorderedPropertySetters = obj.getPropertySetters();
|
|
||||||
|
|
||||||
// Find the biggest property Id
|
|
||||||
int biggestGetter = getBiggestPropertyGetterId(unorderedPropertyGetters);
|
|
||||||
int biggestSetter = getBiggestPropertySetterId(unorderedPropertySetters);
|
|
||||||
int biggestPropertyId = biggestGetter > biggestSetter ? biggestGetter : biggestSetter;
|
|
||||||
|
|
||||||
for (Method property : unorderedPropertySetters) {
|
|
||||||
DbPropertySetter fieldAnnotation = property.getAnnotation(DbPropertySetter.class);
|
|
||||||
int propertyId = fieldAnnotation.id();
|
|
||||||
if (propertyId > biggestPropertyId) {
|
|
||||||
biggestPropertyId = propertyId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Declare the other variables
|
|
||||||
DbDataType[] propertyTypes = new DbDataType[biggestPropertyId + 1];
|
|
||||||
Method[] propertyGetters = new Method[biggestPropertyId + 1];
|
|
||||||
Method[] propertySetters = new Method[biggestPropertyId + 1];
|
|
||||||
Map<String, DbPropertySetter> setterMethods = new LinkedHashMap<>();
|
|
||||||
Map<String, DbPropertyGetter> getterMethods = new LinkedHashMap<>();
|
|
||||||
|
|
||||||
// Load the properties metadata
|
|
||||||
for (Method property : unorderedPropertyGetters) {
|
|
||||||
DbPropertyGetter propertyAnnotation = property.getAnnotation(DbPropertyGetter.class);
|
|
||||||
int propertyId = propertyAnnotation.id();
|
|
||||||
DbDataType propertyType = propertyAnnotation.type();
|
|
||||||
propertyTypes[propertyId] = propertyType;
|
|
||||||
propertyGetters[propertyId] = property;
|
|
||||||
getterMethods.put(property.getName(), propertyAnnotation);
|
|
||||||
}
|
|
||||||
for (Method property : unorderedPropertySetters) {
|
|
||||||
DbPropertySetter propertyAnnotation = property.getAnnotation(DbPropertySetter.class);
|
|
||||||
int propertyId = propertyAnnotation.id();
|
|
||||||
DbDataType propertyType = propertyAnnotation.type();
|
|
||||||
propertyTypes[propertyId] = propertyType;
|
|
||||||
propertySetters[propertyId] = property;
|
|
||||||
setterMethods.put(property.getName(), propertyAnnotation);
|
|
||||||
}
|
|
||||||
// Set properties metadata
|
|
||||||
obj.setProperties(propertyGetters, propertySetters, propertyTypes, propertyReferences, setterMethods,
|
|
||||||
getterMethods);
|
|
||||||
}
|
|
||||||
|
|
||||||
int getBiggestPropertyGetterId(Method[] unorderedPropertyGetters) {
|
|
||||||
int biggestPropertyId = -1;
|
|
||||||
for (Method property : unorderedPropertyGetters) {
|
|
||||||
DbPropertyGetter fieldAnnotation = property.getAnnotation(DbPropertyGetter.class);
|
|
||||||
int propertyId = fieldAnnotation.id();
|
|
||||||
if (propertyId > biggestPropertyId) {
|
|
||||||
biggestPropertyId = propertyId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return biggestPropertyId;
|
|
||||||
}
|
|
||||||
|
|
||||||
int getBiggestPropertySetterId(Method[] unorderedPropertySetters) {
|
|
||||||
int biggestPropertyId = -1;
|
|
||||||
for (Method property : unorderedPropertySetters) {
|
|
||||||
DbPropertySetter fieldAnnotation = property.getAnnotation(DbPropertySetter.class);
|
|
||||||
int propertyId = fieldAnnotation.id();
|
|
||||||
if (propertyId > biggestPropertyId) {
|
|
||||||
biggestPropertyId = propertyId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return biggestPropertyId;
|
|
||||||
}
|
|
||||||
|
|
||||||
private <T extends EnhancedObject> void preloadEnhancedObjectPrimitiveFields(T obj, long nativeFieldsDataReference)
|
|
||||||
throws IOException {
|
|
||||||
// Declare the variables needed to get the biggest field Id
|
|
||||||
Field[] unorderedFields = getPrimitiveFields(obj);
|
|
||||||
// Find the biggest field Id
|
|
||||||
int biggestFieldId = getBiggestPrimitiveFieldId(unorderedFields);
|
|
||||||
|
|
||||||
// Declare the other variables
|
|
||||||
Field[] fields = new Field[biggestFieldId + 1];
|
|
||||||
DbPrimitiveType[] orderedFieldTypes = new DbPrimitiveType[biggestFieldId + 1];
|
|
||||||
|
|
||||||
// Load all fields metadata
|
|
||||||
for (Field field : unorderedFields) {
|
|
||||||
DbPrimitiveField fieldAnnotation = field.getAnnotation(DbPrimitiveField.class);
|
|
||||||
int fieldId = fieldAnnotation.id();
|
|
||||||
DbPrimitiveType fieldType = fieldAnnotation.type();
|
|
||||||
fields[fieldId] = field;
|
|
||||||
orderedFieldTypes[fieldId] = fieldType;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load fields data
|
|
||||||
ByteBuffer buffer = referencesIO.readFromReference(nativeFieldsDataReference);
|
|
||||||
|
|
||||||
// Load fields
|
|
||||||
try {
|
|
||||||
for (int id = 0; id < fields.length; id++) {
|
|
||||||
Field field = fields[id];
|
|
||||||
DbPrimitiveType type = orderedFieldTypes[id];
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case BOOLEAN:
|
|
||||||
FieldUtils.writeField(field, obj, (boolean) (buffer.getLong() % 2 == 1), true);
|
|
||||||
break;
|
|
||||||
case BYTE:
|
|
||||||
FieldUtils.writeField(field, obj, (byte) buffer.getLong(), true);
|
|
||||||
break;
|
|
||||||
case SHORT:
|
|
||||||
FieldUtils.writeField(field, obj, (short) buffer.getLong(), true);
|
|
||||||
break;
|
|
||||||
case CHAR:
|
|
||||||
FieldUtils.writeField(field, obj, (char) buffer.getLong(), true);
|
|
||||||
break;
|
|
||||||
case INTEGER:
|
|
||||||
FieldUtils.writeField(field, obj, (int) buffer.getLong(), true);
|
|
||||||
break;
|
|
||||||
case LONG:
|
|
||||||
FieldUtils.writeField(field, obj, (long) buffer.getLong(), true);
|
|
||||||
break;
|
|
||||||
case FLOAT:
|
|
||||||
FieldUtils.writeField(field, obj, Float.intBitsToFloat((int) buffer.getLong()), true);
|
|
||||||
break;
|
|
||||||
case DOUBLE:
|
|
||||||
FieldUtils.writeField(field, obj, Double.longBitsToDouble(buffer.getLong()), true);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (IllegalArgumentException | IllegalAccessException e) {
|
|
||||||
throw new IOException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set fields metadata
|
|
||||||
obj.setPrimitiveFields(fields, orderedFieldTypes, nativeFieldsDataReference);
|
|
||||||
}
|
|
||||||
|
|
||||||
private <T extends EnhancedObject> void preloadEnhancedObjectFields(T obj, long[] fieldReferences)
|
|
||||||
throws IOException {
|
|
||||||
// Declare the variables needed to get the biggest field Id
|
|
||||||
Field[] unorderedFields = getFields(obj);
|
|
||||||
// Find the biggest field Id
|
|
||||||
int biggestFieldId = getBiggestFieldId(unorderedFields);
|
|
||||||
|
|
||||||
// Declare the other variables
|
|
||||||
Field[] fields = new Field[biggestFieldId + 1];
|
|
||||||
DbDataType[] orderedFieldTypes = new DbDataType[biggestFieldId + 1];
|
|
||||||
|
|
||||||
// Load all fields metadata and load them
|
|
||||||
for (Field field : unorderedFields) {
|
|
||||||
DbField fieldAnnotation = field.getAnnotation(DbField.class);
|
|
||||||
int fieldId = fieldAnnotation.id();
|
|
||||||
DbDataType fieldType = fieldAnnotation.type();
|
|
||||||
loadField(obj, field, fieldType, fieldReferences[fieldId]);
|
|
||||||
fields[fieldId] = field;
|
|
||||||
orderedFieldTypes[fieldId] = fieldType;
|
|
||||||
}
|
|
||||||
// Set fields metadata
|
|
||||||
obj.setFields(fields, orderedFieldTypes, fieldReferences);
|
|
||||||
}
|
|
||||||
|
|
||||||
<T extends EnhancedObject> void loadField(T obj, Field field, DbDataType fieldType, long fieldReference)
|
|
||||||
throws IOException {
|
|
||||||
Object data = loadData(fieldType, fieldReference, field::getType);
|
|
||||||
try {
|
|
||||||
if (fieldType == DbDataType.OBJECT && data != null) {
|
|
||||||
if (!field.getType().isInstance(data)) {
|
|
||||||
throw new IOException("There is an attempt to load an object of type " + data.getClass()
|
|
||||||
+ " into a field of type " + field.getType());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
FieldUtils.writeField(field, obj, data, true);
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
<T extends EnhancedObject> Field[] getFields(T obj) {
|
|
||||||
return FieldUtils.getFieldsWithAnnotation(obj.getClass(), DbField.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
<T extends EnhancedObject> Field[] getPrimitiveFields(T obj) {
|
|
||||||
return FieldUtils.getFieldsWithAnnotation(obj.getClass(), DbPrimitiveField.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
int getBiggestFieldId(Field[] unorderedFields) {
|
|
||||||
int biggestFieldId = -1;
|
|
||||||
for (Field field : unorderedFields) {
|
|
||||||
DbField fieldAnnotation = field.getAnnotation(DbField.class);
|
|
||||||
int propertyId = fieldAnnotation.id();
|
|
||||||
if (propertyId > biggestFieldId) {
|
|
||||||
biggestFieldId = propertyId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return biggestFieldId;
|
|
||||||
}
|
|
||||||
|
|
||||||
int getBiggestPrimitiveFieldId(Field[] unorderedFields) {
|
|
||||||
int biggestFieldId = -1;
|
|
||||||
for (Field field : unorderedFields) {
|
|
||||||
DbPrimitiveField fieldAnnotation = field.getAnnotation(DbPrimitiveField.class);
|
|
||||||
int propertyId = fieldAnnotation.id();
|
|
||||||
if (propertyId > biggestFieldId) {
|
|
||||||
biggestFieldId = propertyId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return biggestFieldId;
|
|
||||||
}
|
|
||||||
|
|
||||||
private <T extends EnhancedObject> T toInstance(Class<T> type) throws IOException {
|
|
||||||
try {
|
|
||||||
T obj = type.getConstructor().newInstance();
|
|
||||||
obj.setDatabaseTools(databaseTools);
|
|
||||||
return obj;
|
|
||||||
} catch (NoSuchMethodException e) {
|
|
||||||
throw new IOException("You must declare a public empty constructor in class " + type + ": public "
|
|
||||||
+ type.getSimpleName() + "()", e);
|
|
||||||
} catch (IllegalAccessException | InvocationTargetException | InstantiationException e) {
|
|
||||||
throw new IOException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private <T extends EnhancedObject> T preloadEnhancedObject(Class<T> objectType, int serializedVersion,
|
|
||||||
long nativeFieldsRef, long[] fieldRefs, long[] methodRefs) throws IOException {
|
|
||||||
// Instantiate the class to an object
|
|
||||||
T obj = toInstance(objectType);
|
|
||||||
|
|
||||||
// Check the serialized version
|
|
||||||
DbClass dbClass = objectType.getAnnotation(DbClass.class);
|
|
||||||
int classVersion = 0;
|
|
||||||
if (dbClass != null) {
|
|
||||||
classVersion = dbClass.version();
|
|
||||||
}
|
|
||||||
if (classVersion > serializedVersion) {
|
|
||||||
DatabaseEnhancedObjectUpgrader enhancedObjectUpgrader = new DatabaseEnhancedObjectUpgrader(this, fieldRefs, methodRefs, nativeFieldsRef);
|
|
||||||
dataInitializer.initializeDbObject(obj);
|
|
||||||
obj.onUpgrade(serializedVersion, enhancedObjectUpgrader);
|
|
||||||
} else if (classVersion < serializedVersion) {
|
|
||||||
throw new IllegalStateException(
|
|
||||||
"The serialized class is more recent than the current version of that class!");
|
|
||||||
} else {
|
|
||||||
preloadEnhancedObjectPrimitiveFields(obj, nativeFieldsRef);
|
|
||||||
preloadEnhancedObjectFields(obj, fieldRefs);
|
|
||||||
preloadEnhancedObjectProperties(obj, methodRefs);
|
|
||||||
}
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long[] allocateNewUIDs(int quantity) throws IOException {
|
|
||||||
long[] ids = new long[quantity];
|
|
||||||
for (int i = 0; i < quantity; i++) {
|
|
||||||
ids[i] = newNullObject();
|
|
||||||
}
|
|
||||||
return ids;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IDataInitializer getDataInitializer() {
|
|
||||||
return dataInitializer;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,40 +0,0 @@
|
|||||||
package it.cavallium.strangedb.database;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
|
|
||||||
import static it.cavallium.strangedb.database.IBlocksMetadata.EMPTY_BLOCK_ID;
|
|
||||||
|
|
||||||
public class DatabaseReferencesIO implements IReferencesIO {
|
|
||||||
|
|
||||||
private final DatabaseBlocksIO blocksIO;
|
|
||||||
private final DatabaseReferencesMetadata referencesMetadata;
|
|
||||||
|
|
||||||
public DatabaseReferencesIO(DatabaseBlocksIO blocksIO, DatabaseReferencesMetadata referencesMetadata) {
|
|
||||||
this.blocksIO = blocksIO;
|
|
||||||
this.referencesMetadata = referencesMetadata;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long allocateReference() throws IOException {
|
|
||||||
return referencesMetadata.newReference(EMPTY_BLOCK_ID);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long allocateReference(int size, ByteBuffer data) throws IOException {
|
|
||||||
long blockId = (size == 0) ? EMPTY_BLOCK_ID : blocksIO.newBlock(size, data);
|
|
||||||
return referencesMetadata.newReference(blockId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeToReference(long reference, int size, ByteBuffer data) throws IOException {
|
|
||||||
long blockId = (size == 0) ? EMPTY_BLOCK_ID : blocksIO.newBlock(size, data);
|
|
||||||
referencesMetadata.editReference(reference, blockId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ByteBuffer readFromReference(long reference) throws IOException {
|
|
||||||
long blockId = referencesMetadata.getReference(reference);
|
|
||||||
return blocksIO.readBlock(blockId);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,84 +0,0 @@
|
|||||||
package it.cavallium.strangedb.database;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.channels.AsynchronousFileChannel;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.nio.file.StandardOpenOption;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
import java.util.concurrent.Future;
|
|
||||||
|
|
||||||
import static it.cavallium.strangedb.database.IBlocksMetadata.EMPTY_BLOCK_ID;
|
|
||||||
import static it.cavallium.strangedb.database.IBlocksMetadata.ERROR_BLOCK_ID;
|
|
||||||
|
|
||||||
public class DatabaseReferencesMetadata implements IReferencesMetadata {
|
|
||||||
private final AsynchronousFileChannel metaFileChannel;
|
|
||||||
private final int REF_META_BYTES_COUNT = Long.BYTES;
|
|
||||||
private final DatabaseReferencesMetadataCache cache;
|
|
||||||
private long firstFreeReference;
|
|
||||||
|
|
||||||
public DatabaseReferencesMetadata(Path refMetaFile) throws IOException {
|
|
||||||
metaFileChannel = AsynchronousFileChannel.open(refMetaFile, StandardOpenOption.READ, StandardOpenOption.WRITE);
|
|
||||||
firstFreeReference = metaFileChannel.size() / REF_META_BYTES_COUNT;
|
|
||||||
this.cache = new DatabaseReferencesMetadataCache(this::writeReferenceToDisk);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getReference(long reference) throws IOException {
|
|
||||||
if (reference >= firstFreeReference) {
|
|
||||||
return EMPTY_BLOCK_ID;
|
|
||||||
}
|
|
||||||
long block;
|
|
||||||
if ((block = cache.get(reference)) != ERROR_BLOCK_ID) {
|
|
||||||
return block;
|
|
||||||
}
|
|
||||||
ByteBuffer buffer = ByteBuffer.allocate(REF_META_BYTES_COUNT);
|
|
||||||
try {
|
|
||||||
metaFileChannel.read(buffer, reference * REF_META_BYTES_COUNT).get();
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
throw new IOException(e);
|
|
||||||
} catch (ExecutionException e) {
|
|
||||||
throw new IOException(e.getCause());
|
|
||||||
}
|
|
||||||
buffer.flip();
|
|
||||||
block = buffer.getLong();
|
|
||||||
if (buffer.limit() != 0 && block != 0xFFFFFFFFFFFFFFFFL) {
|
|
||||||
cache.put(reference, block);
|
|
||||||
return block;
|
|
||||||
} else {
|
|
||||||
cache.put(reference, EMPTY_BLOCK_ID);
|
|
||||||
return EMPTY_BLOCK_ID;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long newReference(long blockId) throws IOException {
|
|
||||||
long newReference = firstFreeReference++;
|
|
||||||
cache.put(newReference, blockId);
|
|
||||||
return newReference;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void editReference(long reference, long blockId) throws IOException {
|
|
||||||
cache.put(reference, blockId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() throws IOException {
|
|
||||||
cache.close();
|
|
||||||
metaFileChannel.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getFirstFreeReference() {
|
|
||||||
return firstFreeReference;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Future<Integer> writeReferenceToDisk(long reference, long blockId) {
|
|
||||||
ByteBuffer data = ByteBuffer.allocate(REF_META_BYTES_COUNT);
|
|
||||||
data.putLong(blockId);
|
|
||||||
data.flip();
|
|
||||||
return metaFileChannel.write(data, reference * REF_META_BYTES_COUNT);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,111 +0,0 @@
|
|||||||
package it.cavallium.strangedb.database;
|
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.longs.*;
|
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectIterator;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
import java.util.concurrent.Future;
|
|
||||||
|
|
||||||
import static it.cavallium.strangedb.database.IBlocksMetadata.ERROR_BLOCK_ID;
|
|
||||||
|
|
||||||
public class DatabaseReferencesMetadataCache {
|
|
||||||
|
|
||||||
private static final int GOOD_CACHE_SIZE = 70000;
|
|
||||||
private static final int MAX_CACHE_SIZE = 100000;
|
|
||||||
|
|
||||||
private final Long2LongMap references2Blocks = new Long2LongLinkedOpenHashMap(MAX_CACHE_SIZE);
|
|
||||||
private final Object readAccessLock = new Object();
|
|
||||||
private final Object writeAccessLock = new Object();
|
|
||||||
private final DatabaseReferencesMetadataCacheFlusher flusher;
|
|
||||||
private volatile boolean closed;
|
|
||||||
|
|
||||||
public DatabaseReferencesMetadataCache(DatabaseReferencesMetadataCacheFlusher flusher) {
|
|
||||||
this.flusher = flusher;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long get(long reference) throws IOException {
|
|
||||||
synchronized (readAccessLock) {
|
|
||||||
if (closed) throw new IOException("Cache already closed!");
|
|
||||||
return references2Blocks.getOrDefault(reference, ERROR_BLOCK_ID);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void put(long reference, long blockId) throws IOException {
|
|
||||||
synchronized (readAccessLock) {
|
|
||||||
synchronized (writeAccessLock) {
|
|
||||||
if (closed) return;
|
|
||||||
references2Blocks.put(reference, blockId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void flush() throws IOException {
|
|
||||||
synchronized (readAccessLock) {
|
|
||||||
synchronized (writeAccessLock) {
|
|
||||||
if (closed) return;
|
|
||||||
int references2BlocksSize = references2Blocks.size();
|
|
||||||
if (references2BlocksSize > MAX_CACHE_SIZE) {
|
|
||||||
synchronized (writeAccessLock) {
|
|
||||||
ObjectIterator<Long2LongMap.Entry> entriesIterator = references2Blocks.long2LongEntrySet().iterator();
|
|
||||||
List<Future<?>> entriesToFlush = new LinkedList<>();
|
|
||||||
while (references2BlocksSize > GOOD_CACHE_SIZE) {
|
|
||||||
Long2LongMap.Entry entry = entriesIterator.next();
|
|
||||||
entriesToFlush.add(flusher.flush(entry.getLongKey(), entry.getLongValue()));
|
|
||||||
entriesIterator.remove();
|
|
||||||
if (entriesToFlush.size() >= 1000) {
|
|
||||||
executeAsyncFlush(entriesToFlush);
|
|
||||||
}
|
|
||||||
references2BlocksSize--;
|
|
||||||
}
|
|
||||||
executeAsyncFlush(entriesToFlush);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void close() throws IOException {
|
|
||||||
synchronized (readAccessLock) {
|
|
||||||
synchronized (writeAccessLock) {
|
|
||||||
if (!closed) {
|
|
||||||
closed = true;
|
|
||||||
int references2BlocksSize = references2Blocks.size();
|
|
||||||
ObjectIterator<Long2LongMap.Entry> entriesIterator = references2Blocks.long2LongEntrySet().iterator();
|
|
||||||
List<Future<?>> entriesToFlush = new LinkedList<>();
|
|
||||||
while (references2BlocksSize > 0) {
|
|
||||||
Long2LongMap.Entry entry = entriesIterator.next();
|
|
||||||
entriesToFlush.add(flusher.flush(entry.getLongKey(), entry.getLongValue()));
|
|
||||||
entriesIterator.remove();
|
|
||||||
if (entriesToFlush.size() >= 1000) {
|
|
||||||
executeAsyncFlush(entriesToFlush);
|
|
||||||
}
|
|
||||||
references2BlocksSize--;
|
|
||||||
}
|
|
||||||
executeAsyncFlush(entriesToFlush);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void executeAsyncFlush(List<Future<?>> entriesToFlush) throws IOException {
|
|
||||||
synchronized (readAccessLock) {
|
|
||||||
synchronized (writeAccessLock) {
|
|
||||||
try {
|
|
||||||
for (Future<?> entryToFlush : entriesToFlush) {
|
|
||||||
entryToFlush.get();
|
|
||||||
}
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
throw new IOException(e);
|
|
||||||
} catch (ExecutionException e) {
|
|
||||||
throw new IOException(e.getCause());
|
|
||||||
} finally {
|
|
||||||
entriesToFlush.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
package it.cavallium.strangedb.database;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.concurrent.Future;
|
|
||||||
|
|
||||||
public interface DatabaseReferencesMetadataCacheFlusher {
|
|
||||||
Future<Integer> flush(long key, long value) throws IOException;
|
|
||||||
}
|
|
@ -1,26 +0,0 @@
|
|||||||
package it.cavallium.strangedb.database;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
|
|
||||||
public interface IBlocksIO {
|
|
||||||
/**
|
|
||||||
* Allocate a block
|
|
||||||
* @param size block size
|
|
||||||
* @param data block data
|
|
||||||
* @return the block id
|
|
||||||
*/
|
|
||||||
long newBlock(int size, ByteBuffer data) throws IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read a block
|
|
||||||
* @param blockId block id
|
|
||||||
* @return block data
|
|
||||||
*/
|
|
||||||
ByteBuffer readBlock(long blockId) throws IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Close file
|
|
||||||
*/
|
|
||||||
void close();
|
|
||||||
}
|
|
@ -1,53 +0,0 @@
|
|||||||
package it.cavallium.strangedb.database;
|
|
||||||
|
|
||||||
import it.cavallium.strangedb.BlockInfo;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
public interface IBlocksMetadata {
|
|
||||||
long EMPTY_BLOCK_ID = -1;
|
|
||||||
long ERROR_BLOCK_ID = -2;
|
|
||||||
BlockInfo EMPTY_BLOCK_INFO = new BlockInfo(0, 0);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get block info
|
|
||||||
* @param blockId block id
|
|
||||||
* @return block metadata
|
|
||||||
*/
|
|
||||||
BlockInfo getBlockInfo(long blockId) throws IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* New empty block info
|
|
||||||
* @return block id
|
|
||||||
*/
|
|
||||||
default long newBlock() {
|
|
||||||
return EMPTY_BLOCK_ID;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set block info
|
|
||||||
* @param index block index
|
|
||||||
* @param size block size
|
|
||||||
* @return block id
|
|
||||||
*/
|
|
||||||
long newBlock(long index, int size) throws IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set block info
|
|
||||||
* @param blockInfo block info
|
|
||||||
* @return block id
|
|
||||||
*/
|
|
||||||
default long newBlock(BlockInfo blockInfo) throws IOException {
|
|
||||||
return this.newBlock(blockInfo.getIndex(), blockInfo.getSize());
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Close file
|
|
||||||
*/
|
|
||||||
void close() throws IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get total count of blocks
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
long getTotalBlocksCount();
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
package it.cavallium.strangedb.database;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
public interface IDatabase {
|
|
||||||
|
|
||||||
void close() throws IOException;
|
|
||||||
|
|
||||||
boolean isClosed();
|
|
||||||
|
|
||||||
void closeAndClean() throws IOException;
|
|
||||||
}
|
|
@ -1,35 +0,0 @@
|
|||||||
package it.cavallium.strangedb.database;
|
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
|
|
||||||
public interface IFileIO {
|
|
||||||
/**
|
|
||||||
* Read *length* bytes in position *index*
|
|
||||||
* @param index index
|
|
||||||
* @param length length
|
|
||||||
* @return bytes
|
|
||||||
*/
|
|
||||||
ByteBuffer readAt(long index, int length) throws IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write *length* bytes in position *index*
|
|
||||||
* @param index index
|
|
||||||
* @param length length
|
|
||||||
* @param data bytes
|
|
||||||
*/
|
|
||||||
void writeAt(long index, int length, ByteBuffer data) throws IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write *length* bytes in position *index*
|
|
||||||
* @param length length
|
|
||||||
* @param data bytes
|
|
||||||
* @return index
|
|
||||||
*/
|
|
||||||
long writeAtEnd(int length, ByteBuffer data) throws IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Close the file
|
|
||||||
*/
|
|
||||||
void close() throws IOException;
|
|
||||||
}
|
|
@ -1,35 +0,0 @@
|
|||||||
package it.cavallium.strangedb.database;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
|
|
||||||
public interface IReferencesIO {
|
|
||||||
/**
|
|
||||||
* Allocate a new empty reference
|
|
||||||
* @return the new reference
|
|
||||||
*/
|
|
||||||
long allocateReference() throws IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Allocate a new reference with that data
|
|
||||||
* @param size data size
|
|
||||||
* @param data bytes
|
|
||||||
* @return the new reference
|
|
||||||
*/
|
|
||||||
long allocateReference(int size, ByteBuffer data) throws IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write some data to the reference
|
|
||||||
* @param reference reference
|
|
||||||
* @param size data size
|
|
||||||
* @param data bytes
|
|
||||||
*/
|
|
||||||
void writeToReference(long reference, int size, ByteBuffer data) throws IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read data from the reference
|
|
||||||
* @param reference reference
|
|
||||||
* @return bytes
|
|
||||||
*/
|
|
||||||
ByteBuffer readFromReference(long reference) throws IOException;
|
|
||||||
}
|
|
@ -1,33 +0,0 @@
|
|||||||
package it.cavallium.strangedb.database;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
public interface IReferencesMetadata {
|
|
||||||
/**
|
|
||||||
* Get block of reference
|
|
||||||
* @param reference reference
|
|
||||||
* @return block id
|
|
||||||
*/
|
|
||||||
long getReference(long reference) throws IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Allocate a block for a new reference
|
|
||||||
* @param blockId block id
|
|
||||||
* @return reference
|
|
||||||
*/
|
|
||||||
long newReference(long blockId) throws IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Change reference size
|
|
||||||
* @param reference reference
|
|
||||||
* @param blockId block id
|
|
||||||
*/
|
|
||||||
void editReference(long reference, long blockId) throws IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Close file
|
|
||||||
*/
|
|
||||||
void close() throws IOException;
|
|
||||||
|
|
||||||
long getFirstFreeReference();
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
package it.cavallium.strangedb.database;
|
|
||||||
|
|
||||||
public interface Long2LongConsumer {
|
|
||||||
void accept(long a, long b);
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
package it.cavallium.strangedb.functionalinterfaces;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
@FunctionalInterface
|
|
||||||
public interface ConsumerWithIO<T> {
|
|
||||||
|
|
||||||
void accept(T t) throws IOException;
|
|
||||||
|
|
||||||
default ConsumerWithIO<T> andThen(ConsumerWithIO<? super T> after) {
|
|
||||||
Objects.requireNonNull(after);
|
|
||||||
return (T t) -> { accept(t); after.accept(t); };
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,78 +0,0 @@
|
|||||||
package it.cavallium.strangedb.functionalinterfaces;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents a function that accepts one argument and produces a result.
|
|
||||||
*
|
|
||||||
* <p>This is a <a href="package-summary.html">functional interface</a>
|
|
||||||
* whose functional method is {@link #apply(Object)}.
|
|
||||||
*
|
|
||||||
* @param <T> the type of the input to the function
|
|
||||||
* @param <R> the type of the result of the function
|
|
||||||
*
|
|
||||||
* @since 1.8
|
|
||||||
*/
|
|
||||||
@FunctionalInterface
|
|
||||||
public interface FunctionWithIO<T, R> {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Applies this function to the given argument.
|
|
||||||
*
|
|
||||||
* @param t the function argument
|
|
||||||
* @return the function result
|
|
||||||
*/
|
|
||||||
R apply(T t) throws IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a composed function that first applies the {@code before}
|
|
||||||
* function to its input, and then applies this function to the result.
|
|
||||||
* If evaluation of either function throws an exception, it is relayed to
|
|
||||||
* the caller of the composed function.
|
|
||||||
*
|
|
||||||
* @param <V> the type of input to the {@code before} function, and to the
|
|
||||||
* composed function
|
|
||||||
* @param before the function to apply before this function is applied
|
|
||||||
* @return a composed function that first applies the {@code before}
|
|
||||||
* function and then applies this function
|
|
||||||
* @throws NullPointerException if before is null
|
|
||||||
*
|
|
||||||
* @see #andThen(FunctionWithIO)
|
|
||||||
*/
|
|
||||||
default <V> FunctionWithIO<V, R> compose(FunctionWithIO<? super V, ? extends T> before) {
|
|
||||||
Objects.requireNonNull(before);
|
|
||||||
return (V v) -> apply(before.apply(v));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a composed function that first applies this function to
|
|
||||||
* its input, and then applies the {@code after} function to the result.
|
|
||||||
* If evaluation of either function throws an exception, it is relayed to
|
|
||||||
* the caller of the composed function.
|
|
||||||
*
|
|
||||||
* @param <V> the type of output of the {@code after} function, and of the
|
|
||||||
* composed function
|
|
||||||
* @param after the function to apply after this function is applied
|
|
||||||
* @return a composed function that first applies this function and then
|
|
||||||
* applies the {@code after} function
|
|
||||||
* @throws NullPointerException if after is null
|
|
||||||
*
|
|
||||||
* @see #compose(FunctionWithIO)
|
|
||||||
*/
|
|
||||||
default <V> FunctionWithIO<T, V> andThen(FunctionWithIO<? super R, ? extends V> after) {
|
|
||||||
Objects.requireNonNull(after);
|
|
||||||
return (T t) -> after.apply(apply(t));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a function that always returns its input argument.
|
|
||||||
*
|
|
||||||
* @param <T> the type of the input and output objects to the function
|
|
||||||
* @return a function that always returns its input argument
|
|
||||||
*/
|
|
||||||
static <T> FunctionWithIO<T, T> identity() {
|
|
||||||
return t -> t;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
|||||||
package it.cavallium.strangedb.functionalinterfaces;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
@FunctionalInterface
|
|
||||||
public interface RunnableWithIO {
|
|
||||||
/**
|
|
||||||
* When an object implementing interface <code>Runnable</code> is used
|
|
||||||
* to create a thread, starting the thread causes the object's
|
|
||||||
* <code>run</code> method to be called in that separately executing
|
|
||||||
* thread.
|
|
||||||
* <p>
|
|
||||||
* The general contract of the method <code>run</code> is that it may
|
|
||||||
* take any action whatsoever.
|
|
||||||
*
|
|
||||||
* @see java.lang.Thread#run()
|
|
||||||
*/
|
|
||||||
public abstract void run() throws IOException;
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
package it.cavallium.strangedb.functionalinterfaces;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
@FunctionalInterface
|
|
||||||
public interface SupplierWithIO<T> {
|
|
||||||
public T getWithIO() throws IOException;
|
|
||||||
}
|
|
@ -1,4 +1,4 @@
|
|||||||
package it.cavallium.strangedb.annotations;
|
package it.cavallium.strangedb.java.annotations;
|
||||||
|
|
||||||
import java.lang.annotation.ElementType;
|
import java.lang.annotation.ElementType;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
@ -1,4 +1,4 @@
|
|||||||
package it.cavallium.strangedb.annotations;
|
package it.cavallium.strangedb.java.annotations;
|
||||||
|
|
||||||
public enum DbDataType {
|
public enum DbDataType {
|
||||||
ENHANCED_OBJECT,
|
ENHANCED_OBJECT,
|
@ -1,4 +1,4 @@
|
|||||||
package it.cavallium.strangedb.annotations;
|
package it.cavallium.strangedb.java.annotations;
|
||||||
|
|
||||||
import java.lang.annotation.ElementType;
|
import java.lang.annotation.ElementType;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
@ -10,4 +10,5 @@ import java.lang.annotation.Target;
|
|||||||
public @interface DbField {
|
public @interface DbField {
|
||||||
int id();
|
int id();
|
||||||
DbDataType type() default DbDataType.OBJECT;
|
DbDataType type() default DbDataType.OBJECT;
|
||||||
|
String name() default "";
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package it.cavallium.strangedb.annotations;
|
package it.cavallium.strangedb.java.annotations;
|
||||||
|
|
||||||
import java.lang.annotation.ElementType;
|
import java.lang.annotation.ElementType;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
@ -10,4 +10,5 @@ import java.lang.annotation.Target;
|
|||||||
public @interface DbPrimitiveField {
|
public @interface DbPrimitiveField {
|
||||||
int id();
|
int id();
|
||||||
DbPrimitiveType type();
|
DbPrimitiveType type();
|
||||||
|
String name() default "";
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package it.cavallium.strangedb.annotations;
|
package it.cavallium.strangedb.java.annotations;
|
||||||
|
|
||||||
public enum DbPrimitiveType {
|
public enum DbPrimitiveType {
|
||||||
BOOLEAN,
|
BOOLEAN,
|
@ -1,4 +1,4 @@
|
|||||||
package it.cavallium.strangedb.annotations;
|
package it.cavallium.strangedb.java.annotations;
|
||||||
|
|
||||||
import java.lang.annotation.ElementType;
|
import java.lang.annotation.ElementType;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
@ -7,7 +7,8 @@ import java.lang.annotation.Target;
|
|||||||
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Target(ElementType.METHOD)
|
@Target(ElementType.METHOD)
|
||||||
public @interface DbPropertyGetter {
|
public @interface DbProperty {
|
||||||
int id();
|
int id();
|
||||||
DbDataType type() default DbDataType.OBJECT;
|
DbDataType type() default DbDataType.OBJECT;
|
||||||
|
String name() default "";
|
||||||
}
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
package it.cavallium.strangedb.java.annotations;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
public @interface DbPropertyGetter {
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package it.cavallium.strangedb.annotations;
|
package it.cavallium.strangedb.java.annotations;
|
||||||
|
|
||||||
import java.lang.annotation.ElementType;
|
import java.lang.annotation.ElementType;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
@ -8,6 +8,4 @@ import java.lang.annotation.Target;
|
|||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Target(ElementType.METHOD)
|
@Target(ElementType.METHOD)
|
||||||
public @interface DbPropertySetter {
|
public @interface DbPropertySetter {
|
||||||
int id();
|
|
||||||
DbDataType type() default DbDataType.OBJECT;
|
|
||||||
}
|
}
|
@ -1,9 +1,9 @@
|
|||||||
package it.cavallium.strangedb.database;
|
package it.cavallium.strangedb.java.database;
|
||||||
|
|
||||||
import it.cavallium.strangedb.annotations.DbDataType;
|
import it.cavallium.strangedb.java.annotations.DbDataType;
|
||||||
import it.cavallium.strangedb.annotations.DbPrimitiveType;
|
import it.cavallium.strangedb.java.annotations.DbPrimitiveType;
|
||||||
import it.unimi.dsi.fastutil.longs.LongList;
|
import it.unimi.dsi.fastutil.longs.LongList;
|
||||||
import it.cavallium.strangedb.EnhancedObjectUpgrader;
|
import it.cavallium.strangedb.java.objects.EnhancedObjectUpgrader;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
@ -23,14 +23,14 @@ public class DatabaseEnhancedObjectUpgrader implements EnhancedObjectUpgrader {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public Object getField(int id, DbDataType type, Supplier<Class<?>> enhancedClassType) throws IOException {
|
public Object getField(int id, DbDataType type) throws IOException {
|
||||||
return objectsIO.loadData(type, fieldRefs[id], enhancedClassType);
|
return objectsIO.loadData(type, fieldRefs[id]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public Object getMethod(int id, DbDataType type, Supplier<Class<?>> enhancedClassType) throws IOException {
|
public Object getMethod(int id, DbDataType type) throws IOException {
|
||||||
return objectsIO.loadData(type, methodRefs[id], enhancedClassType);
|
return objectsIO.loadData(type, methodRefs[id]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
@ -0,0 +1,191 @@
|
|||||||
|
package it.cavallium.strangedb.java.database;
|
||||||
|
|
||||||
|
import it.cavallium.strangedb.database.DatabaseCore;
|
||||||
|
import it.cavallium.strangedb.database.references.ReferenceInfo;
|
||||||
|
import it.cavallium.strangedb.java.objects.EnhancedObject;
|
||||||
|
import it.cavallium.strangedb.functionalinterfaces.FunctionWithIO;
|
||||||
|
import it.cavallium.strangedb.java.objects.EnhancedObjectIndices;
|
||||||
|
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
||||||
|
import it.unimi.dsi.fastutil.longs.LongLinkedOpenHashSet;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.StandardCopyOption;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import static it.cavallium.strangedb.database.references.ConcurrentDatabaseReferencesMetadata.*;
|
||||||
|
import static it.cavallium.strangedb.java.database.DatabaseObjectsIO.ENHANCED_OBJECT_METADATA_CLEANER;
|
||||||
|
import static it.cavallium.strangedb.java.database.DatabaseObjectsIO.REFERENCES_LIST_CLEANER;
|
||||||
|
|
||||||
|
public class DatabaseJava extends DatabaseCore implements IDatabaseTools {
|
||||||
|
private final IDatabaseTools databaseTools;
|
||||||
|
private final DatabaseObjectsIO objectsIO;
|
||||||
|
private EnhancedObject loadedRootObject;
|
||||||
|
private boolean hasLoadedRootObject;
|
||||||
|
|
||||||
|
public DatabaseJava(Path dataFile, Path referencesMetaFile) throws IOException {
|
||||||
|
super(dataFile, referencesMetaFile);
|
||||||
|
this.databaseTools = this;
|
||||||
|
this.objectsIO = new DatabaseObjectsIO(databaseTools, referencesIO);
|
||||||
|
this.hasLoadedRootObject = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void close() throws IOException {
|
||||||
|
if (this.closed) {
|
||||||
|
throw new IOException("The database has been already closed!");
|
||||||
|
}
|
||||||
|
if (hasLoadedRootObject) {
|
||||||
|
this.objectsIO.setEnhancedObject(0, loadedRootObject);
|
||||||
|
}
|
||||||
|
super.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T extends EnhancedObject> T loadRoot(FunctionWithIO<IDatabaseTools, T> ifAbsent) throws IOException {
|
||||||
|
if (hasLoadedRootObject) {
|
||||||
|
throw new RuntimeException("Root already set!");
|
||||||
|
}
|
||||||
|
T root;
|
||||||
|
if (referencesIO.getFirstFreeReference() > 0) {
|
||||||
|
root = objectsIO.loadEnhancedObject(0);
|
||||||
|
} else {
|
||||||
|
if (objectsIO.newNullObject() != 0) {
|
||||||
|
throw new IOException("Can't allocate root!");
|
||||||
|
} else {
|
||||||
|
root = ifAbsent.apply(DatabaseJava.this);
|
||||||
|
objectsIO.setEnhancedObject(0, root);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
loadedRootObject = root;
|
||||||
|
hasLoadedRootObject = true;
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T extends EnhancedObject> T loadRoot(FunctionWithIO<IDatabaseTools, T> ifAbsent, Class<?> forcedClassType) throws IOException {
|
||||||
|
if (hasLoadedRootObject) {
|
||||||
|
throw new RuntimeException("Root already set!");
|
||||||
|
}
|
||||||
|
T root;
|
||||||
|
if (referencesIO.getFirstFreeReference() > 0) {
|
||||||
|
root = objectsIO.loadEnhancedObject(0, forcedClassType);
|
||||||
|
} else {
|
||||||
|
if (objectsIO.newNullObject() != 0) {
|
||||||
|
throw new IOException("Can't allocate root!");
|
||||||
|
} else {
|
||||||
|
root = ifAbsent.apply(DatabaseJava.this);
|
||||||
|
objectsIO.setEnhancedObject(0, root);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
loadedRootObject = root;
|
||||||
|
hasLoadedRootObject = true;
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void closeAndClean() throws IOException {
|
||||||
|
if (!this.closed) {
|
||||||
|
this.close();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
UUID uid = UUID.randomUUID();
|
||||||
|
Path newDataFile = dataFile.resolveSibling("compressed-data-file-" + uid + ".tmp");
|
||||||
|
Path newReferencesFile = referencesMetaFile.resolveSibling("compressed-references-file-" + uid + ".tmp");
|
||||||
|
Path backupDataFile = dataFile.resolveSibling("backup-data-" + uid + ".db.bak");
|
||||||
|
Path backupReferencesFile = referencesMetaFile.resolveSibling("backup-references-" + uid + ".dat.bak");
|
||||||
|
Files.copy(dataFile, backupDataFile, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES);
|
||||||
|
Files.copy(referencesMetaFile, backupReferencesFile, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES);
|
||||||
|
Files.move(dataFile, newDataFile, StandardCopyOption.REPLACE_EXISTING);
|
||||||
|
Files.move(referencesMetaFile, newReferencesFile, StandardCopyOption.REPLACE_EXISTING);
|
||||||
|
DatabaseJava databaseToClean = instantiateNewDatabase(newDataFile, newReferencesFile);
|
||||||
|
DatabaseJava newDatabase = instantiateNewDatabase(dataFile, referencesMetaFile);
|
||||||
|
|
||||||
|
long firstFreeReference = databaseToClean.referencesIO.getFirstFreeReference();
|
||||||
|
long referencesCount = 0;
|
||||||
|
long writtenReferences = 0;
|
||||||
|
|
||||||
|
LongLinkedOpenHashSet idsToKeep = new LongLinkedOpenHashSet((int) (firstFreeReference / 2), 0.75f);
|
||||||
|
cleanRef(databaseToClean, idsToKeep, 0);
|
||||||
|
|
||||||
|
for (int referenceID = 0; referenceID < firstFreeReference; referenceID++) {
|
||||||
|
try {
|
||||||
|
ReferenceInfo ref = databaseToClean.referencesIO.getReferenceInfo(referenceID);
|
||||||
|
if (!NONEXISTENT_REFERENCE_INFO.equals(ref)) {
|
||||||
|
referencesCount++;
|
||||||
|
if (idsToKeep.contains(referenceID)) {
|
||||||
|
ByteBuffer buffer = databaseToClean.referencesIO.readFromReference(referenceID);
|
||||||
|
newDatabase.referencesIO.writeToReference(referenceID, ref.getCleanerId(), buffer.limit(), buffer);
|
||||||
|
writtenReferences++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException ex) {
|
||||||
|
System.out.println("Error while reading reference " + referenceID + ". References written: " + writtenReferences);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
System.out.println("[Java Cleaner] References written: " + writtenReferences + ". Removed " + (referencesCount - writtenReferences) + " references.");
|
||||||
|
databaseToClean.close();
|
||||||
|
newDatabase.close();
|
||||||
|
Files.deleteIfExists(newDataFile);
|
||||||
|
Files.deleteIfExists(newReferencesFile);
|
||||||
|
**/
|
||||||
|
}
|
||||||
|
|
||||||
|
private void cleanRef(DatabaseJava db, LongLinkedOpenHashSet idsToKeep, long ref) throws IOException {
|
||||||
|
idsToKeep.add(ref);
|
||||||
|
ReferenceInfo refInfo = db.referencesIO.getReferenceInfo(ref);
|
||||||
|
if (!NONEXISTENT_REFERENCE_INFO.equals(refInfo)) {
|
||||||
|
switch (refInfo.getCleanerId()) {
|
||||||
|
case ENHANCED_OBJECT_METADATA_CLEANER: {
|
||||||
|
EnhancedObjectIndices enhancedObjectUids = db.objectsIO.loadEnhancedObjectUids(ref);
|
||||||
|
for (long fieldUid : enhancedObjectUids.fieldUids) {
|
||||||
|
cleanRef(db, idsToKeep, fieldUid);
|
||||||
|
}
|
||||||
|
for (long propUid : enhancedObjectUids.propertyUids) {
|
||||||
|
cleanRef(db, idsToKeep, propUid);
|
||||||
|
}
|
||||||
|
cleanRef(db, idsToKeep, enhancedObjectUids.nativeDataUid);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ERRORED_CLEANER: {
|
||||||
|
System.err.println("Errored cleaner found! Skipping...");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case REFERENCES_LIST_CLEANER: {
|
||||||
|
LongArrayList refList = db.objectsIO.loadReferencesList(ref);
|
||||||
|
for (long elemRef : refList) {
|
||||||
|
cleanRef(db, idsToKeep, elemRef);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BLANK_DATA_CLEANER: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
System.err.println("Unrecognized cleaner found! Skipping...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerClass(Class<?> type, int id) {
|
||||||
|
this.objectsIO.registerClass(type, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected DatabaseJava instantiateNewDatabase(Path dataFile, Path referencesMetaFile) throws IOException {
|
||||||
|
DatabaseJava newDatabaseJava = new DatabaseJava(dataFile, referencesMetaFile);
|
||||||
|
this.getObjectsIO().getRegisteredClasses().forEach(newDatabaseJava::registerClass);
|
||||||
|
return newDatabaseJava;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initializeEnhancedObject(EnhancedObject enhancedObject) throws IOException {
|
||||||
|
this.objectsIO.getDataInitializer().initializeDbObject(enhancedObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DatabaseObjectsIO getObjectsIO() {
|
||||||
|
return objectsIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,931 @@
|
|||||||
|
package it.cavallium.strangedb.java.database;
|
||||||
|
|
||||||
|
import it.cavallium.strangedb.database.references.ConcurrentDatabaseReferencesIO;
|
||||||
|
import it.cavallium.strangedb.java.annotations.*;
|
||||||
|
import it.cavallium.strangedb.java.objects.EnhancedObject;
|
||||||
|
import it.cavallium.strangedb.java.objects.EnhancedObjectFullInfo;
|
||||||
|
import it.cavallium.strangedb.java.objects.EnhancedObjectIndices;
|
||||||
|
import it.cavallium.strangedb.java.objects.lists.EnhancedObjectStrangeDbList;
|
||||||
|
import it.cavallium.strangedb.java.objects.lists.ObjectStrangeDbList;
|
||||||
|
import it.cavallium.strangedb.java.objects.lists.StrangeDbList;
|
||||||
|
import it.unimi.dsi.fastutil.booleans.BooleanArrayList;
|
||||||
|
import it.unimi.dsi.fastutil.bytes.ByteArrayList;
|
||||||
|
import it.unimi.dsi.fastutil.chars.CharArrayList;
|
||||||
|
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||||
|
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
||||||
|
import it.unimi.dsi.fastutil.longs.LongList;
|
||||||
|
import it.unimi.dsi.fastutil.shorts.ShortArrayList;
|
||||||
|
import org.apache.commons.lang3.reflect.FieldUtils;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import static it.cavallium.strangedb.database.references.ConcurrentDatabaseReferencesMetadata.BLANK_DATA_CLEANER;
|
||||||
|
|
||||||
|
public class DatabaseObjectsIO implements IObjectsIO {
|
||||||
|
|
||||||
|
public static final byte ENHANCED_OBJECT_METADATA_CLEANER = 1;
|
||||||
|
public static final byte REFERENCES_LIST_CLEANER = 2;
|
||||||
|
|
||||||
|
private final IDatabaseTools databaseTools;
|
||||||
|
private final ConcurrentDatabaseReferencesIO referencesIO;
|
||||||
|
|
||||||
|
private final DatabaseDataInitializer dataInitializer;
|
||||||
|
private final KryoSerializer serializer;
|
||||||
|
|
||||||
|
public DatabaseObjectsIO(IDatabaseTools databaseTools, ConcurrentDatabaseReferencesIO referencesIO) {
|
||||||
|
this.databaseTools = databaseTools;
|
||||||
|
this.referencesIO = referencesIO;
|
||||||
|
this.serializer = new KryoSerializer();
|
||||||
|
this.dataInitializer = new DatabaseDataInitializer();
|
||||||
|
int id = -90;
|
||||||
|
registerClass(boolean[].class, id++);
|
||||||
|
registerClass(byte[].class, id++);
|
||||||
|
registerClass(short[].class, id++);
|
||||||
|
registerClass(char[].class, id++);
|
||||||
|
registerClass(int[].class, id++);
|
||||||
|
registerClass(long[].class, id++);
|
||||||
|
registerClass(Boolean[].class, id++);
|
||||||
|
registerClass(Byte[].class, id++);
|
||||||
|
registerClass(Short[].class, id++);
|
||||||
|
registerClass(Character[].class, id++);
|
||||||
|
registerClass(Integer[].class, id++);
|
||||||
|
registerClass(Long[].class, id++);
|
||||||
|
registerClass(String.class, id++);
|
||||||
|
registerClass(String[].class, id++);
|
||||||
|
registerClass(Boolean.class, id++);
|
||||||
|
registerClass(Byte.class, id++);
|
||||||
|
registerClass(Short.class, id++);
|
||||||
|
registerClass(Character.class, id++);
|
||||||
|
registerClass(Integer.class, id++);
|
||||||
|
registerClass(Class.class, id++);
|
||||||
|
registerClass(Object.class, id++);
|
||||||
|
registerClass(Object[].class, id++);
|
||||||
|
registerClass(Long.class, id++);
|
||||||
|
registerClass(String.class, id++);
|
||||||
|
registerClass(String[].class, id++);
|
||||||
|
registerClass(boolean[][].class, id++);
|
||||||
|
registerClass(byte[][].class, id++);
|
||||||
|
registerClass(short[][].class, id++);
|
||||||
|
registerClass(char[][].class, id++);
|
||||||
|
registerClass(int[][].class, id++);
|
||||||
|
registerClass(long[][].class, id++);
|
||||||
|
registerClass(String[][].class, id++);
|
||||||
|
registerClass(List.class, id++);
|
||||||
|
registerClass(ArrayList.class, id++);
|
||||||
|
registerClass(LinkedList.class, id++);
|
||||||
|
registerClass(Set.class, id++);
|
||||||
|
registerClass(HashSet.class, id++);
|
||||||
|
registerClass(LinkedHashSet.class, id++);
|
||||||
|
registerClass(Map.class, id++);
|
||||||
|
registerClass(HashMap.class, id++);
|
||||||
|
registerClass(LinkedHashMap.class, id++);
|
||||||
|
registerClass(TreeMap.class, id++);
|
||||||
|
registerClass(BooleanArrayList.class, id++);
|
||||||
|
registerClass(ByteArrayList.class, id++);
|
||||||
|
registerClass(ShortArrayList.class, id++);
|
||||||
|
registerClass(CharArrayList.class, id++);
|
||||||
|
registerClass(IntArrayList.class, id++);
|
||||||
|
registerClass(LongArrayList.class, id++);
|
||||||
|
registerClass(TreeSet.class, id++);
|
||||||
|
registerClass(SortedSet.class, id++);
|
||||||
|
registerClass(SortedMap.class, id++);
|
||||||
|
|
||||||
|
registerClass(EnhancedObject.class, id++);
|
||||||
|
registerClass(EnhancedObjectStrangeDbList.class, id++);
|
||||||
|
registerClass(ObjectStrangeDbList.class, id++);
|
||||||
|
registerClass(StrangeDbList.class, id++);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T extends EnhancedObject> T loadEnhancedObject(long reference) throws IOException {
|
||||||
|
return loadEnhancedObject_(reference, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T extends EnhancedObject> T loadEnhancedObject(long reference, Class<?> forcedType) throws IOException {
|
||||||
|
if (forcedType == null) {
|
||||||
|
throw new NullPointerException("The class is null!");
|
||||||
|
}
|
||||||
|
return loadEnhancedObject_(reference, forcedType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private <T extends EnhancedObject> T loadEnhancedObject_(long reference, Class<?> forcedType) throws IOException {
|
||||||
|
ByteBuffer buffer = referencesIO.readFromReference(reference);
|
||||||
|
if (buffer.limit() == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
int serializedVersion = Byte.toUnsignedInt(buffer.get()) - 5;
|
||||||
|
if (serializedVersion < 0) {
|
||||||
|
System.err.println("PLEASE UPGRADE THE DATABASE");
|
||||||
|
throw new IOException("PLEASE UPGRADE THE DATABASE");
|
||||||
|
}
|
||||||
|
int serializedClassLength = Byte.toUnsignedInt(buffer.get());
|
||||||
|
byte[] serializedClassData = new byte[serializedClassLength];
|
||||||
|
buffer.get(serializedClassData);
|
||||||
|
Class<?> objectType;
|
||||||
|
try {
|
||||||
|
objectType = forcedType != null ? forcedType : serializer.readClassBytes(serializedClassData);
|
||||||
|
} catch (Exception | Error ex) {
|
||||||
|
throw new IOException(ex);
|
||||||
|
}
|
||||||
|
int fieldsCount = buffer.getInt();
|
||||||
|
int methodsCount = buffer.getInt();
|
||||||
|
long[] fieldRefs = new long[fieldsCount];
|
||||||
|
long[] methodRefs = new long[methodsCount];
|
||||||
|
for (int i = 0; i < fieldsCount; i++) {
|
||||||
|
fieldRefs[i] = buffer.getLong();
|
||||||
|
}
|
||||||
|
for (int i = 0; i < methodsCount; i++) {
|
||||||
|
methodRefs[i] = buffer.getLong();
|
||||||
|
}
|
||||||
|
long nativeFieldsDataReference = buffer.getLong();
|
||||||
|
return preloadEnhancedObject(objectType, serializedVersion, nativeFieldsDataReference, fieldRefs,
|
||||||
|
methodRefs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public EnhancedObjectIndices loadEnhancedObjectUids(long reference) throws IOException {
|
||||||
|
ByteBuffer buffer = referencesIO.readFromReference(reference);
|
||||||
|
if (buffer.limit() == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
//noinspection unused
|
||||||
|
int serializedVersion = Byte.toUnsignedInt(buffer.get()) - 5;
|
||||||
|
int serializedClassLength = Byte.toUnsignedInt(buffer.get());
|
||||||
|
byte[] serializedClassData = new byte[serializedClassLength];
|
||||||
|
buffer.get(serializedClassData);
|
||||||
|
Class<? extends EnhancedObject> objectType;
|
||||||
|
try {
|
||||||
|
objectType = serializer.readClassBytes(serializedClassData);
|
||||||
|
} catch (Exception | Error ex) {
|
||||||
|
throw new IOException(ex);
|
||||||
|
}
|
||||||
|
int fieldsCount = buffer.getInt();
|
||||||
|
int methodsCount = buffer.getInt();
|
||||||
|
long[] fieldRefs = new long[fieldsCount];
|
||||||
|
long[] methodRefs = new long[methodsCount];
|
||||||
|
for (int i = 0; i < fieldsCount; i++) {
|
||||||
|
fieldRefs[i] = buffer.getLong();
|
||||||
|
}
|
||||||
|
for (int i = 0; i < methodsCount; i++) {
|
||||||
|
methodRefs[i] = buffer.getLong();
|
||||||
|
}
|
||||||
|
long nativeFieldsDataReference = buffer.getLong();
|
||||||
|
return new EnhancedObjectIndices(objectType, reference, fieldRefs, methodRefs, nativeFieldsDataReference);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object loadData(DbDataType propertyType, long dataReference) throws IOException {
|
||||||
|
return loadData_(propertyType, dataReference);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object loadData_(DbDataType propertyType, long dataReference) throws IOException {
|
||||||
|
|
||||||
|
switch (propertyType) {
|
||||||
|
case ENHANCED_OBJECT:
|
||||||
|
return loadEnhancedObject_(dataReference, null);
|
||||||
|
case OBJECT:
|
||||||
|
return loadObject_(dataReference);
|
||||||
|
case REFERENCES_LIST:
|
||||||
|
return loadReferencesList_(dataReference);
|
||||||
|
default:
|
||||||
|
throw new NullPointerException("Unknown data type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> void setData(long reference, DbDataType propertyType, T loadedPropertyValue) throws IOException {
|
||||||
|
switch (propertyType) {
|
||||||
|
case OBJECT:
|
||||||
|
setObject_(reference, loadedPropertyValue);
|
||||||
|
break;
|
||||||
|
case REFERENCES_LIST:
|
||||||
|
setReferencesList_(reference, (LongArrayList) loadedPropertyValue);
|
||||||
|
break;
|
||||||
|
case ENHANCED_OBJECT:
|
||||||
|
setEnhancedObject_(reference, (EnhancedObject) loadedPropertyValue);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T extends EnhancedObject> void setPrimitives(T enhancedObject, long reference, DbPrimitiveType[] types, Field[] fields)
|
||||||
|
throws IOException {
|
||||||
|
ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES * fields.length);
|
||||||
|
try {
|
||||||
|
for (int i = 0; i < fields.length; i++) {
|
||||||
|
Field field = fields[i];
|
||||||
|
DbPrimitiveType type = types[i];
|
||||||
|
if (field == null) {
|
||||||
|
buffer.putLong(0);
|
||||||
|
} else {
|
||||||
|
switch (type) {
|
||||||
|
case BOOLEAN:
|
||||||
|
buffer.putLong(field.getBoolean(enhancedObject) ? 1 : 0);
|
||||||
|
break;
|
||||||
|
case BYTE:
|
||||||
|
buffer.putLong(field.getByte(enhancedObject));
|
||||||
|
break;
|
||||||
|
case SHORT:
|
||||||
|
buffer.putLong(field.getShort(enhancedObject));
|
||||||
|
break;
|
||||||
|
case CHAR:
|
||||||
|
buffer.putLong(field.getChar(enhancedObject));
|
||||||
|
break;
|
||||||
|
case INTEGER:
|
||||||
|
buffer.putLong(field.getInt(enhancedObject));
|
||||||
|
break;
|
||||||
|
case LONG:
|
||||||
|
buffer.putLong(field.getLong(enhancedObject));
|
||||||
|
break;
|
||||||
|
case FLOAT:
|
||||||
|
buffer.putLong(Float.floatToRawIntBits(field.getFloat(enhancedObject)));
|
||||||
|
break;
|
||||||
|
case DOUBLE:
|
||||||
|
buffer.putLong(Double.doubleToRawLongBits(field.getDouble(enhancedObject)));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IllegalArgumentException | IllegalAccessException e) {
|
||||||
|
throw new IOException(e);
|
||||||
|
}
|
||||||
|
buffer.flip();
|
||||||
|
referencesIO.writeToReference(reference, BLANK_DATA_CLEANER, buffer.limit(), buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T extends EnhancedObject> void setEnhancedObject(long reference, T value) throws IOException {
|
||||||
|
setEnhancedObject_(reference, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T extends EnhancedObject> void setEnhancedObject_(long reference, T value) throws IOException {
|
||||||
|
if (value != null) {
|
||||||
|
EnhancedObjectFullInfo objectFullInfo = value.getAllInfo();
|
||||||
|
if (objectFullInfo == null || objectFullInfo.getFieldReferences() == null
|
||||||
|
|| objectFullInfo.getPropertyReferences() == null) {
|
||||||
|
throw new NullPointerException(
|
||||||
|
"An EnhancedObject has been initialized using the empty constructor!");
|
||||||
|
}
|
||||||
|
|
||||||
|
final long[] fieldReferences = objectFullInfo.getFieldReferences();
|
||||||
|
final DbDataType[] fieldTypes = objectFullInfo.getFieldTypes();
|
||||||
|
final Field[] fields = objectFullInfo.getFields();
|
||||||
|
final long nativeFieldDataReference = objectFullInfo.getPrimitiveFieldDataReference();
|
||||||
|
final DbPrimitiveType[] nativeFieldTypes = objectFullInfo.getPrimitiveFieldTypes();
|
||||||
|
final Field[] nativeFields = objectFullInfo.getPrimitiveFields();
|
||||||
|
final long[] propertyReferences = objectFullInfo.getPropertyReferences();
|
||||||
|
final DbDataType[] propertyTypes = objectFullInfo.getPropertyTypes();
|
||||||
|
final Object[] propertyValues = objectFullInfo.getLoadedPropertyValues();
|
||||||
|
byte[] serializedClassData = serializer.writeClassBytes(value.getClass());
|
||||||
|
final int totalSize = Byte.BYTES + serializedClassData.length + Byte.BYTES + Integer.BYTES * 2 + fieldReferences.length * Long.BYTES
|
||||||
|
+ propertyReferences.length * Long.BYTES + Long.BYTES;
|
||||||
|
ByteBuffer buffer = ByteBuffer.allocate(totalSize);
|
||||||
|
if (serializedClassData.length > 255) {
|
||||||
|
throw new IndexOutOfBoundsException("The class name is too long!");
|
||||||
|
}
|
||||||
|
if (objectFullInfo.getVersion() > 250) {
|
||||||
|
throw new IndexOutOfBoundsException("The class version is too long!");
|
||||||
|
}
|
||||||
|
buffer.put((byte) (objectFullInfo.getVersion() + 5));
|
||||||
|
buffer.put((byte) serializedClassData.length);
|
||||||
|
buffer.put(serializedClassData);
|
||||||
|
buffer.putInt(fieldReferences.length);
|
||||||
|
buffer.putInt(propertyReferences.length);
|
||||||
|
for (int i = 0; i < fieldReferences.length; i++) {
|
||||||
|
buffer.putLong(fieldReferences[i]);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < propertyReferences.length; i++) {
|
||||||
|
buffer.putLong(propertyReferences[i]);
|
||||||
|
}
|
||||||
|
buffer.putLong(nativeFieldDataReference);
|
||||||
|
buffer.flip();
|
||||||
|
|
||||||
|
for (int i = 0; i < fieldReferences.length; i++) {
|
||||||
|
if (fields[i] != null) {
|
||||||
|
try {
|
||||||
|
setData(fieldReferences[i], fieldTypes[i], fields[i].get(value));
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new IOException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int i = 0; i < propertyReferences.length; i++) {
|
||||||
|
if (propertyValues[i] != null) {
|
||||||
|
setData(propertyReferences[i], propertyTypes[i], propertyValues[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setPrimitives(value, nativeFieldDataReference, nativeFieldTypes, nativeFields);
|
||||||
|
referencesIO.writeToReference(reference, ENHANCED_OBJECT_METADATA_CLEANER, totalSize, buffer);
|
||||||
|
} else {
|
||||||
|
referencesIO.writeToReference(reference, BLANK_DATA_CLEANER, 0, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> T loadObject(long reference) throws IOException {
|
||||||
|
return loadObject_(reference);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private <T> T loadObject_(long reference) throws IOException {
|
||||||
|
ByteBuffer buffer = referencesIO.readFromReference(reference);
|
||||||
|
if (buffer.limit() == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
buffer.rewind();
|
||||||
|
byte[] data = buffer.array();
|
||||||
|
try {
|
||||||
|
return serializer.readClassAndObjectBytes(data);
|
||||||
|
} catch (Exception | Error ex) {
|
||||||
|
throw new IOException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LongArrayList loadReferencesList(long reference) throws IOException {
|
||||||
|
return loadReferencesList_(reference);
|
||||||
|
}
|
||||||
|
|
||||||
|
private LongArrayList loadReferencesList_(long reference) throws IOException {
|
||||||
|
ByteBuffer buffer = referencesIO.readFromReference(reference);
|
||||||
|
if (buffer.limit() == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
int itemsCount = buffer.getInt();
|
||||||
|
LongArrayList arrayList = new LongArrayList();
|
||||||
|
for (int i = 0; i < itemsCount; i++) {
|
||||||
|
arrayList.add(buffer.getLong());
|
||||||
|
}
|
||||||
|
return arrayList;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long loadReferencesListFirstElement(long reference) throws IOException {
|
||||||
|
return loadReferencesListFirstElement_(reference);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Long loadReferencesListFirstElement_(long reference) throws IOException {
|
||||||
|
ByteBuffer buffer = referencesIO.readFromReference(reference);
|
||||||
|
if (buffer.limit() == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
int itemsCount = buffer.getInt();
|
||||||
|
if (itemsCount <= 0) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return buffer.getLong();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long loadReferencesListLastElement(long reference) throws IOException {
|
||||||
|
return loadReferencesListLastElement_(reference);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Long loadReferencesListLastElement_(long reference) throws IOException {
|
||||||
|
ByteBuffer buffer = referencesIO.readFromReferenceSizeAndLastElementOfReferencesList(reference);
|
||||||
|
if (buffer.limit() == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
int itemsCount = buffer.getInt();
|
||||||
|
if (itemsCount <= 0) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return buffer.getLong();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LongList loadPrimitiveData(long reference) throws IOException {
|
||||||
|
return loadPrimitiveData_(reference);
|
||||||
|
}
|
||||||
|
|
||||||
|
private LongList loadPrimitiveData_(long reference) throws IOException {
|
||||||
|
ByteBuffer buffer = referencesIO.readFromReference(reference);
|
||||||
|
if (buffer.limit() == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
int size = buffer.limit() / Long.BYTES;
|
||||||
|
LongArrayList result = new LongArrayList(size);
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
result.add(buffer.getLong());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> void setObject(long reference, T value) throws IOException {
|
||||||
|
setObject_(reference, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> void setObject_(long reference, T value) throws IOException {
|
||||||
|
if (value != null) {
|
||||||
|
byte[] data = serializer.writeClassAndObjectBytes(value);
|
||||||
|
ByteBuffer dataByteBuffer = ByteBuffer.wrap(data);
|
||||||
|
referencesIO.writeToReference(reference, BLANK_DATA_CLEANER, data.length, dataByteBuffer);
|
||||||
|
} else {
|
||||||
|
referencesIO.writeToReference(reference, BLANK_DATA_CLEANER, 0, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setReferencesList(long reference, LongArrayList value) throws IOException {
|
||||||
|
setReferencesList_(reference, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setReferencesList_(long reference, LongArrayList value) throws IOException {
|
||||||
|
if (value != null) {
|
||||||
|
int items = value.size();
|
||||||
|
ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES * items + Integer.BYTES * 2);
|
||||||
|
buffer.putInt(items);
|
||||||
|
for (int i = 0; i < items; i++) {
|
||||||
|
buffer.putLong(value.getLong(i));
|
||||||
|
}
|
||||||
|
buffer.putInt(items);
|
||||||
|
buffer.flip();
|
||||||
|
referencesIO.writeToReference(reference, REFERENCES_LIST_CLEANER, buffer.limit(), buffer);
|
||||||
|
} else {
|
||||||
|
referencesIO.writeToReference(reference, REFERENCES_LIST_CLEANER, 0, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long newNullObject() throws IOException {
|
||||||
|
return newNullObject_();
|
||||||
|
}
|
||||||
|
|
||||||
|
private long newNullObject_() throws IOException {
|
||||||
|
return referencesIO.allocateReference();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void loadProperty(EnhancedObject obj, int propertyId, DbDataType propertyType,
|
||||||
|
long propertyUID) throws IOException {
|
||||||
|
obj.setProperty(propertyId, loadData_(propertyType, propertyUID));
|
||||||
|
obj.setProperty(propertyId, loadData_(propertyType, propertyUID));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void registerClass(Class<?> type, int id) {
|
||||||
|
if (id < -100) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
final int realId = id + 100;
|
||||||
|
|
||||||
|
serializer.registerClass(type, realId);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Map<Class<?>, Integer> getRegisteredClasses() {
|
||||||
|
Map<Class<?>, Integer> registeredClasses = serializer.getRegisteredClasses();
|
||||||
|
registeredClasses.replaceAll((clazz,id) -> id - 100);
|
||||||
|
registeredClasses.entrySet().removeIf((entry) -> entry.getValue() < 0);
|
||||||
|
return registeredClasses;
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T extends EnhancedObject> void preloadEnhancedObjectProperties(T obj, long[] propertyReferences) {
|
||||||
|
// Declare the variables needed to getBlock the biggest property Id
|
||||||
|
Method[] unorderedPropertyGetters = obj.getPropertyGetters();
|
||||||
|
Method[] unorderedPropertySetters = obj.getPropertySetters();
|
||||||
|
|
||||||
|
// Find the biggest property Id
|
||||||
|
int biggestGetter = getBiggestPropertyGetterId_(unorderedPropertyGetters);
|
||||||
|
int biggestSetter = getBiggestPropertySetterId_(unorderedPropertySetters);
|
||||||
|
int biggestPropertyId = biggestGetter > biggestSetter ? biggestGetter : biggestSetter;
|
||||||
|
|
||||||
|
for (Method property : unorderedPropertySetters) {
|
||||||
|
DbProperty fieldAnnotation = property.getAnnotation(DbProperty.class);
|
||||||
|
int propertyId = fieldAnnotation.id();
|
||||||
|
if (propertyId > biggestPropertyId) {
|
||||||
|
biggestPropertyId = propertyId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Declare the other variables
|
||||||
|
DbDataType[] propertyTypes = new DbDataType[biggestPropertyId + 1];
|
||||||
|
Method[] propertyGetters = new Method[biggestPropertyId + 1];
|
||||||
|
Method[] propertySetters = new Method[biggestPropertyId + 1];
|
||||||
|
Map<String, DbProperty> setterMethods = new LinkedHashMap<>();
|
||||||
|
Map<String, DbProperty> getterMethods = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
// Load the properties metadata
|
||||||
|
for (Method property : unorderedPropertyGetters) {
|
||||||
|
DbProperty propertyAnnotation = property.getAnnotation(DbProperty.class);
|
||||||
|
int propertyId = propertyAnnotation.id();
|
||||||
|
DbDataType propertyType = propertyAnnotation.type();
|
||||||
|
propertyTypes[propertyId] = propertyType;
|
||||||
|
propertyGetters[propertyId] = property;
|
||||||
|
getterMethods.put(property.getName(), propertyAnnotation);
|
||||||
|
}
|
||||||
|
for (Method property : unorderedPropertySetters) {
|
||||||
|
DbProperty propertyAnnotation = property.getAnnotation(DbProperty.class);
|
||||||
|
int propertyId = propertyAnnotation.id();
|
||||||
|
DbDataType propertyType = propertyAnnotation.type();
|
||||||
|
propertyTypes[propertyId] = propertyType;
|
||||||
|
propertySetters[propertyId] = property;
|
||||||
|
setterMethods.put(property.getName(), propertyAnnotation);
|
||||||
|
}
|
||||||
|
// Set properties metadata
|
||||||
|
obj.setProperties(propertyGetters, propertySetters, propertyTypes, propertyReferences, setterMethods,
|
||||||
|
getterMethods);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getBiggestPropertyGetterId(Method[] unorderedPropertyGetters) {
|
||||||
|
return getBiggestPropertyGetterId_(unorderedPropertyGetters);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getBiggestPropertyGetterId_(Method[] unorderedPropertyGetters) {
|
||||||
|
int biggestPropertyId = -1;
|
||||||
|
for (Method property : unorderedPropertyGetters) {
|
||||||
|
DbProperty fieldAnnotation = property.getAnnotation(DbProperty.class);
|
||||||
|
int propertyId = fieldAnnotation.id();
|
||||||
|
if (propertyId > biggestPropertyId) {
|
||||||
|
biggestPropertyId = propertyId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return biggestPropertyId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getBiggestPropertySetterId(Method[] unorderedPropertySetters) {
|
||||||
|
return getBiggestPropertySetterId_(unorderedPropertySetters);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getBiggestPropertySetterId_(Method[] unorderedPropertySetters) {
|
||||||
|
int biggestPropertyId = -1;
|
||||||
|
for (Method property : unorderedPropertySetters) {
|
||||||
|
DbProperty fieldAnnotation = property.getAnnotation(DbProperty.class);
|
||||||
|
int propertyId = fieldAnnotation.id();
|
||||||
|
if (propertyId > biggestPropertyId) {
|
||||||
|
biggestPropertyId = propertyId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return biggestPropertyId;
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T extends EnhancedObject> void preloadEnhancedObjectPrimitiveFields(T obj, long nativeFieldsDataReference)
|
||||||
|
throws IOException {
|
||||||
|
// Declare the variables needed to getBlock the biggest field Id
|
||||||
|
Field[] unorderedFields = getPrimitiveFields_(obj);
|
||||||
|
// Find the biggest field Id
|
||||||
|
int biggestFieldId = getBiggestPrimitiveFieldId_(unorderedFields);
|
||||||
|
|
||||||
|
// Declare the other variables
|
||||||
|
Field[] fields = new Field[biggestFieldId + 1];
|
||||||
|
DbPrimitiveType[] orderedFieldTypes = new DbPrimitiveType[biggestFieldId + 1];
|
||||||
|
|
||||||
|
// Load all fields metadata
|
||||||
|
for (Field field : unorderedFields) {
|
||||||
|
DbPrimitiveField fieldAnnotation = field.getAnnotation(DbPrimitiveField.class);
|
||||||
|
int fieldId = fieldAnnotation.id();
|
||||||
|
DbPrimitiveType fieldType = fieldAnnotation.type();
|
||||||
|
fields[fieldId] = field;
|
||||||
|
orderedFieldTypes[fieldId] = fieldType;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load fields data
|
||||||
|
ByteBuffer buffer = referencesIO.readFromReference(nativeFieldsDataReference);
|
||||||
|
|
||||||
|
// Load fields
|
||||||
|
try {
|
||||||
|
for (int id = 0; id < fields.length; id++) {
|
||||||
|
Field field = fields[id];
|
||||||
|
DbPrimitiveType type = orderedFieldTypes[id];
|
||||||
|
|
||||||
|
if (field != null) {
|
||||||
|
switch (type) {
|
||||||
|
case BOOLEAN:
|
||||||
|
FieldUtils.writeField(field, obj, (boolean) (buffer.getLong() % 2 == 1), true);
|
||||||
|
break;
|
||||||
|
case BYTE:
|
||||||
|
FieldUtils.writeField(field, obj, (byte) buffer.getLong(), true);
|
||||||
|
break;
|
||||||
|
case SHORT:
|
||||||
|
FieldUtils.writeField(field, obj, (short) buffer.getLong(), true);
|
||||||
|
break;
|
||||||
|
case CHAR:
|
||||||
|
FieldUtils.writeField(field, obj, (char) buffer.getLong(), true);
|
||||||
|
break;
|
||||||
|
case INTEGER:
|
||||||
|
FieldUtils.writeField(field, obj, (int) buffer.getLong(), true);
|
||||||
|
break;
|
||||||
|
case LONG:
|
||||||
|
FieldUtils.writeField(field, obj, (long) buffer.getLong(), true);
|
||||||
|
break;
|
||||||
|
case FLOAT:
|
||||||
|
FieldUtils.writeField(field, obj, Float.intBitsToFloat((int) buffer.getLong()), true);
|
||||||
|
break;
|
||||||
|
case DOUBLE:
|
||||||
|
FieldUtils.writeField(field, obj, Double.longBitsToDouble(buffer.getLong()), true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IllegalArgumentException | IllegalAccessException e) {
|
||||||
|
throw new IOException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set fields metadata
|
||||||
|
obj.setPrimitiveFields(fields, orderedFieldTypes, nativeFieldsDataReference);
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T extends EnhancedObject> void preloadEnhancedObjectFields(T obj, long[] fieldReferences)
|
||||||
|
throws IOException {
|
||||||
|
// Declare the variables needed to getBlock the biggest field Id
|
||||||
|
Field[] unorderedFields = getFields_(obj);
|
||||||
|
// Find the biggest field Id
|
||||||
|
int biggestFieldId = getBiggestFieldId(unorderedFields);
|
||||||
|
|
||||||
|
// Declare the other variables
|
||||||
|
Field[] fields = new Field[biggestFieldId + 1];
|
||||||
|
DbDataType[] orderedFieldTypes = new DbDataType[biggestFieldId + 1];
|
||||||
|
|
||||||
|
// Load all fields metadata and load them
|
||||||
|
for (Field field : unorderedFields) {
|
||||||
|
DbField fieldAnnotation = field.getAnnotation(DbField.class);
|
||||||
|
int fieldId = fieldAnnotation.id();
|
||||||
|
DbDataType fieldType = fieldAnnotation.type();
|
||||||
|
loadField_(obj, field, fieldType, fieldReferences[fieldId]);
|
||||||
|
fields[fieldId] = field;
|
||||||
|
orderedFieldTypes[fieldId] = fieldType;
|
||||||
|
}
|
||||||
|
// Set fields metadata
|
||||||
|
obj.setFields(fields, orderedFieldTypes, fieldReferences);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T extends EnhancedObject> void loadField(T obj, Field field, DbDataType fieldType, long fieldReference)
|
||||||
|
throws IOException {
|
||||||
|
loadField_(obj, field, fieldType, fieldReference);
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T extends EnhancedObject> void loadField_(T obj, Field field, DbDataType fieldType, long fieldReference)
|
||||||
|
throws IOException {
|
||||||
|
Object data = loadData_(fieldType, fieldReference);
|
||||||
|
try {
|
||||||
|
if (data != null) {
|
||||||
|
if (!field.getType().isInstance(data)) {
|
||||||
|
throw new IOException("There is an attempt to load an object of type " + data.getClass()
|
||||||
|
+ " into a field of type " + field.getType());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FieldUtils.writeField(field, obj, data, true);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new IOException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T extends EnhancedObject> Field[] getFields(T obj) {
|
||||||
|
return getFields_(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T extends EnhancedObject> Field[] getFields_(T obj) {
|
||||||
|
return FieldUtils.getFieldsWithAnnotation(obj.getClass(), DbField.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T extends EnhancedObject> Field[] getPrimitiveFields(T obj) {
|
||||||
|
return getPrimitiveFields_(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T extends EnhancedObject> Field[] getPrimitiveFields_(T obj) {
|
||||||
|
return FieldUtils.getFieldsWithAnnotation(obj.getClass(), DbPrimitiveField.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getBiggestFieldId(Field[] unorderedFields) {
|
||||||
|
return getBiggestFieldId_(unorderedFields);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getBiggestFieldId_(Field[] unorderedFields) {
|
||||||
|
int biggestFieldId = -1;
|
||||||
|
for (Field field : unorderedFields) {
|
||||||
|
DbField fieldAnnotation = field.getAnnotation(DbField.class);
|
||||||
|
int propertyId = fieldAnnotation.id();
|
||||||
|
if (propertyId > biggestFieldId) {
|
||||||
|
biggestFieldId = propertyId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return biggestFieldId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getBiggestPrimitiveFieldId(Field[] unorderedFields) {
|
||||||
|
return getBiggestPrimitiveFieldId_(unorderedFields);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getBiggestPrimitiveFieldId_(Field[] unorderedFields) {
|
||||||
|
int biggestFieldId = -1;
|
||||||
|
for (Field field : unorderedFields) {
|
||||||
|
DbPrimitiveField fieldAnnotation = field.getAnnotation(DbPrimitiveField.class);
|
||||||
|
int propertyId = fieldAnnotation.id();
|
||||||
|
if (propertyId > biggestFieldId) {
|
||||||
|
biggestFieldId = propertyId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return biggestFieldId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private <T extends EnhancedObject> T toInstance(Class<?> type) throws IOException {
|
||||||
|
try {
|
||||||
|
T obj = (T) type.getConstructor().newInstance();
|
||||||
|
obj.setDatabaseTools(databaseTools);
|
||||||
|
return obj;
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
throw new IOException("You must declare a public empty constructor in class " + type + ": public "
|
||||||
|
+ type.getSimpleName() + "()", e);
|
||||||
|
} catch (IllegalAccessException | InvocationTargetException | InstantiationException e) {
|
||||||
|
throw new IOException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T extends EnhancedObject> T preloadEnhancedObject(Class<?> objectType, int serializedVersion, long nativeFieldsRef, long[] fieldRefs, long[] methodRefs) throws IOException {
|
||||||
|
// Instantiate the class to an object
|
||||||
|
T obj = toInstance(objectType);
|
||||||
|
|
||||||
|
// Check the serialized version
|
||||||
|
DbClass dbClass = objectType.getAnnotation(DbClass.class);
|
||||||
|
int classVersion = 0;
|
||||||
|
if (dbClass != null) {
|
||||||
|
classVersion = dbClass.version();
|
||||||
|
}
|
||||||
|
if (classVersion == serializedVersion) {
|
||||||
|
preloadEnhancedObjectPrimitiveFields(obj, nativeFieldsRef);
|
||||||
|
preloadEnhancedObjectFields(obj, fieldRefs);
|
||||||
|
preloadEnhancedObjectProperties(obj, methodRefs);
|
||||||
|
} else if (classVersion > serializedVersion) {
|
||||||
|
DatabaseEnhancedObjectUpgrader enhancedObjectUpgrader = new DatabaseEnhancedObjectUpgrader(this, fieldRefs, methodRefs, nativeFieldsRef);
|
||||||
|
dataInitializer.initializeDbObject_(obj);
|
||||||
|
obj.onUpgrade(serializedVersion, enhancedObjectUpgrader);
|
||||||
|
} else {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"The serialized class is more recent than the current version of that class!");
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long[] allocateNewUIDs(int quantity) throws IOException {
|
||||||
|
return allocateNewUIDs_(quantity);
|
||||||
|
}
|
||||||
|
|
||||||
|
private long[] allocateNewUIDs_(int quantity) throws IOException {
|
||||||
|
long[] ids = new long[quantity];
|
||||||
|
for (int i = 0; i < quantity; i++) {
|
||||||
|
ids[i] = newNullObject_();
|
||||||
|
}
|
||||||
|
return ids;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IDataInitializer getDataInitializer() {
|
||||||
|
return dataInitializer;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DatabaseDataInitializer implements IDataInitializer {
|
||||||
|
|
||||||
|
public DatabaseDataInitializer() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initializeDbObject(EnhancedObject obj) throws IOException {
|
||||||
|
initializeDbObject_(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeDbObject_(EnhancedObject obj) throws IOException {
|
||||||
|
initializeDbObjectFields(obj);
|
||||||
|
initializeDbObjectPrimitiveFields(obj);
|
||||||
|
initializeDbObjectProperties(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeDbObjectFields(EnhancedObject obj) throws IOException {
|
||||||
|
// Declare the variables needed to getBlock the biggest field Id
|
||||||
|
Field[] unorderedFields = getFields_(obj);
|
||||||
|
// Find the biggest field Id
|
||||||
|
int biggestFieldId = getBiggestFieldId_(unorderedFields);
|
||||||
|
|
||||||
|
// Allocate new UIDs
|
||||||
|
long[] fieldUIDs = allocateNewUIDs_(biggestFieldId + 1);
|
||||||
|
|
||||||
|
// Declare the other variables
|
||||||
|
Field[] fields = new Field[biggestFieldId + 1];
|
||||||
|
DbDataType[] orderedFieldTypes = new DbDataType[biggestFieldId + 1];
|
||||||
|
|
||||||
|
// Load all fields metadata and load them
|
||||||
|
for (Field field : unorderedFields) {
|
||||||
|
DbField fieldAnnotation = field.getAnnotation(DbField.class);
|
||||||
|
int fieldId = fieldAnnotation.id();
|
||||||
|
DbDataType fieldType = fieldAnnotation.type();
|
||||||
|
loadField_(obj, field, fieldType, fieldUIDs[fieldId]);
|
||||||
|
fields[fieldId] = field;
|
||||||
|
orderedFieldTypes[fieldId] = fieldType;
|
||||||
|
}
|
||||||
|
// Set fields metadata
|
||||||
|
obj.setFields(fields, orderedFieldTypes, fieldUIDs);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeDbObjectPrimitiveFields(EnhancedObject obj) throws IOException {
|
||||||
|
// Declare the variables needed to getBlock the biggest field Id
|
||||||
|
Field[] unorderedFields = getPrimitiveFields_(obj);
|
||||||
|
// Find the biggest field Id
|
||||||
|
int biggestFieldId = getBiggestPrimitiveFieldId_(unorderedFields);
|
||||||
|
|
||||||
|
// Allocate new UID
|
||||||
|
long fieldDataUID = newNullObject_();
|
||||||
|
|
||||||
|
// Declare the other variables
|
||||||
|
Field[] fields = new Field[biggestFieldId + 1];
|
||||||
|
DbPrimitiveType[] orderedFieldTypes = new DbPrimitiveType[biggestFieldId + 1];
|
||||||
|
|
||||||
|
// Load all fields metadata and load them
|
||||||
|
try {
|
||||||
|
for (Field field : unorderedFields) {
|
||||||
|
DbPrimitiveField fieldAnnotation = field.getAnnotation(DbPrimitiveField.class);
|
||||||
|
int fieldId = fieldAnnotation.id();
|
||||||
|
DbPrimitiveType fieldType = fieldAnnotation.type();
|
||||||
|
switch (fieldType) {
|
||||||
|
case BOOLEAN:
|
||||||
|
FieldUtils.writeField(field, obj, false, true);
|
||||||
|
break;
|
||||||
|
case BYTE:
|
||||||
|
FieldUtils.writeField(field, obj, (byte) 0, true);
|
||||||
|
break;
|
||||||
|
case CHAR:
|
||||||
|
FieldUtils.writeField(field, obj, (char) 0, true);
|
||||||
|
break;
|
||||||
|
case SHORT:
|
||||||
|
FieldUtils.writeField(field, obj, (short) 0, true);
|
||||||
|
break;
|
||||||
|
case INTEGER:
|
||||||
|
FieldUtils.writeField(field, obj, 0, true);
|
||||||
|
break;
|
||||||
|
case LONG:
|
||||||
|
FieldUtils.writeField(field, obj, (long) 0, true);
|
||||||
|
break;
|
||||||
|
case FLOAT:
|
||||||
|
FieldUtils.writeField(field, obj, (float) 0, true);
|
||||||
|
break;
|
||||||
|
case DOUBLE:
|
||||||
|
FieldUtils.writeField(field, obj, (double) 0, true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
fields[fieldId] = field;
|
||||||
|
orderedFieldTypes[fieldId] = fieldType;
|
||||||
|
}
|
||||||
|
} catch (IllegalArgumentException | IllegalAccessException e) {
|
||||||
|
throw new IOException(e);
|
||||||
|
}
|
||||||
|
// Set fields metadata
|
||||||
|
obj.setPrimitiveFields(fields, orderedFieldTypes, fieldDataUID);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeDbObjectProperties(EnhancedObject obj) throws IOException {
|
||||||
|
// Declare the variables needed to getBlock the biggest property Id
|
||||||
|
Method[] unorderedPropertyGetters = obj.getPropertyGetters();
|
||||||
|
Method[] unorderedPropertySetters = obj.getPropertySetters();
|
||||||
|
|
||||||
|
// Find the biggest property Id
|
||||||
|
int biggestGetter = getBiggestPropertyGetterId_(unorderedPropertyGetters);
|
||||||
|
int biggestSetter = getBiggestPropertySetterId_(unorderedPropertySetters);
|
||||||
|
int biggestPropertyId = biggestGetter > biggestSetter ? biggestGetter : biggestSetter;
|
||||||
|
|
||||||
|
// Allocate new UIDs
|
||||||
|
long[] propertyUIDs = allocateNewUIDs_(biggestPropertyId + 1);
|
||||||
|
|
||||||
|
for (Method property : unorderedPropertySetters) {
|
||||||
|
DbProperty fieldAnnotation = property.getAnnotation(DbProperty.class);
|
||||||
|
int propertyId = fieldAnnotation.id();
|
||||||
|
if (propertyId > biggestPropertyId) {
|
||||||
|
biggestPropertyId = propertyId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Declare the other variables
|
||||||
|
DbDataType[] propertyTypes = new DbDataType[biggestPropertyId + 1];
|
||||||
|
Method[] propertyGetters = new Method[biggestPropertyId + 1];
|
||||||
|
Method[] propertySetters = new Method[biggestPropertyId + 1];
|
||||||
|
Map<String, DbProperty> setterMethods = new LinkedHashMap<>();
|
||||||
|
Map<String, DbProperty> getterMethods = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
// Load the properties metadata
|
||||||
|
for (Method property : unorderedPropertyGetters) {
|
||||||
|
DbProperty propertyAnnotation = property.getAnnotation(DbProperty.class);
|
||||||
|
int propertyId = propertyAnnotation.id();
|
||||||
|
DbDataType propertyType = propertyAnnotation.type();
|
||||||
|
propertyTypes[propertyId] = propertyType;
|
||||||
|
propertyGetters[propertyId] = property;
|
||||||
|
getterMethods.put(property.getName(), propertyAnnotation);
|
||||||
|
}
|
||||||
|
for (Method property : unorderedPropertySetters) {
|
||||||
|
DbProperty propertyAnnotation = property.getAnnotation(DbProperty.class);
|
||||||
|
int propertyId = propertyAnnotation.id();
|
||||||
|
DbDataType propertyType = propertyAnnotation.type();
|
||||||
|
propertyTypes[propertyId] = propertyType;
|
||||||
|
propertySetters[propertyId] = property;
|
||||||
|
setterMethods.put(property.getName(), propertyAnnotation);
|
||||||
|
}
|
||||||
|
// Set properties metadata
|
||||||
|
obj.setProperties(propertyGetters, propertySetters, propertyTypes, propertyUIDs, setterMethods, getterMethods);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,119 @@
|
|||||||
|
/**
|
||||||
|
package it.cavallium.strangedb.java.database;
|
||||||
|
|
||||||
|
import org.nustaq.serialization.FSTConfiguration;
|
||||||
|
import org.nustaq.serialization.FSTObjectInput;
|
||||||
|
import org.nustaq.serialization.FSTObjectOutput;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
|
||||||
|
public class FSTSerializer implements ISerializer {
|
||||||
|
|
||||||
|
private FSTConfiguration conf = FSTConfiguration.createDefaultConfiguration();
|
||||||
|
|
||||||
|
public FSTSerializer() {
|
||||||
|
conf.setStructMode(true);
|
||||||
|
conf.setClassLoader(ClassLoader.getSystemClassLoader());
|
||||||
|
conf.setCrossPlatform(false);
|
||||||
|
conf.setPreferSpeed(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T> T read(InputStream stream, Class<T> type) throws IOException {
|
||||||
|
try {
|
||||||
|
FSTObjectInput in = conf.getObjectInput(stream);
|
||||||
|
T result = (T) in.readObject(type);
|
||||||
|
// DON'T: in.close(); here prevents reuse and will result in an exception
|
||||||
|
stream.close();
|
||||||
|
return result;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new IOException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> T readBytes(byte[] input, Class<T> type) throws IOException {
|
||||||
|
return read(new ByteArrayInputStream(input), type);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T> Class<T> readClass(InputStream stream) throws IOException {
|
||||||
|
try {
|
||||||
|
FSTObjectInput in = conf.getObjectInput(stream);
|
||||||
|
Class<T> result = (Class<T>) in.readObject(Class.class);
|
||||||
|
// DON'T: in.close(); here prevents reuse and will result in an exception
|
||||||
|
stream.close();
|
||||||
|
return result;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new IOException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> Class<T> readClassBytes(byte[] input) throws IOException {
|
||||||
|
return readClass(new ByteArrayInputStream(input));
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> void write(OutputStream stream, T toWrite, Class<?> type) throws IOException {
|
||||||
|
FSTObjectOutput out = conf.getObjectOutput(stream);
|
||||||
|
out.writeObject(toWrite, type);
|
||||||
|
// DON'T out.close() when using factory method;
|
||||||
|
out.flush();
|
||||||
|
stream.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> byte[] writeBytes(T toWrite, Class<?> type) throws IOException {
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
write(baos, toWrite, type);
|
||||||
|
return baos.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void writeClass(OutputStream stream, Class<?> toWrite) throws IOException {
|
||||||
|
FSTObjectOutput out = conf.getObjectOutput(stream);
|
||||||
|
out.writeObject(toWrite, Class.class);
|
||||||
|
// DON'T out.close() when using factory method;
|
||||||
|
out.flush();
|
||||||
|
stream.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] writeClassBytes(Class<?> toWrite) throws IOException {
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
writeClass(baos, toWrite);
|
||||||
|
return baos.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerClass(Class<?> type, int id) {
|
||||||
|
conf.registerClass(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> byte[] writeClassAndObjectBytes(T value) throws IOException {
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
|
||||||
|
FSTObjectOutput out = conf.getObjectOutput(baos);
|
||||||
|
out.writeObject(value);
|
||||||
|
// DON'T out.close() when using factory method;
|
||||||
|
out.flush();
|
||||||
|
baos.close();
|
||||||
|
|
||||||
|
return baos.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T> T readClassAndObjectBytes(byte[] input) throws IOException {
|
||||||
|
try {
|
||||||
|
ByteArrayInputStream bais = new ByteArrayInputStream(input);
|
||||||
|
FSTObjectInput in = conf.getObjectInput(bais);
|
||||||
|
T result;
|
||||||
|
result = (T) in.readObject();
|
||||||
|
// DON'T: in.close(); here prevents reuse and will result in an exception
|
||||||
|
bais.close();
|
||||||
|
return result;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new IOException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
**/
|
@ -1,6 +1,6 @@
|
|||||||
package it.cavallium.strangedb.database;
|
package it.cavallium.strangedb.java.database;
|
||||||
|
|
||||||
import it.cavallium.strangedb.EnhancedObject;
|
import it.cavallium.strangedb.java.objects.EnhancedObject;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
@ -1,6 +1,7 @@
|
|||||||
package it.cavallium.strangedb.database;
|
package it.cavallium.strangedb.java.database;
|
||||||
|
|
||||||
import it.cavallium.strangedb.EnhancedObject;
|
import it.cavallium.strangedb.java.objects.EnhancedObject;
|
||||||
|
import it.cavallium.strangedb.java.database.IObjectsIO;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
@ -1,7 +1,8 @@
|
|||||||
package it.cavallium.strangedb.database;
|
package it.cavallium.strangedb.java.database;
|
||||||
|
|
||||||
import it.cavallium.strangedb.EnhancedObject;
|
import it.cavallium.strangedb.java.objects.EnhancedObject;
|
||||||
import it.cavallium.strangedb.annotations.DbDataType;
|
import it.cavallium.strangedb.java.annotations.DbDataType;
|
||||||
|
import it.cavallium.strangedb.java.objects.EnhancedObjectIndices;
|
||||||
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
||||||
import it.unimi.dsi.fastutil.longs.LongList;
|
import it.unimi.dsi.fastutil.longs.LongList;
|
||||||
|
|
||||||
@ -9,7 +10,13 @@ import java.io.IOException;
|
|||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
public interface IObjectsIO {
|
public interface IObjectsIO {
|
||||||
<T extends EnhancedObject> T loadEnhancedObject(long reference, Class<T> objectType) throws IOException;
|
@SuppressWarnings("unchecked")
|
||||||
|
<T extends EnhancedObject> T loadEnhancedObject(long reference) throws IOException;
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
<T extends EnhancedObject> T loadEnhancedObject(long reference, Class<?> forcedType) throws IOException;
|
||||||
|
|
||||||
|
EnhancedObjectIndices loadEnhancedObjectUids(long reference) throws IOException;
|
||||||
|
|
||||||
<T> T loadObject(long reference) throws IOException;
|
<T> T loadObject(long reference) throws IOException;
|
||||||
|
|
||||||
@ -47,11 +54,15 @@ public interface IObjectsIO {
|
|||||||
return reference;
|
return reference;
|
||||||
}
|
}
|
||||||
|
|
||||||
void loadProperty(EnhancedObject enhancedObject, int propertyId, Method propertyGetter, DbDataType propertyType, long propertyUID) throws IOException;
|
void loadProperty(EnhancedObject enhancedObject, int propertyId, DbDataType propertyType, long propertyUID) throws IOException;
|
||||||
|
|
||||||
void registerClass(Class<?> type, int id);
|
void registerClass(Class<?> type, int id);
|
||||||
|
|
||||||
IDataInitializer getDataInitializer();
|
IDataInitializer getDataInitializer();
|
||||||
|
|
||||||
|
Long loadReferencesListFirstElement(long reference) throws IOException;
|
||||||
|
|
||||||
|
Long loadReferencesListLastElement(long reference) throws IOException;
|
||||||
|
|
||||||
LongList loadPrimitiveData(long id) throws IOException;
|
LongList loadPrimitiveData(long id) throws IOException;
|
||||||
}
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package it.cavallium.strangedb.java.database;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public interface ISerializer {
|
||||||
|
<T> Class<T> readClassBytes(byte[] input) throws IOException;
|
||||||
|
|
||||||
|
byte[] writeClassBytes(Class<?> toWrite) throws IOException;
|
||||||
|
|
||||||
|
<T> byte[] writeClassAndObjectBytes(T value) throws IOException;
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
<T> T readClassAndObjectBytes(byte[] input) throws IOException;
|
||||||
|
|
||||||
|
void registerClass(Class<?> type, int id);
|
||||||
|
|
||||||
|
Map<Class<?>, Integer> getRegisteredClasses();
|
||||||
|
}
|
@ -0,0 +1,111 @@
|
|||||||
|
package it.cavallium.strangedb.java.database;
|
||||||
|
|
||||||
|
import com.esotericsoftware.kryo.Kryo;
|
||||||
|
import com.esotericsoftware.kryo.io.Input;
|
||||||
|
import com.esotericsoftware.kryo.io.Output;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
|
public class KryoSerializer implements ISerializer {
|
||||||
|
|
||||||
|
private static final int KRYO_INSTANCES = 20;
|
||||||
|
|
||||||
|
private ReentrantLock[] locks = new ReentrantLock[KRYO_INSTANCES];
|
||||||
|
private final Kryo[] kryo = new Kryo[KRYO_INSTANCES];
|
||||||
|
private AtomicInteger current = new AtomicInteger(0);
|
||||||
|
private ConcurrentHashMap<Class<?>, Integer> registeredClasses = new ConcurrentHashMap<>(200);
|
||||||
|
|
||||||
|
public KryoSerializer() {
|
||||||
|
for (int i = 0; i < KRYO_INSTANCES; i++) {
|
||||||
|
locks[i] = new ReentrantLock(false);
|
||||||
|
kryo[i] = new Kryo();
|
||||||
|
kryo[i].setRegistrationRequired(false);
|
||||||
|
kryo[i].setWarnUnregisteredClasses(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public <T> Class<T> readClassBytes(byte[] input) {
|
||||||
|
int i = current.getAndUpdate((currentInt) -> currentInt + 1 == KRYO_INSTANCES ? 0 : currentInt + 1);
|
||||||
|
locks[i].lock();
|
||||||
|
try {
|
||||||
|
return (Class<T>) kryo[i].readClass(new Input(input)).getType();
|
||||||
|
} finally {
|
||||||
|
locks[i].unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] writeClassBytes(Class<?> value) {
|
||||||
|
int i = current.getAndUpdate((currentInt) -> currentInt + 1 == KRYO_INSTANCES ? 0 : currentInt + 1);
|
||||||
|
locks[i].lock();
|
||||||
|
try {
|
||||||
|
Output out = new Output(1024, Integer.MAX_VALUE);
|
||||||
|
kryo[i].writeClass(out, value);
|
||||||
|
out.flush();
|
||||||
|
out.close();
|
||||||
|
return out.toBytes();
|
||||||
|
} finally {
|
||||||
|
locks[i].unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> byte[] writeClassAndObjectBytes(T value) {
|
||||||
|
int i = current.getAndUpdate((currentInt) -> currentInt + 1 == KRYO_INSTANCES ? 0 : currentInt + 1);
|
||||||
|
locks[i].lock();
|
||||||
|
try {
|
||||||
|
Output out = new Output(1024, Integer.MAX_VALUE);
|
||||||
|
kryo[i].writeClassAndObject(out, value);
|
||||||
|
out.flush();
|
||||||
|
out.close();
|
||||||
|
return out.toBytes();
|
||||||
|
} finally {
|
||||||
|
locks[i].unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public <T> T readClassAndObjectBytes(byte[] input) {
|
||||||
|
int i = current.getAndUpdate((currentInt) -> currentInt + 1 == KRYO_INSTANCES ? 0 : currentInt + 1);
|
||||||
|
locks[i].lock();
|
||||||
|
try {
|
||||||
|
return (T) kryo[i].readClassAndObject(new Input(input));
|
||||||
|
} finally {
|
||||||
|
locks[i].unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void registerClass(Class<?> type, int id) {
|
||||||
|
registeredClasses.put(type, id);
|
||||||
|
for (int i = 0; i < KRYO_INSTANCES; i++) {
|
||||||
|
locks[i].lock();
|
||||||
|
try {
|
||||||
|
kryo[i].register(type, id);
|
||||||
|
} finally {
|
||||||
|
locks[i].unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<Class<?>, Integer> getRegisteredClasses() {
|
||||||
|
for (int i = 0; i < KRYO_INSTANCES; i++) {
|
||||||
|
locks[i].lock();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return new HashMap<>(registeredClasses);
|
||||||
|
} finally {
|
||||||
|
for (int i = 0; i < KRYO_INSTANCES; i++) {
|
||||||
|
locks[i].unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,19 +1,16 @@
|
|||||||
package it.cavallium.strangedb;
|
package it.cavallium.strangedb.java.objects;
|
||||||
|
|
||||||
import it.cavallium.strangedb.database.EnhancedObjectFullInfo;
|
import it.cavallium.strangedb.java.annotations.*;
|
||||||
import it.cavallium.strangedb.database.IDatabaseTools;
|
import it.cavallium.strangedb.java.database.IDatabaseTools;
|
||||||
import org.apache.commons.lang3.NotImplementedException;
|
import org.apache.commons.lang3.NotImplementedException;
|
||||||
import org.apache.commons.lang3.reflect.MethodUtils;
|
import org.apache.commons.lang3.reflect.MethodUtils;
|
||||||
import it.cavallium.strangedb.annotations.DbClass;
|
|
||||||
import it.cavallium.strangedb.annotations.DbDataType;
|
|
||||||
import it.cavallium.strangedb.annotations.DbPrimitiveType;
|
|
||||||
import it.cavallium.strangedb.annotations.DbPropertyGetter;
|
|
||||||
import it.cavallium.strangedb.annotations.DbPropertySetter;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.locks.Lock;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
public abstract class EnhancedObject {
|
public abstract class EnhancedObject {
|
||||||
protected final int version;
|
protected final int version;
|
||||||
@ -30,8 +27,8 @@ public abstract class EnhancedObject {
|
|||||||
private long[] propertyReferences;
|
private long[] propertyReferences;
|
||||||
private boolean[] loadedProperties;
|
private boolean[] loadedProperties;
|
||||||
private Object[] loadedPropertyValues;
|
private Object[] loadedPropertyValues;
|
||||||
private Map<String, DbPropertySetter> setterMethods;
|
private Map<String, DbProperty> setterMethods;
|
||||||
private Map<String, DbPropertyGetter> getterMethods;
|
private Map<String, DbProperty> getterMethods;
|
||||||
|
|
||||||
public EnhancedObject() {
|
public EnhancedObject() {
|
||||||
version = getClassVersion();
|
version = getClassVersion();
|
||||||
@ -63,7 +60,7 @@ public abstract class EnhancedObject {
|
|||||||
return MethodUtils.getMethodsWithAnnotation(this.getClass(), DbPropertySetter.class);
|
return MethodUtils.getMethodsWithAnnotation(this.getClass(), DbPropertySetter.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setProperties(Method[] propertyGetters, Method[] propertySetters, DbDataType[] propertyTypes, long[] propertyReferences, Map<String, DbPropertySetter> setterMethods, Map<String, DbPropertyGetter> getterMethods) {
|
public void setProperties(Method[] propertyGetters, Method[] propertySetters, DbDataType[] propertyTypes, long[] propertyReferences, Map<String, DbProperty> setterMethods, Map<String, DbProperty> getterMethods) {
|
||||||
this.propertyGetters = propertyGetters;
|
this.propertyGetters = propertyGetters;
|
||||||
this.propertySetters = propertySetters;
|
this.propertySetters = propertySetters;
|
||||||
this.propertyTypes = propertyTypes;
|
this.propertyTypes = propertyTypes;
|
||||||
@ -83,7 +80,7 @@ public abstract class EnhancedObject {
|
|||||||
StackWalker walker = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
|
StackWalker walker = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
|
||||||
StackWalker.StackFrame stackFrame = walker.walk(f -> f.skip(1).findFirst().orElse(null));
|
StackWalker.StackFrame stackFrame = walker.walk(f -> f.skip(1).findFirst().orElse(null));
|
||||||
try {
|
try {
|
||||||
int propertyId = stackFrame.getDeclaringClass().getDeclaredMethod(stackFrame.getMethodName()).getAnnotation(DbPropertyGetter.class).id();
|
int propertyId = stackFrame.getDeclaringClass().getDeclaredMethod(stackFrame.getMethodName()).getAnnotation(DbProperty.class).id();
|
||||||
return getProperty(propertyId);
|
return getProperty(propertyId);
|
||||||
} catch (IOException | NoSuchMethodException e) {
|
} catch (IOException | NoSuchMethodException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
@ -91,10 +88,10 @@ public abstract class EnhancedObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private <T> T getProperty(int propertyId) throws IOException {
|
public <T> T getProperty(int propertyId) throws IOException {
|
||||||
if (!loadedProperties[propertyId]) {
|
if (!loadedProperties[propertyId]) {
|
||||||
long propertyUID = propertyReferences[propertyId];
|
long propertyUID = propertyReferences[propertyId];
|
||||||
databaseTools.getObjectsIO().loadProperty(this, propertyId, propertyGetters[propertyId], propertyTypes[propertyId], propertyUID);
|
databaseTools.getObjectsIO().loadProperty(this, propertyId, propertyTypes[propertyId], propertyUID);
|
||||||
}
|
}
|
||||||
return (T) loadedPropertyValues[propertyId];
|
return (T) loadedPropertyValues[propertyId];
|
||||||
}
|
}
|
||||||
@ -102,7 +99,7 @@ public abstract class EnhancedObject {
|
|||||||
public <T> void setProperty(T value) {
|
public <T> void setProperty(T value) {
|
||||||
StackWalker walker = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
|
StackWalker walker = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
|
||||||
StackWalker.StackFrame stackFrame = walker.walk(f -> f.skip(1).findFirst().orElse(null));
|
StackWalker.StackFrame stackFrame = walker.walk(f -> f.skip(1).findFirst().orElse(null));
|
||||||
DbPropertySetter propertyAnnotation = setterMethods.get(stackFrame.getMethodName());
|
DbProperty propertyAnnotation = setterMethods.get(stackFrame.getMethodName());
|
||||||
setProperty(propertyAnnotation.id(), value);
|
setProperty(propertyAnnotation.id(), value);
|
||||||
}
|
}
|
||||||
|
|
@ -1,7 +1,7 @@
|
|||||||
package it.cavallium.strangedb.database;
|
package it.cavallium.strangedb.java.objects;
|
||||||
|
|
||||||
import it.cavallium.strangedb.annotations.DbDataType;
|
import it.cavallium.strangedb.java.annotations.DbDataType;
|
||||||
import it.cavallium.strangedb.annotations.DbPrimitiveType;
|
import it.cavallium.strangedb.java.annotations.DbPrimitiveType;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
|
|
||||||
@ -33,15 +33,15 @@ public class EnhancedObjectFullInfo {
|
|||||||
this.loadedPropertyValues = loadedPropertyValues;
|
this.loadedPropertyValues = loadedPropertyValues;
|
||||||
}
|
}
|
||||||
|
|
||||||
int getVersion() {
|
public int getVersion() {
|
||||||
return version;
|
return version;
|
||||||
}
|
}
|
||||||
|
|
||||||
long[] getFieldReferences() {
|
public long[] getFieldReferences() {
|
||||||
return fieldReferences;
|
return fieldReferences;
|
||||||
}
|
}
|
||||||
|
|
||||||
DbDataType[] getFieldTypes() {
|
public DbDataType[] getFieldTypes() {
|
||||||
return fieldTypes;
|
return fieldTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,15 +61,15 @@ public class EnhancedObjectFullInfo {
|
|||||||
return primitiveFields;
|
return primitiveFields;
|
||||||
}
|
}
|
||||||
|
|
||||||
long[] getPropertyReferences() {
|
public long[] getPropertyReferences() {
|
||||||
return propertyReferences;
|
return propertyReferences;
|
||||||
}
|
}
|
||||||
|
|
||||||
DbDataType[] getPropertyTypes() {
|
public DbDataType[] getPropertyTypes() {
|
||||||
return propertyTypes;
|
return propertyTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
Object[] getLoadedPropertyValues() {
|
public Object[] getLoadedPropertyValues() {
|
||||||
return loadedPropertyValues;
|
return loadedPropertyValues;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
package it.cavallium.strangedb.java.objects;
|
||||||
|
|
||||||
|
public class EnhancedObjectIndices {
|
||||||
|
public final long objectUid;
|
||||||
|
public final long[] fieldUids;
|
||||||
|
public final long[] propertyUids;
|
||||||
|
public final long nativeDataUid;
|
||||||
|
public final Class<? extends EnhancedObject> type;
|
||||||
|
|
||||||
|
public EnhancedObjectIndices(Class<? extends EnhancedObject> type, long objectUid, long[] fieldUids, long[] propertyUids, long nativeDataUid) {
|
||||||
|
this.type = type;
|
||||||
|
this.objectUid = objectUid;
|
||||||
|
this.fieldUids = fieldUids;
|
||||||
|
this.propertyUids = propertyUids;
|
||||||
|
this.nativeDataUid = nativeDataUid;
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
package it.cavallium.strangedb;
|
package it.cavallium.strangedb.java.objects;
|
||||||
|
|
||||||
import it.cavallium.strangedb.annotations.DbDataType;
|
import it.cavallium.strangedb.java.annotations.DbDataType;
|
||||||
import it.cavallium.strangedb.annotations.DbPrimitiveType;
|
import it.cavallium.strangedb.java.annotations.DbPrimitiveType;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
@ -41,15 +41,7 @@ public interface EnhancedObjectUpgrader {
|
|||||||
return getPrimitiveField(id, DbPrimitiveType.DOUBLE);
|
return getPrimitiveField(id, DbPrimitiveType.DOUBLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
Object getField(int id, DbDataType type, Supplier<Class<?>> enhancedClassType) throws IOException;
|
Object getField(int id, DbDataType type) throws IOException;
|
||||||
|
|
||||||
default Object getField(int id, DbDataType type) throws IOException {
|
Object getMethod(int id, DbDataType type) throws IOException;
|
||||||
return getField(id, type, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
Object getMethod(int id, DbDataType type, Supplier<Class<?>> enhancedClassType) throws IOException;
|
|
||||||
|
|
||||||
default Object getMethod(int id, DbDataType type) throws IOException {
|
|
||||||
return getField(id, type, null);
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
package it.cavallium.strangedb.java.objects.lists;
|
||||||
|
|
||||||
|
public enum ClassPosition {
|
||||||
|
PROPERTY,
|
||||||
|
FIELD,
|
||||||
|
PRIMITIVE_FIELD,
|
||||||
|
ENHANCED_LIST;
|
||||||
|
|
||||||
|
public static final int LIST_ALL = 0;
|
||||||
|
public static final int LIST_FIRST = 1;
|
||||||
|
public static final int LIST_LAST = 2;
|
||||||
|
public static final int LIST_POSITION = 3;
|
||||||
|
}
|
@ -0,0 +1,137 @@
|
|||||||
|
package it.cavallium.strangedb.java.objects.lists;
|
||||||
|
|
||||||
|
import it.cavallium.strangedb.functionalinterfaces.ConsumerWithIO;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CompletionException;
|
||||||
|
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||||
|
|
||||||
|
public class ElementsArrayList<T> implements ElementsList<T> {
|
||||||
|
|
||||||
|
ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(false);
|
||||||
|
|
||||||
|
private final ArrayList<T> list;
|
||||||
|
|
||||||
|
public ElementsArrayList() {
|
||||||
|
this.list = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ElementsArrayList(Collection<T> collection) {
|
||||||
|
this.list = new ArrayList<>(collection);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ElementsArrayList(int initialCapacity) {
|
||||||
|
this.list = new ArrayList<>(initialCapacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T get(int index) {
|
||||||
|
return list.get(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void forEachParallelUnsorted(ConsumerWithIO<T> action) throws IOException {
|
||||||
|
readWriteLock.readLock().lock();
|
||||||
|
try {
|
||||||
|
try {
|
||||||
|
list.parallelStream().forEach((item) -> {
|
||||||
|
try {
|
||||||
|
action.accept(item);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
throw new CompletionException(ex);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (CompletionException ex) {
|
||||||
|
throw new IOException(ex.getCause());
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
readWriteLock.readLock().unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void add(T value) {
|
||||||
|
readWriteLock.writeLock().lock();
|
||||||
|
try {
|
||||||
|
list.add(value);
|
||||||
|
} finally {
|
||||||
|
readWriteLock.writeLock().unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update(int index, T value) {
|
||||||
|
set(index, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void set(int index, T value) {
|
||||||
|
readWriteLock.writeLock().lock();
|
||||||
|
try {
|
||||||
|
list.set(index, value);
|
||||||
|
} finally {
|
||||||
|
readWriteLock.writeLock().unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void add(int index, T value) {
|
||||||
|
readWriteLock.writeLock().lock();
|
||||||
|
try {
|
||||||
|
list.add(index, value);
|
||||||
|
} finally {
|
||||||
|
readWriteLock.writeLock().unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T getLast() {
|
||||||
|
readWriteLock.readLock().lock();
|
||||||
|
try {
|
||||||
|
return list.get(list.size() - 1);
|
||||||
|
} finally {
|
||||||
|
readWriteLock.readLock().unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
readWriteLock.readLock().lock();
|
||||||
|
try {
|
||||||
|
return list.isEmpty();
|
||||||
|
} finally {
|
||||||
|
readWriteLock.readLock().unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
readWriteLock.readLock().lock();
|
||||||
|
try {
|
||||||
|
return list.size();
|
||||||
|
} finally {
|
||||||
|
readWriteLock.readLock().unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean contains(Object o) {
|
||||||
|
readWriteLock.readLock().lock();
|
||||||
|
try {
|
||||||
|
return list.contains(o);
|
||||||
|
} finally {
|
||||||
|
readWriteLock.readLock().unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArrayList<T> asList() {
|
||||||
|
readWriteLock.readLock().lock();
|
||||||
|
try {
|
||||||
|
return list;
|
||||||
|
} finally {
|
||||||
|
readWriteLock.readLock().unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
package it.cavallium.strangedb.java.objects.lists;
|
||||||
|
|
||||||
|
import it.cavallium.strangedb.functionalinterfaces.ConsumerWithIO;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
interface ElementsList<T> {
|
||||||
|
T get(int index) throws IOException;
|
||||||
|
void forEachParallelUnsorted(ConsumerWithIO<T> action) throws IOException;
|
||||||
|
void add(T value) throws IOException;
|
||||||
|
void update(int index, T value) throws IOException;
|
||||||
|
void set(int index, T value) throws IOException;
|
||||||
|
void add(int index, T value) throws IOException;
|
||||||
|
T getLast() throws IOException;
|
||||||
|
boolean isEmpty();
|
||||||
|
int size();
|
||||||
|
}
|
@ -0,0 +1,315 @@
|
|||||||
|
package it.cavallium.strangedb.java.objects.lists;
|
||||||
|
|
||||||
|
import it.cavallium.strangedb.java.annotations.DbDataType;
|
||||||
|
import it.cavallium.strangedb.java.annotations.DbField;
|
||||||
|
import it.cavallium.strangedb.java.database.IDatabaseTools;
|
||||||
|
import it.cavallium.strangedb.java.database.IObjectsIO;
|
||||||
|
import it.cavallium.strangedb.java.objects.EnhancedObject;
|
||||||
|
import it.cavallium.strangedb.java.objects.EnhancedObjectIndices;
|
||||||
|
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
||||||
|
import it.unimi.dsi.fastutil.objects.ObjectList;
|
||||||
|
import it.unimi.dsi.fastutil.objects.ObjectLists;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.CompletionException;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class EnhancedObjectStrangeDbList<T extends EnhancedObject> extends StrangeDbList<T> {
|
||||||
|
|
||||||
|
private static final int INDICES_REF_ID = 0;
|
||||||
|
|
||||||
|
@DbField(id = INDICES_REF_ID, type = DbDataType.REFERENCES_LIST, name = "indices")
|
||||||
|
private LongArrayList indices;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected LongArrayList getIndices() {
|
||||||
|
return indices;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EnhancedObjectStrangeDbList() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
public EnhancedObjectStrangeDbList(IDatabaseTools databaseTools, Class<T> type) throws IOException {
|
||||||
|
this(databaseTools);
|
||||||
|
}
|
||||||
|
|
||||||
|
public EnhancedObjectStrangeDbList(IDatabaseTools databaseTools) throws IOException {
|
||||||
|
super(databaseTools);
|
||||||
|
indices = new LongArrayList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected T loadItem(long uid) throws IOException {
|
||||||
|
return databaseTools.getObjectsIO().loadEnhancedObject(uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void writeItemToDisk(long uid, T item) throws IOException {
|
||||||
|
databaseTools.getObjectsIO().setEnhancedObject(uid, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ElementsArrayList<EnhancedObjectIndices> queryUids(ListQuery query) throws IOException {
|
||||||
|
ElementsArrayList<EnhancedObjectIndices> uids = executeQuery(query, this, databaseTools.getObjectsIO());
|
||||||
|
return uids;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ElementsArrayList<T> query(ListQuery query) throws IOException {
|
||||||
|
ElementsArrayList<EnhancedObjectIndices> uids = queryUids(query);
|
||||||
|
ElementsArrayList<T> elements = new ElementsArrayList<>(uids.size());
|
||||||
|
uids.forEachParallelUnsorted((uid) -> elements.add(databaseTools.getObjectsIO().loadEnhancedObject(uid.objectUid)));
|
||||||
|
return elements;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ElementsArrayList<EnhancedObjectIndices> executeQuery(ListQuery query, ElementsList<?> inputList, IObjectsIO dbio) throws IOException {
|
||||||
|
if (query instanceof ListQuery.ListQueryElement) {
|
||||||
|
return executeQueryElement((ListQuery.ListQueryElement) query, inputList, dbio);
|
||||||
|
} else if (query instanceof ListQuery.ListQueryAnd) {
|
||||||
|
ListQuery[] children = ((ListQuery.ListQueryAnd) query).getQueryChildren();
|
||||||
|
ElementsArrayList<EnhancedObjectIndices> results = null;
|
||||||
|
for (ListQuery childQuery : children) {
|
||||||
|
results = executeQuery(childQuery, results == null ? inputList : results, dbio);
|
||||||
|
if (results.size() == 0) break;
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
} else if (query instanceof ListQuery.ListQueryOr) {
|
||||||
|
ListQuery[] children = ((ListQuery.ListQueryOr) query).getQueryChildren();
|
||||||
|
ElementsArrayList<EnhancedObjectIndices> results = new ElementsArrayList<>();
|
||||||
|
for (ListQuery childQuery : children) {
|
||||||
|
ElementsArrayList<EnhancedObjectIndices> childResults = executeQuery(childQuery, inputList, dbio);
|
||||||
|
int childResultsSize = childResults.size();
|
||||||
|
if (childResultsSize == inputList.size()) {
|
||||||
|
return childResults;
|
||||||
|
}
|
||||||
|
addMissingElements(results, childResults);
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException("Not implemented!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void addMissingElements(ElementsArrayList<EnhancedObjectIndices> main, ElementsList<EnhancedObjectIndices> elementsToAdd) throws IOException {
|
||||||
|
int elementsSize = elementsToAdd.size();
|
||||||
|
for (int i = 0; i < elementsSize; i++) {
|
||||||
|
EnhancedObjectIndices childResultElement = elementsToAdd.get(i);
|
||||||
|
if (!main.contains(childResultElement)) {
|
||||||
|
main.add(childResultElement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is slow
|
||||||
|
*
|
||||||
|
* @param elementsList
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
static ElementsArrayList<Long> toElementsArrayList(ElementsList<?> elementsList) {
|
||||||
|
if (elementsList instanceof EnhancedObjectStrangeDbList<?>) {
|
||||||
|
return new ElementsArrayList<>(((EnhancedObjectStrangeDbList) elementsList).getIndices());
|
||||||
|
} else if (elementsList instanceof ElementsArrayList<?>) {
|
||||||
|
return (ElementsArrayList<Long>) elementsList;
|
||||||
|
} else {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private static ElementsArrayList<EnhancedObjectIndices> executeQueryElement(ListQuery.ListQueryElement query, ElementsList<?> inputList, IObjectsIO dbio) throws IOException {
|
||||||
|
ElementsArrayList<EnhancedObjectIndices> results = new ElementsArrayList<>();
|
||||||
|
if (inputList instanceof EnhancedObjectStrangeDbList<?>) {
|
||||||
|
EnhancedObjectStrangeDbList<?> dbList = ((EnhancedObjectStrangeDbList<?>) inputList);
|
||||||
|
dbList.forEachIndexParallelUnsorted((Long elementUid) -> {
|
||||||
|
EnhancedObjectIndices elementUids = dbio.loadEnhancedObjectUids(elementUid); // check if the parent object is the declared type
|
||||||
|
if (elementUids != null) {
|
||||||
|
Class<?> declaredRootType = query.valuePointer.getRootType();
|
||||||
|
Class<?> obtainedRootType = elementUids.type;
|
||||||
|
if (isInstanceOf(obtainedRootType, declaredRootType)) {
|
||||||
|
List<Object> result = resolveItemFromDb(query.valuePointer, dbio, elementUids);
|
||||||
|
if (result.size() == 1) {
|
||||||
|
if (query.valueOperation.evaluate(result.get(0))) {
|
||||||
|
results.add(elementUids);
|
||||||
|
}
|
||||||
|
} else if (result.size() > 1) {
|
||||||
|
if (result.parallelStream().anyMatch(query.valueOperation::evaluate)) {
|
||||||
|
results.add(elementUids);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//todo: use logging api
|
||||||
|
System.err.println(obtainedRootType.getSimpleName() + " is not instance of " + declaredRootType.getSimpleName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (inputList instanceof ElementsArrayList<?>) {
|
||||||
|
ElementsArrayList<EnhancedObjectIndices> elementsUids = ((ElementsArrayList<EnhancedObjectIndices>) inputList);
|
||||||
|
elementsUids.forEachParallelUnsorted((elementUid) -> {
|
||||||
|
List<Object> result = resolveItemFromDb(query.valuePointer, dbio, elementUid);
|
||||||
|
if (result.parallelStream().anyMatch(query.valueOperation::evaluate)) {
|
||||||
|
results.add(elementUid);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<Object> resolveItemFromDb(ValuePointer pointer, IObjectsIO objectsIO, EnhancedObjectIndices element) throws IOException {
|
||||||
|
return resolveItemFromDbLoop(element, pointer, 0, pointer.size(), objectsIO, element);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private static List<Object> resolveItemFromDbLoop(@NotNull EnhancedObjectIndices currentElement, ValuePointer pointer, int pointerPosition, int pointerSize, IObjectsIO objectsIO, EnhancedObjectIndices element) throws IOException {
|
||||||
|
if (pointerPosition > 0) {
|
||||||
|
// check if the object that we obtained is the declared type
|
||||||
|
Class<?> declaredType = pointer.resetPointerTo(pointerPosition - 1).getAdditionalData();
|
||||||
|
Class<?> obtainedType = currentElement.type;
|
||||||
|
if (!isInstanceOf(obtainedType, declaredType)) {
|
||||||
|
return ObjectLists.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pointerPosition >= pointerSize) {
|
||||||
|
throw new IOException("The pointer is empty");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isLastElement = pointerPosition >= pointer.size() - 1;
|
||||||
|
ValuePointer currentPointer = pointer.resetPointerTo(pointerPosition);
|
||||||
|
|
||||||
|
int pathNumber = currentPointer.getPathNumber();
|
||||||
|
ClassPosition valueType = currentPointer.getPathType();
|
||||||
|
List<EnhancedObjectIndices> multipleCurrentElements = null;
|
||||||
|
|
||||||
|
switch (valueType) {
|
||||||
|
case ENHANCED_LIST: {
|
||||||
|
switch (currentPointer.getPathNumber()) {
|
||||||
|
case ClassPosition.LIST_ALL: {
|
||||||
|
if (isLastElement) {
|
||||||
|
try {
|
||||||
|
return objectsIO.loadReferencesList(currentElement.fieldUids[INDICES_REF_ID]).parallelStream().map((reference) -> {
|
||||||
|
try {
|
||||||
|
return objectsIO.loadEnhancedObject(reference);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new CompletionException(e);
|
||||||
|
}
|
||||||
|
}).filter(Objects::nonNull).collect(Collectors.toList());
|
||||||
|
} catch (CompletionException e) {
|
||||||
|
throw new IOException(e.getCause());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LongArrayList refList = objectsIO.loadReferencesList(currentElement.fieldUids[INDICES_REF_ID]);
|
||||||
|
try {
|
||||||
|
multipleCurrentElements = refList.parallelStream().map((reference) -> {
|
||||||
|
try {
|
||||||
|
return objectsIO.loadEnhancedObjectUids(reference);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new CompletionException(e);
|
||||||
|
}
|
||||||
|
}).collect(Collectors.toList());
|
||||||
|
} catch (CompletionException e) {
|
||||||
|
throw new IOException(e.getCause());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ClassPosition.LIST_FIRST: {
|
||||||
|
try {
|
||||||
|
Long firstElementReference = objectsIO.loadReferencesListFirstElement(currentElement.fieldUids[INDICES_REF_ID]);
|
||||||
|
if (firstElementReference == null) {
|
||||||
|
return ObjectLists.emptyList();
|
||||||
|
} else {
|
||||||
|
currentElement = objectsIO.loadEnhancedObjectUids(firstElementReference);
|
||||||
|
}
|
||||||
|
} catch (CompletionException e) {
|
||||||
|
throw new IOException(e.getCause());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ClassPosition.LIST_LAST: {
|
||||||
|
try {
|
||||||
|
Long firstElementReference = objectsIO.loadReferencesListLastElement(currentElement.fieldUids[INDICES_REF_ID]);
|
||||||
|
if (firstElementReference == null) {
|
||||||
|
return ObjectLists.emptyList();
|
||||||
|
} else {
|
||||||
|
currentElement = objectsIO.loadEnhancedObjectUids(firstElementReference);
|
||||||
|
}
|
||||||
|
} catch (CompletionException e) {
|
||||||
|
throw new IOException(e.getCause());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new IOException("Unknown class position");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case FIELD: {
|
||||||
|
if (isLastElement) {
|
||||||
|
//TODO: getBlock field data type. it can be an enhancedObject or an object
|
||||||
|
Object loadedField = objectsIO.loadObject(currentElement.fieldUids[pathNumber]);
|
||||||
|
if (loadedField == null) return ObjectLists.emptyList();
|
||||||
|
return ObjectLists.singleton(loadedField);
|
||||||
|
} else {
|
||||||
|
currentElement = objectsIO.loadEnhancedObjectUids(currentElement.fieldUids[pathNumber]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case PRIMITIVE_FIELD: {
|
||||||
|
if (isLastElement) {
|
||||||
|
//TODO: getBlock primitive type
|
||||||
|
Object loadedField = objectsIO.loadPrimitiveData(currentElement.nativeDataUid).getLong(pathNumber);
|
||||||
|
return ObjectLists.singleton(loadedField);
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("You can access to a type field only in the last pointer");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case PROPERTY: {
|
||||||
|
if (isLastElement) {
|
||||||
|
//TODO: getBlock field data type. it can be an enhancedObject or an object
|
||||||
|
Object loadedProperty = objectsIO.loadObject(currentElement.fieldUids[pathNumber]);
|
||||||
|
if (loadedProperty == null) return ObjectLists.emptyList();
|
||||||
|
return ObjectLists.singleton(loadedProperty);
|
||||||
|
} else {
|
||||||
|
currentElement = objectsIO.loadEnhancedObjectUids(currentElement.propertyUids[pathNumber]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
throw new IllegalArgumentException("Not implemented");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (multipleCurrentElements == null) {
|
||||||
|
if (currentElement == null) return ObjectLists.emptyList();
|
||||||
|
return resolveItemFromDbLoop(currentElement, pointer, pointerPosition + 1, pointerSize, objectsIO, element);
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
return multipleCurrentElements
|
||||||
|
.parallelStream()
|
||||||
|
.map((elem) -> {
|
||||||
|
try {
|
||||||
|
return resolveItemFromDbLoop(elem, pointer, pointerPosition + 1, pointerSize, objectsIO, element);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new CompletionException(e);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.flatMap(List::stream)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
} catch (CompletionException ex) {
|
||||||
|
throw new IOException(ex.getCause());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static boolean isInstanceOf(Class<?> clazz, Class<?> obj) {
|
||||||
|
return obj.isAssignableFrom(clazz);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,74 @@
|
|||||||
|
package it.cavallium.strangedb.java.objects.lists;
|
||||||
|
|
||||||
|
public class KMP {
|
||||||
|
public static int KMP(CharSequence content, CharSequence stringToFind, boolean ignoreCase) {
|
||||||
|
int[] failureTable = failureTable(ignoreCase, stringToFind);
|
||||||
|
|
||||||
|
int targetPointer = 0; // current char in target string
|
||||||
|
int searchPointer = 0; // current char in search string
|
||||||
|
|
||||||
|
while (searchPointer < content.length()) { // while there is more to search with, keep searching
|
||||||
|
if (charEquals(ignoreCase, content.charAt(searchPointer), stringToFind.charAt(targetPointer))) { // case 1
|
||||||
|
// found current char in targetPointer in search string
|
||||||
|
targetPointer++;
|
||||||
|
if (targetPointer == stringToFind.length()) { // found all characters
|
||||||
|
int x = stringToFind.length() + 1;
|
||||||
|
return searchPointer - x; // return starting index of found target inside searched string
|
||||||
|
}
|
||||||
|
searchPointer++; // move forward if not found target string
|
||||||
|
} else if (targetPointer > 0) { // case 2
|
||||||
|
// use failureTable to use pointer pointed resetPointerTo nearest location of usable string prefix
|
||||||
|
targetPointer = failureTable[targetPointer];
|
||||||
|
} else { // case 3
|
||||||
|
// targetPointer is pointing resetPointerTo state 0, so restart search with current searchPointer index
|
||||||
|
searchPointer++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an int[] that points to last valid string prefix, given target string
|
||||||
|
*/
|
||||||
|
public static int[] failureTable(boolean ignoreCase, CharSequence target) {
|
||||||
|
int[] table = new int[target.length() + 1];
|
||||||
|
// state 0 and 1 are guarenteed be the prior
|
||||||
|
table[0] = -1;
|
||||||
|
table[1] = 0;
|
||||||
|
|
||||||
|
// the pointers pointing resetPointerTo last failure and current satte
|
||||||
|
int left = 0;
|
||||||
|
int right = 2;
|
||||||
|
|
||||||
|
while (right < table.length) { // RIGHT NEVER MOVES RIGHT UNTIL ASSIGNED A VALID POINTER
|
||||||
|
if (charEquals(ignoreCase, target.charAt(right - 1), target.charAt(left))) { // when both chars before left and right are equal, link both and move both forward
|
||||||
|
left++;
|
||||||
|
table[right] = left;
|
||||||
|
right++;
|
||||||
|
} else if (left > 0) { // if left isn't resetPointerTo the very beginning, then send left backward
|
||||||
|
// by following the already set pointer to where it is pointing to
|
||||||
|
left = table[left];
|
||||||
|
} else { // left has fallen all the way back to the beginning
|
||||||
|
table[right] = left;
|
||||||
|
right++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return table;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean charEquals(boolean ignoreCase, char char1, char char2) {
|
||||||
|
if (ignoreCase) {
|
||||||
|
if (char1 >= 65 && char1 <= 90) {
|
||||||
|
if (char2 >= 97 && char2 <= 122) {
|
||||||
|
return char1 == char2 - 32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (char2 >= 65 && char2 <= 90) {
|
||||||
|
if (char1 >= 97 && char1 <= 122) {
|
||||||
|
return char2 == char1 - 32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return char1 == char2;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,105 @@
|
|||||||
|
package it.cavallium.strangedb.java.objects.lists;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public abstract class ListQuery {
|
||||||
|
|
||||||
|
public static ListQuery create(ValuePointer valuePointer, ValueOperation valueOperation) {
|
||||||
|
return new ListQueryElement(valuePointer, valueOperation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ListQuery and(ValuePointer valuePointer, ValueOperation operation) {
|
||||||
|
return new ListQueryAnd(this, create(valuePointer, operation));
|
||||||
|
}
|
||||||
|
|
||||||
|
public ListQuery and(ListQuery query) {
|
||||||
|
return new ListQueryAnd(this, query);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ListQuery or(ValuePointer valuePointer, ValueOperation operation) {
|
||||||
|
return new ListQueryOr(this, create(valuePointer, operation));
|
||||||
|
}
|
||||||
|
|
||||||
|
public ListQuery or(ListQuery query) {
|
||||||
|
return new ListQueryOr(this, query);
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract List<ListQueryElement> getQueryElementsRecursively();
|
||||||
|
|
||||||
|
static class ListQueryElement extends ListQuery {
|
||||||
|
public final ValuePointer valuePointer;
|
||||||
|
public final ValueOperation valueOperation;
|
||||||
|
|
||||||
|
private ListQueryElement(ValuePointer valuePointer, ValueOperation valueOperation) {
|
||||||
|
this.valuePointer = valuePointer;
|
||||||
|
this.valueOperation = valueOperation;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
List<ListQueryElement> getQueryElementsRecursively() {
|
||||||
|
return Collections.singletonList(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class ListQueryCollection extends ListQuery {
|
||||||
|
protected ListQuery[] queries;
|
||||||
|
|
||||||
|
private ListQueryCollection(ListQuery... queries) {
|
||||||
|
this.queries = queries;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
List<ListQueryElement> getQueryElementsRecursively() {
|
||||||
|
List<ListQueryElement> elements = new ArrayList<>();
|
||||||
|
for (ListQuery query : queries) {
|
||||||
|
elements.addAll(query.getQueryElementsRecursively());
|
||||||
|
}
|
||||||
|
return elements;
|
||||||
|
}
|
||||||
|
|
||||||
|
ListQuery[] getQueryChildren() {
|
||||||
|
return queries.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class ListQueryAnd extends ListQueryCollection {
|
||||||
|
private ListQueryAnd(ListQuery... queries) {
|
||||||
|
super(queries);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ListQuery and(ListQuery query) {
|
||||||
|
return new ListQueryAnd(append(this.queries, query));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ListQuery and(ValuePointer valuePointer, ValueOperation operation) {
|
||||||
|
return and(create(valuePointer, operation));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class ListQueryOr extends ListQueryCollection {
|
||||||
|
private ListQueryOr(ListQuery... queries) {
|
||||||
|
super(queries);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ListQuery or(ListQuery query) {
|
||||||
|
return new ListQueryOr(append(this.queries, query));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ListQuery or(ValuePointer valuePointer, ValueOperation operation) {
|
||||||
|
return or(create(valuePointer, operation));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T> T[] append(T[] array, T value) {
|
||||||
|
T[] newArray = Arrays.copyOf(array, array.length + 1);
|
||||||
|
newArray[array.length] = value;
|
||||||
|
return newArray;
|
||||||
|
}
|
||||||
|
}
|
@ -1,13 +1,13 @@
|
|||||||
package it.cavallium.strangedb.lists;
|
package it.cavallium.strangedb.java.objects.lists;
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
||||||
import it.cavallium.strangedb.database.IDatabaseTools;
|
import it.cavallium.strangedb.java.database.IDatabaseTools;
|
||||||
import it.cavallium.strangedb.annotations.DbDataType;
|
import it.cavallium.strangedb.java.annotations.DbDataType;
|
||||||
import it.cavallium.strangedb.annotations.DbField;
|
import it.cavallium.strangedb.java.annotations.DbField;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
public class ObjectStrandeDbList<T> extends StrandeDbList<T> {
|
public class ObjectStrangeDbList<T> extends StrangeDbList<T> {
|
||||||
|
|
||||||
@DbField(id = 0, type = DbDataType.REFERENCES_LIST)
|
@DbField(id = 0, type = DbDataType.REFERENCES_LIST)
|
||||||
private LongArrayList indices;
|
private LongArrayList indices;
|
||||||
@ -17,11 +17,11 @@ public class ObjectStrandeDbList<T> extends StrandeDbList<T> {
|
|||||||
return indices;
|
return indices;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ObjectStrandeDbList() {
|
public ObjectStrangeDbList() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ObjectStrandeDbList(IDatabaseTools databaseTools) throws IOException {
|
public ObjectStrangeDbList(IDatabaseTools databaseTools) throws IOException {
|
||||||
super(databaseTools);
|
super(databaseTools);
|
||||||
indices = new LongArrayList();
|
indices = new LongArrayList();
|
||||||
}
|
}
|
@ -0,0 +1,203 @@
|
|||||||
|
package it.cavallium.strangedb.java.objects.lists;
|
||||||
|
|
||||||
|
import it.cavallium.strangedb.VariableWrapper;
|
||||||
|
import it.cavallium.strangedb.functionalinterfaces.ConsumerWithIO;
|
||||||
|
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
||||||
|
import it.cavallium.strangedb.java.objects.EnhancedObject;
|
||||||
|
import it.cavallium.strangedb.java.database.IDatabaseTools;
|
||||||
|
|
||||||
|
import java.io.IOError;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.StringJoiner;
|
||||||
|
import java.util.concurrent.*;
|
||||||
|
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public abstract class StrangeDbList<T> extends EnhancedObject implements ElementsList<T> {
|
||||||
|
|
||||||
|
private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(false);
|
||||||
|
|
||||||
|
protected abstract LongArrayList getIndices();
|
||||||
|
|
||||||
|
public StrangeDbList() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public StrangeDbList(IDatabaseTools databaseTools) throws IOException {
|
||||||
|
super(databaseTools);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T get(int index) throws IOException {
|
||||||
|
readWriteLock.readLock().lock();
|
||||||
|
try {
|
||||||
|
long uid = getIndices().getLong(index);
|
||||||
|
return loadItem(uid);
|
||||||
|
} finally {
|
||||||
|
readWriteLock.readLock().unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void add(T value) throws IOException {
|
||||||
|
long uid = databaseTools.getObjectsIO().newNullObject();
|
||||||
|
readWriteLock.writeLock().lock();
|
||||||
|
try {
|
||||||
|
getIndices().add(uid);
|
||||||
|
writeItemToDisk(uid, value);
|
||||||
|
} finally {
|
||||||
|
readWriteLock.writeLock().unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void forEachParallelUnsorted(ConsumerWithIO<T> action) throws IOException {
|
||||||
|
readWriteLock.readLock().lock();
|
||||||
|
try {
|
||||||
|
forEachParallelUnsorted_(action);
|
||||||
|
} finally {
|
||||||
|
readWriteLock.readLock().unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void forEachParallelUnsorted_(ConsumerWithIO<T> action) throws IOException {
|
||||||
|
try {
|
||||||
|
int size = size();
|
||||||
|
ExecutorService executorService = Executors.newFixedThreadPool(ForkJoinPool.getCommonPoolParallelism(), (r) -> new Thread(r, "DBList parallel foreach"));
|
||||||
|
VariableWrapper<IOException> exceptionVariableWrapper = new VariableWrapper<>(null);
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
final int index = i;
|
||||||
|
executorService.execute(() -> {
|
||||||
|
try {
|
||||||
|
T t = get(index);
|
||||||
|
action.accept(t);
|
||||||
|
} catch (IOException e) {
|
||||||
|
if (exceptionVariableWrapper.var == null) exceptionVariableWrapper.var = e;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
executorService.shutdown();
|
||||||
|
executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
|
||||||
|
if (exceptionVariableWrapper.var != null) {
|
||||||
|
throw exceptionVariableWrapper.var;
|
||||||
|
}
|
||||||
|
executorService.shutdownNow();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
throw new IOException(e);
|
||||||
|
} catch (CompletionException e) {
|
||||||
|
throw new IOException(e.getCause());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void forEachIndexParallelUnsorted(ConsumerWithIO<Long> action) throws IOException {
|
||||||
|
readWriteLock.readLock().lock();
|
||||||
|
try {
|
||||||
|
forEachIndexParallelUnsorted_(action);
|
||||||
|
} finally {
|
||||||
|
readWriteLock.readLock().unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void forEachIndexParallelUnsorted_(ConsumerWithIO<Long> action) throws IOException {
|
||||||
|
try {
|
||||||
|
this.getIndices().parallelStream().forEach((id) -> {
|
||||||
|
try {
|
||||||
|
action.accept(id);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new CompletionException(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (CompletionException ex) {
|
||||||
|
throw new IOException(ex.getCause());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void forEach(ConsumerWithIO<T> action) throws IOException {
|
||||||
|
readWriteLock.readLock().lock();
|
||||||
|
try {
|
||||||
|
forEach_(action);
|
||||||
|
} finally {
|
||||||
|
readWriteLock.readLock().unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void forEach_(ConsumerWithIO<T> action) throws IOException {
|
||||||
|
int size = size();
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
final int index = i;
|
||||||
|
T value = get(index);
|
||||||
|
action.accept(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update(int index, T value) throws IOException {
|
||||||
|
set(index, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void set(int index, T value) throws IOException {
|
||||||
|
long uid = databaseTools.getObjectsIO().newNullObject();
|
||||||
|
readWriteLock.writeLock().lock();
|
||||||
|
try {
|
||||||
|
getIndices().set(index, uid);
|
||||||
|
writeItemToDisk(uid, value);
|
||||||
|
} finally {
|
||||||
|
readWriteLock.writeLock().unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void add(int index, T value) throws IOException {
|
||||||
|
long uid = databaseTools.getObjectsIO().newNullObject();
|
||||||
|
readWriteLock.writeLock().lock();
|
||||||
|
try {
|
||||||
|
getIndices().add(index, uid);
|
||||||
|
writeItemToDisk(uid, value);
|
||||||
|
} finally {
|
||||||
|
readWriteLock.writeLock().unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public T getLast() throws IOException {
|
||||||
|
readWriteLock.readLock().lock();
|
||||||
|
try {
|
||||||
|
if (getIndices().size() > 0) {
|
||||||
|
return get(getIndices().size() - 1);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
readWriteLock.readLock().unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEmpty() {
|
||||||
|
readWriteLock.readLock().lock();
|
||||||
|
try {
|
||||||
|
return getIndices().size() <= 0;
|
||||||
|
} finally {
|
||||||
|
readWriteLock.readLock().unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int size() {
|
||||||
|
readWriteLock.readLock().lock();
|
||||||
|
try {
|
||||||
|
return getIndices().size();
|
||||||
|
} finally {
|
||||||
|
readWriteLock.readLock().unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract T loadItem(long uid) throws IOException;
|
||||||
|
|
||||||
|
protected abstract void writeItemToDisk(long uid, T item) throws IOException;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return new StringJoiner(", ", StrangeDbList.class.getSimpleName() + "[", "]")
|
||||||
|
.add(getIndices().size() + " items")
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
package it.cavallium.strangedb.java.objects.lists;
|
||||||
|
|
||||||
|
public interface ValueOperation<T> {
|
||||||
|
public boolean evaluate(Object value);
|
||||||
|
}
|
@ -0,0 +1,211 @@
|
|||||||
|
package it.cavallium.strangedb.java.objects.lists;
|
||||||
|
|
||||||
|
import it.cavallium.strangedb.java.annotations.DbField;
|
||||||
|
import it.cavallium.strangedb.java.annotations.DbPrimitiveField;
|
||||||
|
import it.cavallium.strangedb.java.annotations.DbProperty;
|
||||||
|
import it.cavallium.strangedb.java.objects.EnhancedObject;
|
||||||
|
import org.apache.commons.lang3.reflect.FieldUtils;
|
||||||
|
import org.apache.commons.lang3.reflect.MethodUtils;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
public class ValuePointer {
|
||||||
|
|
||||||
|
private static ValuePointer base = new ValuePointer(new int[0], new ClassPosition[0], new Object[0], null);
|
||||||
|
|
||||||
|
private final int[] pathNumbers;
|
||||||
|
private final ClassPosition[] pathTypes;
|
||||||
|
/**
|
||||||
|
* PropertyType
|
||||||
|
*/
|
||||||
|
private final Object[] additionalData;
|
||||||
|
/**
|
||||||
|
* ParentType
|
||||||
|
*/
|
||||||
|
private final Class<?> rootType;
|
||||||
|
|
||||||
|
private ValuePointer(int[] pathNumbers, ClassPosition[] pathTypes, Object[] additionalData, Class<?> rootType) {
|
||||||
|
this.pathNumbers = pathNumbers;
|
||||||
|
this.pathTypes = pathTypes;
|
||||||
|
this.additionalData = additionalData;
|
||||||
|
this.rootType = rootType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T extends EnhancedObject> ValuePointer ofField(Class<T> parentType, String field) {
|
||||||
|
return base.field(parentType, field);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T extends EnhancedObject> ValuePointer ofProp(Class<T> parentType, String property) {
|
||||||
|
return base.prop(parentType, property);
|
||||||
|
}
|
||||||
|
public static <T extends EnhancedObject> ValuePointer ofPrimitiveField(Class<T> parentType, String primitiveFieldName) {
|
||||||
|
return base.primitiveField(parentType, primitiveFieldName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T extends EnhancedObject> ValuePointer prop(Class<T> parentType, String propertyName) {
|
||||||
|
|
||||||
|
ClassPosition[] newPathTypes = append(this.pathTypes, ClassPosition.PROPERTY);
|
||||||
|
|
||||||
|
Method[] methods = MethodUtils.getMethodsWithAnnotation(parentType, DbProperty.class);
|
||||||
|
DbProperty dbProperty = null;
|
||||||
|
Class<?> propType = null;
|
||||||
|
for (Method method : methods) {
|
||||||
|
DbProperty annotation = method.getAnnotation(DbProperty.class);
|
||||||
|
if (annotation.name().equals(propertyName)) {
|
||||||
|
propType = method.getReturnType();
|
||||||
|
dbProperty = annotation;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (dbProperty == null) {
|
||||||
|
throw new IllegalArgumentException("Property '" + propertyName + "' not found in class " + parentType.getSimpleName());
|
||||||
|
}
|
||||||
|
|
||||||
|
int[] newPathNumbers = append(this.pathNumbers, dbProperty.id());
|
||||||
|
Object[] newAdditionalData = append(this.additionalData, propType);
|
||||||
|
Class<?> rootType = this.rootType == null ? parentType : this.rootType;
|
||||||
|
if (newAdditionalData.length > 1) {
|
||||||
|
newAdditionalData[newAdditionalData.length - 2] = parentType;
|
||||||
|
}
|
||||||
|
return new ValuePointer(newPathNumbers, newPathTypes, newAdditionalData, rootType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T extends EnhancedObject> ValuePointer field(Class<T> parentType, String fieldName) {
|
||||||
|
ClassPosition[] newPathTypes = append(this.pathTypes, ClassPosition.FIELD);
|
||||||
|
|
||||||
|
Field[] fields = FieldUtils.getFieldsWithAnnotation(parentType, DbField.class);
|
||||||
|
DbField dbField = null;
|
||||||
|
Class<?> fieldType = null;
|
||||||
|
for (Field field : fields) {
|
||||||
|
DbField annotation = field.getAnnotation(DbField.class);
|
||||||
|
if (annotation.name().equals(fieldName)) {
|
||||||
|
fieldType = field.getType();
|
||||||
|
dbField = annotation;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (dbField == null) {
|
||||||
|
throw new IllegalArgumentException("Field '" + fieldName + "' not found in class " + parentType.getSimpleName());
|
||||||
|
}
|
||||||
|
|
||||||
|
int[] newPathNumbers = append(this.pathNumbers, dbField.id());
|
||||||
|
Object[] newAdditionalData = append(this.additionalData, fieldType);
|
||||||
|
if (newAdditionalData.length > 1) {
|
||||||
|
newAdditionalData[newAdditionalData.length - 2] = parentType;
|
||||||
|
}
|
||||||
|
Class<?> rootType = this.rootType == null ? parentType : this.rootType;
|
||||||
|
return new ValuePointer(newPathNumbers, newPathTypes, newAdditionalData, rootType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T extends EnhancedObjectStrangeDbList<?>> ValuePointer allInList(Class<T> parentType) {
|
||||||
|
return inList(parentType, ClassPosition.LIST_ALL, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T extends EnhancedObjectStrangeDbList<?>> ValuePointer firstInList(Class<T> parentType) {
|
||||||
|
return inList(parentType, ClassPosition.LIST_FIRST, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T extends EnhancedObjectStrangeDbList<?>> ValuePointer lastInList(Class<T> parentType) {
|
||||||
|
return inList(parentType, ClassPosition.LIST_LAST, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T extends EnhancedObjectStrangeDbList<?>> ValuePointer indexInList(Class<T> parentType, int position) {
|
||||||
|
return inList(parentType, ClassPosition.LIST_POSITION, position);
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T extends EnhancedObjectStrangeDbList<?>> ValuePointer inList(Class<T> parentType, int listPosition, int itemIndex) {
|
||||||
|
ClassPosition[] newPathTypes = append(this.pathTypes, ClassPosition.ENHANCED_LIST);
|
||||||
|
|
||||||
|
int[] newPathNumbers = append(this.pathNumbers, listPosition);
|
||||||
|
Object[] newAdditionalData = append(this.additionalData, itemIndex);
|
||||||
|
if (newAdditionalData.length > 1) {
|
||||||
|
newAdditionalData[newAdditionalData.length - 2] = parentType;
|
||||||
|
}
|
||||||
|
Class<?> rootType = this.rootType == null ? parentType : this.rootType;
|
||||||
|
return new ValuePointer(newPathNumbers, newPathTypes, newAdditionalData, rootType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T extends EnhancedObject> ValuePointer primitiveField(Class<T> parentType, String primitiveFieldName) {
|
||||||
|
ClassPosition[] newPathTypes = append(this.pathTypes, ClassPosition.PRIMITIVE_FIELD);
|
||||||
|
|
||||||
|
Field[] fields = FieldUtils.getFieldsWithAnnotation(parentType, DbPrimitiveField.class);
|
||||||
|
DbPrimitiveField dbField = null;
|
||||||
|
Class<?> fieldType = null;
|
||||||
|
for (Field field : fields) {
|
||||||
|
DbPrimitiveField annotation = field.getAnnotation(DbPrimitiveField.class);
|
||||||
|
if (annotation.name().equals(primitiveFieldName)) {
|
||||||
|
fieldType = field.getType();
|
||||||
|
dbField = annotation;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (dbField == null) {
|
||||||
|
throw new IllegalArgumentException("Field '" + primitiveFieldName + "' not found in class " + parentType.getSimpleName());
|
||||||
|
}
|
||||||
|
|
||||||
|
int[] newPathNumbers = append(this.pathNumbers, dbField.id());
|
||||||
|
Object[] newAdditionalData = append(this.additionalData, fieldType);
|
||||||
|
if (newAdditionalData.length > 1) {
|
||||||
|
newAdditionalData[newAdditionalData.length - 2] = parentType;
|
||||||
|
}
|
||||||
|
Class<?> rootType = this.rootType == null ? parentType : this.rootType;
|
||||||
|
return new ValuePointer(newPathNumbers, newPathTypes, newAdditionalData, rootType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValuePointer resetPointerTo(int index) {
|
||||||
|
if (index >= pathNumbers.length) {
|
||||||
|
throw new ArrayIndexOutOfBoundsException();
|
||||||
|
}
|
||||||
|
if (index == pathNumbers.length - 1) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
return new ValuePointer(Arrays.copyOf(this.pathNumbers, index + 1),
|
||||||
|
Arrays.copyOf(this.pathTypes, index + 1),
|
||||||
|
Arrays.copyOf(this.additionalData, index + 1),
|
||||||
|
rootType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int size() {
|
||||||
|
return this.pathNumbers.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasPrevious() {
|
||||||
|
return pathNumbers.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValuePointer previous() {
|
||||||
|
return resetPointerTo(pathNumbers.length - 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int[] append(int[] array, int value) {
|
||||||
|
int[] newArray = Arrays.copyOf(array, array.length + 1);
|
||||||
|
newArray[array.length] = value;
|
||||||
|
return newArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T> T[] append(T[] array, T value) {
|
||||||
|
T[] newArray = Arrays.copyOf(array, array.length + 1);
|
||||||
|
newArray[array.length] = value;
|
||||||
|
return newArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPathNumber() {
|
||||||
|
return pathNumbers[pathNumbers.length - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClassPosition getPathType() {
|
||||||
|
return pathTypes[pathTypes.length - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T> T getAdditionalData() {
|
||||||
|
return (T) additionalData[additionalData.length - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class<?> getRootType() {
|
||||||
|
return rootType;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
package it.cavallium.strangedb.java.objects.lists.operations;
|
||||||
|
|
||||||
|
import it.cavallium.strangedb.java.objects.lists.KMP;
|
||||||
|
import it.cavallium.strangedb.java.objects.lists.ValueOperation;
|
||||||
|
|
||||||
|
public class Contains<T extends CharSequence> implements ValueOperation<T> {
|
||||||
|
|
||||||
|
private final T containsValue;
|
||||||
|
|
||||||
|
private Contains(T containsValue) {
|
||||||
|
this.containsValue = containsValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T extends CharSequence> Contains<T> containsValue(T value) {
|
||||||
|
return new Contains<T>(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean evaluate(Object value) {
|
||||||
|
if (value instanceof CharSequence) {
|
||||||
|
return KMP.KMP((CharSequence) value, containsValue, false) != -1;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
package it.cavallium.strangedb.java.objects.lists.operations;
|
||||||
|
|
||||||
|
import it.cavallium.strangedb.java.objects.lists.KMP;
|
||||||
|
import it.cavallium.strangedb.java.objects.lists.ValueOperation;
|
||||||
|
|
||||||
|
public class ContainsIgnoreCase implements ValueOperation<String> {
|
||||||
|
|
||||||
|
private final CharSequence containsValue;
|
||||||
|
|
||||||
|
private ContainsIgnoreCase(CharSequence containsValue) {
|
||||||
|
this.containsValue = containsValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ContainsIgnoreCase containsValue(CharSequence value) {
|
||||||
|
return new ContainsIgnoreCase(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean evaluate(Object value) {
|
||||||
|
if (value instanceof CharSequence) {
|
||||||
|
return KMP.KMP( (CharSequence) value, containsValue, true) != -1;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
package it.cavallium.strangedb.java.objects.lists.operations;
|
||||||
|
|
||||||
|
import it.cavallium.strangedb.java.objects.lists.ValueOperation;
|
||||||
|
|
||||||
|
public class Equals<T> implements ValueOperation<T> {
|
||||||
|
|
||||||
|
private final T equalToValue;
|
||||||
|
|
||||||
|
private Equals(T equalToValue) {
|
||||||
|
this.equalToValue = equalToValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Equals<T> to(T value) {
|
||||||
|
return new Equals<T>(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean evaluate(Object value) {
|
||||||
|
return equalToValue.equals(value);
|
||||||
|
}
|
||||||
|
}
|
@ -1,43 +0,0 @@
|
|||||||
package it.cavallium.strangedb.lists;
|
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
|
||||||
import it.cavallium.strangedb.EnhancedObject;
|
|
||||||
import it.cavallium.strangedb.database.IDatabaseTools;
|
|
||||||
import it.cavallium.strangedb.annotations.DbDataType;
|
|
||||||
import it.cavallium.strangedb.annotations.DbField;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
public class EnhancedObjectStrandeDbList<T extends EnhancedObject> extends StrandeDbList<T> {
|
|
||||||
|
|
||||||
@DbField(id = 0, type = DbDataType.REFERENCES_LIST)
|
|
||||||
private LongArrayList indices;
|
|
||||||
|
|
||||||
@DbField(id = 1, type = DbDataType.OBJECT)
|
|
||||||
private Class<T> type;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected LongArrayList getIndices() {
|
|
||||||
return indices;
|
|
||||||
}
|
|
||||||
|
|
||||||
public EnhancedObjectStrandeDbList() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
public EnhancedObjectStrandeDbList(IDatabaseTools databaseTools, Class<T> type) throws IOException {
|
|
||||||
super(databaseTools);
|
|
||||||
this.type = type;
|
|
||||||
indices = new LongArrayList();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected T loadItem(long uid) throws IOException {
|
|
||||||
return databaseTools.getObjectsIO().loadEnhancedObject(uid, type);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void writeItemToDisk(long uid, T item) throws IOException {
|
|
||||||
databaseTools.getObjectsIO().setEnhancedObject(uid, item);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,94 +0,0 @@
|
|||||||
package it.cavallium.strangedb.lists;
|
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
|
||||||
import it.cavallium.strangedb.EnhancedObject;
|
|
||||||
import it.cavallium.strangedb.database.IDatabaseTools;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.StringJoiner;
|
|
||||||
|
|
||||||
public abstract class StrandeDbList<T> extends EnhancedObject {
|
|
||||||
|
|
||||||
private final Object indicesAccessLock = new Object();
|
|
||||||
|
|
||||||
protected abstract LongArrayList getIndices();
|
|
||||||
|
|
||||||
public StrandeDbList() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public StrandeDbList(IDatabaseTools databaseTools) throws IOException {
|
|
||||||
super(databaseTools);
|
|
||||||
}
|
|
||||||
|
|
||||||
public T get(int index) throws IOException {
|
|
||||||
synchronized (indicesAccessLock) {
|
|
||||||
long uid = getIndices().getLong(index);
|
|
||||||
return loadItem(uid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void add(T value) throws IOException {
|
|
||||||
long uid = databaseTools.getObjectsIO().newNullObject();
|
|
||||||
synchronized (indicesAccessLock) {
|
|
||||||
getIndices().add(uid);
|
|
||||||
writeItemToDisk(uid, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void update(int index, T value) throws IOException {
|
|
||||||
synchronized (indicesAccessLock) {
|
|
||||||
long uid = getIndices().getLong(index);
|
|
||||||
writeItemToDisk(uid, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void set(int index, T value) throws IOException {
|
|
||||||
long uid = databaseTools.getObjectsIO().newNullObject();
|
|
||||||
synchronized (indicesAccessLock) {
|
|
||||||
getIndices().set(index, uid);
|
|
||||||
writeItemToDisk(uid, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void add(int index, T value) throws IOException {
|
|
||||||
long uid = databaseTools.getObjectsIO().newNullObject();
|
|
||||||
synchronized (indicesAccessLock) {
|
|
||||||
getIndices().add(index, uid);
|
|
||||||
writeItemToDisk(uid, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public T getLast() throws IOException {
|
|
||||||
synchronized (indicesAccessLock) {
|
|
||||||
if (getIndices().size() > 0) {
|
|
||||||
return get(getIndices().size() - 1);
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isEmpty() {
|
|
||||||
synchronized (indicesAccessLock) {
|
|
||||||
return getIndices().size() <= 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int size() {
|
|
||||||
synchronized (indicesAccessLock) {
|
|
||||||
return getIndices().size();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract T loadItem(long uid) throws IOException;
|
|
||||||
|
|
||||||
protected abstract void writeItemToDisk(long uid, T item) throws IOException;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return new StringJoiner(", ", StrandeDbList.class.getSimpleName() + "[", "]")
|
|
||||||
.add(getIndices().size() + " items")
|
|
||||||
.toString();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,14 +1,11 @@
|
|||||||
package it.cavallium.strangedb.tests;
|
package it.cavallium.strangedb.tests;
|
||||||
|
|
||||||
|
import it.cavallium.strangedb.java.annotations.*;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import it.cavallium.strangedb.EnhancedObject;
|
import it.cavallium.strangedb.java.objects.EnhancedObject;
|
||||||
import it.cavallium.strangedb.database.IDatabaseTools;
|
import it.cavallium.strangedb.java.database.IDatabaseTools;
|
||||||
import it.cavallium.strangedb.annotations.DbDataType;
|
|
||||||
import it.cavallium.strangedb.annotations.DbField;
|
|
||||||
import it.cavallium.strangedb.annotations.DbPropertyGetter;
|
|
||||||
import it.cavallium.strangedb.annotations.DbPropertySetter;
|
|
||||||
import it.cavallium.strangedb.utils.NTestUtils;
|
import it.cavallium.strangedb.utils.NTestUtils;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -20,7 +17,7 @@ public class Clean {
|
|||||||
@Before
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
db = NTestUtils.wrapDb().create((db) -> {
|
db = NTestUtils.wrapDb().create((db) -> {
|
||||||
root = db.get().loadRoot(RootTwoClasses.class, RootTwoClasses::new);
|
root = db.get().loadRoot(RootTwoClasses::new);
|
||||||
});
|
});
|
||||||
root.class1 = new NTestUtils.RootClass(db.get());
|
root.class1 = new NTestUtils.RootClass(db.get());
|
||||||
db.setRootClassValues(root.class1);
|
db.setRootClassValues(root.class1);
|
||||||
@ -40,7 +37,7 @@ public class Clean {
|
|||||||
db.testRootClassValues(root.getClass4());
|
db.testRootClassValues(root.getClass4());
|
||||||
db.get().closeAndClean();
|
db.get().closeAndClean();
|
||||||
db = NTestUtils.wrapDb().create((db) -> {
|
db = NTestUtils.wrapDb().create((db) -> {
|
||||||
root = db.get().loadRoot(RootTwoClasses.class, RootTwoClasses::new);
|
root = db.get().loadRoot(RootTwoClasses::new);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,22 +62,26 @@ public class Clean {
|
|||||||
super(databaseTools);
|
super(databaseTools);
|
||||||
}
|
}
|
||||||
|
|
||||||
@DbPropertyGetter(id = 0, type = DbDataType.ENHANCED_OBJECT)
|
@DbProperty(id = 0, type = DbDataType.ENHANCED_OBJECT)
|
||||||
|
@DbPropertyGetter
|
||||||
public NTestUtils.RootClass getClass3() {
|
public NTestUtils.RootClass getClass3() {
|
||||||
return getProperty();
|
return getProperty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@DbPropertySetter(id = 0, type = DbDataType.ENHANCED_OBJECT)
|
@DbProperty(id = 0, type = DbDataType.ENHANCED_OBJECT)
|
||||||
|
@DbPropertySetter
|
||||||
public void setClass3(NTestUtils.RootClass value) {
|
public void setClass3(NTestUtils.RootClass value) {
|
||||||
setProperty(value);
|
setProperty(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@DbPropertyGetter(id = 1, type = DbDataType.ENHANCED_OBJECT)
|
@DbProperty(id = 1, type = DbDataType.ENHANCED_OBJECT)
|
||||||
|
@DbPropertyGetter
|
||||||
public NTestUtils.RootClass getClass4() {
|
public NTestUtils.RootClass getClass4() {
|
||||||
return getProperty();
|
return getProperty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@DbPropertySetter(id = 1, type = DbDataType.ENHANCED_OBJECT)
|
@DbProperty(id = 1, type = DbDataType.ENHANCED_OBJECT)
|
||||||
|
@DbPropertySetter
|
||||||
public void setClass4(NTestUtils.RootClass value) {
|
public void setClass4(NTestUtils.RootClass value) {
|
||||||
setProperty(value);
|
setProperty(value);
|
||||||
}
|
}
|
||||||
|
@ -1,30 +1,31 @@
|
|||||||
package it.cavallium.strangedb.tests;
|
package it.cavallium.strangedb.tests;
|
||||||
|
|
||||||
|
import it.cavallium.strangedb.database.DatabaseCore;
|
||||||
|
import it.cavallium.strangedb.java.database.DatabaseJava;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import it.cavallium.strangedb.database.Database;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.file.DirectoryStream;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.LinkOption;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
public class EnhancedClassUpdate {
|
public class EnhancedClassUpdate {
|
||||||
|
|
||||||
private Path path1;
|
private Path dir;
|
||||||
private Path path2;
|
private DatabaseJava db;
|
||||||
private Path path3;
|
|
||||||
private Database db;
|
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
path1 = Files.createTempFile("db-tests-", ".db");
|
dir = Files.createTempDirectory("db-test");
|
||||||
path2 = Files.createTempFile("db-tests-", ".db");
|
db = new DatabaseJava(dir.resolve("a.db"), dir.resolve("b.db"));
|
||||||
path3 = Files.createTempFile("db-tests-", ".db");
|
db.registerClass(V2Class.class, 0);
|
||||||
db = new Database(path1, path2, path3);
|
db.registerClass(OldClass.class, 1);
|
||||||
OldClass root = db.loadRoot(OldClass.class, OldClass::new);
|
OldClass root = db.loadRoot(OldClass::new);
|
||||||
root.field1 = "Abc";
|
root.field1 = "Abc";
|
||||||
root.field2 = 12;
|
root.field2 = 12;
|
||||||
root.field4 = 13;
|
root.field4 = 13;
|
||||||
@ -33,8 +34,10 @@ public class EnhancedClassUpdate {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void shouldUpdateClass() throws IOException {
|
public void shouldUpdateClass() throws IOException {
|
||||||
db = new Database(path1, path2, path3);
|
db = new DatabaseJava(dir.resolve("a.db"), dir.resolve("b.db"));
|
||||||
V2Class root = db.loadRoot(V2Class.class, V2Class::new);
|
db.registerClass(V2Class.class, 0);
|
||||||
|
db.registerClass(OldClass.class, 1);
|
||||||
|
V2Class root = db.loadRoot(V2Class::new, V2Class.class);
|
||||||
assertEquals(root.field4, "Abc");
|
assertEquals(root.field4, "Abc");
|
||||||
assertEquals(root.field2, 12);
|
assertEquals(root.field2, 12);
|
||||||
assertEquals(root.field1, 13L);
|
assertEquals(root.field1, 13L);
|
||||||
@ -43,8 +46,17 @@ public class EnhancedClassUpdate {
|
|||||||
|
|
||||||
@After
|
@After
|
||||||
public void tearDown() throws Exception {
|
public void tearDown() throws Exception {
|
||||||
Files.deleteIfExists(path1);
|
deleteDirectoryRecursion(dir);
|
||||||
Files.deleteIfExists(path2);
|
}
|
||||||
Files.deleteIfExists(path3);
|
|
||||||
|
void deleteDirectoryRecursion(Path path) throws IOException {
|
||||||
|
if (Files.isDirectory(path, LinkOption.NOFOLLOW_LINKS)) {
|
||||||
|
try (DirectoryStream<Path> entries = Files.newDirectoryStream(path)) {
|
||||||
|
for (Path entry : entries) {
|
||||||
|
deleteDirectoryRecursion(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Files.delete(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,58 @@
|
|||||||
|
package it.cavallium.strangedb.tests;
|
||||||
|
|
||||||
|
import it.cavallium.strangedb.java.annotations.DbDataType;
|
||||||
|
import it.cavallium.strangedb.java.annotations.DbField;
|
||||||
|
import it.cavallium.strangedb.java.database.IDatabaseTools;
|
||||||
|
import it.cavallium.strangedb.java.objects.EnhancedObject;
|
||||||
|
import it.cavallium.strangedb.java.objects.lists.EnhancedObjectStrangeDbList;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.concurrent.*;
|
||||||
|
|
||||||
|
public class ListContainer extends EnhancedObject {
|
||||||
|
|
||||||
|
@DbField(id = 0, type = DbDataType.ENHANCED_OBJECT)
|
||||||
|
public EnhancedObjectStrangeDbList<User> usersList;
|
||||||
|
|
||||||
|
public ListContainer() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public ListContainer(IDatabaseTools databaseTools, int count) throws IOException {
|
||||||
|
super(databaseTools);
|
||||||
|
this.usersList = new EnhancedObjectStrangeDbList<>(databaseTools, User.class);
|
||||||
|
Random random = new Random();
|
||||||
|
ExecutorService threadPool = Executors.newWorkStealingPool();
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
threadPool.execute(() -> {
|
||||||
|
try {
|
||||||
|
User usr;
|
||||||
|
int randomInt = random.nextInt(2);
|
||||||
|
switch (randomInt) {
|
||||||
|
case 0:
|
||||||
|
usr = new User(databaseTools, "Rossi" + count + "Mario" + count, "the" + count + "_mariorossi99", "Long long big " + count + " giant bio mario rossi 99 abcdefghijklmnopqrstuvwxyz");
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
usr = new User(databaseTools, QueryTests.constantFirstName, QueryTests.constantUsername, QueryTests.constantBio);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
usr = new User(databaseTools, QueryTests.constantFirstName, "b" + count + "a", QueryTests.constantBio);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArrayIndexOutOfBoundsException();
|
||||||
|
}
|
||||||
|
this.usersList.add(usr);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
throw new CompletionException(ex);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
threadPool.shutdown();
|
||||||
|
try {
|
||||||
|
threadPool.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
throw new IOException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,11 +1,8 @@
|
|||||||
package it.cavallium.strangedb.tests;
|
package it.cavallium.strangedb.tests;
|
||||||
|
|
||||||
import it.cavallium.strangedb.EnhancedObject;
|
import it.cavallium.strangedb.java.annotations.*;
|
||||||
import it.cavallium.strangedb.annotations.DbDataType;
|
import it.cavallium.strangedb.java.objects.EnhancedObject;
|
||||||
import it.cavallium.strangedb.annotations.DbField;
|
import it.cavallium.strangedb.java.database.IDatabaseTools;
|
||||||
import it.cavallium.strangedb.annotations.DbPropertyGetter;
|
|
||||||
import it.cavallium.strangedb.annotations.DbPropertySetter;
|
|
||||||
import it.cavallium.strangedb.database.IDatabaseTools;
|
|
||||||
import it.cavallium.strangedb.utils.NTestUtils;
|
import it.cavallium.strangedb.utils.NTestUtils;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
@ -20,7 +17,7 @@ public class MultipleEnhancedObjects {
|
|||||||
@Before
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
db = NTestUtils.wrapDb().create((db) -> {
|
db = NTestUtils.wrapDb().create((db) -> {
|
||||||
root = db.get().loadRoot(RootTwoClasses.class, RootTwoClasses::new);
|
root = db.get().loadRoot(RootTwoClasses::new);
|
||||||
});
|
});
|
||||||
root.class1 = new NTestUtils.RootClass(db.get());
|
root.class1 = new NTestUtils.RootClass(db.get());
|
||||||
db.setRootClassValues(root.class1);
|
db.setRootClassValues(root.class1);
|
||||||
@ -62,22 +59,26 @@ public class MultipleEnhancedObjects {
|
|||||||
super(databaseTools);
|
super(databaseTools);
|
||||||
}
|
}
|
||||||
|
|
||||||
@DbPropertyGetter(id = 0, type = DbDataType.ENHANCED_OBJECT)
|
@DbProperty(id = 0, type = DbDataType.ENHANCED_OBJECT)
|
||||||
|
@DbPropertyGetter
|
||||||
public NTestUtils.RootClass getClass3() {
|
public NTestUtils.RootClass getClass3() {
|
||||||
return getProperty();
|
return getProperty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@DbPropertySetter(id = 0, type = DbDataType.ENHANCED_OBJECT)
|
@DbProperty(id = 0, type = DbDataType.ENHANCED_OBJECT)
|
||||||
|
@DbPropertySetter
|
||||||
public void setClass3(NTestUtils.RootClass value) {
|
public void setClass3(NTestUtils.RootClass value) {
|
||||||
setProperty(value);
|
setProperty(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@DbPropertyGetter(id = 1, type = DbDataType.ENHANCED_OBJECT)
|
@DbProperty(id = 1, type = DbDataType.ENHANCED_OBJECT)
|
||||||
|
@DbPropertyGetter
|
||||||
public NTestUtils.RootClass getClass4() {
|
public NTestUtils.RootClass getClass4() {
|
||||||
return getProperty();
|
return getProperty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@DbPropertySetter(id = 1, type = DbDataType.ENHANCED_OBJECT)
|
@DbProperty(id = 1, type = DbDataType.ENHANCED_OBJECT)
|
||||||
|
@DbPropertySetter
|
||||||
public void setClass4(NTestUtils.RootClass value) {
|
public void setClass4(NTestUtils.RootClass value) {
|
||||||
setProperty(value);
|
setProperty(value);
|
||||||
}
|
}
|
||||||
|
140
src/test/java/it/cavallium/strangedb/tests/ObjectListTests.java
Normal file
140
src/test/java/it/cavallium/strangedb/tests/ObjectListTests.java
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
package it.cavallium.strangedb.tests;
|
||||||
|
|
||||||
|
import it.cavallium.strangedb.java.annotations.DbDataType;
|
||||||
|
import it.cavallium.strangedb.java.annotations.DbField;
|
||||||
|
import it.cavallium.strangedb.java.database.DatabaseJava;
|
||||||
|
import it.cavallium.strangedb.java.database.IDatabaseTools;
|
||||||
|
import it.cavallium.strangedb.java.objects.EnhancedObject;
|
||||||
|
import it.cavallium.strangedb.java.objects.lists.EnhancedObjectStrangeDbList;
|
||||||
|
import it.cavallium.strangedb.java.objects.lists.ObjectStrangeDbList;
|
||||||
|
import it.cavallium.strangedb.utils.NSimplestClass;
|
||||||
|
import it.cavallium.strangedb.utils.NTestUtils;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
public class ObjectListTests {
|
||||||
|
|
||||||
|
private Path path1;
|
||||||
|
private Path path3;
|
||||||
|
private DatabaseJava db;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
path1 = Files.createTempFile("db-tests-data-", ".db");
|
||||||
|
path3 = Files.createTempFile("db-tests-references-", ".db");
|
||||||
|
db = new DatabaseJava(path1, path3);
|
||||||
|
registerClasses();
|
||||||
|
ListsRoot root = db.loadRoot(ListsRoot::new);
|
||||||
|
for (int i = 0; i < 500; i++) {
|
||||||
|
root.objectList.add(new ObjectItem(i));
|
||||||
|
root.enhancedObjectList.add(new EnhancedObjectItem(db, i));
|
||||||
|
}
|
||||||
|
for (int i = 0; i < 500; i++) {
|
||||||
|
if (i % 10 == 0) {
|
||||||
|
root.objectList.update(i, new ObjectItem(i));
|
||||||
|
root.enhancedObjectList.update(i, new EnhancedObjectItem(db, i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int i = 0; i < 500; i++) {
|
||||||
|
if (i % 11 == 0) {
|
||||||
|
root.objectList.set(i, new ObjectItem(i));
|
||||||
|
root.enhancedObjectList.set(i, new EnhancedObjectItem(db, i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int i = 500; i < 600; i++) {
|
||||||
|
root.objectList.add(new ObjectItem(0));
|
||||||
|
root.enhancedObjectList.add(i, new EnhancedObjectItem(db, 0));
|
||||||
|
}
|
||||||
|
db.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void registerClasses() {
|
||||||
|
int id = 0;
|
||||||
|
db.registerClass(EnhancedObjectItem.class, id++);
|
||||||
|
db.registerClass(ListsRoot.class, id++);
|
||||||
|
db.registerClass(ObjectItem.class, id++);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldUpdateClass() throws IOException {
|
||||||
|
db = new DatabaseJava(path1, path3);
|
||||||
|
registerClasses();
|
||||||
|
ListsRoot root = db.loadRoot(ListsRoot::new);
|
||||||
|
|
||||||
|
try {
|
||||||
|
for (int i = 0; i < 500; i++) {
|
||||||
|
String val = root.objectList.get(i).value;
|
||||||
|
assertEquals(val, "test_" + i);
|
||||||
|
val = root.enhancedObjectList.get(i).value;
|
||||||
|
assertEquals(val, "test_" + i);
|
||||||
|
}
|
||||||
|
for (int i = 500; i < 600; i++) {
|
||||||
|
assertEquals(root.objectList.get(i).value, "test_" + 0);
|
||||||
|
assertEquals(root.enhancedObjectList.get(i).value, "test_" + 0);
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
db.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() throws Exception {
|
||||||
|
Files.deleteIfExists(path1);
|
||||||
|
Files.deleteIfExists(path3);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ListsRoot extends EnhancedObject {
|
||||||
|
|
||||||
|
@DbField(id = 0,name = "objectList", type = DbDataType.ENHANCED_OBJECT)
|
||||||
|
public ObjectStrangeDbList<ObjectItem> objectList;
|
||||||
|
@DbField(id = 1,name = "enhancedObjectList", type = DbDataType.ENHANCED_OBJECT)
|
||||||
|
public EnhancedObjectStrangeDbList<EnhancedObjectItem> enhancedObjectList;
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
public ListsRoot() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public ListsRoot(IDatabaseTools tools) throws IOException {
|
||||||
|
super(tools);
|
||||||
|
objectList = new ObjectStrangeDbList<>(tools);
|
||||||
|
enhancedObjectList = new EnhancedObjectStrangeDbList<>(tools, EnhancedObjectItem.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ObjectItem {
|
||||||
|
private String value;
|
||||||
|
|
||||||
|
public ObjectItem() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private ObjectItem(int i) {
|
||||||
|
this.value = "test_" + i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class EnhancedObjectItem extends EnhancedObject {
|
||||||
|
@DbField(id = 0, type = DbDataType.OBJECT, name = "value")
|
||||||
|
private String value;
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
public EnhancedObjectItem() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public EnhancedObjectItem(IDatabaseTools tools, int i) throws IOException {
|
||||||
|
super(tools);
|
||||||
|
this.value = "test_" + i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,11 +1,11 @@
|
|||||||
package it.cavallium.strangedb.tests;
|
package it.cavallium.strangedb.tests;
|
||||||
|
|
||||||
import it.cavallium.strangedb.annotations.DbPrimitiveField;
|
import it.cavallium.strangedb.java.annotations.DbPrimitiveField;
|
||||||
import it.cavallium.strangedb.annotations.DbPrimitiveType;
|
import it.cavallium.strangedb.java.annotations.DbPrimitiveType;
|
||||||
import it.cavallium.strangedb.EnhancedObject;
|
import it.cavallium.strangedb.java.objects.EnhancedObject;
|
||||||
import it.cavallium.strangedb.database.IDatabaseTools;
|
import it.cavallium.strangedb.java.database.IDatabaseTools;
|
||||||
import it.cavallium.strangedb.annotations.DbDataType;
|
import it.cavallium.strangedb.java.annotations.DbDataType;
|
||||||
import it.cavallium.strangedb.annotations.DbField;
|
import it.cavallium.strangedb.java.annotations.DbField;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@ -1,29 +1,54 @@
|
|||||||
package it.cavallium.strangedb.tests;
|
package it.cavallium.strangedb.tests;
|
||||||
|
|
||||||
import it.cavallium.strangedb.annotations.*;
|
import it.cavallium.strangedb.database.io.ExtensionUtils;
|
||||||
import it.cavallium.strangedb.functionalinterfaces.RunnableWithIO;
|
import it.cavallium.strangedb.functionalinterfaces.RunnableWithIO;
|
||||||
import it.cavallium.strangedb.lists.EnhancedObjectStrandeDbList;
|
import it.cavallium.strangedb.java.annotations.*;
|
||||||
import it.cavallium.strangedb.lists.ObjectStrandeDbList;
|
import it.cavallium.strangedb.java.database.DatabaseJava;
|
||||||
|
import it.cavallium.strangedb.java.objects.lists.EnhancedObjectStrangeDbList;
|
||||||
|
import it.cavallium.strangedb.java.objects.lists.ListQuery;
|
||||||
|
import it.cavallium.strangedb.java.objects.lists.ObjectStrangeDbList;
|
||||||
|
import it.cavallium.strangedb.java.objects.lists.ValuePointer;
|
||||||
|
import it.cavallium.strangedb.java.objects.lists.operations.ContainsIgnoreCase;
|
||||||
|
import it.cavallium.strangedb.tests.performance.PerformanceListQuery;
|
||||||
|
import it.cavallium.strangedb.tests.performance.PerformanceTest;
|
||||||
|
import it.cavallium.strangedb.tests.query.*;
|
||||||
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
||||||
import it.cavallium.strangedb.database.Database;
|
import it.cavallium.strangedb.java.objects.EnhancedObject;
|
||||||
import it.cavallium.strangedb.EnhancedObject;
|
import it.cavallium.strangedb.java.database.IDatabaseTools;
|
||||||
import it.cavallium.strangedb.database.IDatabaseTools;
|
|
||||||
import it.cavallium.strangedb.VariableWrapper;
|
import it.cavallium.strangedb.VariableWrapper;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
public class Performance {
|
public class Performance {
|
||||||
private static boolean FAST_TESTS;
|
private static int DIVISOR;
|
||||||
|
|
||||||
private static Path rootDirectory;
|
private static Path rootDirectory;
|
||||||
private static Path dbDataFile;
|
private static Path dbDataFile;
|
||||||
private static Path dbBlocksFile;
|
|
||||||
private static Path dbReferencesFile;
|
private static Path dbReferencesFile;
|
||||||
private static Database db;
|
public static DatabaseJava db;
|
||||||
|
private static boolean tempDirectory;
|
||||||
|
private static final int BASE_WIDTH = 35;
|
||||||
|
private static final int SPACE_WIDTH = BASE_WIDTH + 35;
|
||||||
|
private static String spaces = "";
|
||||||
|
private static String bars = "";
|
||||||
|
private static boolean doFillTests;
|
||||||
|
private static boolean doLoadTests;
|
||||||
|
private static boolean doQueryTests;
|
||||||
|
private static boolean doInstantiationTests;
|
||||||
|
|
||||||
|
static {
|
||||||
|
for (int i = 0; i < SPACE_WIDTH; i++) {
|
||||||
|
spaces += " ";
|
||||||
|
bars += "-";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@ -32,43 +57,64 @@ public class Performance {
|
|||||||
* @throws InterruptedException
|
* @throws InterruptedException
|
||||||
*/
|
*/
|
||||||
public static void main(String[] args) throws IOException, InterruptedException {
|
public static void main(String[] args) throws IOException, InterruptedException {
|
||||||
FAST_TESTS = args.length > 0 && args[0].equalsIgnoreCase("true");
|
DIVISOR = args.length > 0 ? Integer.parseInt(args[0]) : 1;
|
||||||
if (args.length >= 2) {
|
if (args.length >= 2) {
|
||||||
rootDirectory = Paths.get(args[1]);
|
doQueryTests =
|
||||||
try {
|
args[1].contains("query");
|
||||||
Files.createDirectory(rootDirectory);
|
doFillTests =
|
||||||
} catch (Exception ex) {
|
args[1].contains("fill");
|
||||||
|
doLoadTests =
|
||||||
|
args[1].contains("load");
|
||||||
|
doInstantiationTests =
|
||||||
|
args[1].contains("new");
|
||||||
|
if (args[1].contains("all")) {
|
||||||
|
doQueryTests = true;
|
||||||
|
doFillTests = true;
|
||||||
|
doLoadTests = true;
|
||||||
|
doInstantiationTests = true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if (args.length >= 3) {
|
||||||
|
rootDirectory = Paths.get(args[2]);
|
||||||
|
if (Files.notExists(rootDirectory)) {
|
||||||
|
Files.createDirectory(rootDirectory);
|
||||||
|
}
|
||||||
|
tempDirectory = false;
|
||||||
} else {
|
} else {
|
||||||
rootDirectory = Files.createTempDirectory("performance-tests");
|
rootDirectory = Files.createTempDirectory("performance-tests");
|
||||||
|
tempDirectory = true;
|
||||||
}
|
}
|
||||||
generateDb();
|
generateDb();
|
||||||
System.out.println("Performance test started.");
|
System.out.println("Performance test started.");
|
||||||
System.out.println("-------------------------------------------------------+-----------------------------------------------------------------");
|
System.out.println(bars + "---------------+-----------------------------------------------------------------");
|
||||||
System.out.println("Test name Total Time | Time at 1 Time at 10 Time at 100 Time at 1K Time at 10K");
|
System.out.println("Test name" + spaces.substring(0, spaces.length() - 5) + "Total Time | Time at 1 Time at 10 Time at 100 Time at 1K Time at 10K");
|
||||||
System.out.println("-------------------------------------------------------+-----------------------------------------------------------------");
|
System.out.println(bars + "---------------+-----------------------------------------------------------------");
|
||||||
testS("Database creation", 3000, Performance::deleteDb, Performance::generateDb, () -> {});
|
if (doInstantiationTests) {
|
||||||
testS("Database root creation", 3000, Performance::regenDb, () -> db.loadRoot(PreloadedListContainer.class, PreloadedListContainer::new), () -> {});
|
testS("DatabaseCore creation", 300,PerformanceTest.createTest(Performance::deleteDb, Performance::generateDb, () -> {}));
|
||||||
|
testS("DatabaseCore root creation", 300, PerformanceTest.createTest(Performance::regenDb, () -> db.loadRoot(PreloadedListContainer::new), () -> {}));
|
||||||
|
}
|
||||||
final VariableWrapper<PreloadedListContainer> preloadedListContainer = new VariableWrapper<>(null);
|
final VariableWrapper<PreloadedListContainer> preloadedListContainer = new VariableWrapper<>(null);
|
||||||
final VariableWrapper<SimpleEnhancedObject> simpleEnhancedObjectContainer = new VariableWrapper<>(null);
|
final VariableWrapper<SimpleEnhancedObject> simpleEnhancedObjectContainer = new VariableWrapper<>(null);
|
||||||
testS("ObjectStrandeDbList<Int> creation", 3000, () -> {
|
if (doInstantiationTests) {
|
||||||
|
testS("ObjectStrangeDbList<Int> creation", 1000, PerformanceTest.createTest(() -> {
|
||||||
regenDb();
|
regenDb();
|
||||||
preloadedListContainer.var = db.loadRoot(PreloadedListContainer.class, PreloadedListContainer::new);
|
preloadedListContainer.var = db.loadRoot(PreloadedListContainer::new);
|
||||||
}, () -> preloadedListContainer.var.list = new ObjectStrandeDbList<>(db), () -> {});
|
}, () -> preloadedListContainer.var.list = new ObjectStrangeDbList<>(db), () -> {}));
|
||||||
testS("ObjectStrandeDbList<Int>: Filling with 1000 items", 100, () -> {
|
}
|
||||||
|
if (doFillTests) {
|
||||||
|
testS("ObjectStrangeDbList<Int>: Filling with 1000 items", 100, PerformanceTest.createTest(() -> {
|
||||||
regenDb();
|
regenDb();
|
||||||
preloadedListContainer.var = db.loadRoot(PreloadedListContainer.class, PreloadedListContainer::new);
|
preloadedListContainer.var = db.loadRoot(PreloadedListContainer::new);
|
||||||
preloadedListContainer.var.list = new ObjectStrandeDbList<>(db);
|
preloadedListContainer.var.list = new ObjectStrangeDbList<>(db);
|
||||||
}, () -> {
|
}, () -> {
|
||||||
for (int i = 0; i < 1000; i++) {
|
for (int i = 0; i < 1000; i++) {
|
||||||
preloadedListContainer.var.list.add(1000);
|
preloadedListContainer.var.list.add(1000);
|
||||||
}
|
}
|
||||||
}, () -> {});
|
}, () -> {}));
|
||||||
testS("ObjectStrandeDbList<EnhancedObject>: Filling with 1000 items", 100, () -> {
|
testS("ObjectStrangeDbList<EnhancedObject>: Filling with 1000 items", 100, PerformanceTest.createTest(() -> {
|
||||||
regenDb();
|
regenDb();
|
||||||
preloadedListContainer.var = db.loadRoot(PreloadedListContainer.class, PreloadedListContainer::new);
|
preloadedListContainer.var = db.loadRoot(PreloadedListContainer::new);
|
||||||
preloadedListContainer.var.listOfEnhancedObj = new EnhancedObjectStrandeDbList<>(db, SimpleEnhancedObject.class);
|
preloadedListContainer.var.listOfEnhancedObj = new EnhancedObjectStrangeDbList<>(db, SimpleEnhancedObject.class);
|
||||||
simpleEnhancedObjectContainer.var = new SimpleEnhancedObject(db);
|
simpleEnhancedObjectContainer.var = new SimpleEnhancedObject(db);
|
||||||
simpleEnhancedObjectContainer.var.integerNumber = 10;
|
simpleEnhancedObjectContainer.var.integerNumber = 10;
|
||||||
simpleEnhancedObjectContainer.var.longNumber = 10L;
|
simpleEnhancedObjectContainer.var.longNumber = 10L;
|
||||||
@ -80,41 +126,41 @@ public class Performance {
|
|||||||
for (int i = 0; i < 1000; i++) {
|
for (int i = 0; i < 1000; i++) {
|
||||||
preloadedListContainer.var.listOfEnhancedObj.add(simpleEnhancedObjectContainer.var);
|
preloadedListContainer.var.listOfEnhancedObj.add(simpleEnhancedObjectContainer.var);
|
||||||
}
|
}
|
||||||
}, () -> {});
|
}, () -> {}));
|
||||||
testS("ObjectStrandeDbList<Int>: Filling with 10000 items", 10, () -> {
|
testS("ObjectStrangeDbList<Int>: Filling with 10000 items", 10, PerformanceTest.createTest(() -> {
|
||||||
regenDb();
|
regenDb();
|
||||||
preloadedListContainer.var = db.loadRoot(PreloadedListContainer.class, PreloadedListContainer::new);
|
preloadedListContainer.var = db.loadRoot(PreloadedListContainer::new);
|
||||||
preloadedListContainer.var.list = new ObjectStrandeDbList<>(db);
|
preloadedListContainer.var.list = new ObjectStrangeDbList<>(db);
|
||||||
}, () -> {
|
}, () -> {
|
||||||
for (int i = 0; i < 10000; i++) {
|
for (int i = 0; i < 10000; i++) {
|
||||||
preloadedListContainer.var.list.add(1000);
|
preloadedListContainer.var.list.add(1000);
|
||||||
}
|
}
|
||||||
}, () -> {});
|
}, () -> {}));
|
||||||
testS("ObjectStrandeDbList<Int>: Filling with 100000 items", 1, () -> {
|
testS("ObjectStrangeDbList<Int>: Filling with 100000 items", 1, PerformanceTest.createTest(() -> {
|
||||||
regenDb();
|
regenDb();
|
||||||
preloadedListContainer.var = db.loadRoot(PreloadedListContainer.class, PreloadedListContainer::new);
|
preloadedListContainer.var = db.loadRoot(PreloadedListContainer::new);
|
||||||
preloadedListContainer.var.list = new ObjectStrandeDbList<>(db);
|
preloadedListContainer.var.list = new ObjectStrangeDbList<>(db);
|
||||||
}, () -> {
|
}, () -> {
|
||||||
for (int i = 0; i < 100000; i++) {
|
for (int i = 0; i < 100000; i++) {
|
||||||
preloadedListContainer.var.list.add(1000);
|
preloadedListContainer.var.list.add(1000);
|
||||||
}
|
}
|
||||||
}, () -> {});
|
}, () -> {}));
|
||||||
testS("ObjectStrandeDbList<Int>: Loading 1000 items", 100, () -> {
|
}
|
||||||
|
if (doLoadTests) {
|
||||||
|
testS("ObjectStrangeDbList<Int>: Loading 1000 items", 100, PerformanceTest.createTest(() -> {
|
||||||
regenDb();
|
regenDb();
|
||||||
preloadedListContainer.var = db.loadRoot(PreloadedListContainer.class, PreloadedListContainer::new);
|
preloadedListContainer.var = db.loadRoot(PreloadedListContainer::new);
|
||||||
preloadedListContainer.var.list = new ObjectStrandeDbList<>(db);
|
preloadedListContainer.var.list = new ObjectStrangeDbList<>(db);
|
||||||
for (int i = 0; i < 1000; i++) {
|
for (int i = 0; i < 1000; i++) {
|
||||||
preloadedListContainer.var.list.add(1000);
|
preloadedListContainer.var.list.add(1000);
|
||||||
}
|
}
|
||||||
}, () -> {
|
}, () -> {
|
||||||
for (int i = 0; i < 1000; i++) {
|
preloadedListContainer.var.list.forEachParallelUnsorted((i) -> {});
|
||||||
preloadedListContainer.var.list.get(i);
|
}, () -> {}));
|
||||||
}
|
testS("ObjectStrangeDbList<EnhancedObject>: Loading with 1000 items", 100, PerformanceTest.createTest(() -> {
|
||||||
}, () -> {});
|
|
||||||
testS("ObjectStrandeDbList<EnhancedObject>: Loading with 1000 items", 100, () -> {
|
|
||||||
regenDb();
|
regenDb();
|
||||||
preloadedListContainer.var = db.loadRoot(PreloadedListContainer.class, PreloadedListContainer::new);
|
preloadedListContainer.var = db.loadRoot(PreloadedListContainer::new);
|
||||||
preloadedListContainer.var.listOfEnhancedObj = new EnhancedObjectStrandeDbList<>(db, SimpleEnhancedObject.class);
|
preloadedListContainer.var.listOfEnhancedObj = new EnhancedObjectStrangeDbList<>(db, SimpleEnhancedObject.class);
|
||||||
simpleEnhancedObjectContainer.var = new SimpleEnhancedObject(db);
|
simpleEnhancedObjectContainer.var = new SimpleEnhancedObject(db);
|
||||||
simpleEnhancedObjectContainer.var.integerNumber = 10;
|
simpleEnhancedObjectContainer.var.integerNumber = 10;
|
||||||
simpleEnhancedObjectContainer.var.longNumber = 10L;
|
simpleEnhancedObjectContainer.var.longNumber = 10L;
|
||||||
@ -126,36 +172,32 @@ public class Performance {
|
|||||||
preloadedListContainer.var.listOfEnhancedObj.add(simpleEnhancedObjectContainer.var);
|
preloadedListContainer.var.listOfEnhancedObj.add(simpleEnhancedObjectContainer.var);
|
||||||
}
|
}
|
||||||
}, () -> {
|
}, () -> {
|
||||||
for (int i = 0; i < 1000; i++) {
|
preloadedListContainer.var.listOfEnhancedObj.forEachParallelUnsorted((i) -> {});
|
||||||
preloadedListContainer.var.listOfEnhancedObj.get(i);
|
}, () -> {}));
|
||||||
}
|
testS("ObjectStrangeDbList<Int>: Loading 10000 items", 10, PerformanceTest.createTest(() -> {
|
||||||
}, () -> {});
|
|
||||||
testS("ObjectStrandeDbList<Int>: Loading 10000 items", 10, () -> {
|
|
||||||
regenDb();
|
regenDb();
|
||||||
preloadedListContainer.var = db.loadRoot(PreloadedListContainer.class, PreloadedListContainer::new);
|
preloadedListContainer.var = db.loadRoot(PreloadedListContainer::new);
|
||||||
preloadedListContainer.var.list = new ObjectStrandeDbList<>(db);
|
preloadedListContainer.var.list = new ObjectStrangeDbList<>(db);
|
||||||
for (int i = 0; i < 10000; i++) {
|
for (int i = 0; i < 10000; i++) {
|
||||||
preloadedListContainer.var.list.add(1000);
|
preloadedListContainer.var.list.add(1000);
|
||||||
}
|
}
|
||||||
}, () -> {
|
}, () -> {
|
||||||
for (int i = 0; i < 10000; i++) {
|
preloadedListContainer.var.list.forEachParallelUnsorted((i) -> {});
|
||||||
preloadedListContainer.var.list.get(i);
|
}, () -> {}));
|
||||||
}
|
testS("ObjectStrangeDbList<Int>: getLast() with 1000 items", 100, PerformanceTest.createTest(() -> {
|
||||||
}, () -> {});
|
|
||||||
testS("ObjectStrandeDbList<Int>: getLast() with 1000 items", 100, () -> {
|
|
||||||
regenDb();
|
regenDb();
|
||||||
preloadedListContainer.var = db.loadRoot(PreloadedListContainer.class, PreloadedListContainer::new);
|
preloadedListContainer.var = db.loadRoot(PreloadedListContainer::new);
|
||||||
preloadedListContainer.var.list = new ObjectStrandeDbList<>(db);
|
preloadedListContainer.var.list = new ObjectStrangeDbList<>(db);
|
||||||
for (int i = 0; i < 1000; i++) {
|
for (int i = 0; i < 1000; i++) {
|
||||||
preloadedListContainer.var.list.add(1000);
|
preloadedListContainer.var.list.add(1000);
|
||||||
}
|
}
|
||||||
}, () -> {
|
}, () -> {
|
||||||
preloadedListContainer.var.list.getLast();
|
preloadedListContainer.var.list.getLast();
|
||||||
}, () -> {});
|
}, () -> {}));
|
||||||
testS("ObjectStrandeDbList<EnhancedObject>: getLast() with 1000 items", 100, () -> {
|
testS("ObjectStrangeDbList<EnhancedObject>: getLast() with 1000 items", 100, PerformanceTest.createTest(() -> {
|
||||||
regenDb();
|
regenDb();
|
||||||
preloadedListContainer.var = db.loadRoot(PreloadedListContainer.class, PreloadedListContainer::new);
|
preloadedListContainer.var = db.loadRoot(PreloadedListContainer::new);
|
||||||
preloadedListContainer.var.listOfEnhancedObj = new EnhancedObjectStrandeDbList<>(db, SimpleEnhancedObject.class);
|
preloadedListContainer.var.listOfEnhancedObj = new EnhancedObjectStrangeDbList<>(db, SimpleEnhancedObject.class);
|
||||||
simpleEnhancedObjectContainer.var = new SimpleEnhancedObject(db);
|
simpleEnhancedObjectContainer.var = new SimpleEnhancedObject(db);
|
||||||
simpleEnhancedObjectContainer.var.integerNumber = 10;
|
simpleEnhancedObjectContainer.var.integerNumber = 10;
|
||||||
simpleEnhancedObjectContainer.var.longNumber = 10L;
|
simpleEnhancedObjectContainer.var.longNumber = 10L;
|
||||||
@ -168,47 +210,53 @@ public class Performance {
|
|||||||
}
|
}
|
||||||
}, () -> {
|
}, () -> {
|
||||||
preloadedListContainer.var.listOfEnhancedObj.getLast();
|
preloadedListContainer.var.listOfEnhancedObj.getLast();
|
||||||
}, () -> {});
|
}, () -> {}));
|
||||||
testS("ObjectStrandeDbList<Int>: size() with 1000 items", 100, () -> {
|
testS("ObjectStrangeDbList<Int>: size() with 1000 items", 100, PerformanceTest.createTest(() -> {
|
||||||
regenDb();
|
regenDb();
|
||||||
preloadedListContainer.var = db.loadRoot(PreloadedListContainer.class, PreloadedListContainer::new);
|
preloadedListContainer.var = db.loadRoot(PreloadedListContainer::new);
|
||||||
preloadedListContainer.var.list = new ObjectStrandeDbList<>(db);
|
preloadedListContainer.var.list = new ObjectStrangeDbList<>(db);
|
||||||
for (int i = 0; i < 1000; i++) {
|
for (int i = 0; i < 1000; i++) {
|
||||||
preloadedListContainer.var.list.add(1000);
|
preloadedListContainer.var.list.add(1000);
|
||||||
}
|
}
|
||||||
}, () -> {
|
}, () -> {
|
||||||
preloadedListContainer.var.list.size();
|
preloadedListContainer.var.list.size();
|
||||||
}, () -> {});
|
}, () -> {}));
|
||||||
System.out.println("-------------------------------------------------------+-----------------------------------------------------------------");
|
}
|
||||||
|
if (doQueryTests) {
|
||||||
|
for (int items = 1000; items <= 100000; items *= 10) {
|
||||||
|
testS("ListQuery: query with " + items + " items", 100 / (items / 1000), new PerformanceListQuery(items));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
System.out.println(bars + "---------------+-----------------------------------------------------------------");
|
||||||
System.out.println("Performance test finished.");
|
System.out.println("Performance test finished.");
|
||||||
deleteDb();
|
deleteDb();
|
||||||
|
if (tempDirectory) {
|
||||||
Files.deleteIfExists(rootDirectory);
|
Files.deleteIfExists(rootDirectory);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static void NtestS(String description, int times, RunnableWithIO beforeAction, RunnableWithIO action, RunnableWithIO afterAction) throws IOException, InterruptedException {
|
private static void NtestS(String description, int times, RunnableWithIO beforeAction, RunnableWithIO action, RunnableWithIO afterAction) throws IOException, InterruptedException {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void testS(String description, int times, PerformanceTest test) throws IOException, InterruptedException {
|
||||||
|
|
||||||
private static void testS(String description, int times, RunnableWithIO beforeAction, RunnableWithIO action, RunnableWithIO afterAction) throws IOException, InterruptedException {
|
if (times >= 5 * DIVISOR) {
|
||||||
if (FAST_TESTS) {
|
times /= 5 * DIVISOR;
|
||||||
if (times >= 5) {
|
} else if (times >= 2 * DIVISOR) {
|
||||||
times /= 5;
|
times /= 2 * DIVISOR;
|
||||||
} else if (times >= 2) {
|
|
||||||
times /= 2;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
description = description + " time is";
|
description = description + " time is";
|
||||||
int spacesCount = 40 - description.length();
|
int spacesCount = SPACE_WIDTH - description.length();
|
||||||
int cutAt = 0;
|
int cutAt = 0;
|
||||||
if (spacesCount < 0) {
|
if (spacesCount < 0) {
|
||||||
spacesCount = 40 - (description.length() - 40);
|
spacesCount = SPACE_WIDTH - (description.length() - SPACE_WIDTH);
|
||||||
cutAt = 40;
|
cutAt = SPACE_WIDTH;
|
||||||
}
|
}
|
||||||
StringBuilder spaces = new StringBuilder();
|
StringBuilder spaces = new StringBuilder();
|
||||||
for (int i = 0; i < spacesCount; i++) {
|
for (int i = 0; i < spacesCount; i++) {
|
||||||
spaces.append(' ');
|
spaces.append(' ');
|
||||||
}
|
}
|
||||||
double[] results = test(times, beforeAction, action, afterAction);
|
double[] results = test(times, test);
|
||||||
if (cutAt > 0) {
|
if (cutAt > 0) {
|
||||||
System.out.println(description.substring(0, cutAt) + " |");
|
System.out.println(description.substring(0, cutAt) + " |");
|
||||||
}
|
}
|
||||||
@ -229,19 +277,21 @@ public class Performance {
|
|||||||
return spaces + String.format("%.2fms", result);
|
return spaces + String.format("%.2fms", result);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static double[] test(int times, RunnableWithIO beforeAction, RunnableWithIO action, RunnableWithIO afterAction) throws IOException, InterruptedException {
|
private static double[] test(int times, PerformanceTest test) throws IOException, InterruptedException {
|
||||||
LongArrayList results = new LongArrayList(times);
|
LongArrayList results = new LongArrayList(times);
|
||||||
Thread.sleep(100);
|
Thread.sleep(100);
|
||||||
System.gc();
|
System.gc();
|
||||||
Thread.sleep(100);
|
Thread.sleep(100);
|
||||||
|
test.setup();
|
||||||
for (int i = 0; i < times; i++) {
|
for (int i = 0; i < times; i++) {
|
||||||
beforeAction.run();
|
test.perTestSetup();
|
||||||
long startTime = System.nanoTime();
|
long startTime = System.nanoTime();
|
||||||
action.run();
|
test.test();
|
||||||
long elapsedTime = System.nanoTime() - startTime;
|
long elapsedTime = System.nanoTime() - startTime;
|
||||||
afterAction.run();
|
test.perTestEnd();
|
||||||
results.add(elapsedTime);
|
results.add(elapsedTime);
|
||||||
}
|
}
|
||||||
|
test.end();
|
||||||
double result1 = results.stream().limit(1).mapToLong(val -> val).average().orElse(0.0) / 1000000d;
|
double result1 = results.stream().limit(1).mapToLong(val -> val).average().orElse(0.0) / 1000000d;
|
||||||
double result10 = results.stream().limit(10).mapToLong(val -> val).average().orElse(0.0) / 1000000d;
|
double result10 = results.stream().limit(10).mapToLong(val -> val).average().orElse(0.0) / 1000000d;
|
||||||
double result100 = results.stream().limit(100).mapToLong(val -> val).average().orElse(0.0) / 1000000d;
|
double result100 = results.stream().limit(100).mapToLong(val -> val).average().orElse(0.0) / 1000000d;
|
||||||
@ -264,17 +314,33 @@ public class Performance {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void generateDb() throws IOException {
|
public static void generateDb() throws IOException {
|
||||||
dbDataFile = Files.createFile(rootDirectory.resolve("db_data.dat"));
|
dbDataFile = rootDirectory.resolve("db_data.dat");
|
||||||
dbBlocksFile = Files.createFile(rootDirectory.resolve("db_blocks.dat"));
|
dbReferencesFile = rootDirectory.resolve("db_references.dat");
|
||||||
dbReferencesFile = Files.createFile(rootDirectory.resolve("db_references.dat"));
|
deleteDbFolders();
|
||||||
db = new Database(dbDataFile, dbBlocksFile, dbReferencesFile);
|
db = new DatabaseJava(dbDataFile, dbReferencesFile);
|
||||||
|
int i = 0;
|
||||||
|
db.registerClass(SimpleEnhancedObject.class, i++);
|
||||||
|
db.registerClass(PreloadedListContainer.class, i++);
|
||||||
|
db.registerClass(DynamicListContainer.class, i++);
|
||||||
|
db.registerClass(EMessage.class, i++);
|
||||||
|
db.registerClass(EMessageContent.class, i++);
|
||||||
|
db.registerClass(EMessageText.class, i++);
|
||||||
|
db.registerClass(EMessageOtherContent.class, i++);
|
||||||
|
db.registerClass(EFormattedText.class, i++);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void deleteDb() throws IOException {
|
public static void deleteDb() throws IOException {
|
||||||
db.close();
|
db.close();
|
||||||
Files.deleteIfExists(dbDataFile);
|
deleteDbFolders();
|
||||||
Files.deleteIfExists(dbBlocksFile);
|
}
|
||||||
Files.deleteIfExists(dbReferencesFile);
|
|
||||||
|
public static void deleteDbFolders() throws IOException {
|
||||||
|
for (int i = 0; i < 100; i++) {
|
||||||
|
Files.deleteIfExists(dbDataFile.resolveSibling(ExtensionUtils.insertExtension(dbDataFile.toFile().getName(), i)));
|
||||||
|
}
|
||||||
|
for (int i = 0; i < 100; i++) {
|
||||||
|
Files.deleteIfExists(dbReferencesFile.resolveSibling(ExtensionUtils.insertExtension(dbReferencesFile.toFile().getName(), i)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void regenDb() throws IOException {
|
public static void regenDb() throws IOException {
|
||||||
@ -285,10 +351,13 @@ public class Performance {
|
|||||||
public static class PreloadedListContainer extends EnhancedObject {
|
public static class PreloadedListContainer extends EnhancedObject {
|
||||||
|
|
||||||
@DbField(id = 0, type = DbDataType.ENHANCED_OBJECT)
|
@DbField(id = 0, type = DbDataType.ENHANCED_OBJECT)
|
||||||
public ObjectStrandeDbList<Integer> list;
|
public ObjectStrangeDbList<Integer> list;
|
||||||
|
|
||||||
@DbField(id = 1, type = DbDataType.ENHANCED_OBJECT)
|
@DbField(id = 1, type = DbDataType.ENHANCED_OBJECT)
|
||||||
public EnhancedObjectStrandeDbList<SimpleEnhancedObject> listOfEnhancedObj;
|
public EnhancedObjectStrangeDbList<SimpleEnhancedObject> listOfEnhancedObj;
|
||||||
|
|
||||||
|
@DbField(id = 2, type = DbDataType.ENHANCED_OBJECT)
|
||||||
|
public EnhancedObjectStrangeDbList<EMessage> listOfMessages;
|
||||||
|
|
||||||
public PreloadedListContainer() {
|
public PreloadedListContainer() {
|
||||||
|
|
||||||
@ -310,13 +379,15 @@ public class Performance {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@DbPropertyGetter(id = 0, type = DbDataType.ENHANCED_OBJECT)
|
@DbProperty(id = 0, type = DbDataType.ENHANCED_OBJECT)
|
||||||
public ObjectStrandeDbList<Integer> getList() {
|
@DbPropertyGetter()
|
||||||
|
public ObjectStrangeDbList<Integer> getList() {
|
||||||
return getProperty();
|
return getProperty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@DbPropertySetter(id = 1, type = DbDataType.ENHANCED_OBJECT)
|
@DbProperty(id = 1, type = DbDataType.ENHANCED_OBJECT)
|
||||||
public void setList(ObjectStrandeDbList<Integer> list) {
|
@DbPropertySetter()
|
||||||
|
public void setList(ObjectStrangeDbList<Integer> list) {
|
||||||
setProperty(list);
|
setProperty(list);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
68
src/test/java/it/cavallium/strangedb/tests/QueryTests.java
Normal file
68
src/test/java/it/cavallium/strangedb/tests/QueryTests.java
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
package it.cavallium.strangedb.tests;
|
||||||
|
|
||||||
|
import it.cavallium.strangedb.java.database.DatabaseJava;
|
||||||
|
import it.cavallium.strangedb.java.objects.lists.ElementsArrayList;
|
||||||
|
import it.cavallium.strangedb.java.objects.lists.ListQuery;
|
||||||
|
import it.cavallium.strangedb.java.objects.lists.ValuePointer;
|
||||||
|
import it.cavallium.strangedb.java.objects.lists.operations.Equals;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.*;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertNotEquals;
|
||||||
|
|
||||||
|
public class QueryTests {
|
||||||
|
|
||||||
|
public static final String constantFirstName = "Jesus christ this is a very strange first name";
|
||||||
|
public static final String constantUsername = "is this an username?";
|
||||||
|
public static final String constantBio = "and is this a bio??? Are you mad?";
|
||||||
|
private DatabaseJava db;
|
||||||
|
private Path dir;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
dir = Files.createTempDirectory("db-test");
|
||||||
|
db = new DatabaseJava(dir.resolve("data.db"), dir.resolve("refs.db"));
|
||||||
|
int i = 0;
|
||||||
|
db.registerClass(UserFullInfo.class, i++);
|
||||||
|
db.registerClass(User.class, i++);
|
||||||
|
db.registerClass(ListContainer.class, i++);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldCreateListAndQuery() throws IOException {
|
||||||
|
ListContainer root = db.loadRoot((db) -> new ListContainer(db, 2000));
|
||||||
|
ListQuery query = ListQuery.create(ValuePointer.ofField(User.class, "firstName"), Equals.to(constantFirstName))
|
||||||
|
.and(ValuePointer.ofField(User.class, "username"), Equals.to(constantUsername))
|
||||||
|
.and(ValuePointer.ofField(User.class, "fullInfo").field(UserFullInfo.class, "bio"), Equals.to(constantBio));
|
||||||
|
long time1 = System.currentTimeMillis();
|
||||||
|
ElementsArrayList<User> elements = root.usersList.query(query);
|
||||||
|
System.out.println("Time elapsed: " + String.format("%.2f", (System.currentTimeMillis() - time1) / 1000d) + " seconds");
|
||||||
|
System.out.println("Found " + elements.size() + " elements. First 5 items:");
|
||||||
|
assertNotEquals(elements.size(), 0);
|
||||||
|
for (int i = 0; i < (elements.size() > 10 ? 10 : elements.size()); i++) {
|
||||||
|
System.out.println(elements.get(i));
|
||||||
|
}
|
||||||
|
db.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() throws Exception {
|
||||||
|
deleteDirectoryRecursion(dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
void deleteDirectoryRecursion(Path path) throws IOException {
|
||||||
|
if (Files.isDirectory(path, LinkOption.NOFOLLOW_LINKS)) {
|
||||||
|
try (DirectoryStream<Path> entries = Files.newDirectoryStream(path)) {
|
||||||
|
for (Path entry : entries) {
|
||||||
|
deleteDirectoryRecursion(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Files.delete(path);
|
||||||
|
}
|
||||||
|
}
|
40
src/test/java/it/cavallium/strangedb/tests/User.java
Normal file
40
src/test/java/it/cavallium/strangedb/tests/User.java
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package it.cavallium.strangedb.tests;
|
||||||
|
|
||||||
|
import it.cavallium.strangedb.java.annotations.DbDataType;
|
||||||
|
import it.cavallium.strangedb.java.annotations.DbField;
|
||||||
|
import it.cavallium.strangedb.java.database.IDatabaseTools;
|
||||||
|
import it.cavallium.strangedb.java.objects.EnhancedObject;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.StringJoiner;
|
||||||
|
|
||||||
|
public class User extends EnhancedObject {
|
||||||
|
|
||||||
|
@DbField(id = 0, name = "firstName")
|
||||||
|
public String firstName;
|
||||||
|
|
||||||
|
@DbField(id = 1, name = "username")
|
||||||
|
public String username;
|
||||||
|
|
||||||
|
@DbField(id = 2, type = DbDataType.ENHANCED_OBJECT, name = "fullInfo")
|
||||||
|
public UserFullInfo fullInfo;
|
||||||
|
|
||||||
|
public User() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public User(IDatabaseTools databaseTools, String firstName, String username, String bio) throws IOException {
|
||||||
|
super(databaseTools);
|
||||||
|
this.firstName = firstName;
|
||||||
|
this.username = username;
|
||||||
|
this.fullInfo = new UserFullInfo(databaseTools, bio);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return new StringJoiner(", ", User.class.getSimpleName() + "[", "]")
|
||||||
|
.add("firstName='" + firstName + "'")
|
||||||
|
.add("username='" + username + "'")
|
||||||
|
.add("fullInfo=" + fullInfo)
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
}
|
31
src/test/java/it/cavallium/strangedb/tests/UserFullInfo.java
Normal file
31
src/test/java/it/cavallium/strangedb/tests/UserFullInfo.java
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
package it.cavallium.strangedb.tests;
|
||||||
|
|
||||||
|
import it.cavallium.strangedb.java.annotations.DbDataType;
|
||||||
|
import it.cavallium.strangedb.java.annotations.DbField;
|
||||||
|
import it.cavallium.strangedb.java.database.IDatabaseTools;
|
||||||
|
import it.cavallium.strangedb.java.objects.EnhancedObject;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.StringJoiner;
|
||||||
|
|
||||||
|
public class UserFullInfo extends EnhancedObject {
|
||||||
|
|
||||||
|
@DbField(id = 0, name = "bio")
|
||||||
|
public String bio;
|
||||||
|
|
||||||
|
public UserFullInfo() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserFullInfo(IDatabaseTools databaseTools, String bio) throws IOException {
|
||||||
|
super(databaseTools);
|
||||||
|
this.bio = bio;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return new StringJoiner(", ", UserFullInfo.class.getSimpleName() + "[", "]")
|
||||||
|
.add("bio='" + bio + "'")
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
}
|
@ -1,13 +1,13 @@
|
|||||||
package it.cavallium.strangedb.tests;
|
package it.cavallium.strangedb.tests;
|
||||||
|
|
||||||
import it.cavallium.strangedb.EnhancedObjectUpgrader;
|
import it.cavallium.strangedb.java.objects.EnhancedObjectUpgrader;
|
||||||
import it.cavallium.strangedb.annotations.DbDataType;
|
import it.cavallium.strangedb.java.annotations.DbDataType;
|
||||||
import it.cavallium.strangedb.annotations.DbField;
|
import it.cavallium.strangedb.java.annotations.DbField;
|
||||||
import it.cavallium.strangedb.annotations.DbPrimitiveField;
|
import it.cavallium.strangedb.java.annotations.DbPrimitiveField;
|
||||||
import it.cavallium.strangedb.annotations.DbPrimitiveType;
|
import it.cavallium.strangedb.java.annotations.DbPrimitiveType;
|
||||||
import it.cavallium.strangedb.EnhancedObject;
|
import it.cavallium.strangedb.java.objects.EnhancedObject;
|
||||||
import it.cavallium.strangedb.database.IDatabaseTools;
|
import it.cavallium.strangedb.java.database.IDatabaseTools;
|
||||||
import it.cavallium.strangedb.annotations.DbClass;
|
import it.cavallium.strangedb.java.annotations.DbClass;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@ -0,0 +1,67 @@
|
|||||||
|
package it.cavallium.strangedb.tests.performance;
|
||||||
|
|
||||||
|
import it.cavallium.strangedb.java.objects.lists.EnhancedObjectStrangeDbList;
|
||||||
|
import it.cavallium.strangedb.java.objects.lists.ListQuery;
|
||||||
|
import it.cavallium.strangedb.java.objects.lists.ValuePointer;
|
||||||
|
import it.cavallium.strangedb.java.objects.lists.operations.ContainsIgnoreCase;
|
||||||
|
import it.cavallium.strangedb.tests.Performance;
|
||||||
|
import it.cavallium.strangedb.tests.query.*;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
public class PerformanceListQuery implements PerformanceTest {
|
||||||
|
private final int items;
|
||||||
|
private Performance.PreloadedListContainer preloadedListContainer;
|
||||||
|
|
||||||
|
public PerformanceListQuery(int items) {
|
||||||
|
this.items = items;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setup() throws IOException {
|
||||||
|
Performance.regenDb();
|
||||||
|
preloadedListContainer = Performance.db.loadRoot(Performance.PreloadedListContainer::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void perTestSetup() throws IOException {
|
||||||
|
preloadedListContainer.listOfMessages = new EnhancedObjectStrangeDbList<>(Performance.db);
|
||||||
|
|
||||||
|
Random random = new Random();
|
||||||
|
for (int i = 0; i < items; i++) {
|
||||||
|
EMessageContent content;
|
||||||
|
if (random.nextBoolean()) {
|
||||||
|
List<String> stringList = new ArrayList<>();
|
||||||
|
for (int j = 0; j < 10; j++) {
|
||||||
|
stringList.add("[entity]");
|
||||||
|
}
|
||||||
|
byte[] stringBytes = new byte[200];
|
||||||
|
random.nextBytes(stringBytes);
|
||||||
|
content = new EMessageText(Performance.db, new EFormattedText(Performance.db, new String(stringBytes, StandardCharsets.UTF_8) + (random.nextBoolean() ? "not found" : " text to find!"), stringList.toArray(new String[0])));
|
||||||
|
} else {
|
||||||
|
content = new EMessageOtherContent(Performance.db, "EMPTY ABCDEFG");
|
||||||
|
}
|
||||||
|
EMessage message = new EMessage(Performance.db, content);
|
||||||
|
preloadedListContainer.listOfMessages.add(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void test() throws IOException {
|
||||||
|
PerformanceListQueryTest.test(preloadedListContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void perTestEnd() throws IOException {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void end() throws IOException {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
package it.cavallium.strangedb.tests.performance;
|
||||||
|
|
||||||
|
import it.cavallium.strangedb.java.objects.lists.ListQuery;
|
||||||
|
import it.cavallium.strangedb.java.objects.lists.ValuePointer;
|
||||||
|
import it.cavallium.strangedb.java.objects.lists.operations.ContainsIgnoreCase;
|
||||||
|
import it.cavallium.strangedb.tests.Performance;
|
||||||
|
import it.cavallium.strangedb.tests.query.EFormattedText;
|
||||||
|
import it.cavallium.strangedb.tests.query.EMessage;
|
||||||
|
import it.cavallium.strangedb.tests.query.EMessageText;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
public class PerformanceListQueryTest {
|
||||||
|
|
||||||
|
public static void test(Performance.PreloadedListContainer preloadedListContainer) throws IOException {
|
||||||
|
ListQuery query = ListQuery.create(
|
||||||
|
ValuePointer.ofField(EMessage.class, "content").field(EMessageText.class, "text").field(EFormattedText.class, "text"),
|
||||||
|
ContainsIgnoreCase.containsValue("text to find"));
|
||||||
|
ArrayList<EMessage> results = preloadedListContainer.listOfMessages.query(query).asList();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,71 @@
|
|||||||
|
package it.cavallium.strangedb.tests.performance;
|
||||||
|
|
||||||
|
import it.cavallium.strangedb.functionalinterfaces.RunnableWithIO;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public interface PerformanceTest {
|
||||||
|
void setup() throws IOException;
|
||||||
|
void perTestSetup() throws IOException;
|
||||||
|
void test() throws IOException;
|
||||||
|
void perTestEnd() throws IOException;
|
||||||
|
void end() throws IOException;
|
||||||
|
|
||||||
|
static PerformanceTest createTest(RunnableWithIO setupRunnable, RunnableWithIO perTestSetupRunnable, RunnableWithIO testRunnable, RunnableWithIO perTestEndRunnable, RunnableWithIO endRunnable) {
|
||||||
|
return new PerformanceTest() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setup() throws IOException {
|
||||||
|
setupRunnable.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void perTestSetup() throws IOException {
|
||||||
|
perTestSetupRunnable.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void test() throws IOException {
|
||||||
|
testRunnable.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void perTestEnd() throws IOException {
|
||||||
|
perTestEndRunnable.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void end() throws IOException {
|
||||||
|
endRunnable.run();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static PerformanceTest createTest(RunnableWithIO perTestSetupRunnable, RunnableWithIO testRunnable, RunnableWithIO perTestEndRunnable) {
|
||||||
|
return new PerformanceTest() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setup() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void perTestSetup() throws IOException {
|
||||||
|
perTestSetupRunnable.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void test() throws IOException {
|
||||||
|
testRunnable.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void perTestEnd() throws IOException {
|
||||||
|
perTestEndRunnable.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void end() {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
package it.cavallium.strangedb.tests.query;
|
||||||
|
|
||||||
|
import it.cavallium.strangedb.java.annotations.DbDataType;
|
||||||
|
import it.cavallium.strangedb.java.annotations.DbField;
|
||||||
|
import it.cavallium.strangedb.java.database.IDatabaseTools;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class EFormattedText extends EMessageContent {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The text.
|
||||||
|
*/
|
||||||
|
@DbField(id = 0, type = DbDataType.OBJECT, name = "text")
|
||||||
|
public String text;
|
||||||
|
/**
|
||||||
|
* Entities contained in the text.
|
||||||
|
*/
|
||||||
|
@DbField(id = 1, type = DbDataType.OBJECT, name = "entities")
|
||||||
|
public String[] entities;
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
public EFormattedText() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public EFormattedText(IDatabaseTools tools, String text, String[] entities) throws IOException {
|
||||||
|
super(tools);
|
||||||
|
this.text = text;
|
||||||
|
this.entities =entities;
|
||||||
|
}
|
||||||
|
}
|
138
src/test/java/it/cavallium/strangedb/tests/query/EMessage.java
Normal file
138
src/test/java/it/cavallium/strangedb/tests/query/EMessage.java
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
package it.cavallium.strangedb.tests.query;
|
||||||
|
|
||||||
|
import it.cavallium.strangedb.java.annotations.DbDataType;
|
||||||
|
import it.cavallium.strangedb.java.annotations.DbField;
|
||||||
|
import it.cavallium.strangedb.java.annotations.DbPrimitiveField;
|
||||||
|
import it.cavallium.strangedb.java.annotations.DbPrimitiveType;
|
||||||
|
import it.cavallium.strangedb.java.database.IDatabaseTools;
|
||||||
|
import it.cavallium.strangedb.java.objects.EnhancedObject;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class EMessage extends EnhancedObject {
|
||||||
|
/**
|
||||||
|
* Message identifier, unique for the chat to which the message belongs.
|
||||||
|
*/
|
||||||
|
@DbPrimitiveField(id = 0, type = DbPrimitiveType.LONG, name = "id")
|
||||||
|
public long id;
|
||||||
|
/**
|
||||||
|
* Identifier of the user who sent the message; 0 if unknown. It is unknown for channel posts.
|
||||||
|
*/
|
||||||
|
@DbPrimitiveField(id = 1, type = DbPrimitiveType.INTEGER, name = "senderUserId")
|
||||||
|
public int senderUserId;
|
||||||
|
/**
|
||||||
|
* Chat identifier.
|
||||||
|
*/
|
||||||
|
@DbPrimitiveField(id = 2, type = DbPrimitiveType.LONG, name = "chatId")
|
||||||
|
public long chatId;
|
||||||
|
/**
|
||||||
|
* Information about the sending state of the message; may be null.
|
||||||
|
*/
|
||||||
|
@DbField(id = 0, type = DbDataType.OBJECT, name = "sendingState")
|
||||||
|
public Object sendingState;
|
||||||
|
/**
|
||||||
|
* True, if the message is outgoing.
|
||||||
|
*/
|
||||||
|
@DbPrimitiveField(id = 3, type = DbPrimitiveType.BOOLEAN, name = "isOutgoing")
|
||||||
|
public boolean isOutgoing;
|
||||||
|
/**
|
||||||
|
* True, if the message can be edited.
|
||||||
|
*/
|
||||||
|
@DbPrimitiveField(id = 4, type = DbPrimitiveType.BOOLEAN, name = "canBeEdited")
|
||||||
|
public boolean canBeEdited;
|
||||||
|
/**
|
||||||
|
* True, if the message can be forwarded.
|
||||||
|
*/
|
||||||
|
@DbPrimitiveField(id = 5, type = DbPrimitiveType.BOOLEAN, name = "canBeForwarded")
|
||||||
|
public boolean canBeForwarded;
|
||||||
|
/**
|
||||||
|
* True, if the message can be deleted only for the current user while other users will continue to see it.
|
||||||
|
*/
|
||||||
|
@DbPrimitiveField(id = 6, type = DbPrimitiveType.BOOLEAN, name = "canBeDeletedOnlyForSelf")
|
||||||
|
public boolean canBeDeletedOnlyForSelf;
|
||||||
|
/**
|
||||||
|
* True, if the message can be deleted for all users.
|
||||||
|
*/
|
||||||
|
@DbPrimitiveField(id = 7, type = DbPrimitiveType.BOOLEAN, name = "canBeDeletedForAllUsers")
|
||||||
|
public boolean canBeDeletedForAllUsers;
|
||||||
|
/**
|
||||||
|
* True, if the message is a channel post. All messages to channels are channel posts, all other messages are not channel posts.
|
||||||
|
*/
|
||||||
|
@DbPrimitiveField(id = 8, type = DbPrimitiveType.BOOLEAN, name = "isChannelPost")
|
||||||
|
public boolean isChannelPost;
|
||||||
|
/**
|
||||||
|
* True, if the message contains an unread mention for the current user.
|
||||||
|
*/
|
||||||
|
@DbPrimitiveField(id = 9, type = DbPrimitiveType.BOOLEAN, name = "containsUnreadMention")
|
||||||
|
public boolean containsUnreadMention;
|
||||||
|
/**
|
||||||
|
* Point in time (Unix timestamp) when the message was sent.
|
||||||
|
*/
|
||||||
|
@DbPrimitiveField(id = 10, type = DbPrimitiveType.INTEGER, name = "date")
|
||||||
|
public int date;
|
||||||
|
/**
|
||||||
|
* Point in time (Unix timestamp) when the message was last edited.
|
||||||
|
*/
|
||||||
|
@DbPrimitiveField(id = 11, type = DbPrimitiveType.INTEGER, name = "editDate")
|
||||||
|
public int editDate;
|
||||||
|
/**
|
||||||
|
* Information about the initial message sender; may be null.
|
||||||
|
*/
|
||||||
|
@DbField(id = 1, type = DbDataType.OBJECT, name = "forwardInfo")
|
||||||
|
public Object forwardInfo;
|
||||||
|
/**
|
||||||
|
* If non-zero, the identifier of the message this message is replying to; can be the identifier of a deleted message.
|
||||||
|
*/
|
||||||
|
@DbPrimitiveField(id = 12, type = DbPrimitiveType.LONG, name = "replyToMessageId")
|
||||||
|
public long replyToMessageId;
|
||||||
|
/**
|
||||||
|
* For self-destructing messages, the message's TTL (Time To Live), in seconds; 0 if none. TDLib will send updateDeleteMessages or updateMessageContent once the TTL expires.
|
||||||
|
*/
|
||||||
|
@DbPrimitiveField(id = 13, type = DbPrimitiveType.INTEGER, name = "ttl")
|
||||||
|
public int ttl;
|
||||||
|
/**
|
||||||
|
* Time left before the message expires, in seconds.
|
||||||
|
*/
|
||||||
|
@DbPrimitiveField(id = 14, type = DbPrimitiveType.DOUBLE, name = "ttlExpiresIn")
|
||||||
|
public double ttlExpiresIn;
|
||||||
|
/**
|
||||||
|
* If non-zero, the user identifier of the bot through which this message was sent.
|
||||||
|
*/
|
||||||
|
@DbPrimitiveField(id = 15, type = DbPrimitiveType.INTEGER, name = "viaBotUserId")
|
||||||
|
public int viaBotUserId;
|
||||||
|
/**
|
||||||
|
* For channel posts, optional author signature.
|
||||||
|
*/
|
||||||
|
@DbField(id = 2, type = DbDataType.OBJECT, name = "authorSignature")
|
||||||
|
public String authorSignature;
|
||||||
|
/**
|
||||||
|
* Number of times this message was viewed.
|
||||||
|
*/
|
||||||
|
@DbPrimitiveField(id = 16, type = DbPrimitiveType.INTEGER, name = "views")
|
||||||
|
public int views;
|
||||||
|
/**
|
||||||
|
* Unique identifier of an album this message belongs to. Only photos and videos can be grouped together in albums.
|
||||||
|
*/
|
||||||
|
@DbPrimitiveField(id = 17, type = DbPrimitiveType.LONG, name = "mediaAlbumId")
|
||||||
|
public long mediaAlbumId;
|
||||||
|
/**
|
||||||
|
* Content of the message.
|
||||||
|
*/
|
||||||
|
@DbField(id = 3, type = DbDataType.ENHANCED_OBJECT, name = "content")
|
||||||
|
public EMessageContent content;
|
||||||
|
/**
|
||||||
|
* Reply markup for the message; may be null.
|
||||||
|
*/
|
||||||
|
@DbField(id = 4, type = DbDataType.OBJECT, name = "replyMarkup")
|
||||||
|
public Object replyMarkup;
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
public EMessage() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public EMessage(IDatabaseTools tools, EMessageContent content) throws IOException {
|
||||||
|
super(tools);
|
||||||
|
this.content = content;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
package it.cavallium.strangedb.tests.query;
|
||||||
|
|
||||||
|
import it.cavallium.strangedb.java.database.IDatabaseTools;
|
||||||
|
import it.cavallium.strangedb.java.objects.EnhancedObject;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public abstract class EMessageContent extends EnhancedObject {
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
public EMessageContent() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public EMessageContent(IDatabaseTools tools) throws IOException {
|
||||||
|
super(tools);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
package it.cavallium.strangedb.tests.query;
|
||||||
|
|
||||||
|
import it.cavallium.strangedb.java.annotations.DbDataType;
|
||||||
|
import it.cavallium.strangedb.java.annotations.DbField;
|
||||||
|
import it.cavallium.strangedb.java.database.IDatabaseTools;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class EMessageOtherContent extends EMessageContent {
|
||||||
|
|
||||||
|
@DbField(id = 0, type = DbDataType.OBJECT, name = "content")
|
||||||
|
public Object content;
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
public EMessageOtherContent() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public EMessageOtherContent(IDatabaseTools tools, Object content) throws IOException {
|
||||||
|
super(tools);
|
||||||
|
this.content = content;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
package it.cavallium.strangedb.tests.query;
|
||||||
|
|
||||||
|
import it.cavallium.strangedb.java.annotations.DbDataType;
|
||||||
|
import it.cavallium.strangedb.java.annotations.DbField;
|
||||||
|
import it.cavallium.strangedb.java.database.IDatabaseTools;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class EMessageText extends EMessageContent {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Text of the message.
|
||||||
|
*/
|
||||||
|
@DbField(id = 0, type = DbDataType.ENHANCED_OBJECT, name = "text")
|
||||||
|
public EFormattedText text;
|
||||||
|
/**
|
||||||
|
* A preview of the web page that's mentioned in the text; may be null.
|
||||||
|
*/
|
||||||
|
@DbField(id = 1, type = DbDataType.OBJECT, name = "webPage")
|
||||||
|
public Object webPage;
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
public EMessageText() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public EMessageText(IDatabaseTools tools, EFormattedText text) throws IOException {
|
||||||
|
super(tools);
|
||||||
|
this.text = text;
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +1,10 @@
|
|||||||
package it.cavallium.strangedb.utils;
|
package it.cavallium.strangedb.utils;
|
||||||
|
|
||||||
import it.cavallium.strangedb.annotations.DbPrimitiveField;
|
import it.cavallium.strangedb.java.annotations.DbPrimitiveField;
|
||||||
import it.cavallium.strangedb.annotations.DbPrimitiveType;
|
import it.cavallium.strangedb.java.annotations.DbPrimitiveType;
|
||||||
import it.cavallium.strangedb.database.Database;
|
import it.cavallium.strangedb.database.DatabaseCore;
|
||||||
import it.cavallium.strangedb.EnhancedObject;
|
import it.cavallium.strangedb.java.database.DatabaseJava;
|
||||||
|
import it.cavallium.strangedb.java.objects.EnhancedObject;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
@ -16,7 +17,7 @@ public class NSimplestClass extends EnhancedObject {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public NSimplestClass(Database database) throws IOException {
|
public NSimplestClass(DatabaseJava database) throws IOException {
|
||||||
super(database);
|
super(database);
|
||||||
field1 = true;
|
field1 = true;
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
package it.cavallium.strangedb.utils;
|
package it.cavallium.strangedb.utils;
|
||||||
|
|
||||||
import it.cavallium.strangedb.annotations.*;
|
import it.cavallium.strangedb.database.DatabaseCore;
|
||||||
import it.cavallium.strangedb.functionalinterfaces.RunnableWithIO;
|
import it.cavallium.strangedb.functionalinterfaces.RunnableWithIO;
|
||||||
|
import it.cavallium.strangedb.java.annotations.*;
|
||||||
|
import it.cavallium.strangedb.java.database.DatabaseJava;
|
||||||
|
import it.cavallium.strangedb.tests.Clean;
|
||||||
|
import it.cavallium.strangedb.tests.MultipleEnhancedObjects;
|
||||||
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
||||||
import it.cavallium.strangedb.database.Database;
|
import it.cavallium.strangedb.java.objects.EnhancedObject;
|
||||||
import it.cavallium.strangedb.EnhancedObject;
|
import it.cavallium.strangedb.java.database.IDatabaseTools;
|
||||||
import it.cavallium.strangedb.database.IDatabaseTools;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -23,7 +26,7 @@ public class NTestUtils {
|
|||||||
|
|
||||||
public static class WrappedDb {
|
public static class WrappedDb {
|
||||||
|
|
||||||
private Database db;
|
private DatabaseJava db;
|
||||||
private Path tempDir;
|
private Path tempDir;
|
||||||
private RunnableWithIO r;
|
private RunnableWithIO r;
|
||||||
|
|
||||||
@ -42,12 +45,18 @@ public class NTestUtils {
|
|||||||
|
|
||||||
public WrappedDb create(ConsumerWithIO<NTestUtils.WrappedDb> r) throws IOException {
|
public WrappedDb create(ConsumerWithIO<NTestUtils.WrappedDb> r) throws IOException {
|
||||||
this.r = () -> r.accept(WrappedDb.this);
|
this.r = () -> r.accept(WrappedDb.this);
|
||||||
this.create();
|
return this.create();
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Database openDatabase() throws IOException {
|
private DatabaseJava openDatabase() throws IOException {
|
||||||
return new Database(tempDir.resolve(Paths.get("data.db")), tempDir.resolve(Paths.get("blocks.dat")), tempDir.resolve(Paths.get("references.dat")));
|
DatabaseJava db = new DatabaseJava(tempDir.resolve(Paths.get("data.db")), tempDir.resolve(Paths.get("references.dat")));
|
||||||
|
int id = 0;
|
||||||
|
db.registerClass(RootClass.class, id++);
|
||||||
|
db.registerClass(LongArrayList.class, id++);
|
||||||
|
db.registerClass(NSimplestClass.class, id++);
|
||||||
|
db.registerClass(Clean.RootTwoClasses.class, id++);
|
||||||
|
db.registerClass(MultipleEnhancedObjects.RootTwoClasses.class, id++);
|
||||||
|
return db;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void delete() throws IOException {
|
public void delete() throws IOException {
|
||||||
@ -55,7 +64,7 @@ public class NTestUtils {
|
|||||||
deleteDir(tempDir);
|
deleteDir(tempDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Database get() {
|
public DatabaseJava get() {
|
||||||
return db;
|
return db;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -232,7 +241,7 @@ public class NTestUtils {
|
|||||||
@DbField(id = 0, type = DbDataType.OBJECT)
|
@DbField(id = 0, type = DbDataType.OBJECT)
|
||||||
public String field7;
|
public String field7;
|
||||||
|
|
||||||
@DbField(id = 1, type = DbDataType.REFERENCES_LIST)
|
@DbField(id = 1, type = DbDataType.OBJECT)
|
||||||
public LongArrayList field8;
|
public LongArrayList field8;
|
||||||
|
|
||||||
@DbField(id = 2, type = DbDataType.ENHANCED_OBJECT)
|
@DbField(id = 2, type = DbDataType.ENHANCED_OBJECT)
|
||||||
@ -246,32 +255,38 @@ public class NTestUtils {
|
|||||||
super(databaseTools);
|
super(databaseTools);
|
||||||
}
|
}
|
||||||
|
|
||||||
@DbPropertyGetter(id = 0, type = DbDataType.OBJECT)
|
@DbProperty(id = 0, type = DbDataType.OBJECT)
|
||||||
|
@DbPropertyGetter
|
||||||
public String get7() {
|
public String get7() {
|
||||||
return getProperty();
|
return getProperty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@DbPropertyGetter(id = 1, type = DbDataType.REFERENCES_LIST)
|
@DbProperty(id = 1, type = DbDataType.OBJECT)
|
||||||
|
@DbPropertyGetter
|
||||||
public LongArrayList get8() {
|
public LongArrayList get8() {
|
||||||
return getProperty();
|
return getProperty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@DbPropertyGetter(id = 2, type = DbDataType.ENHANCED_OBJECT)
|
@DbProperty(id = 2, type = DbDataType.ENHANCED_OBJECT)
|
||||||
|
@DbPropertyGetter
|
||||||
public NSimplestClass get9() {
|
public NSimplestClass get9() {
|
||||||
return getProperty();
|
return getProperty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@DbPropertySetter(id = 0, type = DbDataType.OBJECT)
|
@DbProperty(id = 0, type = DbDataType.OBJECT)
|
||||||
|
@DbPropertySetter
|
||||||
public void set7(String val) {
|
public void set7(String val) {
|
||||||
setProperty(val);
|
setProperty(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
@DbPropertySetter(id = 1, type = DbDataType.REFERENCES_LIST)
|
@DbProperty(id = 1, type = DbDataType.OBJECT)
|
||||||
|
@DbPropertySetter
|
||||||
public void set8(LongArrayList val) {
|
public void set8(LongArrayList val) {
|
||||||
setProperty(val);
|
setProperty(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
@DbPropertySetter(id = 2, type = DbDataType.ENHANCED_OBJECT)
|
@DbProperty(id = 2, type = DbDataType.ENHANCED_OBJECT)
|
||||||
|
@DbPropertySetter
|
||||||
public void set9(NSimplestClass val) {
|
public void set9(NSimplestClass val) {
|
||||||
setProperty(val);
|
setProperty(val);
|
||||||
}
|
}
|
||||||
|
1
strangedb-core
Submodule
1
strangedb-core
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 4d266533db8c0bb7d1d4fa091080ae6dd082d29b
|
Loading…
Reference in New Issue
Block a user