Added default parser, fixed wrong index sharing, moved app package
This commit is contained in:
parent
fa692b391a
commit
69951ebc50
33
src/main/java/org/warp/jcwdb/DBGenericObjectParser.java
Normal file
33
src/main/java/org/warp/jcwdb/DBGenericObjectParser.java
Normal file
|
@ -0,0 +1,33 @@
|
|||
package org.warp.jcwdb;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
|
||||
import com.esotericsoftware.kryo.Kryo;
|
||||
import com.esotericsoftware.kryo.io.Output;
|
||||
|
||||
public class DBGenericObjectParser extends DBTypeParserImpl<Object> {
|
||||
private static final Kryo kryo = new Kryo();
|
||||
static {
|
||||
kryo.setRegistrationRequired(false);
|
||||
}
|
||||
|
||||
private static final DBReader<Object> defaultReader = (i, size) -> {
|
||||
return kryo.readClassAndObject(i);
|
||||
};
|
||||
|
||||
public DBReader<Object> getReader() {
|
||||
return defaultReader;
|
||||
}
|
||||
|
||||
public DBDataOutput<Object> getWriter(final Object value) {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
Output tmpO = new Output(baos);
|
||||
kryo.writeClassAndObject(tmpO, value);
|
||||
tmpO.flush();
|
||||
final byte[] bytes = baos.toByteArray();
|
||||
tmpO.close();
|
||||
return DBDataOutput.create((o) -> {
|
||||
o.write(bytes);
|
||||
}, DBStandardTypes.GENERIC_OBJECT, bytes.length);
|
||||
}
|
||||
}
|
|
@ -1,20 +1,22 @@
|
|||
package org.warp.jcwdb;
|
||||
|
||||
public class DBStandardTypes {
|
||||
private static final int STD = 0xFFFFF000;
|
||||
public static final int BOOLEAN = STD| 0;
|
||||
public static final int BYTE = STD| 1;
|
||||
public static final int SHORT = STD| 2;
|
||||
public static final int CHAR = STD| 3;
|
||||
public static final int INTEGER = STD| 4;
|
||||
public static final int FLOAT = STD| 5;
|
||||
public static final int DOUBLE = STD| 6;
|
||||
public static final int STRING = STD| 7;
|
||||
public static final int BYTE_ARRAY = STD| 8;
|
||||
public static final int LIGHT_LIST = STD| 9;
|
||||
private static final int STD = 0xFFFFF000;
|
||||
public static final int BOOLEAN = STD| 0;
|
||||
public static final int BYTE = STD| 1;
|
||||
public static final int SHORT = STD| 2;
|
||||
public static final int CHAR = STD| 3;
|
||||
public static final int INTEGER = STD| 4;
|
||||
public static final int FLOAT = STD| 5;
|
||||
public static final int DOUBLE = STD| 6;
|
||||
public static final int STRING = STD| 7;
|
||||
public static final int BYTE_ARRAY = STD| 8;
|
||||
public static final int LIGHT_LIST = STD| 9;
|
||||
public static final int GENERIC_OBJECT = STD| 10;
|
||||
|
||||
public static void registerStandardTypes(JCWDatabase db, TypesManager typesManager) {
|
||||
typesManager.registerType(String.class, STRING, new DBStringParser());
|
||||
typesManager.registerType(LightList.class, LIGHT_LIST, new DBLightListParser(db));
|
||||
typesManager.registerTypeFallback(new DBGenericObjectParser());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,13 +5,13 @@ import java.nio.file.Path;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
public class JCWDatabase implements AutoCloseable {
|
||||
protected final TypesManager typesManager;
|
||||
protected final MixedIndexDatabase indices;
|
||||
private final WeakHashMap<Long, EntryReference<?>> references;
|
||||
private final WeakHashMap<Object, EntryReference<?>> referencesByObject;
|
||||
private volatile boolean closed;
|
||||
private final Object closeLock = new Object();
|
||||
private final Object indicesAccessLock = new Object();
|
||||
|
@ -21,7 +21,6 @@ public class JCWDatabase implements AutoCloseable {
|
|||
this.typesManager = new TypesManager(this);
|
||||
this.indices = new MixedIndexDatabase(typesManager, dataFile, metadataFile);
|
||||
this.references = new WeakHashMap<>();
|
||||
this.referencesByObject = new WeakHashMap<>();
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
||||
try {
|
||||
JCWDatabase.this.close();
|
||||
|
@ -53,7 +52,6 @@ public class JCWDatabase implements AutoCloseable {
|
|||
DBTypeParser<T> typeParser = this.typesManager.get(type);
|
||||
ref = new EntryReference<>(this, index, typeParser);
|
||||
this.references.put(index, ref);
|
||||
this.referencesByObject.put(ref.value, ref);
|
||||
}
|
||||
return ref;
|
||||
}
|
||||
|
@ -62,10 +60,7 @@ public class JCWDatabase implements AutoCloseable {
|
|||
protected <T> EntryReference<T> add(T value) throws IOException {
|
||||
checkClosed();
|
||||
synchronized (referencesAccessLock) {
|
||||
EntryReference<T> ref = (EntryReference<T>) referencesByObject.getOrDefault(value, null);
|
||||
if (ref != null) {
|
||||
return ref;
|
||||
}
|
||||
EntryReference<T> ref;
|
||||
DBTypeParser<T> typeParser = this.typesManager.get((Class<T>) value.getClass());
|
||||
long index;
|
||||
synchronized (indicesAccessLock) {
|
||||
|
@ -73,7 +68,6 @@ public class JCWDatabase implements AutoCloseable {
|
|||
}
|
||||
ref = new EntryReference<>(this, index, typeParser, value);
|
||||
this.references.put(index, ref);
|
||||
this.referencesByObject.put(value, ref);
|
||||
return ref;
|
||||
}
|
||||
}
|
||||
|
@ -102,7 +96,6 @@ public class JCWDatabase implements AutoCloseable {
|
|||
}
|
||||
ref = new EntryReference<>(this, index, typeParser);
|
||||
this.references.put(index, ref);
|
||||
this.referencesByObject.put(value, ref);
|
||||
return ref;
|
||||
}
|
||||
}
|
||||
|
@ -115,7 +108,7 @@ public class JCWDatabase implements AutoCloseable {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
public void close() throws IOException {
|
||||
if (closed) {
|
||||
return;
|
||||
}
|
||||
|
|
14
src/main/java/org/warp/jcwdb/NoParserFoundException.java
Normal file
14
src/main/java/org/warp/jcwdb/NoParserFoundException.java
Normal file
|
@ -0,0 +1,14 @@
|
|||
package org.warp.jcwdb;
|
||||
|
||||
public class NoParserFoundException extends NullPointerException {
|
||||
|
||||
public NoParserFoundException(String string) {
|
||||
super(string);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 701010818132241139L;
|
||||
|
||||
}
|
|
@ -7,6 +7,7 @@ import java.util.HashMap;
|
|||
public class TypesManager {
|
||||
private final Map<Integer, DBTypeParser<?>> types;
|
||||
private final Map<Class<?>, DBTypeParser<?>> typesByClass;
|
||||
private DBTypeParser<?> fallbackParser;
|
||||
|
||||
public TypesManager(JCWDatabase db) {
|
||||
types = new HashMap<>();
|
||||
|
@ -19,11 +20,29 @@ public class TypesManager {
|
|||
this.typesByClass.put(clazz, parser);
|
||||
}
|
||||
|
||||
public void registerTypeFallback(DBTypeParser<?> parser) {
|
||||
this.fallbackParser = parser;
|
||||
}
|
||||
|
||||
public <T> DBTypeParser<T> get(int type) {
|
||||
if (types.containsKey(type) == false) {
|
||||
if (fallbackParser == null) {
|
||||
throw new NoParserFoundException("The type " + type + " can't be parsed.");
|
||||
} else {
|
||||
return fallbackParser.cast();
|
||||
}
|
||||
}
|
||||
return types.get(type).cast();
|
||||
}
|
||||
|
||||
public <T> DBTypeParser<T> get(Class<T> type) {
|
||||
if (typesByClass.containsKey(type) == false) {
|
||||
if (fallbackParser == null) {
|
||||
throw new NoParserFoundException("The class " + type.getSimpleName() + " can't be parsed.");
|
||||
} else {
|
||||
return fallbackParser.cast();
|
||||
}
|
||||
}
|
||||
return typesByClass.get(type).cast();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,14 +13,28 @@ public class App {
|
|||
Files.delete(Paths.get(args[0]));
|
||||
Files.delete(Paths.get(args[1]));
|
||||
}
|
||||
System.out.println("Loading database...");
|
||||
long time0 = System.currentTimeMillis();
|
||||
JCWDatabase db = new JCWDatabase(Paths.get(args[0]), Paths.get(args[1]));
|
||||
long time01 = System.currentTimeMillis();
|
||||
System.out.println("Time elapsed: " + (time01 - time0));
|
||||
System.out.println("Loading root...");
|
||||
LightList<String> root = ((EntryReference<LightList<String>>) db.getRoot().cast()).value;
|
||||
long time1 = System.currentTimeMillis();
|
||||
System.out.println("Time elapsed: " + (time1 - time01));
|
||||
System.out.println("Root:");
|
||||
for (int i = 0; i < root.size(); i++) {
|
||||
System.out.println(" - " + root.get(i));
|
||||
}
|
||||
for (int i = 0; i < 100; i++) {
|
||||
root.add("Test " + System.currentTimeMillis());
|
||||
// for (int i = 0; i < root.size(); i++) {
|
||||
// System.out.println(" - " + root.get(i));
|
||||
// }
|
||||
for (int i = 0; i < 500000000; i++) {
|
||||
root.add("Test " + i);
|
||||
}
|
||||
long time2 = System.currentTimeMillis();
|
||||
System.out.println("Root size: "+root.size());
|
||||
System.out.println("Time elapsed: " + (time2 - time1));
|
||||
System.out.println("Saving database...");
|
||||
db.close();
|
||||
long time3 = System.currentTimeMillis();
|
||||
System.out.println("Time elapsed: " + (time3 - time2));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
package org.warp;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Unit test for simple App.
|
||||
*/
|
||||
public class AppTest
|
||||
{
|
||||
/**
|
||||
* Rigorous Test :-)
|
||||
*/
|
||||
@Test
|
||||
public void shouldAnswerWithTrue()
|
||||
{
|
||||
assertTrue( true );
|
||||
}
|
||||
}
|
145
src/test/java/org/warp/jcwdb/AppTest.java
Normal file
145
src/test/java/org/warp/jcwdb/AppTest.java
Normal file
|
@ -0,0 +1,145 @@
|
|||
package org.warp.jcwdb;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.warp.jcwdb.EntryReference;
|
||||
import org.warp.jcwdb.JCWDatabase;
|
||||
import org.warp.jcwdb.LightList;
|
||||
|
||||
/**
|
||||
* Unit test for simple App.
|
||||
*/
|
||||
public class AppTest
|
||||
{
|
||||
/**
|
||||
* Rigorous Test :-)
|
||||
*/
|
||||
@Test
|
||||
public void shouldAnswerWithTrue()
|
||||
{
|
||||
assertTrue( true );
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
@Test
|
||||
public void shouldReadCustomClasses() throws IOException
|
||||
{
|
||||
CustomClass customClass = new CustomClass();
|
||||
customClass.primitive = 1;
|
||||
customClass.string = "test";
|
||||
|
||||
Path path = Files.createTempFile("", ".db"), idx = Files.createTempFile("", ".idx");
|
||||
|
||||
JCWDatabase db = new JCWDatabase(path, idx);
|
||||
EntryReference<LightList<CustomClass>> ref = db.getRoot();
|
||||
LightList<CustomClass> root = ref.value;
|
||||
root.add(customClass);
|
||||
db.close();
|
||||
|
||||
JCWDatabase db2 = new JCWDatabase(path, idx);
|
||||
EntryReference<LightList<CustomClass>> ref2 = db2.getRoot();
|
||||
LightList<CustomClass> root2 = ref2.value;
|
||||
CustomClass customClass2 = root2.get(0);
|
||||
assertTrue(customClass.equals(customClass2));
|
||||
assertTrue(customClass.string.equals(customClass2.string));
|
||||
db2.close();
|
||||
Files.delete(path);
|
||||
Files.delete(idx);
|
||||
}
|
||||
|
||||
public static class CustomClass {
|
||||
public String string;
|
||||
public int primitive;
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + primitive;
|
||||
result = prime * result + ((string == null) ? 0 : string.hashCode());
|
||||
return result;
|
||||
}
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
CustomClass other = (CustomClass) obj;
|
||||
if (primitive != other.primitive)
|
||||
return false;
|
||||
if (string == null) {
|
||||
if (other.string != null)
|
||||
return false;
|
||||
} else if (!string.equals(other.string))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
@Test
|
||||
public void shouldActAsAnArrayListWithEqualObjects() throws IOException
|
||||
{
|
||||
Path path = Files.createTempFile("", ".db"), idx = Files.createTempFile("", ".idx");
|
||||
JCWDatabase db = new JCWDatabase(path, idx);
|
||||
EntryReference<LightList<String>> ref = db.getRoot();
|
||||
LightList<String> list1 = ref.value;
|
||||
ArrayList<String> list2 = new ArrayList<String>();
|
||||
String s = "a";
|
||||
for (int i = 0; i < 10; i++) {
|
||||
list1.add(s);
|
||||
list2.add(s);
|
||||
}
|
||||
assertTrue(list1.size() == list2.size());
|
||||
for (int i = 0; i < 10; i++) {
|
||||
assertTrue(list1.get(i) == list2.get(i));
|
||||
assertTrue(list2.get(i) == list1.get(i));
|
||||
}
|
||||
db.close();
|
||||
Files.delete(path);
|
||||
Files.delete(idx);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
@Test
|
||||
public void shouldActAsAnArrayListWithDifferentObjects() throws IOException
|
||||
{
|
||||
Path path = Files.createTempFile("", ".db"), idx = Files.createTempFile("", ".idx");
|
||||
JCWDatabase db = new JCWDatabase(path, idx);
|
||||
EntryReference<LightList<String>> ref = db.getRoot();
|
||||
LightList<String> list1 = ref.value;
|
||||
ArrayList<String> list2 = new ArrayList<String>();
|
||||
for (int i = 0; i < 10; i++) {
|
||||
String s = String.valueOf(i);
|
||||
list1.add(s);
|
||||
list2.add(s);
|
||||
}
|
||||
assertTrue(list1.size() == list2.size());
|
||||
for (int i = 0; i < 10; i++) {
|
||||
assertTrue(list1.get(i) == list2.get(i));
|
||||
assertTrue(list2.get(i) == list1.get(i));
|
||||
}
|
||||
db.close();
|
||||
Files.delete(path);
|
||||
Files.delete(idx);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user