Add wrapper classes and documentation for FFI functions
This commit is contained in:
parent
b83bec566a
commit
08387d4754
20 changed files with 1641 additions and 0 deletions
|
@ -0,0 +1,51 @@
|
|||
/* -*- 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.Structure;
|
||||
import com.sun.jna.ptr.IntByReference;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents a C struct of a list of Strings containing attributes in the format
|
||||
* `:namespace/name`.
|
||||
*/
|
||||
public class AttributeList extends Structure implements Closeable {
|
||||
public static class ByReference extends AttributeList implements Structure.ByReference {
|
||||
}
|
||||
|
||||
public static class ByValue extends AttributeList implements Structure.ByValue {
|
||||
}
|
||||
|
||||
public IntByReference attributes;
|
||||
public int numberOfItems;
|
||||
// Used by the Swift counterpart, JNA does this for us automagically.
|
||||
// But we still need it here so that the number of fields and their order is correct
|
||||
public int len;
|
||||
|
||||
@Override
|
||||
protected List<String> getFieldOrder() {
|
||||
return Arrays.asList("attributes", "numberOfItems", "len");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
Log.i("AttributeList", "close");
|
||||
|
||||
if (this.getPointer() != null) {
|
||||
JNA.INSTANCE.destroy(this.getPointer());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
/* -*- 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.Iterator;
|
||||
|
||||
/**
|
||||
* Iterator for a {@link CollResult}
|
||||
*/
|
||||
public class ColResultIterator extends RustObject implements Iterator {
|
||||
|
||||
Pointer nextPointer;
|
||||
|
||||
ColResultIterator(Pointer iterator) {
|
||||
this.rawPointer = iterator;
|
||||
}
|
||||
|
||||
private Pointer getNextPointer() {
|
||||
return JNA.INSTANCE.values_iter_next(this.rawPointer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
this.nextPointer = getNextPointer();
|
||||
return this.nextPointer != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypedValue next() {
|
||||
Pointer next = this.nextPointer == null ? getNextPointer() : this.nextPointer;
|
||||
if (next == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new TypedValue(next);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
Log.i("TupleResult", "close");
|
||||
if (this.rawPointer != null) {
|
||||
JNA.INSTANCE.typed_value_list_iter_destroy(this.rawPointer);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
/* -*- 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.util.Date;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Wraps a `Coll` result from a Mentat query.
|
||||
* A `Coll` result is a list of rows of single values of type {@link TypedValue}.
|
||||
* Values for individual rows can be fetched as {@link TypedValue} or converted into a requested type.
|
||||
* <p>
|
||||
* Row values can be fetched as one of the following types:
|
||||
* <ul>
|
||||
* <li>{@link TypedValue}</li>
|
||||
* <li>long</li>
|
||||
* <li>Entid (as long)</li>
|
||||
* <li>Keyword (as String)</li>
|
||||
* <li>boolean</li>
|
||||
* <li>double</li>
|
||||
* <li>{@link Date}</li>
|
||||
* <li>{@link String}</li>
|
||||
* <li>{@link UUID}</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* To iterate over the result set use standard iteration flows.
|
||||
*/
|
||||
public class CollResult extends TupleResult implements Iterable<TypedValue> {
|
||||
|
||||
public CollResult(Pointer pointer) {
|
||||
super(pointer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
Log.i("CollResult", "close");
|
||||
|
||||
if (this.rawPointer != null) {
|
||||
JNA.INSTANCE.destroy(this.rawPointer);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ColResultIterator iterator() {
|
||||
Pointer iterPointer = JNA.INSTANCE.values_iter(this.rawPointer);
|
||||
this.rawPointer = null;
|
||||
if (iterPointer == null) {
|
||||
return null;
|
||||
}
|
||||
return new ColResultIterator(iterPointer);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
/* -*- 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;
|
||||
|
||||
/**
|
||||
* Interface defining the structure of a callback from a query returning a {@link CollResult}.
|
||||
*/
|
||||
public interface CollResultHandler {
|
||||
void handleList(CollResult list);
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
/* -*- 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 com.sun.jna.Library;
|
||||
import com.sun.jna.Native;
|
||||
import com.sun.jna.NativeLibrary;
|
||||
import com.sun.jna.Pointer;
|
||||
|
||||
/**
|
||||
* JNA interface for FFI to Mentat's Rust library
|
||||
* Each function definition here link directly to a function in Mentat's FFI crate.
|
||||
* Signatures must match for the linking to work correctly.
|
||||
*/
|
||||
public interface JNA extends Library {
|
||||
String JNA_LIBRARY_NAME = "mentat_ffi";
|
||||
NativeLibrary JNA_NATIVE_LIB = NativeLibrary.getInstance(JNA_LIBRARY_NAME);
|
||||
|
||||
JNA INSTANCE = (JNA) Native.loadLibrary(JNA_LIBRARY_NAME, JNA.class);
|
||||
|
||||
Pointer store_open(String dbPath);
|
||||
|
||||
void destroy(Pointer obj);
|
||||
void query_builder_destroy(Pointer obj);
|
||||
void store_destroy(Pointer obj);
|
||||
void typed_value_destroy(Pointer obj);
|
||||
void typed_value_list_destroy(Pointer obj);
|
||||
void typed_value_list_iter_destroy(Pointer obj);
|
||||
void typed_value_result_set_destroy(Pointer obj);
|
||||
void typed_value_result_set_iter_destroy(Pointer obj);
|
||||
void tx_report_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);
|
||||
|
||||
// sync
|
||||
RustResult store_sync(Pointer store, String userUuid, String serverUri);
|
||||
|
||||
// observers
|
||||
void store_register_observer(Pointer store, String key, Pointer attributes, int len, TxObserverCallback callback);
|
||||
void store_unregister_observer(Pointer store, String key);
|
||||
long store_entid_for_attribute(Pointer store, String attr);
|
||||
|
||||
// Query Building
|
||||
Pointer store_query(Pointer store, String query);
|
||||
RustResult store_value_for_attribute(Pointer store, long entid, String attribute);
|
||||
void query_builder_bind_long(Pointer query, String var, long value);
|
||||
void query_builder_bind_ref(Pointer query, String var, long value);
|
||||
void query_builder_bind_ref_kw(Pointer query, String var, String value);
|
||||
void query_builder_bind_kw(Pointer query, String var, String value);
|
||||
void query_builder_bind_boolean(Pointer query, String var, int value);
|
||||
void query_builder_bind_double(Pointer query, String var, double value);
|
||||
void query_builder_bind_timestamp(Pointer query, String var, long value);
|
||||
void query_builder_bind_string(Pointer query, String var, String value);
|
||||
void query_builder_bind_uuid(Pointer query, String var, Pointer value);
|
||||
|
||||
// Query Execution
|
||||
RustResult query_builder_execute(Pointer query);
|
||||
RustResult query_builder_execute_scalar(Pointer query);
|
||||
RustResult query_builder_execute_coll(Pointer query);
|
||||
RustResult query_builder_execute_tuple(Pointer query);
|
||||
|
||||
// Query Result Processing
|
||||
long typed_value_as_long(Pointer value);
|
||||
long typed_value_as_entid(Pointer value);
|
||||
String typed_value_as_kw(Pointer value);
|
||||
String typed_value_as_string(Pointer value);
|
||||
Pointer typed_value_as_uuid(Pointer value);
|
||||
int typed_value_as_boolean(Pointer value);
|
||||
double typed_value_as_double(Pointer value);
|
||||
long typed_value_as_timestamp(Pointer value);
|
||||
Pointer typed_value_value_type(Pointer value);
|
||||
|
||||
Pointer row_at_index(Pointer rows, int index);
|
||||
Pointer rows_iter(Pointer rows);
|
||||
Pointer rows_iter_next(Pointer iter);
|
||||
|
||||
Pointer values_iter(Pointer rows);
|
||||
Pointer values_iter_next(Pointer iter);
|
||||
|
||||
Pointer value_at_index(Pointer rows, int index);
|
||||
long value_at_index_as_long(Pointer rows, int index);
|
||||
long value_at_index_as_entid(Pointer rows, int index);
|
||||
String value_at_index_as_kw(Pointer rows, int index);
|
||||
String value_at_index_as_string(Pointer rows, int index);
|
||||
Pointer value_at_index_as_uuid(Pointer rows, int index);
|
||||
long value_at_index_as_boolean(Pointer rows, int index);
|
||||
double value_at_index_as_double(Pointer rows, int index);
|
||||
long value_at_index_as_timestamp(Pointer rows, int index);
|
||||
}
|
|
@ -0,0 +1,152 @@
|
|||
/* -*- 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;
|
||||
|
||||
/**
|
||||
* The primary class for accessing Mentat's API.<br/>
|
||||
* This class provides all of the basic API that can be found in Mentat's Store struct.<br/>
|
||||
* The raw pointer it holds is a pointer to a Store.
|
||||
*/
|
||||
public class Mentat extends RustObject {
|
||||
|
||||
static {
|
||||
System.loadLibrary("mentat_ffi");
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a connection to a Store in a given location.<br/>
|
||||
* If the store does not already exist, one will be created.
|
||||
* @param dbPath The URI as a String of the store to open.
|
||||
*/
|
||||
public Mentat(String dbPath) {
|
||||
this.rawPointer = JNA.INSTANCE.store_open(dbPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a connection to an in-memory Store.
|
||||
*/
|
||||
public Mentat() {
|
||||
this.rawPointer = JNA.INSTANCE.store_open("");
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new Mentat with the provided pointer to a Mentat Store
|
||||
* @param rawPointer A pointer to a Mentat Store.
|
||||
*/
|
||||
public Mentat(Pointer rawPointer) { this.rawPointer = rawPointer; }
|
||||
|
||||
/**
|
||||
* Simple transact of an EDN string.
|
||||
* TODO: Throw an exception if the transact fails
|
||||
* @param transaction The string, as EDN, to be transacted.
|
||||
* @return The {@link TxReport} of the completed transaction
|
||||
*/
|
||||
public TxReport transact(String transaction) {
|
||||
RustResult result = JNA.INSTANCE.store_transact(this.rawPointer, transaction);
|
||||
if (result.isFailure()) {
|
||||
Log.i("Mentat", result.err);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (result.isSuccess()) {
|
||||
return new TxReport(result.ok);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the the `Entid` of the attribute
|
||||
* @param attribute The string represeting the attribute whose `Entid` we are after. The string is represented as `:namespace/name`.
|
||||
* @return The `Entid` associated with the attribute.
|
||||
*/
|
||||
public long entIdForAttribute(String attribute) {
|
||||
return JNA.INSTANCE.store_entid_for_attribute(this.rawPointer, attribute);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a query.
|
||||
* @param query The string represeting the the query to be executed.
|
||||
* @return The {@link Query} representing the query that can be executed.
|
||||
*/
|
||||
public Query query(String query) {
|
||||
return new Query(JNA.INSTANCE.store_query(this.rawPointer, query));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a single value of an attribute for an Entity
|
||||
* TODO: Throw an exception if there is no the result contains an error.
|
||||
* @param attribute The string the attribute whose value is to be returned. The string is represented as `:namespace/name`.
|
||||
* @param entid The `Entid` of the entity we want the value from.
|
||||
* @return The {@link TypedValue} containing the value of the attribute for the entity.
|
||||
*/
|
||||
public TypedValue valueForAttributeOfEntity(String attribute, long entid) {
|
||||
RustResult result = JNA.INSTANCE.store_value_for_attribute(this.rawPointer, entid, attribute);
|
||||
|
||||
if (result.isSuccess()) {
|
||||
return new TypedValue(result.ok);
|
||||
}
|
||||
|
||||
if (result.isFailure()) {
|
||||
Log.i("Mentat", result.err);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an callback and a set of attributes to observer for transaction observation.
|
||||
* The callback function is called when a transaction occurs in the `Store` that this `Mentat`
|
||||
* is connected to that affects the attributes that an observer has registered for.
|
||||
* @param key `String` representing an identifier for the observer.
|
||||
* @param attributes An array of Strings representing the attributes that the observer wishes
|
||||
* to be notified about if they are referenced in a transaction.
|
||||
* @param callback the function to call when an observer notice is fired.
|
||||
*/
|
||||
public void registerObserver(String key, String[] attributes, TxObserverCallback callback) {
|
||||
// turn string array into int array
|
||||
long[] attrEntids = new long[attributes.length];
|
||||
for(int i = 0; i < attributes.length; i++) {
|
||||
attrEntids[i] = JNA.INSTANCE.store_entid_for_attribute(this.rawPointer, attributes[i]);
|
||||
}
|
||||
Log.i("Mentat", "Registering observer {" + key + "} for attributes:");
|
||||
for (int i = 0; i < attrEntids.length; i++) {
|
||||
Log.i("Mentat", "entid: " + attrEntids[i]);
|
||||
}
|
||||
final Pointer entidsNativeArray = new Memory(8 * attrEntids.length);
|
||||
entidsNativeArray.write(0, attrEntids, 0, attrEntids.length);
|
||||
JNA.INSTANCE.store_register_observer(rawPointer, key, entidsNativeArray, attrEntids.length, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister the observer that was registered with the provided key such that it will no longer be called
|
||||
* if a transaction occurs that affects the attributes that the observer was registered to observe.
|
||||
* <p/>
|
||||
* The observer will need to re-register if it wants to start observing again.
|
||||
* @param key String representing an identifier for the observer.
|
||||
*/
|
||||
public void unregisterObserver(String key) {
|
||||
JNA.INSTANCE.store_unregister_observer(rawPointer, key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
Log.i("Mentat", "close");
|
||||
if (this.rawPointer != null) {
|
||||
JNA.INSTANCE.store_destroy(this.rawPointer);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,332 @@
|
|||
/* -*- 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.nio.ByteBuffer;
|
||||
import java.util.Date;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* This class allows you to construct a query, bind values to variables and run those queries against a mentat DB.
|
||||
* <p/>
|
||||
* This class cannot be created directly, but must be created through `Mentat.query(String:)`.
|
||||
* <p/>
|
||||
* The types of values you can bind are:
|
||||
* <ul>
|
||||
* <li>{@link TypedValue}</li>
|
||||
* <li>long</li>
|
||||
* <li>Entid (as long)</li>
|
||||
* <li>Keyword (as String)</li>
|
||||
* <li>boolean</li>
|
||||
* <li>double</li>
|
||||
* <li>{@link Date}</li>
|
||||
* <li>{@link String}</li>
|
||||
* <li>{@link UUID}</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* <p/>
|
||||
* Each bound variable must have a corresponding value in the query string used to create this query.
|
||||
* <p/>
|
||||
* <pre>{@code
|
||||
* String query = "[:find ?name ?cat\n" +
|
||||
* " :in ?type\n" +
|
||||
* " :where\n" +
|
||||
* " [?c :community/name ?name]\n" +
|
||||
* " [?c :community/type ?type]\n" +
|
||||
* " [?c :community/category ?cat]]";
|
||||
* mentat.query(query).bindKeywordReference("?type", ":community.type/website").run(new RelResultHandler() {
|
||||
* @Override
|
||||
* public void handleRows(RelResult rows) {
|
||||
* ...
|
||||
* }
|
||||
* });
|
||||
*}</pre>
|
||||
* <p/>
|
||||
* Queries can be run and the results returned in a number of different formats. Individual result values are returned as `TypedValues` and
|
||||
* the format differences relate to the number and structure of those values. The result format is related to the format provided in the query string.
|
||||
* <p/>
|
||||
* - `Rel` - This is the default `run` function and returns a list of rows of values. Queries that wish to have `Rel` results should format their query strings:
|
||||
*
|
||||
* <pre>{@code
|
||||
* String query = "[: find ?a ?b ?c\n" +
|
||||
* " : where ... ]";
|
||||
* mentat.query(query).run(new RelResultHandler() {
|
||||
* @Override
|
||||
* public void handleRows(RelResult rows) {
|
||||
* ...
|
||||
* }
|
||||
* });
|
||||
*}</pre>
|
||||
* <p/>
|
||||
* - `Scalar` - This returns a single value as a result. This can be optional, as the value may not be present. Queries that wish to have `Scalar` results should format their query strings:
|
||||
*
|
||||
* <pre>{@code
|
||||
* String query = "[: find ?a .\n" +
|
||||
* " : where ... ]";
|
||||
* mentat.query(query).runScalar(new ScalarResultHandler() {
|
||||
* @Override
|
||||
* public void handleValue(TypedValue value) {
|
||||
* ...
|
||||
* }
|
||||
* });
|
||||
*}</pre>
|
||||
* <p/>
|
||||
* - `Coll` - This returns a list of single values as a result. Queries that wish to have `Coll` results should format their query strings:
|
||||
* <pre>{@code
|
||||
* String query = "[: find [?a ...]\n" +
|
||||
* " : where ... ]";
|
||||
* mentat.query(query).runColl(new ScalarResultHandler() {
|
||||
* @Override
|
||||
* public void handleList(CollResult list) {
|
||||
* ...
|
||||
* }
|
||||
* });
|
||||
*}</pre>
|
||||
* <p/>
|
||||
* - `Tuple` - This returns a single row of values. Queries that wish to have `Tuple` results should format their query strings:
|
||||
* <pre>{@code
|
||||
* String query = "[: find [?a ?b ?c]\n" +
|
||||
* " : where ... ]";
|
||||
* mentat.query(query).runTuple(new TupleResultHandler() {
|
||||
* @Override
|
||||
* public void handleRow(TupleResult row) {
|
||||
* ...
|
||||
* }
|
||||
* });
|
||||
*}</pre>
|
||||
*/
|
||||
public class Query extends RustObject {
|
||||
|
||||
public Query(Pointer pointer) {
|
||||
this.rawPointer = pointer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds a long value to the provided variable name.
|
||||
* TODO: Throw an exception if the query raw pointer has been consumed.
|
||||
* @param varName The name of the variable in the format `?name`.
|
||||
* @param value The value to be bound
|
||||
* @return This {@link Query} such that further function can be called.
|
||||
*/
|
||||
Query bindLong(String varName, long value) {
|
||||
this.validate();
|
||||
JNA.INSTANCE.query_builder_bind_long(this.rawPointer, varName, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds a Entid value to the provided variable name.
|
||||
* TODO: Throw an exception if the query raw pointer has been consumed.
|
||||
* @param varName The name of the variable in the format `?name`.
|
||||
* @param value The value to be bound
|
||||
* @return This {@link Query} such that further function can be called.
|
||||
*/
|
||||
Query bindEntidReference(String varName, long value) {
|
||||
this.validate();
|
||||
JNA.INSTANCE.query_builder_bind_ref(this.rawPointer, varName, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds a String keyword value to the provided variable name.
|
||||
* TODO: Throw an exception if the query raw pointer has been consumed.
|
||||
* @param varName The name of the variable in the format `?name`.
|
||||
* @param value The value to be bound
|
||||
* @return This {@link Query} such that further function can be called.
|
||||
*/
|
||||
Query bindKeywordReference(String varName, String value) {
|
||||
this.validate();
|
||||
JNA.INSTANCE.query_builder_bind_ref_kw(this.rawPointer, varName, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds a keyword value to the provided variable name.
|
||||
* TODO: Throw an exception if the query raw pointer has been consumed.
|
||||
* @param varName The name of the variable in the format `?name`.
|
||||
* @param value The value to be bound
|
||||
* @return This {@link Query} such that further function can be called.
|
||||
*/
|
||||
Query bindKeyword(String varName, String value) {
|
||||
this.validate();
|
||||
JNA.INSTANCE.query_builder_bind_kw(this.rawPointer, varName, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds a boolean value to the provided variable name.
|
||||
* TODO: Throw an exception if the query raw pointer has been consumed.
|
||||
* @param varName The name of the variable in the format `?name`.
|
||||
* @param value The value to be bound
|
||||
* @return This {@link Query} such that further function can be called.
|
||||
*/
|
||||
Query bindBoolean(String varName, boolean value) {
|
||||
this.validate();
|
||||
JNA.INSTANCE.query_builder_bind_boolean(this.rawPointer, varName, value ? 1 : 0);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds a double value to the provided variable name.
|
||||
* TODO: Throw an exception if the query raw pointer has been consumed.
|
||||
* @param varName The name of the variable in the format `?name`.
|
||||
* @param value The value to be bound
|
||||
* @return This {@link Query} such that further function can be called.
|
||||
*/
|
||||
Query bindDouble(String varName, double value) {
|
||||
this.validate();
|
||||
JNA.INSTANCE.query_builder_bind_double(this.rawPointer, varName, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds a {@link Date} value to the provided variable name.
|
||||
* TODO: Throw an exception if the query raw pointer has been consumed.
|
||||
* @param varName The name of the variable in the format `?name`.
|
||||
* @param value The value to be bound
|
||||
* @return This {@link Query} such that further function can be called.
|
||||
*/
|
||||
Query bindDate(String varName, Date value) {
|
||||
this.validate();
|
||||
long timestamp = value.getTime() * 1000;
|
||||
JNA.INSTANCE.query_builder_bind_timestamp(this.rawPointer, varName, timestamp);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds a {@link String} value to the provided variable name.
|
||||
* TODO: Throw an exception if the query raw pointer has been consumed.
|
||||
* @param varName The name of the variable in the format `?name`.
|
||||
* @param value The value to be bound
|
||||
* @return This {@link Query} such that further function can be called.
|
||||
*/
|
||||
Query bindString(String varName, String value) {
|
||||
this.validate();
|
||||
JNA.INSTANCE.query_builder_bind_string(this.rawPointer, varName, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds a {@link UUID} value to the provided variable name.
|
||||
* TODO: Throw an exception if the query raw pointer has been consumed.
|
||||
* @param varName The name of the variable in the format `?name`.
|
||||
* @param value The value to be bound
|
||||
* @return This {@link Query} such that further function can be called.
|
||||
*/
|
||||
Query bindUUID(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);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the query with the values bound associated with this {@link Query} and call the provided
|
||||
* callback function with the results as a list of rows of {@link TypedValue}s.
|
||||
* 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 run(final RelResultHandler handler) {
|
||||
this.validate();
|
||||
RustResult result = JNA.INSTANCE.query_builder_execute(rawPointer);
|
||||
rawPointer = null;
|
||||
|
||||
if (result.isFailure()) {
|
||||
Log.e("Query", result.err);
|
||||
return;
|
||||
}
|
||||
handler.handleRows(new RelResult(result.ok));
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the query with the values bound associated with this {@link Query} and call the provided
|
||||
* callback function with the results with the result as a single {@link TypedValue}.
|
||||
* 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) {
|
||||
this.validate();
|
||||
RustResult result = JNA.INSTANCE.query_builder_execute_scalar(rawPointer);
|
||||
rawPointer = null;
|
||||
|
||||
if (result.isFailure()) {
|
||||
Log.e("Query", result.err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.isSuccess()) {
|
||||
handler.handleValue(new TypedValue(result.ok));
|
||||
} else {
|
||||
handler.handleValue(null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the query with the values bound associated with this {@link Query} and call the provided
|
||||
* callback function with the results with the result as a list of single {@link TypedValue}s.
|
||||
* 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) {
|
||||
this.validate();
|
||||
RustResult result = JNA.INSTANCE.query_builder_execute_coll(rawPointer);
|
||||
rawPointer = null;
|
||||
|
||||
if (result.isFailure()) {
|
||||
Log.e("Query", result.err);
|
||||
return;
|
||||
}
|
||||
handler.handleList(new CollResult(result.ok));
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the query with the values bound associated with this {@link Query} and call the provided
|
||||
* callback function with the results with the result as a list of single {@link TypedValue}s.
|
||||
* 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) {
|
||||
this.validate();
|
||||
RustResult result = JNA.INSTANCE.query_builder_execute_tuple(rawPointer);
|
||||
rawPointer = null;
|
||||
|
||||
if (result.isFailure()) {
|
||||
Log.e("Query", result.err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.isSuccess()) {
|
||||
handler.handleRow(new TupleResult(result.ok));
|
||||
} else {
|
||||
handler.handleRow(null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
Log.i("Query", "close");
|
||||
|
||||
if (this.rawPointer == null) {
|
||||
return;
|
||||
}
|
||||
JNA.INSTANCE.query_builder_destroy(this.rawPointer);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
/* -*- 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;
|
||||
|
||||
/**
|
||||
* Wraps a `Rel` result from a Mentat query.
|
||||
* A `Rel` result is a list of rows of `TypedValues`.
|
||||
* Individual rows can be fetched or the set can be iterated.
|
||||
* </p>
|
||||
* To fetch individual rows from a `RelResult` use `row(Int32)`.
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
* mentat.query(query).run(new RelResultHandler() {
|
||||
* @Override
|
||||
* public void handleRows(RelResult rows) {
|
||||
* TupleResult row1 = rows.rowAtIndex(0);
|
||||
* TupleResult row2 = rows.rowAtIndex(1);
|
||||
* ...
|
||||
* }
|
||||
* });
|
||||
*}</pre>
|
||||
* </p>
|
||||
* To iterate over the result set use standard iteration flows.
|
||||
* <pre>{@code
|
||||
* mentat.query(query).run(new RelResultHandler() {
|
||||
* @Override
|
||||
* public void handleRows(RelResult rows) {
|
||||
* for (TupleResult row: rows) {
|
||||
* ...
|
||||
* }
|
||||
* }
|
||||
* });
|
||||
*}</pre>
|
||||
* </p>
|
||||
* Note that iteration is consuming and can only be done once.
|
||||
*/
|
||||
public class RelResult extends RustObject implements Iterable<TupleResult> {
|
||||
|
||||
public RelResult(Pointer pointer) {
|
||||
this.rawPointer = pointer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the row at the requested index.
|
||||
* TODO: Throw an exception if the result set has already been iterated.
|
||||
* @param index the index of the row to be fetched
|
||||
* @return The row at the requested index as a `TupleResult`, if present, or nil if there is no row at that index.
|
||||
*/
|
||||
public TupleResult rowAtIndex(int index) {
|
||||
this.validate();
|
||||
Pointer pointer = JNA.INSTANCE.row_at_index(this.rawPointer, index);
|
||||
if (pointer == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new TupleResult(pointer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RelResultIterator iterator() {
|
||||
this.validate();
|
||||
Pointer iterPointer = JNA.INSTANCE.rows_iter(this.rawPointer);
|
||||
this.rawPointer = null;
|
||||
if (iterPointer == null) {
|
||||
return null;
|
||||
}
|
||||
return new RelResultIterator(iterPointer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
Log.i("RelResult", "close");
|
||||
|
||||
if (this.rawPointer != null) {
|
||||
JNA.INSTANCE.typed_value_result_set_destroy(this.rawPointer);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
/* -*- 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;
|
||||
|
||||
/**
|
||||
* Interface defining the structure of a callback from a query returning a {@link RelResult}.
|
||||
*/
|
||||
public interface RelResultHandler {
|
||||
void handleRows(RelResult rows);
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
/* -*- 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.util.Iterator;
|
||||
/**
|
||||
* Iterator for a {@link RelResult}
|
||||
*/
|
||||
public class RelResultIterator extends RustObject implements Iterator {
|
||||
|
||||
Pointer nextPointer;
|
||||
|
||||
RelResultIterator(Pointer iterator) {
|
||||
this.rawPointer = iterator;
|
||||
}
|
||||
|
||||
private Pointer getNextPointer() {
|
||||
return JNA.INSTANCE.rows_iter_next(this.rawPointer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
this.nextPointer = getNextPointer();
|
||||
return this.nextPointer != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TupleResult next() {
|
||||
Pointer next = this.nextPointer == null ? getNextPointer() : this.nextPointer;
|
||||
if (next == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new TupleResult(next);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
Log.i("TupleResult", "close");
|
||||
if (this.rawPointer != null) {
|
||||
JNA.INSTANCE.typed_value_result_set_iter_destroy(this.rawPointer);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/* -*- 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 com.sun.jna.Pointer;
|
||||
|
||||
import java.io.Closeable;
|
||||
|
||||
/**
|
||||
* Base class that wraps an non-optional {@link Pointer} representing a pointer to a Rust object.
|
||||
* This class implements {@link Closeable} but does not provide an implementation, forcing all
|
||||
* subclasses to implement it. This ensures that all classes that inherit from RustObject
|
||||
* will have their {@link Pointer} destroyed when the Java wrapper is destroyed.
|
||||
*/
|
||||
abstract class RustObject implements Closeable {
|
||||
Pointer rawPointer;
|
||||
|
||||
/**
|
||||
* Throws a {@link NullPointerException} if the underlying {@link Pointer} is null.
|
||||
*/
|
||||
void validate() {
|
||||
if (this.rawPointer == null) {
|
||||
throw new NullPointerException(this.getClass() + " consumed");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
/* -*- 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 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;
|
||||
|
||||
/**
|
||||
* Represents a C struct containing a {@link Pointer}s and String that map to a Rust Result.
|
||||
* A RustResult will contain either an ok value, OR an err value, or neither - never both.
|
||||
*/
|
||||
public class RustResult extends Structure implements Closeable {
|
||||
public static class ByReference extends RustResult implements Structure.ByReference {
|
||||
}
|
||||
|
||||
public static class ByValue extends RustResult implements Structure.ByValue {
|
||||
}
|
||||
|
||||
public Pointer ok;
|
||||
public String err;
|
||||
|
||||
/**
|
||||
* Is there an value attached to this result
|
||||
* @return true if a value is present, false otherwise
|
||||
*/
|
||||
public boolean isSuccess() {
|
||||
return this.ok != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is there an error attached to this result?
|
||||
* @return true is an error is present, false otherwise
|
||||
*/
|
||||
public boolean isFailure() {
|
||||
return this.err != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getFieldOrder() {
|
||||
return Arrays.asList("ok", "err");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (this.getPointer() != null) {
|
||||
JNA.INSTANCE.destroy(this.getPointer());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
/* -*- 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;
|
||||
|
||||
/**
|
||||
* Interface defining the structure of a callback from a query returning a single {@link TypedValue}.
|
||||
*/
|
||||
public interface ScalarResultHandler {
|
||||
void handleValue(TypedValue value);
|
||||
}
|
|
@ -0,0 +1,170 @@
|
|||
/* -*- 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.nio.ByteBuffer;
|
||||
import java.util.Date;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Wraps a `Tuple` result from a Mentat query.
|
||||
* A `Tuple` result is a single row {@link TypedValue}s.
|
||||
* Values for individual fields can be fetched as {@link TypedValue} or converted into a requested type.
|
||||
* <p>
|
||||
* Field values can be fetched as one of the following types:
|
||||
* <ul>
|
||||
* <li>{@link TypedValue}</li>
|
||||
* <li>long</li>
|
||||
* <li>Entid (as long)</li>
|
||||
* <li>Keyword (as String)</li>
|
||||
* <li>boolean</li>
|
||||
* <li>double</li>
|
||||
* <li>{@link Date}</li>
|
||||
* <li>{@link String}</li>
|
||||
* <li>{@link UUID}</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* To iterate over the result set use standard iteration flows.
|
||||
*/
|
||||
public class TupleResult extends RustObject {
|
||||
|
||||
public TupleResult(Pointer pointer) {
|
||||
this.rawPointer = pointer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link TypedValue} at the specified index.
|
||||
* If the index is greater than the number of values then this function will crash.
|
||||
* @param index The index of the value to fetch.
|
||||
* @return The {@link TypedValue} at that index.
|
||||
*/
|
||||
public TypedValue get(Integer index) {
|
||||
this.validate();
|
||||
Pointer pointer = JNA.INSTANCE.value_at_index(this.rawPointer, index);
|
||||
if (pointer == null) {
|
||||
return null;
|
||||
}
|
||||
return new TypedValue(pointer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link Long} at the specified index.
|
||||
* If the index is greater than the number of values then this function will crash.
|
||||
* If the value type if the {@link TypedValue} at this index is not `Long` then this function will crash.
|
||||
* @param index The index of the value to fetch.
|
||||
* @return The {@link Long} at that index.
|
||||
*/
|
||||
public Long asLong(Integer index) {
|
||||
this.validate();
|
||||
return JNA.INSTANCE.value_at_index_as_long(this.rawPointer, index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Entid at the specified index.
|
||||
* If the index is greater than the number of values then this function will crash.
|
||||
* If the value type if the {@link TypedValue} at this index is not `Ref` then this function will crash.
|
||||
* @param index The index of the value to fetch.
|
||||
* @return The Entid at that index.
|
||||
*/
|
||||
public Long asEntid(Integer index) {
|
||||
this.validate();
|
||||
return JNA.INSTANCE.value_at_index_as_entid(this.rawPointer, index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the keyword {@link String} at the specified index.
|
||||
* If the index is greater than the number of values then this function will crash.
|
||||
* If the value type if the {@link TypedValue} at this index is not `Keyword` then this function will crash.
|
||||
* @param index The index of the value to fetch.
|
||||
* @return The keyword at that index.
|
||||
*/
|
||||
public String asKeyword(Integer index) {
|
||||
this.validate();
|
||||
return JNA.INSTANCE.value_at_index_as_kw(this.rawPointer, index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link Boolean} at the specified index.
|
||||
* If the index is greater than the number of values then this function will crash.
|
||||
* If the value type if the {@link TypedValue} at this index is not `Boolean` then this function will crash.
|
||||
* @param index The index of the value to fetch.
|
||||
* @return The {@link Boolean} at that index.
|
||||
*/
|
||||
public Boolean asBool(Integer index) {
|
||||
this.validate();
|
||||
return JNA.INSTANCE.value_at_index_as_boolean(this.rawPointer, index) == 0 ? false : true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link Double} at the specified index.
|
||||
* If the index is greater than the number of values then this function will crash.
|
||||
* If the value type if the {@link TypedValue} at this index is not `Double` then this function will crash.
|
||||
* @param index The index of the value to fetch.
|
||||
* @return The {@link Double} at that index.
|
||||
*/
|
||||
public Double asDouble(Integer index) {
|
||||
this.validate();
|
||||
return JNA.INSTANCE.value_at_index_as_double(this.rawPointer, index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link Date} at the specified index.
|
||||
* If the index is greater than the number of values then this function will crash.
|
||||
* If the value type if the {@link TypedValue} at this index is not `Instant` then this function will crash.
|
||||
* @param index The index of the value to fetch.
|
||||
* @return The {@link Date} at that index.
|
||||
*/
|
||||
public Date asDate(Integer index) {
|
||||
this.validate();
|
||||
return new Date(JNA.INSTANCE.value_at_index_as_timestamp(this.rawPointer, index));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link String} at the specified index.
|
||||
* If the index is greater than the number of values then this function will crash.
|
||||
* If the value type if the {@link TypedValue} at this index is not `String` then this function will crash.
|
||||
* @param index The index of the value to fetch.
|
||||
* @return The {@link String} at that index.
|
||||
*/
|
||||
public String asString(Integer index) {
|
||||
this.validate();
|
||||
return JNA.INSTANCE.value_at_index_as_string(this.rawPointer, index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link UUID} at the specified index.
|
||||
* If the index is greater than the number of values then this function will crash.
|
||||
* If the value type if the {@link TypedValue} at this index is not `Uuid` then this function will crash.
|
||||
* @param index The index of the value to fetch.
|
||||
* @return The {@link UUID} at that index.
|
||||
*/
|
||||
public UUID asUUID(Integer index) {
|
||||
this.validate();
|
||||
Pointer uuidPtr = JNA.INSTANCE.value_at_index_as_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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
Log.i("TupleResult", "close");
|
||||
if (this.rawPointer != null) {
|
||||
JNA.INSTANCE.typed_value_list_destroy(this.rawPointer);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
/* -*- 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;
|
||||
|
||||
/**
|
||||
* Interface defining the structure of a callback from a query returning a {@link TupleResult}.
|
||||
*/
|
||||
public interface TupleResultHandler {
|
||||
void handleRow(TupleResult row);
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
/* -*- 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.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents a C struct representing changes that occured during a transaction.
|
||||
* These changes contain the transaction identifier, a {@link Pointer} to a list of affected attribute
|
||||
* Entids and the number of items that the list contains.
|
||||
*/
|
||||
public class TxChange extends Structure implements Closeable {
|
||||
public static class ByReference extends TxChange implements Structure.ByReference {
|
||||
}
|
||||
|
||||
public static class ByValue extends TxChange implements Structure.ByValue {
|
||||
}
|
||||
|
||||
public int txid;
|
||||
public Pointer changes;
|
||||
public int numberOfItems;
|
||||
// Used by the Swift counterpart, JNA does this for us automagically.
|
||||
// But we still need it here so that the number of fields and their order is correct
|
||||
public int changes_len;
|
||||
|
||||
/**
|
||||
* Get the affected attributes for this transaction
|
||||
* @return The changes as a list of Entids of affected attributes
|
||||
*/
|
||||
public List<Long> getChanges() {
|
||||
final long[] array = (long[]) changes.getLongArray(0, numberOfItems);
|
||||
Long[] longArray = new Long[numberOfItems];
|
||||
int idx = 0;
|
||||
for(long change: array) {
|
||||
longArray[idx++] = change;
|
||||
}
|
||||
return Arrays.asList(longArray);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getFieldOrder() {
|
||||
return Arrays.asList("txid", "changes", "changes_len", "numberOfItems");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
Log.i("TxChange", "close");
|
||||
if (this.getPointer() != null) {
|
||||
JNA.INSTANCE.destroy(this.getPointer());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
/* -*- 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.Structure;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents a C struct containing a list of {@link TxChange}s that occured.
|
||||
*/
|
||||
public class TxChangeList extends Structure implements Closeable {
|
||||
public static class ByReference extends TxChangeList implements Structure.ByReference {
|
||||
}
|
||||
|
||||
public static class ByValue extends TxChangeList implements Structure.ByValue {
|
||||
}
|
||||
|
||||
public TxChange.ByReference reports;
|
||||
public int numberOfItems;
|
||||
// Used by the Swift counterpart, JNA does this for us automagically.
|
||||
// // But we still need it here so that the number of fields and their order is correct
|
||||
public int len;
|
||||
|
||||
/**
|
||||
* Get the changes that occured
|
||||
* @return a list of {@link TxChange}s for the notification
|
||||
*/
|
||||
public List<TxChange> getReports() {
|
||||
final TxChange[] array = (TxChange[]) reports.toArray(numberOfItems);
|
||||
return Arrays.asList(array);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getFieldOrder() {
|
||||
return Arrays.asList("reports", "numberOfItems", "len");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
Log.i("TxChangeList", "close");
|
||||
final TxChange[] nativeReports = (TxChange[]) reports.toArray(numberOfItems);
|
||||
for (TxChange nativeReport : nativeReports) {
|
||||
nativeReport.close();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
/* -*- 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 com.sun.jna.Callback;
|
||||
|
||||
/**
|
||||
* Protocol to be implemented by any object that wishes to register for transaction observation
|
||||
*/
|
||||
public interface TxObserverCallback extends Callback {
|
||||
void transactionObserverCalled(String key, TxChangeList.ByReference reports);
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
/* -*- 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.util.Date;
|
||||
|
||||
/**
|
||||
* This class wraps a raw pointer than points to a Rust `TxReport` object.
|
||||
* </p>
|
||||
* The `TxReport` contains information about a successful Mentat transaction.
|
||||
* </p>
|
||||
* This information includes:
|
||||
* <ul>
|
||||
* <li>`txId` - the identifier for the transaction.</li>
|
||||
* <li>`txInstant` - the time that the transaction occured.</li>
|
||||
* <li>a map of temporary identifiers provided in the transaction and the `Entid`s that they were mapped to.</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
* Access an `Entid` for a temporary identifier that was provided in the transaction can be done through `entid(String:)`.
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
* TxReport report = mentat.transact("[[:db/add "a" :foo/boolean true]]");
|
||||
* long aEntid = report.getEntidForTempId("a");
|
||||
*}</pre>
|
||||
*/
|
||||
public class TxReport extends RustObject {
|
||||
|
||||
private Long txId;
|
||||
private Date txInstant;
|
||||
|
||||
|
||||
public TxReport(Pointer pointer) {
|
||||
this.rawPointer = pointer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the identifier for the transaction.
|
||||
* @return The identifier for the transaction.
|
||||
*/
|
||||
public Long getTxId() {
|
||||
if (this.txId == null) {
|
||||
this.txId = JNA.INSTANCE.tx_report_get_entid(this.rawPointer);
|
||||
}
|
||||
|
||||
return this.txId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the time that the transaction occured.
|
||||
* @return The time that the transaction occured.
|
||||
*/
|
||||
public Date getTxInstant() {
|
||||
if (this.txInstant == null) {
|
||||
this.txInstant = new Date(JNA.INSTANCE.tx_report_get_tx_instant(this.rawPointer));
|
||||
}
|
||||
return this.txInstant;
|
||||
}
|
||||
|
||||
/**
|
||||
* Access an `Entid` for a temporary identifier that was provided in the transaction.
|
||||
* @param tempId A {@link String} representing the temporary identifier to fetch the `Entid` for.
|
||||
* @return The `Entid` for the temporary identifier, if present, otherwise `null`.
|
||||
*/
|
||||
public Long getEntidForTempId(String tempId) {
|
||||
Pointer longPointer = JNA.INSTANCE.tx_report_entity_for_temp_id(this.rawPointer, tempId);
|
||||
if (longPointer == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return longPointer.getLong(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
Log.i("TxReport", "close");
|
||||
if (this.rawPointer != null) {
|
||||
JNA.INSTANCE.tx_report_destroy(this.rawPointer);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,161 @@
|
|||
/* -*- 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.BufferedReader;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Date;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* A wrapper around Mentat's `TypedValue` Rust object. This class wraps a raw pointer to a Rust `TypedValue`
|
||||
* struct and provides accessors to the values according to expected result type.
|
||||
* </p>
|
||||
* As the FFI functions for fetching values are consuming, this class keeps a copy of the result internally after
|
||||
* fetching so that the value can be referenced several times.
|
||||
* </p>
|
||||
* Also, due to the consuming nature of the FFI layer, this class also manages it's raw pointer, nilling it after calling the
|
||||
* FFI conversion function so that the underlying base class can manage cleanup.
|
||||
*/
|
||||
public class TypedValue extends RustObject {
|
||||
|
||||
private Object value;
|
||||
|
||||
private boolean isConsumed() {
|
||||
return this.rawPointer == null;
|
||||
}
|
||||
|
||||
public TypedValue(Pointer pointer) {
|
||||
this.rawPointer = pointer;
|
||||
}
|
||||
|
||||
/**
|
||||
* This value as a {@link Long}. This function will panic if the `ValueType` of this
|
||||
* {@link TypedValue} is not a `Long`
|
||||
* @return the value of this {@link TypedValue} as a {@link Long}
|
||||
*/
|
||||
public Long asLong() {
|
||||
if (!this.isConsumed()) {
|
||||
this.value = JNA.INSTANCE.typed_value_as_long(this.rawPointer);
|
||||
this.rawPointer = null;
|
||||
}
|
||||
return (Long)value;
|
||||
}
|
||||
|
||||
/**
|
||||
* This value as a Entid. This function will panic if the `ValueType` of this
|
||||
* {@link TypedValue} is not a `Ref`
|
||||
* @return the value of this {@link TypedValue} as a Entid
|
||||
*/
|
||||
public Long asEntid() {
|
||||
if (!this.isConsumed()) {
|
||||
this.value = JNA.INSTANCE.typed_value_as_entid(this.rawPointer);
|
||||
this.rawPointer = null;
|
||||
}
|
||||
return (Long)value;
|
||||
}
|
||||
|
||||
/**
|
||||
* This value as a keyword {@link String}. This function will panic if the `ValueType` of this
|
||||
* {@link TypedValue} is not a `Keyword`
|
||||
* @return the value of this {@link TypedValue} as a Keyword
|
||||
*/
|
||||
public String asKeyword() {
|
||||
if (!this.isConsumed()) {
|
||||
this.value = JNA.INSTANCE.typed_value_as_kw(this.rawPointer);
|
||||
this.rawPointer = null;
|
||||
}
|
||||
return (String)value;
|
||||
}
|
||||
|
||||
/**
|
||||
* This value as a {@link Boolean}. This function will panic if the `ValueType` of this
|
||||
* {@link TypedValue} is not a `Boolean`
|
||||
* @return the value of this {@link TypedValue} as a {@link Boolean}
|
||||
*/
|
||||
public Boolean asBoolean() {
|
||||
if (!this.isConsumed()) {
|
||||
long value = JNA.INSTANCE.typed_value_as_boolean(this.rawPointer);
|
||||
this.value = value == 0 ? false : true;
|
||||
this.rawPointer = null;
|
||||
}
|
||||
return (Boolean) this.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* This value as a {@link Double}. This function will panic if the `ValueType` of this
|
||||
* {@link TypedValue} is not a `Double`
|
||||
* @return the value of this {@link TypedValue} as a {@link Double}
|
||||
*/
|
||||
public Double asDouble() {
|
||||
if (!this.isConsumed()) {
|
||||
this.value = JNA.INSTANCE.typed_value_as_double(this.rawPointer);
|
||||
this.rawPointer = null;
|
||||
}
|
||||
return (Double)value;
|
||||
}
|
||||
|
||||
/**
|
||||
* This value as a {@link Date}. This function will panic if the `ValueType` of this
|
||||
* {@link TypedValue} is not a `Instant`
|
||||
* @return the value of this {@link TypedValue} as a {@link Date}
|
||||
*/
|
||||
public Date asDate() {
|
||||
if (!this.isConsumed()) {
|
||||
this.value = new Date(JNA.INSTANCE.typed_value_as_timestamp(this.rawPointer) * 1_000);
|
||||
this.rawPointer = null;
|
||||
}
|
||||
return (Date)this.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* This value as a {@link String}. This function will panic if the `ValueType` of this
|
||||
* {@link TypedValue} is not a `String`
|
||||
* @return the value of this {@link TypedValue} as a {@link String}
|
||||
*/
|
||||
public String asString() {
|
||||
if (!this.isConsumed()) {
|
||||
this.value = JNA.INSTANCE.typed_value_as_string(this.rawPointer);
|
||||
this.rawPointer = null;
|
||||
}
|
||||
return (String)value;
|
||||
}
|
||||
|
||||
/**
|
||||
* This value as a {@link UUID}. This function will panic if the `ValueType` of this
|
||||
* {@link TypedValue} is not a `Uuid`
|
||||
* @return the value of this {@link TypedValue} as a {@link UUID}
|
||||
*/
|
||||
public UUID asUUID() {
|
||||
if (!this.isConsumed()) {
|
||||
Pointer uuidPtr = JNA.INSTANCE.typed_value_as_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.rawPointer = null;
|
||||
}
|
||||
return (UUID)this.value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
Log.i("TypedValue", "close");
|
||||
|
||||
if (this.rawPointer != null) {
|
||||
JNA.INSTANCE.typed_value_destroy(this.rawPointer);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue