Implement InProgress transactions and InProgress and Entity builders on Android.

Rename some of the functions in TypedValue, TupleResult and QueryBuilder to make them more Javay and less Swifty
This commit is contained in:
Emily Toop 2018-04-26 17:29:44 +01:00
parent 38c1a93712
commit e0cd9b6b20
12 changed files with 1698 additions and 76 deletions

View file

@ -22,7 +22,6 @@ import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.ByteBuffer;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
@ -40,6 +39,16 @@ import static org.junit.Assert.*;
@RunWith(AndroidJUnit4.class)
public class FFIIntegrationTest {
class DBSetupResult {
TxReport schemaReport;
TxReport dataReport;
public DBSetupResult(TxReport schemaReport, TxReport dataReport) {
this.schemaReport = schemaReport;
this.dataReport = dataReport;
}
}
Mentat mentat = null;
@Test
@ -95,7 +104,8 @@ public class FFIIntegrationTest {
return this.mentat;
}
public TxReport populateWithTypesSchema(Mentat mentat) {
public DBSetupResult populateWithTypesSchema(Mentat mentat) {
InProgress transaction = mentat.beginTransaction();
String schema = "[\n" +
" [:db/add \"b\" :db/ident :foo/boolean]\n" +
" [:db/add \"b\" :db/valueType :db.type/boolean]\n" +
@ -122,7 +132,7 @@ public class FFIIntegrationTest {
" [:db/add \"u\" :db/valueType :db.type/uuid]\n" +
" [:db/add \"u\" :db/cardinality :db.cardinality/one]\n" +
" ]";
TxReport report = mentat.transact(schema);
TxReport report = transaction.transact(schema);
Long stringEntid = report.getEntidForTempId("s");
String data = "[\n" +
@ -135,13 +145,16 @@ public class FFIIntegrationTest {
" [:db/add \"a\" :foo/uuid #uuid \"550e8400-e29b-41d4-a716-446655440000\"]\n" +
" [:db/add \"b\" :foo/boolean false]\n" +
" [:db/add \"b\" :foo/ref "+ stringEntid +"]\n" +
" [:db/add \"b\" :foo/keyword :foo/string]\n" +
" [:db/add \"b\" :foo/long 50]\n" +
" [:db/add \"b\" :foo/instant #inst \"2018-01-01T11:00:00.000Z\"]\n" +
" [:db/add \"b\" :foo/double 22.46]\n" +
" [:db/add \"b\" :foo/string \"Silence is worse; all truths that are kept silent become poisonous.\"]\n" +
" [:db/add \"b\" :foo/uuid #uuid \"4cb3f828-752d-497a-90c9-b1fd516d5644\"]\n" +
" ]";
return mentat.transact(data);
TxReport dataReport = transaction.transact(data);
transaction.commit();
return new DBSetupResult(report, dataReport);
}
@Test
@ -168,7 +181,7 @@ public class FFIIntegrationTest {
Mentat mentat = openAndInitializeCitiesStore();
String query = "[:find ?n . :in ?name :where [(fulltext $ :community/name ?name) [[?e ?n]]]]";
final Expectation expectation = new Expectation();
mentat.query(query).bindString("?name", "Wallingford").runScalar(new ScalarResultHandler() {
mentat.query(query).bind("?name", "Wallingford").run(new ScalarResultHandler() {
@Override
public void handleValue(TypedValue value) {
assertNotNull(value);
@ -187,7 +200,7 @@ public class FFIIntegrationTest {
Mentat mentat = openAndInitializeCitiesStore();
String query = "[:find [?when ...] :where [_ :db/txInstant ?when] :order (asc ?when)]";
final Expectation expectation = new Expectation();
mentat.query(query).runColl(new CollResultHandler() {
mentat.query(query).run(new CollResultHandler() {
@Override
public void handleList(CollResult list) {
assertNotNull(list);
@ -208,7 +221,7 @@ public class FFIIntegrationTest {
Mentat mentat = openAndInitializeCitiesStore();
String query = "[:find [?when ...] :where [_ :db/txInstant ?when] :order (asc ?when)]";
final Expectation expectation = new Expectation();
mentat.query(query).runColl(new CollResultHandler() {
mentat.query(query).run(new CollResultHandler() {
@Override
public void handleList(CollResult list) {
assertNotNull(list);
@ -234,7 +247,7 @@ public class FFIIntegrationTest {
" [?c :community/type :community.type/website]\n" +
" [(fulltext $ :community/category \"food\") [[?c ?cat]]]]";
final Expectation expectation = new Expectation();
mentat.query(query).runTuple(new TupleResultHandler() {
mentat.query(query).run(new TupleResultHandler() {
@Override
public void handleRow(TupleResult row) {
assertNotNull(row);
@ -333,11 +346,11 @@ public class FFIIntegrationTest {
@Test
public void bindingLongValueSucceeds() throws InterruptedException {
Mentat mentat = new Mentat();
TxReport report = this.populateWithTypesSchema(mentat);
TxReport report = this.populateWithTypesSchema(mentat).dataReport;
final Long aEntid = report.getEntidForTempId("a");
String query = "[:find ?e . :in ?long :where [?e :foo/long ?long]]";
final Expectation expectation = new Expectation();
mentat.query(query).bindLong("?long", 25).runScalar(new ScalarResultHandler() {
mentat.query(query).bind("?long", 25).run(new ScalarResultHandler() {
@Override
public void handleValue(TypedValue value) {
assertNotNull(value);
@ -354,12 +367,12 @@ public class FFIIntegrationTest {
@Test
public void bindingRefValueSucceeds() throws InterruptedException {
Mentat mentat = new Mentat();
TxReport report = this.populateWithTypesSchema(mentat);
TxReport report = this.populateWithTypesSchema(mentat).dataReport;
long stringEntid = mentat.entIdForAttribute(":foo/string");
final Long bEntid = report.getEntidForTempId("b");
String query = "[:find ?e . :in ?ref :where [?e :foo/ref ?ref]]";
final Expectation expectation = new Expectation();
mentat.query(query).bindEntidReference("?ref", stringEntid).runScalar(new ScalarResultHandler() {
mentat.query(query).bindEntidReference("?ref", stringEntid).run(new ScalarResultHandler() {
@Override
public void handleValue(TypedValue value) {
assertNotNull(value);
@ -376,12 +389,12 @@ public class FFIIntegrationTest {
@Test
public void bindingRefKwValueSucceeds() throws InterruptedException {
Mentat mentat = new Mentat();
TxReport report = this.populateWithTypesSchema(mentat);
TxReport report = this.populateWithTypesSchema(mentat).dataReport;
String refKeyword = ":foo/string";
final Long bEntid = report.getEntidForTempId("b");
String query = "[:find ?e . :in ?ref :where [?e :foo/ref ?ref]]";
final Expectation expectation = new Expectation();
mentat.query(query).bindKeywordReference("?ref", refKeyword).runScalar(new ScalarResultHandler() {
mentat.query(query).bindKeywordReference("?ref", refKeyword).run(new ScalarResultHandler() {
@Override
public void handleValue(TypedValue value) {
assertNotNull(value);
@ -398,11 +411,11 @@ public class FFIIntegrationTest {
@Test
public void bindingKwValueSucceeds() throws InterruptedException {
Mentat mentat = new Mentat();
TxReport report = this.populateWithTypesSchema(mentat);
TxReport report = this.populateWithTypesSchema(mentat).dataReport;
final Long aEntid = report.getEntidForTempId("a");
String query = "[:find ?e . :in ?kw :where [?e :foo/keyword ?kw]]";
final Expectation expectation = new Expectation();
mentat.query(query).bindKeyword("?kw", ":foo/string").runScalar(new ScalarResultHandler() {
mentat.query(query).bindKeyword("?kw", ":foo/string").run(new ScalarResultHandler() {
@Override
public void handleValue(TypedValue value) {
assertNotNull(value);
@ -419,13 +432,13 @@ public class FFIIntegrationTest {
@Test
public void bindingDateValueSucceeds() throws InterruptedException, ParseException {
Mentat mentat = new Mentat();
TxReport report = this.populateWithTypesSchema(mentat);
TxReport report = this.populateWithTypesSchema(mentat).dataReport;
final Long aEntid = report.getEntidForTempId("a");
Date date = new Date(1523896758000L);
String query = "[:find [?e ?d] :in ?now :where [?e :foo/instant ?d] [(< ?d ?now)]]";
final Expectation expectation = new Expectation();
mentat.query(query).bindDate("?now", date).runTuple(new TupleResultHandler() {
mentat.query(query).bind("?now", date).run(new TupleResultHandler() {
@Override
public void handleRow(TupleResult row) {
assertNotNull(row);
@ -446,7 +459,7 @@ public class FFIIntegrationTest {
Mentat mentat = this.openAndInitializeCitiesStore();
String query = "[:find ?n . :in ?name :where [(fulltext $ :community/name ?name) [[?e ?n]]]]";
final Expectation expectation = new Expectation();
mentat.query(query).bindString("?name", "Wallingford").runScalar(new ScalarResultHandler() {
mentat.query(query).bind("?name", "Wallingford").run(new ScalarResultHandler() {
@Override
public void handleValue(TypedValue value) {
assertNotNull(value);
@ -463,12 +476,12 @@ public class FFIIntegrationTest {
@Test
public void bindingUuidValueSucceeds() throws InterruptedException {
Mentat mentat = new Mentat();
TxReport report = this.populateWithTypesSchema(mentat);
TxReport report = this.populateWithTypesSchema(mentat).dataReport;
final Long aEntid = report.getEntidForTempId("a");
String query = "[:find ?e . :in ?uuid :where [?e :foo/uuid ?uuid]]";
UUID uuid = UUID.fromString("550e8400-e29b-41d4-a716-446655440000");
final Expectation expectation = new Expectation();
mentat.query(query).bindUUID("?uuid", uuid).runScalar(new ScalarResultHandler() {
mentat.query(query).bind("?uuid", uuid).run(new ScalarResultHandler() {
@Override
public void handleValue(TypedValue value) {
assertNotNull(value);
@ -485,11 +498,11 @@ public class FFIIntegrationTest {
@Test
public void bindingBooleanValueSucceeds() throws InterruptedException {
Mentat mentat = new Mentat();
TxReport report = this.populateWithTypesSchema(mentat);
TxReport report = this.populateWithTypesSchema(mentat).dataReport;
final Long aEntid = report.getEntidForTempId("a");
String query = "[:find ?e . :in ?bool :where [?e :foo/boolean ?bool]]";
final Expectation expectation = new Expectation();
mentat.query(query).bindBoolean("?bool", true).runScalar(new ScalarResultHandler() {
mentat.query(query).bind("?bool", true).run(new ScalarResultHandler() {
@Override
public void handleValue(TypedValue value) {
assertNotNull(value);
@ -507,11 +520,11 @@ public class FFIIntegrationTest {
@Test
public void bindingDoubleValueSucceeds() throws InterruptedException {
Mentat mentat = new Mentat();
TxReport report = this.populateWithTypesSchema(mentat);
TxReport report = this.populateWithTypesSchema(mentat).dataReport;
final Long aEntid = report.getEntidForTempId("a");
String query = "[:find ?e . :in ?double :where [?e :foo/double ?double]]";
final Expectation expectation = new Expectation();
mentat.query(query).bindDouble("?double", 11.23).runScalar(new ScalarResultHandler() {
mentat.query(query).bind("?double", 11.23).run(new ScalarResultHandler() {
@Override
public void handleValue(TypedValue value) {
assertNotNull(value);
@ -528,11 +541,11 @@ public class FFIIntegrationTest {
@Test
public void typedValueConvertsToLong() throws InterruptedException {
Mentat mentat = new Mentat();
TxReport report = this.populateWithTypesSchema(mentat);
TxReport report = this.populateWithTypesSchema(mentat).dataReport;
final Long aEntid = report.getEntidForTempId("a");
String query = "[:find ?v . :in ?e :where [?e :foo/long ?v]]";
final Expectation expectation = new Expectation();
mentat.query(query).bindEntidReference("?e", aEntid).runScalar(new ScalarResultHandler() {
mentat.query(query).bindEntidReference("?e", aEntid).run(new ScalarResultHandler() {
@Override
public void handleValue(TypedValue value) {
assertNotNull(value);
@ -550,11 +563,11 @@ public class FFIIntegrationTest {
@Test
public void typedValueConvertsToRef() throws InterruptedException {
Mentat mentat = new Mentat();
TxReport report = this.populateWithTypesSchema(mentat);
TxReport report = this.populateWithTypesSchema(mentat).dataReport;
final Long aEntid = report.getEntidForTempId("a");
String query = "[:find ?e . :where [?e :foo/long 25]]";
final Expectation expectation = new Expectation();
mentat.query(query).runScalar(new ScalarResultHandler() {
mentat.query(query).run(new ScalarResultHandler() {
@Override
public void handleValue(TypedValue value) {
assertNotNull(value);
@ -572,11 +585,11 @@ public class FFIIntegrationTest {
@Test
public void typedValueConvertsToKeyword() throws InterruptedException {
Mentat mentat = new Mentat();
TxReport report = this.populateWithTypesSchema(mentat);
TxReport report = this.populateWithTypesSchema(mentat).dataReport;
final Long aEntid = report.getEntidForTempId("a");
String query = "[:find ?v . :in ?e :where [?e :foo/keyword ?v]]";
final Expectation expectation = new Expectation();
mentat.query(query).bindEntidReference("?e", aEntid).runScalar(new ScalarResultHandler() {
mentat.query(query).bindEntidReference("?e", aEntid).run(new ScalarResultHandler() {
@Override
public void handleValue(TypedValue value) {
assertNotNull(value);
@ -594,11 +607,11 @@ public class FFIIntegrationTest {
@Test
public void typedValueConvertsToBoolean() throws InterruptedException {
Mentat mentat = new Mentat();
TxReport report = this.populateWithTypesSchema(mentat);
TxReport report = this.populateWithTypesSchema(mentat).dataReport;
final Long aEntid = report.getEntidForTempId("a");
String query = "[:find ?v . :in ?e :where [?e :foo/boolean ?v]]";
final Expectation expectation = new Expectation();
mentat.query(query).bindEntidReference("?e", aEntid).runScalar(new ScalarResultHandler() {
mentat.query(query).bindEntidReference("?e", aEntid).run(new ScalarResultHandler() {
@Override
public void handleValue(TypedValue value) {
assertNotNull(value);
@ -616,11 +629,11 @@ public class FFIIntegrationTest {
@Test
public void typedValueConvertsToDouble() throws InterruptedException {
Mentat mentat = new Mentat();
TxReport report = this.populateWithTypesSchema(mentat);
TxReport report = this.populateWithTypesSchema(mentat).dataReport;
final Long aEntid = report.getEntidForTempId("a");
String query = "[:find ?v . :in ?e :where [?e :foo/double ?v]]";
final Expectation expectation = new Expectation();
mentat.query(query).bindEntidReference("?e", aEntid).runScalar(new ScalarResultHandler() {
mentat.query(query).bindEntidReference("?e", aEntid).run(new ScalarResultHandler() {
@Override
public void handleValue(TypedValue value) {
assertNotNull(value);
@ -638,14 +651,14 @@ public class FFIIntegrationTest {
@Test
public void typedValueConvertsToDate() throws InterruptedException, ParseException {
Mentat mentat = new Mentat();
TxReport report = this.populateWithTypesSchema(mentat);
TxReport report = this.populateWithTypesSchema(mentat).dataReport;
final Long aEntid = report.getEntidForTempId("a");
String query = "[:find ?v . :in ?e :where [?e :foo/instant ?v]]";
final Expectation expectation = new Expectation();
DateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZZZZZ", Locale.ENGLISH);
format.parse("2017-01-01T11:00:00+00:00");
final Calendar expectedDate = format.getCalendar();
mentat.query(query).bindEntidReference("?e", aEntid).runScalar(new ScalarResultHandler() {
mentat.query(query).bindEntidReference("?e", aEntid).run(new ScalarResultHandler() {
@Override
public void handleValue(TypedValue value) {
assertNotNull(value);
@ -663,11 +676,11 @@ public class FFIIntegrationTest {
@Test
public void typedValueConvertsToString() throws InterruptedException {
Mentat mentat = new Mentat();
TxReport report = this.populateWithTypesSchema(mentat);
TxReport report = this.populateWithTypesSchema(mentat).dataReport;
final Long aEntid = report.getEntidForTempId("a");
String query = "[:find ?v . :in ?e :where [?e :foo/string ?v]]";
final Expectation expectation = new Expectation();
mentat.query(query).bindEntidReference("?e", aEntid).runScalar(new ScalarResultHandler() {
mentat.query(query).bindEntidReference("?e", aEntid).run(new ScalarResultHandler() {
@Override
public void handleValue(TypedValue value) {
assertNotNull(value);
@ -685,12 +698,12 @@ public class FFIIntegrationTest {
@Test
public void typedValueConvertsToUUID() throws InterruptedException {
Mentat mentat = new Mentat();
TxReport report = this.populateWithTypesSchema(mentat);
TxReport report = this.populateWithTypesSchema(mentat).dataReport;
final Long aEntid = report.getEntidForTempId("a");
String query = "[:find ?v . :in ?e :where [?e :foo/uuid ?v]]";
final UUID expectedUUID = UUID.fromString("550e8400-e29b-41d4-a716-446655440000");
final Expectation expectation = new Expectation();
mentat.query(query).bindEntidReference("?e", aEntid).runScalar(new ScalarResultHandler() {
mentat.query(query).bindEntidReference("?e", aEntid).run(new ScalarResultHandler() {
@Override
public void handleValue(TypedValue value) {
assertNotNull(value);
@ -708,7 +721,7 @@ public class FFIIntegrationTest {
@Test
public void valueForAttributeOfEntitySucceeds() throws InterruptedException {
Mentat mentat = new Mentat();
TxReport report = this.populateWithTypesSchema(mentat);
TxReport report = this.populateWithTypesSchema(mentat).dataReport;
final Long aEntid = report.getEntidForTempId("a");
TypedValue value = mentat.valueForAttributeOfEntity(":foo/long", aEntid);
assertNotNull(value);
@ -722,4 +735,511 @@ public class FFIIntegrationTest {
long entid = mentat.entIdForAttribute(":foo/long");
assertEquals(65540, entid);
}
@Test
public void testInProgressTransact() {
Mentat mentat = new Mentat();
TxReport report = this.populateWithTypesSchema(mentat).dataReport;
assertNotNull(report);
}
@Test
public void testInProgressRollback() {
Mentat mentat = new Mentat();
TxReport report = this.populateWithTypesSchema(mentat).dataReport;
assertNotNull(report);
long aEntid = report.getEntidForTempId("a");
TypedValue preLongValue = mentat.valueForAttributeOfEntity(":foo/long", aEntid);
assertEquals(25, preLongValue.asLong().longValue());
InProgress inProgress = mentat.beginTransaction();
report = inProgress.transact("[[:db/add "+ aEntid +" :foo/long 22]]");
assertNotNull(report);
inProgress.rollback();
TypedValue postLongValue = mentat.valueForAttributeOfEntity(":foo/long", aEntid);
assertEquals(25, postLongValue.asLong().longValue());
}
@Test
public void testInProgressEntityBuilder() throws InterruptedException {
Mentat mentat = new Mentat();
DBSetupResult reports = this.populateWithTypesSchema(mentat);
long bEntid = reports.dataReport.getEntidForTempId("b");
final long longEntid = reports.schemaReport.getEntidForTempId("l");
final long stringEntid = reports.schemaReport.getEntidForTempId("s");
// test that the values are as expected
String query = "[:find [?b ?i ?u ?l ?d ?s ?k ?r]\n" +
" :in ?e\n" +
" :where [?e :foo/boolean ?b]\n" +
" [?e :foo/instant ?i]\n" +
" [?e :foo/uuid ?u]\n" +
" [?e :foo/long ?l]\n" +
" [?e :foo/double ?d]\n" +
" [?e :foo/string ?s]\n" +
" [?e :foo/keyword ?k]\n" +
" [?e :foo/ref ?r]]";
final Expectation expectation1 = new Expectation();
mentat.query(query).bindEntidReference("?e", bEntid).run(new TupleResultHandler() {
@Override
public void handleRow(TupleResult row) {
assertNotNull(row);
assertEquals(false, row.asBool(0));
assertEquals(new Date(1514804400000l), row.asDate(1));
assertEquals(UUID.fromString("4cb3f828-752d-497a-90c9-b1fd516d5644"), row.asUUID(2));
assertEquals(50, row.asLong(3).longValue());
assertEquals(new Double(22.46), row.asDouble(4));
assertEquals("Silence is worse; all truths that are kept silent become poisonous.", row.asString(5));
assertEquals(":foo/string", row.asKeyword(6));
assertEquals(stringEntid, row.asEntid(7).longValue());
expectation1.fulfill();
}
});
synchronized (expectation1) {
expectation1.wait(1000);
}
assertTrue(expectation1.isFulfilled);
InProgressBuilder builder = mentat.entityBuilder();
builder.add(bEntid, ":foo/boolean", true);
final Date newDate = new Date(1524743301000l);
builder.add(bEntid, ":foo/instant", newDate);
final UUID newUUID = UUID.randomUUID();
builder.add(bEntid, ":foo/uuid", newUUID);
builder.add(bEntid, ":foo/long", 75);
builder.add(bEntid, ":foo/double", 81.3);
builder.add(bEntid, ":foo/string", "Become who you are!");
builder.addKeyword(bEntid, ":foo/keyword", ":foo/long");
builder.addRef(bEntid, ":foo/ref", longEntid);
builder.commit();
final Expectation expectation2 = new Expectation();
mentat.query(query).bindEntidReference("?e", bEntid).run(new TupleResultHandler() {
@Override
public void handleRow(TupleResult row) {
assertNotNull(row);
assertEquals(true, row.asBool(0));
System.out.println(row.asDate(1).getTime());
assertEquals(newDate, row.asDate(1));
assertEquals(newUUID, row.asUUID(2));
assertEquals(75, row.asLong(3).longValue());
assertEquals(new Double(81.3), row.asDouble(4));
assertEquals("Become who you are!", row.asString(5));
assertEquals(":foo/long", row.asKeyword(6));
assertEquals(longEntid, row.asEntid(7).longValue());
expectation2.fulfill();
}
});
synchronized (expectation2) {
expectation2.wait(1000);
}
assertTrue(expectation2.isFulfilled);
}
@Test
public void testEntityBuilderForEntid() throws InterruptedException {
Mentat mentat = new Mentat();
DBSetupResult reports = this.populateWithTypesSchema(mentat);
long bEntid = reports.dataReport.getEntidForTempId("b");
final long longEntid = reports.schemaReport.getEntidForTempId("l");
final long stringEntid = reports.schemaReport.getEntidForTempId("s");
// test that the values are as expected
String query = "[:find [?b ?i ?u ?l ?d ?s ?k ?r]\n" +
" :in ?e\n" +
" :where [?e :foo/boolean ?b]\n" +
" [?e :foo/instant ?i]\n" +
" [?e :foo/uuid ?u]\n" +
" [?e :foo/long ?l]\n" +
" [?e :foo/double ?d]\n" +
" [?e :foo/string ?s]\n" +
" [?e :foo/keyword ?k]\n" +
" [?e :foo/ref ?r]]";
final Expectation expectation1 = new Expectation();
mentat.query(query).bindEntidReference("?e", bEntid).run(new TupleResultHandler() {
@Override
public void handleRow(TupleResult row) {
assertNotNull(row);
assertEquals(false, row.asBool(0));
assertEquals(new Date(1514804400000l), row.asDate(1));
assertEquals(UUID.fromString("4cb3f828-752d-497a-90c9-b1fd516d5644"), row.asUUID(2));
assertEquals(50, row.asLong(3).longValue());
assertEquals(new Double(22.46), row.asDouble(4));
assertEquals("Silence is worse; all truths that are kept silent become poisonous.", row.asString(5));
assertEquals(":foo/string", row.asKeyword(6));
assertEquals(stringEntid, row.asEntid(7).longValue());
expectation1.fulfill();
}
});
synchronized (expectation1) {
expectation1.wait(1000);
}
assertTrue(expectation1.isFulfilled);
EntityBuilder builder = mentat.entityBuilder(bEntid);
builder.add(":foo/boolean", true);
final Date newDate = new Date(1524743301000l);
builder.add(":foo/instant", newDate);
final UUID newUUID = UUID.randomUUID();
builder.add(":foo/uuid", newUUID);
builder.add(":foo/long", 75);
builder.add(":foo/double", 81.3);
builder.add(":foo/string", "Become who you are!");
builder.addKeyword(":foo/keyword", ":foo/long");
builder.addRef(":foo/ref", longEntid);
builder.commit();
final Expectation expectation2 = new Expectation();
mentat.query(query).bindEntidReference("?e", bEntid).run(new TupleResultHandler() {
@Override
public void handleRow(TupleResult row) {
assertNotNull(row);
assertEquals(true, row.asBool(0));
System.out.println(row.asDate(1).getTime());
assertEquals(newDate, row.asDate(1));
assertEquals(newUUID, row.asUUID(2));
assertEquals(75, row.asLong(3).longValue());
assertEquals(new Double(81.3), row.asDouble(4));
assertEquals("Become who you are!", row.asString(5));
assertEquals(":foo/long", row.asKeyword(6));
assertEquals(longEntid, row.asEntid(7).longValue());
expectation2.fulfill();
}
});
synchronized (expectation2) {
expectation2.wait(1000);
}
assertTrue(expectation2.isFulfilled);
}
@Test
public void testEntityBuilderForTempid() throws InterruptedException {
Mentat mentat = new Mentat();
DBSetupResult reports = this.populateWithTypesSchema(mentat);
final long longEntid = reports.schemaReport.getEntidForTempId("l");
EntityBuilder builder = mentat.entityBuilder("c");
builder.add(":foo/boolean", true);
final Date newDate = new Date(1524743301000l);
builder.add(":foo/instant", newDate);
final UUID newUUID = UUID.randomUUID();
builder.add(":foo/uuid", newUUID);
builder.add(":foo/long", 75);
builder.add(":foo/double", 81.3);
builder.add(":foo/string", "Become who you are!");
builder.addKeyword(":foo/keyword", ":foo/long");
builder.addRef(":foo/ref", longEntid);
TxReport report = builder.commit();
long cEntid = report.getEntidForTempId("c");
// test that the values are as expected
String query = "[:find [?b ?i ?u ?l ?d ?s ?k ?r]\n" +
" :in ?e\n" +
" :where [?e :foo/boolean ?b]\n" +
" [?e :foo/instant ?i]\n" +
" [?e :foo/uuid ?u]\n" +
" [?e :foo/long ?l]\n" +
" [?e :foo/double ?d]\n" +
" [?e :foo/string ?s]\n" +
" [?e :foo/keyword ?k]\n" +
" [?e :foo/ref ?r]]";
final Expectation expectation = new Expectation();
mentat.query(query).bindEntidReference("?e", cEntid).run(new TupleResultHandler() {
@Override
public void handleRow(TupleResult row) {
assertNotNull(row);
assertEquals(true, row.asBool(0));
System.out.println(row.asDate(1).getTime());
assertEquals(newDate, row.asDate(1));
assertEquals(newUUID, row.asUUID(2));
assertEquals(75, row.asLong(3).longValue());
assertEquals(new Double(81.3), row.asDouble(4));
assertEquals("Become who you are!", row.asString(5));
assertEquals(":foo/long", row.asKeyword(6));
assertEquals(longEntid, row.asEntid(7).longValue());
expectation.fulfill();
}
});
synchronized (expectation) {
expectation.wait(1000);
}
assertTrue(expectation.isFulfilled);
}
@Test
public void testInProgressBuilderTransact() throws InterruptedException {
Mentat mentat = new Mentat();
DBSetupResult reports = this.populateWithTypesSchema(mentat);
long aEntid = reports.dataReport.getEntidForTempId("a");
long bEntid = reports.dataReport.getEntidForTempId("b");
final long longEntid = reports.schemaReport.getEntidForTempId("l");
InProgressBuilder builder = mentat.entityBuilder();
builder.add(bEntid, ":foo/boolean", true);
final Date newDate = new Date(1524743301000l);
builder.add(bEntid, ":foo/instant", newDate);
final UUID newUUID = UUID.randomUUID();
builder.add(bEntid, ":foo/uuid", newUUID);
builder.add(bEntid, ":foo/long", 75);
builder.add(bEntid, ":foo/double", 81.3);
builder.add(bEntid, ":foo/string", "Become who you are!");
builder.addKeyword(bEntid, ":foo/keyword", ":foo/long");
builder.addRef(bEntid, ":foo/ref", longEntid);
InProgressTransactionResult result = builder.transact();
assertNotNull(result);
assertNotNull(result.getInProgress());
assertNotNull(result.getReport());
result.getInProgress().transact("[[:db/add "+ aEntid +" :foo/long 22]]");
result.getInProgress().commit();
// test that the values are as expected
String query = "[:find [?b ?i ?u ?l ?d ?s ?k ?r]\n" +
" :in ?e\n" +
" :where [?e :foo/boolean ?b]\n" +
" [?e :foo/instant ?i]\n" +
" [?e :foo/uuid ?u]\n" +
" [?e :foo/long ?l]\n" +
" [?e :foo/double ?d]\n" +
" [?e :foo/string ?s]\n" +
" [?e :foo/keyword ?k]\n" +
" [?e :foo/ref ?r]]";
final Expectation expectation = new Expectation();
mentat.query(query).bindEntidReference("?e", bEntid).run(new TupleResultHandler() {
@Override
public void handleRow(TupleResult row) {
assertNotNull(row);
assertEquals(true, row.asBool(0));
System.out.println(row.asDate(1).getTime());
assertEquals(newDate, row.asDate(1));
assertEquals(newUUID, row.asUUID(2));
assertEquals(75, row.asLong(3).longValue());
assertEquals(new Double(81.3), row.asDouble(4));
assertEquals("Become who you are!", row.asString(5));
assertEquals(":foo/long", row.asKeyword(6));
assertEquals(longEntid, row.asEntid(7).longValue());
expectation.fulfill();
}
});
synchronized (expectation) {
expectation.wait(1000);
}
assertTrue(expectation.isFulfilled);
TypedValue longValue = mentat.valueForAttributeOfEntity(":foo/long", aEntid);
assertEquals(22, longValue.asLong().longValue());
}
@Test
public void testEntityBuilderTransact() throws InterruptedException {
Mentat mentat = new Mentat();
DBSetupResult reports = this.populateWithTypesSchema(mentat);
long aEntid = reports.dataReport.getEntidForTempId("a");
long bEntid = reports.dataReport.getEntidForTempId("b");
final long longEntid = reports.schemaReport.getEntidForTempId("l");
EntityBuilder builder = mentat.entityBuilder(bEntid);
builder.add(":foo/boolean", true);
final Date newDate = new Date(1524743301000l);
builder.add(":foo/instant", newDate);
final UUID newUUID = UUID.randomUUID();
builder.add(":foo/uuid", newUUID);
builder.add(":foo/long", 75);
builder.add(":foo/double", 81.3);
builder.add(":foo/string", "Become who you are!");
builder.addKeyword(":foo/keyword", ":foo/long");
builder.addRef(":foo/ref", longEntid);
InProgressTransactionResult result = builder.transact();
assertNotNull(result);
assertNotNull(result.getInProgress());
assertNotNull(result.getReport());
result.getInProgress().transact("[[:db/add "+ aEntid +" :foo/long 22]]");
result.getInProgress().commit();
// test that the values are as expected
String query = "[:find [?b ?i ?u ?l ?d ?s ?k ?r]\n" +
" :in ?e\n" +
" :where [?e :foo/boolean ?b]\n" +
" [?e :foo/instant ?i]\n" +
" [?e :foo/uuid ?u]\n" +
" [?e :foo/long ?l]\n" +
" [?e :foo/double ?d]\n" +
" [?e :foo/string ?s]\n" +
" [?e :foo/keyword ?k]\n" +
" [?e :foo/ref ?r]]";
final Expectation expectation = new Expectation();
mentat.query(query).bindEntidReference("?e", bEntid).run(new TupleResultHandler() {
@Override
public void handleRow(TupleResult row) {
assertNotNull(row);
assertEquals(true, row.asBool(0));
System.out.println(row.asDate(1).getTime());
assertEquals(newDate, row.asDate(1));
assertEquals(newUUID, row.asUUID(2));
assertEquals(75, row.asLong(3).longValue());
assertEquals(new Double(81.3), row.asDouble(4));
assertEquals("Become who you are!", row.asString(5));
assertEquals(":foo/long", row.asKeyword(6));
assertEquals(longEntid, row.asEntid(7).longValue());
expectation.fulfill();
}
});
synchronized (expectation) {
expectation.wait(1000);
}
assertTrue(expectation.isFulfilled);
TypedValue longValue = mentat.valueForAttributeOfEntity(":foo/long", aEntid);
assertEquals(22, longValue.asLong().longValue());
}
@Test
public void testEntityBuilderRetract() throws InterruptedException {
Mentat mentat = new Mentat();
DBSetupResult reports = this.populateWithTypesSchema(mentat);
long bEntid = reports.dataReport.getEntidForTempId("b");
final long longEntid = reports.schemaReport.getEntidForTempId("l");
final long stringEntid = reports.schemaReport.getEntidForTempId("s");
// test that the values are as expected
String query = "[:find [?b ?i ?u ?l ?d ?s ?k ?r]\n" +
" :in ?e\n" +
" :where [?e :foo/boolean ?b]\n" +
" [?e :foo/instant ?i]\n" +
" [?e :foo/uuid ?u]\n" +
" [?e :foo/long ?l]\n" +
" [?e :foo/double ?d]\n" +
" [?e :foo/string ?s]\n" +
" [?e :foo/keyword ?k]\n" +
" [?e :foo/ref ?r]]";
final Expectation expectation1 = new Expectation();
final Date previousDate = new Date(1514804400000l);
final UUID previousUuid = UUID.fromString("4cb3f828-752d-497a-90c9-b1fd516d5644");
mentat.query(query).bindEntidReference("?e", bEntid).run(new TupleResultHandler() {
@Override
public void handleRow(TupleResult row) {
assertNotNull(row);
assertEquals(false, row.asBool(0));
assertEquals(previousDate, row.asDate(1));
assertEquals(previousUuid, row.asUUID(2));
assertEquals(50, row.asLong(3).longValue());
assertEquals(new Double(22.46), row.asDouble(4));
assertEquals("Silence is worse; all truths that are kept silent become poisonous.", row.asString(5));
assertEquals(":foo/string", row.asKeyword(6));
assertEquals(stringEntid, row.asEntid(7).longValue());
expectation1.fulfill();
}
});
synchronized (expectation1) {
expectation1.wait(1000);
}
EntityBuilder builder = mentat.entityBuilder(bEntid);
builder.retract(":foo/boolean", false);
builder.retract(":foo/instant", previousDate);
builder.retract(":foo/uuid", previousUuid);
builder.retract(":foo/long", 50);
builder.retract(":foo/double", 22.46);
builder.retract(":foo/string", "Silence is worse; all truths that are kept silent become poisonous.");
builder.retractKeyword(":foo/keyword", ":foo/string");
builder.retractRef(":foo/ref", stringEntid);
builder.commit();
final Expectation expectation2 = new Expectation();
mentat.query(query).bindEntidReference("?e", bEntid).run(new TupleResultHandler() {
@Override
public void handleRow(TupleResult row) {
assertNull(row);
expectation2.fulfill();
}
});
synchronized (expectation2) {
expectation2.wait(1000);
}
}
@Test
public void testInProgressBuilderRetract() throws InterruptedException {
Mentat mentat = new Mentat();
DBSetupResult reports = this.populateWithTypesSchema(mentat);
long bEntid = reports.dataReport.getEntidForTempId("b");
final long longEntid = reports.schemaReport.getEntidForTempId("l");
final long stringEntid = reports.schemaReport.getEntidForTempId("s");
// test that the values are as expected
String query = "[:find [?b ?i ?u ?l ?d ?s ?k ?r]\n" +
" :in ?e\n" +
" :where [?e :foo/boolean ?b]\n" +
" [?e :foo/instant ?i]\n" +
" [?e :foo/uuid ?u]\n" +
" [?e :foo/long ?l]\n" +
" [?e :foo/double ?d]\n" +
" [?e :foo/string ?s]\n" +
" [?e :foo/keyword ?k]\n" +
" [?e :foo/ref ?r]]";
final Expectation expectation1 = new Expectation();
final Date previousDate = new Date(1514804400000l);
final UUID previousUuid = UUID.fromString("4cb3f828-752d-497a-90c9-b1fd516d5644");
mentat.query(query).bindEntidReference("?e", bEntid).run(new TupleResultHandler() {
@Override
public void handleRow(TupleResult row) {
assertNotNull(row);
assertEquals(false, row.asBool(0));
assertEquals(previousDate, row.asDate(1));
assertEquals(previousUuid, row.asUUID(2));
assertEquals(50, row.asLong(3).longValue());
assertEquals(new Double(22.46), row.asDouble(4));
assertEquals("Silence is worse; all truths that are kept silent become poisonous.", row.asString(5));
assertEquals(":foo/string", row.asKeyword(6));
assertEquals(stringEntid, row.asEntid(7).longValue());
expectation1.fulfill();
}
});
synchronized (expectation1) {
expectation1.wait(1000);
}
InProgressBuilder builder = mentat.entityBuilder();
builder.retract(bEntid, ":foo/boolean", false);
builder.retract(bEntid, ":foo/instant", previousDate);
builder.retract(bEntid, ":foo/uuid", previousUuid);
builder.retract(bEntid, ":foo/long", 50);
builder.retract(bEntid, ":foo/double", 22.46);
builder.retract(bEntid, ":foo/string", "Silence is worse; all truths that are kept silent become poisonous.");
builder.retractKeyword(bEntid, ":foo/keyword", ":foo/string");
builder.retractRef(bEntid, ":foo/ref", stringEntid);
builder.commit();
final Expectation expectation2 = new Expectation();
mentat.query(query).bindEntidReference("?e", bEntid).run(new TupleResultHandler() {
@Override
public void handleRow(TupleResult row) {
assertNull(row);
expectation2.fulfill();
}
});
synchronized (expectation2) {
expectation2.wait(1000);
}
}
}

View file

@ -0,0 +1,357 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
* Copyright 2018 Mozilla
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
* this file except in compliance with the License. You may obtain a copy of the
* License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License. */
package com.mozilla.mentat;
import android.util.Log;
import com.sun.jna.Memory;
import com.sun.jna.Pointer;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Date;
import java.util.UUID;
/**
* This class wraps a raw pointer that points to a Rust `EntityBuilder<InProgressBuilder>` object.
*
* {@link EntityBuilder} provides a programmatic interface to performing assertions on a specific entity.
* It provides functions for adding and retracting values for attributes for an entity within
* an in progress transaction.
* <p/>
* The `transact` function will transact the assertions that have been added to the {@link EntityBuilder}
* and pass back the {@link TxReport} that was generated by this transact and the {@link InProgress} that was
* used to perform the transact. This enables you to perform further transacts on the same {@link InProgress}
* before committing.
* <p/>
* <pre>{@code
* long aEntid = txReport.getEntidForTempId("a");
* long bEntid = txReport.getEntidForTempId("b");
* EntityBuilder builder = mentat.getEntityBuilderForEntid(bEntid);
* builder.add(":foo/boolean", true);
* builder.add(":foo/instant", newDate);
* InProgress inProgress = builder.transact().getInProgress();
* inProgress.transact("[[:db/add \(aEntid) :foo/long 22]]");
* inProgress.commit();
* }</pre>
* <p/>
* The `commit` function will transact and commit the assertions that have been added to the {@link EntityBuilder}.
* It will consume the {@link InProgress} used to perform the transact. It returns the {@link TxReport} generated by
* the transact. After calling `commit`, a new transaction must be started by calling <pre>Mentat.beginTransaction()</pre>
* in order to perform further actions.
* <p/>
* <pre>{@code
* long aEntid = txReport.getEntidForTempId("a");
* EntityBuilder builder = mentat.getEntityBuilderForEntid(bEntid);
* builder.add(":foo/boolean", true);
* builder.add(":foo/instant", newDate);
* builder.commit();
* }</pre>
*/
public class EntityBuilder extends RustObject {
public EntityBuilder(Pointer pointer) {
this.rawPointer = pointer;
}
/**
* Asserts the value of attribute `keyword` to be the provided `value`.
* @param keyword The name of the attribute in the format `:namespace/name`.
* @param value The value to be asserted
*/
public void add(String keyword, long value) {
this.validate();
RustResult result = JNA.INSTANCE.entity_builder_add_long(this.rawPointer, keyword, value);
if (result.isFailure()) {
Log.e("EntityBuilder", result.err);
}
}
/**
* Asserts the value of attribute `keyword` to be the provided `value`.
* // TODO throw exception if error occurs
* @param keyword The name of the attribute in the format `:namespace/name`.
* @param value The value to be asserted
*/
public void addRef(String keyword, long value) {
this.validate();
RustResult result = JNA.INSTANCE.entity_builder_add_ref(this.rawPointer, keyword, value);
if (result.isFailure()) {
Log.e("EntityBuilder", result.err);
}
}
/**
* Asserts the value of attribute `keyword` to be the provided `value`.
* // TODO throw exception if error occurs
* @param keyword The name of the attribute in the format `:namespace/name`.
* @param value The value to be asserted
*/
public void addKeyword(String keyword, String value) {
this.validate();
RustResult result = JNA.INSTANCE.entity_builder_add_keyword(this.rawPointer, keyword, value);
if (result.isFailure()) {
Log.e("EntityBuilder", result.err);
}
}
/**
* Asserts the value of attribute `keyword` to be the provided `value`.
* // TODO throw exception if error occurs
* @param keyword The name of the attribute in the format `:namespace/name`.
* @param value The value to be asserted
*/
public void add(String keyword, boolean value) {
this.validate();
RustResult result = JNA.INSTANCE.entity_builder_add_boolean(this.rawPointer, keyword, value ? 1 : 0);
if (result.isFailure()) {
Log.e("EntityBuilder", result.err);
}
}
/**
* Asserts the value of attribute `keyword` to be the provided `value`.
* // TODO throw exception if error occurs
* @param keyword The name of the attribute in the format `:namespace/name`.
* @param value The value to be asserted
*/
public void add(String keyword, double value) {
this.validate();
RustResult result = JNA.INSTANCE.entity_builder_add_double(this.rawPointer, keyword, value);
if (result.isFailure()) {
Log.e("EntityBuilder", result.err);
}
}
/**
* Asserts the value of attribute `keyword` to be the provided `value`.
* // TODO throw exception if error occurs
* @param keyword The name of the attribute in the format `:namespace/name`.
* @param value The value to be asserted
*/
public void add(String keyword, Date value) {
this.validate();
RustResult result = JNA.INSTANCE.entity_builder_add_timestamp(this.rawPointer, keyword, value.getTime() * 1_000);
if (result.isFailure()) {
Log.e("EntityBuilder", result.err);
}
}
/**
* Asserts the value of attribute `keyword` to be the provided `value`.
* // TODO throw exception if error occurs
* @param keyword The name of the attribute in the format `:namespace/name`.
* @param value The value to be asserted
*/
public void add(String keyword, String value) {
this.validate();
RustResult result = JNA.INSTANCE.entity_builder_add_string(this.rawPointer, keyword, value);
if (result.isFailure()) {
Log.e("EntityBuilder", result.err);
}
}
/**
* Asserts the value of attribute `keyword` to be the provided `value`.
* // TODO throw exception if error occurs
* @param keyword The name of the attribute in the format `:namespace/name`.
* @param value The value to be asserted
*/
public void add(String keyword, UUID value) {
this.validate();
RustResult result = JNA.INSTANCE.entity_builder_add_uuid(this.rawPointer, keyword, getPointerForUUID(value));
if (result.isFailure()) {
Log.e("EntityBuilder", result.err);
}
}
/**
* Retracts the value of attribute `keyword` from the provided `value`.
*
* TODO throw exception if error occurs
*
* @param keyword The name of the attribute in the format `:namespace/name`.
* @param value The value to be retracted
*/
public void retract(String keyword, long value) {
this.validate();
RustResult result = JNA.INSTANCE.entity_builder_retract_long(this.rawPointer, keyword, value);
if (result.isFailure()) {
Log.e("EntityBuilder", result.err);
}
}
/**
* Retracts the value of attribute `keyword` from the provided `value`.
*
* TODO throw exception if error occurs
*
* @param keyword The name of the attribute in the format `:namespace/name`.
* @param value The value to be retracted
*/
public void retractRef(String keyword, long value) {
this.validate();
RustResult result = JNA.INSTANCE.entity_builder_retract_ref(this.rawPointer, keyword, value);
if (result.isFailure()) {
Log.e("EntityBuilder", result.err);
}
}
/**
* Retracts the value of attribute `keyword` from the provided `value`.
*
* TODO throw exception if error occurs
*
* @param keyword The name of the attribute in the format `:namespace/name`.
* @param value The value to be retracted
*/
public void retractKeyword(String keyword, String value) {
this.validate();
RustResult result = JNA.INSTANCE.entity_builder_retract_keyword(this.rawPointer, keyword, value);
if (result.isFailure()) {
Log.e("EntityBuilder", result.err);
}
}
/**
* Retracts the value of attribute `keyword` from the provided `value`.
*
* TODO throw exception if error occurs
*
* @param keyword The name of the attribute in the format `:namespace/name`.
* @param value The value to be retracted
*/
public void retract(String keyword, boolean value) {
this.validate();
RustResult result = JNA.INSTANCE.entity_builder_retract_boolean(this.rawPointer, keyword, value ? 1 : 0);
if (result.isFailure()) {
Log.e("EntityBuilder", result.err);
}
}
/**
* Retracts the value of attribute `keyword` from the provided `value`.
*
* TODO throw exception if error occurs
*
* @param keyword The name of the attribute in the format `:namespace/name`.
* @param value The value to be retracted
*/
public void retract(String keyword, double value) {
this.validate();
RustResult result = JNA.INSTANCE.entity_builder_retract_double(this.rawPointer, keyword, value);
if (result.isFailure()) {
Log.e("EntityBuilder", result.err);
}
}
/**
* Retracts the value of attribute `keyword` from the provided `value`.
*
* TODO throw exception if error occurs
*
* @param keyword The name of the attribute in the format `:namespace/name`.
* @param value The value to be retracted
*/
public void retract(String keyword, Date value) {
this.validate();
RustResult result = JNA.INSTANCE.entity_builder_retract_timestamp(this.rawPointer, keyword, value.getTime() * 1_000);
if (result.isFailure()) {
Log.e("EntityBuilder", result.err);
}
}
/**
* Retracts the value of attribute `keyword` from the provided `value`.
*
* TODO throw exception if error occurs
*
* @param keyword The name of the attribute in the format `:namespace/name`.
* @param value The value to be retracted
*/
public void retract(String keyword, String value) {
this.validate();
RustResult result = JNA.INSTANCE.entity_builder_retract_string(this.rawPointer, keyword, value);
if (result.isFailure()) {
Log.e("EntityBuilder", result.err);
}
}
/**
* Retracts the value of attribute `keyword` from the provided `value`.
*
* TODO throw exception if error occurs
*
* @param keyword The name of the attribute in the format `:namespace/name`.
* @param value The value to be retracted
*/
public void retract(String keyword, UUID value) {
this.validate();
RustResult result = JNA.INSTANCE.entity_builder_retract_uuid(this.rawPointer, keyword, this.getPointerForUUID(value));
if (result.isFailure()) {
Log.e("EntityBuilder", result.err);
}
}
/**
* Transacts the added assertions. This consumes the pointer associated with this {@link EntityBuilder}
* such that no further assertions can be added after the `transact` has completed. To perform
* further assertions, use the {@link InProgress} inside the {@link InProgressTransactionResult}
* returned from this function.
* <p/>
* This does not commit the transaction. In order to do so, `commit` can be called on the
* {@link InProgress} inside the {@link InProgressTransactionResult} returned from this function.
*
* TODO throw exception if error occurs
*
* @return A {@link InProgressTransactionResult} containing the current {@link InProgress} and
* the {@link TxReport} generated by the transact.
*/
public InProgressTransactionResult transact() {
this.validate();
InProgressTransactionResult result = JNA.INSTANCE.entity_builder_transact(this.rawPointer);
this.rawPointer = null;
return result;
}
/**
* Transacts the added assertions and commits. This consumes the pointer associated with this
* {@link EntityBuilder} and the associated {@link InProgress} such that no further assertions
* can be added after the `commit` has completed.
* <p/>
* To perform further assertions, a new `{@link InProgress} or {@link EntityBuilder} should be
* created.
*
* TODO throw exception if error occurs
*
* @return
*/
public TxReport commit() {
this.validate();
RustResult result = JNA.INSTANCE.entity_builder_commit(this.rawPointer);
this.rawPointer = null;
if (result.isFailure()) {
Log.e("EntityBuilder", result.err);
return null;
}
return new TxReport(result.ok);
}
@Override
public void close() throws IOException {
if (this.rawPointer != null) {
JNA.INSTANCE.entity_builder_destroy(this.rawPointer);
}
}
}

View file

@ -0,0 +1,195 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
* Copyright 2018 Mozilla
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
* this file except in compliance with the License. You may obtain a copy of the
* License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License. */
package com.mozilla.mentat;
import android.util.Log;
import com.sun.jna.Pointer;
import java.io.IOException;
/**
* This class wraps a raw pointer that points to a Rust {@link InProgress} object.
* </p>
* {@link InProgress} allows for multiple transacts to be performed in a single transaction.
* Each transact performed results in a {@link TxReport} that can be used to gather information
* to be used in subsequent transacts.
* </p>
* Committing an {@link InProgress} commits all the transacts that have been performed using
* that {@link InProgress}.
* </p>
* Rolling back and {@link InProgress} rolls back all the transacts that have been performed
* using that {@link InProgress}.
* </p>
* <pre>{@code
* do {
* let inProgress = try mentat.beginTransaction()
* let txReport = try inProgress.transact(transaction: "[[:db/add "a" :foo/long 22]]")
* let aEntid = txReport.entid(forTempId: "a")
* let report = try inProgress.transact(transaction: "[[:db/add "b" :foo/ref \(aEntid)] [:db/add "b" :foo/boolean true]]")
* try inProgress.commit()
* } catch {
* ...
* }
* }</pre>
* </p>
* {@link InProgress} also provides a number of functions to generating an builder to assert datoms
* programatically. The two types of builder are {@link InProgressBuilder} and {@link EntityBuilder}.
* </p>
* {@link InProgressBuilder} takes the current {@link InProgress} and provides a programmatic
* interface to add and retract values from entities for which there exists an `Entid`. The provided
* {@link InProgress} is used to perform the transacts.
* </p>
* <pre>{@code
* long aEntid = txReport.getEntidForTempId("a");
* long bEntid = txReport.getEntidForTempId("b");
* InProgress inProgress = mentat.beginTransaction();
* InProgressBuilder builder = inProgress.builder();
* builder.add(bEntid, ":foo/boolean", true);
* builder.add(aEntid, ":foo/instant", newDate);
* inProgress = builder.transact().getInProgress();
* inProgress.transact("[[:db/add \(aEntid) :foo/long 22]]");
* inProgress.commit();
* }
* }</pre>
* </p>
* {@link EntityBuilder} takes the current {@link InProgress} and either an `Entid` or a `tempid` to
* provide a programmatic interface to add and retract values from a specific entity. The provided
* {@link InProgress} is used to perform the transacts.
* </p>
* <pre>{@code
* long aEntid = txReport.getEntidForTempId("a");
* long bEntid = txReport.getEntidForTempId("b");
* InProgress inProgress = mentat.beginTransaction();
* EntityBuilder builder = inProgress.builderForEntid(bEntid);
* builder.add(":foo/boolean", true);
* builder.add(":foo/instant", newDate);
* inProgress = builder.transact().getInProgress();
* inProgress.transact("[[:db/add \(aEntid) :foo/long 22]]");
* inProgress.commit();
* }</pre>
*/
public class InProgress extends RustObject {
public InProgress(Pointer pointer) {
this.rawPointer = pointer;
}
/**
* Creates an {@link InProgressBuilder} using this {@link InProgress} .
*
* @return an {@link InProgressBuilder} for this {@link InProgress}
*/
public InProgressBuilder builder() {
this.validate();
InProgressBuilder builder = new InProgressBuilder(JNA.INSTANCE.in_progress_builder(this.rawPointer));
this.rawPointer = null;
return builder;
}
/**
* Creates an `EntityBuilder` using this `InProgress` for the entity with `entid`.
*
*
* @param entid The `Entid` for this entity.
* @return an `EntityBuilder` for this `InProgress`
*/
public EntityBuilder builderForEntid(long entid){
this.validate();
EntityBuilder builder = new EntityBuilder(JNA.INSTANCE.in_progress_entity_builder_from_entid(this.rawPointer, entid));
this.rawPointer = null;
return builder;
}
/**
* Creates an `EntityBuilder` using this `InProgress` for a new entity with `tempid`.
*
*
* @param tempid The temporary identifier for this entity.
* @return an `EntityBuilder` for this `InProgress`
*/
public EntityBuilder builderForTempid(String tempid){
this.validate();
EntityBuilder builder = new EntityBuilder(JNA.INSTANCE.in_progress_entity_builder_from_temp_id(this.rawPointer, tempid));
this.rawPointer = null;
return builder;
}
/**
Transacts the `transaction`
This does not commit the transaction. In order to do so, `commit` can be called.
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the builder
has already been transacted or committed.
- Throws: `ResultError.error` if the transaction failed.
- Throws: `ResultError.empty` if no `TxReport` is returned from the transact.
- Returns: The `TxReport` generated by the transact.
*/
/**
* Transacts the `transaction`
*
* This does not commit the transaction. In order to do so, `commit` can be called.
*
* TODO throw Exception on result failure.
*
* @param transaction The EDN string to be transacted.
* @return The `TxReport` generated by the transact.
*/
public TxReport transact(String transaction) {
this.validate();
RustResult result = JNA.INSTANCE.in_progress_transact(this.rawPointer, transaction);
if (result.isFailure()) {
Log.e("InProgress", result.err);
return null;
}
return new TxReport(result.ok);
}
/**
* Commits all the transacts that have been performed on this `InProgress`, either directly
* or through a Builder. This consumes the pointer associated with this {@link InProgress} such
* that no further assertions can be performed after the `commit` has completed.
*
* TODO throw exception if error occurs
*/
public void commit() {
this.validate();
RustResult result = JNA.INSTANCE.in_progress_commit(this.rawPointer);
this.rawPointer = null;
if (result.isFailure()) {
Log.e("InProgressBuilder", result.err);
}
}
/**
* Rolls back all the transacts that have been performed on this `InProgress`, either directly
* or through a Builder.
*
* TODO throw exception if error occurs
*/
public void rollback() {
this.validate();
RustResult result = JNA.INSTANCE.in_progress_rollback(this.rawPointer);
this.rawPointer = null;
if (result.isFailure()) {
Log.e("InProgressBuilder", result.err);
}
}
@Override
public void close() throws IOException {
if (this.rawPointer != null) {
JNA.INSTANCE.in_progress_destroy(this.rawPointer);
}
}
}

View file

@ -0,0 +1,382 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
* Copyright 2018 Mozilla
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
* this file except in compliance with the License. You may obtain a copy of the
* License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License. */
package com.mozilla.mentat;
import android.util.Log;
import com.sun.jna.Pointer;
import java.io.IOException;
import java.util.Date;
import java.util.UUID;
/**
* This class wraps a raw pointer that points to a Rust `InProgressBuilder` object.
*
* {@link InProgressBuilder} provides a programmatic interface to performing assertions for entities.
* It provides functions for adding and retracting values for attributes for an entity within
* an in progress transaction.
* <p/>
* The `transact` function will transact the assertions that have been added to the {@link InProgressBuilder}
* and pass back the {@link TxReport} that was generated by this transact and the {@link InProgress} that was
* used to perform the transact. This enables you to perform further transacts on the same {@link InProgress}
* before committing.
* <p/>
* <pre>{@code
* long aEntid = txReport.getEntidForTempId("a");
* long bEntid = txReport.getEntidForTempId("b");
* InProgressBuilder builder = mentat.getEntityBuilder();
* builder.add(aEntid, ":foo/boolean", true);
* builder.add(bEntid, ":foo/instant", newDate);
* InProgress inProgress = builder.transact().getInProgress();
* inProgress.transact("[[:db/add \(aEntid) :foo/long 22]]");
* inProgress.commit();
* }</pre>
* <p/>
* The `commit` function will transact and commit the assertions that have been added to the
* {@link InProgressBuilder}.
* It will consume the {@link InProgress} used to perform the transact. It returns the {@link TxReport}
* generated by the transact. After calling `commit`, a new transaction must be started by calling
* <pre>Mentat.beginTransaction()</pre> in order to perform further actions.
* <p/>
* <pre>{@code
* long aEntid = txReport.getEntidForTempId("a");
* long bEntid = txReport.getEntidForTempId("b");
* InProgressBuilder builder = mentat.getEntityBuilder();
* builder.add(aEntid, ":foo/boolean", true);
* builder.add(bEntid, ":foo/instant", newDate);
* builder.commit();
* }</pre>
*/
public class InProgressBuilder extends RustObject {
public InProgressBuilder(Pointer pointer) {
this.rawPointer = pointer;
}
/**
* Asserts the value of attribute `keyword` to be the provided `value`.
*
* // TODO throw exception if error occurs
*
* @param entid The `Entid` of the entity to be touched.
* @param keyword The name of the attribute in the format `:namespace/name`.
* @param value The value to be asserted
*/
public void add(long entid, String keyword, long value) {
this.validate();
RustResult result = JNA.INSTANCE.in_progress_builder_add_long(this.rawPointer, entid, keyword, value);
if (result.isFailure()) {
Log.e("InProgressBuilder", result.err);
}
}
/**
* Asserts the value of attribute `keyword` to be the provided `value`.
* // TODO throw exception if error occurs
*
* @param entid The `Entid` of the entity to be touched.
* @param keyword The name of the attribute in the format `:namespace/name`.
* @param value The value to be asserted
*/
public void addRef(long entid, String keyword, long value) {
this.validate();
RustResult result = JNA.INSTANCE.in_progress_builder_add_ref(this.rawPointer, entid, keyword, value);
if (result.isFailure()) {
Log.e("InProgressBuilder", result.err);
}
}
/**
* Asserts the value of attribute `keyword` to be the provided `value`.
* // TODO throw exception if error occurs
*
* @param entid The `Entid` of the entity to be touched.
* @param keyword The name of the attribute in the format `:namespace/name`.
* @param value The value to be asserted
*/
public void addKeyword(long entid, String keyword, String value) {
this.validate();
RustResult result = JNA.INSTANCE.in_progress_builder_add_keyword(this.rawPointer, entid, keyword, value);
if (result.isFailure()) {
Log.e("InProgressBuilder", result.err);
}
}
/**
* Asserts the value of attribute `keyword` to be the provided `value`.
* // TODO throw exception if error occurs
*
* @param entid The `Entid` of the entity to be touched.
* @param keyword The name of the attribute in the format `:namespace/name`.
* @param value The value to be asserted
*/
public void add(long entid, String keyword, boolean value) {
this.validate();
RustResult result = JNA.INSTANCE.in_progress_builder_add_boolean(this.rawPointer, entid, keyword, value ? 1 : 0);
if (result.isFailure()) {
Log.e("InProgressBuilder", result.err);
}
}
/**
* Asserts the value of attribute `keyword` to be the provided `value`.
* // TODO throw exception if error occurs
*
* @param entid The `Entid` of the entity to be touched.
* @param keyword The name of the attribute in the format `:namespace/name`.
* @param value The value to be asserted
*/
public void add(long entid, String keyword, double value) {
this.validate();
RustResult result = JNA.INSTANCE.in_progress_builder_add_double(this.rawPointer, entid, keyword, value);
if (result.isFailure()) {
Log.e("InProgressBuilder", result.err);
}
}
/**
* Asserts the value of attribute `keyword` to be the provided `value`.
* // TODO throw exception if error occurs
*
* @param entid The `Entid` of the entity to be touched.
* @param keyword The name of the attribute in the format `:namespace/name`.
* @param value The value to be asserted
*/
public void add(long entid, String keyword, Date value) {
this.validate();
RustResult result = JNA.INSTANCE.in_progress_builder_add_timestamp(this.rawPointer, entid, keyword, value.getTime() * 1_000);
if (result.isFailure()) {
Log.e("InProgressBuilder", result.err);
}
}
/**
* Asserts the value of attribute `keyword` to be the provided `value`.
* // TODO throw exception if error occurs
*
* @param entid The `Entid` of the entity to be touched.
* @param keyword The name of the attribute in the format `:namespace/name`.
* @param value The value to be asserted
*/
public void add(long entid, String keyword, String value) {
this.validate();
RustResult result = JNA.INSTANCE.in_progress_builder_add_string(this.rawPointer, entid, keyword, value);
if (result.isFailure()) {
Log.e("InProgressBuilder", result.err);
}
}
/**
* Asserts the value of attribute `keyword` to be the provided `value`.
* // TODO throw exception if error occurs
*
* @param entid The `Entid` of the entity to be touched.
* @param keyword The name of the attribute in the format `:namespace/name`.
* @param value The value to be asserted
*/
public void add(long entid, String keyword, UUID value) {
this.validate();
RustResult result = JNA.INSTANCE.in_progress_builder_add_uuid(this.rawPointer, entid, keyword, getPointerForUUID(value));
if (result.isFailure()) {
Log.e("InProgressBuilder", result.err);
}
}
/**
* Retracts the value of attribute `keyword` from the provided `value`.
*
* TODO throw exception if error occurs
*
* @param entid The `Entid` of the entity to be touched.
* @param keyword The name of the attribute in the format `:namespace/name`.
* @param value The value to be retracted
*/
public void retract(long entid, String keyword, long value) {
this.validate();
RustResult result = JNA.INSTANCE.in_progress_builder_retract_long(this.rawPointer, entid, keyword, value);
if (result.isFailure()) {
Log.e("InProgressBuilder", result.err);
}
}
/**
* Retracts the value of attribute `keyword` from the provided `value`.
*
* TODO throw exception if error occurs
*
* @param entid The `Entid` of the entity to be touched.
* @param keyword The name of the attribute in the format `:namespace/name`.
* @param value The value to be retracted
*/
public void retractRef(long entid, String keyword, long value) {
this.validate();
RustResult result = JNA.INSTANCE.in_progress_builder_retract_ref(this.rawPointer, entid, keyword, value);
if (result.isFailure()) {
Log.e("InProgressBuilder", result.err);
}
}
/**
* Retracts the value of attribute `keyword` from the provided `value`.
*
* TODO throw exception if error occurs
*
* @param entid The `Entid` of the entity to be touched.
* @param keyword The name of the attribute in the format `:namespace/name`.
* @param value The value to be retracted
*/
public void retractKeyword(long entid, String keyword, String value) {
this.validate();
RustResult result = JNA.INSTANCE.in_progress_builder_retract_keyword(this.rawPointer, entid, keyword, value);
if (result.isFailure()) {
Log.e("InProgressBuilder", result.err);
}
}
/**
* Retracts the value of attribute `keyword` from the provided `value`.
*
* TODO throw exception if error occurs
*
* @param entid The `Entid` of the entity to be touched.
* @param keyword The name of the attribute in the format `:namespace/name`.
* @param value The value to be retracted
*/
public void retract(long entid, String keyword, boolean value) {
this.validate();
RustResult result = JNA.INSTANCE.in_progress_builder_retract_boolean(this.rawPointer, entid, keyword, value ? 1 : 0);
if (result.isFailure()) {
Log.e("InProgressBuilder", result.err);
}
}
/**
* Retracts the value of attribute `keyword` from the provided `value`.
*
* TODO throw exception if error occurs
*
* @param entid The `Entid` of the entity to be touched.
* @param keyword The name of the attribute in the format `:namespace/name`.
* @param value The value to be retracted
*/
public void retract(long entid, String keyword, double value) {
this.validate();
RustResult result = JNA.INSTANCE.in_progress_builder_retract_double(this.rawPointer, entid, keyword, value);
if (result.isFailure()) {
Log.e("InProgressBuilder", result.err);
}
}
/**
* Retracts the value of attribute `keyword` from the provided `value`.
*
* TODO throw exception if error occurs
*
* @param entid The `Entid` of the entity to be touched.
* @param keyword The name of the attribute in the format `:namespace/name`.
* @param value The value to be retracted
*/
public void retract(long entid, String keyword, Date value) {
this.validate();
RustResult result = JNA.INSTANCE.in_progress_builder_retract_timestamp(this.rawPointer, entid, keyword, value.getTime() * 1_000);
if (result.isFailure()) {
Log.e("InProgressBuilder", result.err);
}
}
/**
* Retracts the value of attribute `keyword` from the provided `value`.
*
* TODO throw exception if error occurs
*
* @param entid The `Entid` of the entity to be touched.
* @param keyword The name of the attribute in the format `:namespace/name`.
* @param value The value to be retracted
*/
public void retract(long entid, String keyword, String value) {
this.validate();
RustResult result = JNA.INSTANCE.in_progress_builder_retract_string(this.rawPointer, entid, keyword, value);
if (result.isFailure()) {
Log.e("InProgressBuilder", result.err);
}
}
/**
* Retracts the value of attribute `keyword` from the provided `value`.
*
* TODO throw exception if error occurs
*
* @param entid The `Entid` of the entity to be touched.
* @param keyword The name of the attribute in the format `:namespace/name`.
* @param value The value to be retracted
*/
public void retract(long entid, String keyword, UUID value) {
this.validate();
RustResult result = JNA.INSTANCE.in_progress_builder_retract_uuid(this.rawPointer, entid, keyword, this.getPointerForUUID(value));
if (result.isFailure()) {
Log.e("InProgressBuilder", result.err);
}
}
/**
* Transacts the added assertions. This consumes the pointer associated with this {@link InProgressBuilder}
* such that no further assertions can be added after the `transact` has completed. To perform
* further assertions, use the {@link InProgress} inside the {@link InProgressTransactionResult}
* returned from this function.
* <p/>
* This does not commit the transaction. In order to do so, `commit` can be called on the
* {@link InProgress} inside the {@link InProgressTransactionResult} returned from this function.
*
* TODO throw exception if error occurs
*
* @return A {@link InProgressTransactionResult} containing the current {@link InProgress} and
* the {@link TxReport} generated by the transact.
*/
public InProgressTransactionResult transact() {
this.validate();
InProgressTransactionResult result = JNA.INSTANCE.in_progress_builder_transact(this.rawPointer);
this.rawPointer = null;
return result;
}
/**
* Transacts the added assertions and commits. This consumes the pointer associated with this
* {@link InProgressBuilder} and the associated {@link InProgress} such that no further assertions
* can be added after the `commit` has completed.
* <p/>
* To perform further assertions, a new `{@link InProgress} or {@link InProgressBuilder} should be
* created.
*
* TODO throw exception if error occurs
*
* @return
*/
public TxReport commit() {
this.validate();
RustResult result = JNA.INSTANCE.in_progress_builder_commit(this.rawPointer);
this.rawPointer = null;
if (result.isFailure()) {
Log.e("InProgressBuilder", result.err);
return null;
}
return new TxReport(result.ok);
}
@Override
public void close() throws IOException {
if (this.rawPointer != null) {
JNA.INSTANCE.in_progress_builder_destroy(this.rawPointer);
}
}
}

View file

@ -0,0 +1,56 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
* Copyright 2018 Mozilla
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
* this file except in compliance with the License. You may obtain a copy of the
* License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License. */
package com.mozilla.mentat;
import android.util.Log;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import java.io.Closeable;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
public class InProgressTransactionResult extends Structure implements Closeable {
public static class ByReference extends InProgressTransactionResult implements Structure.ByReference {
}
public static class ByValue extends InProgressTransactionResult implements Structure.ByValue {
}
public Pointer inProgress;
public RustResult.ByReference result;
@Override
protected List<String> getFieldOrder() {
return Arrays.asList("inProgress", "result");
}
public InProgress getInProgress() {
return new InProgress(this.inProgress);
}
public TxReport getReport() {
if (this.result.isFailure()) {
Log.e("InProgressTransactionResult", this.result.err);
return null;
}
return new TxReport(this.result.ok);
}
@Override
public void close() throws IOException {
if (this.getPointer() != null) {
JNA.INSTANCE.destroy(this.getPointer());
}
}
}

View file

@ -37,12 +37,67 @@ public interface JNA extends Library {
void typed_value_result_set_destroy(Pointer obj);
void typed_value_result_set_iter_destroy(Pointer obj);
void tx_report_destroy(Pointer obj);
void in_progress_destroy(Pointer obj);
void in_progress_builder_destroy(Pointer obj);
void entity_builder_destroy(Pointer obj);
// transact
RustResult store_transact(Pointer store, String transaction);
Pointer tx_report_entity_for_temp_id(Pointer report, String tempid);
long tx_report_get_entid(Pointer report);
long tx_report_get_tx_instant(Pointer report);
RustResult store_begin_transaction(Pointer store);
// in progress
RustResult in_progress_transact(Pointer in_progress, String transaction);
RustResult in_progress_commit(Pointer in_progress);
RustResult in_progress_rollback(Pointer in_progress);
Pointer in_progress_builder(Pointer in_progress);
Pointer in_progress_entity_builder_from_temp_id(Pointer in_progress, String temp_id);
Pointer in_progress_entity_builder_from_entid(Pointer in_progress, long entid);
// in_progress entity building
RustResult store_in_progress_builder(Pointer store);
RustResult in_progress_builder_add_string(Pointer builder, long entid, String kw, String value);
RustResult in_progress_builder_add_long(Pointer builder, long entid, String kw, long value);
RustResult in_progress_builder_add_ref(Pointer builder, long entid, String kw, long value);
RustResult in_progress_builder_add_keyword(Pointer builder, long entid, String kw, String value);
RustResult in_progress_builder_add_timestamp(Pointer builder, long entid, String kw, long value);
RustResult in_progress_builder_add_boolean(Pointer builder, long entid, String kw, int value);
RustResult in_progress_builder_add_double(Pointer builder, long entid, String kw, double value);
RustResult in_progress_builder_add_uuid(Pointer builder, long entid, String kw, Pointer value);
RustResult in_progress_builder_retract_string(Pointer builder, long entid, String kw, String value);
RustResult in_progress_builder_retract_long(Pointer builder, long entid, String kw, long value);
RustResult in_progress_builder_retract_ref(Pointer builder, long entid, String kw, long value);
RustResult in_progress_builder_retract_keyword(Pointer builder, long entid, String kw, String value);
RustResult in_progress_builder_retract_timestamp(Pointer builder, long entid, String kw, long value);
RustResult in_progress_builder_retract_boolean(Pointer builder, long entid, String kw, int value);
RustResult in_progress_builder_retract_double(Pointer builder, long entid, String kw, double value);
RustResult in_progress_builder_retract_uuid(Pointer builder, long entid, String kw, Pointer value);
InProgressTransactionResult in_progress_builder_transact(Pointer builder);
RustResult in_progress_builder_commit(Pointer builder);
// entity building
RustResult store_entity_builder_from_temp_id(Pointer store, String temp_id);
RustResult store_entity_builder_from_entid(Pointer store, long entid);
RustResult entity_builder_add_string(Pointer builder, String kw, String value);
RustResult entity_builder_add_long(Pointer builder, String kw, long value);
RustResult entity_builder_add_ref(Pointer builder, String kw, long value);
RustResult entity_builder_add_keyword(Pointer builder, String kw, String value);
RustResult entity_builder_add_boolean(Pointer builder, String kw, int value);
RustResult entity_builder_add_double(Pointer builder, String kw, double value);
RustResult entity_builder_add_timestamp(Pointer builder, String kw, long value);
RustResult entity_builder_add_uuid(Pointer builder, String kw, Pointer value);
RustResult entity_builder_retract_string(Pointer builder, String kw, String value);
RustResult entity_builder_retract_long(Pointer builder, String kw, long value);
RustResult entity_builder_retract_ref(Pointer builder, String kw, long value);
RustResult entity_builder_retract_keyword(Pointer builder, String kw, String value);
RustResult entity_builder_retract_boolean(Pointer builder, String kw, int value);
RustResult entity_builder_retract_double(Pointer builder, String kw, double value);
RustResult entity_builder_retract_timestamp(Pointer builder, String kw, long value);
RustResult entity_builder_retract_uuid(Pointer builder, String kw, Pointer value);
InProgressTransactionResult entity_builder_transact(Pointer builder);
RustResult entity_builder_commit(Pointer builder);
// sync
RustResult store_sync(Pointer store, String userUuid, String serverUri);
@ -95,7 +150,7 @@ public interface JNA extends Library {
String value_at_index_into_kw(Pointer rows, int index);
String value_at_index_into_string(Pointer rows, int index);
Pointer value_at_index_into_uuid(Pointer rows, int index);
long value_at_index_into_boolean(Pointer rows, int index);
int value_at_index_into_boolean(Pointer rows, int index);
double value_at_index_into_double(Pointer rows, int index);
long value_at_index_into_timestamp(Pointer rows, int index);
}

View file

@ -138,6 +138,59 @@ public class Mentat extends RustObject {
JNA.INSTANCE.store_unregister_observer(rawPointer, key);
}
public InProgress beginTransaction() {
RustResult result = JNA.INSTANCE.store_begin_transaction(this.rawPointer);
if (result.isSuccess()) {
return new InProgress(result.ok);
}
if (result.isFailure()) {
Log.i("Mentat", result.err);
}
return null;
}
public InProgressBuilder entityBuilder() {
RustResult result = JNA.INSTANCE.store_in_progress_builder(this.rawPointer);
if (result.isSuccess()) {
return new InProgressBuilder(result.ok);
}
if (result.isFailure()) {
Log.i("Mentat", result.err);
}
return null;
}
public EntityBuilder entityBuilder(long entid) {
RustResult result = JNA.INSTANCE.store_entity_builder_from_entid(this.rawPointer, entid);
if (result.isSuccess()) {
return new EntityBuilder(result.ok);
}
if (result.isFailure()) {
Log.i("Mentat", result.err);
}
return null;
}
public EntityBuilder entityBuilder(String tempId) {
RustResult result = JNA.INSTANCE.store_entity_builder_from_temp_id(this.rawPointer, tempId);
if (result.isSuccess()) {
return new EntityBuilder(result.ok);
}
if (result.isFailure()) {
Log.i("Mentat", result.err);
}
return null;
}
@Override
public void close() {
if (this.rawPointer != null) {

View file

@ -76,7 +76,7 @@ import java.util.UUID;
* <pre>{@code
* String query = "[: find ?a .\n" +
* " : where ... ]";
* mentat.query(query).runScalar(new ScalarResultHandler() {
* mentat.query(query).run(new ScalarResultHandler() {
* @Override
* public void handleValue(TypedValue value) {
* ...
@ -88,7 +88,7 @@ import java.util.UUID;
* <pre>{@code
* String query = "[: find [?a ...]\n" +
* " : where ... ]";
* mentat.query(query).runColl(new ScalarResultHandler() {
* mentat.query(query).run(new ScalarResultHandler() {
* @Override
* public void handleList(CollResult list) {
* ...
@ -100,7 +100,7 @@ import java.util.UUID;
* <pre>{@code
* String query = "[: find [?a ?b ?c]\n" +
* " : where ... ]";
* mentat.query(query).runTuple(new TupleResultHandler() {
* mentat.query(query).run(new TupleResultHandler() {
* @Override
* public void handleRow(TupleResult row) {
* ...
@ -121,7 +121,7 @@ public class Query extends RustObject {
* @param value The value to be bound
* @return This {@link Query} such that further function can be called.
*/
Query bindLong(String varName, long value) {
Query bind(String varName, long value) {
this.validate();
JNA.INSTANCE.query_builder_bind_long(this.rawPointer, varName, value);
return this;
@ -173,7 +173,7 @@ public class Query extends RustObject {
* @param value The value to be bound
* @return This {@link Query} such that further function can be called.
*/
Query bindBoolean(String varName, boolean value) {
Query bind(String varName, boolean value) {
this.validate();
JNA.INSTANCE.query_builder_bind_boolean(this.rawPointer, varName, value ? 1 : 0);
return this;
@ -186,7 +186,7 @@ public class Query extends RustObject {
* @param value The value to be bound
* @return This {@link Query} such that further function can be called.
*/
Query bindDouble(String varName, double value) {
Query bind(String varName, double value) {
this.validate();
JNA.INSTANCE.query_builder_bind_double(this.rawPointer, varName, value);
return this;
@ -199,7 +199,7 @@ public class Query extends RustObject {
* @param value The value to be bound
* @return This {@link Query} such that further function can be called.
*/
Query bindDate(String varName, Date value) {
Query bind(String varName, Date value) {
this.validate();
long timestamp = value.getTime() * 1000;
JNA.INSTANCE.query_builder_bind_timestamp(this.rawPointer, varName, timestamp);
@ -213,7 +213,7 @@ public class Query extends RustObject {
* @param value The value to be bound
* @return This {@link Query} such that further function can be called.
*/
Query bindString(String varName, String value) {
Query bind(String varName, String value) {
this.validate();
JNA.INSTANCE.query_builder_bind_string(this.rawPointer, varName, value);
return this;
@ -226,15 +226,9 @@ public class Query extends RustObject {
* @param value The value to be bound
* @return This {@link Query} such that further function can be called.
*/
Query bindUUID(String varName, UUID value) {
Query bind(String varName, UUID value) {
this.validate();
ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
bb.putLong(value.getMostSignificantBits());
bb.putLong(value.getLeastSignificantBits());
byte[] bytes = bb.array();
final Pointer bytesNativeArray = new Memory(bytes.length);
bytesNativeArray.write(0, bytes, 0, bytes.length);
JNA.INSTANCE.query_builder_bind_uuid(this.rawPointer, varName, bytesNativeArray);
JNA.INSTANCE.query_builder_bind_uuid(this.rawPointer, varName, getPointerForUUID(value));
return this;
}
@ -262,7 +256,7 @@ public class Query extends RustObject {
* TODO: Throw an exception if the query raw pointer has been consumed or the query fails to execute
* @param handler the handler to call with the results of this query
*/
void runScalar(final ScalarResultHandler handler) {
void run(final ScalarResultHandler handler) {
this.validate();
RustResult result = JNA.INSTANCE.query_builder_execute_scalar(rawPointer);
rawPointer = null;
@ -285,7 +279,7 @@ public class Query extends RustObject {
* TODO: Throw an exception if the query raw pointer has been consumed or the query fails to execute
* @param handler the handler to call with the results of this query
*/
void runColl(final CollResultHandler handler) {
void run(final CollResultHandler handler) {
this.validate();
RustResult result = JNA.INSTANCE.query_builder_execute_coll(rawPointer);
rawPointer = null;
@ -303,7 +297,7 @@ public class Query extends RustObject {
* TODO: Throw an exception if the query raw pointer has been consumed or the query fails to execute
* @param handler the handler to call with the results of this query
*/
void runTuple(final TupleResultHandler handler) {
void run(final TupleResultHandler handler) {
this.validate();
RustResult result = JNA.INSTANCE.query_builder_execute_tuple(rawPointer);
rawPointer = null;

View file

@ -10,9 +10,12 @@
package com.mozilla.mentat;
import com.sun.jna.Memory;
import com.sun.jna.Pointer;
import java.io.Closeable;
import java.nio.ByteBuffer;
import java.util.UUID;
/**
* Base class that wraps an non-optional {@link Pointer} representing a pointer to a Rust object.
@ -31,4 +34,22 @@ abstract class RustObject implements Closeable {
throw new NullPointerException(this.getClass() + " consumed");
}
}
public Pointer getPointerForUUID(UUID uuid) {
ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
bb.putLong(uuid.getMostSignificantBits());
bb.putLong(uuid.getLeastSignificantBits());
byte[] bytes = bb.array();
final Pointer bytesNativeArray = new Memory(bytes.length);
bytesNativeArray.write(0, bytes, 0, bytes.length);
return bytesNativeArray;
}
public UUID getUUIDFromPointer(Pointer uuidPtr) {
byte[] bytes = uuidPtr.getByteArray(0, 16);
ByteBuffer bb = ByteBuffer.wrap(bytes);
long high = bb.getLong();
long low = bb.getLong();
return new UUID(high, low);
}
}

View file

@ -126,7 +126,7 @@ public class TupleResult extends RustObject {
*/
public Date asDate(Integer index) {
this.validate();
return new Date(JNA.INSTANCE.value_at_index_into_timestamp(this.rawPointer, index));
return new Date(JNA.INSTANCE.value_at_index_into_timestamp(this.rawPointer, index) * 1_000);
}
/**
@ -150,13 +150,7 @@ public class TupleResult extends RustObject {
*/
public UUID asUUID(Integer index) {
this.validate();
Pointer uuidPtr = JNA.INSTANCE.value_at_index_into_uuid(this.rawPointer, index);
byte[] bytes = uuidPtr.getByteArray(0, 16);
ByteBuffer bb = ByteBuffer.wrap(bytes);
long high = bb.getLong();
long low = bb.getLong();
return new UUID(high, low);
return getUUIDFromPointer(JNA.INSTANCE.value_at_index_into_uuid(this.rawPointer, index));
}
@Override

View file

@ -137,12 +137,7 @@ public class TypedValue extends RustObject {
*/
public UUID asUUID() {
if (!this.isConsumed()) {
Pointer uuidPtr = JNA.INSTANCE.typed_value_into_uuid(this.rawPointer);
byte[] bytes = uuidPtr.getByteArray(0, 16);
ByteBuffer bb = ByteBuffer.wrap(bytes);
long high = bb.getLong();
long low = bb.getLong();
this.value = new UUID(high, low);
this.value = getUUIDFromPointer(JNA.INSTANCE.typed_value_into_uuid(this.rawPointer));
this.rawPointer = null;
}
return (UUID)this.value;