diff --git a/sdks/android/Mentat/library/src/androidTest/java/com/mozilla/mentat/FFIIntegrationTest.java b/sdks/android/Mentat/library/src/androidTest/java/com/mozilla/mentat/FFIIntegrationTest.java
index dfe8b515..7561455a 100644
--- a/sdks/android/Mentat/library/src/androidTest/java/com/mozilla/mentat/FFIIntegrationTest.java
+++ b/sdks/android/Mentat/library/src/androidTest/java/com/mozilla/mentat/FFIIntegrationTest.java
@@ -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);
+ }
+ }
}
diff --git a/sdks/android/Mentat/library/src/main/java/com/mozilla/mentat/EntityBuilder.java b/sdks/android/Mentat/library/src/main/java/com/mozilla/mentat/EntityBuilder.java
new file mode 100644
index 00000000..e2f067bd
--- /dev/null
+++ b/sdks/android/Mentat/library/src/main/java/com/mozilla/mentat/EntityBuilder.java
@@ -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{@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();
+ * }
+ *
+ * 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 Mentat.beginTransaction()
+ * in order to perform further actions.
+ *
+ * {@code
+ * long aEntid = txReport.getEntidForTempId("a");
+ * EntityBuilder builder = mentat.getEntityBuilderForEntid(bEntid);
+ * builder.add(":foo/boolean", true);
+ * builder.add(":foo/instant", newDate);
+ * builder.commit();
+ * }
+ */
+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.
+ *
+ * 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.
+ *
+ * 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);
+ }
+ }
+}
diff --git a/sdks/android/Mentat/library/src/main/java/com/mozilla/mentat/InProgress.java b/sdks/android/Mentat/library/src/main/java/com/mozilla/mentat/InProgress.java
new file mode 100644
index 00000000..0af3fba8
--- /dev/null
+++ b/sdks/android/Mentat/library/src/main/java/com/mozilla/mentat/InProgress.java
@@ -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.
+ *
{@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 { + * ... + * } + * }+ * + * {@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}. + * + * {@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. + * + *
{@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(); + * } + * }+ * + * {@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. + * + *
{@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(); + * }+ */ +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); + } + } +} diff --git a/sdks/android/Mentat/library/src/main/java/com/mozilla/mentat/InProgressBuilder.java b/sdks/android/Mentat/library/src/main/java/com/mozilla/mentat/InProgressBuilder.java new file mode 100644 index 00000000..81072fb2 --- /dev/null +++ b/sdks/android/Mentat/library/src/main/java/com/mozilla/mentat/InProgressBuilder.java @@ -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. + * + * 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. + * + *
{@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(); + * }+ * + * 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 + *
Mentat.beginTransaction()in order to perform further actions. + * + *
{@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(); + * }+ */ +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. + * + * 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. + * + * 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); + } + } +} diff --git a/sdks/android/Mentat/library/src/main/java/com/mozilla/mentat/InProgressTransactionResult.java b/sdks/android/Mentat/library/src/main/java/com/mozilla/mentat/InProgressTransactionResult.java new file mode 100644 index 00000000..c7b39c8a --- /dev/null +++ b/sdks/android/Mentat/library/src/main/java/com/mozilla/mentat/InProgressTransactionResult.java @@ -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
{@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; *{@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; *{@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; diff --git a/sdks/android/Mentat/library/src/main/java/com/mozilla/mentat/RustObject.java b/sdks/android/Mentat/library/src/main/java/com/mozilla/mentat/RustObject.java index 35f7b8fb..e55c4e5f 100644 --- a/sdks/android/Mentat/library/src/main/java/com/mozilla/mentat/RustObject.java +++ b/sdks/android/Mentat/library/src/main/java/com/mozilla/mentat/RustObject.java @@ -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); + } } diff --git a/sdks/android/Mentat/library/src/main/java/com/mozilla/mentat/TupleResult.java b/sdks/android/Mentat/library/src/main/java/com/mozilla/mentat/TupleResult.java index a9701682..d1b4c327 100644 --- a/sdks/android/Mentat/library/src/main/java/com/mozilla/mentat/TupleResult.java +++ b/sdks/android/Mentat/library/src/main/java/com/mozilla/mentat/TupleResult.java @@ -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 diff --git a/sdks/android/Mentat/library/src/main/java/com/mozilla/mentat/TypedValue.java b/sdks/android/Mentat/library/src/main/java/com/mozilla/mentat/TypedValue.java index 943e9753..3c89a5df 100644 --- a/sdks/android/Mentat/library/src/main/java/com/mozilla/mentat/TypedValue.java +++ b/sdks/android/Mentat/library/src/main/java/com/mozilla/mentat/TypedValue.java @@ -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; diff --git a/sdks/android/Mentat/library/src/main/jniLibs/x86/libmentat_ffi.so b/sdks/android/Mentat/library/src/main/jniLibs/x86/libmentat_ffi.so index 1160a11a..6edd9a18 100755 Binary files a/sdks/android/Mentat/library/src/main/jniLibs/x86/libmentat_ffi.so and b/sdks/android/Mentat/library/src/main/jniLibs/x86/libmentat_ffi.so differ