Compare commits

...

5 Commits

Author SHA1 Message Date
Andrea Cavalli 641233af3e Multithread 2019-04-20 03:46:59 +02:00
Andrea Cavalli 14702d759c Merge branch 'fast-search-2' 2019-04-19 02:56:38 +02:00
Andrea Cavalli e0a7c2f6c0 Cleaner, queries and locks 2019-04-19 02:55:47 +02:00
Andrea Cavalli a1624bf91e Added queries 2019-04-18 16:08:22 +02:00
Andrea Cavalli dea0eea5b7 Bugfix 2019-04-17 18:19:01 +02:00
44 changed files with 2467 additions and 475 deletions

13
pom.xml
View File

@ -6,7 +6,7 @@
<groupId>it.cavallium</groupId>
<artifactId>strangedb</artifactId>
<version>1.5.5</version>
<version>1.5.7</version>
<name>strangedb-java</name>
<url>https://git.ignuranza.net/andreacavalli/strangedb</url>
@ -37,15 +37,22 @@
<artifactId>fastutil</artifactId>
<version>8.2.2</version>
</dependency>
<!--
<dependency>
<groupId>de.ruedigermoeller</groupId>
<artifactId>fst</artifactId>
<version>2.56</version>
</dependency>
-->
<dependency>
<groupId>com.esotericsoftware</groupId>
<artifactId>kryo</artifactId>
<version>5.0.0-RC1</version>
<version>5.0.0-RC4</version>
</dependency>
<dependency>
<groupId>it.cavallium</groupId>
<artifactId>strangedb-core</artifactId>
<version>1.5.5</version>
<version>1.5.7</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
<dependency>

View File

@ -10,4 +10,5 @@ import java.lang.annotation.Target;
public @interface DbField {
int id();
DbDataType type() default DbDataType.OBJECT;
String name() default "";
}

View File

@ -10,4 +10,5 @@ import java.lang.annotation.Target;
public @interface DbPrimitiveField {
int id();
DbPrimitiveType type();
String name() default "";
}

View File

@ -0,0 +1,14 @@
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 DbProperty {
int id();
DbDataType type() default DbDataType.OBJECT;
String name() default "";
}

View File

@ -8,6 +8,4 @@ import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DbPropertyGetter {
int id();
DbDataType type() default DbDataType.OBJECT;
}

View File

@ -8,6 +8,4 @@ import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DbPropertySetter {
int id();
DbDataType type() default DbDataType.OBJECT;
}

View File

@ -1,157 +0,0 @@
package it.cavallium.strangedb.java.database;
import it.cavallium.strangedb.java.annotations.*;
import org.apache.commons.lang3.reflect.FieldUtils;
import it.cavallium.strangedb.java.objects.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);
}
}

View File

@ -23,14 +23,14 @@ public class DatabaseEnhancedObjectUpgrader implements EnhancedObjectUpgrader {
@Override
@SuppressWarnings("unchecked")
public Object getField(int id, DbDataType type, Supplier<Class<?>> enhancedClassType) throws IOException {
return objectsIO.loadData(type, fieldRefs[id], enhancedClassType);
public Object getField(int id, DbDataType type) throws IOException {
return objectsIO.loadData(type, fieldRefs[id]);
}
@Override
@SuppressWarnings("unchecked")
public Object getMethod(int id, DbDataType type, Supplier<Class<?>> enhancedClassType) throws IOException {
return objectsIO.loadData(type, methodRefs[id], enhancedClassType);
public Object getMethod(int id, DbDataType type) throws IOException {
return objectsIO.loadData(type, methodRefs[id]);
}
@SuppressWarnings("unchecked")

View File

@ -1,21 +1,33 @@
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 java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import static it.cavallium.strangedb.database.references.DatabaseReferencesMetadata.*;
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 blocksMetaFile, Path referencesMetaFile) throws IOException {
super(dataFile, blocksMetaFile, referencesMetaFile);
this.databaseTools = this;
this.objectsIO = new DatabaseObjectsIO(databaseTools, referencesIO);
this.hasLoadedRootObject = false;
}
@Override
@ -23,17 +35,19 @@ public class DatabaseJava extends DatabaseCore implements IDatabaseTools {
if (this.closed) {
throw new IOException("The database has been already closed!");
}
this.objectsIO.setEnhancedObject(0, loadedRootObject);
if (hasLoadedRootObject) {
this.objectsIO.setEnhancedObject(0, loadedRootObject);
}
super.close();
}
public <T extends EnhancedObject> T loadRoot(Class<T> type, FunctionWithIO<IDatabaseTools, T> ifAbsent) throws IOException {
if (loadedRootObject != null) {
public <T extends EnhancedObject> T loadRoot(FunctionWithIO<IDatabaseTools, T> ifAbsent) throws IOException {
if (hasLoadedRootObject) {
throw new RuntimeException("Root already set!");
}
T root;
if (referencesMetadata.getFirstFreeReference() > 0) {
root = objectsIO.loadEnhancedObject(0, type);
root = objectsIO.loadEnhancedObject(0);
} else {
if (objectsIO.newNullObject() != 0) {
throw new IOException("Can't allocate root!");
@ -43,13 +57,130 @@ public class DatabaseJava extends DatabaseCore implements IDatabaseTools {
}
}
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 (referencesMetadata.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();
}
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);
DatabaseJava databaseToClean = instantiateNewDatabase(newDataFile, newBlocksFile, newReferencesFile);
DatabaseJava newDatabase = instantiateNewDatabase(dataFile, blocksMetaFile, referencesMetaFile);
long firstFreeReference = databaseToClean.referencesMetadata.getFirstFreeReference();
long referencesCount = 0;
long blocksCount = databaseToClean.blocksMetadata.getTotalBlocksCount();
long writtenReferences = 0;
long writtenBlocks = 0;
LongArrayList idsToKeep = new LongArrayList();
cleanRef(databaseToClean, idsToKeep, 0);
for (int referenceID = 0; referenceID < firstFreeReference; referenceID++) {
try {
ReferenceInfo ref = databaseToClean.referencesMetadata.getCleanReference(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++;
if (buffer.limit() > 0) {
writtenBlocks++;
}
}
}
} catch (IOException ex) {
System.out.println("Error while reading reference " + referenceID + ". References written: " + writtenReferences);
}
}
System.out.println("[Java Cleaner] References written: " + writtenReferences + ". Removed " + (blocksCount - writtenBlocks) + " blocks. Removed " + (referencesCount - writtenReferences) + " references.");
databaseToClean.close();
newDatabase.close();
Files.deleteIfExists(newDataFile);
Files.deleteIfExists(newBlocksFile);
Files.deleteIfExists(newReferencesFile);
}
private void cleanRef(DatabaseJava db, LongArrayList idsToKeep, long ref) throws IOException {
idsToKeep.add(ref);
ReferenceInfo refInfo = db.referencesMetadata.getCleanReference(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...");
}
}
}
}
protected void registerClass(Class<?> type, int id) {
this.objectsIO.registerClass(type, id);
}
protected DatabaseJava instantiateNewDatabase(Path dataFile, Path blocksMetaFile, Path referencesMetaFile) throws IOException {
return new DatabaseJava(dataFile, blocksMetaFile, referencesMetaFile);
}
@Override
public void initializeEnhancedObject(EnhancedObject enhancedObject) throws IOException {
this.objectsIO.getDataInitializer().initializeDbObject(enhancedObject);

View File

@ -1,12 +1,13 @@
package it.cavallium.strangedb.java.database;
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import it.cavallium.strangedb.java.objects.EnhancedObject;
import it.cavallium.strangedb.java.objects.EnhancedObjectFullInfo;
import it.cavallium.strangedb.database.references.DatabaseReferencesIO;
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;
@ -16,30 +17,31 @@ import it.unimi.dsi.fastutil.longs.LongList;
import it.unimi.dsi.fastutil.shorts.ShortArrayList;
import org.apache.commons.lang3.reflect.FieldUtils;
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;
import static it.cavallium.strangedb.database.references.DatabaseReferencesMetadata.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 DatabaseReferencesIO referencesIO;
private final Object accessLock = new Object();
private final DatabaseDataInitializer dataInitializer;
private Kryo kryo = new Kryo();
private final KryoSerializer serializer;
public DatabaseObjectsIO(IDatabaseTools databaseTools, DatabaseReferencesIO referencesIO) {
this.databaseTools = databaseTools;
this.referencesIO = referencesIO;
this.dataInitializer = new DatabaseDataInitializer(this);
kryo.setRegistrationRequired(false);
this.serializer = new KryoSerializer();
this.dataInitializer = new DatabaseDataInitializer();
int id = -90;
registerClass(boolean[].class, id++);
registerClass(byte[].class, id++);
@ -92,16 +94,79 @@ public class DatabaseObjectsIO implements IObjectsIO {
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, Class<T> objectType) throws IOException {
synchronized (accessLock) {
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;
}
int serializedVersion = Byte.toUnsignedInt(buffer.get());
//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];
@ -113,40 +178,42 @@ public class DatabaseObjectsIO implements IObjectsIO {
methodRefs[i] = buffer.getLong();
}
long nativeFieldsDataReference = buffer.getLong();
return preloadEnhancedObject(objectType, serializedVersion, nativeFieldsDataReference, fieldRefs,
methodRefs);
}
return new EnhancedObjectIndices(objectType, reference, fieldRefs, methodRefs, nativeFieldsDataReference);
}
@SuppressWarnings("unchecked")
Object loadData(DbDataType propertyType, long dataReference, Supplier<Class<?>> returnType) throws IOException {
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, (Class<? extends EnhancedObject>) returnType.get());
return loadEnhancedObject_(dataReference, null);
case OBJECT:
return loadObject(dataReference);
return loadObject_(dataReference);
case REFERENCES_LIST:
return loadReferencesList(dataReference);
return loadReferencesList_(dataReference);
default:
throw new NullPointerException("Unknown data type");
}
}
<T> void setData(long reference, DbDataType propertyType, T loadedPropertyValue) throws IOException {
private <T> void setData(long reference, DbDataType propertyType, T loadedPropertyValue) throws IOException {
switch (propertyType) {
case OBJECT:
setObject(reference, loadedPropertyValue);
setObject_(reference, loadedPropertyValue);
break;
case REFERENCES_LIST:
setReferencesList(reference, (LongArrayList) loadedPropertyValue);
setReferencesList_(reference, (LongArrayList) loadedPropertyValue);
break;
case ENHANCED_OBJECT:
setEnhancedObject(reference, (EnhancedObject) loadedPropertyValue);
setEnhancedObject_(reference, (EnhancedObject) loadedPropertyValue);
break;
}
}
<T extends EnhancedObject> void setPrimitives(T enhancedObject, long reference, DbPrimitiveType[] types, Field[] fields)
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 {
@ -188,160 +255,182 @@ public class DatabaseObjectsIO implements IObjectsIO {
throw new IOException(e);
}
buffer.flip();
referencesIO.writeToReference(reference, buffer.limit(), buffer);
referencesIO.writeToReference(reference, BLANK_DATA_CLEANER, 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!");
}
setEnhancedObject_(reference, value);
}
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);
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);
}
}
@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));
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 {
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;
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 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;
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 {
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);
}
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 {
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);
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);
buffer.putInt(items);
for (int i = 0; i < items; i++) {
buffer.putLong(value.getLong(i));
}
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 {
synchronized (accessLock) {
return referencesIO.allocateReference();
}
return newNullObject_();
}
private long newNullObject_() throws IOException {
return referencesIO.allocateReference();
}
@Override
public void loadProperty(EnhancedObject obj, int propertyId, Method property, DbDataType propertyType,
public void loadProperty(EnhancedObject obj, int propertyId, DbDataType propertyType,
long propertyUID) throws IOException {
synchronized (accessLock) {
obj.setProperty(propertyId, loadData(propertyType, propertyUID, property::getReturnType));
}
obj.setProperty(propertyId, loadData_(propertyType, propertyUID));
obj.setProperty(propertyId, loadData_(propertyType, propertyUID));
}
@Override
@ -349,21 +438,23 @@ public class DatabaseObjectsIO implements IObjectsIO {
if (id < -100) {
throw new IllegalArgumentException();
}
kryo.register(type, 100 + id);
final int realId = id + 100;
serializer.registerClass(type, realId);
}
private <T extends EnhancedObject> void preloadEnhancedObjectProperties(T obj, long[] propertyReferences) {
// Declare the variables needed to get the biggest property Id
// 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 biggestGetter = getBiggestPropertyGetterId_(unorderedPropertyGetters);
int biggestSetter = getBiggestPropertySetterId_(unorderedPropertySetters);
int biggestPropertyId = biggestGetter > biggestSetter ? biggestGetter : biggestSetter;
for (Method property : unorderedPropertySetters) {
DbPropertySetter fieldAnnotation = property.getAnnotation(DbPropertySetter.class);
DbProperty fieldAnnotation = property.getAnnotation(DbProperty.class);
int propertyId = fieldAnnotation.id();
if (propertyId > biggestPropertyId) {
biggestPropertyId = propertyId;
@ -374,12 +465,12 @@ public class DatabaseObjectsIO implements IObjectsIO {
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<>();
Map<String, DbProperty> setterMethods = new LinkedHashMap<>();
Map<String, DbProperty> getterMethods = new LinkedHashMap<>();
// Load the properties metadata
for (Method property : unorderedPropertyGetters) {
DbPropertyGetter propertyAnnotation = property.getAnnotation(DbPropertyGetter.class);
DbProperty propertyAnnotation = property.getAnnotation(DbProperty.class);
int propertyId = propertyAnnotation.id();
DbDataType propertyType = propertyAnnotation.type();
propertyTypes[propertyId] = propertyType;
@ -387,7 +478,7 @@ public class DatabaseObjectsIO implements IObjectsIO {
getterMethods.put(property.getName(), propertyAnnotation);
}
for (Method property : unorderedPropertySetters) {
DbPropertySetter propertyAnnotation = property.getAnnotation(DbPropertySetter.class);
DbProperty propertyAnnotation = property.getAnnotation(DbProperty.class);
int propertyId = propertyAnnotation.id();
DbDataType propertyType = propertyAnnotation.type();
propertyTypes[propertyId] = propertyType;
@ -399,10 +490,14 @@ public class DatabaseObjectsIO implements IObjectsIO {
getterMethods);
}
int getBiggestPropertyGetterId(Method[] unorderedPropertyGetters) {
public int getBiggestPropertyGetterId(Method[] unorderedPropertyGetters) {
return getBiggestPropertyGetterId_(unorderedPropertyGetters);
}
private int getBiggestPropertyGetterId_(Method[] unorderedPropertyGetters) {
int biggestPropertyId = -1;
for (Method property : unorderedPropertyGetters) {
DbPropertyGetter fieldAnnotation = property.getAnnotation(DbPropertyGetter.class);
DbProperty fieldAnnotation = property.getAnnotation(DbProperty.class);
int propertyId = fieldAnnotation.id();
if (propertyId > biggestPropertyId) {
biggestPropertyId = propertyId;
@ -411,10 +506,14 @@ public class DatabaseObjectsIO implements IObjectsIO {
return biggestPropertyId;
}
int getBiggestPropertySetterId(Method[] unorderedPropertySetters) {
public int getBiggestPropertySetterId(Method[] unorderedPropertySetters) {
return getBiggestPropertySetterId_(unorderedPropertySetters);
}
private int getBiggestPropertySetterId_(Method[] unorderedPropertySetters) {
int biggestPropertyId = -1;
for (Method property : unorderedPropertySetters) {
DbPropertySetter fieldAnnotation = property.getAnnotation(DbPropertySetter.class);
DbProperty fieldAnnotation = property.getAnnotation(DbProperty.class);
int propertyId = fieldAnnotation.id();
if (propertyId > biggestPropertyId) {
biggestPropertyId = propertyId;
@ -425,10 +524,10 @@ public class DatabaseObjectsIO implements IObjectsIO {
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);
// Declare the variables needed to getBlock the biggest field Id
Field[] unorderedFields = getPrimitiveFields_(obj);
// Find the biggest field Id
int biggestFieldId = getBiggestPrimitiveFieldId(unorderedFields);
int biggestFieldId = getBiggestPrimitiveFieldId_(unorderedFields);
// Declare the other variables
Field[] fields = new Field[biggestFieldId + 1];
@ -452,31 +551,33 @@ public class DatabaseObjectsIO implements IObjectsIO {
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;
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) {
@ -489,8 +590,8 @@ public class DatabaseObjectsIO implements IObjectsIO {
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);
// Declare the variables needed to getBlock the biggest field Id
Field[] unorderedFields = getFields_(obj);
// Find the biggest field Id
int biggestFieldId = getBiggestFieldId(unorderedFields);
@ -503,7 +604,7 @@ public class DatabaseObjectsIO implements IObjectsIO {
DbField fieldAnnotation = field.getAnnotation(DbField.class);
int fieldId = fieldAnnotation.id();
DbDataType fieldType = fieldAnnotation.type();
loadField(obj, field, fieldType, fieldReferences[fieldId]);
loadField_(obj, field, fieldType, fieldReferences[fieldId]);
fields[fieldId] = field;
orderedFieldTypes[fieldId] = fieldType;
}
@ -511,9 +612,14 @@ public class DatabaseObjectsIO implements IObjectsIO {
obj.setFields(fields, orderedFieldTypes, fieldReferences);
}
<T extends EnhancedObject> void loadField(T obj, Field field, DbDataType fieldType, long fieldReference)
public <T extends EnhancedObject> void loadField(T obj, Field field, DbDataType fieldType, long fieldReference)
throws IOException {
Object data = loadData(fieldType, fieldReference, field::getType);
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 (fieldType == DbDataType.OBJECT && data != null) {
if (!field.getType().isInstance(data)) {
@ -527,15 +633,27 @@ public class DatabaseObjectsIO implements IObjectsIO {
}
}
<T extends EnhancedObject> Field[] getFields(T obj) {
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);
}
<T extends EnhancedObject> Field[] getPrimitiveFields(T obj) {
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);
}
int getBiggestFieldId(Field[] unorderedFields) {
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);
@ -547,7 +665,11 @@ public class DatabaseObjectsIO implements IObjectsIO {
return biggestFieldId;
}
int getBiggestPrimitiveFieldId(Field[] unorderedFields) {
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);
@ -559,9 +681,10 @@ public class DatabaseObjectsIO implements IObjectsIO {
return biggestFieldId;
}
private <T extends EnhancedObject> T toInstance(Class<T> type) throws IOException {
@SuppressWarnings("unchecked")
private <T extends EnhancedObject> T toInstance(Class<?> type) throws IOException {
try {
T obj = type.getConstructor().newInstance();
T obj = (T) type.getConstructor().newInstance();
obj.setDatabaseTools(databaseTools);
return obj;
} catch (NoSuchMethodException e) {
@ -572,8 +695,7 @@ public class DatabaseObjectsIO implements IObjectsIO {
}
}
private <T extends EnhancedObject> T preloadEnhancedObject(Class<T> objectType, int serializedVersion,
long nativeFieldsRef, long[] fieldRefs, long[] methodRefs) throws IOException {
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);
@ -583,25 +705,29 @@ public class DatabaseObjectsIO implements IObjectsIO {
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 {
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();
ids[i] = newNullObject_();
}
return ids;
}
@ -611,4 +737,151 @@ public class DatabaseObjectsIO implements IObjectsIO {
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);
}
}
}

View File

@ -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);
}
}
}
**/

View File

@ -2,6 +2,7 @@ package it.cavallium.strangedb.java.database;
import it.cavallium.strangedb.java.objects.EnhancedObject;
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.LongList;
@ -9,7 +10,13 @@ import java.io.IOException;
import java.lang.reflect.Method;
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;
@ -47,7 +54,7 @@ public interface IObjectsIO {
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);

View File

@ -0,0 +1,16 @@
package it.cavallium.strangedb.java.database;
import java.io.IOException;
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);
}

View File

@ -0,0 +1,97 @@
package it.cavallium.strangedb.java.database;
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.ByteBufferOutput;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.KryoDataOutput;
import com.esotericsoftware.kryo.io.Output;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
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);
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) {
for (int i = 0; i < KRYO_INSTANCES; i++) {
locks[i].lock();
try {
kryo[i].register(type, id);
} finally {
locks[i].unlock();
}
}
}
}

View File

@ -1,18 +1,16 @@
package it.cavallium.strangedb.java.objects;
import it.cavallium.strangedb.java.annotations.*;
import it.cavallium.strangedb.java.database.IDatabaseTools;
import org.apache.commons.lang3.NotImplementedException;
import org.apache.commons.lang3.reflect.MethodUtils;
import it.cavallium.strangedb.java.annotations.DbClass;
import it.cavallium.strangedb.java.annotations.DbDataType;
import it.cavallium.strangedb.java.annotations.DbPrimitiveType;
import it.cavallium.strangedb.java.annotations.DbPropertyGetter;
import it.cavallium.strangedb.java.annotations.DbPropertySetter;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public abstract class EnhancedObject {
protected final int version;
@ -29,8 +27,8 @@ public abstract class EnhancedObject {
private long[] propertyReferences;
private boolean[] loadedProperties;
private Object[] loadedPropertyValues;
private Map<String, DbPropertySetter> setterMethods;
private Map<String, DbPropertyGetter> getterMethods;
private Map<String, DbProperty> setterMethods;
private Map<String, DbProperty> getterMethods;
public EnhancedObject() {
version = getClassVersion();
@ -62,7 +60,7 @@ public abstract class EnhancedObject {
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.propertySetters = propertySetters;
this.propertyTypes = propertyTypes;
@ -82,7 +80,7 @@ public abstract class EnhancedObject {
StackWalker walker = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
StackWalker.StackFrame stackFrame = walker.walk(f -> f.skip(1).findFirst().orElse(null));
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);
} catch (IOException | NoSuchMethodException e) {
throw new RuntimeException(e);
@ -90,10 +88,10 @@ public abstract class EnhancedObject {
}
@SuppressWarnings("unchecked")
private <T> T getProperty(int propertyId) throws IOException {
public <T> T getProperty(int propertyId) throws IOException {
if (!loadedProperties[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];
}
@ -101,7 +99,7 @@ public abstract class EnhancedObject {
public <T> void setProperty(T value) {
StackWalker walker = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
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);
}

View File

@ -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;
}
}

View File

@ -41,15 +41,7 @@ public interface EnhancedObjectUpgrader {
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 {
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);
}
Object getMethod(int id, DbDataType type) throws IOException;
}

View File

@ -0,0 +1,7 @@
package it.cavallium.strangedb.java.objects.lists;
public enum ClassPosition {
PROPERTY,
FIELD,
PRIMITIVE_FIELD
}

View File

@ -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();
}
}
}

View File

@ -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();
}

View File

@ -1,5 +1,7 @@
package it.cavallium.strangedb.java.objects.lists;
import it.cavallium.strangedb.java.database.IObjectsIO;
import it.cavallium.strangedb.java.objects.EnhancedObjectIndices;
import it.unimi.dsi.fastutil.longs.LongArrayList;
import it.cavallium.strangedb.java.objects.EnhancedObject;
import it.cavallium.strangedb.java.database.IDatabaseTools;
@ -13,9 +15,6 @@ public class EnhancedObjectStrangeDbList<T extends EnhancedObject> extends Stran
@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;
@ -25,19 +24,185 @@ public class EnhancedObjectStrangeDbList<T extends EnhancedObject> extends Stran
super();
}
@Deprecated
public EnhancedObjectStrangeDbList(IDatabaseTools databaseTools, Class<T> type) throws IOException {
this(databaseTools);
}
public EnhancedObjectStrangeDbList(IDatabaseTools databaseTools) throws IOException {
super(databaseTools);
this.type = type;
indices = new LongArrayList();
}
@Override
protected T loadItem(long uid) throws IOException {
return databaseTools.getObjectsIO().loadEnhancedObject(uid, type);
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
Class<?> declaredRootType = query.valuePointer.getRootType();
Class<?> obtainedRootType = elementUids.type;
if (isInstanceOf(obtainedRootType, declaredRootType)) {
Object result = resolveItemFromDb(query.valuePointer, dbio, elementUids);
if (query.valueOperation.evaluate(result)) {
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) -> {
Object result = resolveItemFromDb(query.valuePointer, dbio, elementUid);
if (query.valueOperation.evaluate(result)) {
results.add(elementUid);
}
});
}
return results;
}
private static Object resolveItemFromDb(ValuePointer pointer, IObjectsIO objectsIO, EnhancedObjectIndices element) throws IOException {
EnhancedObjectIndices currentElement = element;
boolean isLastElement;
for (int i = 0; i < pointer.size(); i++) {
isLastElement = i >= pointer.size() - 1;
ValuePointer currentPointer = pointer.at(i);
int pathNumber = currentPointer.getPathNumber();
ClassPosition valueType = currentPointer.getPathType();
Object value;
switch (valueType) {
case FIELD: {
if (isLastElement) {
//TODO: getBlock field data type. it can be an enhancedObject or an object
value = objectsIO.loadObject(currentElement.fieldUids[pathNumber]);
} else {
value = objectsIO.loadEnhancedObjectUids(currentElement.fieldUids[pathNumber]);
}
break;
}
case PRIMITIVE_FIELD: {
if (isLastElement) {
//TODO: getBlock primitive type
value = objectsIO.loadPrimitiveData(currentElement.nativeDataUid).getLong(pathNumber);
} else {
throw new IllegalArgumentException("You can access to a type field only in the last pointer");
}
break;
}
case PROPERTY: {
if (isLastElement) {
//TODO: getBlock field data type. it can be an enhancedObject or an object
value = objectsIO.loadObject(currentElement.fieldUids[pathNumber]);
} else {
value = objectsIO.loadEnhancedObjectUids(currentElement.propertyUids[pathNumber]);
}
}
default: {
throw new IllegalArgumentException("Not implemented");
}
}
if (isLastElement) {
return value;
} else {
currentElement = (EnhancedObjectIndices) value;
// check if the object that we obtained is the declared type
Class<?> declaredType = currentPointer.getAdditionalData();
Class<?> obtainedType = currentElement.type;
if (!isInstanceOf(obtainedType, declaredType)) {
return null;
}
}
}
throw new IOException("The pointer is empty");
}
private static boolean isInstanceOf(Class<?> clazz, Class<?> obj){
return obj.isAssignableFrom(clazz);
}
}

View File

@ -0,0 +1,58 @@
package it.cavallium.strangedb.java.objects.lists;
public class KMP {
public static int KMP(CharSequence content, CharSequence stringToFind) {
int[] failureTable = failureTable(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 (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 at nearest location of usable string prefix
targetPointer = failureTable[targetPointer];
} else { // case 3
// targetPointer is pointing at 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(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 at 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 (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 at 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;
}
}

View File

@ -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;
}
}

View File

@ -1,15 +1,21 @@
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 {
public abstract class StrangeDbList<T> extends EnhancedObject implements ElementsList<T> {
private final Object indicesAccessLock = new Object();
private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(false);
protected abstract LongArrayList getIndices();
@ -21,63 +27,166 @@ public abstract class StrangeDbList<T> extends EnhancedObject {
super(databaseTools);
}
@Override
public T get(int index) throws IOException {
synchronized (indicesAccessLock) {
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();
synchronized (indicesAccessLock) {
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(16, (r) -> new Thread(r, "StrangeDbList.forEachParallelUnsorted worker"));
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 {
synchronized (indicesAccessLock) {
long uid = getIndices().getLong(index);
writeItemToDisk(uid, value);
}
set(index, value);
}
@Override
public void set(int index, T value) throws IOException {
long uid = databaseTools.getObjectsIO().newNullObject();
synchronized (indicesAccessLock) {
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();
synchronized (indicesAccessLock) {
readWriteLock.writeLock().lock();
try {
getIndices().add(index, uid);
writeItemToDisk(uid, value);
} finally {
readWriteLock.writeLock().unlock();
}
}
public T getLast() throws IOException {
synchronized (indicesAccessLock) {
readWriteLock.readLock().lock();
try {
if (getIndices().size() > 0) {
return get(getIndices().size() - 1);
} else {
return null;
}
} finally {
readWriteLock.readLock().unlock();
}
}
public boolean isEmpty() {
synchronized (indicesAccessLock) {
readWriteLock.readLock().lock();
try {
return getIndices().size() <= 0;
} finally {
readWriteLock.readLock().unlock();
}
}
public int size() {
synchronized (indicesAccessLock) {
readWriteLock.readLock().lock();
try {
return getIndices().size();
} finally {
readWriteLock.readLock().unlock();
}
}
@ -87,7 +196,7 @@ public abstract class StrangeDbList<T> extends EnhancedObject {
@Override
public String toString() {
return new StringJoiner(", ", StrangeDbList.class.getSimpleName() + "[", "]")
return new StringJoiner(", ", StrangeDbList.class.getSimpleName() + "[", "]")
.add(getIndices().size() + " items")
.toString();
}

View File

@ -0,0 +1,5 @@
package it.cavallium.strangedb.java.objects.lists;
public interface ValueOperation<T> {
public boolean evaluate(Object value);
}

View File

@ -0,0 +1,182 @@
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.FIELD);
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 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 at(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 at(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;
}
}

View File

@ -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) != -1;
}
return false;
}
}

View File

@ -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 String containsValue;
private ContainsIgnoreCase(String containsValue) {
this.containsValue = containsValue.toLowerCase();
}
public static ContainsIgnoreCase containsValue(String value) {
return new ContainsIgnoreCase(value);
}
@Override
public boolean evaluate(Object value) {
if (value instanceof String) {
return KMP.KMP(((String) value).toLowerCase(), containsValue) != -1;
}
return false;
}
}

View File

@ -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);
}
}

View File

@ -1,14 +1,11 @@
package it.cavallium.strangedb.tests;
import it.cavallium.strangedb.java.annotations.*;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import it.cavallium.strangedb.java.objects.EnhancedObject;
import it.cavallium.strangedb.java.database.IDatabaseTools;
import it.cavallium.strangedb.java.annotations.DbDataType;
import it.cavallium.strangedb.java.annotations.DbField;
import it.cavallium.strangedb.java.annotations.DbPropertyGetter;
import it.cavallium.strangedb.java.annotations.DbPropertySetter;
import it.cavallium.strangedb.utils.NTestUtils;
import java.io.IOException;
@ -20,7 +17,7 @@ public class Clean {
@Before
public void setUp() throws Exception {
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());
db.setRootClassValues(root.class1);
@ -40,7 +37,7 @@ public class Clean {
db.testRootClassValues(root.getClass4());
db.get().closeAndClean();
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);
}
@DbPropertyGetter(id = 0, type = DbDataType.ENHANCED_OBJECT)
@DbProperty(id = 0, type = DbDataType.ENHANCED_OBJECT)
@DbPropertyGetter
public NTestUtils.RootClass getClass3() {
return getProperty();
}
@DbPropertySetter(id = 0, type = DbDataType.ENHANCED_OBJECT)
@DbProperty(id = 0, type = DbDataType.ENHANCED_OBJECT)
@DbPropertySetter
public void setClass3(NTestUtils.RootClass value) {
setProperty(value);
}
@DbPropertyGetter(id = 1, type = DbDataType.ENHANCED_OBJECT)
@DbProperty(id = 1, type = DbDataType.ENHANCED_OBJECT)
@DbPropertyGetter
public NTestUtils.RootClass getClass4() {
return getProperty();
}
@DbPropertySetter(id = 1, type = DbDataType.ENHANCED_OBJECT)
@DbProperty(id = 1, type = DbDataType.ENHANCED_OBJECT)
@DbPropertySetter
public void setClass4(NTestUtils.RootClass value) {
setProperty(value);
}

View File

@ -25,7 +25,7 @@ public class EnhancedClassUpdate {
path2 = Files.createTempFile("db-tests-blocks-", ".db");
path3 = Files.createTempFile("db-tests-references-", ".db");
db = new DatabaseJava(path1, path2, path3);
OldClass root = db.loadRoot(OldClass.class, OldClass::new);
OldClass root = db.loadRoot(OldClass::new);
root.field1 = "Abc";
root.field2 = 12;
root.field4 = 13;
@ -35,7 +35,7 @@ public class EnhancedClassUpdate {
@Test
public void shouldUpdateClass() throws IOException {
db = new DatabaseJava(path1, path2, path3);
V2Class root = db.loadRoot(V2Class.class, V2Class::new);
V2Class root = db.loadRoot(V2Class::new, V2Class.class);
assertEquals(root.field4, "Abc");
assertEquals(root.field2, 12);
assertEquals(root.field1, 13L);

View File

@ -0,0 +1,44 @@
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;
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();
for (int i = 0; i < count; i++) {
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);
}
}
}

View File

@ -1,10 +1,7 @@
package it.cavallium.strangedb.tests;
import it.cavallium.strangedb.java.annotations.*;
import it.cavallium.strangedb.java.objects.EnhancedObject;
import it.cavallium.strangedb.java.annotations.DbDataType;
import it.cavallium.strangedb.java.annotations.DbField;
import it.cavallium.strangedb.java.annotations.DbPropertyGetter;
import it.cavallium.strangedb.java.annotations.DbPropertySetter;
import it.cavallium.strangedb.java.database.IDatabaseTools;
import it.cavallium.strangedb.utils.NTestUtils;
import org.junit.After;
@ -20,7 +17,7 @@ public class MultipleEnhancedObjects {
@Before
public void setUp() throws Exception {
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());
db.setRootClassValues(root.class1);
@ -62,22 +59,26 @@ public class MultipleEnhancedObjects {
super(databaseTools);
}
@DbPropertyGetter(id = 0, type = DbDataType.ENHANCED_OBJECT)
@DbProperty(id = 0, type = DbDataType.ENHANCED_OBJECT)
@DbPropertyGetter
public NTestUtils.RootClass getClass3() {
return getProperty();
}
@DbPropertySetter(id = 0, type = DbDataType.ENHANCED_OBJECT)
@DbProperty(id = 0, type = DbDataType.ENHANCED_OBJECT)
@DbPropertySetter
public void setClass3(NTestUtils.RootClass value) {
setProperty(value);
}
@DbPropertyGetter(id = 1, type = DbDataType.ENHANCED_OBJECT)
@DbProperty(id = 1, type = DbDataType.ENHANCED_OBJECT)
@DbPropertyGetter
public NTestUtils.RootClass getClass4() {
return getProperty();
}
@DbPropertySetter(id = 1, type = DbDataType.ENHANCED_OBJECT)
@DbProperty(id = 1, type = DbDataType.ENHANCED_OBJECT)
@DbPropertySetter
public void setClass4(NTestUtils.RootClass value) {
setProperty(value);
}

View File

@ -0,0 +1,139 @@
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 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 path2;
private Path path3;
private DatabaseJava db;
@Before
public void setUp() throws Exception {
path1 = Files.createTempFile("db-tests-data-", ".db");
path2 = Files.createTempFile("db-tests-blocks-", ".db");
path3 = Files.createTempFile("db-tests-references-", ".db");
db = new DatabaseJava(path1, path2, path3);
ListsRoot root = db.loadRoot(ListsRoot::new);
for (int i = 0; i < 5000; i++) {
root.objectList.add(new ObjectItem(i));
root.enhancedObjectList.add(new EnhancedObjectItem(db, i));
}
for (int i = 0; i < 5000; 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 < 5000; i++) {
if (i % 11 == 0) {
root.objectList.set(i, new ObjectItem(i));
root.enhancedObjectList.set(i, new EnhancedObjectItem(db, i));
}
}
for (int i = 5000; i < 6000; i++) {
root.objectList.add(new ObjectItem(0));
root.enhancedObjectList.add(i, new EnhancedObjectItem(db, 0));
}
db.close();
}
@Test
public void shouldUpdateClass() throws IOException {
db = new DatabaseJava(path1, path2, path3);
ListsRoot root = db.loadRoot(ListsRoot::new);
new Thread(() -> {
try {
for (int i = 0; i < 5000; 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 = 5000; i < 6000; i++) {
assertEquals(root.objectList.get(i).value, "test_" + 0);
assertEquals(root.enhancedObjectList.get(i).value, "test_" + 0);
}
} catch (IOException e) {
e.printStackTrace();
}
}).start();
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
db.close();
}
@After
public void tearDown() throws Exception {
Files.deleteIfExists(path1);
Files.deleteIfExists(path2);
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;
}
}
}

View File

@ -4,17 +4,24 @@ import it.cavallium.strangedb.functionalinterfaces.RunnableWithIO;
import it.cavallium.strangedb.java.annotations.*;
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.query.*;
import it.unimi.dsi.fastutil.longs.LongArrayList;
import it.cavallium.strangedb.java.objects.EnhancedObject;
import it.cavallium.strangedb.java.database.IDatabaseTools;
import it.cavallium.strangedb.VariableWrapper;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class Performance {
private static boolean FAST_TESTS;
@ -24,6 +31,7 @@ public class Performance {
private static Path dbBlocksFile;
private static Path dbReferencesFile;
private static DatabaseJava db;
private static boolean tempDirectory;
/**
*
@ -40,25 +48,28 @@ public class Performance {
} catch (Exception ex) {
}
tempDirectory = false;
} else {
rootDirectory = Files.createTempDirectory("performance-tests");
tempDirectory = true;
}
generateDb();
System.out.println("Performance test started.");
System.out.println("-------------------------------------------------------+-----------------------------------------------------------------");
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("-------------------------------------------------------+-----------------------------------------------------------------");
testS("DatabaseCore creation", 3000, Performance::deleteDb, Performance::generateDb, () -> {});
testS("DatabaseCore root creation", 3000, Performance::regenDb, () -> db.loadRoot(PreloadedListContainer.class, PreloadedListContainer::new), () -> {});
testS("DatabaseCore creation", 300, Performance::deleteDb, Performance::generateDb, () -> {});
testS("DatabaseCore root creation", 300, Performance::regenDb, () -> db.loadRoot(PreloadedListContainer::new), () -> {});
final VariableWrapper<PreloadedListContainer> preloadedListContainer = new VariableWrapper<>(null);
final VariableWrapper<SimpleEnhancedObject> simpleEnhancedObjectContainer = new VariableWrapper<>(null);
/*
testS("ObjectStrangeDbList<Int> creation", 3000, () -> {
regenDb();
preloadedListContainer.var = db.loadRoot(PreloadedListContainer.class, PreloadedListContainer::new);
preloadedListContainer.var = db.loadRoot(PreloadedListContainer::new);
}, () -> preloadedListContainer.var.list = new ObjectStrangeDbList<>(db), () -> {});
testS("ObjectStrangeDbList<Int>: Filling with 1000 items", 100, () -> {
regenDb();
preloadedListContainer.var = db.loadRoot(PreloadedListContainer.class, PreloadedListContainer::new);
preloadedListContainer.var = db.loadRoot(PreloadedListContainer::new);
preloadedListContainer.var.list = new ObjectStrangeDbList<>(db);
}, () -> {
for (int i = 0; i < 1000; i++) {
@ -67,7 +78,7 @@ public class Performance {
}, () -> {});
testS("ObjectStrangeDbList<EnhancedObject>: Filling with 1000 items", 100, () -> {
regenDb();
preloadedListContainer.var = db.loadRoot(PreloadedListContainer.class, PreloadedListContainer::new);
preloadedListContainer.var = db.loadRoot(PreloadedListContainer::new);
preloadedListContainer.var.listOfEnhancedObj = new EnhancedObjectStrangeDbList<>(db, SimpleEnhancedObject.class);
simpleEnhancedObjectContainer.var = new SimpleEnhancedObject(db);
simpleEnhancedObjectContainer.var.integerNumber = 10;
@ -83,7 +94,7 @@ public class Performance {
}, () -> {});
testS("ObjectStrangeDbList<Int>: Filling with 10000 items", 10, () -> {
regenDb();
preloadedListContainer.var = db.loadRoot(PreloadedListContainer.class, PreloadedListContainer::new);
preloadedListContainer.var = db.loadRoot(PreloadedListContainer::new);
preloadedListContainer.var.list = new ObjectStrangeDbList<>(db);
}, () -> {
for (int i = 0; i < 10000; i++) {
@ -92,7 +103,7 @@ public class Performance {
}, () -> {});
testS("ObjectStrangeDbList<Int>: Filling with 100000 items", 1, () -> {
regenDb();
preloadedListContainer.var = db.loadRoot(PreloadedListContainer.class, PreloadedListContainer::new);
preloadedListContainer.var = db.loadRoot(PreloadedListContainer::new);
preloadedListContainer.var.list = new ObjectStrangeDbList<>(db);
}, () -> {
for (int i = 0; i < 100000; i++) {
@ -101,19 +112,17 @@ public class Performance {
}, () -> {});
testS("ObjectStrangeDbList<Int>: Loading 1000 items", 100, () -> {
regenDb();
preloadedListContainer.var = db.loadRoot(PreloadedListContainer.class, PreloadedListContainer::new);
preloadedListContainer.var = db.loadRoot(PreloadedListContainer::new);
preloadedListContainer.var.list = new ObjectStrangeDbList<>(db);
for (int i = 0; i < 1000; i++) {
preloadedListContainer.var.list.add(1000);
}
}, () -> {
for (int i = 0; i < 1000; i++) {
preloadedListContainer.var.list.get(i);
}
preloadedListContainer.var.list.forEachParallelUnsorted((i) -> {});
}, () -> {});
testS("ObjectStrangeDbList<EnhancedObject>: Loading with 1000 items", 100, () -> {
regenDb();
preloadedListContainer.var = db.loadRoot(PreloadedListContainer.class, PreloadedListContainer::new);
preloadedListContainer.var = db.loadRoot(PreloadedListContainer::new);
preloadedListContainer.var.listOfEnhancedObj = new EnhancedObjectStrangeDbList<>(db, SimpleEnhancedObject.class);
simpleEnhancedObjectContainer.var = new SimpleEnhancedObject(db);
simpleEnhancedObjectContainer.var.integerNumber = 10;
@ -126,25 +135,21 @@ public class Performance {
preloadedListContainer.var.listOfEnhancedObj.add(simpleEnhancedObjectContainer.var);
}
}, () -> {
for (int i = 0; i < 1000; i++) {
preloadedListContainer.var.listOfEnhancedObj.get(i);
}
preloadedListContainer.var.listOfEnhancedObj.forEachParallelUnsorted((i) -> {});
}, () -> {});
testS("ObjectStrangeDbList<Int>: Loading 10000 items", 10, () -> {
regenDb();
preloadedListContainer.var = db.loadRoot(PreloadedListContainer.class, PreloadedListContainer::new);
preloadedListContainer.var = db.loadRoot(PreloadedListContainer::new);
preloadedListContainer.var.list = new ObjectStrangeDbList<>(db);
for (int i = 0; i < 10000; i++) {
preloadedListContainer.var.list.add(1000);
}
}, () -> {
for (int i = 0; i < 10000; i++) {
preloadedListContainer.var.list.get(i);
}
preloadedListContainer.var.list.forEachParallelUnsorted((i) -> {});
}, () -> {});
testS("ObjectStrangeDbList<Int>: getLast() with 1000 items", 100, () -> {
regenDb();
preloadedListContainer.var = db.loadRoot(PreloadedListContainer.class, PreloadedListContainer::new);
preloadedListContainer.var = db.loadRoot(PreloadedListContainer::new);
preloadedListContainer.var.list = new ObjectStrangeDbList<>(db);
for (int i = 0; i < 1000; i++) {
preloadedListContainer.var.list.add(1000);
@ -154,7 +159,7 @@ public class Performance {
}, () -> {});
testS("ObjectStrangeDbList<EnhancedObject>: getLast() with 1000 items", 100, () -> {
regenDb();
preloadedListContainer.var = db.loadRoot(PreloadedListContainer.class, PreloadedListContainer::new);
preloadedListContainer.var = db.loadRoot(PreloadedListContainer::new);
preloadedListContainer.var.listOfEnhancedObj = new EnhancedObjectStrangeDbList<>(db, SimpleEnhancedObject.class);
simpleEnhancedObjectContainer.var = new SimpleEnhancedObject(db);
simpleEnhancedObjectContainer.var.integerNumber = 10;
@ -171,7 +176,7 @@ public class Performance {
}, () -> {});
testS("ObjectStrangeDbList<Int>: size() with 1000 items", 100, () -> {
regenDb();
preloadedListContainer.var = db.loadRoot(PreloadedListContainer.class, PreloadedListContainer::new);
preloadedListContainer.var = db.loadRoot(PreloadedListContainer::new);
preloadedListContainer.var.list = new ObjectStrangeDbList<>(db);
for (int i = 0; i < 1000; i++) {
preloadedListContainer.var.list.add(1000);
@ -179,10 +184,44 @@ public class Performance {
}, () -> {
preloadedListContainer.var.list.size();
}, () -> {});
*/
for (int items = 1000; items <= 100000; items *= 10) {
final int itemsF = items;
testS("ListQuery: query with " + items + " items", 100 / (items / 1000), () -> {
regenDb();
preloadedListContainer.var = db.loadRoot(PreloadedListContainer::new);
preloadedListContainer.var.listOfMessages = new EnhancedObjectStrangeDbList<>(db);
Random random = new Random();
for (int i = 0; i < itemsF; 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(db, new EFormattedText(db, new String(stringBytes, StandardCharsets.UTF_8) + (random.nextBoolean() ? "not found" : " text to find!"), stringList.toArray(new String[0])));
} else {
content = new EMessageOtherContent(db, "EMPTY ABCDEFG");
}
EMessage message = new EMessage(db, content);
preloadedListContainer.var.listOfMessages.add(message);
}
}, () -> {
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.var.listOfMessages.query(query).asList();
}, () -> {});
}
System.out.println("-------------------------------------------------------+-----------------------------------------------------------------");
System.out.println("Performance test finished.");
deleteDb();
Files.deleteIfExists(rootDirectory);
if (tempDirectory) {
Files.deleteIfExists(rootDirectory);
}
}
private static void NtestS(String description, int times, RunnableWithIO beforeAction, RunnableWithIO action, RunnableWithIO afterAction) throws IOException, InterruptedException {
@ -264,14 +303,31 @@ public class Performance {
}
public static void generateDb() throws IOException {
dbDataFile = Files.createFile(rootDirectory.resolve("db_data.dat"));
dbBlocksFile = Files.createFile(rootDirectory.resolve("db_blocks.dat"));
dbReferencesFile = Files.createFile(rootDirectory.resolve("db_references.dat"));
dbDataFile = rootDirectory.resolve("db_data.dat");
dbBlocksFile = rootDirectory.resolve("db_blocks.dat");
dbReferencesFile = rootDirectory.resolve("db_references.dat");
deleteDbFolders();
Files.createFile(dbDataFile);
Files.createFile(dbBlocksFile);
Files.createFile(dbReferencesFile);
db = new DatabaseJava(dbDataFile, dbBlocksFile, dbReferencesFile);
int i = 0;
db.getObjectsIO().registerClass(SimpleEnhancedObject.class, i++);
db.getObjectsIO().registerClass(PreloadedListContainer.class, i++);
db.getObjectsIO().registerClass(DynamicListContainer.class, i++);
db.getObjectsIO().registerClass(EMessage.class, i++);
db.getObjectsIO().registerClass(EMessageContent.class, i++);
db.getObjectsIO().registerClass(EMessageText.class, i++);
db.getObjectsIO().registerClass(EMessageOtherContent.class, i++);
db.getObjectsIO().registerClass(EFormattedText.class, i++);
}
public static void deleteDb() throws IOException {
db.close();
deleteDbFolders();
}
public static void deleteDbFolders() throws IOException {
Files.deleteIfExists(dbDataFile);
Files.deleteIfExists(dbBlocksFile);
Files.deleteIfExists(dbReferencesFile);
@ -290,6 +346,9 @@ public class Performance {
@DbField(id = 1, type = DbDataType.ENHANCED_OBJECT)
public EnhancedObjectStrangeDbList<SimpleEnhancedObject> listOfEnhancedObj;
@DbField(id = 2, type = DbDataType.ENHANCED_OBJECT)
public EnhancedObjectStrangeDbList<EMessage> listOfMessages;
public PreloadedListContainer() {
}
@ -310,12 +369,14 @@ public class Performance {
}
@DbPropertyGetter(id = 0, type = DbDataType.ENHANCED_OBJECT)
@DbProperty(id = 0, type = DbDataType.ENHANCED_OBJECT)
@DbPropertyGetter()
public ObjectStrangeDbList<Integer> getList() {
return getProperty();
}
@DbPropertySetter(id = 1, type = DbDataType.ENHANCED_OBJECT)
@DbProperty(id = 1, type = DbDataType.ENHANCED_OBJECT)
@DbPropertySetter()
public void setList(ObjectStrangeDbList<Integer> list) {
setProperty(list);
}

View File

@ -0,0 +1,59 @@
package it.cavallium.strangedb.tests;
import it.cavallium.strangedb.java.database.DatabaseJava;
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.Files;
import java.nio.file.Path;
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 Path path1;
private Path path2;
private Path path3;
private DatabaseJava db;
@Before
public void setUp() throws Exception {
path1 = Files.createTempFile("db-tests-data-", ".db");
path2 = Files.createTempFile("db-tests-blocks-", ".db");
path3 = Files.createTempFile("db-tests-references-", ".db");
db = new DatabaseJava(path1, path2, path3);
}
@Test
public void shouldCreateListAndQuery() throws IOException {
ListContainer root = db.loadRoot((db) -> new ListContainer(db, 1000));
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();
ArrayList<User> elements = root.usersList.query(query).asList();
System.out.println("Time elapsed: " + (System.currentTimeMillis() - time1));
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 {
Files.deleteIfExists(path1);
Files.deleteIfExists(path2);
Files.deleteIfExists(path3);
}
}

View 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();
}
}

View 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();
}
}

View File

@ -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;
}
}

View 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;
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -233,7 +233,7 @@ public class NTestUtils {
@DbField(id = 0, type = DbDataType.OBJECT)
public String field7;
@DbField(id = 1, type = DbDataType.REFERENCES_LIST)
@DbField(id = 1, type = DbDataType.OBJECT)
public LongArrayList field8;
@DbField(id = 2, type = DbDataType.ENHANCED_OBJECT)
@ -247,32 +247,38 @@ public class NTestUtils {
super(databaseTools);
}
@DbPropertyGetter(id = 0, type = DbDataType.OBJECT)
@DbProperty(id = 0, type = DbDataType.OBJECT)
@DbPropertyGetter
public String get7() {
return getProperty();
}
@DbPropertyGetter(id = 1, type = DbDataType.REFERENCES_LIST)
@DbProperty(id = 1, type = DbDataType.OBJECT)
@DbPropertyGetter
public LongArrayList get8() {
return getProperty();
}
@DbPropertyGetter(id = 2, type = DbDataType.ENHANCED_OBJECT)
@DbProperty(id = 2, type = DbDataType.ENHANCED_OBJECT)
@DbPropertyGetter
public NSimplestClass get9() {
return getProperty();
}
@DbPropertySetter(id = 0, type = DbDataType.OBJECT)
@DbProperty(id = 0, type = DbDataType.OBJECT)
@DbPropertySetter
public void set7(String val) {
setProperty(val);
}
@DbPropertySetter(id = 1, type = DbDataType.REFERENCES_LIST)
@DbProperty(id = 1, type = DbDataType.OBJECT)
@DbPropertySetter
public void set8(LongArrayList val) {
setProperty(val);
}
@DbPropertySetter(id = 2, type = DbDataType.ENHANCED_OBJECT)
@DbProperty(id = 2, type = DbDataType.ENHANCED_OBJECT)
@DbPropertySetter
public void set9(NSimplestClass val) {
setProperty(val);
}