diff --git a/pom.xml b/pom.xml index 5ead887..8b1b990 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ it.cavallium strangedb - 1.5.5 + 1.5.7 strangedb-java https://git.ignuranza.net/andreacavalli/strangedb @@ -45,7 +45,7 @@ it.cavallium strangedb-core - 1.5.5 + 1.5.7 diff --git a/src/main/java/it/cavallium/strangedb/java/annotations/DbField.java b/src/main/java/it/cavallium/strangedb/java/annotations/DbField.java index 489b1a1..1ea47df 100644 --- a/src/main/java/it/cavallium/strangedb/java/annotations/DbField.java +++ b/src/main/java/it/cavallium/strangedb/java/annotations/DbField.java @@ -10,4 +10,5 @@ import java.lang.annotation.Target; public @interface DbField { int id(); DbDataType type() default DbDataType.OBJECT; + String name() default ""; } diff --git a/src/main/java/it/cavallium/strangedb/java/annotations/DbPrimitiveField.java b/src/main/java/it/cavallium/strangedb/java/annotations/DbPrimitiveField.java index eb50598..aad0385 100644 --- a/src/main/java/it/cavallium/strangedb/java/annotations/DbPrimitiveField.java +++ b/src/main/java/it/cavallium/strangedb/java/annotations/DbPrimitiveField.java @@ -10,4 +10,5 @@ import java.lang.annotation.Target; public @interface DbPrimitiveField { int id(); DbPrimitiveType type(); + String name() default ""; } diff --git a/src/main/java/it/cavallium/strangedb/java/annotations/DbProperty.java b/src/main/java/it/cavallium/strangedb/java/annotations/DbProperty.java new file mode 100644 index 0000000..8258179 --- /dev/null +++ b/src/main/java/it/cavallium/strangedb/java/annotations/DbProperty.java @@ -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 ""; +} diff --git a/src/main/java/it/cavallium/strangedb/java/annotations/DbPropertyGetter.java b/src/main/java/it/cavallium/strangedb/java/annotations/DbPropertyGetter.java index 808045e..43e4522 100644 --- a/src/main/java/it/cavallium/strangedb/java/annotations/DbPropertyGetter.java +++ b/src/main/java/it/cavallium/strangedb/java/annotations/DbPropertyGetter.java @@ -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; } diff --git a/src/main/java/it/cavallium/strangedb/java/annotations/DbPropertySetter.java b/src/main/java/it/cavallium/strangedb/java/annotations/DbPropertySetter.java index b7f5593..b6debbd 100644 --- a/src/main/java/it/cavallium/strangedb/java/annotations/DbPropertySetter.java +++ b/src/main/java/it/cavallium/strangedb/java/annotations/DbPropertySetter.java @@ -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; } diff --git a/src/main/java/it/cavallium/strangedb/java/database/DatabaseDataInitializer.java b/src/main/java/it/cavallium/strangedb/java/database/DatabaseDataInitializer.java index 792fd7f..83f5498 100644 --- a/src/main/java/it/cavallium/strangedb/java/database/DatabaseDataInitializer.java +++ b/src/main/java/it/cavallium/strangedb/java/database/DatabaseDataInitializer.java @@ -26,7 +26,7 @@ public class DatabaseDataInitializer implements IDataInitializer { } private void initializeDbObjectFields(EnhancedObject obj) throws IOException { - // Declare the variables needed to get the biggest field Id + // Declare the variables needed to getBlock the biggest field Id Field[] unorderedFields = objectsIO.getFields(obj); // Find the biggest field Id int biggestFieldId = objectsIO.getBiggestFieldId(unorderedFields); @@ -52,7 +52,7 @@ public class DatabaseDataInitializer implements IDataInitializer { } private void initializeDbObjectPrimitiveFields(EnhancedObject obj) throws IOException { - // Declare the variables needed to get the biggest field Id + // Declare the variables needed to getBlock the biggest field Id Field[] unorderedFields = objectsIO.getPrimitiveFields(obj); // Find the biggest field Id int biggestFieldId = objectsIO.getBiggestPrimitiveFieldId(unorderedFields); @@ -107,7 +107,7 @@ public class DatabaseDataInitializer implements IDataInitializer { } private void initializeDbObjectProperties(EnhancedObject obj) throws IOException { - // 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(); @@ -120,7 +120,7 @@ public class DatabaseDataInitializer implements IDataInitializer { long[] propertyUIDs = objectsIO.allocateNewUIDs(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; @@ -131,12 +131,12 @@ public class DatabaseDataInitializer implements IDataInitializer { DbDataType[] propertyTypes = new DbDataType[biggestPropertyId + 1]; Method[] propertyGetters = new Method[biggestPropertyId + 1]; Method[] propertySetters = new Method[biggestPropertyId + 1]; - Map setterMethods = new LinkedHashMap<>(); - Map getterMethods = new LinkedHashMap<>(); + Map setterMethods = new LinkedHashMap<>(); + Map 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; @@ -144,7 +144,7 @@ public class DatabaseDataInitializer implements IDataInitializer { 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; diff --git a/src/main/java/it/cavallium/strangedb/java/database/DatabaseEnhancedObjectUpgrader.java b/src/main/java/it/cavallium/strangedb/java/database/DatabaseEnhancedObjectUpgrader.java index c820dfa..96d73d7 100644 --- a/src/main/java/it/cavallium/strangedb/java/database/DatabaseEnhancedObjectUpgrader.java +++ b/src/main/java/it/cavallium/strangedb/java/database/DatabaseEnhancedObjectUpgrader.java @@ -23,14 +23,14 @@ public class DatabaseEnhancedObjectUpgrader implements EnhancedObjectUpgrader { @Override @SuppressWarnings("unchecked") - public Object getField(int id, DbDataType type, Supplier> 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> 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") diff --git a/src/main/java/it/cavallium/strangedb/java/database/DatabaseJava.java b/src/main/java/it/cavallium/strangedb/java/database/DatabaseJava.java index 630834f..f434515 100644 --- a/src/main/java/it/cavallium/strangedb/java/database/DatabaseJava.java +++ b/src/main/java/it/cavallium/strangedb/java/database/DatabaseJava.java @@ -1,21 +1,38 @@ 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.ints.Int2ObjectLinkedOpenHashMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.longs.LongArrayList; +import it.unimi.dsi.fastutil.longs.LongArraySet; +import it.unimi.dsi.fastutil.longs.LongSet; +import it.unimi.dsi.fastutil.objects.ObjectIterator; 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 +40,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 loadRoot(Class type, FunctionWithIO ifAbsent) throws IOException { - if (loadedRootObject != null) { + public T loadRoot(FunctionWithIO 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 +62,110 @@ public class DatabaseJava extends DatabaseCore implements IDatabaseTools { } } 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.getReference(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.getReference(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); diff --git a/src/main/java/it/cavallium/strangedb/java/database/DatabaseObjectsIO.java b/src/main/java/it/cavallium/strangedb/java/database/DatabaseObjectsIO.java index 5e2dde1..e630ffa 100644 --- a/src/main/java/it/cavallium/strangedb/java/database/DatabaseObjectsIO.java +++ b/src/main/java/it/cavallium/strangedb/java/database/DatabaseObjectsIO.java @@ -3,10 +3,14 @@ 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; @@ -23,14 +27,20 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.nio.ByteBuffer; import java.util.*; -import java.util.function.Supplier; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +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 Lock lock = new ReentrantLock(); private final DatabaseDataInitializer dataInitializer; private Kryo kryo = new Kryo(); @@ -92,16 +102,68 @@ 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 loadEnhancedObject(long reference, Class objectType) throws IOException { - synchronized (accessLock) { + public T loadEnhancedObject(long reference) throws IOException { + lock.lock(); + try { + return loadEnhancedObject_(reference); + } finally { + lock.unlock(); + } + } + + @SuppressWarnings("unchecked") + private T loadEnhancedObject_(long reference) 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 IllegalAccessError("PLEASE UPGRADE THE DATABASE"); + } + int serializedClassLength = Byte.toUnsignedInt(buffer.get()); + byte[] serializedClassData = new byte[serializedClassLength]; + buffer.get(serializedClassData); + Class objectType = kryo.readClass(new Input(serializedClassData)).getType(); + 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 { + lock.lock(); + try { 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 objectType = (Class) kryo.readClass(new Input(serializedClassData)).getType(); int fieldsCount = buffer.getInt(); int methodsCount = buffer.getInt(); long[] fieldRefs = new long[fieldsCount]; @@ -113,40 +175,50 @@ 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); + } finally { + lock.unlock(); } } - @SuppressWarnings("unchecked") - Object loadData(DbDataType propertyType, long dataReference, Supplier> returnType) throws IOException { + public Object loadData(DbDataType propertyType, long dataReference) throws IOException { + lock.lock(); + try { + return loadData_(propertyType, dataReference); + } finally { + lock.unlock(); + } + } + + private Object loadData_(DbDataType propertyType, long dataReference) throws IOException { + switch (propertyType) { case ENHANCED_OBJECT: - return loadEnhancedObject(dataReference, (Class) returnType.get()); + return loadEnhancedObject_(dataReference); case OBJECT: - return loadObject(dataReference); + return loadObject_(dataReference); case REFERENCES_LIST: - return loadReferencesList(dataReference); + return loadReferencesList_(dataReference); default: throw new NullPointerException("Unknown data type"); } } - void setData(long reference, DbDataType propertyType, T loadedPropertyValue) throws IOException { + private 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; } } - void setPrimitives(T enhancedObject, long reference, DbPrimitiveType[] types, Field[] fields) + private void setPrimitives(T enhancedObject, long reference, DbPrimitiveType[] types, Field[] fields) throws IOException { ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES * fields.length); try { @@ -188,160 +260,219 @@ 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 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!"); - } + lock.lock(); + try { + setEnhancedObject_(reference, value); + } finally { + lock.unlock(); + } + } - 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 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(); + Output serializedClassDataStream = new Output(1024, 8192); + kryo.writeClass(serializedClassDataStream, value.getClass()); + byte[] serializedClassData = serializedClassDataStream.toBytes(); + final int totalSize = Byte.BYTES + serializedClassData.length + Byte.BYTES + Integer.BYTES * 2 + fieldReferences.length * Long.BYTES + + propertyReferences.length * Long.BYTES + Long.BYTES; + ByteBuffer buffer = ByteBuffer.allocate(totalSize); + if (serializedClassData.length > 255) { + throw new IndexOutOfBoundsException("The class name is too long!"); + } + if (objectFullInfo.getVersion() > 250) { + throw new IndexOutOfBoundsException("The class version is too long!"); + } + buffer.put((byte) (objectFullInfo.getVersion() + 5)); + buffer.put((byte) serializedClassData.length); + buffer.put(serializedClassData); + buffer.putInt(fieldReferences.length); + buffer.putInt(propertyReferences.length); + for (int i = 0; i < fieldReferences.length; i++) { + buffer.putLong(fieldReferences[i]); + } + for (int i = 0; i < propertyReferences.length; i++) { + buffer.putLong(propertyReferences[i]); + } + buffer.putLong(nativeFieldDataReference); + buffer.flip(); + + for (int i = 0; i < fieldReferences.length; i++) { + if (fields[i] != null) { + try { + setData(fieldReferences[i], fieldTypes[i], fields[i].get(value)); + } catch (IllegalAccessException e) { + throw new IOException(e); + } + } + } + for (int i = 0; i < propertyReferences.length; i++) { + if (propertyValues[i] != null) { + setData(propertyReferences[i], propertyTypes[i], propertyValues[i]); + } + } + setPrimitives(value, nativeFieldDataReference, nativeFieldTypes, nativeFields); + referencesIO.writeToReference(reference, ENHANCED_OBJECT_METADATA_CLEANER, totalSize, buffer); + } else { + referencesIO.writeToReference(reference, BLANK_DATA_CLEANER, 0, null); + } + } + + @Override + public T loadObject(long reference) throws IOException { + lock.lock(); + try { + return loadObject_(reference); + } finally { + lock.unlock(); } } @SuppressWarnings("unchecked") - @Override - public 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)); + private T loadObject_(long reference) throws IOException { + ByteBuffer buffer = referencesIO.readFromReference(reference); + if (buffer.limit() == 0) { + return null; } + buffer.rewind(); + byte[] data = buffer.array(); + return (T) kryo.readClassAndObject(new Input(data)); } @Override public LongArrayList loadReferencesList(long reference) throws IOException { - synchronized (accessLock) { - ByteBuffer buffer = referencesIO.readFromReference(reference); - if (buffer.limit() == 0) { - return null; - } - int itemsCount = buffer.getInt(); - LongArrayList arrayList = new LongArrayList(); - for (int i = 0; i < itemsCount; i++) { - arrayList.add(buffer.getLong()); - } - return arrayList; + lock.lock(); + try { + return loadReferencesList_(reference); + } finally { + lock.unlock(); } } + 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; + lock.lock(); + try { + return loadPrimitiveData_(reference); + } finally { + lock.unlock(); } } + 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 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); - } + lock.lock(); + try { + setObject_(reference, value); + } finally { + lock.unlock(); + } + } + + private void setObject_(long reference, T value) throws IOException { + 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, 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); + lock.lock(); + try { + setReferencesList_(reference, value); + } finally { + lock.unlock(); + } + } + + 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(); + lock.lock(); + try { + return newNullObject_(); + } finally { + lock.unlock(); } } + 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 @@ -353,17 +484,17 @@ public class DatabaseObjectsIO implements IObjectsIO { } private 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 +505,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 setterMethods = new LinkedHashMap<>(); - Map getterMethods = new LinkedHashMap<>(); + Map setterMethods = new LinkedHashMap<>(); + Map 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 +518,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 +530,19 @@ public class DatabaseObjectsIO implements IObjectsIO { getterMethods); } - int getBiggestPropertyGetterId(Method[] unorderedPropertyGetters) { + public int getBiggestPropertyGetterId(Method[] unorderedPropertyGetters) { + lock.lock(); + try { + return getBiggestPropertyGetterId_(unorderedPropertyGetters); + } finally { + lock.unlock(); + } + } + + 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 +551,19 @@ public class DatabaseObjectsIO implements IObjectsIO { return biggestPropertyId; } - int getBiggestPropertySetterId(Method[] unorderedPropertySetters) { + public int getBiggestPropertySetterId(Method[] unorderedPropertySetters) { + lock.lock(); + try { + return getBiggestPropertySetterId_(unorderedPropertySetters); + } finally { + lock.unlock(); + } + } + + 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 +574,10 @@ public class DatabaseObjectsIO implements IObjectsIO { private 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]; @@ -489,8 +638,8 @@ public class DatabaseObjectsIO implements IObjectsIO { private 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 +652,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 +660,19 @@ public class DatabaseObjectsIO implements IObjectsIO { obj.setFields(fields, orderedFieldTypes, fieldReferences); } - void loadField(T obj, Field field, DbDataType fieldType, long fieldReference) + public void loadField(T obj, Field field, DbDataType fieldType, long fieldReference) throws IOException { - Object data = loadData(fieldType, fieldReference, field::getType); + lock.lock(); + try { + loadField_(obj, field, fieldType, fieldReference); + } finally { + lock.unlock(); + } + } + + private 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 +686,42 @@ public class DatabaseObjectsIO implements IObjectsIO { } } - Field[] getFields(T obj) { + public Field[] getFields(T obj) { + lock.lock(); + try { + return getFields_(obj); + } finally { + lock.unlock(); + } + } + + private Field[] getFields_(T obj) { return FieldUtils.getFieldsWithAnnotation(obj.getClass(), DbField.class); } - Field[] getPrimitiveFields(T obj) { + public Field[] getPrimitiveFields(T obj) { + lock.lock(); + try { + return getPrimitiveFields_(obj); + } finally { + lock.unlock(); + } + } + + private Field[] getPrimitiveFields_(T obj) { return FieldUtils.getFieldsWithAnnotation(obj.getClass(), DbPrimitiveField.class); } - int getBiggestFieldId(Field[] unorderedFields) { + public int getBiggestFieldId(Field[] unorderedFields) { + lock.lock(); + try { + return getBiggestFieldId_(unorderedFields); + } finally { + lock.unlock(); + } + } + + private int getBiggestFieldId_(Field[] unorderedFields) { int biggestFieldId = -1; for (Field field : unorderedFields) { DbField fieldAnnotation = field.getAnnotation(DbField.class); @@ -547,7 +733,16 @@ public class DatabaseObjectsIO implements IObjectsIO { return biggestFieldId; } - int getBiggestPrimitiveFieldId(Field[] unorderedFields) { + public int getBiggestPrimitiveFieldId(Field[] unorderedFields) { + lock.lock(); + try { + return getBiggestPrimitiveFieldId_(unorderedFields); + } finally { + lock.unlock(); + } + } + + private int getBiggestPrimitiveFieldId_(Field[] unorderedFields) { int biggestFieldId = -1; for (Field field : unorderedFields) { DbPrimitiveField fieldAnnotation = field.getAnnotation(DbPrimitiveField.class); @@ -572,8 +767,7 @@ public class DatabaseObjectsIO implements IObjectsIO { } } - private T preloadEnhancedObject(Class objectType, int serializedVersion, - long nativeFieldsRef, long[] fieldRefs, long[] methodRefs) throws IOException { + private T preloadEnhancedObject(Class objectType, int serializedVersion, long nativeFieldsRef, long[] fieldRefs, long[] methodRefs) throws IOException { // Instantiate the class to an object T obj = toInstance(objectType); @@ -601,7 +795,7 @@ public class DatabaseObjectsIO implements IObjectsIO { public 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; } diff --git a/src/main/java/it/cavallium/strangedb/java/database/IObjectsIO.java b/src/main/java/it/cavallium/strangedb/java/database/IObjectsIO.java index b5f7504..542a1a8 100644 --- a/src/main/java/it/cavallium/strangedb/java/database/IObjectsIO.java +++ b/src/main/java/it/cavallium/strangedb/java/database/IObjectsIO.java @@ -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,10 @@ import java.io.IOException; import java.lang.reflect.Method; public interface IObjectsIO { - T loadEnhancedObject(long reference, Class objectType) throws IOException; + @SuppressWarnings("unchecked") + T loadEnhancedObject(long reference) throws IOException; + + EnhancedObjectIndices loadEnhancedObjectUids(long reference) throws IOException; T loadObject(long reference) throws IOException; @@ -47,7 +51,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); diff --git a/src/main/java/it/cavallium/strangedb/java/objects/EnhancedObject.java b/src/main/java/it/cavallium/strangedb/java/objects/EnhancedObject.java index 07b4b1f..7de5882 100644 --- a/src/main/java/it/cavallium/strangedb/java/objects/EnhancedObject.java +++ b/src/main/java/it/cavallium/strangedb/java/objects/EnhancedObject.java @@ -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 setterMethods; - private Map getterMethods; + private Map setterMethods; + private Map 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 setterMethods, Map getterMethods) { + public void setProperties(Method[] propertyGetters, Method[] propertySetters, DbDataType[] propertyTypes, long[] propertyReferences, Map setterMethods, Map 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 getProperty(int propertyId) throws IOException { + public 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 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); } diff --git a/src/main/java/it/cavallium/strangedb/java/objects/EnhancedObjectIndices.java b/src/main/java/it/cavallium/strangedb/java/objects/EnhancedObjectIndices.java new file mode 100644 index 0000000..2553acc --- /dev/null +++ b/src/main/java/it/cavallium/strangedb/java/objects/EnhancedObjectIndices.java @@ -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 type; + + public EnhancedObjectIndices(Class type, long objectUid, long[] fieldUids, long[] propertyUids, long nativeDataUid) { + this.type = type; + this.objectUid = objectUid; + this.fieldUids = fieldUids; + this.propertyUids = propertyUids; + this.nativeDataUid = nativeDataUid; + } +} diff --git a/src/main/java/it/cavallium/strangedb/java/objects/EnhancedObjectUpgrader.java b/src/main/java/it/cavallium/strangedb/java/objects/EnhancedObjectUpgrader.java index dfa40c7..89c7836 100644 --- a/src/main/java/it/cavallium/strangedb/java/objects/EnhancedObjectUpgrader.java +++ b/src/main/java/it/cavallium/strangedb/java/objects/EnhancedObjectUpgrader.java @@ -41,15 +41,7 @@ public interface EnhancedObjectUpgrader { return getPrimitiveField(id, DbPrimitiveType.DOUBLE); } - Object getField(int id, DbDataType type, Supplier> 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> 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; } diff --git a/src/main/java/it/cavallium/strangedb/java/objects/lists/ClassPosition.java b/src/main/java/it/cavallium/strangedb/java/objects/lists/ClassPosition.java new file mode 100644 index 0000000..d9ace17 --- /dev/null +++ b/src/main/java/it/cavallium/strangedb/java/objects/lists/ClassPosition.java @@ -0,0 +1,7 @@ +package it.cavallium.strangedb.java.objects.lists; + +public enum ClassPosition { + PROPERTY, + FIELD, + PRIMITIVE_FIELD +} diff --git a/src/main/java/it/cavallium/strangedb/java/objects/lists/ElementsArrayList.java b/src/main/java/it/cavallium/strangedb/java/objects/lists/ElementsArrayList.java new file mode 100644 index 0000000..afd22c7 --- /dev/null +++ b/src/main/java/it/cavallium/strangedb/java/objects/lists/ElementsArrayList.java @@ -0,0 +1,70 @@ +package it.cavallium.strangedb.java.objects.lists; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +public class ElementsArrayList implements ElementsList { + + private final ArrayList list; + + public ElementsArrayList() { + this.list = new ArrayList<>(); + } + + public ElementsArrayList(Collection 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 add(T value) { + list.add(value); + } + + @Override + public void update(int index, T value) { + set(index, value); + } + + @Override + public void set(int index, T value) { + list.set(index, value); + } + + @Override + public void add(int index, T value) { + list.add(index, value); + } + + @Override + public T getLast() { + return list.get(list.size() - 1); + } + + @Override + public boolean isEmpty() { + return list.isEmpty(); + } + + @Override + public int size() { + return list.size(); + } + + public boolean contains(Object o) { + return list.contains(o); + } + + public ArrayList asList() { + return list; + } +} diff --git a/src/main/java/it/cavallium/strangedb/java/objects/lists/ElementsList.java b/src/main/java/it/cavallium/strangedb/java/objects/lists/ElementsList.java new file mode 100644 index 0000000..1ffcd41 --- /dev/null +++ b/src/main/java/it/cavallium/strangedb/java/objects/lists/ElementsList.java @@ -0,0 +1,14 @@ +package it.cavallium.strangedb.java.objects.lists; + +import java.io.IOException; + +interface ElementsList { + T get(int index) 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(); +} diff --git a/src/main/java/it/cavallium/strangedb/java/objects/lists/EnhancedObjectStrangeDbList.java b/src/main/java/it/cavallium/strangedb/java/objects/lists/EnhancedObjectStrangeDbList.java index e1bb0ce..2637530 100644 --- a/src/main/java/it/cavallium/strangedb/java/objects/lists/EnhancedObjectStrangeDbList.java +++ b/src/main/java/it/cavallium/strangedb/java/objects/lists/EnhancedObjectStrangeDbList.java @@ -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; @@ -33,11 +35,175 @@ public class EnhancedObjectStrangeDbList extends Stran @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 queryUids(ListQuery query) throws IOException { + ElementsArrayList uids = executeQuery(query, this, databaseTools.getObjectsIO()); + return uids; + } + + public ElementsArrayList query(ListQuery query) throws IOException { + ElementsArrayList uids = queryUids(query); + ElementsArrayList elements = new ElementsArrayList<>(uids.size()); + for (int i = 0; i < uids.size(); i++) { + T element = databaseTools.getObjectsIO().loadEnhancedObject(uids.get(i).objectUid); + elements.add(element); + } + return elements; + } + + private ElementsArrayList 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 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 results = new ElementsArrayList<>(); + for (ListQuery childQuery : children) { + ElementsArrayList 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 main, ElementsList 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); + } + } + } + + @SuppressWarnings("unchecked") + static ElementsArrayList toElementsArrayList(ElementsList elementsList) { + if (elementsList instanceof EnhancedObjectStrangeDbList) { + return new ElementsArrayList<>(((EnhancedObjectStrangeDbList) elementsList).getIndices()); + } else if (elementsList instanceof ElementsArrayList) { + return (ElementsArrayList) elementsList; + } else { + throw new UnsupportedOperationException(); + } + } + + @SuppressWarnings("unchecked") + private static ElementsArrayList executeQueryElement(ListQuery.ListQueryElement query, ElementsList inputList, IObjectsIO dbio) throws IOException { + ElementsArrayList results = new ElementsArrayList<>(); + if (inputList instanceof EnhancedObjectStrangeDbList) { + EnhancedObjectStrangeDbList dbList = ((EnhancedObjectStrangeDbList) inputList); + final int listSize = inputList.size(); + final LongArrayList indices = dbList.getIndices(); + for (int i = 0; i < listSize; i++) { + Long elementUid = indices.get(i); + 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) { + final int listSize = inputList.size(); + ElementsArrayList elementsUids = ((ElementsArrayList) inputList); + for (int i = 0; i < listSize; i++) { + EnhancedObjectIndices elementUid = elementsUids.get(i); + Object result = resolveItemFromDb(query.valuePointer, dbio, elementUid); + if (query.valueOperation.evaluate(result)) { + results.add(elementUid); + } + } + } + return results; + } + + 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); + } } diff --git a/src/main/java/it/cavallium/strangedb/java/objects/lists/KMP.java b/src/main/java/it/cavallium/strangedb/java/objects/lists/KMP.java new file mode 100644 index 0000000..3c41d71 --- /dev/null +++ b/src/main/java/it/cavallium/strangedb/java/objects/lists/KMP.java @@ -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; + } +} diff --git a/src/main/java/it/cavallium/strangedb/java/objects/lists/ListQuery.java b/src/main/java/it/cavallium/strangedb/java/objects/lists/ListQuery.java new file mode 100644 index 0000000..e19343d --- /dev/null +++ b/src/main/java/it/cavallium/strangedb/java/objects/lists/ListQuery.java @@ -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 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 getQueryElementsRecursively() { + return Collections.singletonList(this); + } + } + + static class ListQueryCollection extends ListQuery { + protected ListQuery[] queries; + + private ListQueryCollection(ListQuery... queries) { + this.queries = queries; + } + + @Override + List getQueryElementsRecursively() { + List 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[] append(T[] array, T value) { + T[] newArray = Arrays.copyOf(array, array.length + 1); + newArray[array.length] = value; + return newArray; + } +} diff --git a/src/main/java/it/cavallium/strangedb/java/objects/lists/ObjectStrangeDbList.java b/src/main/java/it/cavallium/strangedb/java/objects/lists/ObjectStrangeDbList.java index 0bd76c8..fc3f489 100644 --- a/src/main/java/it/cavallium/strangedb/java/objects/lists/ObjectStrangeDbList.java +++ b/src/main/java/it/cavallium/strangedb/java/objects/lists/ObjectStrangeDbList.java @@ -17,7 +17,7 @@ public class ObjectStrangeDbList extends StrangeDbList { return indices; } - protected ObjectStrangeDbList() { + public ObjectStrangeDbList() { super(); } diff --git a/src/main/java/it/cavallium/strangedb/java/objects/lists/StrangeDbList.java b/src/main/java/it/cavallium/strangedb/java/objects/lists/StrangeDbList.java index 05696aa..1d4f9e6 100644 --- a/src/main/java/it/cavallium/strangedb/java/objects/lists/StrangeDbList.java +++ b/src/main/java/it/cavallium/strangedb/java/objects/lists/StrangeDbList.java @@ -5,10 +5,9 @@ import it.cavallium.strangedb.java.objects.EnhancedObject; import it.cavallium.strangedb.java.database.IDatabaseTools; import java.io.IOException; -import java.util.Collection; import java.util.StringJoiner; -public abstract class StrangeDbList extends EnhancedObject { +public abstract class StrangeDbList extends EnhancedObject implements ElementsList { private final Object indicesAccessLock = new Object(); @@ -22,6 +21,7 @@ public abstract class StrangeDbList extends EnhancedObject { super(databaseTools); } + @Override public T get(int index) throws IOException { synchronized (indicesAccessLock) { long uid = getIndices().getLong(index); @@ -29,6 +29,7 @@ public abstract class StrangeDbList extends EnhancedObject { } } + @Override public void add(T value) throws IOException { long uid = databaseTools.getObjectsIO().newNullObject(); synchronized (indicesAccessLock) { @@ -37,13 +38,12 @@ public abstract class StrangeDbList extends EnhancedObject { } } + @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) { @@ -52,6 +52,7 @@ public abstract class StrangeDbList extends EnhancedObject { } } + @Override public void add(int index, T value) throws IOException { long uid = databaseTools.getObjectsIO().newNullObject(); synchronized (indicesAccessLock) { @@ -88,7 +89,7 @@ public abstract class StrangeDbList extends EnhancedObject { @Override public String toString() { - return new StringJoiner(", ", StrangeDbList.class.getSimpleName() + "[", "]") + return new StringJoiner(", ", StrangeDbList.class.getSimpleName() + "[", "]") .add(getIndices().size() + " items") .toString(); } diff --git a/src/main/java/it/cavallium/strangedb/java/objects/lists/ValueOperation.java b/src/main/java/it/cavallium/strangedb/java/objects/lists/ValueOperation.java new file mode 100644 index 0000000..9675c3a --- /dev/null +++ b/src/main/java/it/cavallium/strangedb/java/objects/lists/ValueOperation.java @@ -0,0 +1,5 @@ +package it.cavallium.strangedb.java.objects.lists; + +public interface ValueOperation { + public boolean evaluate(Object value); +} diff --git a/src/main/java/it/cavallium/strangedb/java/objects/lists/ValuePointer.java b/src/main/java/it/cavallium/strangedb/java/objects/lists/ValuePointer.java new file mode 100644 index 0000000..0e6723f --- /dev/null +++ b/src/main/java/it/cavallium/strangedb/java/objects/lists/ValuePointer.java @@ -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 ValuePointer ofField(Class parentType, String field) { + return base.field(parentType, field); + } + + public static ValuePointer ofProp(Class parentType, String property) { + return base.prop(parentType, property); + } + public static ValuePointer ofPrimitiveField(Class parentType, String primitiveFieldName) { + return base.primitiveField(parentType, primitiveFieldName); + } + + public ValuePointer prop(Class 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 ValuePointer field(Class 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 ValuePointer primitiveField(Class 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[] 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 getAdditionalData() { + return (T) additionalData[additionalData.length - 1]; + } + + public Class getRootType() { + return rootType; + } +} diff --git a/src/main/java/it/cavallium/strangedb/java/objects/lists/operations/Contains.java b/src/main/java/it/cavallium/strangedb/java/objects/lists/operations/Contains.java new file mode 100644 index 0000000..b40f556 --- /dev/null +++ b/src/main/java/it/cavallium/strangedb/java/objects/lists/operations/Contains.java @@ -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 implements ValueOperation { + + private final T containsValue; + + private Contains(T containsValue) { + this.containsValue = containsValue; + } + + public static Contains containsValue(T value) { + return new Contains(value); + } + + @Override + public boolean evaluate(Object value) { + if (value instanceof CharSequence) { + return KMP.KMP((CharSequence) value, containsValue) != -1; + } + return false; + } +} diff --git a/src/main/java/it/cavallium/strangedb/java/objects/lists/operations/ContainsIgnoreCase.java b/src/main/java/it/cavallium/strangedb/java/objects/lists/operations/ContainsIgnoreCase.java new file mode 100644 index 0000000..8594775 --- /dev/null +++ b/src/main/java/it/cavallium/strangedb/java/objects/lists/operations/ContainsIgnoreCase.java @@ -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 { + + 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; + } +} diff --git a/src/main/java/it/cavallium/strangedb/java/objects/lists/operations/Equals.java b/src/main/java/it/cavallium/strangedb/java/objects/lists/operations/Equals.java new file mode 100644 index 0000000..a8e5c1b --- /dev/null +++ b/src/main/java/it/cavallium/strangedb/java/objects/lists/operations/Equals.java @@ -0,0 +1,21 @@ +package it.cavallium.strangedb.java.objects.lists.operations; + +import it.cavallium.strangedb.java.objects.lists.ValueOperation; + +public class Equals implements ValueOperation { + + private final T equalToValue; + + private Equals(T equalToValue) { + this.equalToValue = equalToValue; + } + + public static Equals to(T value) { + return new Equals(value); + } + + @Override + public boolean evaluate(Object value) { + return equalToValue.equals(value); + } +} diff --git a/src/test/java/it/cavallium/strangedb/tests/Clean.java b/src/test/java/it/cavallium/strangedb/tests/Clean.java index fc32e0d..bd0a977 100644 --- a/src/test/java/it/cavallium/strangedb/tests/Clean.java +++ b/src/test/java/it/cavallium/strangedb/tests/Clean.java @@ -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); } diff --git a/src/test/java/it/cavallium/strangedb/tests/EnhancedClassUpdate.java b/src/test/java/it/cavallium/strangedb/tests/EnhancedClassUpdate.java index 2416e48..bb6b272 100644 --- a/src/test/java/it/cavallium/strangedb/tests/EnhancedClassUpdate.java +++ b/src/test/java/it/cavallium/strangedb/tests/EnhancedClassUpdate.java @@ -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); assertEquals(root.field4, "Abc"); assertEquals(root.field2, 12); assertEquals(root.field1, 13L); diff --git a/src/test/java/it/cavallium/strangedb/tests/ListContainer.java b/src/test/java/it/cavallium/strangedb/tests/ListContainer.java new file mode 100644 index 0000000..ad27ca7 --- /dev/null +++ b/src/test/java/it/cavallium/strangedb/tests/ListContainer.java @@ -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 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); + } + } + +} diff --git a/src/test/java/it/cavallium/strangedb/tests/MultipleEnhancedObjects.java b/src/test/java/it/cavallium/strangedb/tests/MultipleEnhancedObjects.java index 23cd1db..d6177d7 100644 --- a/src/test/java/it/cavallium/strangedb/tests/MultipleEnhancedObjects.java +++ b/src/test/java/it/cavallium/strangedb/tests/MultipleEnhancedObjects.java @@ -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); } diff --git a/src/test/java/it/cavallium/strangedb/tests/ObjectListTests.java b/src/test/java/it/cavallium/strangedb/tests/ObjectListTests.java new file mode 100644 index 0000000..784337e --- /dev/null +++ b/src/test/java/it/cavallium/strangedb/tests/ObjectListTests.java @@ -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 objectList; + @DbField(id = 1,name = "enhancedObjectList", type = DbDataType.ENHANCED_OBJECT) + public EnhancedObjectStrangeDbList 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; + } + } +} diff --git a/src/test/java/it/cavallium/strangedb/tests/Performance.java b/src/test/java/it/cavallium/strangedb/tests/Performance.java index 2fa5bab..1f82833 100644 --- a/src/test/java/it/cavallium/strangedb/tests/Performance.java +++ b/src/test/java/it/cavallium/strangedb/tests/Performance.java @@ -49,16 +49,16 @@ public class Performance { 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 root creation", 3000, Performance::regenDb, () -> db.loadRoot(PreloadedListContainer::new), () -> {}); final VariableWrapper preloadedListContainer = new VariableWrapper<>(null); final VariableWrapper simpleEnhancedObjectContainer = new VariableWrapper<>(null); testS("ObjectStrangeDbList 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: 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 +67,7 @@ public class Performance { }, () -> {}); testS("ObjectStrangeDbList: 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 +83,7 @@ public class Performance { }, () -> {}); testS("ObjectStrangeDbList: 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 +92,7 @@ public class Performance { }, () -> {}); testS("ObjectStrangeDbList: 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,7 +101,7 @@ public class Performance { }, () -> {}); testS("ObjectStrangeDbList: 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); @@ -113,7 +113,7 @@ public class Performance { }, () -> {}); testS("ObjectStrangeDbList: 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; @@ -132,7 +132,7 @@ public class Performance { }, () -> {}); testS("ObjectStrangeDbList: 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); @@ -144,7 +144,7 @@ public class Performance { }, () -> {}); testS("ObjectStrangeDbList: 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 +154,7 @@ public class Performance { }, () -> {}); testS("ObjectStrangeDbList: 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 +171,7 @@ public class Performance { }, () -> {}); testS("ObjectStrangeDbList: 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); @@ -318,12 +318,14 @@ public class Performance { } - @DbPropertyGetter(id = 0, type = DbDataType.ENHANCED_OBJECT) + @DbProperty(id = 0, type = DbDataType.ENHANCED_OBJECT) + @DbPropertyGetter() public ObjectStrangeDbList getList() { return getProperty(); } - @DbPropertySetter(id = 1, type = DbDataType.ENHANCED_OBJECT) + @DbProperty(id = 1, type = DbDataType.ENHANCED_OBJECT) + @DbPropertySetter() public void setList(ObjectStrangeDbList list) { setProperty(list); } diff --git a/src/test/java/it/cavallium/strangedb/tests/QueryTests.java b/src/test/java/it/cavallium/strangedb/tests/QueryTests.java new file mode 100644 index 0000000..9d4aaad --- /dev/null +++ b/src/test/java/it/cavallium/strangedb/tests/QueryTests.java @@ -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 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); + } +} diff --git a/src/test/java/it/cavallium/strangedb/tests/User.java b/src/test/java/it/cavallium/strangedb/tests/User.java new file mode 100644 index 0000000..202001a --- /dev/null +++ b/src/test/java/it/cavallium/strangedb/tests/User.java @@ -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(); + } +} diff --git a/src/test/java/it/cavallium/strangedb/tests/UserFullInfo.java b/src/test/java/it/cavallium/strangedb/tests/UserFullInfo.java new file mode 100644 index 0000000..d9fd2bf --- /dev/null +++ b/src/test/java/it/cavallium/strangedb/tests/UserFullInfo.java @@ -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(); + } +} diff --git a/src/test/java/it/cavallium/strangedb/utils/NTestUtils.java b/src/test/java/it/cavallium/strangedb/utils/NTestUtils.java index e79be44..f34246b 100644 --- a/src/test/java/it/cavallium/strangedb/utils/NTestUtils.java +++ b/src/test/java/it/cavallium/strangedb/utils/NTestUtils.java @@ -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.REFERENCES_LIST) + @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.REFERENCES_LIST) + @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); }