diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index 44595fc..6f88cdf 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -66,7 +66,7 @@ spotless { targetExclude("src/templates/*", "build/generated/java/BuildInfo.java") removeUnusedImports() cleanthat() - eclipse() + eclipse().configFile("${project.rootDir}/spotless.eclipseformat.xml") formatAnnotations() } } diff --git a/lib/src/main/java/org/automerge/AmValue.java b/lib/src/main/java/org/automerge/AmValue.java index 132aec6..444a021 100644 --- a/lib/src/main/java/org/automerge/AmValue.java +++ b/lib/src/main/java/org/automerge/AmValue.java @@ -16,191 +16,191 @@ */ public abstract class AmValue { - /** An unsigned integer */ - public static class UInt extends AmValue { - private long value; - - public long getValue() { - return value; - } - - @Override - public String toString() { - return "UInt [value=" + value + "]"; - } - } - - /** A 64bit integer */ - public static class Int extends AmValue { - private long value; - - public long getValue() { - return value; - } - - @Override - public String toString() { - return "Int [value=" + value + "]"; - } - } - - /** A Boolean */ - public static class Bool extends AmValue { - private boolean value; - - public boolean getValue() { - return value; - } - - @Override - public String toString() { - return "Bool [value=" + value + "]"; - } - } - - /** A byte array */ - public static class Bytes extends AmValue { - private byte[] value; - - public byte[] getValue() { - return value; - } - - @Override - public String toString() { - return "Bytes [value=" + Arrays.toString(value) + "]"; - } - } - - /** A string */ - public static class Str extends AmValue { - private String value; - - public String getValue() { - return value; - } - - @Override - public String toString() { - return "Str [value=" + value + "]"; - } - } - - /** A 64 bit floating point number */ - public static class F64 extends AmValue { - private double value; - - public double getValue() { - return value; - } - - @Override - public String toString() { - return "F64 [value=" + value + "]"; - } - } - - /** A counter */ - public static class Counter extends AmValue { - private org.automerge.Counter value; - - public long getValue() { - return value.getValue(); - } - - @Override - public String toString() { - return "Counter [value=" + value.getValue() + "]"; - } - } - - /** A timestamp */ - public static class Timestamp extends AmValue { - private Date value; - - public Date getValue() { - return value; - } - - @Override - public String toString() { - return "Timestamp [value=" + value + "]"; - } - } - - /** A null value */ - public static class Null extends AmValue { - - @Override - public String toString() { - return "Null"; - } - } - - /** - * An unknown value - * - *

- * This is used to represent values which may be added by future versions of - * automerge. - */ - public static class Unknown extends AmValue { - private int typeCode; - private byte[] value; - - public int getTypeCode() { - return typeCode; - } - - public byte[] getValue() { - return value; - } - - @Override - public String toString() { - return "Unknown [typeCode=" + typeCode + "]"; - } - } - - /** A map object */ - public static class Map extends AmValue { - private ObjectId id; - - public ObjectId getId() { - return id; - } - - @Override - public String toString() { - return "Map [id=" + id + "]"; - } - } - - /** A list object */ - public static class List extends AmValue { - private ObjectId id; - - public ObjectId getId() { - return id; - } - - @Override - public String toString() { - return "List [id=" + id + "]"; - } - } - - /** A text object */ - public static class Text extends AmValue { - private ObjectId id; - - public ObjectId getId() { - return id; - } - - @Override - public String toString() { - return "Text [id=" + id + "]"; - } - } + /** An unsigned integer */ + public static class UInt extends AmValue { + private long value; + + public long getValue() { + return value; + } + + @Override + public String toString() { + return "UInt [value=" + value + "]"; + } + } + + /** A 64bit integer */ + public static class Int extends AmValue { + private long value; + + public long getValue() { + return value; + } + + @Override + public String toString() { + return "Int [value=" + value + "]"; + } + } + + /** A Boolean */ + public static class Bool extends AmValue { + private boolean value; + + public boolean getValue() { + return value; + } + + @Override + public String toString() { + return "Bool [value=" + value + "]"; + } + } + + /** A byte array */ + public static class Bytes extends AmValue { + private byte[] value; + + public byte[] getValue() { + return value; + } + + @Override + public String toString() { + return "Bytes [value=" + Arrays.toString(value) + "]"; + } + } + + /** A string */ + public static class Str extends AmValue { + private String value; + + public String getValue() { + return value; + } + + @Override + public String toString() { + return "Str [value=" + value + "]"; + } + } + + /** A 64 bit floating point number */ + public static class F64 extends AmValue { + private double value; + + public double getValue() { + return value; + } + + @Override + public String toString() { + return "F64 [value=" + value + "]"; + } + } + + /** A counter */ + public static class Counter extends AmValue { + private org.automerge.Counter value; + + public long getValue() { + return value.getValue(); + } + + @Override + public String toString() { + return "Counter [value=" + value.getValue() + "]"; + } + } + + /** A timestamp */ + public static class Timestamp extends AmValue { + private Date value; + + public Date getValue() { + return value; + } + + @Override + public String toString() { + return "Timestamp [value=" + value + "]"; + } + } + + /** A null value */ + public static class Null extends AmValue { + + @Override + public String toString() { + return "Null"; + } + } + + /** + * An unknown value + * + *

+ * This is used to represent values which may be added by future versions of + * automerge. + */ + public static class Unknown extends AmValue { + private int typeCode; + private byte[] value; + + public int getTypeCode() { + return typeCode; + } + + public byte[] getValue() { + return value; + } + + @Override + public String toString() { + return "Unknown [typeCode=" + typeCode + "]"; + } + } + + /** A map object */ + public static class Map extends AmValue { + private ObjectId id; + + public ObjectId getId() { + return id; + } + + @Override + public String toString() { + return "Map [id=" + id + "]"; + } + } + + /** A list object */ + public static class List extends AmValue { + private ObjectId id; + + public ObjectId getId() { + return id; + } + + @Override + public String toString() { + return "List [id=" + id + "]"; + } + } + + /** A text object */ + public static class Text extends AmValue { + private ObjectId id; + + public ObjectId getId() { + return id; + } + + @Override + public String toString() { + return "Text [id=" + id + "]"; + } + } } diff --git a/lib/src/main/java/org/automerge/AutomergeException.java b/lib/src/main/java/org/automerge/AutomergeException.java index 4a3f50c..709dfbb 100644 --- a/lib/src/main/java/org/automerge/AutomergeException.java +++ b/lib/src/main/java/org/automerge/AutomergeException.java @@ -1,7 +1,7 @@ package org.automerge; class AutomergeException extends RuntimeException { - public AutomergeException(String message) { - super(message); - } + public AutomergeException(String message) { + super(message); + } } diff --git a/lib/src/main/java/org/automerge/AutomergeSys.java b/lib/src/main/java/org/automerge/AutomergeSys.java index c66b16f..5cee68c 100644 --- a/lib/src/main/java/org/automerge/AutomergeSys.java +++ b/lib/src/main/java/org/automerge/AutomergeSys.java @@ -8,341 +8,341 @@ import java.util.Optional; class AutomergeSys { - protected class DocPointer { - private long pointer; - } + protected class DocPointer { + private long pointer; + } - protected class TransactionPointer { - private long pointer; - } + protected class TransactionPointer { + private long pointer; + } - protected class SyncStatePointer { - private long pointer; - } + protected class SyncStatePointer { + private long pointer; + } - protected class PatchLogPointer { - private long pointer; - } + protected class PatchLogPointer { + private long pointer; + } - // Get the version of the JNI libs - public static native String rustLibVersion(); + // Get the version of the JNI libs + public static native String rustLibVersion(); - // Document methods - public static native DocPointer createDoc(); + // Document methods + public static native DocPointer createDoc(); - public static native DocPointer createDocWithActor(byte[] actorId); + public static native DocPointer createDocWithActor(byte[] actorId); - public static native DocPointer loadDoc(byte[] bytes); + public static native DocPointer loadDoc(byte[] bytes); - public static native void freeDoc(DocPointer pointer); + public static native void freeDoc(DocPointer pointer); - public static native byte[] saveDoc(DocPointer pointer); + public static native byte[] saveDoc(DocPointer pointer); - public static native DocPointer forkDoc(DocPointer pointer); + public static native DocPointer forkDoc(DocPointer pointer); - public static native DocPointer forkDocWithActor(DocPointer pointer, byte[] actorId); + public static native DocPointer forkDocWithActor(DocPointer pointer, byte[] actorId); - public static native DocPointer forkDocAt(DocPointer pointer, ChangeHash[] heads); + public static native DocPointer forkDocAt(DocPointer pointer, ChangeHash[] heads); - public static native DocPointer forkDocAtWithActor(DocPointer pointer, ChangeHash[] heads, byte[] actorId); + public static native DocPointer forkDocAtWithActor(DocPointer pointer, ChangeHash[] heads, byte[] actorId); - public static native void mergeDoc(DocPointer pointer, DocPointer other); + public static native void mergeDoc(DocPointer pointer, DocPointer other); - public static native void mergeDocLogPatches(DocPointer pointer, DocPointer other, PatchLogPointer patchLog); + public static native void mergeDocLogPatches(DocPointer pointer, DocPointer other, PatchLogPointer patchLog); - public static native byte[] getActorId(DocPointer pointer); + public static native byte[] getActorId(DocPointer pointer); - public static native TransactionPointer startTransaction(DocPointer doc); + public static native TransactionPointer startTransaction(DocPointer doc); - public static native TransactionPointer startTransactionLogPatches(DocPointer doc, PatchLogPointer patchLog); + public static native TransactionPointer startTransactionLogPatches(DocPointer doc, PatchLogPointer patchLog); - public static native TransactionPointer startTransactionAt(DocPointer doc, PatchLogPointer patchLog, - ChangeHash[] heads); + public static native TransactionPointer startTransactionAt(DocPointer doc, PatchLogPointer patchLog, + ChangeHash[] heads); - public static native byte[] encodeChangesSince(DocPointer doc, ChangeHash[] heads); + public static native byte[] encodeChangesSince(DocPointer doc, ChangeHash[] heads); - public static native void applyEncodedChanges(DocPointer doc, byte[] changes); + public static native void applyEncodedChanges(DocPointer doc, byte[] changes); - public static native void applyEncodedChangesLogPatches(DocPointer doc, PatchLogPointer patchLog, byte[] changes); + public static native void applyEncodedChangesLogPatches(DocPointer doc, PatchLogPointer patchLog, byte[] changes); - public static native ArrayList makePatches(DocPointer doc, PatchLogPointer patchLog); + public static native ArrayList makePatches(DocPointer doc, PatchLogPointer patchLog); - // Read methods - public static native Optional getInMapInDoc(DocPointer doc, ObjectId obj, String key); + // Read methods + public static native Optional getInMapInDoc(DocPointer doc, ObjectId obj, String key); - public static native Optional getInListInDoc(DocPointer doc, ObjectId obj, long index); + public static native Optional getInListInDoc(DocPointer doc, ObjectId obj, long index); - public static native Optional getInMapInTx(TransactionPointer tx, ObjectId obj, String key); + public static native Optional getInMapInTx(TransactionPointer tx, ObjectId obj, String key); - public static native Optional getInListInTx(TransactionPointer tx, ObjectId obj, long index); + public static native Optional getInListInTx(TransactionPointer tx, ObjectId obj, long index); - public static native Optional getAtInMapInDoc(DocPointer doc, ObjectId obj, String key, - ChangeHash[] heads); + public static native Optional getAtInMapInDoc(DocPointer doc, ObjectId obj, String key, + ChangeHash[] heads); - public static native Optional getAtInListInDoc(DocPointer doc, ObjectId obj, long index, - ChangeHash[] heads); + public static native Optional getAtInListInDoc(DocPointer doc, ObjectId obj, long index, + ChangeHash[] heads); - public static native Optional getAtInMapInTx(TransactionPointer tx, ObjectId obj, String key, - ChangeHash[] heads); + public static native Optional getAtInMapInTx(TransactionPointer tx, ObjectId obj, String key, + ChangeHash[] heads); - public static native Optional getAtInListInTx(TransactionPointer tx, ObjectId obj, long index, - ChangeHash[] heads); + public static native Optional getAtInListInTx(TransactionPointer tx, ObjectId obj, long index, + ChangeHash[] heads); - public static native Optional getAllInMapInDoc(DocPointer doc, ObjectId obj, String key); + public static native Optional getAllInMapInDoc(DocPointer doc, ObjectId obj, String key); - public static native Optional getAllInListInDoc(DocPointer doc, ObjectId obj, long idx); + public static native Optional getAllInListInDoc(DocPointer doc, ObjectId obj, long idx); - public static native Optional getAllInMapInTx(TransactionPointer tx, ObjectId obj, String key); + public static native Optional getAllInMapInTx(TransactionPointer tx, ObjectId obj, String key); - public static native Optional getAllInListInTx(TransactionPointer tx, ObjectId obj, long idx); + public static native Optional getAllInListInTx(TransactionPointer tx, ObjectId obj, long idx); - public static native Optional getAllAtInMapInDoc(DocPointer doc, ObjectId obj, String key, - ChangeHash[] heads); + public static native Optional getAllAtInMapInDoc(DocPointer doc, ObjectId obj, String key, + ChangeHash[] heads); - public static native Optional getAllAtInListInDoc(DocPointer doc, ObjectId obj, long idx, - ChangeHash[] heads); + public static native Optional getAllAtInListInDoc(DocPointer doc, ObjectId obj, long idx, + ChangeHash[] heads); - public static native Optional getAllAtInMapInTx(TransactionPointer tx, ObjectId obj, String key, - ChangeHash[] heads); + public static native Optional getAllAtInMapInTx(TransactionPointer tx, ObjectId obj, String key, + ChangeHash[] heads); - public static native Optional getAllAtInListInTx(TransactionPointer tx, ObjectId obj, long idx, - ChangeHash[] heads); + public static native Optional getAllAtInListInTx(TransactionPointer tx, ObjectId obj, long idx, + ChangeHash[] heads); - // Transaction mutation methods - // Set in map - public static native void setDoubleInMap(TransactionPointer tx, ObjectId obj, String key, double value); + // Transaction mutation methods + // Set in map + public static native void setDoubleInMap(TransactionPointer tx, ObjectId obj, String key, double value); - public static native void setBytesInMap(TransactionPointer tx, ObjectId obj, String key, byte[] value); + public static native void setBytesInMap(TransactionPointer tx, ObjectId obj, String key, byte[] value); - public static native void setStringInMap(TransactionPointer tx, ObjectId obj, String key, String value); + public static native void setStringInMap(TransactionPointer tx, ObjectId obj, String key, String value); - public static native void setIntInMap(TransactionPointer tx, ObjectId obj, String key, long value); + public static native void setIntInMap(TransactionPointer tx, ObjectId obj, String key, long value); - public static native void setUintInMap(TransactionPointer tx, ObjectId obj, String key, long value); + public static native void setUintInMap(TransactionPointer tx, ObjectId obj, String key, long value); - public static native void setBoolInMap(TransactionPointer tx, ObjectId obj, String key, boolean value); + public static native void setBoolInMap(TransactionPointer tx, ObjectId obj, String key, boolean value); - public static native void setCounterInMap(TransactionPointer tx, ObjectId obj, String key, long value); + public static native void setCounterInMap(TransactionPointer tx, ObjectId obj, String key, long value); - public static native void setDateInMap(TransactionPointer transactionPointer, ObjectId obj, String key, Date value); + public static native void setDateInMap(TransactionPointer transactionPointer, ObjectId obj, String key, Date value); - public static native void setNullInMap(TransactionPointer tx, ObjectId obj, String key); + public static native void setNullInMap(TransactionPointer tx, ObjectId obj, String key); - public static native ObjectId setObjectInMap(TransactionPointer tx, ObjectId obj, String key, ObjectType objType); + public static native ObjectId setObjectInMap(TransactionPointer tx, ObjectId obj, String key, ObjectType objType); - // Set in list - public static native void setDoubleInList(TransactionPointer tx, ObjectId obj, long idx, double value); + // Set in list + public static native void setDoubleInList(TransactionPointer tx, ObjectId obj, long idx, double value); - public static native void setIntInList(TransactionPointer tx, ObjectId obj, long idx, long value); + public static native void setIntInList(TransactionPointer tx, ObjectId obj, long idx, long value); - public static native void setUintInList(TransactionPointer tx, ObjectId obj, long idx, long value); + public static native void setUintInList(TransactionPointer tx, ObjectId obj, long idx, long value); - public static native void setStringInList(TransactionPointer tx, ObjectId obj, long idx, String value); + public static native void setStringInList(TransactionPointer tx, ObjectId obj, long idx, String value); - public static native void setBytesInList(TransactionPointer tx, ObjectId obj, long idx, byte[] value); + public static native void setBytesInList(TransactionPointer tx, ObjectId obj, long idx, byte[] value); - public static native void setBoolInList(TransactionPointer tx, ObjectId obj, long idx, boolean value); + public static native void setBoolInList(TransactionPointer tx, ObjectId obj, long idx, boolean value); - public static native void setDateInList(TransactionPointer tx, ObjectId obj, long idx, Date value); + public static native void setDateInList(TransactionPointer tx, ObjectId obj, long idx, Date value); - public static native void setCounterInList(TransactionPointer tx, ObjectId obj, long idx, long value); + public static native void setCounterInList(TransactionPointer tx, ObjectId obj, long idx, long value); - public static native void setNullInList(TransactionPointer tx, ObjectId obj, long idx); + public static native void setNullInList(TransactionPointer tx, ObjectId obj, long idx); - public static native ObjectId setObjectInList(TransactionPointer tx, ObjectId obj, long idx, ObjectType objType); + public static native ObjectId setObjectInList(TransactionPointer tx, ObjectId obj, long idx, ObjectType objType); - // Insert in list - public static native void insertDoubleInList(TransactionPointer tx, ObjectId obj, long index, double value); + // Insert in list + public static native void insertDoubleInList(TransactionPointer tx, ObjectId obj, long index, double value); - public static native void insertStringInList(TransactionPointer tx, ObjectId obj, long index, String value); + public static native void insertStringInList(TransactionPointer tx, ObjectId obj, long index, String value); - public static native void insertIntInList(TransactionPointer tx, ObjectId obj, long index, long value); + public static native void insertIntInList(TransactionPointer tx, ObjectId obj, long index, long value); - public static native void insertBytesInList(TransactionPointer tx, ObjectId obj, long index, byte[] value); + public static native void insertBytesInList(TransactionPointer tx, ObjectId obj, long index, byte[] value); - public static native void insertUintInList(TransactionPointer tx, ObjectId obj, long index, long value); + public static native void insertUintInList(TransactionPointer tx, ObjectId obj, long index, long value); - public static native void insertNullInList(TransactionPointer tx, ObjectId obj, long index); + public static native void insertNullInList(TransactionPointer tx, ObjectId obj, long index); - public static native void insertCounterInList(TransactionPointer transactionPointer, ObjectId obj, long index, - long value); + public static native void insertCounterInList(TransactionPointer transactionPointer, ObjectId obj, long index, + long value); - public static native void insertDateInList(TransactionPointer transactionPointer, ObjectId obj, long index, - Date value); + public static native void insertDateInList(TransactionPointer transactionPointer, ObjectId obj, long index, + Date value); - public static native void insertBoolInList(TransactionPointer transactionPointer, ObjectId obj, long index, - boolean value); + public static native void insertBoolInList(TransactionPointer transactionPointer, ObjectId obj, long index, + boolean value); - public static native ObjectId insertObjectInList(TransactionPointer tx, ObjectId obj, long index, - ObjectType objType); + public static native ObjectId insertObjectInList(TransactionPointer tx, ObjectId obj, long index, + ObjectType objType); - // Increment - public static native void incrementInMap(TransactionPointer tx, ObjectId obj, String key, long value); + // Increment + public static native void incrementInMap(TransactionPointer tx, ObjectId obj, String key, long value); - public static native void incrementInList(TransactionPointer tx, ObjectId obj, long idx, long value); + public static native void incrementInList(TransactionPointer tx, ObjectId obj, long idx, long value); - // Delete - public static native void deleteInMap(TransactionPointer tx, ObjectId obj, String key); + // Delete + public static native void deleteInMap(TransactionPointer tx, ObjectId obj, String key); - public static native void deleteInList(TransactionPointer tx, ObjectId obj, long idx); + public static native void deleteInList(TransactionPointer tx, ObjectId obj, long idx); - // Splice - public static native void splice(TransactionPointer tx, ObjectId obj, long start, long deleteCount, - Iterator values); + // Splice + public static native void splice(TransactionPointer tx, ObjectId obj, long start, long deleteCount, + Iterator values); - // Text - public static native void spliceText(TransactionPointer tx, ObjectId obj, long start, long deleteCount, - String text); + // Text + public static native void spliceText(TransactionPointer tx, ObjectId obj, long start, long deleteCount, + String text); - public static native Optional getTextInDoc(DocPointer pointer, ObjectId obj); + public static native Optional getTextInDoc(DocPointer pointer, ObjectId obj); - public static native Optional getTextInTx(TransactionPointer pointer, ObjectId obj); + public static native Optional getTextInTx(TransactionPointer pointer, ObjectId obj); - public static native Optional getTextAtInDoc(DocPointer pointer, ObjectId obj, ChangeHash[] heads); + public static native Optional getTextAtInDoc(DocPointer pointer, ObjectId obj, ChangeHash[] heads); - public static native Optional getTextAtInTx(TransactionPointer pointer, ObjectId obj, ChangeHash[] heads); + public static native Optional getTextAtInTx(TransactionPointer pointer, ObjectId obj, ChangeHash[] heads); - // Keys - public static native Optional getKeysInTx(TransactionPointer tx, ObjectId obj); + // Keys + public static native Optional getKeysInTx(TransactionPointer tx, ObjectId obj); - public static native Optional getKeysInDoc(DocPointer doc, ObjectId obj); + public static native Optional getKeysInDoc(DocPointer doc, ObjectId obj); - public static native Optional getKeysAtInTx(TransactionPointer tx, ObjectId obj, ChangeHash[] heads); + public static native Optional getKeysAtInTx(TransactionPointer tx, ObjectId obj, ChangeHash[] heads); - public static native Optional getKeysAtInDoc(DocPointer doc, ObjectId obj, ChangeHash[] heads); + public static native Optional getKeysAtInDoc(DocPointer doc, ObjectId obj, ChangeHash[] heads); - // Map entries - public static native Optional getMapEntriesInTx(TransactionPointer tx, ObjectId obj); + // Map entries + public static native Optional getMapEntriesInTx(TransactionPointer tx, ObjectId obj); - public static native Optional getMapEntriesInDoc(DocPointer doc, ObjectId obj); + public static native Optional getMapEntriesInDoc(DocPointer doc, ObjectId obj); - public static native Optional getMapEntriesAtInTx(TransactionPointer tx, ObjectId obj, - ChangeHash[] heads); + public static native Optional getMapEntriesAtInTx(TransactionPointer tx, ObjectId obj, + ChangeHash[] heads); - public static native Optional getMapEntriesAtInDoc(DocPointer doc, ObjectId obj, ChangeHash[] heads); + public static native Optional getMapEntriesAtInDoc(DocPointer doc, ObjectId obj, ChangeHash[] heads); - // List items - public static native Optional getListItemsInTx(TransactionPointer tx, ObjectId obj); + // List items + public static native Optional getListItemsInTx(TransactionPointer tx, ObjectId obj); - public static native Optional getListItemsInDoc(DocPointer doc, ObjectId obj); + public static native Optional getListItemsInDoc(DocPointer doc, ObjectId obj); - public static native Optional getListItemsAtInTx(TransactionPointer tx, ObjectId obj, - ChangeHash[] heads); + public static native Optional getListItemsAtInTx(TransactionPointer tx, ObjectId obj, + ChangeHash[] heads); - public static native Optional getListItemsAtInDoc(DocPointer doc, ObjectId obj, ChangeHash[] heads); + public static native Optional getListItemsAtInDoc(DocPointer doc, ObjectId obj, ChangeHash[] heads); - public static native long getListLengthInTx(TransactionPointer tx, ObjectId obj); + public static native long getListLengthInTx(TransactionPointer tx, ObjectId obj); - public static native long getListLengthInDoc(DocPointer doc, ObjectId obj); + public static native long getListLengthInDoc(DocPointer doc, ObjectId obj); - public static native long getListLengthAtInTx(TransactionPointer tx, ObjectId obj, ChangeHash[] heads); + public static native long getListLengthAtInTx(TransactionPointer tx, ObjectId obj, ChangeHash[] heads); - public static native long getListLengthAtInDoc(DocPointer doc, ObjectId obj, ChangeHash[] heads); + public static native long getListLengthAtInDoc(DocPointer doc, ObjectId obj, ChangeHash[] heads); - // Marks - public static native List getMarksInDoc(DocPointer doc, ObjectId obj, Optional heads); + // Marks + public static native List getMarksInDoc(DocPointer doc, ObjectId obj, Optional heads); - public static native List getMarksInTx(TransactionPointer tx, ObjectId obj, Optional heads); + public static native List getMarksInTx(TransactionPointer tx, ObjectId obj, Optional heads); - public static native HashMap getMarksAtIndexInDoc(DocPointer doc, ObjectId obj, long index, - Optional heads); + public static native HashMap getMarksAtIndexInDoc(DocPointer doc, ObjectId obj, long index, + Optional heads); - public static native HashMap getMarksAtIndexInTx(TransactionPointer tx, ObjectId obj, long index, - Optional heads); + public static native HashMap getMarksAtIndexInTx(TransactionPointer tx, ObjectId obj, long index, + Optional heads); - public static native void markUint(TransactionPointer tx, ObjectId obj, String name, long start, long end, - long value, ExpandMark expand); + public static native void markUint(TransactionPointer tx, ObjectId obj, String name, long start, long end, + long value, ExpandMark expand); - public static native void markInt(TransactionPointer tx, ObjectId obj, String name, long start, long end, - long value, ExpandMark expand); + public static native void markInt(TransactionPointer tx, ObjectId obj, String name, long start, long end, + long value, ExpandMark expand); - public static native void markDouble(TransactionPointer tx, ObjectId obj, String name, long start, long end, - double value, ExpandMark expand); + public static native void markDouble(TransactionPointer tx, ObjectId obj, String name, long start, long end, + double value, ExpandMark expand); - public static native void markBytes(TransactionPointer tx, ObjectId obj, String name, long start, long end, - byte[] value, ExpandMark expand); + public static native void markBytes(TransactionPointer tx, ObjectId obj, String name, long start, long end, + byte[] value, ExpandMark expand); - public static native void markString(TransactionPointer tx, ObjectId obj, String name, long start, long end, - String value, ExpandMark expand); + public static native void markString(TransactionPointer tx, ObjectId obj, String name, long start, long end, + String value, ExpandMark expand); - public static native void markCounter(TransactionPointer tx, ObjectId obj, String name, long start, long end, - long value, ExpandMark expand); + public static native void markCounter(TransactionPointer tx, ObjectId obj, String name, long start, long end, + long value, ExpandMark expand); - public static native void markDate(TransactionPointer tx, ObjectId obj, String name, long start, long end, - Date value, ExpandMark expand); + public static native void markDate(TransactionPointer tx, ObjectId obj, String name, long start, long end, + Date value, ExpandMark expand); - public static native void markBool(TransactionPointer tx, ObjectId obj, String name, long start, long end, - boolean value, ExpandMark expand); + public static native void markBool(TransactionPointer tx, ObjectId obj, String name, long start, long end, + boolean value, ExpandMark expand); - public static native void markNull(TransactionPointer tx, ObjectId obj, String name, long start, long end, - ExpandMark expand); + public static native void markNull(TransactionPointer tx, ObjectId obj, String name, long start, long end, + ExpandMark expand); - public static native void unMark(TransactionPointer tx, ObjectId obj, String name, long start, long end, - ExpandMark expand); + public static native void unMark(TransactionPointer tx, ObjectId obj, String name, long start, long end, + ExpandMark expand); - // Transactions - public static native CommitResult commitTransaction(TransactionPointer tx); + // Transactions + public static native CommitResult commitTransaction(TransactionPointer tx); - public static native void rollbackTransaction(TransactionPointer tx); + public static native void rollbackTransaction(TransactionPointer tx); - // Heads - public static native ChangeHash[] getHeadsInDoc(DocPointer doc); + // Heads + public static native ChangeHash[] getHeadsInDoc(DocPointer doc); - public static native ChangeHash[] getHeadsInTx(TransactionPointer tx); + public static native ChangeHash[] getHeadsInTx(TransactionPointer tx); - // Object ID methods - public static native ObjectId rootObjectId(); + // Object ID methods + public static native ObjectId rootObjectId(); - public static native boolean isRootObjectId(ObjectId obj); + public static native boolean isRootObjectId(ObjectId obj); - public static native String objectIdToString(ObjectId obj); + public static native String objectIdToString(ObjectId obj); - public static native boolean objectIdsEqual(ObjectId left, ObjectId right); + public static native boolean objectIdsEqual(ObjectId left, ObjectId right); - public static native int objectIdHash(ObjectId left); + public static native int objectIdHash(ObjectId left); - // Sync - public static native SyncStatePointer createSyncState(); + // Sync + public static native SyncStatePointer createSyncState(); - public static native Optional generateSyncMessage(SyncStatePointer syncState, DocPointer doc); + public static native Optional generateSyncMessage(SyncStatePointer syncState, DocPointer doc); - public static native void receiveSyncMessage(SyncStatePointer syncState, DocPointer doc, byte[] message); + public static native void receiveSyncMessage(SyncStatePointer syncState, DocPointer doc, byte[] message); - public static native void receiveSyncMessageLogPatches(SyncStatePointer syncState, DocPointer doc, - PatchLogPointer patchLog, byte[] message); + public static native void receiveSyncMessageLogPatches(SyncStatePointer syncState, DocPointer doc, + PatchLogPointer patchLog, byte[] message); - public static native SyncStatePointer decodeSyncState(byte[] encoded); + public static native SyncStatePointer decodeSyncState(byte[] encoded); - public static native byte[] encodeSyncState(SyncStatePointer syncState); + public static native byte[] encodeSyncState(SyncStatePointer syncState); - public static native void freeSyncState(SyncStatePointer syncState); + public static native void freeSyncState(SyncStatePointer syncState); - public static native ChangeHash[] syncStateSharedHeads(SyncStatePointer syncState); + public static native ChangeHash[] syncStateSharedHeads(SyncStatePointer syncState); - public static native PatchLogPointer createPatchLog(); + public static native PatchLogPointer createPatchLog(); - public static native void freePatchLog(PatchLogPointer pointer); + public static native void freePatchLog(PatchLogPointer pointer); - public static native ArrayList diff(DocPointer doc, ChangeHash[] before, ChangeHash[] after); + public static native ArrayList diff(DocPointer doc, ChangeHash[] before, ChangeHash[] after); - public static native Cursor makeCursorInDoc(DocPointer doc, ObjectId obj, long index, Optional heads); + public static native Cursor makeCursorInDoc(DocPointer doc, ObjectId obj, long index, Optional heads); - public static native Cursor makeCursorInTx(TransactionPointer tx, ObjectId obj, long index, - Optional heads); + public static native Cursor makeCursorInTx(TransactionPointer tx, ObjectId obj, long index, + Optional heads); - public static native long lookupCursorIndexInDoc(DocPointer doc, ObjectId obj, Cursor cursor, - Optional heads); + public static native long lookupCursorIndexInDoc(DocPointer doc, ObjectId obj, Cursor cursor, + Optional heads); - public static native long lookupCursorIndexInTx(TransactionPointer tx, ObjectId obj, Cursor cursor, - Optional heads); + public static native long lookupCursorIndexInTx(TransactionPointer tx, ObjectId obj, Cursor cursor, + Optional heads); - public static native String cursorToString(Cursor cursor); + public static native String cursorToString(Cursor cursor); - public static native Cursor cursorFromString(String encoded); + public static native Cursor cursorFromString(String encoded); - public static native Cursor cursorFromBytes(byte[] encoded); + public static native Cursor cursorFromBytes(byte[] encoded); - public static native Optional getObjectTypeInDoc(DocPointer doc, ObjectId obj); + public static native Optional getObjectTypeInDoc(DocPointer doc, ObjectId obj); - public static native Optional getObjectTypeInTx(TransactionPointer tx, ObjectId obj); + public static native Optional getObjectTypeInTx(TransactionPointer tx, ObjectId obj); } diff --git a/lib/src/main/java/org/automerge/ChangeHash.java b/lib/src/main/java/org/automerge/ChangeHash.java index 67617dd..65c5fca 100644 --- a/lib/src/main/java/org/automerge/ChangeHash.java +++ b/lib/src/main/java/org/automerge/ChangeHash.java @@ -4,38 +4,38 @@ /** The hash of a single change to an automerge document */ public class ChangeHash { - private byte[] hash; + private byte[] hash; - protected ChangeHash(byte[] hash) { - this.hash = hash; - } + protected ChangeHash(byte[] hash) { + this.hash = hash; + } - /** - * @return the bytes of the hash - */ - public byte[] getBytes() { - return hash.clone(); - } + /** + * @return the bytes of the hash + */ + public byte[] getBytes() { + return hash.clone(); + } - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + Arrays.hashCode(hash); - return result; - } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + Arrays.hashCode(hash); + return result; + } - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - ChangeHash other = (ChangeHash) obj; - if (!Arrays.equals(hash, other.hash)) - return false; - return true; - } + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + ChangeHash other = (ChangeHash) obj; + if (!Arrays.equals(hash, other.hash)) + return false; + return true; + } } diff --git a/lib/src/main/java/org/automerge/CommitResult.java b/lib/src/main/java/org/automerge/CommitResult.java index b37e66f..ec50cf6 100644 --- a/lib/src/main/java/org/automerge/CommitResult.java +++ b/lib/src/main/java/org/automerge/CommitResult.java @@ -3,19 +3,20 @@ import java.util.Optional; class CommitResult { - private Optional hash; - private AutomergeSys.PatchLogPointer patchLog; + private Optional hash; + private AutomergeSys.PatchLogPointer patchLog; - protected CommitResult(Optional hash, AutomergeSys.PatchLogPointer patchLog) { - this.hash = hash; - this.patchLog = patchLog; - } + protected CommitResult(Optional hash, AutomergeSys.PatchLogPointer patchLog) { + this.hash = hash; + this.patchLog = patchLog; + } - protected Optional getHash() { - return hash; - } - protected AutomergeSys.PatchLogPointer getPatchLog() { - return patchLog; - } + protected Optional getHash() { + return hash; + } + + protected AutomergeSys.PatchLogPointer getPatchLog() { + return patchLog; + } } diff --git a/lib/src/main/java/org/automerge/Conflicts.java b/lib/src/main/java/org/automerge/Conflicts.java index 3bb5055..a221445 100644 --- a/lib/src/main/java/org/automerge/Conflicts.java +++ b/lib/src/main/java/org/automerge/Conflicts.java @@ -4,9 +4,9 @@ import java.util.HashMap; class Conflicts { - private HashMap values; + private HashMap values; - public Collection values() { - return this.values.values(); - } + public Collection values() { + return this.values.values(); + } } diff --git a/lib/src/main/java/org/automerge/Counter.java b/lib/src/main/java/org/automerge/Counter.java index f8eb09f..672ca3d 100644 --- a/lib/src/main/java/org/automerge/Counter.java +++ b/lib/src/main/java/org/automerge/Counter.java @@ -1,35 +1,35 @@ package org.automerge; class Counter { - private final long value; + private final long value; - public Counter(long value) { - this.value = value; - } + public Counter(long value) { + this.value = value; + } - public long getValue() { - return this.value; - } + public long getValue() { + return this.value; + } - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + (int) (value ^ (value >>> 32)); - return result; - } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (int) (value ^ (value >>> 32)); + return result; + } - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - Counter other = (Counter) obj; - if (value != other.value) - return false; - return true; - } + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Counter other = (Counter) obj; + if (value != other.value) + return false; + return true; + } } diff --git a/lib/src/main/java/org/automerge/Cursor.java b/lib/src/main/java/org/automerge/Cursor.java index 1058ea3..5eb27d1 100644 --- a/lib/src/main/java/org/automerge/Cursor.java +++ b/lib/src/main/java/org/automerge/Cursor.java @@ -17,57 +17,57 @@ * important you should use the string methods. */ public class Cursor { - private byte[] raw; + private byte[] raw; - /** - * Parse the output of {@link toBytes()} - * - * @param encoded - * the output of {@link toBytes()} - * - * @throws IllegalArgumentException - * if the input is not a valid cursor - * - * @return the parsed cursor - */ - public static Cursor fromBytes(byte[] encoded) { - return AutomergeSys.cursorFromBytes(encoded); - } + /** + * Parse the output of {@link toBytes()} + * + * @param encoded + * the output of {@link toBytes()} + * + * @throws IllegalArgumentException + * if the input is not a valid cursor + * + * @return the parsed cursor + */ + public static Cursor fromBytes(byte[] encoded) { + return AutomergeSys.cursorFromBytes(encoded); + } - /** - * Parse the output of {@link toString()} - * - * @param encoded - * the output of {@link toString()} - * - * @throws IllegalArgumentException - * if the input is not a valid cursor - * - * @return the parsed cursor - */ - public static Cursor fromString(String encoded) { - return AutomergeSys.cursorFromString(encoded); - } + /** + * Parse the output of {@link toString()} + * + * @param encoded + * the output of {@link toString()} + * + * @throws IllegalArgumentException + * if the input is not a valid cursor + * + * @return the parsed cursor + */ + public static Cursor fromString(String encoded) { + return AutomergeSys.cursorFromString(encoded); + } - /** - * Encode a cursor as a string - *

- * This encoding is interoperable with cursors produced by the JavaScript - * implementation. - * - * @return the encoded cursor - */ - @Override - public String toString() { - return AutomergeSys.cursorToString(this); - } + /** + * Encode a cursor as a string + *

+ * This encoding is interoperable with cursors produced by the JavaScript + * implementation. + * + * @return the encoded cursor + */ + @Override + public String toString() { + return AutomergeSys.cursorToString(this); + } - /** - * Encode a cursor as a byte array - * - * @return the encoded cursor - */ - public byte[] toBytes() { - return raw.clone(); - } + /** + * Encode a cursor as a byte array + * + * @return the encoded cursor + */ + public byte[] toBytes() { + return raw.clone(); + } } diff --git a/lib/src/main/java/org/automerge/Document.java b/lib/src/main/java/org/automerge/Document.java index dfd4a19..eaaf089 100644 --- a/lib/src/main/java/org/automerge/Document.java +++ b/lib/src/main/java/org/automerge/Document.java @@ -68,659 +68,659 @@ * many times it may be worth reusing actor IDs. */ public class Document implements Read { - private Optional pointer; - // Keep actor ID here so we a) don't have to keep passing it across the JNI - // boundary and b) can access it when a transaction is in progress - private byte[] actorId; - // If a transaction is in progress we must forward all calls to the transaction. - // In rust code the transaction holds a mutable reference to the document, so - // any - // calls to the document whilst the transaction exists would be unsafe - private Optional transactionPtr; - - /** Create a new document with a random actor ID */ - public Document() { - LoadLibrary.initialize(); - this.pointer = Optional.of(AutomergeSys.createDoc()); - this.actorId = AutomergeSys.getActorId(this.pointer.get()); - this.transactionPtr = Optional.empty(); - } - - /** - * Create a new document with a specific actor ID - * - * @param actorId - * the actor ID to use for this document - */ - public Document(byte[] actorId) { - LoadLibrary.initialize(); - this.actorId = actorId; - this.pointer = Optional.of(AutomergeSys.createDocWithActor(actorId)); - this.transactionPtr = Optional.empty(); - } - - private Document(DocPointer pointer) { - LoadLibrary.initialize(); - this.pointer = Optional.of(pointer); - this.actorId = AutomergeSys.getActorId(this.pointer.get()); - this.transactionPtr = Optional.empty(); - } - - /** - * Get the actor ID for this document - * - * @return the actor ID for this document - */ - public byte[] getActorId() { - return this.actorId; - } - - /** - * Free the memory associated with this document - * - *

- * Once this method has been called any further attempts to interact with the - * document will raise an exception. This call itself is idempotent though, so - * it's safe to call mutliple times. - */ - public synchronized void free() { - if (this.transactionPtr.isPresent()) { - throw new TransactionInProgress(); - } - if (this.pointer.isPresent()) { - AutomergeSys.freeDoc(this.pointer.get()); - this.pointer = Optional.empty(); - } - } - - /** - * Load a document from disk - * - *

- * This can be used to load bytes produced by {@link save} or - * {@link encodeChangesSince} - * - * @param bytes - * The bytes of the document to load - * @return The loaded document - */ - public static Document load(byte[] bytes) { - LoadLibrary.initialize(); - return new Document(AutomergeSys.loadDoc(bytes)); - } - - /** - * Save a document - * - *

- * The saved document can be loaded again with {@link load} - * - * @return The bytes of the saved document - */ - public synchronized byte[] save() { - if (this.transactionPtr.isPresent()) { - throw new TransactionInProgress(); - } - return AutomergeSys.saveDoc(this.pointer.get()); - } - - /** - * Create a copy of this document with a new random actor ID - * - * @return The new document - */ - public synchronized Document fork() { - if (this.transactionPtr.isPresent()) { - throw new TransactionInProgress(); - } - return new Document(AutomergeSys.forkDoc(this.pointer.get())); - } - - /** - * Create a copy of this document with the given actor ID - * - * @param newActor - * The actor ID to use for the new document - * @return The new document - */ - public synchronized Document fork(byte[] newActor) { - if (this.transactionPtr.isPresent()) { - throw new TransactionInProgress(); - } - return new Document(AutomergeSys.forkDocWithActor(this.pointer.get(), newActor)); - } - - /** - * Create a copy of this document as at the given heads - * - * @param heads - * The heads to fork the document at - * @return The new document - */ - public synchronized Document fork(ChangeHash[] heads) { - if (this.transactionPtr.isPresent()) { - throw new TransactionInProgress(); - } - return new Document(AutomergeSys.forkDocAt(this.pointer.get(), heads)); - } - - /** - * Create a copy of this document as at the given heads with the given actor ID - * - * @param heads - * The heads to fork the document at - * @param newActor - * The actor ID to use for the new document - * @return The new document - */ - public synchronized Document fork(ChangeHash[] heads, byte[] newActor) { - if (this.transactionPtr.isPresent()) { - throw new TransactionInProgress(); - } - return new Document(AutomergeSys.forkDocAtWithActor(this.pointer.get(), heads, newActor)); - } - - /** - * Merge another document into this one - * - * @param other - * The document to merge into this one - * @throws TransactionInProgress - * if there is a transaction in progress on this document or on the - * other document - */ - public synchronized void merge(Document other) { - if (this.transactionPtr.isPresent() || other.transactionPtr.isPresent()) { - throw new TransactionInProgress(); - } - AutomergeSys.mergeDoc(this.pointer.get(), other.pointer.get()); - } - - /** - * Merge another document into this one logging patches - * - * @param other - * The document to merge into this one - * @param patchLog - * The patch log in which to record any changes to the current state - * which occur as a result of the merge - * @throws TransactionInProgress - * if there is a transaction in progress on this document or on the - * other document - */ - public synchronized void merge(Document other, PatchLog patchLog) { - if (this.transactionPtr.isPresent() || other.transactionPtr.isPresent()) { - throw new TransactionInProgress(); - } - patchLog.with((pointer) -> { - AutomergeSys.mergeDocLogPatches(this.pointer.get(), other.pointer.get(), pointer); - }); - } - - /** - * Encode changes since the given heads - * - *

- * The encoded changes this method returns can be used in - * {@link applyEncodedChanges} - * - * @param heads - * The heads to encode changes since - * @return The encoded changes - */ - public synchronized byte[] encodeChangesSince(ChangeHash[] heads) { - if (this.transactionPtr.isPresent()) { - throw new TransactionInProgress(); - } - return AutomergeSys.encodeChangesSince(this.pointer.get(), heads); - } - - /** - * Incorporate changes from another document into this document - * - * @param changes - * The changes to incorporate. Produced by {@link encodeChangesSince} - * or {@link save} - * @throws TransactionInProgress - * if a transaction is in progress - * @throws AutomergeException - * if the changes are not valid - */ - public synchronized void applyEncodedChanges(byte[] changes) { - if (this.transactionPtr.isPresent()) { - throw new TransactionInProgress(); - } - AutomergeSys.applyEncodedChanges(this.pointer.get(), changes); - } - - /** - * The same as {@link applyEncodedChanges} but logs any changes to the current - * state that result from applying the change in the given patch log - * - *

- * Creating patches does imply a performance penalty, so if you don't need them - * you should use {@link applyEncodedChanges} - * - * @param changes - * The changes to incorporate. Produced by {@link encodeChangesSince} - * or {@link save} - * @param patchLog - * The patch log in which to record any changes to the current state - * @throws TransactionInProgress - * if a transaction is in progress - * @throws AutomergeException - * if the changes are not valid - */ - public synchronized void applyEncodedChanges(byte[] changes, PatchLog patchLog) { - if (this.transactionPtr.isPresent()) { - throw new TransactionInProgress(); - } - patchLog.with((AutomergeSys.PatchLogPointer patchLogPointer) -> AutomergeSys - .applyEncodedChangesLogPatches(this.pointer.get(), patchLogPointer, changes)); - } - - public synchronized Optional get(ObjectId obj, String key) { - if (this.transactionPtr.isPresent()) { - return AutomergeSys.getInMapInTx(this.transactionPtr.get(), obj, key); - } else { - return AutomergeSys.getInMapInDoc(this.pointer.get(), obj, key); - } - } - - public synchronized Optional get(ObjectId obj, long key) { - if (this.transactionPtr.isPresent()) { - return AutomergeSys.getInListInTx(this.transactionPtr.get(), obj, key); - } else { - return AutomergeSys.getInListInDoc(this.pointer.get(), obj, key); - } - } - - public synchronized Optional get(ObjectId obj, String key, ChangeHash[] heads) { - if (this.transactionPtr.isPresent()) { - return AutomergeSys.getAtInMapInTx(this.transactionPtr.get(), obj, key, heads); - } else { - return AutomergeSys.getAtInMapInDoc(this.pointer.get(), obj, key, heads); - } - } - - public synchronized Optional get(ObjectId obj, long idx, ChangeHash[] heads) { - if (this.transactionPtr.isPresent()) { - return AutomergeSys.getAtInListInTx(this.transactionPtr.get(), obj, idx, heads); - } else { - return AutomergeSys.getAtInListInDoc(this.pointer.get(), obj, idx, heads); - } - } - - public synchronized Optional getAll(ObjectId obj, String key) { - if (this.transactionPtr.isPresent()) { - return AutomergeSys.getAllInMapInTx(this.transactionPtr.get(), obj, key); - } else { - return AutomergeSys.getAllInMapInDoc(this.pointer.get(), obj, key); - } - } - - public synchronized Optional getAll(ObjectId obj, long idx) { - if (this.transactionPtr.isPresent()) { - return AutomergeSys.getAllInListInTx(this.transactionPtr.get(), obj, idx); - } else { - return AutomergeSys.getAllInListInDoc(this.pointer.get(), obj, idx); - } - } - - public synchronized Optional getAll(ObjectId obj, String key, ChangeHash[] heads) { - if (this.transactionPtr.isPresent()) { - return AutomergeSys.getAllAtInMapInTx(this.transactionPtr.get(), obj, key, heads); - } else { - return AutomergeSys.getAllAtInMapInDoc(this.pointer.get(), obj, key, heads); - } - } - - public synchronized Optional getAll(ObjectId obj, long idx, ChangeHash[] heads) { - if (this.transactionPtr.isPresent()) { - return AutomergeSys.getAllAtInListInTx(this.transactionPtr.get(), obj, idx, heads); - } else { - return AutomergeSys.getAllAtInListInDoc(this.pointer.get(), obj, idx, heads); - } - } - - public synchronized Optional text(ObjectId obj) { - if (this.transactionPtr.isPresent()) { - return AutomergeSys.getTextInTx(this.transactionPtr.get(), obj); - } else { - return AutomergeSys.getTextInDoc(this.pointer.get(), obj); - } - } - - public synchronized Optional text(ObjectId obj, ChangeHash[] heads) { - if (this.transactionPtr.isPresent()) { - return AutomergeSys.getTextAtInTx(this.transactionPtr.get(), obj, heads); - } else { - return AutomergeSys.getTextAtInDoc(this.pointer.get(), obj, heads); - } - } - - public synchronized Optional keys(ObjectId obj) { - if (this.transactionPtr.isPresent()) { - return AutomergeSys.getKeysInTx(this.transactionPtr.get(), obj); - } else { - return AutomergeSys.getKeysInDoc(this.pointer.get(), obj); - } - } - - public synchronized Optional keys(ObjectId obj, ChangeHash[] heads) { - if (this.transactionPtr.isPresent()) { - return AutomergeSys.getKeysAtInTx(this.transactionPtr.get(), obj, heads); - } else { - return AutomergeSys.getKeysAtInDoc(this.pointer.get(), obj, heads); - } - } - - public synchronized Optional mapEntries(ObjectId obj) { - if (this.transactionPtr.isPresent()) { - return AutomergeSys.getMapEntriesInTx(this.transactionPtr.get(), obj); - } else { - return AutomergeSys.getMapEntriesInDoc(this.pointer.get(), obj); - } - } - - public synchronized Optional mapEntries(ObjectId obj, ChangeHash[] heads) { - if (this.transactionPtr.isPresent()) { - return AutomergeSys.getMapEntriesAtInTx(this.transactionPtr.get(), obj, heads); - } else { - return AutomergeSys.getMapEntriesAtInDoc(this.pointer.get(), obj, heads); - } - } - - public synchronized long length(ObjectId obj) { - if (this.transactionPtr.isPresent()) { - return AutomergeSys.getListLengthInTx(this.transactionPtr.get(), obj); - } else { - return AutomergeSys.getListLengthInDoc(this.pointer.get(), obj); - } - } - - public synchronized long length(ObjectId obj, ChangeHash[] heads) { - if (this.transactionPtr.isPresent()) { - return AutomergeSys.getListLengthAtInTx(this.transactionPtr.get(), obj, heads); - } else { - return AutomergeSys.getListLengthAtInDoc(this.pointer.get(), obj, heads); - } - } - - /** - * Start a transaction to change this document - * - *

- * There can only be one active transaction per document. Any method which - * mutates the document (e.g. {@link merge} or {@link receiveSyncMessage} will - * throw an exception if a transaction is in progress. Therefore keep - * transactions short lived. - * - * @return a new transaction - * @throws TransactionInProgress - * if a transaction is already in progress - */ - public synchronized Transaction startTransaction() { - if (this.transactionPtr.isPresent()) { - throw new TransactionInProgress(); - } - AutomergeSys.TransactionPointer ptr = AutomergeSys.startTransaction(this.pointer.get()); - this.transactionPtr = Optional.of(ptr); - return new TransactionImpl(this, ptr); - } - - /** - * Start a transaction to change this document which logs changes in a patch log - * - *

- * There can only be one active transaction per document. Any method which - * mutates the document (e.g. {@link merge} or {@link receiveSyncMessage} will - * throw an exception if a transaction is in progress. Therefore keep - * transactions short lived. - * - * @param patchLog - * the {@link PatchLog} to log changes to - * @return a new transaction - * @throws TransactionInProgress - * if a transaction is already in progress - */ - public synchronized Transaction startTransaction(PatchLog patchLog) { - if (this.transactionPtr.isPresent()) { - throw new TransactionInProgress(); - } - AutomergeSys.PatchLogPointer patchLogPointer = patchLog.take(); - AutomergeSys.TransactionPointer ptr = AutomergeSys.startTransactionLogPatches(this.pointer.get(), - patchLogPointer); - this.transactionPtr = Optional.of(ptr); - return new TransactionImpl(this, ptr, (AutomergeSys.PatchLogPointer returnedPointer) -> { - patchLog.put(returnedPointer); - }); - } - - /** - * Start a transaction to change this document based on the document at a given - * heads - * - *

- * There can only be one active transaction per document. Any method which - * mutates the document (e.g. {@link merge} or {@link receiveSyncMessage} will - * throw an exception if a transaction is in progress. Therefore keep - * transactions short lived. - * - * @param patchLog - * the {@link PatchLog} to log changes to. Note that the the changes - * logged here will represent changes from the state as at the given - * heads, not the state of the document when calling this method. - * @param heads - * the heads to begin the transaction at - * - * @return a new transaction - * @throws TransactionInProgress - * if a transaction is already in progress - */ - public synchronized Transaction startTransactionAt(PatchLog patchLog, ChangeHash[] heads) { - if (this.transactionPtr.isPresent()) { - throw new TransactionInProgress(); - } - AutomergeSys.TransactionPointer ptr = AutomergeSys.startTransactionAt(this.pointer.get(), patchLog.take(), - heads); - return new TransactionImpl(this, ptr, (AutomergeSys.PatchLogPointer returnedPointer) -> { - patchLog.put(returnedPointer); - }); - } - - public synchronized Optional listItems(ObjectId obj) { - if (this.transactionPtr.isPresent()) { - return AutomergeSys.getListItemsInTx(this.transactionPtr.get(), obj); - } else { - return AutomergeSys.getListItemsInDoc(this.pointer.get(), obj); - } - } - - public synchronized Optional listItems(ObjectId obj, ChangeHash[] heads) { - if (this.transactionPtr.isPresent()) { - return AutomergeSys.getListItemsAtInTx(this.transactionPtr.get(), obj, heads); - } else { - return AutomergeSys.getListItemsAtInDoc(this.pointer.get(), obj, heads); - } - } - - public synchronized ChangeHash[] getHeads() { - if (this.transactionPtr.isPresent()) { - return AutomergeSys.getHeadsInTx(this.transactionPtr.get()); - } else { - return AutomergeSys.getHeadsInDoc(this.pointer.get()); - } - } - - public synchronized List marks(ObjectId obj) { - if (this.transactionPtr.isPresent()) { - return AutomergeSys.getMarksInTx(this.transactionPtr.get(), obj, Optional.empty()); - } else { - return AutomergeSys.getMarksInDoc(this.pointer.get(), obj, Optional.empty()); - } - } - - public synchronized List marks(ObjectId obj, ChangeHash[] heads) { - if (this.transactionPtr.isPresent()) { - return AutomergeSys.getMarksInTx(this.transactionPtr.get(), obj, Optional.of(heads)); - } else { - return AutomergeSys.getMarksInDoc(this.pointer.get(), obj, Optional.of(heads)); - } - } - - protected synchronized void clearTransaction() { - this.transactionPtr = Optional.empty(); - } - - protected synchronized Optional generateSyncMessage(AutomergeSys.SyncStatePointer syncState) { - if (this.transactionPtr.isPresent()) { - throw new TransactionInProgress(); - } - return AutomergeSys.generateSyncMessage(syncState, this.pointer.get()); - } - - /** - * Generate a sync message - * - * @param syncState - * the {@link SyncState} for the connection you are syncing with - * @return the sync message to send to the other side, or {@link Optional#empty} - * if there is nothing to send - */ - public synchronized Optional generateSyncMessage(SyncState syncState) { - return syncState.generateSyncMessage(this); - } - - protected synchronized void receiveSyncMessage(AutomergeSys.SyncStatePointer syncState, byte[] message) { - if (this.transactionPtr.isPresent()) { - throw new TransactionInProgress(); - } - AutomergeSys.receiveSyncMessage(syncState, this.pointer.get(), message); - } - - /** - * Applies a sync message to the document. - * - *

- * If you need to know what changes happened as a result of the message use - * {@link receiveSyncMessage(SyncState,PatchLog,byte[])} instead. - * - * @param syncState - * the {@link SyncState} for the connection you are syncing with - * @param message - * The sync message to apply. - * @throws TransactionInProgress - * if a transaction is already in progress - */ - public synchronized void receiveSyncMessage(SyncState syncState, byte[] message) { - syncState.receiveSyncMessage(this, message); - } - - /** - * Applies a sync message to the document logging any changes in a PatchLog. - * - * @param syncState - * the {@link SyncState} for the connection you are syncing with - * @param patchLog - * the {@link PatchLog} to log changes to - * @param message - * The sync message to apply. - * @throws TransactionInProgress - * if a transaction is already in progress - */ - public synchronized void receiveSyncMessage(SyncState syncState, PatchLog patchLog, byte[] message) { - syncState.receiveSyncMessageLogPatches(this, patchLog, message); - } - - public synchronized List makePatches(PatchLog patchLog) { - if (this.transactionPtr.isPresent()) { - throw new TransactionInProgress(); - } - return patchLog.with((AutomergeSys.PatchLogPointer p) -> AutomergeSys.makePatches(this.pointer.get(), p)); - } - - protected synchronized void receiveSyncMessageLogPatches(AutomergeSys.SyncStatePointer syncState, - AutomergeSys.PatchLogPointer patchLog, byte[] message) { - AutomergeSys.receiveSyncMessageLogPatches(syncState, this.pointer.get(), patchLog, message); - } - - /** - * Return the patches that would be required to modify the state at `before` to - * become the state at `after` - * - * @param before - * The heads of the statre to start from - * @param after - * The heads of the state to end at - * @return The patches required to transform the state at `before` to the state - * at `after` - */ - public synchronized List diff(ChangeHash[] before, ChangeHash[] after) { - return AutomergeSys.diff(this.pointer.get(), before, after); - } - - @Override - public synchronized HashMap getMarksAtIndex(ObjectId obj, long index) { - if (this.transactionPtr.isPresent()) { - return AutomergeSys.getMarksAtIndexInTx(this.transactionPtr.get(), obj, index, Optional.empty()); - } else { - return AutomergeSys.getMarksAtIndexInDoc(this.pointer.get(), obj, index, Optional.empty()); - } - } - - @Override - public synchronized HashMap getMarksAtIndex(ObjectId obj, long index, ChangeHash[] heads) { - if (this.transactionPtr.isPresent()) { - return AutomergeSys.getMarksAtIndexInTx(this.transactionPtr.get(), obj, index, Optional.of(heads)); - } else { - return AutomergeSys.getMarksAtIndexInDoc(this.pointer.get(), obj, index, Optional.of(heads)); - } - } - - @Override - public synchronized Cursor makeCursor(ObjectId obj, long index) { - if (this.transactionPtr.isPresent()) { - return AutomergeSys.makeCursorInTx(this.transactionPtr.get(), obj, index, Optional.empty()); - } else { - return AutomergeSys.makeCursorInDoc(this.pointer.get(), obj, index, Optional.empty()); - } - } - - @Override - public synchronized Cursor makeCursor(ObjectId obj, long index, ChangeHash[] heads) { - if (this.transactionPtr.isPresent()) { - return AutomergeSys.makeCursorInTx(this.transactionPtr.get(), obj, index, Optional.of(heads)); - } else { - return AutomergeSys.makeCursorInDoc(this.pointer.get(), obj, index, Optional.of(heads)); - } - } - - @Override - public synchronized long lookupCursorIndex(ObjectId obj, Cursor cursor) { - if (this.transactionPtr.isPresent()) { - return AutomergeSys.lookupCursorIndexInTx(this.transactionPtr.get(), obj, cursor, Optional.empty()); - } else { - return AutomergeSys.lookupCursorIndexInDoc(this.pointer.get(), obj, cursor, Optional.empty()); - } - - } - - @Override - public synchronized long lookupCursorIndex(ObjectId obj, Cursor cursor, ChangeHash[] heads) { - if (this.transactionPtr.isPresent()) { - return AutomergeSys.lookupCursorIndexInTx(this.transactionPtr.get(), obj, cursor, Optional.of(heads)); - } else { - return AutomergeSys.lookupCursorIndexInDoc(this.pointer.get(), obj, cursor, Optional.of(heads)); - } - } - - @Override - public synchronized Optional getObjectType(ObjectId obj) { - if (this.transactionPtr.isPresent()) { - return AutomergeSys.getObjectTypeInTx(this.transactionPtr.get(), obj); - } else { - return AutomergeSys.getObjectTypeInDoc(this.pointer.get(), obj); - } - } + private Optional pointer; + // Keep actor ID here so we a) don't have to keep passing it across the JNI + // boundary and b) can access it when a transaction is in progress + private byte[] actorId; + // If a transaction is in progress we must forward all calls to the transaction. + // In rust code the transaction holds a mutable reference to the document, so + // any + // calls to the document whilst the transaction exists would be unsafe + private Optional transactionPtr; + + /** Create a new document with a random actor ID */ + public Document() { + LoadLibrary.initialize(); + this.pointer = Optional.of(AutomergeSys.createDoc()); + this.actorId = AutomergeSys.getActorId(this.pointer.get()); + this.transactionPtr = Optional.empty(); + } + + /** + * Create a new document with a specific actor ID + * + * @param actorId + * the actor ID to use for this document + */ + public Document(byte[] actorId) { + LoadLibrary.initialize(); + this.actorId = actorId; + this.pointer = Optional.of(AutomergeSys.createDocWithActor(actorId)); + this.transactionPtr = Optional.empty(); + } + + private Document(DocPointer pointer) { + LoadLibrary.initialize(); + this.pointer = Optional.of(pointer); + this.actorId = AutomergeSys.getActorId(this.pointer.get()); + this.transactionPtr = Optional.empty(); + } + + /** + * Get the actor ID for this document + * + * @return the actor ID for this document + */ + public byte[] getActorId() { + return this.actorId; + } + + /** + * Free the memory associated with this document + * + *

+ * Once this method has been called any further attempts to interact with the + * document will raise an exception. This call itself is idempotent though, so + * it's safe to call mutliple times. + */ + public synchronized void free() { + if (this.transactionPtr.isPresent()) { + throw new TransactionInProgress(); + } + if (this.pointer.isPresent()) { + AutomergeSys.freeDoc(this.pointer.get()); + this.pointer = Optional.empty(); + } + } + + /** + * Load a document from disk + * + *

+ * This can be used to load bytes produced by {@link save} or + * {@link encodeChangesSince} + * + * @param bytes + * The bytes of the document to load + * @return The loaded document + */ + public static Document load(byte[] bytes) { + LoadLibrary.initialize(); + return new Document(AutomergeSys.loadDoc(bytes)); + } + + /** + * Save a document + * + *

+ * The saved document can be loaded again with {@link load} + * + * @return The bytes of the saved document + */ + public synchronized byte[] save() { + if (this.transactionPtr.isPresent()) { + throw new TransactionInProgress(); + } + return AutomergeSys.saveDoc(this.pointer.get()); + } + + /** + * Create a copy of this document with a new random actor ID + * + * @return The new document + */ + public synchronized Document fork() { + if (this.transactionPtr.isPresent()) { + throw new TransactionInProgress(); + } + return new Document(AutomergeSys.forkDoc(this.pointer.get())); + } + + /** + * Create a copy of this document with the given actor ID + * + * @param newActor + * The actor ID to use for the new document + * @return The new document + */ + public synchronized Document fork(byte[] newActor) { + if (this.transactionPtr.isPresent()) { + throw new TransactionInProgress(); + } + return new Document(AutomergeSys.forkDocWithActor(this.pointer.get(), newActor)); + } + + /** + * Create a copy of this document as at the given heads + * + * @param heads + * The heads to fork the document at + * @return The new document + */ + public synchronized Document fork(ChangeHash[] heads) { + if (this.transactionPtr.isPresent()) { + throw new TransactionInProgress(); + } + return new Document(AutomergeSys.forkDocAt(this.pointer.get(), heads)); + } + + /** + * Create a copy of this document as at the given heads with the given actor ID + * + * @param heads + * The heads to fork the document at + * @param newActor + * The actor ID to use for the new document + * @return The new document + */ + public synchronized Document fork(ChangeHash[] heads, byte[] newActor) { + if (this.transactionPtr.isPresent()) { + throw new TransactionInProgress(); + } + return new Document(AutomergeSys.forkDocAtWithActor(this.pointer.get(), heads, newActor)); + } + + /** + * Merge another document into this one + * + * @param other + * The document to merge into this one + * @throws TransactionInProgress + * if there is a transaction in progress on this document or on the + * other document + */ + public synchronized void merge(Document other) { + if (this.transactionPtr.isPresent() || other.transactionPtr.isPresent()) { + throw new TransactionInProgress(); + } + AutomergeSys.mergeDoc(this.pointer.get(), other.pointer.get()); + } + + /** + * Merge another document into this one logging patches + * + * @param other + * The document to merge into this one + * @param patchLog + * The patch log in which to record any changes to the current state + * which occur as a result of the merge + * @throws TransactionInProgress + * if there is a transaction in progress on this document or on the + * other document + */ + public synchronized void merge(Document other, PatchLog patchLog) { + if (this.transactionPtr.isPresent() || other.transactionPtr.isPresent()) { + throw new TransactionInProgress(); + } + patchLog.with((pointer) -> { + AutomergeSys.mergeDocLogPatches(this.pointer.get(), other.pointer.get(), pointer); + }); + } + + /** + * Encode changes since the given heads + * + *

+ * The encoded changes this method returns can be used in + * {@link applyEncodedChanges} + * + * @param heads + * The heads to encode changes since + * @return The encoded changes + */ + public synchronized byte[] encodeChangesSince(ChangeHash[] heads) { + if (this.transactionPtr.isPresent()) { + throw new TransactionInProgress(); + } + return AutomergeSys.encodeChangesSince(this.pointer.get(), heads); + } + + /** + * Incorporate changes from another document into this document + * + * @param changes + * The changes to incorporate. Produced by {@link encodeChangesSince} + * or {@link save} + * @throws TransactionInProgress + * if a transaction is in progress + * @throws AutomergeException + * if the changes are not valid + */ + public synchronized void applyEncodedChanges(byte[] changes) { + if (this.transactionPtr.isPresent()) { + throw new TransactionInProgress(); + } + AutomergeSys.applyEncodedChanges(this.pointer.get(), changes); + } + + /** + * The same as {@link applyEncodedChanges} but logs any changes to the current + * state that result from applying the change in the given patch log + * + *

+ * Creating patches does imply a performance penalty, so if you don't need them + * you should use {@link applyEncodedChanges} + * + * @param changes + * The changes to incorporate. Produced by {@link encodeChangesSince} + * or {@link save} + * @param patchLog + * The patch log in which to record any changes to the current state + * @throws TransactionInProgress + * if a transaction is in progress + * @throws AutomergeException + * if the changes are not valid + */ + public synchronized void applyEncodedChanges(byte[] changes, PatchLog patchLog) { + if (this.transactionPtr.isPresent()) { + throw new TransactionInProgress(); + } + patchLog.with((AutomergeSys.PatchLogPointer patchLogPointer) -> AutomergeSys + .applyEncodedChangesLogPatches(this.pointer.get(), patchLogPointer, changes)); + } + + public synchronized Optional get(ObjectId obj, String key) { + if (this.transactionPtr.isPresent()) { + return AutomergeSys.getInMapInTx(this.transactionPtr.get(), obj, key); + } else { + return AutomergeSys.getInMapInDoc(this.pointer.get(), obj, key); + } + } + + public synchronized Optional get(ObjectId obj, long key) { + if (this.transactionPtr.isPresent()) { + return AutomergeSys.getInListInTx(this.transactionPtr.get(), obj, key); + } else { + return AutomergeSys.getInListInDoc(this.pointer.get(), obj, key); + } + } + + public synchronized Optional get(ObjectId obj, String key, ChangeHash[] heads) { + if (this.transactionPtr.isPresent()) { + return AutomergeSys.getAtInMapInTx(this.transactionPtr.get(), obj, key, heads); + } else { + return AutomergeSys.getAtInMapInDoc(this.pointer.get(), obj, key, heads); + } + } + + public synchronized Optional get(ObjectId obj, long idx, ChangeHash[] heads) { + if (this.transactionPtr.isPresent()) { + return AutomergeSys.getAtInListInTx(this.transactionPtr.get(), obj, idx, heads); + } else { + return AutomergeSys.getAtInListInDoc(this.pointer.get(), obj, idx, heads); + } + } + + public synchronized Optional getAll(ObjectId obj, String key) { + if (this.transactionPtr.isPresent()) { + return AutomergeSys.getAllInMapInTx(this.transactionPtr.get(), obj, key); + } else { + return AutomergeSys.getAllInMapInDoc(this.pointer.get(), obj, key); + } + } + + public synchronized Optional getAll(ObjectId obj, long idx) { + if (this.transactionPtr.isPresent()) { + return AutomergeSys.getAllInListInTx(this.transactionPtr.get(), obj, idx); + } else { + return AutomergeSys.getAllInListInDoc(this.pointer.get(), obj, idx); + } + } + + public synchronized Optional getAll(ObjectId obj, String key, ChangeHash[] heads) { + if (this.transactionPtr.isPresent()) { + return AutomergeSys.getAllAtInMapInTx(this.transactionPtr.get(), obj, key, heads); + } else { + return AutomergeSys.getAllAtInMapInDoc(this.pointer.get(), obj, key, heads); + } + } + + public synchronized Optional getAll(ObjectId obj, long idx, ChangeHash[] heads) { + if (this.transactionPtr.isPresent()) { + return AutomergeSys.getAllAtInListInTx(this.transactionPtr.get(), obj, idx, heads); + } else { + return AutomergeSys.getAllAtInListInDoc(this.pointer.get(), obj, idx, heads); + } + } + + public synchronized Optional text(ObjectId obj) { + if (this.transactionPtr.isPresent()) { + return AutomergeSys.getTextInTx(this.transactionPtr.get(), obj); + } else { + return AutomergeSys.getTextInDoc(this.pointer.get(), obj); + } + } + + public synchronized Optional text(ObjectId obj, ChangeHash[] heads) { + if (this.transactionPtr.isPresent()) { + return AutomergeSys.getTextAtInTx(this.transactionPtr.get(), obj, heads); + } else { + return AutomergeSys.getTextAtInDoc(this.pointer.get(), obj, heads); + } + } + + public synchronized Optional keys(ObjectId obj) { + if (this.transactionPtr.isPresent()) { + return AutomergeSys.getKeysInTx(this.transactionPtr.get(), obj); + } else { + return AutomergeSys.getKeysInDoc(this.pointer.get(), obj); + } + } + + public synchronized Optional keys(ObjectId obj, ChangeHash[] heads) { + if (this.transactionPtr.isPresent()) { + return AutomergeSys.getKeysAtInTx(this.transactionPtr.get(), obj, heads); + } else { + return AutomergeSys.getKeysAtInDoc(this.pointer.get(), obj, heads); + } + } + + public synchronized Optional mapEntries(ObjectId obj) { + if (this.transactionPtr.isPresent()) { + return AutomergeSys.getMapEntriesInTx(this.transactionPtr.get(), obj); + } else { + return AutomergeSys.getMapEntriesInDoc(this.pointer.get(), obj); + } + } + + public synchronized Optional mapEntries(ObjectId obj, ChangeHash[] heads) { + if (this.transactionPtr.isPresent()) { + return AutomergeSys.getMapEntriesAtInTx(this.transactionPtr.get(), obj, heads); + } else { + return AutomergeSys.getMapEntriesAtInDoc(this.pointer.get(), obj, heads); + } + } + + public synchronized long length(ObjectId obj) { + if (this.transactionPtr.isPresent()) { + return AutomergeSys.getListLengthInTx(this.transactionPtr.get(), obj); + } else { + return AutomergeSys.getListLengthInDoc(this.pointer.get(), obj); + } + } + + public synchronized long length(ObjectId obj, ChangeHash[] heads) { + if (this.transactionPtr.isPresent()) { + return AutomergeSys.getListLengthAtInTx(this.transactionPtr.get(), obj, heads); + } else { + return AutomergeSys.getListLengthAtInDoc(this.pointer.get(), obj, heads); + } + } + + /** + * Start a transaction to change this document + * + *

+ * There can only be one active transaction per document. Any method which + * mutates the document (e.g. {@link merge} or {@link receiveSyncMessage} will + * throw an exception if a transaction is in progress. Therefore keep + * transactions short lived. + * + * @return a new transaction + * @throws TransactionInProgress + * if a transaction is already in progress + */ + public synchronized Transaction startTransaction() { + if (this.transactionPtr.isPresent()) { + throw new TransactionInProgress(); + } + AutomergeSys.TransactionPointer ptr = AutomergeSys.startTransaction(this.pointer.get()); + this.transactionPtr = Optional.of(ptr); + return new TransactionImpl(this, ptr); + } + + /** + * Start a transaction to change this document which logs changes in a patch log + * + *

+ * There can only be one active transaction per document. Any method which + * mutates the document (e.g. {@link merge} or {@link receiveSyncMessage} will + * throw an exception if a transaction is in progress. Therefore keep + * transactions short lived. + * + * @param patchLog + * the {@link PatchLog} to log changes to + * @return a new transaction + * @throws TransactionInProgress + * if a transaction is already in progress + */ + public synchronized Transaction startTransaction(PatchLog patchLog) { + if (this.transactionPtr.isPresent()) { + throw new TransactionInProgress(); + } + AutomergeSys.PatchLogPointer patchLogPointer = patchLog.take(); + AutomergeSys.TransactionPointer ptr = AutomergeSys.startTransactionLogPatches(this.pointer.get(), + patchLogPointer); + this.transactionPtr = Optional.of(ptr); + return new TransactionImpl(this, ptr, (AutomergeSys.PatchLogPointer returnedPointer) -> { + patchLog.put(returnedPointer); + }); + } + + /** + * Start a transaction to change this document based on the document at a given + * heads + * + *

+ * There can only be one active transaction per document. Any method which + * mutates the document (e.g. {@link merge} or {@link receiveSyncMessage} will + * throw an exception if a transaction is in progress. Therefore keep + * transactions short lived. + * + * @param patchLog + * the {@link PatchLog} to log changes to. Note that the the changes + * logged here will represent changes from the state as at the given + * heads, not the state of the document when calling this method. + * @param heads + * the heads to begin the transaction at + * + * @return a new transaction + * @throws TransactionInProgress + * if a transaction is already in progress + */ + public synchronized Transaction startTransactionAt(PatchLog patchLog, ChangeHash[] heads) { + if (this.transactionPtr.isPresent()) { + throw new TransactionInProgress(); + } + AutomergeSys.TransactionPointer ptr = AutomergeSys.startTransactionAt(this.pointer.get(), patchLog.take(), + heads); + return new TransactionImpl(this, ptr, (AutomergeSys.PatchLogPointer returnedPointer) -> { + patchLog.put(returnedPointer); + }); + } + + public synchronized Optional listItems(ObjectId obj) { + if (this.transactionPtr.isPresent()) { + return AutomergeSys.getListItemsInTx(this.transactionPtr.get(), obj); + } else { + return AutomergeSys.getListItemsInDoc(this.pointer.get(), obj); + } + } + + public synchronized Optional listItems(ObjectId obj, ChangeHash[] heads) { + if (this.transactionPtr.isPresent()) { + return AutomergeSys.getListItemsAtInTx(this.transactionPtr.get(), obj, heads); + } else { + return AutomergeSys.getListItemsAtInDoc(this.pointer.get(), obj, heads); + } + } + + public synchronized ChangeHash[] getHeads() { + if (this.transactionPtr.isPresent()) { + return AutomergeSys.getHeadsInTx(this.transactionPtr.get()); + } else { + return AutomergeSys.getHeadsInDoc(this.pointer.get()); + } + } + + public synchronized List marks(ObjectId obj) { + if (this.transactionPtr.isPresent()) { + return AutomergeSys.getMarksInTx(this.transactionPtr.get(), obj, Optional.empty()); + } else { + return AutomergeSys.getMarksInDoc(this.pointer.get(), obj, Optional.empty()); + } + } + + public synchronized List marks(ObjectId obj, ChangeHash[] heads) { + if (this.transactionPtr.isPresent()) { + return AutomergeSys.getMarksInTx(this.transactionPtr.get(), obj, Optional.of(heads)); + } else { + return AutomergeSys.getMarksInDoc(this.pointer.get(), obj, Optional.of(heads)); + } + } + + protected synchronized void clearTransaction() { + this.transactionPtr = Optional.empty(); + } + + protected synchronized Optional generateSyncMessage(AutomergeSys.SyncStatePointer syncState) { + if (this.transactionPtr.isPresent()) { + throw new TransactionInProgress(); + } + return AutomergeSys.generateSyncMessage(syncState, this.pointer.get()); + } + + /** + * Generate a sync message + * + * @param syncState + * the {@link SyncState} for the connection you are syncing with + * @return the sync message to send to the other side, or {@link Optional#empty} + * if there is nothing to send + */ + public synchronized Optional generateSyncMessage(SyncState syncState) { + return syncState.generateSyncMessage(this); + } + + protected synchronized void receiveSyncMessage(AutomergeSys.SyncStatePointer syncState, byte[] message) { + if (this.transactionPtr.isPresent()) { + throw new TransactionInProgress(); + } + AutomergeSys.receiveSyncMessage(syncState, this.pointer.get(), message); + } + + /** + * Applies a sync message to the document. + * + *

+ * If you need to know what changes happened as a result of the message use + * {@link receiveSyncMessage(SyncState,PatchLog,byte[])} instead. + * + * @param syncState + * the {@link SyncState} for the connection you are syncing with + * @param message + * The sync message to apply. + * @throws TransactionInProgress + * if a transaction is already in progress + */ + public synchronized void receiveSyncMessage(SyncState syncState, byte[] message) { + syncState.receiveSyncMessage(this, message); + } + + /** + * Applies a sync message to the document logging any changes in a PatchLog. + * + * @param syncState + * the {@link SyncState} for the connection you are syncing with + * @param patchLog + * the {@link PatchLog} to log changes to + * @param message + * The sync message to apply. + * @throws TransactionInProgress + * if a transaction is already in progress + */ + public synchronized void receiveSyncMessage(SyncState syncState, PatchLog patchLog, byte[] message) { + syncState.receiveSyncMessageLogPatches(this, patchLog, message); + } + + public synchronized List makePatches(PatchLog patchLog) { + if (this.transactionPtr.isPresent()) { + throw new TransactionInProgress(); + } + return patchLog.with((AutomergeSys.PatchLogPointer p) -> AutomergeSys.makePatches(this.pointer.get(), p)); + } + + protected synchronized void receiveSyncMessageLogPatches(AutomergeSys.SyncStatePointer syncState, + AutomergeSys.PatchLogPointer patchLog, byte[] message) { + AutomergeSys.receiveSyncMessageLogPatches(syncState, this.pointer.get(), patchLog, message); + } + + /** + * Return the patches that would be required to modify the state at `before` to + * become the state at `after` + * + * @param before + * The heads of the statre to start from + * @param after + * The heads of the state to end at + * @return The patches required to transform the state at `before` to the state + * at `after` + */ + public synchronized List diff(ChangeHash[] before, ChangeHash[] after) { + return AutomergeSys.diff(this.pointer.get(), before, after); + } + + @Override + public synchronized HashMap getMarksAtIndex(ObjectId obj, long index) { + if (this.transactionPtr.isPresent()) { + return AutomergeSys.getMarksAtIndexInTx(this.transactionPtr.get(), obj, index, Optional.empty()); + } else { + return AutomergeSys.getMarksAtIndexInDoc(this.pointer.get(), obj, index, Optional.empty()); + } + } + + @Override + public synchronized HashMap getMarksAtIndex(ObjectId obj, long index, ChangeHash[] heads) { + if (this.transactionPtr.isPresent()) { + return AutomergeSys.getMarksAtIndexInTx(this.transactionPtr.get(), obj, index, Optional.of(heads)); + } else { + return AutomergeSys.getMarksAtIndexInDoc(this.pointer.get(), obj, index, Optional.of(heads)); + } + } + + @Override + public synchronized Cursor makeCursor(ObjectId obj, long index) { + if (this.transactionPtr.isPresent()) { + return AutomergeSys.makeCursorInTx(this.transactionPtr.get(), obj, index, Optional.empty()); + } else { + return AutomergeSys.makeCursorInDoc(this.pointer.get(), obj, index, Optional.empty()); + } + } + + @Override + public synchronized Cursor makeCursor(ObjectId obj, long index, ChangeHash[] heads) { + if (this.transactionPtr.isPresent()) { + return AutomergeSys.makeCursorInTx(this.transactionPtr.get(), obj, index, Optional.of(heads)); + } else { + return AutomergeSys.makeCursorInDoc(this.pointer.get(), obj, index, Optional.of(heads)); + } + } + + @Override + public synchronized long lookupCursorIndex(ObjectId obj, Cursor cursor) { + if (this.transactionPtr.isPresent()) { + return AutomergeSys.lookupCursorIndexInTx(this.transactionPtr.get(), obj, cursor, Optional.empty()); + } else { + return AutomergeSys.lookupCursorIndexInDoc(this.pointer.get(), obj, cursor, Optional.empty()); + } + + } + + @Override + public synchronized long lookupCursorIndex(ObjectId obj, Cursor cursor, ChangeHash[] heads) { + if (this.transactionPtr.isPresent()) { + return AutomergeSys.lookupCursorIndexInTx(this.transactionPtr.get(), obj, cursor, Optional.of(heads)); + } else { + return AutomergeSys.lookupCursorIndexInDoc(this.pointer.get(), obj, cursor, Optional.of(heads)); + } + } + + @Override + public synchronized Optional getObjectType(ObjectId obj) { + if (this.transactionPtr.isPresent()) { + return AutomergeSys.getObjectTypeInTx(this.transactionPtr.get(), obj); + } else { + return AutomergeSys.getObjectTypeInDoc(this.pointer.get(), obj); + } + } } diff --git a/lib/src/main/java/org/automerge/ExpandMark.java b/lib/src/main/java/org/automerge/ExpandMark.java index e00496e..cb57a6e 100644 --- a/lib/src/main/java/org/automerge/ExpandMark.java +++ b/lib/src/main/java/org/automerge/ExpandMark.java @@ -1,5 +1,5 @@ package org.automerge; public enum ExpandMark { - BEFORE, AFTER, BOTH, NONE, + BEFORE, AFTER, BOTH, NONE, } diff --git a/lib/src/main/java/org/automerge/LoadLibrary.java b/lib/src/main/java/org/automerge/LoadLibrary.java index 21c024c..d4c932d 100644 --- a/lib/src/main/java/org/automerge/LoadLibrary.java +++ b/lib/src/main/java/org/automerge/LoadLibrary.java @@ -12,278 +12,278 @@ class LoadLibrary { - private static class Library { - public String target; - public String prefix; - public String suffix; + private static class Library { + public String target; + public String prefix; + public String suffix; - public Library(String target, String prefix, String suffix) { - this.target = target; - this.prefix = prefix; - this.suffix = suffix; - } + public Library(String target, String prefix, String suffix) { + this.target = target; + this.prefix = prefix; + this.suffix = suffix; + } - public String getResourcePath() { - return String.format("native/%s/%sautomerge_jni.%s", target, prefix, suffix); - } - } + public String getResourcePath() { + return String.format("native/%s/%sautomerge_jni.%s", target, prefix, suffix); + } + } - private enum Platform { - UNKNOWN, WINDOWS_X86_32, WINDOWS_X86_64, WINDOWS_ARM, LINUX_X86_32, LINUX_X86_64, LINUX_ARM64, SOLARIS_X86_32, SOLARIS_X86_64, SOLARIS_SPARC_32, SOLARIS_SPARC_64, MACOSX_X86_32, MACOSX_X86_64, MACOSX_ARM64, ANDROID_ARM, ANDROID_ARM64, ANDROID_X86_32, ANDROID_X86_64, ANDROID_UNKNOWN; + private enum Platform { + UNKNOWN, WINDOWS_X86_32, WINDOWS_X86_64, WINDOWS_ARM, LINUX_X86_32, LINUX_X86_64, LINUX_ARM64, SOLARIS_X86_32, SOLARIS_X86_64, SOLARIS_SPARC_32, SOLARIS_SPARC_64, MACOSX_X86_32, MACOSX_X86_64, MACOSX_ARM64, ANDROID_ARM, ANDROID_ARM64, ANDROID_X86_32, ANDROID_X86_64, ANDROID_UNKNOWN; - Optional library() { - switch (this) { - case WINDOWS_X86_64 : - return Optional.of(new Library("x86_64-pc-windows-gnu", "", "dll")); - case WINDOWS_X86_32 : - return Optional.of(new Library("i686-pc-windows-gnu", "", "dll")); - case WINDOWS_ARM : - return Optional.of(new Library("aarch64-pc-windows-gnullvm", "", "dll")); - case LINUX_X86_64 : - return Optional.of(new Library("x86_64-unknown-linux-gnu", "lib", "so")); - case LINUX_ARM64 : - return Optional.of(new Library("aarch64-unknown-linux-gnu", "lib", "so")); - case MACOSX_X86_64 : - return Optional.of(new Library("x86_64-apple-darwin", "lib", "dylib")); - case MACOSX_ARM64 : - return Optional.of(new Library("aarch64-apple-darwin", "lib", "dylib")); - case ANDROID_ARM : - return Optional.of(new Library("armv7-linux-androideabi", "lib", "so")); - case ANDROID_ARM64 : - return Optional.of(new Library("aarch64-linux-android", "lib", "so")); - case ANDROID_X86_32 : - return Optional.of(new Library("i686-linux-android", "lib", "so")); - case ANDROID_X86_64 : - return Optional.of(new Library("x86_64-linux-android", "lib", "so")); - default : - return Optional.empty(); - } - } + Optional library() { + switch (this) { + case WINDOWS_X86_64: + return Optional.of(new Library("x86_64-pc-windows-gnu", "", "dll")); + case WINDOWS_X86_32: + return Optional.of(new Library("i686-pc-windows-gnu", "", "dll")); + case WINDOWS_ARM: + return Optional.of(new Library("aarch64-pc-windows-gnullvm", "", "dll")); + case LINUX_X86_64: + return Optional.of(new Library("x86_64-unknown-linux-gnu", "lib", "so")); + case LINUX_ARM64: + return Optional.of(new Library("aarch64-unknown-linux-gnu", "lib", "so")); + case MACOSX_X86_64: + return Optional.of(new Library("x86_64-apple-darwin", "lib", "dylib")); + case MACOSX_ARM64: + return Optional.of(new Library("aarch64-apple-darwin", "lib", "dylib")); + case ANDROID_ARM: + return Optional.of(new Library("armv7-linux-androideabi", "lib", "so")); + case ANDROID_ARM64: + return Optional.of(new Library("aarch64-linux-android", "lib", "so")); + case ANDROID_X86_32: + return Optional.of(new Library("i686-linux-android", "lib", "so")); + case ANDROID_X86_64: + return Optional.of(new Library("x86_64-linux-android", "lib", "so")); + default: + return Optional.empty(); + } + } - public boolean isAndroid() { - switch (this) { - case ANDROID_ARM : - case ANDROID_ARM64 : - case ANDROID_X86_32 : - case ANDROID_X86_64 : - case ANDROID_UNKNOWN : - return true; - default : - return false; - } - } - } + public boolean isAndroid() { + switch (this) { + case ANDROID_ARM: + case ANDROID_ARM64: + case ANDROID_X86_32: + case ANDROID_X86_64: + case ANDROID_UNKNOWN: + return true; + default: + return false; + } + } + } - static Platform CURRENT_PLATFORM; + static Platform CURRENT_PLATFORM; - static { - String name = System.getProperty("os.name").toLowerCase(Locale.ENGLISH); - String arch = System.getProperty("os.arch").toLowerCase(Locale.ENGLISH); - String vm = System.getProperty("java.vm.name").toLowerCase(Locale.ENGLISH); - if (name.startsWith("windows") && "x86".equals(arch)) { - CURRENT_PLATFORM = Platform.WINDOWS_X86_32; - } else if (name.startsWith("windows") && ("x86_64".equals(arch) || "amd64".equals(arch))) { - CURRENT_PLATFORM = Platform.WINDOWS_X86_64; - } else if (name.startsWith("windows") && "aarch64".equals(arch)) { - CURRENT_PLATFORM = Platform.WINDOWS_ARM; - } else if ("dalvik".equals(vm) && "armeabi-v7a".equals(arch)) { - CURRENT_PLATFORM = Platform.ANDROID_ARM; - } else if ("dalvik".equals(vm) && "aarch64".equals(arch)) { - CURRENT_PLATFORM = Platform.ANDROID_ARM64; - } else if ("dalvik".equals(vm) && "x64".equals(arch)) { - CURRENT_PLATFORM = Platform.ANDROID_X86_32; - } else if ("dalvik".equals(vm) && "x64_64".equals(arch)) { - CURRENT_PLATFORM = Platform.ANDROID_X86_64; - } else if ("dalvik".equals(vm)) { - CURRENT_PLATFORM = Platform.ANDROID_UNKNOWN; - } else if ("linux".equals(name) && "i386".equals(arch)) { - CURRENT_PLATFORM = Platform.LINUX_X86_32; - } else if ("linux".equals(name) && "amd64".equals(arch)) { - CURRENT_PLATFORM = Platform.LINUX_X86_64; - } else if ("linux".equals(name) && "aarch64".equals(arch)) { - CURRENT_PLATFORM = Platform.LINUX_ARM64; - } else if ("sunos".equals(name) && "x86".equals(arch)) { - CURRENT_PLATFORM = Platform.SOLARIS_X86_32; - } else if ("sunos".equals(name) && "amd64".equals(arch)) { - CURRENT_PLATFORM = Platform.SOLARIS_X86_64; - } else if ("sunos".equals(name) && "sparc".equals(arch)) { - CURRENT_PLATFORM = Platform.SOLARIS_SPARC_32; - } else if ("sunos".equals(name) && "sparcv9".equals(arch)) { - CURRENT_PLATFORM = Platform.SOLARIS_SPARC_64; - } else if ("mac os x".equals(name) && "x86".equals(arch)) { - CURRENT_PLATFORM = Platform.MACOSX_X86_32; - } else if ("mac os x".equals(name) && ("x86_64".equals(arch) || "amd64".equals(arch))) { - CURRENT_PLATFORM = Platform.MACOSX_X86_64; - } else if ("mac os x".equals(name) && "aarch64".equals(arch)) { - CURRENT_PLATFORM = Platform.MACOSX_ARM64; - } else { - CURRENT_PLATFORM = Platform.UNKNOWN; - } - } + static { + String name = System.getProperty("os.name").toLowerCase(Locale.ENGLISH); + String arch = System.getProperty("os.arch").toLowerCase(Locale.ENGLISH); + String vm = System.getProperty("java.vm.name").toLowerCase(Locale.ENGLISH); + if (name.startsWith("windows") && "x86".equals(arch)) { + CURRENT_PLATFORM = Platform.WINDOWS_X86_32; + } else if (name.startsWith("windows") && ("x86_64".equals(arch) || "amd64".equals(arch))) { + CURRENT_PLATFORM = Platform.WINDOWS_X86_64; + } else if (name.startsWith("windows") && "aarch64".equals(arch)) { + CURRENT_PLATFORM = Platform.WINDOWS_ARM; + } else if ("dalvik".equals(vm) && "armeabi-v7a".equals(arch)) { + CURRENT_PLATFORM = Platform.ANDROID_ARM; + } else if ("dalvik".equals(vm) && "aarch64".equals(arch)) { + CURRENT_PLATFORM = Platform.ANDROID_ARM64; + } else if ("dalvik".equals(vm) && "x64".equals(arch)) { + CURRENT_PLATFORM = Platform.ANDROID_X86_32; + } else if ("dalvik".equals(vm) && "x64_64".equals(arch)) { + CURRENT_PLATFORM = Platform.ANDROID_X86_64; + } else if ("dalvik".equals(vm)) { + CURRENT_PLATFORM = Platform.ANDROID_UNKNOWN; + } else if ("linux".equals(name) && "i386".equals(arch)) { + CURRENT_PLATFORM = Platform.LINUX_X86_32; + } else if ("linux".equals(name) && "amd64".equals(arch)) { + CURRENT_PLATFORM = Platform.LINUX_X86_64; + } else if ("linux".equals(name) && "aarch64".equals(arch)) { + CURRENT_PLATFORM = Platform.LINUX_ARM64; + } else if ("sunos".equals(name) && "x86".equals(arch)) { + CURRENT_PLATFORM = Platform.SOLARIS_X86_32; + } else if ("sunos".equals(name) && "amd64".equals(arch)) { + CURRENT_PLATFORM = Platform.SOLARIS_X86_64; + } else if ("sunos".equals(name) && "sparc".equals(arch)) { + CURRENT_PLATFORM = Platform.SOLARIS_SPARC_32; + } else if ("sunos".equals(name) && "sparcv9".equals(arch)) { + CURRENT_PLATFORM = Platform.SOLARIS_SPARC_64; + } else if ("mac os x".equals(name) && "x86".equals(arch)) { + CURRENT_PLATFORM = Platform.MACOSX_X86_32; + } else if ("mac os x".equals(name) && ("x86_64".equals(arch) || "amd64".equals(arch))) { + CURRENT_PLATFORM = Platform.MACOSX_X86_64; + } else if ("mac os x".equals(name) && "aarch64".equals(arch)) { + CURRENT_PLATFORM = Platform.MACOSX_ARM64; + } else { + CURRENT_PLATFORM = Platform.UNKNOWN; + } + } - // The extension added to the library name to create a lockfile for it - private static final String LIBRARY_LOCK_EXT = ".lck"; + // The extension added to the library name to create a lockfile for it + private static final String LIBRARY_LOCK_EXT = ".lck"; - static boolean loaded = false; + static boolean loaded = false; - public static synchronized void initialize() { - // Only do this on first load and _dont_ do it on android, where libraries - // are provided by jniLibs and the version.properties file is removed from the - // jar by the build process - if (!loaded && !CURRENT_PLATFORM.isAndroid()) { - cleanup(); - } - loadAutomergeJniLib(); - } + public static synchronized void initialize() { + // Only do this on first load and _dont_ do it on android, where libraries + // are provided by jniLibs and the version.properties file is removed from the + // jar by the build process + if (!loaded && !CURRENT_PLATFORM.isAndroid()) { + cleanup(); + } + loadAutomergeJniLib(); + } - private static File getTempDir() { - return new File(System.getProperty("java.io.tmpdir")); - } + private static File getTempDir() { + return new File(System.getProperty("java.io.tmpdir")); + } - /** - * Delete unused library files - * - *

- * On windows the library files are locked by the JVM and will not be deleted on - * close. To prevent accumulating many versions of the lockfile we create a file - * with the `LIBRARY_LOCK_EXT` extension which is deleted on close and then we - * delete every library file that does not have a lockfile the next time we - * load. This should mean we never have more library files than the number of - * instances of the JVM which have loaded the library. - */ - static void cleanup() { - String searchPattern = "automerge-jni-" + getVersion(); + /** + * Delete unused library files + * + *

+ * On windows the library files are locked by the JVM and will not be deleted on + * close. To prevent accumulating many versions of the lockfile we create a file + * with the `LIBRARY_LOCK_EXT` extension which is deleted on close and then we + * delete every library file that does not have a lockfile the next time we + * load. This should mean we never have more library files than the number of + * instances of the JVM which have loaded the library. + */ + static void cleanup() { + String searchPattern = "automerge-jni-" + getVersion(); - try (Stream dirList = Files.list(getTempDir().toPath())) { - dirList.filter(path -> !path.getFileName().toString().endsWith(LIBRARY_LOCK_EXT) - && path.getFileName().toString().startsWith(searchPattern)).forEach(nativeLib -> { - Path lckFile = Paths.get(nativeLib + LIBRARY_LOCK_EXT); - if (Files.notExists(lckFile)) { - try { - Files.delete(nativeLib); - } catch (Exception e) { - System.err.println("Failed to delete old native lib: " + e.getMessage()); - } - } - }); - } catch (IOException e) { - System.err.println("Failed to open directory: " + e.getMessage()); - } - } + try (Stream dirList = Files.list(getTempDir().toPath())) { + dirList.filter(path -> !path.getFileName().toString().endsWith(LIBRARY_LOCK_EXT) + && path.getFileName().toString().startsWith(searchPattern)).forEach(nativeLib -> { + Path lckFile = Paths.get(nativeLib + LIBRARY_LOCK_EXT); + if (Files.notExists(lckFile)) { + try { + Files.delete(nativeLib); + } catch (Exception e) { + System.err.println("Failed to delete old native lib: " + e.getMessage()); + } + } + }); + } catch (IOException e) { + System.err.println("Failed to open directory: " + e.getMessage()); + } + } - private static void extractAndLoadLibraryFile(Library library, String targetFolder) { - String uuid = UUID.randomUUID().toString(); - String extractedLibFileName = String.format("automerge-jni-%s-%s-%s.%s", getVersion(), uuid, library.target, - library.suffix); - String extractedLckFileName = extractedLibFileName + LIBRARY_LOCK_EXT; + private static void extractAndLoadLibraryFile(Library library, String targetFolder) { + String uuid = UUID.randomUUID().toString(); + String extractedLibFileName = String.format("automerge-jni-%s-%s-%s.%s", getVersion(), uuid, library.target, + library.suffix); + String extractedLckFileName = extractedLibFileName + LIBRARY_LOCK_EXT; - Path extractedLibFile = Paths.get(targetFolder, extractedLibFileName); - Path extractedLckFile = Paths.get(targetFolder, extractedLckFileName); + Path extractedLibFile = Paths.get(targetFolder, extractedLibFileName); + Path extractedLckFile = Paths.get(targetFolder, extractedLckFileName); - try { - // Extract a native library file into the target directory - try (InputStream reader = getResourceAsStream(library.getResourcePath())) { - if (Files.notExists(extractedLckFile)) { - Files.createFile(extractedLckFile); - } + try { + // Extract a native library file into the target directory + try (InputStream reader = getResourceAsStream(library.getResourcePath())) { + if (Files.notExists(extractedLckFile)) { + Files.createFile(extractedLckFile); + } - Files.copy(reader, extractedLibFile, StandardCopyOption.REPLACE_EXISTING); - } finally { - // Delete the extracted lib file on JVM exit. - extractedLibFile.toFile().deleteOnExit(); - extractedLckFile.toFile().deleteOnExit(); - } + Files.copy(reader, extractedLibFile, StandardCopyOption.REPLACE_EXISTING); + } finally { + // Delete the extracted lib file on JVM exit. + extractedLibFile.toFile().deleteOnExit(); + extractedLckFile.toFile().deleteOnExit(); + } - // Set executable (x) flag to enable Java to load the native library - extractedLibFile.toFile().setReadable(true); - extractedLibFile.toFile().setWritable(true, true); - extractedLibFile.toFile().setExecutable(true); + // Set executable (x) flag to enable Java to load the native library + extractedLibFile.toFile().setReadable(true); + extractedLibFile.toFile().setWritable(true, true); + extractedLibFile.toFile().setExecutable(true); - System.load(extractedLibFile.toString()); - } catch (IOException e) { - throw new RuntimeException("unable to load automerge-jni", e); - } - } + System.load(extractedLibFile.toString()); + } catch (IOException e) { + throw new RuntimeException("unable to load automerge-jni", e); + } + } - // Replacement of java.lang.Class#getResourceAsStream(String) to disable sharing - // the resource - // stream in multiple class loaders and specifically to avoid - // https://bugs.openjdk.java.net/browse/JDK-8205976 - private static InputStream getResourceAsStream(String name) { - ClassLoader cl = LoadLibrary.class.getClassLoader(); - URL url = cl.getResource(name); - if (url == null) { - throw new RuntimeException("Resource not found: " + name); - } - try { - URLConnection connection = url.openConnection(); - connection.setUseCaches(false); - return connection.getInputStream(); - } catch (IOException e) { - throw new RuntimeException("unable to get resource reader", e); - } - } + // Replacement of java.lang.Class#getResourceAsStream(String) to disable sharing + // the resource + // stream in multiple class loaders and specifically to avoid + // https://bugs.openjdk.java.net/browse/JDK-8205976 + private static InputStream getResourceAsStream(String name) { + ClassLoader cl = LoadLibrary.class.getClassLoader(); + URL url = cl.getResource(name); + if (url == null) { + throw new RuntimeException("Resource not found: " + name); + } + try { + URLConnection connection = url.openConnection(); + connection.setUseCaches(false); + return connection.getInputStream(); + } catch (IOException e) { + throw new RuntimeException("unable to get resource reader", e); + } + } - /** - * Loads the native library - * - *

- * We first try the system library path, then if that fails we try and load one - * of the bundled libraries from this jar. - */ - private static void loadAutomergeJniLib() { - if (loaded) { - return; - } + /** + * Loads the native library + * + *

+ * We first try the system library path, then if that fails we try and load one + * of the bundled libraries from this jar. + */ + private static void loadAutomergeJniLib() { + if (loaded) { + return; + } - String libName = "automerge_jni_" + BuildInfo.getExpectedRustLibVersion().replace('.', '_'); - // Try System.loadLibrary first - try { - System.loadLibrary(libName); - loaded = true; - return; - } catch (UnsatisfiedLinkError e) { - if (CURRENT_PLATFORM.isAndroid()) { - // We can't bundle the libs in android except via jniLibs, if - // we were unable to load using loadLibrary then there's no hope - throw e; - } - } - // Alright, it's not on the library path, lets find it in the jar + String libName = "automerge_jni_" + BuildInfo.getExpectedRustLibVersion().replace('.', '_'); + // Try System.loadLibrary first + try { + System.loadLibrary(libName); + loaded = true; + return; + } catch (UnsatisfiedLinkError e) { + if (CURRENT_PLATFORM.isAndroid()) { + // We can't bundle the libs in android except via jniLibs, if + // we were unable to load using loadLibrary then there's no hope + throw e; + } + } + // Alright, it's not on the library path, lets find it in the jar - // temporary library folder - String tempFolder = getTempDir().getAbsolutePath(); - if (!CURRENT_PLATFORM.library().isPresent()) { - throw new UnsupportedPlatformException("no native automerge library found for " + CURRENT_PLATFORM.name()); - } - Library lib = CURRENT_PLATFORM.library().get(); - // Try extracting the library from jar - extractAndLoadLibraryFile(lib, tempFolder); + // temporary library folder + String tempFolder = getTempDir().getAbsolutePath(); + if (!CURRENT_PLATFORM.library().isPresent()) { + throw new UnsupportedPlatformException("no native automerge library found for " + CURRENT_PLATFORM.name()); + } + Library lib = CURRENT_PLATFORM.library().get(); + // Try extracting the library from jar + extractAndLoadLibraryFile(lib, tempFolder); - // Check that we have the correct version of the library (BuildInfo is - // generated by gradle and contains the version of the rust library we - // were built against) - String expectedLibVersion = BuildInfo.getExpectedRustLibVersion(); - String actualLibVersion = AutomergeSys.rustLibVersion(); - if (!expectedLibVersion.equals(actualLibVersion)) { - throw new RuntimeException("Automerge native library version mismatch. Expected " + expectedLibVersion - + " but got " + actualLibVersion); - } + // Check that we have the correct version of the library (BuildInfo is + // generated by gradle and contains the version of the rust library we + // were built against) + String expectedLibVersion = BuildInfo.getExpectedRustLibVersion(); + String actualLibVersion = AutomergeSys.rustLibVersion(); + if (!expectedLibVersion.equals(actualLibVersion)) { + throw new RuntimeException("Automerge native library version mismatch. Expected " + expectedLibVersion + + " but got " + actualLibVersion); + } - loaded = true; - } + loaded = true; + } - public static String getVersion() { - InputStream input = getResourceAsStream("version.properties"); + public static String getVersion() { + InputStream input = getResourceAsStream("version.properties"); - String version = "unknown"; - try { - Properties versionData = new Properties(); - versionData.load(input); - version = versionData.getProperty("version", version); - return version; - } catch (IOException e) { - throw new RuntimeException("unable to load version properties", e); - } - } + String version = "unknown"; + try { + Properties versionData = new Properties(); + versionData.load(input); + version = versionData.getProperty("version", version); + return version; + } catch (IOException e) { + throw new RuntimeException("unable to load version properties", e); + } + } } diff --git a/lib/src/main/java/org/automerge/MapEntry.java b/lib/src/main/java/org/automerge/MapEntry.java index aeab612..e0c9fac 100644 --- a/lib/src/main/java/org/automerge/MapEntry.java +++ b/lib/src/main/java/org/automerge/MapEntry.java @@ -1,14 +1,14 @@ package org.automerge; public class MapEntry { - private String key; - private AmValue value; + private String key; + private AmValue value; - public String getKey() { - return key; - } + public String getKey() { + return key; + } - public AmValue getValue() { - return value; - } + public AmValue getValue() { + return value; + } } diff --git a/lib/src/main/java/org/automerge/Mark.java b/lib/src/main/java/org/automerge/Mark.java index d042466..2938e24 100644 --- a/lib/src/main/java/org/automerge/Mark.java +++ b/lib/src/main/java/org/automerge/Mark.java @@ -1,36 +1,36 @@ package org.automerge; public class Mark { - private final long start; - private final long end; - private final String name; - private final AmValue value; + private final long start; + private final long end; + private final String name; + private final AmValue value; - protected Mark(long start, long end, String name, AmValue value) { - this.start = start; - this.end = end; - this.name = name; - this.value = value; - } + protected Mark(long start, long end, String name, AmValue value) { + this.start = start; + this.end = end; + this.name = name; + this.value = value; + } - public long getStart() { - return start; - } + public long getStart() { + return start; + } - public long getEnd() { - return end; - } + public long getEnd() { + return end; + } - public String getName() { - return name; - } + public String getName() { + return name; + } - public AmValue getValue() { - return value; - } + public AmValue getValue() { + return value; + } - @Override - public String toString() { - return "Mark [start=" + start + ", end=" + end + ", name=" + name + ", value=" + value + "]"; - } + @Override + public String toString() { + return "Mark [start=" + start + ", end=" + end + ", name=" + name + ", value=" + value + "]"; + } } diff --git a/lib/src/main/java/org/automerge/NewValue.java b/lib/src/main/java/org/automerge/NewValue.java index fb1b11f..6f61e9c 100644 --- a/lib/src/main/java/org/automerge/NewValue.java +++ b/lib/src/main/java/org/automerge/NewValue.java @@ -13,363 +13,363 @@ * static methods on NewValue to create an instance. */ public abstract class NewValue { - protected abstract void set(Transaction tx, ObjectId obj, String key); - - protected abstract void set(Transaction tx, ObjectId obj, long idx); - - protected abstract void insert(Transaction tx, ObjectId obj, long idx); - - protected abstract void mark(Transaction tx, ObjectId obj, long start, long end, String markName, - ExpandMark expand); - - /** - * Create a new unsigned integer value - * - * @param value - * the positive integer value - * @return a new unsigned integer value - * @throws IllegalArgumentException - * if the value is negative - */ - public static NewValue uint(long value) { - return new UInt(value); - } - - /** - * A new integer value - * - * @param value - * the integer value - * @return a new integer value - */ - public static NewValue integer(long value) { - return new Int(value); - } - - /** - * A new floating point value - * - * @param value - * the floating point value - * @return a new floating point value - */ - public static NewValue f64(double value) { - return new NewValue.F64(value); - } - - /** - * A new boolean value - * - * @param value - * the boolean value - * @return a new boolean value - */ - public static NewValue bool(boolean value) { - return new NewValue.Bool(value); - } - - /** - * A new string value - * - * @param value - * the string value - * @return a new string value - */ - public static NewValue str(String value) { - return new NewValue.Str(value); - } - - /** - * A new byte array value - * - * @param value - * the byte array value - * @return a new byte array value - */ - public static NewValue bytes(byte[] value) { - return new NewValue.Bytes(value); - } - - /** - * A new counter value - * - * @param value - * the initial value of the counter - * @return a new counter value - */ - public static NewValue counter(long value) { - return new NewValue.Counter(value); - } - - /** - * A new timestamp value - * - * @param value - * the value of the timestamp - * @return a new timestamp value - */ - public static NewValue timestamp(Date value) { - return new NewValue.Timestamp(value); - } - - /** The null value */ - public static NewValue NULL = new NewValue.Null(); - - /** A new unsigned integer value */ - public static class UInt extends NewValue { - private long value; - - protected UInt(long value) { - if (value < 0) { - throw new IllegalArgumentException("UInt must be positive"); - } - this.value = value; - } - - @Override - protected void set(Transaction tx, ObjectId obj, String key) { - tx.setUint(obj, key, value); - } - - @Override - protected void set(Transaction tx, ObjectId obj, long idx) { - tx.setUint(obj, idx, value); - } - - @Override - protected void insert(Transaction tx, ObjectId obj, long idx) { - tx.insertUint(obj, idx, value); - } - - @Override - protected void mark(Transaction tx, ObjectId obj, long start, long end, String markName, ExpandMark expand) { - tx.markUint(obj, start, end, markName, value, expand); - } - } - - /** A new integer value */ - public static class Int extends NewValue { - private long value; - - protected Int(long value) { - this.value = value; - } - - @Override - protected void set(Transaction tx, ObjectId obj, String key) { - tx.set(obj, key, value); - } - - @Override - protected void set(Transaction tx, ObjectId obj, long idx) { - tx.set(obj, idx, value); - } - - @Override - protected void insert(Transaction tx, ObjectId obj, long idx) { - tx.insert(obj, idx, value); - } - - @Override - protected void mark(Transaction tx, ObjectId obj, long start, long end, String markName, ExpandMark expand) { - tx.mark(obj, start, end, markName, value, expand); - } - } - - /** A new floating point value */ - public static class F64 extends NewValue { - private double value; - - protected F64(double value) { - this.value = value; - } - - @Override - protected void set(Transaction tx, ObjectId obj, String key) { - tx.set(obj, key, value); - } - - @Override - protected void set(Transaction tx, ObjectId obj, long idx) { - tx.set(obj, idx, value); - } - - @Override - protected void insert(Transaction tx, ObjectId obj, long idx) { - tx.insert(obj, idx, value); - } - - @Override - protected void mark(Transaction tx, ObjectId obj, long start, long end, String markName, ExpandMark expand) { - tx.mark(obj, start, end, markName, value, expand); - } - } - - /** A new string value */ - public static class Str extends NewValue { - private java.lang.String value; - - protected Str(java.lang.String value) { - this.value = value; - } - - @Override - protected void set(Transaction tx, ObjectId obj, String key) { - tx.set(obj, key, value); - } - - @Override - protected void set(Transaction tx, ObjectId obj, long idx) { - tx.set(obj, idx, value); - } - - @Override - protected void insert(Transaction tx, ObjectId obj, long idx) { - tx.insert(obj, idx, value); - } - - @Override - protected void mark(Transaction tx, ObjectId obj, long start, long end, String markName, ExpandMark expand) { - tx.mark(obj, start, end, markName, value, expand); - } - } - - /** A new boolean value */ - public static class Bool extends NewValue { - private boolean value; - - protected Bool(boolean value) { - this.value = value; - } - - @Override - protected void set(Transaction tx, ObjectId obj, String key) { - tx.set(obj, key, value); - } - - @Override - protected void set(Transaction tx, ObjectId obj, long idx) { - tx.set(obj, idx, value); - } - - @Override - protected void insert(Transaction tx, ObjectId obj, long idx) { - tx.insert(obj, idx, value); - } - - @Override - protected void mark(Transaction tx, ObjectId obj, long start, long end, String markName, ExpandMark expand) { - tx.mark(obj, start, end, markName, value, expand); - } - } - - /** A new null value */ - public static class Null extends NewValue { - @Override - protected void set(Transaction tx, ObjectId obj, String key) { - tx.setNull(obj, key); - } - - @Override - protected void set(Transaction tx, ObjectId obj, long idx) { - tx.setNull(obj, idx); - } - - @Override - protected void insert(Transaction tx, ObjectId obj, long idx) { - tx.insertNull(obj, idx); - } - - @Override - protected void mark(Transaction tx, ObjectId obj, long start, long end, String markName, ExpandMark expand) { - tx.markNull(obj, start, end, markName, expand); - } - } - - /** A new byte array value */ - public static class Bytes extends NewValue { - private byte[] value; - - protected Bytes(byte[] value) { - this.value = value; - } - - @Override - protected void set(Transaction tx, ObjectId obj, String key) { - tx.set(obj, key, value); - } - - @Override - protected void set(Transaction tx, ObjectId obj, long idx) { - tx.set(obj, idx, value); - } - - @Override - protected void insert(Transaction tx, ObjectId obj, long idx) { - tx.insert(obj, idx, value); - } - - @Override - protected void mark(Transaction tx, ObjectId obj, long start, long end, String markName, ExpandMark expand) { - tx.mark(obj, start, end, markName, value, expand); - } - } - - /** A new counter value */ - public static class Counter extends NewValue { - private long value; - - protected Counter(long value) { - this.value = value; - } - - @Override - protected void set(Transaction tx, ObjectId obj, String key) { - tx.set(obj, key, new org.automerge.Counter(value)); - } - - @Override - protected void set(Transaction tx, ObjectId obj, long idx) { - tx.set(obj, idx, new org.automerge.Counter(value)); - } - - @Override - protected void insert(Transaction tx, ObjectId obj, long idx) { - tx.insert(obj, idx, new org.automerge.Counter(value)); - } - - @Override - protected void mark(Transaction tx, ObjectId obj, long start, long end, String markName, ExpandMark expand) { - tx.mark(obj, start, end, markName, new org.automerge.Counter(value), expand); - } - } - - /** A new timestamp value */ - public static class Timestamp extends NewValue { - private Date value; - - protected Timestamp(Date value) { - this.value = value; - } - - @Override - protected void set(Transaction tx, ObjectId obj, String key) { - tx.set(obj, key, value); - } - - @Override - protected void set(Transaction tx, ObjectId obj, long idx) { - tx.set(obj, idx, value); - } - - @Override - protected void insert(Transaction tx, ObjectId obj, long idx) { - tx.insert(obj, idx, value); - } - - @Override - protected void mark(Transaction tx, ObjectId obj, long start, long end, String markName, ExpandMark expand) { - tx.mark(obj, start, end, markName, value, expand); - } - } + protected abstract void set(Transaction tx, ObjectId obj, String key); + + protected abstract void set(Transaction tx, ObjectId obj, long idx); + + protected abstract void insert(Transaction tx, ObjectId obj, long idx); + + protected abstract void mark(Transaction tx, ObjectId obj, long start, long end, String markName, + ExpandMark expand); + + /** + * Create a new unsigned integer value + * + * @param value + * the positive integer value + * @return a new unsigned integer value + * @throws IllegalArgumentException + * if the value is negative + */ + public static NewValue uint(long value) { + return new UInt(value); + } + + /** + * A new integer value + * + * @param value + * the integer value + * @return a new integer value + */ + public static NewValue integer(long value) { + return new Int(value); + } + + /** + * A new floating point value + * + * @param value + * the floating point value + * @return a new floating point value + */ + public static NewValue f64(double value) { + return new NewValue.F64(value); + } + + /** + * A new boolean value + * + * @param value + * the boolean value + * @return a new boolean value + */ + public static NewValue bool(boolean value) { + return new NewValue.Bool(value); + } + + /** + * A new string value + * + * @param value + * the string value + * @return a new string value + */ + public static NewValue str(String value) { + return new NewValue.Str(value); + } + + /** + * A new byte array value + * + * @param value + * the byte array value + * @return a new byte array value + */ + public static NewValue bytes(byte[] value) { + return new NewValue.Bytes(value); + } + + /** + * A new counter value + * + * @param value + * the initial value of the counter + * @return a new counter value + */ + public static NewValue counter(long value) { + return new NewValue.Counter(value); + } + + /** + * A new timestamp value + * + * @param value + * the value of the timestamp + * @return a new timestamp value + */ + public static NewValue timestamp(Date value) { + return new NewValue.Timestamp(value); + } + + /** The null value */ + public static NewValue NULL = new NewValue.Null(); + + /** A new unsigned integer value */ + public static class UInt extends NewValue { + private long value; + + protected UInt(long value) { + if (value < 0) { + throw new IllegalArgumentException("UInt must be positive"); + } + this.value = value; + } + + @Override + protected void set(Transaction tx, ObjectId obj, String key) { + tx.setUint(obj, key, value); + } + + @Override + protected void set(Transaction tx, ObjectId obj, long idx) { + tx.setUint(obj, idx, value); + } + + @Override + protected void insert(Transaction tx, ObjectId obj, long idx) { + tx.insertUint(obj, idx, value); + } + + @Override + protected void mark(Transaction tx, ObjectId obj, long start, long end, String markName, ExpandMark expand) { + tx.markUint(obj, start, end, markName, value, expand); + } + } + + /** A new integer value */ + public static class Int extends NewValue { + private long value; + + protected Int(long value) { + this.value = value; + } + + @Override + protected void set(Transaction tx, ObjectId obj, String key) { + tx.set(obj, key, value); + } + + @Override + protected void set(Transaction tx, ObjectId obj, long idx) { + tx.set(obj, idx, value); + } + + @Override + protected void insert(Transaction tx, ObjectId obj, long idx) { + tx.insert(obj, idx, value); + } + + @Override + protected void mark(Transaction tx, ObjectId obj, long start, long end, String markName, ExpandMark expand) { + tx.mark(obj, start, end, markName, value, expand); + } + } + + /** A new floating point value */ + public static class F64 extends NewValue { + private double value; + + protected F64(double value) { + this.value = value; + } + + @Override + protected void set(Transaction tx, ObjectId obj, String key) { + tx.set(obj, key, value); + } + + @Override + protected void set(Transaction tx, ObjectId obj, long idx) { + tx.set(obj, idx, value); + } + + @Override + protected void insert(Transaction tx, ObjectId obj, long idx) { + tx.insert(obj, idx, value); + } + + @Override + protected void mark(Transaction tx, ObjectId obj, long start, long end, String markName, ExpandMark expand) { + tx.mark(obj, start, end, markName, value, expand); + } + } + + /** A new string value */ + public static class Str extends NewValue { + private java.lang.String value; + + protected Str(java.lang.String value) { + this.value = value; + } + + @Override + protected void set(Transaction tx, ObjectId obj, String key) { + tx.set(obj, key, value); + } + + @Override + protected void set(Transaction tx, ObjectId obj, long idx) { + tx.set(obj, idx, value); + } + + @Override + protected void insert(Transaction tx, ObjectId obj, long idx) { + tx.insert(obj, idx, value); + } + + @Override + protected void mark(Transaction tx, ObjectId obj, long start, long end, String markName, ExpandMark expand) { + tx.mark(obj, start, end, markName, value, expand); + } + } + + /** A new boolean value */ + public static class Bool extends NewValue { + private boolean value; + + protected Bool(boolean value) { + this.value = value; + } + + @Override + protected void set(Transaction tx, ObjectId obj, String key) { + tx.set(obj, key, value); + } + + @Override + protected void set(Transaction tx, ObjectId obj, long idx) { + tx.set(obj, idx, value); + } + + @Override + protected void insert(Transaction tx, ObjectId obj, long idx) { + tx.insert(obj, idx, value); + } + + @Override + protected void mark(Transaction tx, ObjectId obj, long start, long end, String markName, ExpandMark expand) { + tx.mark(obj, start, end, markName, value, expand); + } + } + + /** A new null value */ + public static class Null extends NewValue { + @Override + protected void set(Transaction tx, ObjectId obj, String key) { + tx.setNull(obj, key); + } + + @Override + protected void set(Transaction tx, ObjectId obj, long idx) { + tx.setNull(obj, idx); + } + + @Override + protected void insert(Transaction tx, ObjectId obj, long idx) { + tx.insertNull(obj, idx); + } + + @Override + protected void mark(Transaction tx, ObjectId obj, long start, long end, String markName, ExpandMark expand) { + tx.markNull(obj, start, end, markName, expand); + } + } + + /** A new byte array value */ + public static class Bytes extends NewValue { + private byte[] value; + + protected Bytes(byte[] value) { + this.value = value; + } + + @Override + protected void set(Transaction tx, ObjectId obj, String key) { + tx.set(obj, key, value); + } + + @Override + protected void set(Transaction tx, ObjectId obj, long idx) { + tx.set(obj, idx, value); + } + + @Override + protected void insert(Transaction tx, ObjectId obj, long idx) { + tx.insert(obj, idx, value); + } + + @Override + protected void mark(Transaction tx, ObjectId obj, long start, long end, String markName, ExpandMark expand) { + tx.mark(obj, start, end, markName, value, expand); + } + } + + /** A new counter value */ + public static class Counter extends NewValue { + private long value; + + protected Counter(long value) { + this.value = value; + } + + @Override + protected void set(Transaction tx, ObjectId obj, String key) { + tx.set(obj, key, new org.automerge.Counter(value)); + } + + @Override + protected void set(Transaction tx, ObjectId obj, long idx) { + tx.set(obj, idx, new org.automerge.Counter(value)); + } + + @Override + protected void insert(Transaction tx, ObjectId obj, long idx) { + tx.insert(obj, idx, new org.automerge.Counter(value)); + } + + @Override + protected void mark(Transaction tx, ObjectId obj, long start, long end, String markName, ExpandMark expand) { + tx.mark(obj, start, end, markName, new org.automerge.Counter(value), expand); + } + } + + /** A new timestamp value */ + public static class Timestamp extends NewValue { + private Date value; + + protected Timestamp(Date value) { + this.value = value; + } + + @Override + protected void set(Transaction tx, ObjectId obj, String key) { + tx.set(obj, key, value); + } + + @Override + protected void set(Transaction tx, ObjectId obj, long idx) { + tx.set(obj, idx, value); + } + + @Override + protected void insert(Transaction tx, ObjectId obj, long idx) { + tx.insert(obj, idx, value); + } + + @Override + protected void mark(Transaction tx, ObjectId obj, long start, long end, String markName, ExpandMark expand) { + tx.mark(obj, start, end, markName, value, expand); + } + } } diff --git a/lib/src/main/java/org/automerge/ObjectId.java b/lib/src/main/java/org/automerge/ObjectId.java index 65c450a..3e60f4c 100644 --- a/lib/src/main/java/org/automerge/ObjectId.java +++ b/lib/src/main/java/org/automerge/ObjectId.java @@ -9,40 +9,40 @@ * object in automerge is a map, the ID of whic his {@link ObjectId#ROOT}. */ public class ObjectId { - private byte[] raw; - - public static ObjectId ROOT; - - static { - ROOT = AutomergeSys.rootObjectId(); - } - - private ObjectId(byte[] raw) { - this.raw = raw; - } - - public boolean isRoot() { - return AutomergeSys.isRootObjectId(this); - } - - public String toString() { - return AutomergeSys.objectIdToString(this); - } - - @Override - public int hashCode() { - return AutomergeSys.objectIdHash(this); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - ObjectId other = (ObjectId) obj; - return AutomergeSys.objectIdsEqual(this, other); - } + private byte[] raw; + + public static ObjectId ROOT; + + static { + ROOT = AutomergeSys.rootObjectId(); + } + + private ObjectId(byte[] raw) { + this.raw = raw; + } + + public boolean isRoot() { + return AutomergeSys.isRootObjectId(this); + } + + public String toString() { + return AutomergeSys.objectIdToString(this); + } + + @Override + public int hashCode() { + return AutomergeSys.objectIdHash(this); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + ObjectId other = (ObjectId) obj; + return AutomergeSys.objectIdsEqual(this, other); + } } diff --git a/lib/src/main/java/org/automerge/ObjectType.java b/lib/src/main/java/org/automerge/ObjectType.java index d50788e..1d31dc5 100644 --- a/lib/src/main/java/org/automerge/ObjectType.java +++ b/lib/src/main/java/org/automerge/ObjectType.java @@ -1,5 +1,5 @@ package org.automerge; public enum ObjectType { - MAP, LIST, TEXT + MAP, LIST, TEXT } diff --git a/lib/src/main/java/org/automerge/Patch.java b/lib/src/main/java/org/automerge/Patch.java index fe86f1e..5edf495 100644 --- a/lib/src/main/java/org/automerge/Patch.java +++ b/lib/src/main/java/org/automerge/Patch.java @@ -11,40 +11,40 @@ * {@link Document} and describes a change that was made to the document. */ public class Patch { - private final ObjectId obj; - private final ArrayList path; - private final PatchAction action; + private final ObjectId obj; + private final ArrayList path; + private final PatchAction action; - protected Patch(ObjectId obj, PathElement[] path, PatchAction action) { - this.obj = obj; - this.path = new ArrayList<>(Arrays.asList(path)); - this.action = action; - } + protected Patch(ObjectId obj, PathElement[] path, PatchAction action) { + this.obj = obj; + this.path = new ArrayList<>(Arrays.asList(path)); + this.action = action; + } - /** - * The object this patch modifies - * - * @return The object this patch modifies - */ - public ObjectId getObj() { - return obj; - } + /** + * The object this patch modifies + * + * @return The object this patch modifies + */ + public ObjectId getObj() { + return obj; + } - /** - * The path to the object this patch modifies - * - * @return The path to the object this patch modifies - */ - public ArrayList getPath() { - return path; - } + /** + * The path to the object this patch modifies + * + * @return The path to the object this patch modifies + */ + public ArrayList getPath() { + return path; + } - /** - * The modification this patch makes - * - * @return The modification this patch makes - */ - public PatchAction getAction() { - return action; - } + /** + * The modification this patch makes + * + * @return The modification this patch makes + */ + public PatchAction getAction() { + return action; + } } diff --git a/lib/src/main/java/org/automerge/PatchAction.java b/lib/src/main/java/org/automerge/PatchAction.java index 3607ddc..24d618b 100644 --- a/lib/src/main/java/org/automerge/PatchAction.java +++ b/lib/src/main/java/org/automerge/PatchAction.java @@ -6,253 +6,253 @@ /** The set of possible patches */ public abstract class PatchAction { - /** A property was set in a map */ - public static class PutMap extends PatchAction { - private final String key; - private final AmValue value; - private final boolean conflict; - - protected PutMap(String key, AmValue value, boolean conflict) { - this.key = key; - this.value = value; - this.conflict = conflict; - } - - /** - * The key in the map - * - * @return The key in the map - */ - public String getKey() { - return key; - } - - /** - * The new value that was set - * - * @return The new value that was set - */ - public AmValue getValue() { - return value; - } - - /** - * Whether there is now a conflict on this property - * - * @return Whether there is now a conflict on this property - */ - public boolean isConflict() { - return conflict; - } - } - - /** A property was set in a list */ - public static class PutList extends PatchAction { - private final long index; - private final AmValue value; - private final boolean conflict; - - protected PutList(long index, AmValue value, boolean conflict) { - this.index = index; - this.value = value; - this.conflict = conflict; - } - - /** - * The index that was set - * - * @return The index that was set - */ - public long getIndex() { - return index; - } - - /** - * The new value that was set - * - * @return The new value that was set - */ - public AmValue getValue() { - return value; - } - - /** - * Whether there is now a conflict at this index - * - * @return Whether there is now a conflict at this index - */ - public boolean isConflict() { - return conflict; - } - } - - /** Some values were inserted into a list */ - public static class Insert extends PatchAction { - private final long index; - private final ArrayList values; - - protected Insert(long index, AmValue[] values) { - this.index = index; - this.values = new ArrayList<>(Arrays.asList(values)); - } - - /** - * The index that was inserted into - * - * @return The index that was inserted into - */ - public long getIndex() { - return index; - } - - /** - * The new values that were inserted - * - * @return The new values that were inserted - */ - public ArrayList getValues() { - return values; - } - } - - /** Values were spliced into a text object */ - public static class SpliceText extends PatchAction { - private final long index; - private final java.lang.String text; - - protected SpliceText(long index, java.lang.String text) { - this.index = index; - this.text = text; - } - - /** - * The index that was spliced into - * - * @return The index that was spliced into - */ - public long getIndex() { - return index; - } - - /** - * The new text that was spliced in - * - * @return The new text that was spliced in - */ - public java.lang.String getText() { - return text; - } - } - - /** A counter was incremented */ - public static class Increment extends PatchAction { - private Prop property; - private final long value; - - protected Increment(Prop property, long value) { - this.property = property; - this.value = value; - } - - /** - * The property that was incremented - * - * @return The property that was incremented - */ - public Prop getProperty() { - return property; - } - - /** - * The value that was added to the counter - * - * @return The value that was added to the counter - */ - public long getValue() { - return value; - } - } - - /** A key was deleted from a map */ - public static class DeleteMap extends PatchAction { - private final String key; - - protected DeleteMap(String key) { - this.key = key; - } - - /** - * The key that was deleted - * - * @return The key that was deleted - */ - public String getKey() { - return key; - } - } - - /** One or more values were deleted from a list */ - public static class DeleteList extends PatchAction { - private final long index; - private final long length; - - protected DeleteList(long index, long length) { - this.index = index; - this.length = length; - } - - /** - * The index that was deleted - * - * @return The index that was deleted - */ - public long getIndex() { - return index; - } - - /** - * The number of values that were deleted - * - * @return The number of values that were deleted - */ - public long getLength() { - return length; - } - } - - /** One or more marks were created in a text object */ - public static class Mark extends PatchAction { - private final org.automerge.Mark[] marks; - - protected Mark(org.automerge.Mark[] marks) { - this.marks = marks; - } - - /** - * The marks that were created - * - * @return The marks that were created - */ - public org.automerge.Mark[] getMarks() { - return marks; - } - } - - /** A property which was already in the document is now conflicted */ - public static class FlagConflict extends PatchAction { - private final Prop property; - - protected FlagConflict(Prop property) { - this.property = property; - } - - /** - * The property that was conflicted - * - * @return The property that was conflicted - */ - public Prop getProperty() { - return property; - } - } + /** A property was set in a map */ + public static class PutMap extends PatchAction { + private final String key; + private final AmValue value; + private final boolean conflict; + + protected PutMap(String key, AmValue value, boolean conflict) { + this.key = key; + this.value = value; + this.conflict = conflict; + } + + /** + * The key in the map + * + * @return The key in the map + */ + public String getKey() { + return key; + } + + /** + * The new value that was set + * + * @return The new value that was set + */ + public AmValue getValue() { + return value; + } + + /** + * Whether there is now a conflict on this property + * + * @return Whether there is now a conflict on this property + */ + public boolean isConflict() { + return conflict; + } + } + + /** A property was set in a list */ + public static class PutList extends PatchAction { + private final long index; + private final AmValue value; + private final boolean conflict; + + protected PutList(long index, AmValue value, boolean conflict) { + this.index = index; + this.value = value; + this.conflict = conflict; + } + + /** + * The index that was set + * + * @return The index that was set + */ + public long getIndex() { + return index; + } + + /** + * The new value that was set + * + * @return The new value that was set + */ + public AmValue getValue() { + return value; + } + + /** + * Whether there is now a conflict at this index + * + * @return Whether there is now a conflict at this index + */ + public boolean isConflict() { + return conflict; + } + } + + /** Some values were inserted into a list */ + public static class Insert extends PatchAction { + private final long index; + private final ArrayList values; + + protected Insert(long index, AmValue[] values) { + this.index = index; + this.values = new ArrayList<>(Arrays.asList(values)); + } + + /** + * The index that was inserted into + * + * @return The index that was inserted into + */ + public long getIndex() { + return index; + } + + /** + * The new values that were inserted + * + * @return The new values that were inserted + */ + public ArrayList getValues() { + return values; + } + } + + /** Values were spliced into a text object */ + public static class SpliceText extends PatchAction { + private final long index; + private final java.lang.String text; + + protected SpliceText(long index, java.lang.String text) { + this.index = index; + this.text = text; + } + + /** + * The index that was spliced into + * + * @return The index that was spliced into + */ + public long getIndex() { + return index; + } + + /** + * The new text that was spliced in + * + * @return The new text that was spliced in + */ + public java.lang.String getText() { + return text; + } + } + + /** A counter was incremented */ + public static class Increment extends PatchAction { + private Prop property; + private final long value; + + protected Increment(Prop property, long value) { + this.property = property; + this.value = value; + } + + /** + * The property that was incremented + * + * @return The property that was incremented + */ + public Prop getProperty() { + return property; + } + + /** + * The value that was added to the counter + * + * @return The value that was added to the counter + */ + public long getValue() { + return value; + } + } + + /** A key was deleted from a map */ + public static class DeleteMap extends PatchAction { + private final String key; + + protected DeleteMap(String key) { + this.key = key; + } + + /** + * The key that was deleted + * + * @return The key that was deleted + */ + public String getKey() { + return key; + } + } + + /** One or more values were deleted from a list */ + public static class DeleteList extends PatchAction { + private final long index; + private final long length; + + protected DeleteList(long index, long length) { + this.index = index; + this.length = length; + } + + /** + * The index that was deleted + * + * @return The index that was deleted + */ + public long getIndex() { + return index; + } + + /** + * The number of values that were deleted + * + * @return The number of values that were deleted + */ + public long getLength() { + return length; + } + } + + /** One or more marks were created in a text object */ + public static class Mark extends PatchAction { + private final org.automerge.Mark[] marks; + + protected Mark(org.automerge.Mark[] marks) { + this.marks = marks; + } + + /** + * The marks that were created + * + * @return The marks that were created + */ + public org.automerge.Mark[] getMarks() { + return marks; + } + } + + /** A property which was already in the document is now conflicted */ + public static class FlagConflict extends PatchAction { + private final Prop property; + + protected FlagConflict(Prop property) { + this.property = property; + } + + /** + * The property that was conflicted + * + * @return The property that was conflicted + */ + public Prop getProperty() { + return property; + } + } } diff --git a/lib/src/main/java/org/automerge/PatchLog.java b/lib/src/main/java/org/automerge/PatchLog.java index b0c2c02..819c510 100644 --- a/lib/src/main/java/org/automerge/PatchLog.java +++ b/lib/src/main/java/org/automerge/PatchLog.java @@ -6,40 +6,40 @@ import org.automerge.AutomergeSys.PatchLogPointer; public class PatchLog { - private Optional pointer; - - public PatchLog() { - pointer = Optional.of(AutomergeSys.createPatchLog()); - } - - synchronized T with(Function f) { - return f.apply(pointer.get()); - } - - synchronized PatchLogPointer take() { - if (pointer.isPresent()) { - PatchLogPointer p = pointer.get(); - pointer = Optional.empty(); - return p; - } else { - throw new IllegalStateException("PatchLog already in use"); - } - } - - synchronized void put(PatchLogPointer p) { - if (pointer.isPresent()) { - throw new IllegalStateException("PatchLog already in use"); - } else { - pointer = Optional.of(p); - } - } - - synchronized void with(Consumer f) { - f.accept(pointer.get()); - } - - public synchronized void free() { - pointer.ifPresent(AutomergeSys::freePatchLog); - pointer = Optional.empty(); - } + private Optional pointer; + + public PatchLog() { + pointer = Optional.of(AutomergeSys.createPatchLog()); + } + + synchronized T with(Function f) { + return f.apply(pointer.get()); + } + + synchronized PatchLogPointer take() { + if (pointer.isPresent()) { + PatchLogPointer p = pointer.get(); + pointer = Optional.empty(); + return p; + } else { + throw new IllegalStateException("PatchLog already in use"); + } + } + + synchronized void put(PatchLogPointer p) { + if (pointer.isPresent()) { + throw new IllegalStateException("PatchLog already in use"); + } else { + pointer = Optional.of(p); + } + } + + synchronized void with(Consumer f) { + f.accept(pointer.get()); + } + + public synchronized void free() { + pointer.ifPresent(AutomergeSys::freePatchLog); + pointer = Optional.empty(); + } } diff --git a/lib/src/main/java/org/automerge/PathElement.java b/lib/src/main/java/org/automerge/PathElement.java index 41304d8..69d0f90 100644 --- a/lib/src/main/java/org/automerge/PathElement.java +++ b/lib/src/main/java/org/automerge/PathElement.java @@ -2,60 +2,60 @@ /** A single element in a path to a property in a document */ public class PathElement { - private final ObjectId objectId; - private final Prop prop; + private final ObjectId objectId; + private final Prop prop; - protected PathElement(ObjectId objectId, Prop prop) { - this.objectId = objectId; - this.prop = prop; - } + protected PathElement(ObjectId objectId, Prop prop) { + this.objectId = objectId; + this.prop = prop; + } - /** - * The object this element points at - * - * @return The object this element points at - */ - public ObjectId getObjectId() { - return objectId; - } + /** + * The object this element points at + * + * @return The object this element points at + */ + public ObjectId getObjectId() { + return objectId; + } - /** - * The property within the object that this path points at - * - * @return The property within the object that this path points at - */ - public Prop getProp() { - return prop; - } + /** + * The property within the object that this path points at + * + * @return The property within the object that this path points at + */ + public Prop getProp() { + return prop; + } - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((objectId == null) ? 0 : objectId.hashCode()); - result = prime * result + ((prop == null) ? 0 : prop.hashCode()); - return result; - } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((objectId == null) ? 0 : objectId.hashCode()); + result = prime * result + ((prop == null) ? 0 : prop.hashCode()); + return result; + } - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - PathElement other = (PathElement) obj; - if (objectId == null) { - if (other.objectId != null) - return false; - } else if (!objectId.equals(other.objectId)) - return false; - if (prop == null) { - if (other.prop != null) - return false; - } else if (!prop.equals(other.prop)) - return false; - return true; - } + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + PathElement other = (PathElement) obj; + if (objectId == null) { + if (other.objectId != null) + return false; + } else if (!objectId.equals(other.objectId)) + return false; + if (prop == null) { + if (other.prop != null) + return false; + } else if (!prop.equals(other.prop)) + return false; + return true; + } } diff --git a/lib/src/main/java/org/automerge/Prop.java b/lib/src/main/java/org/automerge/Prop.java index 71604d4..8102c1f 100644 --- a/lib/src/main/java/org/automerge/Prop.java +++ b/lib/src/main/java/org/automerge/Prop.java @@ -2,86 +2,86 @@ /** The two kinds of property in a document */ public abstract class Prop { - /** A key in a map */ - public static final class Key extends Prop { - public final String key; + /** A key in a map */ + public static final class Key extends Prop { + public final String key; - public Key(String key) { - this.key = key; - } + public Key(String key) { + this.key = key; + } - /** - * The key - * - * @return The key - */ - public String getValue() { - return key; - } + /** + * The key + * + * @return The key + */ + public String getValue() { + return key; + } - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((key == null) ? 0 : key.hashCode()); - return result; - } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((key == null) ? 0 : key.hashCode()); + return result; + } - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - Key other = (Key) obj; - if (key == null) { - if (other.key != null) - return false; - } else if (!key.equals(other.key)) - return false; - return true; - } - } + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Key other = (Key) obj; + if (key == null) { + if (other.key != null) + return false; + } else if (!key.equals(other.key)) + return false; + return true; + } + } - /** An index in a list or text */ - public static final class Index extends Prop { - public final long index; + /** An index in a list or text */ + public static final class Index extends Prop { + public final long index; - public Index(long index) { - this.index = index; - } + public Index(long index) { + this.index = index; + } - /** - * The index - * - * @return The index - */ - public long getValue() { - return index; - } + /** + * The index + * + * @return The index + */ + public long getValue() { + return index; + } - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + (int) (index ^ (index >>> 32)); - return result; - } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (int) (index ^ (index >>> 32)); + return result; + } - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - Index other = (Index) obj; - if (index != other.index) - return false; - return true; - } - } + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Index other = (Index) obj; + if (index != other.index) + return false; + return true; + } + } } diff --git a/lib/src/main/java/org/automerge/Read.java b/lib/src/main/java/org/automerge/Read.java index da56ce9..751116e 100644 --- a/lib/src/main/java/org/automerge/Read.java +++ b/lib/src/main/java/org/automerge/Read.java @@ -6,417 +6,417 @@ /** Methods to read from a document */ public interface Read { - /** - * Get a value from the map given by obj - * - *

- * Note that if there are multiple conflicting values for the key this method - * will arbirtarily return one of them. The choice will be deterministic, in the - * sense that any other document with the same set of changes will return the - * same value. To get all the values use {@link getAll} - * - * @param obj - * - The ID of the map to get the value from - * @param key - * - The key to get the value for - * @return The value of the key or `Optional.empty` if not present - * @throws AutomergeException - * if the object ID is not a map - */ - public Optional get(ObjectId obj, String key); + /** + * Get a value from the map given by obj + * + *

+ * Note that if there are multiple conflicting values for the key this method + * will arbirtarily return one of them. The choice will be deterministic, in the + * sense that any other document with the same set of changes will return the + * same value. To get all the values use {@link getAll} + * + * @param obj + * - The ID of the map to get the value from + * @param key + * - The key to get the value for + * @return The value of the key or `Optional.empty` if not present + * @throws AutomergeException + * if the object ID is not a map + */ + public Optional get(ObjectId obj, String key); - /** - * Get a value from the map given by obj as at heads - * - *

- * Note that if there are multiple conflicting values for the key this method - * will arbirtarily return one of them. The choice will be deterministic, in the - * sense that any other document with the same set of changes will return the - * same value. To get all the values use {@link getAll} - * - * @param obj - * - The ID of the map to get the value from - * @param key - * - The key to get the value for - * @param heads - * - The heads of the version of the document to get the value from - * @return The value of the key or `Optional.empty` if not present - * @throws AutomergeException - * if the object ID is not a map - */ - public Optional get(ObjectId obj, String key, ChangeHash[] heads); + /** + * Get a value from the map given by obj as at heads + * + *

+ * Note that if there are multiple conflicting values for the key this method + * will arbirtarily return one of them. The choice will be deterministic, in the + * sense that any other document with the same set of changes will return the + * same value. To get all the values use {@link getAll} + * + * @param obj + * - The ID of the map to get the value from + * @param key + * - The key to get the value for + * @param heads + * - The heads of the version of the document to get the value from + * @return The value of the key or `Optional.empty` if not present + * @throws AutomergeException + * if the object ID is not a map + */ + public Optional get(ObjectId obj, String key, ChangeHash[] heads); - /** - * Get a value from the list given by obj - * - *

- * Note that if there are multiple conflicting values for the index this method - * will arbirtarily return one of them. The choice will be deterministic, in the - * sense that any other document with the same set of changes will return the - * same value. To get all the values use {@link getAll} - * - * @param obj - * - The ID of the list to get the value from - * @param idx - * - The index to get the value for - * @return The value at the index or `Optional.empty` if the index is out of - * range - * @throws AutomergeException - * if the object ID is not a list - */ - public Optional get(ObjectId obj, long idx); + /** + * Get a value from the list given by obj + * + *

+ * Note that if there are multiple conflicting values for the index this method + * will arbirtarily return one of them. The choice will be deterministic, in the + * sense that any other document with the same set of changes will return the + * same value. To get all the values use {@link getAll} + * + * @param obj + * - The ID of the list to get the value from + * @param idx + * - The index to get the value for + * @return The value at the index or `Optional.empty` if the index is out of + * range + * @throws AutomergeException + * if the object ID is not a list + */ + public Optional get(ObjectId obj, long idx); - /** - * Get a value from the list given by obj as at heads - * - *

- * Note that if there are multiple conflicting values for the index this method - * will arbirtarily return one of them. The choice will be deterministic, in the - * sense that any other document with the same set of changes will return the - * same value. To get all the values use {@link getAll} - * - * @param obj - * - The ID of the list to get the value from - * @param idx - * - The index to get the value for - * @param heads - * - The heads of the version of the document to get the value from - * @return The value at the index or `Optional.empty` if the index is out of - * range - * @throws AutomergeException - * if the object ID is not a list - */ - public Optional get(ObjectId obj, long idx, ChangeHash[] heads); + /** + * Get a value from the list given by obj as at heads + * + *

+ * Note that if there are multiple conflicting values for the index this method + * will arbirtarily return one of them. The choice will be deterministic, in the + * sense that any other document with the same set of changes will return the + * same value. To get all the values use {@link getAll} + * + * @param obj + * - The ID of the list to get the value from + * @param idx + * - The index to get the value for + * @param heads + * - The heads of the version of the document to get the value from + * @return The value at the index or `Optional.empty` if the index is out of + * range + * @throws AutomergeException + * if the object ID is not a list + */ + public Optional get(ObjectId obj, long idx, ChangeHash[] heads); - /** - * Get all the possibly conflicting values for a key from the map given by obj - * - *

- * If there are concurrent set operations to a key in a map there is no way to - * resolve that conflict so automerge retains all concurrently set values which - * can then be obtained via this method. If you don't care about conflicts and - * just want to arbitrarily (but deterministically) choose a value use - * {@link get} - * - * @param obj - * - The ID of the map to get the value from - * @param key - * - The key to get the value for - * @return The values - * @throws AutomergeException - * if the object ID refers to an object which is not a map - */ - public Optional getAll(ObjectId obj, String key); + /** + * Get all the possibly conflicting values for a key from the map given by obj + * + *

+ * If there are concurrent set operations to a key in a map there is no way to + * resolve that conflict so automerge retains all concurrently set values which + * can then be obtained via this method. If you don't care about conflicts and + * just want to arbitrarily (but deterministically) choose a value use + * {@link get} + * + * @param obj + * - The ID of the map to get the value from + * @param key + * - The key to get the value for + * @return The values + * @throws AutomergeException + * if the object ID refers to an object which is not a map + */ + public Optional getAll(ObjectId obj, String key); - /** - * Get all the possibly conflicting values for a key from the map given by obj - * as at the given heads - * - *

- * If there are concurrent set operations to a key in a map there is no way to - * resolve that conflict so automerge retains all concurrently set values which - * can then be obtained via this method. If you don't care about conflicts and - * just want to arbitrarily (but deterministically) choose a value use - * {@link get} - * - * @param obj - * - The ID of the map to get the value from - * @param key - * - The key to get the value for - * @param heads - * - The heads of the version of the document to get the value from - * @return The values - * @throws AutomergeException - * if the object ID refers to an object which is not a map - */ - public Optional getAll(ObjectId obj, String key, ChangeHash[] heads); + /** + * Get all the possibly conflicting values for a key from the map given by obj + * as at the given heads + * + *

+ * If there are concurrent set operations to a key in a map there is no way to + * resolve that conflict so automerge retains all concurrently set values which + * can then be obtained via this method. If you don't care about conflicts and + * just want to arbitrarily (but deterministically) choose a value use + * {@link get} + * + * @param obj + * - The ID of the map to get the value from + * @param key + * - The key to get the value for + * @param heads + * - The heads of the version of the document to get the value from + * @return The values + * @throws AutomergeException + * if the object ID refers to an object which is not a map + */ + public Optional getAll(ObjectId obj, String key, ChangeHash[] heads); - /** - * Get all the possibly conflicting values for an index in the list given by obj - * - *

- * If there are concurrent set operations to an index in a list there is no way - * to resolve that conflict so automerge retains all concurrently set values - * which can then be obtained via this method. If you don't care about conflicts - * and just want to arbitrarily (but deterministically) choose a value use - * {@link get} - * - * @param obj - * - The ID of the map to get the value from - * @param idx - * - The index to get the value for - * @return The values - * @throws AutomergeException - * if the object ID refers to an object which is not a map - */ - public Optional getAll(ObjectId obj, long idx); + /** + * Get all the possibly conflicting values for an index in the list given by obj + * + *

+ * If there are concurrent set operations to an index in a list there is no way + * to resolve that conflict so automerge retains all concurrently set values + * which can then be obtained via this method. If you don't care about conflicts + * and just want to arbitrarily (but deterministically) choose a value use + * {@link get} + * + * @param obj + * - The ID of the map to get the value from + * @param idx + * - The index to get the value for + * @return The values + * @throws AutomergeException + * if the object ID refers to an object which is not a map + */ + public Optional getAll(ObjectId obj, long idx); - /** - * Get all the possibly conflicting values for an index in the list given by obj - * as at the given heads - * - *

- * If there are concurrent set operations to an index in a list there is no way - * to resolve that conflict so automerge retains all concurrently set values - * which can then be obtained via this method. If you don't care about conflicts - * and just want to arbitrarily (but deterministically) choose a value use - * {@link get} - * - * @param obj - * - The ID of the map to get the value from - * @param idx - * - The index to get the value for - * @param heads - * - The heads of the version of the document to get the value from - * @return The values - * @throws AutomergeException - * if the object ID refers to an object which is not a map - */ - public Optional getAll(ObjectId obj, long idx, ChangeHash[] heads); + /** + * Get all the possibly conflicting values for an index in the list given by obj + * as at the given heads + * + *

+ * If there are concurrent set operations to an index in a list there is no way + * to resolve that conflict so automerge retains all concurrently set values + * which can then be obtained via this method. If you don't care about conflicts + * and just want to arbitrarily (but deterministically) choose a value use + * {@link get} + * + * @param obj + * - The ID of the map to get the value from + * @param idx + * - The index to get the value for + * @param heads + * - The heads of the version of the document to get the value from + * @return The values + * @throws AutomergeException + * if the object ID refers to an object which is not a map + */ + public Optional getAll(ObjectId obj, long idx, ChangeHash[] heads); - /** - * Get the value of a text object - * - * @param obj - * - The ID of the text object to get the value from - * @return The text or None if no such object exists - * @throws AutomergeException - * if the object ID refers to an object which is not a text object - */ - public Optional text(ObjectId obj); + /** + * Get the value of a text object + * + * @param obj + * - The ID of the text object to get the value from + * @return The text or None if no such object exists + * @throws AutomergeException + * if the object ID refers to an object which is not a text object + */ + public Optional text(ObjectId obj); - /** - * Get the value of a text object as at the given heads - * - * @param obj - * - The ID of the text object to get the value from - * @param heads - * - The heads of the version of the document to get the value from - * @return The text or None if it does not exist - * @throws AutomergeException - * if the object ID refers to an object which is not a text object - */ - public Optional text(ObjectId obj, ChangeHash[] heads); + /** + * Get the value of a text object as at the given heads + * + * @param obj + * - The ID of the text object to get the value from + * @param heads + * - The heads of the version of the document to get the value from + * @return The text or None if it does not exist + * @throws AutomergeException + * if the object ID refers to an object which is not a text object + */ + public Optional text(ObjectId obj, ChangeHash[] heads); - /** - * Get the keys of the object given by obj - * - * @param obj - * - The ID of the object to get the keys from - * @return The keys of the object or None if the object is not a map - */ - public Optional keys(ObjectId obj); + /** + * Get the keys of the object given by obj + * + * @param obj + * - The ID of the object to get the keys from + * @return The keys of the object or None if the object is not a map + */ + public Optional keys(ObjectId obj); - /** - * Get the keys of the object given by obj as at the given heads - * - * @param obj - * - The ID of the object to get the keys from - * @param heads - * - The heads of the version of the document to get the keys from - * @return The keys of the object or None if the object is not a map - */ - public Optional keys(ObjectId obj, ChangeHash[] heads); + /** + * Get the keys of the object given by obj as at the given heads + * + * @param obj + * - The ID of the object to get the keys from + * @param heads + * - The heads of the version of the document to get the keys from + * @return The keys of the object or None if the object is not a map + */ + public Optional keys(ObjectId obj, ChangeHash[] heads); - /** - * Get the entries of the map given by obj - * - * @param obj - * - The ID of the map to get the entries from - * @return The entries of the map or None if the object is not a map - */ - public Optional mapEntries(ObjectId obj); + /** + * Get the entries of the map given by obj + * + * @param obj + * - The ID of the map to get the entries from + * @return The entries of the map or None if the object is not a map + */ + public Optional mapEntries(ObjectId obj); - /** - * Get the entries of the map given by obj as at the given heads - * - * @param obj - * - The ID of the map to get the entries from - * @param heads - * - The heads of the version of the document to get the entries from - * @return The entries of the map or None if the object is not a map - */ - public Optional mapEntries(ObjectId obj, ChangeHash[] heads); + /** + * Get the entries of the map given by obj as at the given heads + * + * @param obj + * - The ID of the map to get the entries from + * @param heads + * - The heads of the version of the document to get the entries from + * @return The entries of the map or None if the object is not a map + */ + public Optional mapEntries(ObjectId obj, ChangeHash[] heads); - /** - * Get the values in the list given by obj - * - * @param obj - * - The ID of the list to get the values from - * @return The values of the list or None if the object is not a list - */ - public Optional listItems(ObjectId obj); + /** + * Get the values in the list given by obj + * + * @param obj + * - The ID of the list to get the values from + * @return The values of the list or None if the object is not a list + */ + public Optional listItems(ObjectId obj); - /** - * Get the values in the list given by obj as at the given heads - * - * @param obj - * - The ID of the list to get the values from - * @param heads - * - The heads of the version of the document to get the values from - * @return The values of the list or None if the object is not a list - */ - public Optional listItems(ObjectId obj, ChangeHash[] heads); + /** + * Get the values in the list given by obj as at the given heads + * + * @param obj + * - The ID of the list to get the values from + * @param heads + * - The heads of the version of the document to get the values from + * @return The values of the list or None if the object is not a list + */ + public Optional listItems(ObjectId obj, ChangeHash[] heads); - /** - * Get the length of the list given by obj - * - * @param obj - * - The ID of the list to get the length of - * @return The length of the list (this will be zero if the object is not a - * list) - */ - public long length(ObjectId obj); + /** + * Get the length of the list given by obj + * + * @param obj + * - The ID of the list to get the length of + * @return The length of the list (this will be zero if the object is not a + * list) + */ + public long length(ObjectId obj); - /** - * Get the length of the list given by obj as at the given heads - * - * @param obj - * - The ID of the list to get the length of - * @param heads - * - The heads of the version of the document to get the length from - * @return The length of the list (this will be zero if the object is not a - * list) - */ - public long length(ObjectId obj, ChangeHash[] heads); + /** + * Get the length of the list given by obj as at the given heads + * + * @param obj + * - The ID of the list to get the length of + * @param heads + * - The heads of the version of the document to get the length from + * @return The length of the list (this will be zero if the object is not a + * list) + */ + public long length(ObjectId obj, ChangeHash[] heads); - /** - * Get the marks for the text object given by obj - * - * @param obj - * - The ID of the text object to get the marks from - * @return The marks of the text object or None if the object is not a text - * object - */ - public List marks(ObjectId obj); + /** + * Get the marks for the text object given by obj + * + * @param obj + * - The ID of the text object to get the marks from + * @return The marks of the text object or None if the object is not a text + * object + */ + public List marks(ObjectId obj); - /** - * Get the marks for the text object given by obj as at the given heads - * - * @param obj - * - The ID of the text object to get the marks from - * @param heads - * - The heads of the version of the document to get the marks from - * @return The marks of the text object or None if the object is not a text - * object - */ - public List marks(ObjectId obj, ChangeHash[] heads); + /** + * Get the marks for the text object given by obj as at the given heads + * + * @param obj + * - The ID of the text object to get the marks from + * @param heads + * - The heads of the version of the document to get the marks from + * @return The marks of the text object or None if the object is not a text + * object + */ + public List marks(ObjectId obj, ChangeHash[] heads); - /** - * Get the marks defined at the given index in a text object - * - * @param obj - * - The ID of the text object to get the marks from - * @param index - * - The index to get the marks at - * @return The marks at the given index or None if the object is not a text - * object - */ - public HashMap getMarksAtIndex(ObjectId obj, long index); + /** + * Get the marks defined at the given index in a text object + * + * @param obj + * - The ID of the text object to get the marks from + * @param index + * - The index to get the marks at + * @return The marks at the given index or None if the object is not a text + * object + */ + public HashMap getMarksAtIndex(ObjectId obj, long index); - /** - * Get the marks defined at the given index in a text object - * - * @param obj - * - The ID of the text object to get the marks from - * @param index - * - The index to get the marks at - * @param heads - * - The heads of the version of the document to get the marks from - * @return The marks at the given index or None if the object is not a text - * object - */ - public HashMap getMarksAtIndex(ObjectId obj, long index, ChangeHash[] heads); + /** + * Get the marks defined at the given index in a text object + * + * @param obj + * - The ID of the text object to get the marks from + * @param index + * - The index to get the marks at + * @param heads + * - The heads of the version of the document to get the marks from + * @return The marks at the given index or None if the object is not a text + * object + */ + public HashMap getMarksAtIndex(ObjectId obj, long index, ChangeHash[] heads); - /** - * Get the heads of the object - * - * @return The heads of the document - *

- * The returned heads represent the current version of the document and - * can be passed to many other methods to refer to the document as at - * this moment. - */ - public ChangeHash[] getHeads(); + /** + * Get the heads of the object + * + * @return The heads of the document + *

+ * The returned heads represent the current version of the document and + * can be passed to many other methods to refer to the document as at + * this moment. + */ + public ChangeHash[] getHeads(); - /** - * Get a cursor which refers to the given index in a list or text object - * - * @param obj - * - The ID of the list or text object to get the cursor for - * @param index - * - The index to get the cursor for - * - * @return The cursor - * - * @throws AutomergeException - * if the object ID refers to an object which is not a list or text - * object or if the index is out of range - */ - public Cursor makeCursor(ObjectId obj, long index); + /** + * Get a cursor which refers to the given index in a list or text object + * + * @param obj + * - The ID of the list or text object to get the cursor for + * @param index + * - The index to get the cursor for + * + * @return The cursor + * + * @throws AutomergeException + * if the object ID refers to an object which is not a list or text + * object or if the index is out of range + */ + public Cursor makeCursor(ObjectId obj, long index); - /** - * Get a cursor which refers to the given index in a list or text object as at - * the given heads - * - * @param obj - * - The ID of the list or text object to get the cursor for - * @param index - * - The index to get the cursor for - * @param heads - * - The heads of the version of the document to make the cursor from - * - * @return The cursor - * - * @throws AutomergeException - * if the object ID refers to an object which is not a list or text - * object or if the index is out of range - */ - public Cursor makeCursor(ObjectId obj, long index, ChangeHash[] heads); + /** + * Get a cursor which refers to the given index in a list or text object as at + * the given heads + * + * @param obj + * - The ID of the list or text object to get the cursor for + * @param index + * - The index to get the cursor for + * @param heads + * - The heads of the version of the document to make the cursor from + * + * @return The cursor + * + * @throws AutomergeException + * if the object ID refers to an object which is not a list or text + * object or if the index is out of range + */ + public Cursor makeCursor(ObjectId obj, long index, ChangeHash[] heads); - /** - * Given a cursor for an object, get the index the cursor points at - * - * @param obj - * - The ID of the object the cursor refers into - * @param cursor - * - The cursor - * - * @return The index the cursor points at - * @throws AutomergeException - * if the object ID refers to an object which is not a list or text - * object or if the cursor does not refer to an element in the - * object - */ - public long lookupCursorIndex(ObjectId obj, Cursor cursor); + /** + * Given a cursor for an object, get the index the cursor points at + * + * @param obj + * - The ID of the object the cursor refers into + * @param cursor + * - The cursor + * + * @return The index the cursor points at + * @throws AutomergeException + * if the object ID refers to an object which is not a list or text + * object or if the cursor does not refer to an element in the + * object + */ + public long lookupCursorIndex(ObjectId obj, Cursor cursor); - /** - * Given a cursor for an object, get the index the cursor points at as at the - * given heads - * - * @param obj - * - The ID of the object the cursor refers into - * @param cursor - * - The cursor - * @param heads - * - The heads of the version of the document to make the cursor from - * - * @return The index the cursor points at - * @throws AutomergeException - * if the object ID refers to an object which is not a list or text - * object or if the cursor does not refer to an element in the - * object - */ - public long lookupCursorIndex(ObjectId obj, Cursor cursor, ChangeHash[] heads); + /** + * Given a cursor for an object, get the index the cursor points at as at the + * given heads + * + * @param obj + * - The ID of the object the cursor refers into + * @param cursor + * - The cursor + * @param heads + * - The heads of the version of the document to make the cursor from + * + * @return The index the cursor points at + * @throws AutomergeException + * if the object ID refers to an object which is not a list or text + * object or if the cursor does not refer to an element in the + * object + */ + public long lookupCursorIndex(ObjectId obj, Cursor cursor, ChangeHash[] heads); - /** - * Get the object type of the object given by obj - * - * @param obj - * - The ID of the object to get the type of - * - * @return The type of the object or Optional.empty if the object does not exist - * in this document - */ - public Optional getObjectType(ObjectId obj); + /** + * Get the object type of the object given by obj + * + * @param obj + * - The ID of the object to get the type of + * + * @return The type of the object or Optional.empty if the object does not exist + * in this document + */ + public Optional getObjectType(ObjectId obj); } diff --git a/lib/src/main/java/org/automerge/SyncState.java b/lib/src/main/java/org/automerge/SyncState.java index fa390ed..b2947c8 100644 --- a/lib/src/main/java/org/automerge/SyncState.java +++ b/lib/src/main/java/org/automerge/SyncState.java @@ -27,72 +27,72 @@ * will mean re-syncing later may require less round trips. */ public class SyncState { - private Optional pointer; + private Optional pointer; - private SyncState(AutomergeSys.SyncStatePointer pointer) { - this.pointer = Optional.of(pointer); - } + private SyncState(AutomergeSys.SyncStatePointer pointer) { + this.pointer = Optional.of(pointer); + } - /** Create a new sync state for a new connection */ - public SyncState() { - this(AutomergeSys.createSyncState()); - } + /** Create a new sync state for a new connection */ + public SyncState() { + this(AutomergeSys.createSyncState()); + } - /** - * Decode a previously encoded sync state - * - * @param encoded - * The encoded sync state - * @return The decoded sync state - * @throws AutomergeException - * if the encoded sync state is not valid - */ - public static SyncState decode(byte[] encoded) { - return new SyncState(AutomergeSys.decodeSyncState(encoded)); - } + /** + * Decode a previously encoded sync state + * + * @param encoded + * The encoded sync state + * @return The decoded sync state + * @throws AutomergeException + * if the encoded sync state is not valid + */ + public static SyncState decode(byte[] encoded) { + return new SyncState(AutomergeSys.decodeSyncState(encoded)); + } - protected synchronized Optional generateSyncMessage(Document doc) { - return doc.generateSyncMessage(this.pointer.get()); - } + protected synchronized Optional generateSyncMessage(Document doc) { + return doc.generateSyncMessage(this.pointer.get()); + } - protected synchronized void receiveSyncMessage(Document doc, byte[] message) { - doc.receiveSyncMessage(this.pointer.get(), message); - } + protected synchronized void receiveSyncMessage(Document doc, byte[] message) { + doc.receiveSyncMessage(this.pointer.get(), message); + } - protected synchronized void receiveSyncMessageLogPatches(Document doc, PatchLog patchLog, byte[] message) { - patchLog.with(p -> { - doc.receiveSyncMessageLogPatches(this.pointer.get(), p, message); - }); - } + protected synchronized void receiveSyncMessageLogPatches(Document doc, PatchLog patchLog, byte[] message) { + patchLog.with(p -> { + doc.receiveSyncMessageLogPatches(this.pointer.get(), p, message); + }); + } - /** - * Encode the sync state for storage - * - * @return The encoded sync state - */ - public synchronized byte[] encode() { - return AutomergeSys.encodeSyncState(this.pointer.get()); - } + /** + * Encode the sync state for storage + * + * @return The encoded sync state + */ + public synchronized byte[] encode() { + return AutomergeSys.encodeSyncState(this.pointer.get()); + } - /** Free the memory associated with this sync state */ - public synchronized void free() { - if (this.pointer.isPresent()) { - AutomergeSys.freeSyncState(this.pointer.get()); - this.pointer = Optional.empty(); - } - } + /** Free the memory associated with this sync state */ + public synchronized void free() { + if (this.pointer.isPresent()) { + AutomergeSys.freeSyncState(this.pointer.get()); + this.pointer = Optional.empty(); + } + } - /** - * Whether the other end of this sync connection is in sync with `doc` - * - * @param doc - * The document to check we are in sync with - * @return Whether we are in sync - */ - public synchronized boolean isInSync(Document doc) { - Set shared = new HashSet( - Arrays.asList(AutomergeSys.syncStateSharedHeads(this.pointer.get()))); - Set ours = new HashSet(Arrays.asList(doc.getHeads())); - return shared.equals(ours); - } + /** + * Whether the other end of this sync connection is in sync with `doc` + * + * @param doc + * The document to check we are in sync with + * @return Whether we are in sync + */ + public synchronized boolean isInSync(Document doc) { + Set shared = new HashSet( + Arrays.asList(AutomergeSys.syncStateSharedHeads(this.pointer.get()))); + Set ours = new HashSet(Arrays.asList(doc.getHeads())); + return shared.equals(ours); + } } diff --git a/lib/src/main/java/org/automerge/Transaction.java b/lib/src/main/java/org/automerge/Transaction.java index 08e083e..3d6f777 100644 --- a/lib/src/main/java/org/automerge/Transaction.java +++ b/lib/src/main/java/org/automerge/Transaction.java @@ -9,807 +9,807 @@ * */ public interface Transaction extends Read, AutoCloseable { - /** - * Commit the transaction - * - *

- * Once a transaction has been committed any attempt to use it will throw an - * exception - * - * @return the result of the commit or {@link Optional#empty()} if the - * transaction made no changes - */ - public Optional commit(); - - /** - * Close the transaction and reverse any changes made - * - *

- * Once a transaction has been closed any attempt to use it will throw an - * exception - */ - public void rollback(); - - /** - * Set a string value in a map - * - * @param obj - * the object id of the map - * @param key - * the key in the map - * @param value - * the string to set - * @throws AutomergeException - * if the object is not a map - */ - public void set(ObjectId obj, String key, String value); - - /** - * Set a string value in a list - * - * @param obj - * the object id of the list - * @param index - * the index in the list - * @param value - * the string to set - * @throws AutomergeException - * if the object is not a list - */ - public void set(ObjectId obj, long index, String value); - - /** - * Set a double value in a map - * - * @param obj - * the object id of the map - * @param key - * the key in the map - * @param value - * the double to set - * @throws AutomergeException - * if the object is not a map - */ - public void set(ObjectId obj, String key, double value); - - /** - * Set a double value in a list - * - * @param obj - * the object id of the list - * @param index - * the index in the list to set - * @param value - * the double to set - * @throws AutomergeException - * if the object is not a list - */ - public void set(ObjectId obj, long index, double value); - - /** - * Set an int value in a list - * - * @param obj - * the object id of the list - * @param index - * the index in the list to set - * @param value - * the int to set - * @throws AutomergeException - * if the object is not a list - */ - public void set(ObjectId obj, long index, int value); - - /** - * Set an int value in a map - * - * @param obj - * the object id of the map - * @param key - * the key in the map - * @param value - * the int to set - * @throws AutomergeException - * if the object is not a map - */ - public void set(ObjectId obj, String key, int value); - - /** - * Set any non-object value in a map - * - * @param obj - * the object id of the map - * @param key - * the key in the map - * @param value - * the {@link NewValue} to set - * @throws AutomergeException - * if the object is not a map - */ - public void set(ObjectId obj, String key, NewValue value); - - /** - * Set any non-object value in a list - * - * @param obj - * the object id of the list - * @param index - * the index in the list - * @param value - * the {@link NewValue} to set - * @throws AutomergeException - * if the object is not a list - */ - public void set(ObjectId obj, long index, NewValue value); - - /** - * Set a byte array in a map - * - * @param obj - * the object id of the map - * @param key - * the key in the map - * @param value - * the bytes to set - * @throws AutomergeException - * if the object is not a map - */ - public void set(ObjectId obj, String key, byte[] value); - - /** - * Set a byte array in a list - * - * @param obj - * the object id of the list - * @param index - * the index in the list - * @param value - * the bytes to set - * @throws AutomergeException - * if the object is not a list - */ - public void set(ObjectId obj, long index, byte[] value); - - /** - * Set a boolean in a map - * - * @param obj - * the object id of the map - * @param key - * the key in the map - * @param value - * the boolean to set - * @throws AutomergeException - * if the object is not a map - */ - public void set(ObjectId obj, String key, boolean value); - - /** - * Set a boolean in a list - * - * @param obj - * the object id of the list - * @param index - * the index in the list - * @param value - * the boolean to set - * @throws AutomergeException - * if the object is not a list - */ - public void set(ObjectId obj, long index, boolean value); - - /** - * Set a counter in a map - * - * @param obj - * the object id of the map - * @param key - * the key in the map - * @param value - * the counter to set - * @throws AutomergeException - * if the object is not a map - */ - public void set(ObjectId obj, String key, Counter value); - - /** - * Set a counter in a list - * - * @param obj - * the object id of the list - * @param index - * the index in the list - * @param value - * the counter to set - * @throws AutomergeException - * if the object is not a list - */ - public void set(ObjectId obj, long index, Counter value); - - /** - * Set a date in a map - * - * @param obj - * the object id of the map - * @param key - * the key in the map - * @param value - * the date to set - * @throws AutomergeException - * if the object is not a map - */ - public void set(ObjectId obj, String key, Date value); - - /** - * Set a date in a list - * - * @param obj - * the object id of the list - * @param index - * the index in the list - * @param value - * the date to set - * @throws AutomergeException - * if the object is not a list - */ - public void set(ObjectId obj, long index, Date value); - - /** - * Set an unsigned integer in a map - * - * @param obj - * the object id of the map - * @param key - * the key in the map - * @param value - * the integer to set - * @throws AutomergeException - * if the object is not a map - * @throws IllegalArgumentException - * if the value is negative - */ - public void setUint(ObjectId obj, String key, long value); - - /** - * Set an unsigned integer in a list - * - * @param obj - * the object id of the list - * @param index - * the index in the list - * @param value - * the integer to set - * @throws AutomergeException - * if the object is not a list - * @throws IllegalArgumentException - * if the value is negative - */ - public void setUint(ObjectId obj, long index, long value); - - /** - * Set a key in a map to null - * - * @param obj - * the object id of the map - * @param key - * the key in the map - * @throws AutomergeException - * if the object is not a map - * @throws IllegalArgumentException - * if the value is negative - */ - public void setNull(ObjectId obj, String key); - - /** - * Set an index in a list to null - * - * @param obj - * the object id of the list - * @param index - * the index in the list - * @throws AutomergeException - * if the object is not a map - * @throws IllegalArgumentException - * if the value is negative - */ - public void setNull(ObjectId obj, long index); - - /** - * Create a new object at the given key in a map - * - * @param parent - * the object id of the map to set the key in - * @param key - * the key in the map - * @param objType - * the type of object to create - * @return the object id of the new object - * @throws AutomergeException - * if the object is not a map - */ - public ObjectId set(ObjectId parent, String key, ObjectType objType); - - /** - * Create a new object at the given index in a list - * - * @param parent - * the object id of the list to set inside - * @param index - * the index in the list - * @param objType - * the type of object to create - * @return the object id of the new object - * @throws AutomergeException - * if the object is not a list or the index is out of range - */ - public ObjectId set(ObjectId parent, long index, ObjectType objType); - - /** - * Insert a double into a list - * - * @param obj - * the object id of the list - * @param index - * the index in the list - * @param value - * the double to insert - * @throws AutomergeException - * if the object is not a list or the index is out of range - */ - public void insert(ObjectId obj, long index, double value); - - /** - * Insert a string into a list - * - * @param obj - * the object id of the list - * @param index - * the index in the list - * @param value - * the string to insert - * @throws AutomergeException - * if the object is not a list or the index is out of range - */ - public void insert(ObjectId obj, long index, String value); - - /** - * Insert an int into a list - * - * @param obj - * the object id of the list - * @param index - * the index in the list - * @param value - * the integer to insert - * @throws AutomergeException - * if the object is not a list or the index is out of range - */ - public void insert(ObjectId obj, long index, int value); - - /** - * Insert a byte array into a list - * - * @param obj - * the object id of the list - * @param index - * the index in the list - * @param value - * the bytes to insert - * @throws AutomergeException - * if the object is not a list or the index is out of range - */ - public void insert(ObjectId obj, long index, byte[] value); - - /** - * Insert a counter into a list - * - * @param obj - * the object id of the list - * @param index - * the index in the list - * @param value - * the counter to insert - * @throws AutomergeException - * if the object is not a list or the index is out of range - */ - public void insert(ObjectId obj, long index, Counter value); - - /** - * Insert a date into a list - * - * @param obj - * the object id of the list - * @param index - * the index in the list - * @param value - * the date to insert - * @throws AutomergeException - * if the object is not a list or the index is out of range - */ - public void insert(ObjectId obj, long index, Date value); - - /** - * Insert a boolean into a list - * - * @param obj - * the object id of the list - * @param index - * the index in the list - * @param value - * the boolean to insert - * @throws AutomergeException - * if the object is not a list or the index is out of range - */ - public void insert(ObjectId obj, long index, boolean value); - - /** - * Insert any non-object value into a list - * - * @param obj - * the object id of the list - * @param index - * the index in the list - * @param value - * the new value to insert - * @throws AutomergeException - * if the object is not a list or the index is out of range - */ - public void insert(ObjectId obj, long index, NewValue value); - - /** - * Insert a new object in a list - * - * @param parent - * the object id of the list to insert into - * @param index - * the index in the list - * @param objType - * the object type to insert - * @return the object id of the new object - * @throws AutomergeException - * if the object is not a list or the index is out of range - */ - public ObjectId insert(ObjectId parent, long index, ObjectType objType); - - /** - * Insert a null into a list - * - * @param obj - * the object id of the list - * @param index - * the index in the list - * @throws AutomergeException - * if the object is not a list or the index is out of range - */ - public void insertNull(ObjectId obj, long index); - - /** - * Insert an unsigned integer into a list - * - * @param obj - * the object id of the list - * @param index - * the index in the list - * @param value - * the integer to insert - * @throws AutomergeException - * if the object is not a list or the index is out of range - * @throws IllegalArgumentException - * if the value is negative - */ - public void insertUint(ObjectId obj, long index, long value); - - /** - * Increment a counter in a map - * - * @param obj - * the object id of the map - * @param key - * the key in the map where the counter is - * @param amount - * the amount to increment by - * @throws AutomergeException - * if the object is not a map or the key is not a counter - */ - public void increment(ObjectId obj, String key, long amount); - - /** - * Increment a counter in a list - * - * @param obj - * the object id of the list - * @param index - * the idx in the list where the counter is - * @param amount - * the amount to increment by - * @throws AutomergeException - * if the object is not a list or the index is not a counter - */ - public void increment(ObjectId obj, long index, long amount); - - /** - * Delete a key from a map - * - * @param obj - * the object id of the map - * @param key - * the key to delete - * @throws AutomergeException - * if the object is not a map or the key does not exist - */ - public void delete(ObjectId obj, String key); - - /** - * Delete an element from a list - * - * @param obj - * the object id of the map - * @param index - * the index of the element to delete - * @throws AutomergeException - * if the object is not a list or the index is out of bounds - */ - public void delete(ObjectId obj, long index); - - /** - * Splice multiple non-object values into a list - * - * @param obj - * the object id of the list - * @param start - * the index in the list to start splicing - * @param deleteCount - * the number of elements to delete - * @param items - * the new values to insert - * @throws AutomergeException - * if the object is not a list or the start index is out of range - */ - public void splice(ObjectId obj, long start, long deleteCount, Iterator items); - - /** - * Splice text into a text object - * - * @param obj - * the object id of the text object - * @param start - * the index in the text to start splicing - * @param deleteCount - * the number of characters to delete - * @param text - * the new text to insert - * @throws AutomergeException - * if the object is not a text object or the start index is out of - * range - */ - public void spliceText(ObjectId obj, long start, long deleteCount, String text); - - /** - * Create a mark - * - * @param obj - * the object id of the text object to create the mark on - * @param start - * the index in the text object to start the mark at - * @param end - * the index in the text object to end the mark at - * @param markName - * the name of the mark - * @param value - * the value to associate with the mark - * @param expand - * how to expand the mark - * @throws AutomergeException - * if the object is not a text object or the start or end index is - * out of range - */ - public void mark(ObjectId obj, long start, long end, String markName, NewValue value, ExpandMark expand); - - /** - * Create a mark with a string value - * - * @param obj - * the object id of the text object to create the mark on - * @param start - * the index in the text object to start the mark at - * @param end - * the index in the text object to end the mark at - * @param markName - * the name of the mark - * @param value - * the string to associate with the mark - * @param expand - * how to expand the mark - * @throws AutomergeException - * if the object is not a text object or the start or end index is - * out of range - */ - public void mark(ObjectId obj, long start, long end, String markName, String value, ExpandMark expand); - - /** - * Create a mark with an integer value - * - * @param obj - * the object id of the text object to create the mark on - * @param start - * the index in the text object to start the mark at - * @param end - * the index in the text object to end the mark at - * @param markName - * the name of the mark - * @param value - * the integer to associate with the mark - * @param expand - * how to expand the mark - * @throws AutomergeException - * if the object is not a text object or the start or end index is - * out of range - */ - public void mark(ObjectId obj, long start, long end, String markName, long value, ExpandMark expand); - - /** - * Create a mark with an unsigned integer value - * - * @param obj - * the object id of the text object to create the mark on - * @param start - * the index in the text object to start the mark at - * @param end - * the index in the text object to end the mark at - * @param markName - * the name of the mark - * @param value - * the integer to associate with the mark - * @param expand - * how to expand the mark - * @throws AutomergeException - * if the object is not a text object or the start or end index is - * out of range - * @throws IllegalArgumentException - * if the value is negative - */ - public void markUint(ObjectId obj, long start, long end, String markName, long value, ExpandMark expand); - - /** - * Create a mark with a double value - * - * @param obj - * the object id of the text object to create the mark on - * @param start - * the index in the text object to start the mark at - * @param end - * the index in the text object to end the mark at - * @param markName - * the name of the mark - * @param value - * the double to associate with the mark - * @param expand - * how to expand the mark - * @throws AutomergeException - * if the object is not a text object or the start or end index is - * out of range - */ - public void mark(ObjectId obj, long start, long end, String markName, double value, ExpandMark expand); - - /** - * Create a mark with a byte array - * - * @param obj - * the object id of the text object to create the mark on - * @param start - * the index in the text object to start the mark at - * @param end - * the index in the text object to end the mark at - * @param markName - * the name of the mark - * @param value - * the byte array to associate with the mark - * @param expand - * how to expand the mark - * @throws AutomergeException - * if the object is not a text object or the start or end index is - * out of range - */ - public void mark(ObjectId obj, long start, long end, String markName, byte[] value, ExpandMark expand); - - /** - * Create a mark with a counter value - * - * @param obj - * the object id of the text object to create the mark on - * @param start - * the index in the text object to start the mark at - * @param end - * the index in the text object to end the mark at - * @param markName - * the name of the mark - * @param value - * the counter to associate with the mark - * @param expand - * how to expand the mark - * @throws AutomergeException - * if the object is not a text object or the start or end index is - * out of range - */ - public void mark(ObjectId obj, long start, long end, String markName, Counter value, ExpandMark expand); - - /** - * Create a mark with a {@link Date} value - * - * @param obj - * the object id of the text object to create the mark on - * @param start - * the index in the text object to start the mark at - * @param end - * the index in the text object to end the mark at - * @param markName - * the name of the mark - * @param value - * the date to associate with the mark - * @param expand - * how to expand the mark - * @throws AutomergeException - * if the object is not a text object or the start or end index is - * out of range - */ - public void mark(ObjectId obj, long start, long end, String markName, Date value, ExpandMark expand); - - /** - * Create a mark with a boolean value - * - * @param obj - * the object id of the text object to create the mark on - * @param start - * the index in the text object to start the mark at - * @param end - * the index in the text object to end the mark at - * @param markName - * the name of the mark - * @param value - * the boolean to associate with the mark - * @param expand - * how to expand the mark - * @throws AutomergeException - * if the object is not a text object or the start or end index is - * out of range - */ - public void mark(ObjectId obj, long start, long end, String markName, boolean value, ExpandMark expand); - - /** - * Create a mark with a null value - * - * @param obj - * the object id of the text object to create the mark on - * @param start - * the index in the text object to start the mark at - * @param end - * the index in the text object to end the mark at - * @param markName - * the name of the mark - * @param expand - * how to expand the mark - * @throws AutomergeException - * if the object is not a text object or the start or end index is - * out of range - */ - public void markNull(ObjectId obj, long start, long end, String markName, ExpandMark expand); - - /** - * remove a mark from a range of characters in a text object - * - * @param obj - * the object id of the text object to remove the mark from - * @param start - * the index in the text object to start removing from - * @param end - * the index in the text object to end removing at - * @param markName - * the name of the mark to remove - * @param expand - * how the removed span should expand - */ - public void unmark(ObjectId obj, String markName, long start, long end, ExpandMark expand); - - @Override - public void close(); + /** + * Commit the transaction + * + *

+ * Once a transaction has been committed any attempt to use it will throw an + * exception + * + * @return the result of the commit or {@link Optional#empty()} if the + * transaction made no changes + */ + public Optional commit(); + + /** + * Close the transaction and reverse any changes made + * + *

+ * Once a transaction has been closed any attempt to use it will throw an + * exception + */ + public void rollback(); + + /** + * Set a string value in a map + * + * @param obj + * the object id of the map + * @param key + * the key in the map + * @param value + * the string to set + * @throws AutomergeException + * if the object is not a map + */ + public void set(ObjectId obj, String key, String value); + + /** + * Set a string value in a list + * + * @param obj + * the object id of the list + * @param index + * the index in the list + * @param value + * the string to set + * @throws AutomergeException + * if the object is not a list + */ + public void set(ObjectId obj, long index, String value); + + /** + * Set a double value in a map + * + * @param obj + * the object id of the map + * @param key + * the key in the map + * @param value + * the double to set + * @throws AutomergeException + * if the object is not a map + */ + public void set(ObjectId obj, String key, double value); + + /** + * Set a double value in a list + * + * @param obj + * the object id of the list + * @param index + * the index in the list to set + * @param value + * the double to set + * @throws AutomergeException + * if the object is not a list + */ + public void set(ObjectId obj, long index, double value); + + /** + * Set an int value in a list + * + * @param obj + * the object id of the list + * @param index + * the index in the list to set + * @param value + * the int to set + * @throws AutomergeException + * if the object is not a list + */ + public void set(ObjectId obj, long index, int value); + + /** + * Set an int value in a map + * + * @param obj + * the object id of the map + * @param key + * the key in the map + * @param value + * the int to set + * @throws AutomergeException + * if the object is not a map + */ + public void set(ObjectId obj, String key, int value); + + /** + * Set any non-object value in a map + * + * @param obj + * the object id of the map + * @param key + * the key in the map + * @param value + * the {@link NewValue} to set + * @throws AutomergeException + * if the object is not a map + */ + public void set(ObjectId obj, String key, NewValue value); + + /** + * Set any non-object value in a list + * + * @param obj + * the object id of the list + * @param index + * the index in the list + * @param value + * the {@link NewValue} to set + * @throws AutomergeException + * if the object is not a list + */ + public void set(ObjectId obj, long index, NewValue value); + + /** + * Set a byte array in a map + * + * @param obj + * the object id of the map + * @param key + * the key in the map + * @param value + * the bytes to set + * @throws AutomergeException + * if the object is not a map + */ + public void set(ObjectId obj, String key, byte[] value); + + /** + * Set a byte array in a list + * + * @param obj + * the object id of the list + * @param index + * the index in the list + * @param value + * the bytes to set + * @throws AutomergeException + * if the object is not a list + */ + public void set(ObjectId obj, long index, byte[] value); + + /** + * Set a boolean in a map + * + * @param obj + * the object id of the map + * @param key + * the key in the map + * @param value + * the boolean to set + * @throws AutomergeException + * if the object is not a map + */ + public void set(ObjectId obj, String key, boolean value); + + /** + * Set a boolean in a list + * + * @param obj + * the object id of the list + * @param index + * the index in the list + * @param value + * the boolean to set + * @throws AutomergeException + * if the object is not a list + */ + public void set(ObjectId obj, long index, boolean value); + + /** + * Set a counter in a map + * + * @param obj + * the object id of the map + * @param key + * the key in the map + * @param value + * the counter to set + * @throws AutomergeException + * if the object is not a map + */ + public void set(ObjectId obj, String key, Counter value); + + /** + * Set a counter in a list + * + * @param obj + * the object id of the list + * @param index + * the index in the list + * @param value + * the counter to set + * @throws AutomergeException + * if the object is not a list + */ + public void set(ObjectId obj, long index, Counter value); + + /** + * Set a date in a map + * + * @param obj + * the object id of the map + * @param key + * the key in the map + * @param value + * the date to set + * @throws AutomergeException + * if the object is not a map + */ + public void set(ObjectId obj, String key, Date value); + + /** + * Set a date in a list + * + * @param obj + * the object id of the list + * @param index + * the index in the list + * @param value + * the date to set + * @throws AutomergeException + * if the object is not a list + */ + public void set(ObjectId obj, long index, Date value); + + /** + * Set an unsigned integer in a map + * + * @param obj + * the object id of the map + * @param key + * the key in the map + * @param value + * the integer to set + * @throws AutomergeException + * if the object is not a map + * @throws IllegalArgumentException + * if the value is negative + */ + public void setUint(ObjectId obj, String key, long value); + + /** + * Set an unsigned integer in a list + * + * @param obj + * the object id of the list + * @param index + * the index in the list + * @param value + * the integer to set + * @throws AutomergeException + * if the object is not a list + * @throws IllegalArgumentException + * if the value is negative + */ + public void setUint(ObjectId obj, long index, long value); + + /** + * Set a key in a map to null + * + * @param obj + * the object id of the map + * @param key + * the key in the map + * @throws AutomergeException + * if the object is not a map + * @throws IllegalArgumentException + * if the value is negative + */ + public void setNull(ObjectId obj, String key); + + /** + * Set an index in a list to null + * + * @param obj + * the object id of the list + * @param index + * the index in the list + * @throws AutomergeException + * if the object is not a map + * @throws IllegalArgumentException + * if the value is negative + */ + public void setNull(ObjectId obj, long index); + + /** + * Create a new object at the given key in a map + * + * @param parent + * the object id of the map to set the key in + * @param key + * the key in the map + * @param objType + * the type of object to create + * @return the object id of the new object + * @throws AutomergeException + * if the object is not a map + */ + public ObjectId set(ObjectId parent, String key, ObjectType objType); + + /** + * Create a new object at the given index in a list + * + * @param parent + * the object id of the list to set inside + * @param index + * the index in the list + * @param objType + * the type of object to create + * @return the object id of the new object + * @throws AutomergeException + * if the object is not a list or the index is out of range + */ + public ObjectId set(ObjectId parent, long index, ObjectType objType); + + /** + * Insert a double into a list + * + * @param obj + * the object id of the list + * @param index + * the index in the list + * @param value + * the double to insert + * @throws AutomergeException + * if the object is not a list or the index is out of range + */ + public void insert(ObjectId obj, long index, double value); + + /** + * Insert a string into a list + * + * @param obj + * the object id of the list + * @param index + * the index in the list + * @param value + * the string to insert + * @throws AutomergeException + * if the object is not a list or the index is out of range + */ + public void insert(ObjectId obj, long index, String value); + + /** + * Insert an int into a list + * + * @param obj + * the object id of the list + * @param index + * the index in the list + * @param value + * the integer to insert + * @throws AutomergeException + * if the object is not a list or the index is out of range + */ + public void insert(ObjectId obj, long index, int value); + + /** + * Insert a byte array into a list + * + * @param obj + * the object id of the list + * @param index + * the index in the list + * @param value + * the bytes to insert + * @throws AutomergeException + * if the object is not a list or the index is out of range + */ + public void insert(ObjectId obj, long index, byte[] value); + + /** + * Insert a counter into a list + * + * @param obj + * the object id of the list + * @param index + * the index in the list + * @param value + * the counter to insert + * @throws AutomergeException + * if the object is not a list or the index is out of range + */ + public void insert(ObjectId obj, long index, Counter value); + + /** + * Insert a date into a list + * + * @param obj + * the object id of the list + * @param index + * the index in the list + * @param value + * the date to insert + * @throws AutomergeException + * if the object is not a list or the index is out of range + */ + public void insert(ObjectId obj, long index, Date value); + + /** + * Insert a boolean into a list + * + * @param obj + * the object id of the list + * @param index + * the index in the list + * @param value + * the boolean to insert + * @throws AutomergeException + * if the object is not a list or the index is out of range + */ + public void insert(ObjectId obj, long index, boolean value); + + /** + * Insert any non-object value into a list + * + * @param obj + * the object id of the list + * @param index + * the index in the list + * @param value + * the new value to insert + * @throws AutomergeException + * if the object is not a list or the index is out of range + */ + public void insert(ObjectId obj, long index, NewValue value); + + /** + * Insert a new object in a list + * + * @param parent + * the object id of the list to insert into + * @param index + * the index in the list + * @param objType + * the object type to insert + * @return the object id of the new object + * @throws AutomergeException + * if the object is not a list or the index is out of range + */ + public ObjectId insert(ObjectId parent, long index, ObjectType objType); + + /** + * Insert a null into a list + * + * @param obj + * the object id of the list + * @param index + * the index in the list + * @throws AutomergeException + * if the object is not a list or the index is out of range + */ + public void insertNull(ObjectId obj, long index); + + /** + * Insert an unsigned integer into a list + * + * @param obj + * the object id of the list + * @param index + * the index in the list + * @param value + * the integer to insert + * @throws AutomergeException + * if the object is not a list or the index is out of range + * @throws IllegalArgumentException + * if the value is negative + */ + public void insertUint(ObjectId obj, long index, long value); + + /** + * Increment a counter in a map + * + * @param obj + * the object id of the map + * @param key + * the key in the map where the counter is + * @param amount + * the amount to increment by + * @throws AutomergeException + * if the object is not a map or the key is not a counter + */ + public void increment(ObjectId obj, String key, long amount); + + /** + * Increment a counter in a list + * + * @param obj + * the object id of the list + * @param index + * the idx in the list where the counter is + * @param amount + * the amount to increment by + * @throws AutomergeException + * if the object is not a list or the index is not a counter + */ + public void increment(ObjectId obj, long index, long amount); + + /** + * Delete a key from a map + * + * @param obj + * the object id of the map + * @param key + * the key to delete + * @throws AutomergeException + * if the object is not a map or the key does not exist + */ + public void delete(ObjectId obj, String key); + + /** + * Delete an element from a list + * + * @param obj + * the object id of the map + * @param index + * the index of the element to delete + * @throws AutomergeException + * if the object is not a list or the index is out of bounds + */ + public void delete(ObjectId obj, long index); + + /** + * Splice multiple non-object values into a list + * + * @param obj + * the object id of the list + * @param start + * the index in the list to start splicing + * @param deleteCount + * the number of elements to delete + * @param items + * the new values to insert + * @throws AutomergeException + * if the object is not a list or the start index is out of range + */ + public void splice(ObjectId obj, long start, long deleteCount, Iterator items); + + /** + * Splice text into a text object + * + * @param obj + * the object id of the text object + * @param start + * the index in the text to start splicing + * @param deleteCount + * the number of characters to delete + * @param text + * the new text to insert + * @throws AutomergeException + * if the object is not a text object or the start index is out of + * range + */ + public void spliceText(ObjectId obj, long start, long deleteCount, String text); + + /** + * Create a mark + * + * @param obj + * the object id of the text object to create the mark on + * @param start + * the index in the text object to start the mark at + * @param end + * the index in the text object to end the mark at + * @param markName + * the name of the mark + * @param value + * the value to associate with the mark + * @param expand + * how to expand the mark + * @throws AutomergeException + * if the object is not a text object or the start or end index is + * out of range + */ + public void mark(ObjectId obj, long start, long end, String markName, NewValue value, ExpandMark expand); + + /** + * Create a mark with a string value + * + * @param obj + * the object id of the text object to create the mark on + * @param start + * the index in the text object to start the mark at + * @param end + * the index in the text object to end the mark at + * @param markName + * the name of the mark + * @param value + * the string to associate with the mark + * @param expand + * how to expand the mark + * @throws AutomergeException + * if the object is not a text object or the start or end index is + * out of range + */ + public void mark(ObjectId obj, long start, long end, String markName, String value, ExpandMark expand); + + /** + * Create a mark with an integer value + * + * @param obj + * the object id of the text object to create the mark on + * @param start + * the index in the text object to start the mark at + * @param end + * the index in the text object to end the mark at + * @param markName + * the name of the mark + * @param value + * the integer to associate with the mark + * @param expand + * how to expand the mark + * @throws AutomergeException + * if the object is not a text object or the start or end index is + * out of range + */ + public void mark(ObjectId obj, long start, long end, String markName, long value, ExpandMark expand); + + /** + * Create a mark with an unsigned integer value + * + * @param obj + * the object id of the text object to create the mark on + * @param start + * the index in the text object to start the mark at + * @param end + * the index in the text object to end the mark at + * @param markName + * the name of the mark + * @param value + * the integer to associate with the mark + * @param expand + * how to expand the mark + * @throws AutomergeException + * if the object is not a text object or the start or end index is + * out of range + * @throws IllegalArgumentException + * if the value is negative + */ + public void markUint(ObjectId obj, long start, long end, String markName, long value, ExpandMark expand); + + /** + * Create a mark with a double value + * + * @param obj + * the object id of the text object to create the mark on + * @param start + * the index in the text object to start the mark at + * @param end + * the index in the text object to end the mark at + * @param markName + * the name of the mark + * @param value + * the double to associate with the mark + * @param expand + * how to expand the mark + * @throws AutomergeException + * if the object is not a text object or the start or end index is + * out of range + */ + public void mark(ObjectId obj, long start, long end, String markName, double value, ExpandMark expand); + + /** + * Create a mark with a byte array + * + * @param obj + * the object id of the text object to create the mark on + * @param start + * the index in the text object to start the mark at + * @param end + * the index in the text object to end the mark at + * @param markName + * the name of the mark + * @param value + * the byte array to associate with the mark + * @param expand + * how to expand the mark + * @throws AutomergeException + * if the object is not a text object or the start or end index is + * out of range + */ + public void mark(ObjectId obj, long start, long end, String markName, byte[] value, ExpandMark expand); + + /** + * Create a mark with a counter value + * + * @param obj + * the object id of the text object to create the mark on + * @param start + * the index in the text object to start the mark at + * @param end + * the index in the text object to end the mark at + * @param markName + * the name of the mark + * @param value + * the counter to associate with the mark + * @param expand + * how to expand the mark + * @throws AutomergeException + * if the object is not a text object or the start or end index is + * out of range + */ + public void mark(ObjectId obj, long start, long end, String markName, Counter value, ExpandMark expand); + + /** + * Create a mark with a {@link Date} value + * + * @param obj + * the object id of the text object to create the mark on + * @param start + * the index in the text object to start the mark at + * @param end + * the index in the text object to end the mark at + * @param markName + * the name of the mark + * @param value + * the date to associate with the mark + * @param expand + * how to expand the mark + * @throws AutomergeException + * if the object is not a text object or the start or end index is + * out of range + */ + public void mark(ObjectId obj, long start, long end, String markName, Date value, ExpandMark expand); + + /** + * Create a mark with a boolean value + * + * @param obj + * the object id of the text object to create the mark on + * @param start + * the index in the text object to start the mark at + * @param end + * the index in the text object to end the mark at + * @param markName + * the name of the mark + * @param value + * the boolean to associate with the mark + * @param expand + * how to expand the mark + * @throws AutomergeException + * if the object is not a text object or the start or end index is + * out of range + */ + public void mark(ObjectId obj, long start, long end, String markName, boolean value, ExpandMark expand); + + /** + * Create a mark with a null value + * + * @param obj + * the object id of the text object to create the mark on + * @param start + * the index in the text object to start the mark at + * @param end + * the index in the text object to end the mark at + * @param markName + * the name of the mark + * @param expand + * how to expand the mark + * @throws AutomergeException + * if the object is not a text object or the start or end index is + * out of range + */ + public void markNull(ObjectId obj, long start, long end, String markName, ExpandMark expand); + + /** + * remove a mark from a range of characters in a text object + * + * @param obj + * the object id of the text object to remove the mark from + * @param start + * the index in the text object to start removing from + * @param end + * the index in the text object to end removing at + * @param markName + * the name of the mark to remove + * @param expand + * how the removed span should expand + */ + public void unmark(ObjectId obj, String markName, long start, long end, ExpandMark expand); + + @Override + public void close(); } diff --git a/lib/src/main/java/org/automerge/TransactionImpl.java b/lib/src/main/java/org/automerge/TransactionImpl.java index c6661b7..b5f90d6 100644 --- a/lib/src/main/java/org/automerge/TransactionImpl.java +++ b/lib/src/main/java/org/automerge/TransactionImpl.java @@ -8,379 +8,379 @@ import java.util.function.Consumer; public class TransactionImpl implements Transaction { - private Optional pointer; - private Document doc; - private Optional> finish; - - protected TransactionImpl(Document doc, AutomergeSys.TransactionPointer pointer) { - this.pointer = Optional.of(pointer); - this.doc = doc; - this.finish = Optional.empty(); - } - - protected TransactionImpl(Document doc, AutomergeSys.TransactionPointer pointer, - Consumer finish) { - this.pointer = Optional.of(pointer); - this.doc = doc; - this.finish = Optional.of(finish); - } - - public synchronized Optional commit() { - CommitResult result = AutomergeSys.commitTransaction(pointer.get()); - this.pointer = Optional.empty(); - this.doc.clearTransaction(); - if (finish.isPresent()) { - finish.get().accept(result.getPatchLog()); - } - return result.getHash(); - } - - public synchronized void rollback() { - AutomergeSys.rollbackTransaction(this.pointer.get()); - this.pointer = Optional.empty(); - this.doc.clearTransaction(); - } - - public synchronized Optional get(ObjectId obj, String key) { - return AutomergeSys.getInMapInTx(this.pointer.get(), obj, key); - } - - public synchronized Optional get(ObjectId obj, long key) { - return AutomergeSys.getInListInTx(this.pointer.get(), obj, key); - } - - public synchronized Optional getAll(ObjectId obj, String key) { - return AutomergeSys.getAllInMapInTx(this.pointer.get(), obj, key); - } - - public synchronized Optional getAll(ObjectId obj, long idx) { - return AutomergeSys.getAllInListInTx(this.pointer.get(), obj, idx); - } - - public synchronized Optional getAll(ObjectId obj, String key, ChangeHash[] heads) { - return AutomergeSys.getAllAtInMapInTx(this.pointer.get(), obj, key, heads); - } - - public synchronized Optional getAll(ObjectId obj, long idx, ChangeHash[] heads) { - return AutomergeSys.getAllAtInListInTx(this.pointer.get(), obj, idx, heads); - } - - public Optional get(ObjectId obj, String key, ChangeHash[] heads) { - return AutomergeSys.getAtInMapInTx(this.pointer.get(), obj, key, heads); - } - - public Optional get(ObjectId obj, long idx, ChangeHash[] heads) { - return AutomergeSys.getAtInListInTx(this.pointer.get(), obj, idx, heads); - } - - public void set(ObjectId obj, String key, String value) { - AutomergeSys.setStringInMap(this.pointer.get(), obj, key, value); - } - - public void set(ObjectId obj, long idx, String value) { - AutomergeSys.setStringInList(this.pointer.get(), obj, idx, value); - } - - public void set(ObjectId obj, String key, double value) { - AutomergeSys.setDoubleInMap(this.pointer.get(), obj, key, value); - } - - public void set(ObjectId obj, long idx, double value) { - AutomergeSys.setDoubleInList(this.pointer.get(), obj, idx, value); - } - - public void set(ObjectId obj, long idx, int value) { - AutomergeSys.setIntInList(this.pointer.get(), obj, idx, value); - } - - public void set(ObjectId obj, String key, int value) { - AutomergeSys.setIntInMap(this.pointer.get(), obj, key, value); - } - - public void set(ObjectId obj, String key, NewValue value) { - value.set(this, obj, key); - } - - public void set(ObjectId obj, long index, NewValue value) { - value.set(this, obj, index); - } - - public void setUint(ObjectId obj, String key, long value) { - AutomergeSys.setUintInMap(this.pointer.get(), obj, key, value); - } - - public void setUint(ObjectId obj, long idx, long value) { - AutomergeSys.setUintInList(this.pointer.get(), obj, idx, value); - } - - public void set(ObjectId obj, String key, byte[] value) { - AutomergeSys.setBytesInMap(this.pointer.get(), obj, key, value); - } - - public void set(ObjectId obj, long idx, byte[] value) { - AutomergeSys.setBytesInList(this.pointer.get(), obj, idx, value); - } - - public void set(ObjectId obj, String key, boolean value) { - AutomergeSys.setBoolInMap(this.pointer.get(), obj, key, value); - } - - public void set(ObjectId obj, long idx, boolean value) { - AutomergeSys.setBoolInList(this.pointer.get(), obj, idx, value); - } - - public void set(ObjectId obj, String key, Counter value) { - AutomergeSys.setCounterInMap(this.pointer.get(), obj, key, value.getValue()); - } + private Optional pointer; + private Document doc; + private Optional> finish; + + protected TransactionImpl(Document doc, AutomergeSys.TransactionPointer pointer) { + this.pointer = Optional.of(pointer); + this.doc = doc; + this.finish = Optional.empty(); + } + + protected TransactionImpl(Document doc, AutomergeSys.TransactionPointer pointer, + Consumer finish) { + this.pointer = Optional.of(pointer); + this.doc = doc; + this.finish = Optional.of(finish); + } + + public synchronized Optional commit() { + CommitResult result = AutomergeSys.commitTransaction(pointer.get()); + this.pointer = Optional.empty(); + this.doc.clearTransaction(); + if (finish.isPresent()) { + finish.get().accept(result.getPatchLog()); + } + return result.getHash(); + } + + public synchronized void rollback() { + AutomergeSys.rollbackTransaction(this.pointer.get()); + this.pointer = Optional.empty(); + this.doc.clearTransaction(); + } + + public synchronized Optional get(ObjectId obj, String key) { + return AutomergeSys.getInMapInTx(this.pointer.get(), obj, key); + } + + public synchronized Optional get(ObjectId obj, long key) { + return AutomergeSys.getInListInTx(this.pointer.get(), obj, key); + } + + public synchronized Optional getAll(ObjectId obj, String key) { + return AutomergeSys.getAllInMapInTx(this.pointer.get(), obj, key); + } + + public synchronized Optional getAll(ObjectId obj, long idx) { + return AutomergeSys.getAllInListInTx(this.pointer.get(), obj, idx); + } + + public synchronized Optional getAll(ObjectId obj, String key, ChangeHash[] heads) { + return AutomergeSys.getAllAtInMapInTx(this.pointer.get(), obj, key, heads); + } + + public synchronized Optional getAll(ObjectId obj, long idx, ChangeHash[] heads) { + return AutomergeSys.getAllAtInListInTx(this.pointer.get(), obj, idx, heads); + } + + public Optional get(ObjectId obj, String key, ChangeHash[] heads) { + return AutomergeSys.getAtInMapInTx(this.pointer.get(), obj, key, heads); + } + + public Optional get(ObjectId obj, long idx, ChangeHash[] heads) { + return AutomergeSys.getAtInListInTx(this.pointer.get(), obj, idx, heads); + } + + public void set(ObjectId obj, String key, String value) { + AutomergeSys.setStringInMap(this.pointer.get(), obj, key, value); + } + + public void set(ObjectId obj, long idx, String value) { + AutomergeSys.setStringInList(this.pointer.get(), obj, idx, value); + } + + public void set(ObjectId obj, String key, double value) { + AutomergeSys.setDoubleInMap(this.pointer.get(), obj, key, value); + } + + public void set(ObjectId obj, long idx, double value) { + AutomergeSys.setDoubleInList(this.pointer.get(), obj, idx, value); + } + + public void set(ObjectId obj, long idx, int value) { + AutomergeSys.setIntInList(this.pointer.get(), obj, idx, value); + } + + public void set(ObjectId obj, String key, int value) { + AutomergeSys.setIntInMap(this.pointer.get(), obj, key, value); + } + + public void set(ObjectId obj, String key, NewValue value) { + value.set(this, obj, key); + } + + public void set(ObjectId obj, long index, NewValue value) { + value.set(this, obj, index); + } + + public void setUint(ObjectId obj, String key, long value) { + AutomergeSys.setUintInMap(this.pointer.get(), obj, key, value); + } + + public void setUint(ObjectId obj, long idx, long value) { + AutomergeSys.setUintInList(this.pointer.get(), obj, idx, value); + } + + public void set(ObjectId obj, String key, byte[] value) { + AutomergeSys.setBytesInMap(this.pointer.get(), obj, key, value); + } + + public void set(ObjectId obj, long idx, byte[] value) { + AutomergeSys.setBytesInList(this.pointer.get(), obj, idx, value); + } + + public void set(ObjectId obj, String key, boolean value) { + AutomergeSys.setBoolInMap(this.pointer.get(), obj, key, value); + } + + public void set(ObjectId obj, long idx, boolean value) { + AutomergeSys.setBoolInList(this.pointer.get(), obj, idx, value); + } + + public void set(ObjectId obj, String key, Counter value) { + AutomergeSys.setCounterInMap(this.pointer.get(), obj, key, value.getValue()); + } - public void set(ObjectId obj, long idx, Counter value) { - AutomergeSys.setCounterInList(this.pointer.get(), obj, idx, value.getValue()); - } + public void set(ObjectId obj, long idx, Counter value) { + AutomergeSys.setCounterInList(this.pointer.get(), obj, idx, value.getValue()); + } - public void set(ObjectId obj, String key, Date value) { - AutomergeSys.setDateInMap(this.pointer.get(), obj, key, value); - } + public void set(ObjectId obj, String key, Date value) { + AutomergeSys.setDateInMap(this.pointer.get(), obj, key, value); + } - public void set(ObjectId obj, long idx, Date value) { - AutomergeSys.setDateInList(this.pointer.get(), obj, idx, value); - } + public void set(ObjectId obj, long idx, Date value) { + AutomergeSys.setDateInList(this.pointer.get(), obj, idx, value); + } - public ObjectId set(ObjectId parent, String key, ObjectType objType) { - return AutomergeSys.setObjectInMap(this.pointer.get(), parent, key, objType); - } + public ObjectId set(ObjectId parent, String key, ObjectType objType) { + return AutomergeSys.setObjectInMap(this.pointer.get(), parent, key, objType); + } - public ObjectId set(ObjectId parent, long idx, ObjectType objType) { - return AutomergeSys.setObjectInList(this.pointer.get(), parent, idx, objType); - } + public ObjectId set(ObjectId parent, long idx, ObjectType objType) { + return AutomergeSys.setObjectInList(this.pointer.get(), parent, idx, objType); + } - public void setNull(ObjectId obj, String key) { - AutomergeSys.setNullInMap(this.pointer.get(), obj, key); - } + public void setNull(ObjectId obj, String key) { + AutomergeSys.setNullInMap(this.pointer.get(), obj, key); + } - public void setNull(ObjectId obj, long idx) { - AutomergeSys.setNullInList(this.pointer.get(), obj, idx); - } + public void setNull(ObjectId obj, long idx) { + AutomergeSys.setNullInList(this.pointer.get(), obj, idx); + } - public void insert(ObjectId obj, long index, double value) { - AutomergeSys.insertDoubleInList(this.pointer.get(), obj, index, value); - } + public void insert(ObjectId obj, long index, double value) { + AutomergeSys.insertDoubleInList(this.pointer.get(), obj, index, value); + } - public void insert(ObjectId obj, long index, String value) { - AutomergeSys.insertStringInList(this.pointer.get(), obj, index, value); - } + public void insert(ObjectId obj, long index, String value) { + AutomergeSys.insertStringInList(this.pointer.get(), obj, index, value); + } - public void insert(ObjectId obj, long index, int value) { - AutomergeSys.insertIntInList(this.pointer.get(), obj, index, value); - } + public void insert(ObjectId obj, long index, int value) { + AutomergeSys.insertIntInList(this.pointer.get(), obj, index, value); + } - public void insert(ObjectId obj, long index, byte[] value) { - AutomergeSys.insertBytesInList(this.pointer.get(), obj, index, value); - } + public void insert(ObjectId obj, long index, byte[] value) { + AutomergeSys.insertBytesInList(this.pointer.get(), obj, index, value); + } - public void insert(ObjectId obj, long index, Counter value) { - AutomergeSys.insertCounterInList(this.pointer.get(), obj, index, value.getValue()); - } + public void insert(ObjectId obj, long index, Counter value) { + AutomergeSys.insertCounterInList(this.pointer.get(), obj, index, value.getValue()); + } - public void insert(ObjectId obj, long index, Date value) { - AutomergeSys.insertDateInList(this.pointer.get(), obj, index, value); - } + public void insert(ObjectId obj, long index, Date value) { + AutomergeSys.insertDateInList(this.pointer.get(), obj, index, value); + } - public void insert(ObjectId obj, long index, boolean value) { - AutomergeSys.insertBoolInList(this.pointer.get(), obj, index, value); - } - - public void insertNull(ObjectId obj, long index) { - AutomergeSys.insertNullInList(this.pointer.get(), obj, index); - } - - public void insertUint(ObjectId obj, long index, long value) { - AutomergeSys.insertUintInList(this.pointer.get(), obj, index, value); - } - - public ObjectId insert(ObjectId parent, long index, ObjectType objType) { - return AutomergeSys.insertObjectInList(this.pointer.get(), parent, index, objType); - } - - public void insert(ObjectId obj, long index, NewValue value) { - value.insert(this, obj, index); - } - - public void increment(ObjectId obj, String key, long amount) { - AutomergeSys.incrementInMap(this.pointer.get(), obj, key, amount); - } - - public void increment(ObjectId obj, long idx, long amount) { - AutomergeSys.incrementInList(this.pointer.get(), obj, idx, amount); - } - - public void delete(ObjectId obj, String key) { - AutomergeSys.deleteInMap(this.pointer.get(), obj, key); - } - - public void delete(ObjectId obj, long idx) { - AutomergeSys.deleteInList(this.pointer.get(), obj, idx); - } - - public void splice(ObjectId obj, long start, long deleteCount, Iterator items) { - AutomergeSys.splice(this.pointer.get(), obj, start, deleteCount, items); - } - - public void spliceText(ObjectId obj, long start, long deleteCount, String text) { - AutomergeSys.spliceText(this.pointer.get(), obj, start, deleteCount, text); - } - - public synchronized Optional text(ObjectId obj) { - return AutomergeSys.getTextInTx(this.pointer.get(), obj); - } - - public synchronized Optional text(ObjectId obj, ChangeHash[] heads) { - return AutomergeSys.getTextAtInTx(this.pointer.get(), obj, heads); - } - - public synchronized Optional keys(ObjectId obj) { - return AutomergeSys.getKeysInTx(this.pointer.get(), obj); - } - - public synchronized Optional keys(ObjectId obj, ChangeHash[] heads) { - return AutomergeSys.getKeysAtInTx(this.pointer.get(), obj, heads); - } - - public synchronized Optional mapEntries(ObjectId obj) { - return AutomergeSys.getMapEntriesInTx(this.pointer.get(), obj); - } - - public synchronized Optional mapEntries(ObjectId obj, ChangeHash[] heads) { - return AutomergeSys.getMapEntriesAtInTx(this.pointer.get(), obj, heads); - } - - public synchronized Optional listItems(ObjectId obj) { - return AutomergeSys.getListItemsInTx(this.pointer.get(), obj); - } - - public synchronized Optional listItems(ObjectId obj, ChangeHash[] heads) { - return AutomergeSys.getListItemsAtInTx(this.pointer.get(), obj, heads); - } - - public synchronized long length(ObjectId obj) { - return AutomergeSys.getListLengthInTx(this.pointer.get(), obj); - } - - public synchronized long length(ObjectId obj, ChangeHash[] heads) { - return AutomergeSys.getListLengthAtInTx(this.pointer.get(), obj, heads); - } - - public synchronized ChangeHash[] getHeads() { - return AutomergeSys.getHeadsInTx(this.pointer.get()); - } - - @Override - public synchronized void mark(ObjectId obj, long start, long end, String markName, NewValue value, - ExpandMark expand) { - value.mark(this, obj, start, end, markName, expand); - } - - @Override - public synchronized void mark(ObjectId obj, long start, long end, String markName, String value, - ExpandMark expand) { - AutomergeSys.markString(this.pointer.get(), obj, markName, start, end, value, expand); - } - - @Override - public synchronized void mark(ObjectId obj, long start, long end, String markName, long value, ExpandMark expand) { - AutomergeSys.markInt(this.pointer.get(), obj, markName, start, end, value, expand); - } - - @Override - public synchronized void markUint(ObjectId obj, long start, long end, String markName, long value, - ExpandMark expand) { - AutomergeSys.markUint(this.pointer.get(), obj, markName, start, end, value, expand); - } - - @Override - public synchronized void mark(ObjectId obj, long start, long end, String markName, double value, - ExpandMark expand) { - AutomergeSys.markDouble(this.pointer.get(), obj, markName, start, end, value, expand); - } - - @Override - public synchronized void mark(ObjectId obj, long start, long end, String markName, byte[] value, - ExpandMark expand) { - AutomergeSys.markBytes(this.pointer.get(), obj, markName, start, end, value, expand); - } - - @Override - public synchronized void mark(ObjectId obj, long start, long end, String markName, Counter value, - ExpandMark expand) { - AutomergeSys.markCounter(this.pointer.get(), obj, markName, start, end, value.getValue(), expand); - } - - @Override - public synchronized void mark(ObjectId obj, long start, long end, String markName, Date value, ExpandMark expand) { - AutomergeSys.markDate(this.pointer.get(), obj, markName, start, end, value, expand); - } - - @Override - public synchronized void mark(ObjectId obj, long start, long end, String markName, boolean value, - ExpandMark expand) { - AutomergeSys.markBool(this.pointer.get(), obj, markName, start, end, value, expand); - } - - @Override - public synchronized void markNull(ObjectId obj, long start, long end, String markName, ExpandMark expand) { - AutomergeSys.markNull(this.pointer.get(), obj, markName, start, end, expand); - } - - public synchronized void unmark(ObjectId obj, String markName, long start, long end, ExpandMark expand) { - AutomergeSys.unMark(this.pointer.get(), obj, markName, start, end, expand); - } - - public synchronized List marks(ObjectId obj) { - return AutomergeSys.getMarksInTx(this.pointer.get(), obj, Optional.empty()); - } - - public synchronized List marks(ObjectId obj, ChangeHash[] heads) { - return AutomergeSys.getMarksInTx(this.pointer.get(), obj, Optional.of(heads)); - } - - public synchronized void close() { - if (this.pointer.isPresent()) { - this.rollback(); - } - } - - @Override - public synchronized HashMap getMarksAtIndex(ObjectId obj, long index) { - return AutomergeSys.getMarksAtIndexInTx(this.pointer.get(), obj, index, Optional.empty()); - } - - @Override - public synchronized HashMap getMarksAtIndex(ObjectId obj, long index, ChangeHash[] heads) { - return AutomergeSys.getMarksAtIndexInTx(this.pointer.get(), obj, index, Optional.of(heads)); - } - - @Override - public synchronized Cursor makeCursor(ObjectId obj, long index) { - return AutomergeSys.makeCursorInTx(this.pointer.get(), obj, index, Optional.empty()); - } - - @Override - public synchronized Cursor makeCursor(ObjectId obj, long index, ChangeHash[] heads) { - return AutomergeSys.makeCursorInTx(this.pointer.get(), obj, index, Optional.of(heads)); - } - - @Override - public synchronized long lookupCursorIndex(ObjectId obj, Cursor cursor) { - return AutomergeSys.lookupCursorIndexInTx(this.pointer.get(), obj, cursor, Optional.empty()); - } - - @Override - public synchronized long lookupCursorIndex(ObjectId obj, Cursor cursor, ChangeHash[] heads) { - return AutomergeSys.lookupCursorIndexInTx(this.pointer.get(), obj, cursor, Optional.of(heads)); - } - - @Override - public synchronized Optional getObjectType(ObjectId obj) { - return AutomergeSys.getObjectTypeInTx(this.pointer.get(), obj); - } + public void insert(ObjectId obj, long index, boolean value) { + AutomergeSys.insertBoolInList(this.pointer.get(), obj, index, value); + } + + public void insertNull(ObjectId obj, long index) { + AutomergeSys.insertNullInList(this.pointer.get(), obj, index); + } + + public void insertUint(ObjectId obj, long index, long value) { + AutomergeSys.insertUintInList(this.pointer.get(), obj, index, value); + } + + public ObjectId insert(ObjectId parent, long index, ObjectType objType) { + return AutomergeSys.insertObjectInList(this.pointer.get(), parent, index, objType); + } + + public void insert(ObjectId obj, long index, NewValue value) { + value.insert(this, obj, index); + } + + public void increment(ObjectId obj, String key, long amount) { + AutomergeSys.incrementInMap(this.pointer.get(), obj, key, amount); + } + + public void increment(ObjectId obj, long idx, long amount) { + AutomergeSys.incrementInList(this.pointer.get(), obj, idx, amount); + } + + public void delete(ObjectId obj, String key) { + AutomergeSys.deleteInMap(this.pointer.get(), obj, key); + } + + public void delete(ObjectId obj, long idx) { + AutomergeSys.deleteInList(this.pointer.get(), obj, idx); + } + + public void splice(ObjectId obj, long start, long deleteCount, Iterator items) { + AutomergeSys.splice(this.pointer.get(), obj, start, deleteCount, items); + } + + public void spliceText(ObjectId obj, long start, long deleteCount, String text) { + AutomergeSys.spliceText(this.pointer.get(), obj, start, deleteCount, text); + } + + public synchronized Optional text(ObjectId obj) { + return AutomergeSys.getTextInTx(this.pointer.get(), obj); + } + + public synchronized Optional text(ObjectId obj, ChangeHash[] heads) { + return AutomergeSys.getTextAtInTx(this.pointer.get(), obj, heads); + } + + public synchronized Optional keys(ObjectId obj) { + return AutomergeSys.getKeysInTx(this.pointer.get(), obj); + } + + public synchronized Optional keys(ObjectId obj, ChangeHash[] heads) { + return AutomergeSys.getKeysAtInTx(this.pointer.get(), obj, heads); + } + + public synchronized Optional mapEntries(ObjectId obj) { + return AutomergeSys.getMapEntriesInTx(this.pointer.get(), obj); + } + + public synchronized Optional mapEntries(ObjectId obj, ChangeHash[] heads) { + return AutomergeSys.getMapEntriesAtInTx(this.pointer.get(), obj, heads); + } + + public synchronized Optional listItems(ObjectId obj) { + return AutomergeSys.getListItemsInTx(this.pointer.get(), obj); + } + + public synchronized Optional listItems(ObjectId obj, ChangeHash[] heads) { + return AutomergeSys.getListItemsAtInTx(this.pointer.get(), obj, heads); + } + + public synchronized long length(ObjectId obj) { + return AutomergeSys.getListLengthInTx(this.pointer.get(), obj); + } + + public synchronized long length(ObjectId obj, ChangeHash[] heads) { + return AutomergeSys.getListLengthAtInTx(this.pointer.get(), obj, heads); + } + + public synchronized ChangeHash[] getHeads() { + return AutomergeSys.getHeadsInTx(this.pointer.get()); + } + + @Override + public synchronized void mark(ObjectId obj, long start, long end, String markName, NewValue value, + ExpandMark expand) { + value.mark(this, obj, start, end, markName, expand); + } + + @Override + public synchronized void mark(ObjectId obj, long start, long end, String markName, String value, + ExpandMark expand) { + AutomergeSys.markString(this.pointer.get(), obj, markName, start, end, value, expand); + } + + @Override + public synchronized void mark(ObjectId obj, long start, long end, String markName, long value, ExpandMark expand) { + AutomergeSys.markInt(this.pointer.get(), obj, markName, start, end, value, expand); + } + + @Override + public synchronized void markUint(ObjectId obj, long start, long end, String markName, long value, + ExpandMark expand) { + AutomergeSys.markUint(this.pointer.get(), obj, markName, start, end, value, expand); + } + + @Override + public synchronized void mark(ObjectId obj, long start, long end, String markName, double value, + ExpandMark expand) { + AutomergeSys.markDouble(this.pointer.get(), obj, markName, start, end, value, expand); + } + + @Override + public synchronized void mark(ObjectId obj, long start, long end, String markName, byte[] value, + ExpandMark expand) { + AutomergeSys.markBytes(this.pointer.get(), obj, markName, start, end, value, expand); + } + + @Override + public synchronized void mark(ObjectId obj, long start, long end, String markName, Counter value, + ExpandMark expand) { + AutomergeSys.markCounter(this.pointer.get(), obj, markName, start, end, value.getValue(), expand); + } + + @Override + public synchronized void mark(ObjectId obj, long start, long end, String markName, Date value, ExpandMark expand) { + AutomergeSys.markDate(this.pointer.get(), obj, markName, start, end, value, expand); + } + + @Override + public synchronized void mark(ObjectId obj, long start, long end, String markName, boolean value, + ExpandMark expand) { + AutomergeSys.markBool(this.pointer.get(), obj, markName, start, end, value, expand); + } + + @Override + public synchronized void markNull(ObjectId obj, long start, long end, String markName, ExpandMark expand) { + AutomergeSys.markNull(this.pointer.get(), obj, markName, start, end, expand); + } + + public synchronized void unmark(ObjectId obj, String markName, long start, long end, ExpandMark expand) { + AutomergeSys.unMark(this.pointer.get(), obj, markName, start, end, expand); + } + + public synchronized List marks(ObjectId obj) { + return AutomergeSys.getMarksInTx(this.pointer.get(), obj, Optional.empty()); + } + + public synchronized List marks(ObjectId obj, ChangeHash[] heads) { + return AutomergeSys.getMarksInTx(this.pointer.get(), obj, Optional.of(heads)); + } + + public synchronized void close() { + if (this.pointer.isPresent()) { + this.rollback(); + } + } + + @Override + public synchronized HashMap getMarksAtIndex(ObjectId obj, long index) { + return AutomergeSys.getMarksAtIndexInTx(this.pointer.get(), obj, index, Optional.empty()); + } + + @Override + public synchronized HashMap getMarksAtIndex(ObjectId obj, long index, ChangeHash[] heads) { + return AutomergeSys.getMarksAtIndexInTx(this.pointer.get(), obj, index, Optional.of(heads)); + } + + @Override + public synchronized Cursor makeCursor(ObjectId obj, long index) { + return AutomergeSys.makeCursorInTx(this.pointer.get(), obj, index, Optional.empty()); + } + + @Override + public synchronized Cursor makeCursor(ObjectId obj, long index, ChangeHash[] heads) { + return AutomergeSys.makeCursorInTx(this.pointer.get(), obj, index, Optional.of(heads)); + } + + @Override + public synchronized long lookupCursorIndex(ObjectId obj, Cursor cursor) { + return AutomergeSys.lookupCursorIndexInTx(this.pointer.get(), obj, cursor, Optional.empty()); + } + + @Override + public synchronized long lookupCursorIndex(ObjectId obj, Cursor cursor, ChangeHash[] heads) { + return AutomergeSys.lookupCursorIndexInTx(this.pointer.get(), obj, cursor, Optional.of(heads)); + } + + @Override + public synchronized Optional getObjectType(ObjectId obj) { + return AutomergeSys.getObjectTypeInTx(this.pointer.get(), obj); + } } diff --git a/lib/src/main/java/org/automerge/TransactionInProgress.java b/lib/src/main/java/org/automerge/TransactionInProgress.java index 872ddb3..841314d 100644 --- a/lib/src/main/java/org/automerge/TransactionInProgress.java +++ b/lib/src/main/java/org/automerge/TransactionInProgress.java @@ -1,5 +1,5 @@ package org.automerge; public class TransactionInProgress extends RuntimeException { - private static final long serialVersionUID = 392751779216244453L; + private static final long serialVersionUID = 392751779216244453L; } diff --git a/lib/src/main/java/org/automerge/UnsupportedPlatformException.java b/lib/src/main/java/org/automerge/UnsupportedPlatformException.java index a0f7786..0c24113 100644 --- a/lib/src/main/java/org/automerge/UnsupportedPlatformException.java +++ b/lib/src/main/java/org/automerge/UnsupportedPlatformException.java @@ -1,7 +1,7 @@ package org.automerge; class UnsupportedPlatformException extends RuntimeException { - UnsupportedPlatformException(String message) { - super(message); - } + UnsupportedPlatformException(String message) { + super(message); + } } diff --git a/lib/src/test/java/org/automerge/PathBuilder.java b/lib/src/test/java/org/automerge/PathBuilder.java index 9fb7c05..aa24f26 100644 --- a/lib/src/test/java/org/automerge/PathBuilder.java +++ b/lib/src/test/java/org/automerge/PathBuilder.java @@ -3,29 +3,29 @@ import java.util.ArrayList; class PathBuilder { - private ArrayList elements = new ArrayList<>(); + private ArrayList elements = new ArrayList<>(); - public static ArrayList empty() { - return new ArrayList<>(); - } + public static ArrayList empty() { + return new ArrayList<>(); + } - public static PathBuilder root(String key) { - PathBuilder pb = new PathBuilder(); - pb.elements.add(new PathElement(ObjectId.ROOT, new Prop.Key(key))); - return pb; - } + public static PathBuilder root(String key) { + PathBuilder pb = new PathBuilder(); + pb.elements.add(new PathElement(ObjectId.ROOT, new Prop.Key(key))); + return pb; + } - public PathBuilder key(ObjectId obj, String key) { - elements.add(new PathElement(obj, new Prop.Key(key))); - return this; - } + public PathBuilder key(ObjectId obj, String key) { + elements.add(new PathElement(obj, new Prop.Key(key))); + return this; + } - public PathBuilder index(ObjectId obj, long idx) { - elements.add(new PathElement(obj, new Prop.Index(idx))); - return this; - } + public PathBuilder index(ObjectId obj, long idx) { + elements.add(new PathElement(obj, new Prop.Index(idx))); + return this; + } - public ArrayList build() { - return elements; - } + public ArrayList build() { + return elements; + } } diff --git a/lib/src/test/java/org/automerge/TestChanges.java b/lib/src/test/java/org/automerge/TestChanges.java index 2a7578a..716ccc0 100644 --- a/lib/src/test/java/org/automerge/TestChanges.java +++ b/lib/src/test/java/org/automerge/TestChanges.java @@ -4,21 +4,21 @@ import org.junit.jupiter.api.Test; class TestChanges { - @Test - public void testApplyEncodedChanges() { - Document doc = new Document(); - try (Transaction tx = doc.startTransaction()) { - tx.set(ObjectId.ROOT, "key", "value"); - tx.commit(); - } - ChangeHash[] heads = doc.getHeads(); - Document doc2 = doc.fork(); - try (Transaction tx = doc2.startTransaction()) { - tx.set(ObjectId.ROOT, "key", "value2"); - tx.commit(); - } - byte[] changes = doc2.encodeChangesSince(heads); - doc.applyEncodedChanges(changes); - Assertions.assertEquals("value2", ((AmValue.Str) doc.get(ObjectId.ROOT, "key").get()).getValue()); - } + @Test + public void testApplyEncodedChanges() { + Document doc = new Document(); + try (Transaction tx = doc.startTransaction()) { + tx.set(ObjectId.ROOT, "key", "value"); + tx.commit(); + } + ChangeHash[] heads = doc.getHeads(); + Document doc2 = doc.fork(); + try (Transaction tx = doc2.startTransaction()) { + tx.set(ObjectId.ROOT, "key", "value2"); + tx.commit(); + } + byte[] changes = doc2.encodeChangesSince(heads); + doc.applyEncodedChanges(changes); + Assertions.assertEquals("value2", ((AmValue.Str) doc.get(ObjectId.ROOT, "key").get()).getValue()); + } } diff --git a/lib/src/test/java/org/automerge/TestCursor.java b/lib/src/test/java/org/automerge/TestCursor.java index 3e9765b..3b4e464 100644 --- a/lib/src/test/java/org/automerge/TestCursor.java +++ b/lib/src/test/java/org/automerge/TestCursor.java @@ -5,80 +5,80 @@ import org.junit.jupiter.api.Test; public final class TestCursor { - private Document doc; - private ObjectId text; + private Document doc; + private ObjectId text; - @BeforeEach - public void setup() { - doc = new Document(); - try (Transaction tx = doc.startTransaction()) { - text = tx.set(ObjectId.ROOT, "text", ObjectType.TEXT); - tx.spliceText(text, 0, 0, "hello world"); - tx.commit(); - } - } + @BeforeEach + public void setup() { + doc = new Document(); + try (Transaction tx = doc.startTransaction()) { + text = tx.set(ObjectId.ROOT, "text", ObjectType.TEXT); + tx.spliceText(text, 0, 0, "hello world"); + tx.commit(); + } + } - @Test - public void testCursorInDoc() { - Cursor cursor = doc.makeCursor(text, 3); - Assertions.assertEquals(doc.lookupCursorIndex(text, cursor), 3); + @Test + public void testCursorInDoc() { + Cursor cursor = doc.makeCursor(text, 3); + Assertions.assertEquals(doc.lookupCursorIndex(text, cursor), 3); - ChangeHash[] heads = doc.getHeads(); + ChangeHash[] heads = doc.getHeads(); - try (Transaction tx = doc.startTransaction()) { - tx.spliceText(text, 3, 0, "!"); - tx.commit(); - } + try (Transaction tx = doc.startTransaction()) { + tx.spliceText(text, 3, 0, "!"); + tx.commit(); + } - Assertions.assertEquals(doc.lookupCursorIndex(text, cursor), 4); - Assertions.assertEquals(doc.lookupCursorIndex(text, cursor, heads), 3); + Assertions.assertEquals(doc.lookupCursorIndex(text, cursor), 4); + Assertions.assertEquals(doc.lookupCursorIndex(text, cursor, heads), 3); - Cursor oldCursor = doc.makeCursor(text, 3, heads); - Assertions.assertEquals(doc.lookupCursorIndex(text, oldCursor), 4); - Assertions.assertEquals(doc.lookupCursorIndex(text, oldCursor, heads), 3); + Cursor oldCursor = doc.makeCursor(text, 3, heads); + Assertions.assertEquals(doc.lookupCursorIndex(text, oldCursor), 4); + Assertions.assertEquals(doc.lookupCursorIndex(text, oldCursor, heads), 3); - } + } - @Test - public void testCursorInTx() { - ChangeHash[] heads = doc.getHeads(); - Cursor cursor; - try (Transaction tx = doc.startTransaction()) { - cursor = tx.makeCursor(text, 3); - Assertions.assertEquals(tx.lookupCursorIndex(text, cursor), 3); - tx.spliceText(text, 3, 0, "!"); - Assertions.assertEquals(tx.lookupCursorIndex(text, cursor), 4); - tx.commit(); - } + @Test + public void testCursorInTx() { + ChangeHash[] heads = doc.getHeads(); + Cursor cursor; + try (Transaction tx = doc.startTransaction()) { + cursor = tx.makeCursor(text, 3); + Assertions.assertEquals(tx.lookupCursorIndex(text, cursor), 3); + tx.spliceText(text, 3, 0, "!"); + Assertions.assertEquals(tx.lookupCursorIndex(text, cursor), 4); + tx.commit(); + } - try (Transaction tx = doc.startTransaction()) { - Cursor oldCursor = tx.makeCursor(text, 3, heads); - Assertions.assertEquals(tx.lookupCursorIndex(text, oldCursor), 4); - Assertions.assertEquals(tx.lookupCursorIndex(text, oldCursor, heads), 3); - tx.commit(); - } - } + try (Transaction tx = doc.startTransaction()) { + Cursor oldCursor = tx.makeCursor(text, 3, heads); + Assertions.assertEquals(tx.lookupCursorIndex(text, oldCursor), 4); + Assertions.assertEquals(tx.lookupCursorIndex(text, oldCursor, heads), 3); + tx.commit(); + } + } - @Test - public void testToFromString() { - Cursor cursor = doc.makeCursor(text, 3); - String encoded = cursor.toString(); - Cursor decoded = Cursor.fromString(encoded); - Assertions.assertEquals(doc.lookupCursorIndex(text, decoded), 3); + @Test + public void testToFromString() { + Cursor cursor = doc.makeCursor(text, 3); + String encoded = cursor.toString(); + Cursor decoded = Cursor.fromString(encoded); + Assertions.assertEquals(doc.lookupCursorIndex(text, decoded), 3); - Assertions.assertThrows(IllegalArgumentException.class, () -> { - Cursor.fromString("invalid"); - }); - } + Assertions.assertThrows(IllegalArgumentException.class, () -> { + Cursor.fromString("invalid"); + }); + } - @Test - public void testToFromBytes() { - Cursor cursor = doc.makeCursor(text, 3); - byte[] encoded = cursor.toBytes(); - Cursor decoded = Cursor.fromBytes(encoded); - Assertions.assertEquals(doc.lookupCursorIndex(text, decoded), 3); - Assertions.assertThrows(IllegalArgumentException.class, () -> { - Cursor.fromBytes(new byte[]{0x11, 0x01}); - }); - } + @Test + public void testToFromBytes() { + Cursor cursor = doc.makeCursor(text, 3); + byte[] encoded = cursor.toBytes(); + Cursor decoded = Cursor.fromBytes(encoded); + Assertions.assertEquals(doc.lookupCursorIndex(text, decoded), 3); + Assertions.assertThrows(IllegalArgumentException.class, () -> { + Cursor.fromBytes(new byte[]{0x11, 0x01}); + }); + } } diff --git a/lib/src/test/java/org/automerge/TestDelete.java b/lib/src/test/java/org/automerge/TestDelete.java index ce04974..b53902e 100644 --- a/lib/src/test/java/org/automerge/TestDelete.java +++ b/lib/src/test/java/org/automerge/TestDelete.java @@ -6,32 +6,32 @@ import org.junit.jupiter.api.Test; public final class TestDelete { - private Document doc; - private Transaction tx; + private Document doc; + private Transaction tx; - public TestDelete() { - super(); - } + public TestDelete() { + super(); + } - @BeforeEach - public void setup() { - doc = new Document(); - tx = doc.startTransaction(); - } + @BeforeEach + public void setup() { + doc = new Document(); + tx = doc.startTransaction(); + } - @Test - public void testDeleteInMap() { - tx.set(ObjectId.ROOT, "key", new Counter(10)); - tx.delete(ObjectId.ROOT, "key"); - Assertions.assertEquals(tx.get(ObjectId.ROOT, "key"), Optional.empty()); - } + @Test + public void testDeleteInMap() { + tx.set(ObjectId.ROOT, "key", new Counter(10)); + tx.delete(ObjectId.ROOT, "key"); + Assertions.assertEquals(tx.get(ObjectId.ROOT, "key"), Optional.empty()); + } - @Test - public void testDeleteInList() { - ObjectId list = tx.set(ObjectId.ROOT, "list", ObjectType.LIST); - tx.insert(list, 0, 123); - tx.delete(list, 0); - Assertions.assertEquals(tx.get(list, 0), Optional.empty()); - } + @Test + public void testDeleteInList() { + ObjectId list = tx.set(ObjectId.ROOT, "list", ObjectType.LIST); + tx.insert(list, 0, 123); + tx.delete(list, 0); + Assertions.assertEquals(tx.get(list, 0), Optional.empty()); + } } // Check that committing the transaction clears Document.transactionPtr diff --git a/lib/src/test/java/org/automerge/TestDiff.java b/lib/src/test/java/org/automerge/TestDiff.java index ad28e89..8c8eed4 100644 --- a/lib/src/test/java/org/automerge/TestDiff.java +++ b/lib/src/test/java/org/automerge/TestDiff.java @@ -6,29 +6,29 @@ public final class TestDiff { - @Test - public void testTransactionAt() { - Document doc = new Document(); - ChangeHash firstHeads; - ChangeHash secondHeads; + @Test + public void testTransactionAt() { + Document doc = new Document(); + ChangeHash firstHeads; + ChangeHash secondHeads; - try (Transaction tx = doc.startTransaction()) { - tx.set(ObjectId.ROOT, "key", 1.23); - firstHeads = tx.commit().get(); - } + try (Transaction tx = doc.startTransaction()) { + tx.set(ObjectId.ROOT, "key", 1.23); + firstHeads = tx.commit().get(); + } - try (Transaction tx = doc.startTransaction()) { - tx.set(ObjectId.ROOT, "key", 4.56); - secondHeads = tx.commit().get(); - } + try (Transaction tx = doc.startTransaction()) { + tx.set(ObjectId.ROOT, "key", 4.56); + secondHeads = tx.commit().get(); + } - List patches = doc.diff(new ChangeHash[]{firstHeads}, new ChangeHash[]{secondHeads}); + List patches = doc.diff(new ChangeHash[]{firstHeads}, new ChangeHash[]{secondHeads}); - Assertions.assertEquals(patches.size(), 1); - Assertions.assertEquals(patches.get(0).getObj(), ObjectId.ROOT); - PatchAction.PutMap action = (PatchAction.PutMap) patches.get(0).getAction(); - Assertions.assertEquals(action.getKey(), "key"); - AmValue.F64 value = ((AmValue.F64) action.getValue()); - Assertions.assertEquals(value.getValue(), 4.56); - } + Assertions.assertEquals(patches.size(), 1); + Assertions.assertEquals(patches.get(0).getObj(), ObjectId.ROOT); + PatchAction.PutMap action = (PatchAction.PutMap) patches.get(0).getAction(); + Assertions.assertEquals(action.getKey(), "key"); + AmValue.F64 value = ((AmValue.F64) action.getValue()); + Assertions.assertEquals(value.getValue(), 4.56); + } } diff --git a/lib/src/test/java/org/automerge/TestDocument.java b/lib/src/test/java/org/automerge/TestDocument.java index c25c1db..8fe6b88 100644 --- a/lib/src/test/java/org/automerge/TestDocument.java +++ b/lib/src/test/java/org/automerge/TestDocument.java @@ -6,72 +6,72 @@ public final class TestDocument { - public TestDocument() { - super(); - } + public TestDocument() { + super(); + } - @Test - public void testConstructWithActorId() { - Document doc = new Document("actorId".getBytes()); - Assertions.assertArrayEquals("actorId".getBytes(), doc.getActorId()); - } + @Test + public void testConstructWithActorId() { + Document doc = new Document("actorId".getBytes()); + Assertions.assertArrayEquals("actorId".getBytes(), doc.getActorId()); + } - @Test - public final void startAndCommitTransaction() { - Document doc = new Document(); - try (Transaction tx = doc.startTransaction()) { - tx.set(ObjectId.ROOT, "key", 1.23); - tx.commit(); - } - Optional result = doc.get(ObjectId.ROOT, "key"); - Assertions.assertEquals(1.23, ((AmValue.F64) result.get()).getValue()); - } + @Test + public final void startAndCommitTransaction() { + Document doc = new Document(); + try (Transaction tx = doc.startTransaction()) { + tx.set(ObjectId.ROOT, "key", 1.23); + tx.commit(); + } + Optional result = doc.get(ObjectId.ROOT, "key"); + Assertions.assertEquals(1.23, ((AmValue.F64) result.get()).getValue()); + } - @Test - public void testThrowsOnSaveWhileTransactionInProgress() { - Document doc = new Document(); - Transaction tx = doc.startTransaction(); - tx.set(ObjectId.ROOT, "key", 1.23); - Assertions.assertThrows(TransactionInProgress.class, () -> { - doc.save(); - }); - } + @Test + public void testThrowsOnSaveWhileTransactionInProgress() { + Document doc = new Document(); + Transaction tx = doc.startTransaction(); + tx.set(ObjectId.ROOT, "key", 1.23); + Assertions.assertThrows(TransactionInProgress.class, () -> { + doc.save(); + }); + } - @Test - public void testSaveAndLoad() { - Document doc = new Document(); - try (Transaction tx = doc.startTransaction()) { - tx.set(ObjectId.ROOT, "key", 1.23); - tx.commit(); - } - byte[] bytes = doc.save(); - Document doc2 = Document.load(bytes); - Optional result = doc2.get(ObjectId.ROOT, "key"); - Assertions.assertEquals(1.23, ((AmValue.F64) result.get()).getValue()); - } + @Test + public void testSaveAndLoad() { + Document doc = new Document(); + try (Transaction tx = doc.startTransaction()) { + tx.set(ObjectId.ROOT, "key", 1.23); + tx.commit(); + } + byte[] bytes = doc.save(); + Document doc2 = Document.load(bytes); + Optional result = doc2.get(ObjectId.ROOT, "key"); + Assertions.assertEquals(1.23, ((AmValue.F64) result.get()).getValue()); + } - @Test - public void testThrowsAutomergeExceptionOnInvalidBytes() { - byte[] badBytes = "badbytes".getBytes(); - Assertions.assertThrows(AutomergeException.class, () -> { - Document.load(badBytes); - }); - } + @Test + public void testThrowsAutomergeExceptionOnInvalidBytes() { + byte[] badBytes = "badbytes".getBytes(); + Assertions.assertThrows(AutomergeException.class, () -> { + Document.load(badBytes); + }); + } - @Test - public void testFree() { - Document doc = new Document(); - doc.free(); - } + @Test + public void testFree() { + Document doc = new Document(); + doc.free(); + } - @Test - public void testNullObjectIdThrows() { - // this test is actually a test for any path which uses an `ObjectId` - // as the code which throws the exception is in the rust implementation - // which converts the `ObjectId` into an internal rust type - Document doc = new Document(); - Assertions.assertThrows(IllegalArgumentException.class, () -> { - doc.get(null, "key"); - }); - } + @Test + public void testNullObjectIdThrows() { + // this test is actually a test for any path which uses an `ObjectId` + // as the code which throws the exception is in the rust implementation + // which converts the `ObjectId` into an internal rust type + Document doc = new Document(); + Assertions.assertThrows(IllegalArgumentException.class, () -> { + doc.get(null, "key"); + }); + } } diff --git a/lib/src/test/java/org/automerge/TestFork.java b/lib/src/test/java/org/automerge/TestFork.java index a8b17e5..94e23d2 100644 --- a/lib/src/test/java/org/automerge/TestFork.java +++ b/lib/src/test/java/org/automerge/TestFork.java @@ -6,105 +6,105 @@ class TestFork { - @Test - public void testFork() { - Document doc = new Document(); - try (Transaction tx = doc.startTransaction()) { - tx.set(ObjectId.ROOT, "key", 1.23); - tx.commit(); - } - Document doc2 = doc.fork(); - try (Transaction tx = doc2.startTransaction()) { - tx.set(ObjectId.ROOT, "key", 4.56); - tx.commit(); - } - Optional result = doc.get(ObjectId.ROOT, "key"); - Assertions.assertEquals(1.23, ((AmValue.F64) result.get()).getValue()); - Optional result2 = doc2.get(ObjectId.ROOT, "key"); - Assertions.assertEquals(4.56, ((AmValue.F64) result2.get()).getValue()); - } + @Test + public void testFork() { + Document doc = new Document(); + try (Transaction tx = doc.startTransaction()) { + tx.set(ObjectId.ROOT, "key", 1.23); + tx.commit(); + } + Document doc2 = doc.fork(); + try (Transaction tx = doc2.startTransaction()) { + tx.set(ObjectId.ROOT, "key", 4.56); + tx.commit(); + } + Optional result = doc.get(ObjectId.ROOT, "key"); + Assertions.assertEquals(1.23, ((AmValue.F64) result.get()).getValue()); + Optional result2 = doc2.get(ObjectId.ROOT, "key"); + Assertions.assertEquals(4.56, ((AmValue.F64) result2.get()).getValue()); + } - @Test - public void testForkWithActor() { - Document doc = new Document(); - try (Transaction tx = doc.startTransaction()) { - tx.set(ObjectId.ROOT, "key", 1.23); - tx.commit(); - } - Document doc2 = doc.fork("actor2".getBytes()); - Optional result = doc.get(ObjectId.ROOT, "key"); - Assertions.assertEquals(1.23, ((AmValue.F64) result.get()).getValue()); - Assertions.assertArrayEquals("actor2".getBytes(), doc2.getActorId()); - } + @Test + public void testForkWithActor() { + Document doc = new Document(); + try (Transaction tx = doc.startTransaction()) { + tx.set(ObjectId.ROOT, "key", 1.23); + tx.commit(); + } + Document doc2 = doc.fork("actor2".getBytes()); + Optional result = doc.get(ObjectId.ROOT, "key"); + Assertions.assertEquals(1.23, ((AmValue.F64) result.get()).getValue()); + Assertions.assertArrayEquals("actor2".getBytes(), doc2.getActorId()); + } - @Test - public void testForkWhileTransactionInProgressThrows() { - Document doc = new Document(); - Transaction tx = doc.startTransaction(); - Assertions.assertThrows(TransactionInProgress.class, () -> { - doc.fork(); - }); - } + @Test + public void testForkWhileTransactionInProgressThrows() { + Document doc = new Document(); + Transaction tx = doc.startTransaction(); + Assertions.assertThrows(TransactionInProgress.class, () -> { + doc.fork(); + }); + } - @Test - public void testForkWithActorWhileTransactionInProgressThrows() { - Document doc = new Document(); - Transaction tx = doc.startTransaction(); - Assertions.assertThrows(TransactionInProgress.class, () -> { - doc.fork("actor2".getBytes()); - }); - } + @Test + public void testForkWithActorWhileTransactionInProgressThrows() { + Document doc = new Document(); + Transaction tx = doc.startTransaction(); + Assertions.assertThrows(TransactionInProgress.class, () -> { + doc.fork("actor2".getBytes()); + }); + } - @Test - public void testForkAt() { - Document doc = new Document(); - try (Transaction tx = doc.startTransaction()) { - tx.set(ObjectId.ROOT, "key", 1.23); - tx.commit(); - } - ChangeHash[] heads = doc.getHeads(); - try (Transaction tx = doc.startTransaction()) { - tx.set(ObjectId.ROOT, "key", 4.56); - tx.commit(); - } - Document doc2 = doc.fork(heads); - Optional result = doc2.get(ObjectId.ROOT, "key"); - Assertions.assertEquals(1.23, ((AmValue.F64) result.get()).getValue()); - } + @Test + public void testForkAt() { + Document doc = new Document(); + try (Transaction tx = doc.startTransaction()) { + tx.set(ObjectId.ROOT, "key", 1.23); + tx.commit(); + } + ChangeHash[] heads = doc.getHeads(); + try (Transaction tx = doc.startTransaction()) { + tx.set(ObjectId.ROOT, "key", 4.56); + tx.commit(); + } + Document doc2 = doc.fork(heads); + Optional result = doc2.get(ObjectId.ROOT, "key"); + Assertions.assertEquals(1.23, ((AmValue.F64) result.get()).getValue()); + } - @Test - public void testForkAtWithActor() { - Document doc = new Document(); - try (Transaction tx = doc.startTransaction()) { - tx.set(ObjectId.ROOT, "key", 1.23); - tx.commit(); - } - ChangeHash[] heads = doc.getHeads(); - try (Transaction tx = doc.startTransaction()) { - tx.set(ObjectId.ROOT, "key", 4.56); - tx.commit(); - } - Document doc2 = doc.fork(heads, "actor2".getBytes()); - Optional result = doc2.get(ObjectId.ROOT, "key"); - Assertions.assertEquals(1.23, ((AmValue.F64) result.get()).getValue()); - Assertions.assertArrayEquals("actor2".getBytes(), doc2.getActorId()); - } + @Test + public void testForkAtWithActor() { + Document doc = new Document(); + try (Transaction tx = doc.startTransaction()) { + tx.set(ObjectId.ROOT, "key", 1.23); + tx.commit(); + } + ChangeHash[] heads = doc.getHeads(); + try (Transaction tx = doc.startTransaction()) { + tx.set(ObjectId.ROOT, "key", 4.56); + tx.commit(); + } + Document doc2 = doc.fork(heads, "actor2".getBytes()); + Optional result = doc2.get(ObjectId.ROOT, "key"); + Assertions.assertEquals(1.23, ((AmValue.F64) result.get()).getValue()); + Assertions.assertArrayEquals("actor2".getBytes(), doc2.getActorId()); + } - @Test - public void testForkAtWhileTransactionInProgressThrows() { - Document doc = new Document(); - Transaction tx = doc.startTransaction(); - Assertions.assertThrows(TransactionInProgress.class, () -> { - doc.fork(new ChangeHash[]{}); - }); - } + @Test + public void testForkAtWhileTransactionInProgressThrows() { + Document doc = new Document(); + Transaction tx = doc.startTransaction(); + Assertions.assertThrows(TransactionInProgress.class, () -> { + doc.fork(new ChangeHash[]{}); + }); + } - @Test - public void testForkAtWithActorWhileTransactionInProgressThrows() { - Document doc = new Document(); - Transaction tx = doc.startTransaction(); - Assertions.assertThrows(TransactionInProgress.class, () -> { - doc.fork(new ChangeHash[]{}, "actor2".getBytes()); - }); - } + @Test + public void testForkAtWithActorWhileTransactionInProgressThrows() { + Document doc = new Document(); + Transaction tx = doc.startTransaction(); + Assertions.assertThrows(TransactionInProgress.class, () -> { + doc.fork(new ChangeHash[]{}, "actor2".getBytes()); + }); + } } diff --git a/lib/src/test/java/org/automerge/TestGet.java b/lib/src/test/java/org/automerge/TestGet.java index eeef12f..651f162 100644 --- a/lib/src/test/java/org/automerge/TestGet.java +++ b/lib/src/test/java/org/automerge/TestGet.java @@ -7,254 +7,253 @@ class TestGet { - interface ValueCheck { - } - - interface TestCase extends ValueCheck { - void set(Transaction tx, ObjectId obj, String key); - - void set(Transaction tx, ObjectId obj, long idx); - - void check(AmValue value); - } - - ArrayList makeTestCases() { - ArrayList testCases = new ArrayList(); - // Uint - testCases.add(new TestCase() { - @Override - public void set(Transaction tx, ObjectId obj, String key) { - tx.set(obj, key, NewValue.uint(1)); - // tx.setUint(obj, key, 1); - } - - @Override - public void set(Transaction tx, ObjectId obj, long idx) { - tx.set(obj, idx, NewValue.uint(1)); - } - - @Override - public void check(AmValue value) { - Assertions.assertEquals(1, ((AmValue.UInt) value).getValue()); - } - }); - // Int - testCases.add(new TestCase() { - @Override - public void set(Transaction tx, ObjectId obj, String key) { - tx.set(obj, key, 1); - } - - @Override - public void set(Transaction tx, ObjectId obj, long idx) { - tx.set(obj, idx, 1); - } - - @Override - public void check(AmValue value) { - Assertions.assertEquals(1, ((AmValue.Int) value).getValue()); - } - }); - // F64 - testCases.add(new TestCase() { - @Override - public void set(Transaction tx, ObjectId obj, String key) { - tx.set(obj, key, 2.0); - } - - @Override - public void set(Transaction tx, ObjectId obj, long idx) { - tx.set(obj, idx, 2.0); - } - - @Override - public void check(AmValue value) { - assert ((AmValue.F64) value).getValue() == 2.0; - } - }); - // Bool - testCases.add(new TestCase() { - @Override - public void set(Transaction tx, ObjectId obj, String key) { - tx.set(obj, key, true); - } - - @Override - public void set(Transaction tx, ObjectId obj, long idx) { - tx.set(obj, idx, true); - } - - @Override - public void check(AmValue value) { - Assertions.assertEquals(true, ((AmValue.Bool) value).getValue()); - } - }); - // Str - testCases.add(new TestCase() { - @Override - public void set(Transaction tx, ObjectId obj, String key) { - tx.set(obj, key, "hello"); - } - - public void set(Transaction tx, ObjectId obj, long idx) { - tx.set(obj, idx, "hello"); - } - - @Override - public void check(AmValue value) { - Assertions.assertEquals("hello", ((AmValue.Str) value).getValue()); - } - }); - // Bytes - testCases.add(new TestCase() { - @Override - public void set(Transaction tx, ObjectId obj, String key) { - tx.set(obj, key, new byte[]{1, 2, 3}); - } - - public void set(Transaction tx, ObjectId obj, long idx) { - tx.set(obj, idx, new byte[]{1, 2, 3}); - } - - @Override - public void check(AmValue value) { - Assertions.assertArrayEquals(new byte[]{1, 2, 3}, ((AmValue.Bytes) value).getValue()); - } - }); - // Counter - testCases.add(new TestCase() { - @Override - public void set(Transaction tx, ObjectId obj, String key) { - tx.set(obj, key, new Counter(1)); - } - - public void set(Transaction tx, ObjectId obj, long idx) { - tx.set(obj, idx, new Counter(1)); - } - - @Override - public void check(AmValue value) { - Assertions.assertEquals(1, ((AmValue.Counter) value).getValue()); - } - }); - // Timestamp - testCases.add(new TestCase() { - private Date date = new Date(); - - @Override - public void set(Transaction tx, ObjectId obj, String key) { - tx.set(obj, key, date); - } - - public void set(Transaction tx, ObjectId obj, long idx) { - tx.set(obj, idx, date); - } - - @Override - public void check(AmValue value) { - Assertions.assertEquals(date, ((AmValue.Timestamp) value).getValue()); - } - }); - // Null - testCases.add(new TestCase() { - @Override - public void set(Transaction tx, ObjectId obj, String key) { - tx.set(obj, key, NewValue.NULL); - } - - public void set(Transaction tx, ObjectId obj, long idx) { - tx.set(obj, idx, NewValue.NULL); - } - - @Override - public void check(AmValue value) { - Assertions.assertInstanceOf(AmValue.Null.class, value); - } - }); - // List - testCases.add(new TestCase() { - ObjectId list; - - @Override - public void set(Transaction tx, ObjectId obj, String key) { - list = tx.set(obj, key, ObjectType.LIST); - } - - public void set(Transaction tx, ObjectId obj, long idx) { - list = tx.set(obj, idx, ObjectType.LIST); - } - - @Override - public void check(AmValue value) { - Assertions.assertEquals(list, ((AmValue.List) value).getId()); - } - }); - // Map - testCases.add(new TestCase() { - ObjectId map; - - @Override - public void set(Transaction tx, ObjectId obj, String key) { - map = tx.set(obj, key, ObjectType.MAP); - } - - public void set(Transaction tx, ObjectId obj, long idx) { - map = tx.set(obj, idx, ObjectType.MAP); - } - - @Override - public void check(AmValue value) { - Assertions.assertEquals(map, ((AmValue.Map) value).getId()); - } - }); - // Text - testCases.add(new TestCase() { - ObjectId text; - - @Override - public void set(Transaction tx, ObjectId obj, String key) { - text = tx.set(obj, key, ObjectType.TEXT); - } - - public void set(Transaction tx, ObjectId obj, long idx) { - text = tx.set(obj, idx, ObjectType.TEXT); - } - - @Override - public void check(AmValue value) { - Assertions.assertEquals(text, ((AmValue.Text) value).getId()); - } - }); - return testCases; - } - - @Test - public void testGetMap() { - for (TestCase testCase : makeTestCases()) { - Document doc = new Document(); - try (Transaction tx = doc.startTransaction()) { - testCase.set(tx, ObjectId.ROOT, "key"); - testCase.check(tx.get(ObjectId.ROOT, "key").get()); - testCase.check(doc.get(ObjectId.ROOT, "key").get()); - tx.commit(); - testCase.check(doc.get(ObjectId.ROOT, "key").get()); - } - } - } - - @Test - public void testGetList() { - for (TestCase testCase : makeTestCases()) { - Document doc = new Document(); - try (Transaction tx = doc.startTransaction()) { - ObjectId list = tx.set(ObjectId.ROOT, "list", ObjectType.LIST); - tx.insert(list, 0, NewValue.NULL); - testCase.set(tx, list, 0); - testCase.check(tx.get(list, 0).get()); - testCase.check(doc.get(list, 0).get()); - tx.commit(); - testCase.check(doc.get(list, 0).get()); - } - } - } + interface ValueCheck {} + + interface TestCase extends ValueCheck { + void set(Transaction tx, ObjectId obj, String key); + + void set(Transaction tx, ObjectId obj, long idx); + + void check(AmValue value); + } + + ArrayList makeTestCases() { + ArrayList testCases = new ArrayList(); + // Uint + testCases.add(new TestCase() { + @Override + public void set(Transaction tx, ObjectId obj, String key) { + tx.set(obj, key, NewValue.uint(1)); + // tx.setUint(obj, key, 1); + } + + @Override + public void set(Transaction tx, ObjectId obj, long idx) { + tx.set(obj, idx, NewValue.uint(1)); + } + + @Override + public void check(AmValue value) { + Assertions.assertEquals(1, ((AmValue.UInt) value).getValue()); + } + }); + // Int + testCases.add(new TestCase() { + @Override + public void set(Transaction tx, ObjectId obj, String key) { + tx.set(obj, key, 1); + } + + @Override + public void set(Transaction tx, ObjectId obj, long idx) { + tx.set(obj, idx, 1); + } + + @Override + public void check(AmValue value) { + Assertions.assertEquals(1, ((AmValue.Int) value).getValue()); + } + }); + // F64 + testCases.add(new TestCase() { + @Override + public void set(Transaction tx, ObjectId obj, String key) { + tx.set(obj, key, 2.0); + } + + @Override + public void set(Transaction tx, ObjectId obj, long idx) { + tx.set(obj, idx, 2.0); + } + + @Override + public void check(AmValue value) { + assert ((AmValue.F64) value).getValue() == 2.0; + } + }); + // Bool + testCases.add(new TestCase() { + @Override + public void set(Transaction tx, ObjectId obj, String key) { + tx.set(obj, key, true); + } + + @Override + public void set(Transaction tx, ObjectId obj, long idx) { + tx.set(obj, idx, true); + } + + @Override + public void check(AmValue value) { + Assertions.assertEquals(true, ((AmValue.Bool) value).getValue()); + } + }); + // Str + testCases.add(new TestCase() { + @Override + public void set(Transaction tx, ObjectId obj, String key) { + tx.set(obj, key, "hello"); + } + + public void set(Transaction tx, ObjectId obj, long idx) { + tx.set(obj, idx, "hello"); + } + + @Override + public void check(AmValue value) { + Assertions.assertEquals("hello", ((AmValue.Str) value).getValue()); + } + }); + // Bytes + testCases.add(new TestCase() { + @Override + public void set(Transaction tx, ObjectId obj, String key) { + tx.set(obj, key, new byte[]{1, 2, 3}); + } + + public void set(Transaction tx, ObjectId obj, long idx) { + tx.set(obj, idx, new byte[]{1, 2, 3}); + } + + @Override + public void check(AmValue value) { + Assertions.assertArrayEquals(new byte[]{1, 2, 3}, ((AmValue.Bytes) value).getValue()); + } + }); + // Counter + testCases.add(new TestCase() { + @Override + public void set(Transaction tx, ObjectId obj, String key) { + tx.set(obj, key, new Counter(1)); + } + + public void set(Transaction tx, ObjectId obj, long idx) { + tx.set(obj, idx, new Counter(1)); + } + + @Override + public void check(AmValue value) { + Assertions.assertEquals(1, ((AmValue.Counter) value).getValue()); + } + }); + // Timestamp + testCases.add(new TestCase() { + private Date date = new Date(); + + @Override + public void set(Transaction tx, ObjectId obj, String key) { + tx.set(obj, key, date); + } + + public void set(Transaction tx, ObjectId obj, long idx) { + tx.set(obj, idx, date); + } + + @Override + public void check(AmValue value) { + Assertions.assertEquals(date, ((AmValue.Timestamp) value).getValue()); + } + }); + // Null + testCases.add(new TestCase() { + @Override + public void set(Transaction tx, ObjectId obj, String key) { + tx.set(obj, key, NewValue.NULL); + } + + public void set(Transaction tx, ObjectId obj, long idx) { + tx.set(obj, idx, NewValue.NULL); + } + + @Override + public void check(AmValue value) { + Assertions.assertInstanceOf(AmValue.Null.class, value); + } + }); + // List + testCases.add(new TestCase() { + ObjectId list; + + @Override + public void set(Transaction tx, ObjectId obj, String key) { + list = tx.set(obj, key, ObjectType.LIST); + } + + public void set(Transaction tx, ObjectId obj, long idx) { + list = tx.set(obj, idx, ObjectType.LIST); + } + + @Override + public void check(AmValue value) { + Assertions.assertEquals(list, ((AmValue.List) value).getId()); + } + }); + // Map + testCases.add(new TestCase() { + ObjectId map; + + @Override + public void set(Transaction tx, ObjectId obj, String key) { + map = tx.set(obj, key, ObjectType.MAP); + } + + public void set(Transaction tx, ObjectId obj, long idx) { + map = tx.set(obj, idx, ObjectType.MAP); + } + + @Override + public void check(AmValue value) { + Assertions.assertEquals(map, ((AmValue.Map) value).getId()); + } + }); + // Text + testCases.add(new TestCase() { + ObjectId text; + + @Override + public void set(Transaction tx, ObjectId obj, String key) { + text = tx.set(obj, key, ObjectType.TEXT); + } + + public void set(Transaction tx, ObjectId obj, long idx) { + text = tx.set(obj, idx, ObjectType.TEXT); + } + + @Override + public void check(AmValue value) { + Assertions.assertEquals(text, ((AmValue.Text) value).getId()); + } + }); + return testCases; + } + + @Test + public void testGetMap() { + for (TestCase testCase : makeTestCases()) { + Document doc = new Document(); + try (Transaction tx = doc.startTransaction()) { + testCase.set(tx, ObjectId.ROOT, "key"); + testCase.check(tx.get(ObjectId.ROOT, "key").get()); + testCase.check(doc.get(ObjectId.ROOT, "key").get()); + tx.commit(); + testCase.check(doc.get(ObjectId.ROOT, "key").get()); + } + } + } + + @Test + public void testGetList() { + for (TestCase testCase : makeTestCases()) { + Document doc = new Document(); + try (Transaction tx = doc.startTransaction()) { + ObjectId list = tx.set(ObjectId.ROOT, "list", ObjectType.LIST); + tx.insert(list, 0, NewValue.NULL); + testCase.set(tx, list, 0); + testCase.check(tx.get(list, 0).get()); + testCase.check(doc.get(list, 0).get()); + tx.commit(); + testCase.check(doc.get(list, 0).get()); + } + } + } } diff --git a/lib/src/test/java/org/automerge/TestGetAll.java b/lib/src/test/java/org/automerge/TestGetAll.java index bbf3b10..e979b95 100644 --- a/lib/src/test/java/org/automerge/TestGetAll.java +++ b/lib/src/test/java/org/automerge/TestGetAll.java @@ -6,156 +6,156 @@ import org.junit.jupiter.api.Test; class TestGetAll { - interface TestCase { - void init(Transaction tx, double value); - - void branch1(Transaction tx, double value); - - void branch2(Transaction tx, double value); - - void merge(Transaction tx, double value); - - Conflicts getConflicts(Read doc); - - Conflicts getConflictsAt(Read doc, ChangeHash[] heads); - } - - @Test - public void testGetAllInMap() { - runMap(doc -> doc.startTransaction()); - } - - @Test - public void testGetAllInList() { - runList(doc -> doc.startTransaction()); - } - - public void runMap(Function createTx) { - run(new TestCase() { - public void init(Transaction tx, double value) { - tx.set(ObjectId.ROOT, "key", value); - } - - public void branch1(Transaction tx, double value) { - tx.set(ObjectId.ROOT, "key", value); - } - - public void branch2(Transaction tx, double value) { - tx.set(ObjectId.ROOT, "key", value); - } - - public void merge(Transaction tx, double value) { - tx.set(ObjectId.ROOT, "key", value); - } - - public Conflicts getConflicts(Read doc) { - return doc.getAll(ObjectId.ROOT, "key").get(); - } - - public Conflicts getConflictsAt(Read doc, ChangeHash[] heads) { - return doc.getAll(ObjectId.ROOT, "key", heads).get(); - } - }, createTx); - } - - public void runList(Function createTx) { - run(new TestCase() { - ObjectId list; - - public void init(Transaction tx, double value) { - list = tx.set(ObjectId.ROOT, "list", ObjectType.LIST); - tx.insert(list, 0, value); - } - - public void branch1(Transaction tx, double value) { - tx.set(list, 0, value); - } - - public void branch2(Transaction tx, double value) { - tx.set(list, 0, value); - } - - public void merge(Transaction tx, double value) { - tx.set(list, 0, value); - } - - public Conflicts getConflicts(Read doc) { - return doc.getAll(list, 0).get(); - } - - public Conflicts getConflictsAt(Read doc, ChangeHash[] heads) { - return doc.getAll(list, 0, heads).get(); - } - }, createTx); - } - - void run(TestCase testCase, Function createTx) { - Document doc = new Document(); - try (Transaction tx = createTx.apply(doc)) { - testCase.init(tx, 1.23); - tx.commit(); - } - Document doc2 = doc.fork(); - try (Transaction tx = createTx.apply(doc)) { - testCase.branch1(tx, 4.56); - tx.commit(); - } - try (Transaction tx = createTx.apply(doc2)) { - testCase.branch2(tx, 7.89); - tx.commit(); - } - doc.merge(doc2); - ChangeHash[] heads = doc.getHeads(); - // Check it works with an open transaction - Transaction tx = createTx.apply(doc); - Conflicts conflicts = testCase.getConflicts(doc); - assertConflicts(conflicts); - - // check the version from the transaction works - conflicts = testCase.getConflicts(tx); - assertConflicts(conflicts); - - // Check works without an open transaction - tx.commit(); - conflicts = testCase.getConflicts(doc); - assertConflicts(conflicts); - - // create a merge commit - try (Transaction tx2 = createTx.apply(doc)) { - testCase.merge(tx2, 2.00); - tx2.commit(); - } - - // Check there are no conflicts after the merge - conflicts = testCase.getConflicts(doc); - Assertions.assertEquals(1, conflicts.values().size()); - - // Check getAllAt works with an open transaction - tx = createTx.apply(doc); - conflicts = testCase.getConflictsAt(doc, heads); - assertConflicts(conflicts); - - // check the version from the transaction works - conflicts = testCase.getConflictsAt(tx, heads); - assertConflicts(conflicts); - - // Check works without an open transaction - tx.commit(); - conflicts = testCase.getConflictsAt(doc, heads); - assertConflicts(conflicts); - } - - void assertConflicts(Conflicts conflicts) { - Assertions.assertEquals(2, conflicts.values().size()); - - HashSet expected = new HashSet(); - expected.add(4.56); - expected.add(7.89); - - HashSet values = new HashSet(); - for (AmValue value : conflicts.values()) { - values.add(((AmValue.F64) value).getValue()); - } - Assertions.assertEquals(expected, values); - } + interface TestCase { + void init(Transaction tx, double value); + + void branch1(Transaction tx, double value); + + void branch2(Transaction tx, double value); + + void merge(Transaction tx, double value); + + Conflicts getConflicts(Read doc); + + Conflicts getConflictsAt(Read doc, ChangeHash[] heads); + } + + @Test + public void testGetAllInMap() { + runMap(doc -> doc.startTransaction()); + } + + @Test + public void testGetAllInList() { + runList(doc -> doc.startTransaction()); + } + + public void runMap(Function createTx) { + run(new TestCase() { + public void init(Transaction tx, double value) { + tx.set(ObjectId.ROOT, "key", value); + } + + public void branch1(Transaction tx, double value) { + tx.set(ObjectId.ROOT, "key", value); + } + + public void branch2(Transaction tx, double value) { + tx.set(ObjectId.ROOT, "key", value); + } + + public void merge(Transaction tx, double value) { + tx.set(ObjectId.ROOT, "key", value); + } + + public Conflicts getConflicts(Read doc) { + return doc.getAll(ObjectId.ROOT, "key").get(); + } + + public Conflicts getConflictsAt(Read doc, ChangeHash[] heads) { + return doc.getAll(ObjectId.ROOT, "key", heads).get(); + } + }, createTx); + } + + public void runList(Function createTx) { + run(new TestCase() { + ObjectId list; + + public void init(Transaction tx, double value) { + list = tx.set(ObjectId.ROOT, "list", ObjectType.LIST); + tx.insert(list, 0, value); + } + + public void branch1(Transaction tx, double value) { + tx.set(list, 0, value); + } + + public void branch2(Transaction tx, double value) { + tx.set(list, 0, value); + } + + public void merge(Transaction tx, double value) { + tx.set(list, 0, value); + } + + public Conflicts getConflicts(Read doc) { + return doc.getAll(list, 0).get(); + } + + public Conflicts getConflictsAt(Read doc, ChangeHash[] heads) { + return doc.getAll(list, 0, heads).get(); + } + }, createTx); + } + + void run(TestCase testCase, Function createTx) { + Document doc = new Document(); + try (Transaction tx = createTx.apply(doc)) { + testCase.init(tx, 1.23); + tx.commit(); + } + Document doc2 = doc.fork(); + try (Transaction tx = createTx.apply(doc)) { + testCase.branch1(tx, 4.56); + tx.commit(); + } + try (Transaction tx = createTx.apply(doc2)) { + testCase.branch2(tx, 7.89); + tx.commit(); + } + doc.merge(doc2); + ChangeHash[] heads = doc.getHeads(); + // Check it works with an open transaction + Transaction tx = createTx.apply(doc); + Conflicts conflicts = testCase.getConflicts(doc); + assertConflicts(conflicts); + + // check the version from the transaction works + conflicts = testCase.getConflicts(tx); + assertConflicts(conflicts); + + // Check works without an open transaction + tx.commit(); + conflicts = testCase.getConflicts(doc); + assertConflicts(conflicts); + + // create a merge commit + try (Transaction tx2 = createTx.apply(doc)) { + testCase.merge(tx2, 2.00); + tx2.commit(); + } + + // Check there are no conflicts after the merge + conflicts = testCase.getConflicts(doc); + Assertions.assertEquals(1, conflicts.values().size()); + + // Check getAllAt works with an open transaction + tx = createTx.apply(doc); + conflicts = testCase.getConflictsAt(doc, heads); + assertConflicts(conflicts); + + // check the version from the transaction works + conflicts = testCase.getConflictsAt(tx, heads); + assertConflicts(conflicts); + + // Check works without an open transaction + tx.commit(); + conflicts = testCase.getConflictsAt(doc, heads); + assertConflicts(conflicts); + } + + void assertConflicts(Conflicts conflicts) { + Assertions.assertEquals(2, conflicts.values().size()); + + HashSet expected = new HashSet(); + expected.add(4.56); + expected.add(7.89); + + HashSet values = new HashSet(); + for (AmValue value : conflicts.values()) { + values.add(((AmValue.F64) value).getValue()); + } + Assertions.assertEquals(expected, values); + } } diff --git a/lib/src/test/java/org/automerge/TestGetAt.java b/lib/src/test/java/org/automerge/TestGetAt.java index 69cfca0..1153d08 100644 --- a/lib/src/test/java/org/automerge/TestGetAt.java +++ b/lib/src/test/java/org/automerge/TestGetAt.java @@ -5,99 +5,99 @@ class TestGetAt { - interface TestCase { - void init(Transaction doc, String value); - - void update(Transaction tx, String value); - - AmValue get(Read r); - - AmValue getAt(Read r, ChangeHash[] heads); - } - - void run(TestCase testcase) { - // Steps - // Create the document - Document doc = new Document(); - // Create the first state and commit - Transaction tx = doc.startTransaction(); - testcase.init(tx, "value1"); - tx.commit(); - // save the heads - ChangeHash[] heads = doc.getHeads(); - // Create a new transaction - tx = doc.startTransaction(); - // Run the second update - testcase.update(tx, "value2"); - tx.commit(); - // Check that get returns current value - tx = doc.startTransaction(); - // Check that get and getAt on open tx work as expected - Assertions.assertEquals("value2", ((AmValue.Str) testcase.get(tx)).getValue()); - Assertions.assertEquals("value1", ((AmValue.Str) testcase.getAt(tx, heads)).getValue()); - - // Check that get and getAt on document with open tx work - Assertions.assertEquals("value2", ((AmValue.Str) testcase.get(doc)).getValue()); - Assertions.assertEquals("value1", ((AmValue.Str) testcase.getAt(doc, heads)).getValue()); - - tx.commit(); - - // Check that get and getAt on doc with closed tx work as expected - Assertions.assertEquals("value2", ((AmValue.Str) testcase.get(doc)).getValue()); - Assertions.assertEquals("value1", ((AmValue.Str) testcase.getAt(doc, heads)).getValue()); - } - - @Test - public void testGetAtInMap() { - run(new TestCase() { - @Override - public void init(Transaction tx, String value) { - tx.set(ObjectId.ROOT, "key", value); - } - - @Override - public void update(Transaction tx, String value) { - tx.set(ObjectId.ROOT, "key", value); - } - - @Override - public AmValue get(Read r) { - return r.get(ObjectId.ROOT, "key").get(); - } - - @Override - public AmValue getAt(Read r, ChangeHash[] heads) { - return r.get(ObjectId.ROOT, "key", heads).get(); - } - }); - } - - @Test - public void testGetAtInList() { - run(new TestCase() { - ObjectId list; - - @Override - public void init(Transaction tx, String value) { - list = tx.set(ObjectId.ROOT, "list", ObjectType.LIST); - tx.insert(list, 0, value); - } - - @Override - public void update(Transaction tx, String value) { - tx.set(list, 0, value); - } - - @Override - public AmValue get(Read r) { - return r.get(list, 0).get(); - } - - @Override - public AmValue getAt(Read r, ChangeHash[] heads) { - return r.get(list, 0, heads).get(); - } - }); - } + interface TestCase { + void init(Transaction doc, String value); + + void update(Transaction tx, String value); + + AmValue get(Read r); + + AmValue getAt(Read r, ChangeHash[] heads); + } + + void run(TestCase testcase) { + // Steps + // Create the document + Document doc = new Document(); + // Create the first state and commit + Transaction tx = doc.startTransaction(); + testcase.init(tx, "value1"); + tx.commit(); + // save the heads + ChangeHash[] heads = doc.getHeads(); + // Create a new transaction + tx = doc.startTransaction(); + // Run the second update + testcase.update(tx, "value2"); + tx.commit(); + // Check that get returns current value + tx = doc.startTransaction(); + // Check that get and getAt on open tx work as expected + Assertions.assertEquals("value2", ((AmValue.Str) testcase.get(tx)).getValue()); + Assertions.assertEquals("value1", ((AmValue.Str) testcase.getAt(tx, heads)).getValue()); + + // Check that get and getAt on document with open tx work + Assertions.assertEquals("value2", ((AmValue.Str) testcase.get(doc)).getValue()); + Assertions.assertEquals("value1", ((AmValue.Str) testcase.getAt(doc, heads)).getValue()); + + tx.commit(); + + // Check that get and getAt on doc with closed tx work as expected + Assertions.assertEquals("value2", ((AmValue.Str) testcase.get(doc)).getValue()); + Assertions.assertEquals("value1", ((AmValue.Str) testcase.getAt(doc, heads)).getValue()); + } + + @Test + public void testGetAtInMap() { + run(new TestCase() { + @Override + public void init(Transaction tx, String value) { + tx.set(ObjectId.ROOT, "key", value); + } + + @Override + public void update(Transaction tx, String value) { + tx.set(ObjectId.ROOT, "key", value); + } + + @Override + public AmValue get(Read r) { + return r.get(ObjectId.ROOT, "key").get(); + } + + @Override + public AmValue getAt(Read r, ChangeHash[] heads) { + return r.get(ObjectId.ROOT, "key", heads).get(); + } + }); + } + + @Test + public void testGetAtInList() { + run(new TestCase() { + ObjectId list; + + @Override + public void init(Transaction tx, String value) { + list = tx.set(ObjectId.ROOT, "list", ObjectType.LIST); + tx.insert(list, 0, value); + } + + @Override + public void update(Transaction tx, String value) { + tx.set(list, 0, value); + } + + @Override + public AmValue get(Read r) { + return r.get(list, 0).get(); + } + + @Override + public AmValue getAt(Read r, ChangeHash[] heads) { + return r.get(list, 0, heads).get(); + } + }); + } } diff --git a/lib/src/test/java/org/automerge/TestGetObjType.java b/lib/src/test/java/org/automerge/TestGetObjType.java index 5963cb3..44148d2 100644 --- a/lib/src/test/java/org/automerge/TestGetObjType.java +++ b/lib/src/test/java/org/automerge/TestGetObjType.java @@ -6,39 +6,39 @@ class TestGetObjType { - @Test - public void testGetObjType() { - Document doc = new Document(); - ObjectId map; - ObjectId list; - ObjectId text; - try (Transaction tx = doc.startTransaction()) { - map = tx.set(ObjectId.ROOT, "map", ObjectType.MAP); - list = tx.set(ObjectId.ROOT, "list", ObjectType.LIST); - text = tx.set(ObjectId.ROOT, "text", ObjectType.TEXT); - tx.commit(); - } + @Test + public void testGetObjType() { + Document doc = new Document(); + ObjectId map; + ObjectId list; + ObjectId text; + try (Transaction tx = doc.startTransaction()) { + map = tx.set(ObjectId.ROOT, "map", ObjectType.MAP); + list = tx.set(ObjectId.ROOT, "list", ObjectType.LIST); + text = tx.set(ObjectId.ROOT, "text", ObjectType.TEXT); + tx.commit(); + } - // make an object ID from a different document - Document otherDoc = new Document(); - ObjectId missingObj; - try (Transaction tx = otherDoc.startTransaction()) { - missingObj = tx.set(ObjectId.ROOT, "other", ObjectType.MAP); - tx.commit(); - } + // make an object ID from a different document + Document otherDoc = new Document(); + ObjectId missingObj; + try (Transaction tx = otherDoc.startTransaction()) { + missingObj = tx.set(ObjectId.ROOT, "other", ObjectType.MAP); + tx.commit(); + } - Assertions.assertEquals(Optional.of(ObjectType.MAP), doc.getObjectType(map)); - Assertions.assertEquals(Optional.of(ObjectType.LIST), doc.getObjectType(list)); - Assertions.assertEquals(Optional.of(ObjectType.TEXT), doc.getObjectType(text)); - Assertions.assertEquals(Optional.empty(), doc.getObjectType(missingObj)); + Assertions.assertEquals(Optional.of(ObjectType.MAP), doc.getObjectType(map)); + Assertions.assertEquals(Optional.of(ObjectType.LIST), doc.getObjectType(list)); + Assertions.assertEquals(Optional.of(ObjectType.TEXT), doc.getObjectType(text)); + Assertions.assertEquals(Optional.empty(), doc.getObjectType(missingObj)); - // now the same tests but in a transaction - try (Transaction tx = doc.startTransaction()) { - Assertions.assertEquals(Optional.of(ObjectType.MAP), tx.getObjectType(map)); - Assertions.assertEquals(Optional.of(ObjectType.LIST), tx.getObjectType(list)); - Assertions.assertEquals(Optional.of(ObjectType.TEXT), tx.getObjectType(text)); - Assertions.assertEquals(Optional.empty(), tx.getObjectType(missingObj)); - } - } + // now the same tests but in a transaction + try (Transaction tx = doc.startTransaction()) { + Assertions.assertEquals(Optional.of(ObjectType.MAP), tx.getObjectType(map)); + Assertions.assertEquals(Optional.of(ObjectType.LIST), tx.getObjectType(list)); + Assertions.assertEquals(Optional.of(ObjectType.TEXT), tx.getObjectType(text)); + Assertions.assertEquals(Optional.empty(), tx.getObjectType(missingObj)); + } + } } diff --git a/lib/src/test/java/org/automerge/TestHeads.java b/lib/src/test/java/org/automerge/TestHeads.java index d0a58dc..655ca0b 100644 --- a/lib/src/test/java/org/automerge/TestHeads.java +++ b/lib/src/test/java/org/automerge/TestHeads.java @@ -4,15 +4,15 @@ import org.junit.jupiter.api.Test; public class TestHeads { - @Test - public void testGetHeads() { - Document doc = new Document(); - Transaction tx = doc.startTransaction(); - tx.set(ObjectId.ROOT, "key", "value"); - Assertions.assertEquals(tx.getHeads().length, 0); - Assertions.assertEquals(doc.getHeads().length, 0); - tx.commit(); - Assertions.assertEquals(doc.getHeads().length, 1); - Assertions.assertEquals(doc.getHeads()[0].getBytes().length, 32); - } + @Test + public void testGetHeads() { + Document doc = new Document(); + Transaction tx = doc.startTransaction(); + tx.set(ObjectId.ROOT, "key", "value"); + Assertions.assertEquals(tx.getHeads().length, 0); + Assertions.assertEquals(doc.getHeads().length, 0); + tx.commit(); + Assertions.assertEquals(doc.getHeads().length, 1); + Assertions.assertEquals(doc.getHeads()[0].getBytes().length, 32); + } } diff --git a/lib/src/test/java/org/automerge/TestIncrement.java b/lib/src/test/java/org/automerge/TestIncrement.java index 0df9ddc..1317c63 100644 --- a/lib/src/test/java/org/automerge/TestIncrement.java +++ b/lib/src/test/java/org/automerge/TestIncrement.java @@ -5,31 +5,31 @@ import org.junit.jupiter.api.Test; public final class TestIncrement { - private Document doc; - private Transaction tx; + private Document doc; + private Transaction tx; - public TestIncrement() { - super(); - } + public TestIncrement() { + super(); + } - @BeforeEach - public void setup() { - doc = new Document(); - tx = doc.startTransaction(); - } + @BeforeEach + public void setup() { + doc = new Document(); + tx = doc.startTransaction(); + } - @Test - public void testIncrementInMap() { - tx.set(ObjectId.ROOT, "key", new Counter(10)); - tx.increment(ObjectId.ROOT, "key", 5); - Assertions.assertEquals(15, ((AmValue.Counter) doc.get(ObjectId.ROOT, "key").get()).getValue()); - } + @Test + public void testIncrementInMap() { + tx.set(ObjectId.ROOT, "key", new Counter(10)); + tx.increment(ObjectId.ROOT, "key", 5); + Assertions.assertEquals(15, ((AmValue.Counter) doc.get(ObjectId.ROOT, "key").get()).getValue()); + } - @Test - public void testIncrementInList() { - ObjectId list = tx.set(ObjectId.ROOT, "list", ObjectType.LIST); - tx.insert(list, 0, new Counter(10)); - tx.increment(list, 0, 5); - Assertions.assertEquals(15, ((AmValue.Counter) doc.get(list, 0).get()).getValue()); - } + @Test + public void testIncrementInList() { + ObjectId list = tx.set(ObjectId.ROOT, "list", ObjectType.LIST); + tx.insert(list, 0, new Counter(10)); + tx.increment(list, 0, 5); + Assertions.assertEquals(15, ((AmValue.Counter) doc.get(list, 0).get()).getValue()); + } } diff --git a/lib/src/test/java/org/automerge/TestInsert.java b/lib/src/test/java/org/automerge/TestInsert.java index cc049dd..650a742 100644 --- a/lib/src/test/java/org/automerge/TestInsert.java +++ b/lib/src/test/java/org/automerge/TestInsert.java @@ -7,88 +7,88 @@ import org.junit.jupiter.api.Test; public final class TestInsert { - private Document doc; - private Transaction tx; - private ObjectId list; + private Document doc; + private Transaction tx; + private ObjectId list; - public TestInsert() { - super(); - } + public TestInsert() { + super(); + } - @BeforeEach - public void setup() { - doc = new Document(); - tx = doc.startTransaction(); - list = tx.set(ObjectId.ROOT, "key", ObjectType.LIST); - } + @BeforeEach + public void setup() { + doc = new Document(); + tx = doc.startTransaction(); + list = tx.set(ObjectId.ROOT, "key", ObjectType.LIST); + } - @Test - public void testInsertDoubleInList() { - tx.insert(list, 0, 1.23); - Assertions.assertEquals(1.23, ((AmValue.F64) doc.get(list, 0).get()).getValue()); - } + @Test + public void testInsertDoubleInList() { + tx.insert(list, 0, 1.23); + Assertions.assertEquals(1.23, ((AmValue.F64) doc.get(list, 0).get()).getValue()); + } - @Test - public void testInsertStringInList() { - tx.insert(list, 0, "something"); - Assertions.assertEquals("something", ((AmValue.Str) doc.get(list, 0).get()).getValue()); - } + @Test + public void testInsertStringInList() { + tx.insert(list, 0, "something"); + Assertions.assertEquals("something", ((AmValue.Str) doc.get(list, 0).get()).getValue()); + } - @Test - public void testInsertIntInList() { - tx.insert(list, 0, 10); - Assertions.assertEquals(10, ((AmValue.Int) doc.get(list, 0).get()).getValue()); - } + @Test + public void testInsertIntInList() { + tx.insert(list, 0, 10); + Assertions.assertEquals(10, ((AmValue.Int) doc.get(list, 0).get()).getValue()); + } - @Test - public void testInsertUingInList() { - tx.insert(list, 0, NewValue.uint(10)); - Assertions.assertEquals(10, ((AmValue.UInt) doc.get(list, 0).get()).getValue()); - } + @Test + public void testInsertUingInList() { + tx.insert(list, 0, NewValue.uint(10)); + Assertions.assertEquals(10, ((AmValue.UInt) doc.get(list, 0).get()).getValue()); + } - @Test - public void testInsertBytesInList() { - byte[] value = "somebytes".getBytes(); - tx.insert(list, 0, value); - Optional result = doc.get(list, 0); - Assertions.assertTrue(result.isPresent()); - Assertions.assertArrayEquals(((AmValue.Bytes) result.get()).getValue(), value); - } + @Test + public void testInsertBytesInList() { + byte[] value = "somebytes".getBytes(); + tx.insert(list, 0, value); + Optional result = doc.get(list, 0); + Assertions.assertTrue(result.isPresent()); + Assertions.assertArrayEquals(((AmValue.Bytes) result.get()).getValue(), value); + } - @Test - public void testInsertNullInList() { - tx.insert(list, 0, NewValue.NULL); - Assertions.assertInstanceOf(AmValue.Null.class, doc.get(list, 0).get()); - } + @Test + public void testInsertNullInList() { + tx.insert(list, 0, NewValue.NULL); + Assertions.assertInstanceOf(AmValue.Null.class, doc.get(list, 0).get()); + } - @Test - public void testInsertCounterInList() { - tx.insert(list, 0, new Counter(10)); - Assertions.assertEquals(10, ((AmValue.Counter) doc.get(list, 0).get()).getValue()); - } + @Test + public void testInsertCounterInList() { + tx.insert(list, 0, new Counter(10)); + Assertions.assertEquals(10, ((AmValue.Counter) doc.get(list, 0).get()).getValue()); + } - @Test - public void testInsertDateInList() { - Date now = new Date(); - tx.insert(list, 0, now); - Assertions.assertEquals(now, ((AmValue.Timestamp) doc.get(list, 0).get()).getValue()); - } + @Test + public void testInsertDateInList() { + Date now = new Date(); + tx.insert(list, 0, now); + Assertions.assertEquals(now, ((AmValue.Timestamp) doc.get(list, 0).get()).getValue()); + } - @Test - public void testBoolInList() { - tx.insert(list, 0, false); - tx.insert(list, 1, true); - Assertions.assertEquals(false, ((AmValue.Bool) doc.get(list, 0).get()).getValue()); - Assertions.assertEquals(true, ((AmValue.Bool) doc.get(list, 1).get()).getValue()); - } + @Test + public void testBoolInList() { + tx.insert(list, 0, false); + tx.insert(list, 1, true); + Assertions.assertEquals(false, ((AmValue.Bool) doc.get(list, 0).get()).getValue()); + Assertions.assertEquals(true, ((AmValue.Bool) doc.get(list, 1).get()).getValue()); + } - @Test - public void testInsertObjInList() { - ObjectId listId = tx.insert(list, 0, ObjectType.LIST); - ObjectId textId = tx.insert(list, 1, ObjectType.TEXT); - ObjectId mapId = tx.insert(list, 2, ObjectType.MAP); - Assertions.assertEquals(listId, ((AmValue.List) doc.get(list, 0).get()).getId()); - Assertions.assertEquals(textId, ((AmValue.Text) doc.get(list, 1).get()).getId()); - Assertions.assertEquals(mapId, ((AmValue.Map) doc.get(list, 2).get()).getId()); - } + @Test + public void testInsertObjInList() { + ObjectId listId = tx.insert(list, 0, ObjectType.LIST); + ObjectId textId = tx.insert(list, 1, ObjectType.TEXT); + ObjectId mapId = tx.insert(list, 2, ObjectType.MAP); + Assertions.assertEquals(listId, ((AmValue.List) doc.get(list, 0).get()).getId()); + Assertions.assertEquals(textId, ((AmValue.Text) doc.get(list, 1).get()).getId()); + Assertions.assertEquals(mapId, ((AmValue.Map) doc.get(list, 2).get()).getId()); + } } diff --git a/lib/src/test/java/org/automerge/TestKeys.java b/lib/src/test/java/org/automerge/TestKeys.java index b3a2ad2..2415a9c 100644 --- a/lib/src/test/java/org/automerge/TestKeys.java +++ b/lib/src/test/java/org/automerge/TestKeys.java @@ -5,31 +5,31 @@ import org.junit.jupiter.api.Test; class TestKeys { - @Test - public void testKeys() { - Document doc = new Document(); - Transaction tx = doc.startTransaction(); - tx.set(ObjectId.ROOT, "key1", ObjectType.MAP); - tx.set(ObjectId.ROOT, "key2", ObjectType.LIST); - ObjectId list = tx.set(ObjectId.ROOT, "list", ObjectType.LIST); + @Test + public void testKeys() { + Document doc = new Document(); + Transaction tx = doc.startTransaction(); + tx.set(ObjectId.ROOT, "key1", ObjectType.MAP); + tx.set(ObjectId.ROOT, "key2", ObjectType.LIST); + ObjectId list = tx.set(ObjectId.ROOT, "list", ObjectType.LIST); - String[] expected = {"key1", "key2", "list",}; - Assertions.assertArrayEquals(expected, tx.keys(ObjectId.ROOT).get()); - Assertions.assertArrayEquals(expected, doc.keys(ObjectId.ROOT).get()); + String[] expected = {"key1", "key2", "list",}; + Assertions.assertArrayEquals(expected, tx.keys(ObjectId.ROOT).get()); + Assertions.assertArrayEquals(expected, doc.keys(ObjectId.ROOT).get()); - tx.commit(); + tx.commit(); - ChangeHash[] heads = doc.getHeads(); - tx = doc.startTransaction(); - String[] expectedBefore = {"key1", "key2", "list",}; - tx.delete(ObjectId.ROOT, "key1"); - String[] expectedAfter = {"key2", "list",}; - Assertions.assertArrayEquals(expectedAfter, tx.keys(ObjectId.ROOT).get()); - Assertions.assertArrayEquals(expectedBefore, tx.keys(ObjectId.ROOT, heads).get()); - Assertions.assertArrayEquals(expectedBefore, doc.keys(ObjectId.ROOT, heads).get()); - Assertions.assertEquals(Optional.empty(), tx.keys(list)); - tx.commit(); - Assertions.assertArrayEquals(expectedBefore, doc.keys(ObjectId.ROOT, heads).get()); - Assertions.assertEquals(Optional.empty(), doc.keys(list)); - } + ChangeHash[] heads = doc.getHeads(); + tx = doc.startTransaction(); + String[] expectedBefore = {"key1", "key2", "list",}; + tx.delete(ObjectId.ROOT, "key1"); + String[] expectedAfter = {"key2", "list",}; + Assertions.assertArrayEquals(expectedAfter, tx.keys(ObjectId.ROOT).get()); + Assertions.assertArrayEquals(expectedBefore, tx.keys(ObjectId.ROOT, heads).get()); + Assertions.assertArrayEquals(expectedBefore, doc.keys(ObjectId.ROOT, heads).get()); + Assertions.assertEquals(Optional.empty(), tx.keys(list)); + tx.commit(); + Assertions.assertArrayEquals(expectedBefore, doc.keys(ObjectId.ROOT, heads).get()); + Assertions.assertEquals(Optional.empty(), doc.keys(list)); + } } diff --git a/lib/src/test/java/org/automerge/TestListItems.java b/lib/src/test/java/org/automerge/TestListItems.java index bde4620..0a0965b 100644 --- a/lib/src/test/java/org/automerge/TestListItems.java +++ b/lib/src/test/java/org/automerge/TestListItems.java @@ -7,113 +7,113 @@ import org.junit.jupiter.api.Test; public class TestListItems { - private Document doc; - private ObjectId list; - private ObjectId subList; - private ObjectId map; - private ObjectId text; - private byte[] bytes; - private Date date; - - @BeforeEach - public void setup() { - doc = new Document(); - } - - @Test - void testListItems() { - Transaction tx = doc.startTransaction(); - // Insert a bunch of items - insertListItems(tx); - - // Check we can read them from a doc with open transaction - assertListItems(doc); - // Check we can read them from the transaction - assertListItems(tx); - tx.commit(); - // Check we can read them from a doc with closed transaction - assertListItems(doc); - - // Save the heads - ChangeHash[] heads = doc.getHeads(); - - // Now delete the items we inserted - tx = doc.startTransaction(); - tx.splice(list, 1, 11, Collections.emptyIterator()); - - // Check the current length in the open transaction - Assertions.assertEquals(1, tx.length(list)); - // Check the current length in the doc with open transaction - Assertions.assertEquals(1, doc.length(list)); - - // Check the current items in the open transaction - AmValue[] items = tx.listItems(list).get(); - Assertions.assertEquals(1, ((AmValue.Int) items[0]).getValue()); - // Check the current items in the doc with open transaction - items = doc.listItems(list).get(); - Assertions.assertEquals(1, ((AmValue.Int) items[0]).getValue()); - - // Check the length at heads in the open transaction - Assertions.assertEquals(12, tx.length(list, heads)); - // Check the length at heads in the doc with open transaction - Assertions.assertEquals(12, doc.length(list, heads)); - - // Check the list items at heads in the open transaction - items = tx.listItems(list, heads).get(); - assertItems(items); - - // Check the list items at heads in the doc with open transaction - items = doc.listItems(list, heads).get(); - assertItems(items); - tx.commit(); - - // Check the current items in doc with closed transaction - items = doc.listItems(list).get(); - Assertions.assertEquals(1, ((AmValue.Int) items[0]).getValue()); - - // Check the list items at heads in the doc with closed transaction - items = doc.listItems(list, heads).get(); - assertItems(items); - } - - void insertListItems(Transaction tx) { - list = tx.set(ObjectId.ROOT, "list", ObjectType.LIST); - - tx.insert(list, 0, 1); - tx.insert(list, 1, NewValue.uint(2)); - tx.insert(list, 2, false); - bytes = "bytes".getBytes(); - tx.insert(list, 3, bytes); - date = new Date(); - tx.insert(list, 4, date); - tx.insert(list, 5, 1.2); - tx.insert(list, 6, "somestring"); - tx.insert(list, 7, NewValue.NULL); - tx.insert(list, 8, new Counter(10)); - map = tx.insert(list, 9, ObjectType.MAP); - subList = tx.insert(list, 10, ObjectType.LIST); - text = tx.insert(list, 11, ObjectType.TEXT); - } - - void assertListItems(R read) { - Assertions.assertEquals(12, read.length(list)); - AmValue[] items = read.listItems(list).get(); - assertItems(items); - } - - void assertItems(AmValue[] items) { - Assertions.assertEquals(12, items.length); - Assertions.assertEquals(1, ((AmValue.Int) items[0]).getValue()); - Assertions.assertEquals(2, ((AmValue.UInt) items[1]).getValue()); - Assertions.assertEquals(false, ((AmValue.Bool) items[2]).getValue()); - Assertions.assertArrayEquals(bytes, ((AmValue.Bytes) items[3]).getValue()); - Assertions.assertEquals(date, ((AmValue.Timestamp) items[4]).getValue()); - Assertions.assertEquals(1.2, ((AmValue.F64) items[5]).getValue()); - Assertions.assertEquals("somestring", ((AmValue.Str) items[6]).getValue()); - Assertions.assertInstanceOf(AmValue.Null.class, items[7]); - Assertions.assertEquals(10, ((AmValue.Counter) items[8]).getValue()); - Assertions.assertEquals(map, ((AmValue.Map) items[9]).getId()); - Assertions.assertEquals(subList, ((AmValue.List) items[10]).getId()); - Assertions.assertEquals(text, ((AmValue.Text) items[11]).getId()); - } + private Document doc; + private ObjectId list; + private ObjectId subList; + private ObjectId map; + private ObjectId text; + private byte[] bytes; + private Date date; + + @BeforeEach + public void setup() { + doc = new Document(); + } + + @Test + void testListItems() { + Transaction tx = doc.startTransaction(); + // Insert a bunch of items + insertListItems(tx); + + // Check we can read them from a doc with open transaction + assertListItems(doc); + // Check we can read them from the transaction + assertListItems(tx); + tx.commit(); + // Check we can read them from a doc with closed transaction + assertListItems(doc); + + // Save the heads + ChangeHash[] heads = doc.getHeads(); + + // Now delete the items we inserted + tx = doc.startTransaction(); + tx.splice(list, 1, 11, Collections.emptyIterator()); + + // Check the current length in the open transaction + Assertions.assertEquals(1, tx.length(list)); + // Check the current length in the doc with open transaction + Assertions.assertEquals(1, doc.length(list)); + + // Check the current items in the open transaction + AmValue[] items = tx.listItems(list).get(); + Assertions.assertEquals(1, ((AmValue.Int) items[0]).getValue()); + // Check the current items in the doc with open transaction + items = doc.listItems(list).get(); + Assertions.assertEquals(1, ((AmValue.Int) items[0]).getValue()); + + // Check the length at heads in the open transaction + Assertions.assertEquals(12, tx.length(list, heads)); + // Check the length at heads in the doc with open transaction + Assertions.assertEquals(12, doc.length(list, heads)); + + // Check the list items at heads in the open transaction + items = tx.listItems(list, heads).get(); + assertItems(items); + + // Check the list items at heads in the doc with open transaction + items = doc.listItems(list, heads).get(); + assertItems(items); + tx.commit(); + + // Check the current items in doc with closed transaction + items = doc.listItems(list).get(); + Assertions.assertEquals(1, ((AmValue.Int) items[0]).getValue()); + + // Check the list items at heads in the doc with closed transaction + items = doc.listItems(list, heads).get(); + assertItems(items); + } + + void insertListItems(Transaction tx) { + list = tx.set(ObjectId.ROOT, "list", ObjectType.LIST); + + tx.insert(list, 0, 1); + tx.insert(list, 1, NewValue.uint(2)); + tx.insert(list, 2, false); + bytes = "bytes".getBytes(); + tx.insert(list, 3, bytes); + date = new Date(); + tx.insert(list, 4, date); + tx.insert(list, 5, 1.2); + tx.insert(list, 6, "somestring"); + tx.insert(list, 7, NewValue.NULL); + tx.insert(list, 8, new Counter(10)); + map = tx.insert(list, 9, ObjectType.MAP); + subList = tx.insert(list, 10, ObjectType.LIST); + text = tx.insert(list, 11, ObjectType.TEXT); + } + + void assertListItems(R read) { + Assertions.assertEquals(12, read.length(list)); + AmValue[] items = read.listItems(list).get(); + assertItems(items); + } + + void assertItems(AmValue[] items) { + Assertions.assertEquals(12, items.length); + Assertions.assertEquals(1, ((AmValue.Int) items[0]).getValue()); + Assertions.assertEquals(2, ((AmValue.UInt) items[1]).getValue()); + Assertions.assertEquals(false, ((AmValue.Bool) items[2]).getValue()); + Assertions.assertArrayEquals(bytes, ((AmValue.Bytes) items[3]).getValue()); + Assertions.assertEquals(date, ((AmValue.Timestamp) items[4]).getValue()); + Assertions.assertEquals(1.2, ((AmValue.F64) items[5]).getValue()); + Assertions.assertEquals("somestring", ((AmValue.Str) items[6]).getValue()); + Assertions.assertInstanceOf(AmValue.Null.class, items[7]); + Assertions.assertEquals(10, ((AmValue.Counter) items[8]).getValue()); + Assertions.assertEquals(map, ((AmValue.Map) items[9]).getId()); + Assertions.assertEquals(subList, ((AmValue.List) items[10]).getId()); + Assertions.assertEquals(text, ((AmValue.Text) items[11]).getId()); + } } diff --git a/lib/src/test/java/org/automerge/TestMapEntries.java b/lib/src/test/java/org/automerge/TestMapEntries.java index 8bdb6b2..f31526c 100644 --- a/lib/src/test/java/org/automerge/TestMapEntries.java +++ b/lib/src/test/java/org/automerge/TestMapEntries.java @@ -7,116 +7,116 @@ import org.junit.jupiter.api.Test; class TestMapEntries { - private ObjectId map; - private ObjectId list; - private ObjectId text; - private Date dateValue; - - @Test - public void testMapEntries() { - run(doc -> doc.startTransaction()); - } - - void run(Function createTx) { - Document doc = new Document(); - Transaction tx = createTx.apply(doc); - insertMapEntries(tx); - assertMapEntries(tx); - assertMapEntries(doc); - tx.commit(); - assertMapEntries(doc); - - ChangeHash[] heads = doc.getHeads(); - tx = createTx.apply(doc); - for (String key : tx.keys(ObjectId.ROOT).get()) { - tx.delete(ObjectId.ROOT, key); - } - tx.set(ObjectId.ROOT, "newkey", "newvalue"); - - assertMapEntriesAfter(tx); - assertMapEntriesAfter(doc); - tx.commit(); - assertMapEntriesAfter(doc); - - tx = createTx.apply(doc); - MapEntry[] txAt = tx.mapEntries(ObjectId.ROOT, heads).get(); - assertMapEntries(txAt); - MapEntry[] docAtPreCommit = doc.mapEntries(ObjectId.ROOT, heads).get(); - assertMapEntries(docAtPreCommit); - tx.commit(); - MapEntry[] docAtPostCommit = doc.mapEntries(ObjectId.ROOT, heads).get(); - assertMapEntries(docAtPostCommit); - } - - void insertMapEntries(Transaction tx) { - map = tx.set(ObjectId.ROOT, "map", ObjectType.MAP); - list = tx.set(ObjectId.ROOT, "list", ObjectType.LIST); - text = tx.set(ObjectId.ROOT, "text", ObjectType.TEXT); - - tx.set(ObjectId.ROOT, "int", 1); - tx.set(ObjectId.ROOT, "uint", NewValue.uint(1)); - tx.set(ObjectId.ROOT, "float", 1.0); - tx.set(ObjectId.ROOT, "str", "str"); - tx.set(ObjectId.ROOT, "bytes", "bytes".getBytes()); - tx.set(ObjectId.ROOT, "counter", new Counter(10)); - dateValue = new Date(); - tx.set(ObjectId.ROOT, "date", dateValue); - tx.set(ObjectId.ROOT, "null", NewValue.NULL); - tx.set(ObjectId.ROOT, "bool", true); - } - - void assertMapEntries(Read read) { - MapEntry[] result = read.mapEntries(ObjectId.ROOT).get(); - assertMapEntries(result); - } - - void assertMapEntries(MapEntry[] entries) { - HashMap entrysByKey = new HashMap(); - for (MapEntry entry : entries) { - entrysByKey.put(entry.getKey(), entry); - } - - MapEntry mapEntry = entrysByKey.get("map"); - Assertions.assertEquals(map, ((AmValue.Map) mapEntry.getValue()).getId()); - - MapEntry listEntry = entrysByKey.get("list"); - Assertions.assertEquals(list, ((AmValue.List) listEntry.getValue()).getId()); - - MapEntry textEntry = entrysByKey.get("text"); - Assertions.assertEquals(text, ((AmValue.Text) textEntry.getValue()).getId()); - - MapEntry intEntry = entrysByKey.get("int"); - Assertions.assertEquals(1, ((AmValue.Int) intEntry.getValue()).getValue()); - - MapEntry uintEntry = entrysByKey.get("uint"); - Assertions.assertEquals(1, ((AmValue.UInt) uintEntry.getValue()).getValue()); - - MapEntry floatEntry = entrysByKey.get("float"); - Assertions.assertEquals(1.0, ((AmValue.F64) floatEntry.getValue()).getValue()); - - MapEntry strEntry = entrysByKey.get("str"); - Assertions.assertEquals("str", ((AmValue.Str) strEntry.getValue()).getValue()); - - MapEntry bytesEntry = entrysByKey.get("bytes"); - Assertions.assertArrayEquals("bytes".getBytes(), ((AmValue.Bytes) bytesEntry.getValue()).getValue()); - - MapEntry counterEntry = entrysByKey.get("counter"); - Assertions.assertEquals(10, ((AmValue.Counter) counterEntry.getValue()).getValue()); - - MapEntry dateEntry = entrysByKey.get("date"); - Assertions.assertEquals(dateValue, ((AmValue.Timestamp) dateEntry.getValue()).getValue()); - - MapEntry nullEntry = entrysByKey.get("null"); - Assertions.assertInstanceOf(AmValue.Null.class, nullEntry.getValue()); - - MapEntry boolEntry = entrysByKey.get("bool"); - Assertions.assertEquals(true, ((AmValue.Bool) boolEntry.getValue()).getValue()); - } - - void assertMapEntriesAfter(Read r) { - MapEntry[] entries = r.mapEntries(ObjectId.ROOT).get(); - Assertions.assertEquals(1, entries.length); - Assertions.assertEquals("newkey", entries[0].getKey()); - Assertions.assertEquals("newvalue", ((AmValue.Str) entries[0].getValue()).getValue()); - } + private ObjectId map; + private ObjectId list; + private ObjectId text; + private Date dateValue; + + @Test + public void testMapEntries() { + run(doc -> doc.startTransaction()); + } + + void run(Function createTx) { + Document doc = new Document(); + Transaction tx = createTx.apply(doc); + insertMapEntries(tx); + assertMapEntries(tx); + assertMapEntries(doc); + tx.commit(); + assertMapEntries(doc); + + ChangeHash[] heads = doc.getHeads(); + tx = createTx.apply(doc); + for (String key : tx.keys(ObjectId.ROOT).get()) { + tx.delete(ObjectId.ROOT, key); + } + tx.set(ObjectId.ROOT, "newkey", "newvalue"); + + assertMapEntriesAfter(tx); + assertMapEntriesAfter(doc); + tx.commit(); + assertMapEntriesAfter(doc); + + tx = createTx.apply(doc); + MapEntry[] txAt = tx.mapEntries(ObjectId.ROOT, heads).get(); + assertMapEntries(txAt); + MapEntry[] docAtPreCommit = doc.mapEntries(ObjectId.ROOT, heads).get(); + assertMapEntries(docAtPreCommit); + tx.commit(); + MapEntry[] docAtPostCommit = doc.mapEntries(ObjectId.ROOT, heads).get(); + assertMapEntries(docAtPostCommit); + } + + void insertMapEntries(Transaction tx) { + map = tx.set(ObjectId.ROOT, "map", ObjectType.MAP); + list = tx.set(ObjectId.ROOT, "list", ObjectType.LIST); + text = tx.set(ObjectId.ROOT, "text", ObjectType.TEXT); + + tx.set(ObjectId.ROOT, "int", 1); + tx.set(ObjectId.ROOT, "uint", NewValue.uint(1)); + tx.set(ObjectId.ROOT, "float", 1.0); + tx.set(ObjectId.ROOT, "str", "str"); + tx.set(ObjectId.ROOT, "bytes", "bytes".getBytes()); + tx.set(ObjectId.ROOT, "counter", new Counter(10)); + dateValue = new Date(); + tx.set(ObjectId.ROOT, "date", dateValue); + tx.set(ObjectId.ROOT, "null", NewValue.NULL); + tx.set(ObjectId.ROOT, "bool", true); + } + + void assertMapEntries(Read read) { + MapEntry[] result = read.mapEntries(ObjectId.ROOT).get(); + assertMapEntries(result); + } + + void assertMapEntries(MapEntry[] entries) { + HashMap entrysByKey = new HashMap(); + for (MapEntry entry : entries) { + entrysByKey.put(entry.getKey(), entry); + } + + MapEntry mapEntry = entrysByKey.get("map"); + Assertions.assertEquals(map, ((AmValue.Map) mapEntry.getValue()).getId()); + + MapEntry listEntry = entrysByKey.get("list"); + Assertions.assertEquals(list, ((AmValue.List) listEntry.getValue()).getId()); + + MapEntry textEntry = entrysByKey.get("text"); + Assertions.assertEquals(text, ((AmValue.Text) textEntry.getValue()).getId()); + + MapEntry intEntry = entrysByKey.get("int"); + Assertions.assertEquals(1, ((AmValue.Int) intEntry.getValue()).getValue()); + + MapEntry uintEntry = entrysByKey.get("uint"); + Assertions.assertEquals(1, ((AmValue.UInt) uintEntry.getValue()).getValue()); + + MapEntry floatEntry = entrysByKey.get("float"); + Assertions.assertEquals(1.0, ((AmValue.F64) floatEntry.getValue()).getValue()); + + MapEntry strEntry = entrysByKey.get("str"); + Assertions.assertEquals("str", ((AmValue.Str) strEntry.getValue()).getValue()); + + MapEntry bytesEntry = entrysByKey.get("bytes"); + Assertions.assertArrayEquals("bytes".getBytes(), ((AmValue.Bytes) bytesEntry.getValue()).getValue()); + + MapEntry counterEntry = entrysByKey.get("counter"); + Assertions.assertEquals(10, ((AmValue.Counter) counterEntry.getValue()).getValue()); + + MapEntry dateEntry = entrysByKey.get("date"); + Assertions.assertEquals(dateValue, ((AmValue.Timestamp) dateEntry.getValue()).getValue()); + + MapEntry nullEntry = entrysByKey.get("null"); + Assertions.assertInstanceOf(AmValue.Null.class, nullEntry.getValue()); + + MapEntry boolEntry = entrysByKey.get("bool"); + Assertions.assertEquals(true, ((AmValue.Bool) boolEntry.getValue()).getValue()); + } + + void assertMapEntriesAfter(Read r) { + MapEntry[] entries = r.mapEntries(ObjectId.ROOT).get(); + Assertions.assertEquals(1, entries.length); + Assertions.assertEquals("newkey", entries[0].getKey()); + Assertions.assertEquals("newvalue", ((AmValue.Str) entries[0].getValue()).getValue()); + } } diff --git a/lib/src/test/java/org/automerge/TestMarks.java b/lib/src/test/java/org/automerge/TestMarks.java index c18ace9..8b3358d 100644 --- a/lib/src/test/java/org/automerge/TestMarks.java +++ b/lib/src/test/java/org/automerge/TestMarks.java @@ -9,197 +9,197 @@ class TestMarks { - @Test - public void testCreateMarks() { - Document doc = new Document(); - ObjectId text; - Date now = new Date(); - try (Transaction tx = doc.startTransaction()) { - text = tx.set(ObjectId.ROOT, "text", ObjectType.TEXT); - tx.spliceText(text, 0, 0, "Hello"); - - tx.mark(text, 1, 3, "bool", true, ExpandMark.BOTH); - tx.mark(text, 1, 3, "string", "string", ExpandMark.BOTH); - tx.mark(text, 1, 3, "bytes", "bytes".getBytes(), ExpandMark.BOTH); - tx.mark(text, 1, 3, "int", -1, ExpandMark.BOTH); - tx.markUint(text, 1, 3, "uint", 1, ExpandMark.BOTH); - tx.mark(text, 1, 3, "float", 2.5, ExpandMark.BOTH); - tx.mark(text, 1, 3, "date", now, ExpandMark.BOTH); - tx.mark(text, 1, 3, "counter", new Counter(5), ExpandMark.BOTH); - tx.spliceText(text, 1, 0, "oo"); - tx.commit(); - } - - List marks = doc.marks(text); - Assertions.assertEquals(8, marks.size()); - - // Note that the marks are returned in alphabetical order - - Mark boolMark = marks.get(0); - assertMark(boolMark, 1, 5, "bool", value -> Assertions.assertTrue(((AmValue.Bool) value).getValue())); - - Mark bytesMark = marks.get(1); - assertMark(bytesMark, 1, 5, "bytes", - value -> Assertions.assertArrayEquals(((AmValue.Bytes) value).getValue(), "bytes".getBytes())); - - Mark counterMark = marks.get(2); - assertMark(counterMark, 1, 5, "counter", - value -> Assertions.assertEquals(((AmValue.Counter) value).getValue(), 5)); - - Mark dateMark = marks.get(3); - assertMark(dateMark, 1, 5, "date", - value -> Assertions.assertEquals(((AmValue.Timestamp) value).getValue(), now)); - - Mark floatMark = marks.get(4); - assertMark(floatMark, 1, 5, "float", value -> Assertions.assertEquals(((AmValue.F64) value).getValue(), 2.5)); - - Mark intMark = marks.get(5); - assertMark(intMark, 1, 5, "int", value -> Assertions.assertEquals(((AmValue.Int) value).getValue(), -1)); - - Mark stringMark = marks.get(6); - assertMark(stringMark, 1, 5, "string", - value -> Assertions.assertEquals(((AmValue.Str) value).getValue(), "string")); - - Mark uintMark = marks.get(7); - assertMark(uintMark, 1, 5, "uint", value -> Assertions.assertEquals(((AmValue.UInt) value).getValue(), 1)); - - } - - @Test - public void testNullMark() { - testMarkNull(doc -> doc.startTransaction()); - } - - @Test - public void testMarkPatch() { - Document doc = new Document(); - ObjectId text; - try (Transaction tx = doc.startTransaction()) { - text = tx.set(ObjectId.ROOT, "text", ObjectType.TEXT); - tx.spliceText(text, 0, 0, "Hello"); - tx.commit(); - } - - PatchLog patchLog = new PatchLog(); - try (Transaction tx = doc.startTransaction(patchLog)) { - tx.mark(text, 0, 5, "bold", true, ExpandMark.BOTH); - tx.commit(); - } - List patches = doc.makePatches(patchLog); - - Assertions.assertEquals(patches.size(), 1); - Patch patch = patches.get(0); - PatchAction.Mark action = (PatchAction.Mark) patch.getAction(); - - Assertions.assertEquals(action.getMarks().length, 1); - Mark mark = action.getMarks()[0]; - Assertions.assertEquals(mark.getName(), "bold"); - Assertions.assertEquals(mark.getStart(), 0); - Assertions.assertEquals(mark.getEnd(), 5); - Assertions.assertEquals(true, ((AmValue.Bool) mark.getValue()).getValue()); - } - - @Test - public void testUnmark() { - testUnmark(doc -> doc.startTransaction()); - } - - void testUnmark(Function createTx) { - Document doc = new Document(); - ObjectId text; - try (Transaction tx = createTx.apply(doc)) { - text = tx.set(ObjectId.ROOT, "text", ObjectType.TEXT); - tx.spliceText(text, 0, 0, "Hello"); - tx.mark(text, 0, 5, "bold", true, ExpandMark.NONE); - tx.commit(); - } - - try (Transaction tx = createTx.apply(doc)) { - tx.unmark(text, "bold", 0, 5, ExpandMark.BOTH); - tx.commit(); - } - - List marks = doc.marks(text); - Assertions.assertEquals(marks.size(), 0); - } - - void testMarkNull(Function createTx) { - Document doc = new Document(); - ObjectId text; - try (Transaction tx = createTx.apply(doc)) { - text = tx.set(ObjectId.ROOT, "text", ObjectType.TEXT); - tx.spliceText(text, 0, 0, "Hello"); - tx.mark(text, 0, 5, "comment", "1234", ExpandMark.NONE); - tx.commit(); - } - try (Transaction tx = createTx.apply(doc)) { - tx.markNull(text, 0, 2, "comment", ExpandMark.BOTH); - tx.commit(); - } - - List marks = doc.marks(text); - Assertions.assertEquals(1, marks.size()); - Mark mark = marks.get(0); - assertMark(mark, 2, 5, "comment", value -> Assertions.assertEquals("1234", ((AmValue.Str) value).getValue())); - } - - @Test - void testMarksAtIndex() { - Document doc = new Document(); - ObjectId text; - try (Transaction tx = doc.startTransaction()) { - text = tx.set(ObjectId.ROOT, "text", ObjectType.TEXT); - tx.spliceText(text, 0, 0, "Hello"); - tx.mark(text, 0, 5, "bold", true, ExpandMark.NONE); - HashMap marks = tx.getMarksAtIndex(text, 1); - Assertions.assertEquals(1, marks.size()); - Assertions.assertEquals(true, ((AmValue.Bool) marks.get("bold")).getValue()); - tx.commit(); - } - - HashMap marks = doc.getMarksAtIndex(text, 1); - Assertions.assertEquals(1, marks.size()); - Assertions.assertEquals(true, ((AmValue.Bool) marks.get("bold")).getValue()); - } - - @Test - void testMarksAtIndexAtHeads() { - Document doc = new Document(); - ObjectId text; - try (Transaction tx = doc.startTransaction()) { - text = tx.set(ObjectId.ROOT, "text", ObjectType.TEXT); - tx.spliceText(text, 0, 0, "Hello"); - tx.mark(text, 0, 5, "bold", true, ExpandMark.NONE); - HashMap marks = tx.getMarksAtIndex(text, 1); - Assertions.assertEquals(1, marks.size()); - Assertions.assertEquals(true, ((AmValue.Bool) marks.get("bold")).getValue()); - tx.commit(); - } - - // Save the heads - ChangeHash[] heads = doc.getHeads(); - - try (Transaction tx = doc.startTransaction()) { - tx.markNull(text, 0, 5, "bold", ExpandMark.NONE); - HashMap marks = tx.getMarksAtIndex(text, 1, heads); - Assertions.assertEquals(1, marks.size()); - Assertions.assertEquals(true, ((AmValue.Bool) marks.get("bold")).getValue()); - tx.commit(); - } - - HashMap marks = doc.getMarksAtIndex(text, 1, heads); - Assertions.assertEquals(1, marks.size()); - Assertions.assertEquals(true, ((AmValue.Bool) marks.get("bold")).getValue()); - } - - void assertMark(Mark mark, long start, long end, String name, MarkValueAssertion assertion) { - Assertions.assertEquals(start, mark.getStart()); - Assertions.assertEquals(end, mark.getEnd()); - Assertions.assertEquals(name, mark.getName()); - assertion.assertMarkValue(mark.getValue()); - } - - private interface MarkValueAssertion { - void assertMarkValue(AmValue value); - } + @Test + public void testCreateMarks() { + Document doc = new Document(); + ObjectId text; + Date now = new Date(); + try (Transaction tx = doc.startTransaction()) { + text = tx.set(ObjectId.ROOT, "text", ObjectType.TEXT); + tx.spliceText(text, 0, 0, "Hello"); + + tx.mark(text, 1, 3, "bool", true, ExpandMark.BOTH); + tx.mark(text, 1, 3, "string", "string", ExpandMark.BOTH); + tx.mark(text, 1, 3, "bytes", "bytes".getBytes(), ExpandMark.BOTH); + tx.mark(text, 1, 3, "int", -1, ExpandMark.BOTH); + tx.markUint(text, 1, 3, "uint", 1, ExpandMark.BOTH); + tx.mark(text, 1, 3, "float", 2.5, ExpandMark.BOTH); + tx.mark(text, 1, 3, "date", now, ExpandMark.BOTH); + tx.mark(text, 1, 3, "counter", new Counter(5), ExpandMark.BOTH); + tx.spliceText(text, 1, 0, "oo"); + tx.commit(); + } + + List marks = doc.marks(text); + Assertions.assertEquals(8, marks.size()); + + // Note that the marks are returned in alphabetical order + + Mark boolMark = marks.get(0); + assertMark(boolMark, 1, 5, "bool", value -> Assertions.assertTrue(((AmValue.Bool) value).getValue())); + + Mark bytesMark = marks.get(1); + assertMark(bytesMark, 1, 5, "bytes", + value -> Assertions.assertArrayEquals(((AmValue.Bytes) value).getValue(), "bytes".getBytes())); + + Mark counterMark = marks.get(2); + assertMark(counterMark, 1, 5, "counter", + value -> Assertions.assertEquals(((AmValue.Counter) value).getValue(), 5)); + + Mark dateMark = marks.get(3); + assertMark(dateMark, 1, 5, "date", + value -> Assertions.assertEquals(((AmValue.Timestamp) value).getValue(), now)); + + Mark floatMark = marks.get(4); + assertMark(floatMark, 1, 5, "float", value -> Assertions.assertEquals(((AmValue.F64) value).getValue(), 2.5)); + + Mark intMark = marks.get(5); + assertMark(intMark, 1, 5, "int", value -> Assertions.assertEquals(((AmValue.Int) value).getValue(), -1)); + + Mark stringMark = marks.get(6); + assertMark(stringMark, 1, 5, "string", + value -> Assertions.assertEquals(((AmValue.Str) value).getValue(), "string")); + + Mark uintMark = marks.get(7); + assertMark(uintMark, 1, 5, "uint", value -> Assertions.assertEquals(((AmValue.UInt) value).getValue(), 1)); + + } + + @Test + public void testNullMark() { + testMarkNull(doc -> doc.startTransaction()); + } + + @Test + public void testMarkPatch() { + Document doc = new Document(); + ObjectId text; + try (Transaction tx = doc.startTransaction()) { + text = tx.set(ObjectId.ROOT, "text", ObjectType.TEXT); + tx.spliceText(text, 0, 0, "Hello"); + tx.commit(); + } + + PatchLog patchLog = new PatchLog(); + try (Transaction tx = doc.startTransaction(patchLog)) { + tx.mark(text, 0, 5, "bold", true, ExpandMark.BOTH); + tx.commit(); + } + List patches = doc.makePatches(patchLog); + + Assertions.assertEquals(patches.size(), 1); + Patch patch = patches.get(0); + PatchAction.Mark action = (PatchAction.Mark) patch.getAction(); + + Assertions.assertEquals(action.getMarks().length, 1); + Mark mark = action.getMarks()[0]; + Assertions.assertEquals(mark.getName(), "bold"); + Assertions.assertEquals(mark.getStart(), 0); + Assertions.assertEquals(mark.getEnd(), 5); + Assertions.assertEquals(true, ((AmValue.Bool) mark.getValue()).getValue()); + } + + @Test + public void testUnmark() { + testUnmark(doc -> doc.startTransaction()); + } + + void testUnmark(Function createTx) { + Document doc = new Document(); + ObjectId text; + try (Transaction tx = createTx.apply(doc)) { + text = tx.set(ObjectId.ROOT, "text", ObjectType.TEXT); + tx.spliceText(text, 0, 0, "Hello"); + tx.mark(text, 0, 5, "bold", true, ExpandMark.NONE); + tx.commit(); + } + + try (Transaction tx = createTx.apply(doc)) { + tx.unmark(text, "bold", 0, 5, ExpandMark.BOTH); + tx.commit(); + } + + List marks = doc.marks(text); + Assertions.assertEquals(marks.size(), 0); + } + + void testMarkNull(Function createTx) { + Document doc = new Document(); + ObjectId text; + try (Transaction tx = createTx.apply(doc)) { + text = tx.set(ObjectId.ROOT, "text", ObjectType.TEXT); + tx.spliceText(text, 0, 0, "Hello"); + tx.mark(text, 0, 5, "comment", "1234", ExpandMark.NONE); + tx.commit(); + } + try (Transaction tx = createTx.apply(doc)) { + tx.markNull(text, 0, 2, "comment", ExpandMark.BOTH); + tx.commit(); + } + + List marks = doc.marks(text); + Assertions.assertEquals(1, marks.size()); + Mark mark = marks.get(0); + assertMark(mark, 2, 5, "comment", value -> Assertions.assertEquals("1234", ((AmValue.Str) value).getValue())); + } + + @Test + void testMarksAtIndex() { + Document doc = new Document(); + ObjectId text; + try (Transaction tx = doc.startTransaction()) { + text = tx.set(ObjectId.ROOT, "text", ObjectType.TEXT); + tx.spliceText(text, 0, 0, "Hello"); + tx.mark(text, 0, 5, "bold", true, ExpandMark.NONE); + HashMap marks = tx.getMarksAtIndex(text, 1); + Assertions.assertEquals(1, marks.size()); + Assertions.assertEquals(true, ((AmValue.Bool) marks.get("bold")).getValue()); + tx.commit(); + } + + HashMap marks = doc.getMarksAtIndex(text, 1); + Assertions.assertEquals(1, marks.size()); + Assertions.assertEquals(true, ((AmValue.Bool) marks.get("bold")).getValue()); + } + + @Test + void testMarksAtIndexAtHeads() { + Document doc = new Document(); + ObjectId text; + try (Transaction tx = doc.startTransaction()) { + text = tx.set(ObjectId.ROOT, "text", ObjectType.TEXT); + tx.spliceText(text, 0, 0, "Hello"); + tx.mark(text, 0, 5, "bold", true, ExpandMark.NONE); + HashMap marks = tx.getMarksAtIndex(text, 1); + Assertions.assertEquals(1, marks.size()); + Assertions.assertEquals(true, ((AmValue.Bool) marks.get("bold")).getValue()); + tx.commit(); + } + + // Save the heads + ChangeHash[] heads = doc.getHeads(); + + try (Transaction tx = doc.startTransaction()) { + tx.markNull(text, 0, 5, "bold", ExpandMark.NONE); + HashMap marks = tx.getMarksAtIndex(text, 1, heads); + Assertions.assertEquals(1, marks.size()); + Assertions.assertEquals(true, ((AmValue.Bool) marks.get("bold")).getValue()); + tx.commit(); + } + + HashMap marks = doc.getMarksAtIndex(text, 1, heads); + Assertions.assertEquals(1, marks.size()); + Assertions.assertEquals(true, ((AmValue.Bool) marks.get("bold")).getValue()); + } + + void assertMark(Mark mark, long start, long end, String name, MarkValueAssertion assertion) { + Assertions.assertEquals(start, mark.getStart()); + Assertions.assertEquals(end, mark.getEnd()); + Assertions.assertEquals(name, mark.getName()); + assertion.assertMarkValue(mark.getValue()); + } + + private interface MarkValueAssertion { + void assertMarkValue(AmValue value); + } } diff --git a/lib/src/test/java/org/automerge/TestMerge.java b/lib/src/test/java/org/automerge/TestMerge.java index a9be6a8..350b78e 100644 --- a/lib/src/test/java/org/automerge/TestMerge.java +++ b/lib/src/test/java/org/automerge/TestMerge.java @@ -4,40 +4,40 @@ import org.junit.jupiter.api.Test; class TestMerge { - @Test - public void testMerge() { - Document doc1 = new Document(); - try (Transaction tx = doc1.startTransaction()) { - tx.set(ObjectId.ROOT, "key1", 1.23); - tx.commit(); - } - Document doc2 = new Document(); - try (Transaction tx = doc2.startTransaction()) { - tx.set(ObjectId.ROOT, "key2", 4.56); - tx.commit(); - } - doc1.merge(doc2); - Assertions.assertEquals(1.23, ((AmValue.F64) doc1.get(ObjectId.ROOT, "key1").get()).getValue()); - Assertions.assertEquals(4.56, ((AmValue.F64) doc1.get(ObjectId.ROOT, "key2").get()).getValue()); - } + @Test + public void testMerge() { + Document doc1 = new Document(); + try (Transaction tx = doc1.startTransaction()) { + tx.set(ObjectId.ROOT, "key1", 1.23); + tx.commit(); + } + Document doc2 = new Document(); + try (Transaction tx = doc2.startTransaction()) { + tx.set(ObjectId.ROOT, "key2", 4.56); + tx.commit(); + } + doc1.merge(doc2); + Assertions.assertEquals(1.23, ((AmValue.F64) doc1.get(ObjectId.ROOT, "key1").get()).getValue()); + Assertions.assertEquals(4.56, ((AmValue.F64) doc1.get(ObjectId.ROOT, "key2").get()).getValue()); + } - @Test - public void testMergeThrowsIfTransactionInProgress() { - Document doc1 = new Document(); - doc1.startTransaction(); - Document doc2 = new Document(); - Assertions.assertThrows(TransactionInProgress.class, () -> { - doc1.merge(doc2); - }); - } + @Test + public void testMergeThrowsIfTransactionInProgress() { + Document doc1 = new Document(); + doc1.startTransaction(); + Document doc2 = new Document(); + Assertions.assertThrows(TransactionInProgress.class, () -> { + doc1.merge(doc2); + }); + } - @Test - public void testMergeThrowsIfOtherTransactionInProgress() { - Document doc1 = new Document(); - Document doc2 = new Document(); - doc2.startTransaction(); - Assertions.assertThrows(TransactionInProgress.class, () -> { - doc1.merge(doc2); - }); - } + @Test + public void testMergeThrowsIfOtherTransactionInProgress() { + Document doc1 = new Document(); + Document doc2 = new Document(); + doc2.startTransaction(); + Assertions.assertThrows(TransactionInProgress.class, () -> { + doc1.merge(doc2); + }); + } } diff --git a/lib/src/test/java/org/automerge/TestObjectId.java b/lib/src/test/java/org/automerge/TestObjectId.java index 5d18be3..783fc90 100644 --- a/lib/src/test/java/org/automerge/TestObjectId.java +++ b/lib/src/test/java/org/automerge/TestObjectId.java @@ -5,13 +5,13 @@ public final class TestObjectId { - public TestObjectId() { - super(); - } + public TestObjectId() { + super(); + } - @Test - public final void rootObj() { - ObjectId root = ObjectId.ROOT; - Assertions.assertTrue(root.isRoot()); - } + @Test + public final void rootObj() { + ObjectId root = ObjectId.ROOT; + Assertions.assertTrue(root.isRoot()); + } } diff --git a/lib/src/test/java/org/automerge/TestPatches.java b/lib/src/test/java/org/automerge/TestPatches.java index 75f938f..44d6b18 100644 --- a/lib/src/test/java/org/automerge/TestPatches.java +++ b/lib/src/test/java/org/automerge/TestPatches.java @@ -8,514 +8,514 @@ class TestPatches { - @Test - public void testSetInMap() { - Document doc = new Document(); - PatchLog patchlog = new PatchLog(); - Transaction tx = doc.startTransaction(patchlog); - tx.set(ObjectId.ROOT, "uint", NewValue.uint(0)); - tx.set(ObjectId.ROOT, "int", 1); - tx.set(ObjectId.ROOT, "float", 2.0); - tx.set(ObjectId.ROOT, "string", "string"); - tx.set(ObjectId.ROOT, "bytes", "bytes".getBytes()); - tx.set(ObjectId.ROOT, "bool", true); - tx.set(ObjectId.ROOT, "null", NewValue.NULL); - tx.set(ObjectId.ROOT, "counter", new Counter(3)); - Date now = new Date(); - tx.set(ObjectId.ROOT, "timestamp", now); - ObjectId list = tx.set(ObjectId.ROOT, "list", ObjectType.LIST); - ObjectId map = tx.set(ObjectId.ROOT, "map", ObjectType.MAP); - ObjectId text = tx.set(ObjectId.ROOT, "text", ObjectType.TEXT); - - tx.commit(); - List patches = doc.makePatches(patchlog); - - assertPutMap(patches.get(0), ObjectId.ROOT, emptyPath(), "uint", - (AmValue value) -> Assertions.assertEquals(((AmValue.UInt) value).getValue(), 0)); - - assertPutMap(patches.get(1), ObjectId.ROOT, emptyPath(), "int", (AmValue value) -> { - Assertions.assertEquals(((AmValue.Int) value).getValue(), 1); - }); - - assertPutMap(patches.get(2), ObjectId.ROOT, emptyPath(), "float", (AmValue value) -> { - Assertions.assertEquals(((AmValue.F64) value).getValue(), 2.0); - }); - - assertPutMap(patches.get(3), ObjectId.ROOT, emptyPath(), "string", (AmValue value) -> { - Assertions.assertEquals(((AmValue.Str) value).getValue(), "string"); - }); - - assertPutMap(patches.get(4), ObjectId.ROOT, emptyPath(), "bytes", (AmValue value) -> { - Assertions.assertArrayEquals(((AmValue.Bytes) value).getValue(), "bytes".getBytes()); - }); - - assertPutMap(patches.get(5), ObjectId.ROOT, emptyPath(), "bool", (AmValue value) -> { - Assertions.assertEquals(((AmValue.Bool) value).getValue(), true); - }); - - assertPutMap(patches.get(6), ObjectId.ROOT, emptyPath(), "null", (AmValue value) -> { - Assertions.assertInstanceOf(AmValue.Null.class, value); - }); - - assertPutMap(patches.get(7), ObjectId.ROOT, emptyPath(), "counter", (AmValue value) -> { - Assertions.assertEquals(((AmValue.Counter) value).getValue(), 3); - }); - - assertPutMap(patches.get(8), ObjectId.ROOT, emptyPath(), "timestamp", (AmValue value) -> { - Assertions.assertEquals(((AmValue.Timestamp) value).getValue(), now); - }); - - assertPutMap(patches.get(9), ObjectId.ROOT, emptyPath(), "list", (AmValue value) -> { - Assertions.assertEquals(((AmValue.List) value).getId(), list); - }); - - assertPutMap(patches.get(10), ObjectId.ROOT, emptyPath(), "map", (AmValue value) -> { - Assertions.assertEquals(((AmValue.Map) value).getId(), map); - }); - - assertPutMap(patches.get(11), ObjectId.ROOT, emptyPath(), "text", (AmValue value) -> { - Assertions.assertEquals(((AmValue.Text) value).getId(), text); - }); - patchlog.free(); - } - - @Test - public void testSetInList() { - Document doc = new Document(); - ObjectId list; - try (Transaction tx = doc.startTransaction()) { - list = tx.set(ObjectId.ROOT, "list", ObjectType.LIST); - for (int i = 0; i < 12; i++) { - tx.insert(list, i, NewValue.NULL); - } - tx.commit(); - } - PatchLog patchLog = new PatchLog(); - Transaction tx = doc.startTransaction(patchLog); - tx.set(list, 0, NewValue.uint(0)); - - tx.set(list, 1, 1); - tx.set(list, 2, 2.0); - tx.set(list, 3, "string"); - tx.set(list, 4, "bytes".getBytes()); - tx.set(list, 5, true); - - // Have to set index 6 to non-null otherwise the setNull is a noop and so no - // observer method is called - tx.set(list, 6, 4); - tx.set(list, 6, NewValue.NULL); - - tx.set(list, 7, new Counter(3)); - Date now = new Date(); - tx.set(list, 8, now); - ObjectId innerList = tx.set(list, 9, ObjectType.LIST); - ObjectId map = tx.set(list, 10, ObjectType.MAP); - ObjectId text = tx.set(list, 11, ObjectType.TEXT); - - tx.commit(); - List patches = doc.makePatches(patchLog); - - assertSetInList(patches.get(0), list, PathBuilder.root("list").build(), 0, (AmValue value) -> { - Assertions.assertEquals(((AmValue.UInt) value).getValue(), 0); - }); - - assertSetInList(patches.get(1), list, PathBuilder.root("list").build(), 1, (AmValue value) -> { - Assertions.assertEquals(((AmValue.Int) value).getValue(), 1); - }); - - assertSetInList(patches.get(2), list, PathBuilder.root("list").build(), 2, (AmValue value) -> { - Assertions.assertEquals(((AmValue.F64) value).getValue(), 2.0); - }); - - assertSetInList(patches.get(3), list, PathBuilder.root("list").build(), 3, (AmValue value) -> { - Assertions.assertEquals(((AmValue.Str) value).getValue(), "string"); - }); - - assertSetInList(patches.get(4), list, PathBuilder.root("list").build(), 4, (AmValue value) -> { - Assertions.assertArrayEquals(((AmValue.Bytes) value).getValue(), "bytes".getBytes()); - }); - - assertSetInList(patches.get(5), list, PathBuilder.root("list").build(), 5, (AmValue value) -> { - Assertions.assertEquals(((AmValue.Bool) value).getValue(), true); - }); - - // Note we skip an index here due to the patch which sets the 6th index to - // non-null - assertSetInList(patches.get(7), list, PathBuilder.root("list").build(), 6, (AmValue value) -> { - Assertions.assertInstanceOf(AmValue.Null.class, value); - }); - - assertSetInList(patches.get(8), list, PathBuilder.root("list").build(), 7, (AmValue value) -> { - Assertions.assertEquals(((AmValue.Counter) value).getValue(), 3); - }); - - assertSetInList(patches.get(9), list, PathBuilder.root("list").build(), 8, (AmValue value) -> { - Assertions.assertEquals(((AmValue.Timestamp) value).getValue(), now); - }); - - assertSetInList(patches.get(10), list, PathBuilder.root("list").build(), 9, (AmValue value) -> { - Assertions.assertEquals(((AmValue.List) value).getId(), innerList); - }); - - assertSetInList(patches.get(11), list, PathBuilder.root("list").build(), 10, (AmValue value) -> { - Assertions.assertEquals(((AmValue.Map) value).getId(), map); - }); - - assertSetInList(patches.get(12), list, PathBuilder.root("list").build(), 11, (AmValue value) -> { - Assertions.assertEquals(((AmValue.Text) value).getId(), text); - }); - } - - @Test - public void testInsertInList() { - Document doc = new Document(); - ObjectId list; - try (Transaction tx = doc.startTransaction()) { - list = tx.set(ObjectId.ROOT, "list", ObjectType.LIST); - tx.commit(); - } - PatchLog patchLog = new PatchLog(); - Transaction tx = doc.startTransaction(patchLog); - - tx.insert(list, 0, NewValue.uint(0)); - tx.insert(list, 1, 1); - tx.insert(list, 2, 2.0); - tx.insert(list, 3, "string"); - tx.insert(list, 4, "bytes".getBytes()); - tx.insert(list, 5, true); - tx.insert(list, 6, NewValue.NULL); - tx.insert(list, 7, new Counter(3)); - Date now = new Date(); - tx.insert(list, 8, now); - ObjectId innerList = tx.insert(list, 9, ObjectType.LIST); - ObjectId map = tx.insert(list, 10, ObjectType.MAP); - ObjectId text = tx.insert(list, 11, ObjectType.TEXT); - - tx.commit(); - List patches = doc.makePatches(patchLog); - Assertions.assertEquals(patches.size(), 1); - - Patch patch = patches.get(0); - Assertions.assertEquals(patch.getObj(), list); - // Assertions.assertEquals(patch.getPath(), path(new PathElement(ObjectId.ROOT, - // new - // Prop("list"))); - Assertions.assertEquals(patch.getPath(), PathBuilder.root("list").build()); - ArrayList values = ((PatchAction.Insert) patch.getAction()).getValues(); - - Assertions.assertEquals(((AmValue.UInt) values.get(0)).getValue(), 0); - Assertions.assertEquals(((AmValue.Int) values.get(1)).getValue(), 1); - Assertions.assertEquals(((AmValue.F64) values.get(2)).getValue(), 2.0); - Assertions.assertEquals(((AmValue.Str) values.get(3)).getValue(), "string"); - Assertions.assertArrayEquals(((AmValue.Bytes) values.get(4)).getValue(), "bytes".getBytes()); - Assertions.assertEquals(((AmValue.Bool) values.get(5)).getValue(), true); - Assertions.assertInstanceOf(AmValue.Null.class, values.get(6)); - Assertions.assertEquals(((AmValue.Counter) values.get(7)).getValue(), 3); - Assertions.assertEquals(((AmValue.Timestamp) values.get(8)).getValue(), now); - Assertions.assertEquals(((AmValue.List) values.get(9)).getId(), innerList); - Assertions.assertEquals(((AmValue.Map) values.get(10)).getId(), map); - Assertions.assertEquals(((AmValue.Text) values.get(11)).getId(), text); - } - - @Test - public void testSpliceText() { - Document doc = new Document(); - ObjectId text; - try (Transaction tx = doc.startTransaction()) { - text = tx.set(ObjectId.ROOT, "text", ObjectType.TEXT); - tx.spliceText(text, 0, 0, "Hello"); - tx.commit(); - } - - PatchLog patchLog = new PatchLog(); - Transaction tx = doc.startTransaction(patchLog); - tx.spliceText(text, 5, 0, " world"); - - tx.commit(); - List patches = doc.makePatches(patchLog); - - Assertions.assertEquals(patches.size(), 1); - Patch patch = patches.get(0); - - Assertions.assertEquals(patch.getObj(), text); - Assertions.assertEquals(patch.getPath(), PathBuilder.root("text").build()); - - PatchAction.SpliceText action = (PatchAction.SpliceText) patch.getAction(); - Assertions.assertEquals(action.getIndex(), 5); - Assertions.assertEquals(action.getText(), " world"); - } - - @Test - public void testConflictedPutInMap() { - Document doc1 = new Document("bbbb".getBytes()); - Document doc2 = new Document("aaaa".getBytes()); - try (Transaction tx = doc1.startTransaction()) { - tx.set(ObjectId.ROOT, "key", "value_1"); - tx.commit(); - } - try (Transaction tx = doc2.startTransaction()) { - tx.set(ObjectId.ROOT, "key", "value_2"); - tx.commit(); - } - PatchLog patchLog = new PatchLog(); - doc2.merge(doc1, patchLog); - List patches = doc2.makePatches(patchLog); - - Assertions.assertEquals(patches.size(), 1); - Patch patch = patches.get(0); - Assertions.assertEquals(patch.getObj(), ObjectId.ROOT); - Assertions.assertEquals(patch.getPath(), emptyPath()); - - PatchAction.PutMap action = (PatchAction.PutMap) patch.getAction(); - Assertions.assertTrue(action.isConflict()); - } - - @Test - public void testFlagConflictMap() { - Document doc1 = new Document("bbbb".getBytes()); - Document doc2 = new Document("aaaa".getBytes()); - try (Transaction tx = doc1.startTransaction()) { - tx.set(ObjectId.ROOT, "key", "value_1"); - tx.commit(); - } - try (Transaction tx = doc2.startTransaction()) { - tx.set(ObjectId.ROOT, "key", "value_2"); - tx.commit(); - } - PatchLog patchLog = new PatchLog(); - doc1.merge(doc2, patchLog); - List patches = doc1.makePatches(patchLog); - - Assertions.assertEquals(patches.size(), 1); - Patch patch = patches.get(0); - Assertions.assertEquals(patch.getObj(), ObjectId.ROOT); - Assertions.assertEquals(patch.getPath(), emptyPath()); - - PatchAction.FlagConflict action = (PatchAction.FlagConflict) patch.getAction(); - } - - @Test - public void testConflictedPutInList() { - Document doc1 = new Document("bbbb".getBytes()); - ObjectId list; - try (Transaction tx = doc1.startTransaction()) { - list = tx.set(ObjectId.ROOT, "list", ObjectType.LIST); - tx.insert(list, 0, NewValue.NULL); - tx.commit(); - } - - Document doc2 = doc1.fork("aaaa".getBytes()); - try (Transaction tx = doc2.startTransaction()) { - tx.set(list, 0, "value_2"); - tx.commit(); - } - try (Transaction tx = doc1.startTransaction()) { - tx.set(list, 0, "value_1"); - tx.commit(); - } - - PatchLog patchLog = new PatchLog(); - doc2.merge(doc1, patchLog); - List patches = doc2.makePatches(patchLog); - - Assertions.assertEquals(patches.size(), 1); - Patch patch = patches.get(0); - Assertions.assertEquals(patch.getObj(), list); - Assertions.assertEquals(patch.getPath(), PathBuilder.root("list").build()); - - PatchAction.PutList action = (PatchAction.PutList) patch.getAction(); - Assertions.assertTrue(action.isConflict()); - } - - @Test - public void testFlagConflictList() { - Document doc1 = new Document("bbbb".getBytes()); - ObjectId list; - try (Transaction tx = doc1.startTransaction()) { - list = tx.set(ObjectId.ROOT, "list", ObjectType.LIST); - tx.insert(list, 0, NewValue.NULL); - tx.commit(); - } - - Document doc2 = doc1.fork("aaaa".getBytes()); - try (Transaction tx = doc1.startTransaction()) { - tx.set(list, 0, "value_2"); - tx.commit(); - } - try (Transaction tx = doc2.startTransaction()) { - tx.set(list, 0, "value_1"); - tx.commit(); - } - - PatchLog patchLog = new PatchLog(); - doc1.merge(doc2, patchLog); - List patches = doc1.makePatches(patchLog); - - Assertions.assertEquals(patches.size(), 1); - Patch patch = patches.get(0); - Assertions.assertEquals(patch.getObj(), list); - Assertions.assertEquals(patch.getPath(), PathBuilder.root("list").build()); - - PatchAction.FlagConflict action = (PatchAction.FlagConflict) patch.getAction(); - } - - @Test - public void testIncrementInMap() { - Document doc = new Document(); - try (Transaction tx = doc.startTransaction()) { - tx.set(ObjectId.ROOT, "counter", new Counter(0)); - tx.commit(); - } - - PatchLog patchLog = new PatchLog(); - try (Transaction tx = doc.startTransaction(patchLog)) { - tx.increment(ObjectId.ROOT, "counter", 5); - tx.commit(); - } - List patches = doc.makePatches(patchLog); - - Assertions.assertEquals(patches.size(), 1); - Patch patch = patches.get(0); - - Assertions.assertEquals(patch.getObj(), ObjectId.ROOT); - Assertions.assertEquals(patch.getPath(), emptyPath()); - Assertions.assertEquals(((PatchAction.Increment) patch.getAction()).getValue(), 5); - } - - @Test - public void testIncrementInList() { - Document doc = new Document(); - ObjectId list; - try (Transaction tx = doc.startTransaction()) { - list = tx.set(ObjectId.ROOT, "list", ObjectType.LIST); - tx.insert(list, 0, new Counter(10)); - tx.commit(); - } - - PatchLog patchLog = new PatchLog(); - try (Transaction tx = doc.startTransaction(patchLog)) { - tx.increment(list, 0, 5); - tx.commit(); - } - List patches = doc.makePatches(patchLog); - - Assertions.assertEquals(patches.size(), 1); - Patch patch = patches.get(0); - - Assertions.assertEquals(patch.getObj(), list); - Assertions.assertEquals(patch.getPath(), PathBuilder.root("list").build()); - Assertions.assertEquals(((PatchAction.Increment) patch.getAction()).getValue(), 5); - } - - @Test - public void testDeleteInMap() { - Document doc = new Document(); - try (Transaction tx = doc.startTransaction()) { - tx.set(ObjectId.ROOT, "key", "value"); - tx.commit(); - } - - PatchLog patchLog = new PatchLog(); - try (Transaction tx = doc.startTransaction(patchLog)) { - tx.delete(ObjectId.ROOT, "key"); - tx.commit(); - } - - List patches = doc.makePatches(patchLog); - - Assertions.assertEquals(patches.size(), 1); - Patch patch = patches.get(0); - Assertions.assertEquals(patch.getObj(), ObjectId.ROOT); - Assertions.assertEquals(patch.getPath(), emptyPath()); - PatchAction.DeleteMap action = (PatchAction.DeleteMap) patch.getAction(); - Assertions.assertEquals(action.getKey(), "key"); - } - - @Test - public void testDeleteInList() { - Document doc = new Document(); - ObjectId list; - try (Transaction tx = doc.startTransaction()) { - list = tx.set(ObjectId.ROOT, "list", ObjectType.LIST); - tx.insert(list, 0, "value"); - tx.insert(list, 1, "value2"); - tx.commit(); - } - - PatchLog patchLog = new PatchLog(); - try (Transaction tx = doc.startTransaction(patchLog)) { - tx.delete(list, 0); - tx.delete(list, 0); - tx.commit(); - } - List patches = doc.makePatches(patchLog); - - Assertions.assertEquals(patches.size(), 1); - Patch patch1 = patches.get(0); - Assertions.assertEquals(patch1.getObj(), list); - Assertions.assertEquals(patch1.getPath(), PathBuilder.root("list").build()); - PatchAction.DeleteList action1 = (PatchAction.DeleteList) patch1.getAction(); - Assertions.assertEquals(action1.getIndex(), 0); - Assertions.assertEquals(action1.getLength(), 2); - } - - @Test - public void testApplyEncodedChangesForPatches() { - Document doc = new Document(); - try (Transaction tx = doc.startTransaction()) { - tx.set(ObjectId.ROOT, "key", "value"); - tx.commit(); - } - byte[] changes = doc.encodeChangesSince(new ChangeHash[]{}); - Document doc2 = new Document(); - PatchLog patchLog = new PatchLog(); - doc2.applyEncodedChanges(changes, patchLog); - - List patches = doc2.makePatches(patchLog); - - Assertions.assertEquals(patches.size(), 1); - Patch patch = patches.get(0); - - Assertions.assertEquals(patch.getObj(), ObjectId.ROOT); - Assertions.assertEquals(patch.getPath(), emptyPath()); - - PatchAction.PutMap action = (PatchAction.PutMap) patch.getAction(); - Assertions.assertEquals(action.getKey(), "key"); - } - - public ArrayList emptyPath() { - return PathBuilder.empty(); - } - - void assertPutMap(Patch patch, ObjectId expectedObj, ArrayList expectedPath, String expectedKey, - ValueAssertion value) { - Assertions.assertEquals(patch.getPath(), expectedPath); - Assertions.assertEquals(patch.getObj(), expectedObj); - Assertions.assertEquals(((PatchAction.PutMap) patch.getAction()).getKey(), expectedKey); - value.assertValue(((PatchAction.PutMap) patch.getAction()).getValue()); - } - - void assertSetInList(Patch patch, ObjectId expectedObj, ArrayList expectedPath, long expectedIndex, - ValueAssertion value) { - Assertions.assertEquals(patch.getPath(), expectedPath); - Assertions.assertEquals(patch.getObj(), expectedObj); - Assertions.assertEquals(((PatchAction.PutList) patch.getAction()).getIndex(), expectedIndex); - value.assertValue(((PatchAction.PutList) patch.getAction()).getValue()); - } - - void assertInsertInList(Patch patch, ObjectId expectedObj, ArrayList expectedPath, long expectedIndex, - ValuesAssertion value) { - Assertions.assertEquals(patch.getPath(), expectedPath); - Assertions.assertEquals(patch.getObj(), expectedObj); - Assertions.assertEquals(((PatchAction.Insert) patch.getAction()).getIndex(), expectedIndex); - value.assertValues(((PatchAction.Insert) patch.getAction()).getValues()); - } - - public interface ValueAssertion { - void assertValue(AmValue value); - } - - public interface ValuesAssertion { - void assertValues(ArrayList values); - } + @Test + public void testSetInMap() { + Document doc = new Document(); + PatchLog patchlog = new PatchLog(); + Transaction tx = doc.startTransaction(patchlog); + tx.set(ObjectId.ROOT, "uint", NewValue.uint(0)); + tx.set(ObjectId.ROOT, "int", 1); + tx.set(ObjectId.ROOT, "float", 2.0); + tx.set(ObjectId.ROOT, "string", "string"); + tx.set(ObjectId.ROOT, "bytes", "bytes".getBytes()); + tx.set(ObjectId.ROOT, "bool", true); + tx.set(ObjectId.ROOT, "null", NewValue.NULL); + tx.set(ObjectId.ROOT, "counter", new Counter(3)); + Date now = new Date(); + tx.set(ObjectId.ROOT, "timestamp", now); + ObjectId list = tx.set(ObjectId.ROOT, "list", ObjectType.LIST); + ObjectId map = tx.set(ObjectId.ROOT, "map", ObjectType.MAP); + ObjectId text = tx.set(ObjectId.ROOT, "text", ObjectType.TEXT); + + tx.commit(); + List patches = doc.makePatches(patchlog); + + assertPutMap(patches.get(0), ObjectId.ROOT, emptyPath(), "uint", + (AmValue value) -> Assertions.assertEquals(((AmValue.UInt) value).getValue(), 0)); + + assertPutMap(patches.get(1), ObjectId.ROOT, emptyPath(), "int", (AmValue value) -> { + Assertions.assertEquals(((AmValue.Int) value).getValue(), 1); + }); + + assertPutMap(patches.get(2), ObjectId.ROOT, emptyPath(), "float", (AmValue value) -> { + Assertions.assertEquals(((AmValue.F64) value).getValue(), 2.0); + }); + + assertPutMap(patches.get(3), ObjectId.ROOT, emptyPath(), "string", (AmValue value) -> { + Assertions.assertEquals(((AmValue.Str) value).getValue(), "string"); + }); + + assertPutMap(patches.get(4), ObjectId.ROOT, emptyPath(), "bytes", (AmValue value) -> { + Assertions.assertArrayEquals(((AmValue.Bytes) value).getValue(), "bytes".getBytes()); + }); + + assertPutMap(patches.get(5), ObjectId.ROOT, emptyPath(), "bool", (AmValue value) -> { + Assertions.assertEquals(((AmValue.Bool) value).getValue(), true); + }); + + assertPutMap(patches.get(6), ObjectId.ROOT, emptyPath(), "null", (AmValue value) -> { + Assertions.assertInstanceOf(AmValue.Null.class, value); + }); + + assertPutMap(patches.get(7), ObjectId.ROOT, emptyPath(), "counter", (AmValue value) -> { + Assertions.assertEquals(((AmValue.Counter) value).getValue(), 3); + }); + + assertPutMap(patches.get(8), ObjectId.ROOT, emptyPath(), "timestamp", (AmValue value) -> { + Assertions.assertEquals(((AmValue.Timestamp) value).getValue(), now); + }); + + assertPutMap(patches.get(9), ObjectId.ROOT, emptyPath(), "list", (AmValue value) -> { + Assertions.assertEquals(((AmValue.List) value).getId(), list); + }); + + assertPutMap(patches.get(10), ObjectId.ROOT, emptyPath(), "map", (AmValue value) -> { + Assertions.assertEquals(((AmValue.Map) value).getId(), map); + }); + + assertPutMap(patches.get(11), ObjectId.ROOT, emptyPath(), "text", (AmValue value) -> { + Assertions.assertEquals(((AmValue.Text) value).getId(), text); + }); + patchlog.free(); + } + + @Test + public void testSetInList() { + Document doc = new Document(); + ObjectId list; + try (Transaction tx = doc.startTransaction()) { + list = tx.set(ObjectId.ROOT, "list", ObjectType.LIST); + for (int i = 0; i < 12; i++) { + tx.insert(list, i, NewValue.NULL); + } + tx.commit(); + } + PatchLog patchLog = new PatchLog(); + Transaction tx = doc.startTransaction(patchLog); + tx.set(list, 0, NewValue.uint(0)); + + tx.set(list, 1, 1); + tx.set(list, 2, 2.0); + tx.set(list, 3, "string"); + tx.set(list, 4, "bytes".getBytes()); + tx.set(list, 5, true); + + // Have to set index 6 to non-null otherwise the setNull is a noop and so no + // observer method is called + tx.set(list, 6, 4); + tx.set(list, 6, NewValue.NULL); + + tx.set(list, 7, new Counter(3)); + Date now = new Date(); + tx.set(list, 8, now); + ObjectId innerList = tx.set(list, 9, ObjectType.LIST); + ObjectId map = tx.set(list, 10, ObjectType.MAP); + ObjectId text = tx.set(list, 11, ObjectType.TEXT); + + tx.commit(); + List patches = doc.makePatches(patchLog); + + assertSetInList(patches.get(0), list, PathBuilder.root("list").build(), 0, (AmValue value) -> { + Assertions.assertEquals(((AmValue.UInt) value).getValue(), 0); + }); + + assertSetInList(patches.get(1), list, PathBuilder.root("list").build(), 1, (AmValue value) -> { + Assertions.assertEquals(((AmValue.Int) value).getValue(), 1); + }); + + assertSetInList(patches.get(2), list, PathBuilder.root("list").build(), 2, (AmValue value) -> { + Assertions.assertEquals(((AmValue.F64) value).getValue(), 2.0); + }); + + assertSetInList(patches.get(3), list, PathBuilder.root("list").build(), 3, (AmValue value) -> { + Assertions.assertEquals(((AmValue.Str) value).getValue(), "string"); + }); + + assertSetInList(patches.get(4), list, PathBuilder.root("list").build(), 4, (AmValue value) -> { + Assertions.assertArrayEquals(((AmValue.Bytes) value).getValue(), "bytes".getBytes()); + }); + + assertSetInList(patches.get(5), list, PathBuilder.root("list").build(), 5, (AmValue value) -> { + Assertions.assertEquals(((AmValue.Bool) value).getValue(), true); + }); + + // Note we skip an index here due to the patch which sets the 6th index to + // non-null + assertSetInList(patches.get(7), list, PathBuilder.root("list").build(), 6, (AmValue value) -> { + Assertions.assertInstanceOf(AmValue.Null.class, value); + }); + + assertSetInList(patches.get(8), list, PathBuilder.root("list").build(), 7, (AmValue value) -> { + Assertions.assertEquals(((AmValue.Counter) value).getValue(), 3); + }); + + assertSetInList(patches.get(9), list, PathBuilder.root("list").build(), 8, (AmValue value) -> { + Assertions.assertEquals(((AmValue.Timestamp) value).getValue(), now); + }); + + assertSetInList(patches.get(10), list, PathBuilder.root("list").build(), 9, (AmValue value) -> { + Assertions.assertEquals(((AmValue.List) value).getId(), innerList); + }); + + assertSetInList(patches.get(11), list, PathBuilder.root("list").build(), 10, (AmValue value) -> { + Assertions.assertEquals(((AmValue.Map) value).getId(), map); + }); + + assertSetInList(patches.get(12), list, PathBuilder.root("list").build(), 11, (AmValue value) -> { + Assertions.assertEquals(((AmValue.Text) value).getId(), text); + }); + } + + @Test + public void testInsertInList() { + Document doc = new Document(); + ObjectId list; + try (Transaction tx = doc.startTransaction()) { + list = tx.set(ObjectId.ROOT, "list", ObjectType.LIST); + tx.commit(); + } + PatchLog patchLog = new PatchLog(); + Transaction tx = doc.startTransaction(patchLog); + + tx.insert(list, 0, NewValue.uint(0)); + tx.insert(list, 1, 1); + tx.insert(list, 2, 2.0); + tx.insert(list, 3, "string"); + tx.insert(list, 4, "bytes".getBytes()); + tx.insert(list, 5, true); + tx.insert(list, 6, NewValue.NULL); + tx.insert(list, 7, new Counter(3)); + Date now = new Date(); + tx.insert(list, 8, now); + ObjectId innerList = tx.insert(list, 9, ObjectType.LIST); + ObjectId map = tx.insert(list, 10, ObjectType.MAP); + ObjectId text = tx.insert(list, 11, ObjectType.TEXT); + + tx.commit(); + List patches = doc.makePatches(patchLog); + Assertions.assertEquals(patches.size(), 1); + + Patch patch = patches.get(0); + Assertions.assertEquals(patch.getObj(), list); + // Assertions.assertEquals(patch.getPath(), path(new PathElement(ObjectId.ROOT, + // new + // Prop("list"))); + Assertions.assertEquals(patch.getPath(), PathBuilder.root("list").build()); + ArrayList values = ((PatchAction.Insert) patch.getAction()).getValues(); + + Assertions.assertEquals(((AmValue.UInt) values.get(0)).getValue(), 0); + Assertions.assertEquals(((AmValue.Int) values.get(1)).getValue(), 1); + Assertions.assertEquals(((AmValue.F64) values.get(2)).getValue(), 2.0); + Assertions.assertEquals(((AmValue.Str) values.get(3)).getValue(), "string"); + Assertions.assertArrayEquals(((AmValue.Bytes) values.get(4)).getValue(), "bytes".getBytes()); + Assertions.assertEquals(((AmValue.Bool) values.get(5)).getValue(), true); + Assertions.assertInstanceOf(AmValue.Null.class, values.get(6)); + Assertions.assertEquals(((AmValue.Counter) values.get(7)).getValue(), 3); + Assertions.assertEquals(((AmValue.Timestamp) values.get(8)).getValue(), now); + Assertions.assertEquals(((AmValue.List) values.get(9)).getId(), innerList); + Assertions.assertEquals(((AmValue.Map) values.get(10)).getId(), map); + Assertions.assertEquals(((AmValue.Text) values.get(11)).getId(), text); + } + + @Test + public void testSpliceText() { + Document doc = new Document(); + ObjectId text; + try (Transaction tx = doc.startTransaction()) { + text = tx.set(ObjectId.ROOT, "text", ObjectType.TEXT); + tx.spliceText(text, 0, 0, "Hello"); + tx.commit(); + } + + PatchLog patchLog = new PatchLog(); + Transaction tx = doc.startTransaction(patchLog); + tx.spliceText(text, 5, 0, " world"); + + tx.commit(); + List patches = doc.makePatches(patchLog); + + Assertions.assertEquals(patches.size(), 1); + Patch patch = patches.get(0); + + Assertions.assertEquals(patch.getObj(), text); + Assertions.assertEquals(patch.getPath(), PathBuilder.root("text").build()); + + PatchAction.SpliceText action = (PatchAction.SpliceText) patch.getAction(); + Assertions.assertEquals(action.getIndex(), 5); + Assertions.assertEquals(action.getText(), " world"); + } + + @Test + public void testConflictedPutInMap() { + Document doc1 = new Document("bbbb".getBytes()); + Document doc2 = new Document("aaaa".getBytes()); + try (Transaction tx = doc1.startTransaction()) { + tx.set(ObjectId.ROOT, "key", "value_1"); + tx.commit(); + } + try (Transaction tx = doc2.startTransaction()) { + tx.set(ObjectId.ROOT, "key", "value_2"); + tx.commit(); + } + PatchLog patchLog = new PatchLog(); + doc2.merge(doc1, patchLog); + List patches = doc2.makePatches(patchLog); + + Assertions.assertEquals(patches.size(), 1); + Patch patch = patches.get(0); + Assertions.assertEquals(patch.getObj(), ObjectId.ROOT); + Assertions.assertEquals(patch.getPath(), emptyPath()); + + PatchAction.PutMap action = (PatchAction.PutMap) patch.getAction(); + Assertions.assertTrue(action.isConflict()); + } + + @Test + public void testFlagConflictMap() { + Document doc1 = new Document("bbbb".getBytes()); + Document doc2 = new Document("aaaa".getBytes()); + try (Transaction tx = doc1.startTransaction()) { + tx.set(ObjectId.ROOT, "key", "value_1"); + tx.commit(); + } + try (Transaction tx = doc2.startTransaction()) { + tx.set(ObjectId.ROOT, "key", "value_2"); + tx.commit(); + } + PatchLog patchLog = new PatchLog(); + doc1.merge(doc2, patchLog); + List patches = doc1.makePatches(patchLog); + + Assertions.assertEquals(patches.size(), 1); + Patch patch = patches.get(0); + Assertions.assertEquals(patch.getObj(), ObjectId.ROOT); + Assertions.assertEquals(patch.getPath(), emptyPath()); + + PatchAction.FlagConflict action = (PatchAction.FlagConflict) patch.getAction(); + } + + @Test + public void testConflictedPutInList() { + Document doc1 = new Document("bbbb".getBytes()); + ObjectId list; + try (Transaction tx = doc1.startTransaction()) { + list = tx.set(ObjectId.ROOT, "list", ObjectType.LIST); + tx.insert(list, 0, NewValue.NULL); + tx.commit(); + } + + Document doc2 = doc1.fork("aaaa".getBytes()); + try (Transaction tx = doc2.startTransaction()) { + tx.set(list, 0, "value_2"); + tx.commit(); + } + try (Transaction tx = doc1.startTransaction()) { + tx.set(list, 0, "value_1"); + tx.commit(); + } + + PatchLog patchLog = new PatchLog(); + doc2.merge(doc1, patchLog); + List patches = doc2.makePatches(patchLog); + + Assertions.assertEquals(patches.size(), 1); + Patch patch = patches.get(0); + Assertions.assertEquals(patch.getObj(), list); + Assertions.assertEquals(patch.getPath(), PathBuilder.root("list").build()); + + PatchAction.PutList action = (PatchAction.PutList) patch.getAction(); + Assertions.assertTrue(action.isConflict()); + } + + @Test + public void testFlagConflictList() { + Document doc1 = new Document("bbbb".getBytes()); + ObjectId list; + try (Transaction tx = doc1.startTransaction()) { + list = tx.set(ObjectId.ROOT, "list", ObjectType.LIST); + tx.insert(list, 0, NewValue.NULL); + tx.commit(); + } + + Document doc2 = doc1.fork("aaaa".getBytes()); + try (Transaction tx = doc1.startTransaction()) { + tx.set(list, 0, "value_2"); + tx.commit(); + } + try (Transaction tx = doc2.startTransaction()) { + tx.set(list, 0, "value_1"); + tx.commit(); + } + + PatchLog patchLog = new PatchLog(); + doc1.merge(doc2, patchLog); + List patches = doc1.makePatches(patchLog); + + Assertions.assertEquals(patches.size(), 1); + Patch patch = patches.get(0); + Assertions.assertEquals(patch.getObj(), list); + Assertions.assertEquals(patch.getPath(), PathBuilder.root("list").build()); + + PatchAction.FlagConflict action = (PatchAction.FlagConflict) patch.getAction(); + } + + @Test + public void testIncrementInMap() { + Document doc = new Document(); + try (Transaction tx = doc.startTransaction()) { + tx.set(ObjectId.ROOT, "counter", new Counter(0)); + tx.commit(); + } + + PatchLog patchLog = new PatchLog(); + try (Transaction tx = doc.startTransaction(patchLog)) { + tx.increment(ObjectId.ROOT, "counter", 5); + tx.commit(); + } + List patches = doc.makePatches(patchLog); + + Assertions.assertEquals(patches.size(), 1); + Patch patch = patches.get(0); + + Assertions.assertEquals(patch.getObj(), ObjectId.ROOT); + Assertions.assertEquals(patch.getPath(), emptyPath()); + Assertions.assertEquals(((PatchAction.Increment) patch.getAction()).getValue(), 5); + } + + @Test + public void testIncrementInList() { + Document doc = new Document(); + ObjectId list; + try (Transaction tx = doc.startTransaction()) { + list = tx.set(ObjectId.ROOT, "list", ObjectType.LIST); + tx.insert(list, 0, new Counter(10)); + tx.commit(); + } + + PatchLog patchLog = new PatchLog(); + try (Transaction tx = doc.startTransaction(patchLog)) { + tx.increment(list, 0, 5); + tx.commit(); + } + List patches = doc.makePatches(patchLog); + + Assertions.assertEquals(patches.size(), 1); + Patch patch = patches.get(0); + + Assertions.assertEquals(patch.getObj(), list); + Assertions.assertEquals(patch.getPath(), PathBuilder.root("list").build()); + Assertions.assertEquals(((PatchAction.Increment) patch.getAction()).getValue(), 5); + } + + @Test + public void testDeleteInMap() { + Document doc = new Document(); + try (Transaction tx = doc.startTransaction()) { + tx.set(ObjectId.ROOT, "key", "value"); + tx.commit(); + } + + PatchLog patchLog = new PatchLog(); + try (Transaction tx = doc.startTransaction(patchLog)) { + tx.delete(ObjectId.ROOT, "key"); + tx.commit(); + } + + List patches = doc.makePatches(patchLog); + + Assertions.assertEquals(patches.size(), 1); + Patch patch = patches.get(0); + Assertions.assertEquals(patch.getObj(), ObjectId.ROOT); + Assertions.assertEquals(patch.getPath(), emptyPath()); + PatchAction.DeleteMap action = (PatchAction.DeleteMap) patch.getAction(); + Assertions.assertEquals(action.getKey(), "key"); + } + + @Test + public void testDeleteInList() { + Document doc = new Document(); + ObjectId list; + try (Transaction tx = doc.startTransaction()) { + list = tx.set(ObjectId.ROOT, "list", ObjectType.LIST); + tx.insert(list, 0, "value"); + tx.insert(list, 1, "value2"); + tx.commit(); + } + + PatchLog patchLog = new PatchLog(); + try (Transaction tx = doc.startTransaction(patchLog)) { + tx.delete(list, 0); + tx.delete(list, 0); + tx.commit(); + } + List patches = doc.makePatches(patchLog); + + Assertions.assertEquals(patches.size(), 1); + Patch patch1 = patches.get(0); + Assertions.assertEquals(patch1.getObj(), list); + Assertions.assertEquals(patch1.getPath(), PathBuilder.root("list").build()); + PatchAction.DeleteList action1 = (PatchAction.DeleteList) patch1.getAction(); + Assertions.assertEquals(action1.getIndex(), 0); + Assertions.assertEquals(action1.getLength(), 2); + } + + @Test + public void testApplyEncodedChangesForPatches() { + Document doc = new Document(); + try (Transaction tx = doc.startTransaction()) { + tx.set(ObjectId.ROOT, "key", "value"); + tx.commit(); + } + byte[] changes = doc.encodeChangesSince(new ChangeHash[]{}); + Document doc2 = new Document(); + PatchLog patchLog = new PatchLog(); + doc2.applyEncodedChanges(changes, patchLog); + + List patches = doc2.makePatches(patchLog); + + Assertions.assertEquals(patches.size(), 1); + Patch patch = patches.get(0); + + Assertions.assertEquals(patch.getObj(), ObjectId.ROOT); + Assertions.assertEquals(patch.getPath(), emptyPath()); + + PatchAction.PutMap action = (PatchAction.PutMap) patch.getAction(); + Assertions.assertEquals(action.getKey(), "key"); + } + + public ArrayList emptyPath() { + return PathBuilder.empty(); + } + + void assertPutMap(Patch patch, ObjectId expectedObj, ArrayList expectedPath, String expectedKey, + ValueAssertion value) { + Assertions.assertEquals(patch.getPath(), expectedPath); + Assertions.assertEquals(patch.getObj(), expectedObj); + Assertions.assertEquals(((PatchAction.PutMap) patch.getAction()).getKey(), expectedKey); + value.assertValue(((PatchAction.PutMap) patch.getAction()).getValue()); + } + + void assertSetInList(Patch patch, ObjectId expectedObj, ArrayList expectedPath, long expectedIndex, + ValueAssertion value) { + Assertions.assertEquals(patch.getPath(), expectedPath); + Assertions.assertEquals(patch.getObj(), expectedObj); + Assertions.assertEquals(((PatchAction.PutList) patch.getAction()).getIndex(), expectedIndex); + value.assertValue(((PatchAction.PutList) patch.getAction()).getValue()); + } + + void assertInsertInList(Patch patch, ObjectId expectedObj, ArrayList expectedPath, long expectedIndex, + ValuesAssertion value) { + Assertions.assertEquals(patch.getPath(), expectedPath); + Assertions.assertEquals(patch.getObj(), expectedObj); + Assertions.assertEquals(((PatchAction.Insert) patch.getAction()).getIndex(), expectedIndex); + value.assertValues(((PatchAction.Insert) patch.getAction()).getValues()); + } + + public interface ValueAssertion { + void assertValue(AmValue value); + } + + public interface ValuesAssertion { + void assertValues(ArrayList values); + } } diff --git a/lib/src/test/java/org/automerge/TestSetList.java b/lib/src/test/java/org/automerge/TestSetList.java index ef07a16..9e443e2 100644 --- a/lib/src/test/java/org/automerge/TestSetList.java +++ b/lib/src/test/java/org/automerge/TestSetList.java @@ -6,88 +6,88 @@ import org.junit.jupiter.api.Test; public final class TestSetList { - private Document doc; - private Transaction tx; - private ObjectId list; + private Document doc; + private Transaction tx; + private ObjectId list; - public TestSetList() { - super(); - } + public TestSetList() { + super(); + } - @BeforeEach - public void setup() { - doc = new Document(); - tx = doc.startTransaction(); - list = tx.set(ObjectId.ROOT, "list", ObjectType.LIST); - tx.insert(list, 0, "something"); - } + @BeforeEach + public void setup() { + doc = new Document(); + tx = doc.startTransaction(); + list = tx.set(ObjectId.ROOT, "list", ObjectType.LIST); + tx.insert(list, 0, "something"); + } - @Test - public void testSetDoubleInList() { - tx.set(list, 0, 1.23); - Assertions.assertEquals(1.23, ((AmValue.F64) doc.get(list, 0).get()).getValue()); - } + @Test + public void testSetDoubleInList() { + tx.set(list, 0, 1.23); + Assertions.assertEquals(1.23, ((AmValue.F64) doc.get(list, 0).get()).getValue()); + } - @Test - public void testSetIntInList() { - tx.set(list, 0, 123); - Assertions.assertEquals(123, ((AmValue.Int) doc.get(list, 0).get()).getValue()); - } + @Test + public void testSetIntInList() { + tx.set(list, 0, 123); + Assertions.assertEquals(123, ((AmValue.Int) doc.get(list, 0).get()).getValue()); + } - @Test - public void testSetUintInList() { - tx.set(list, 0, NewValue.uint(123)); - Assertions.assertEquals(123, ((AmValue.UInt) doc.get(list, 0).get()).getValue()); - Assertions.assertInstanceOf(AmValue.UInt.class, doc.get(list, 0).get()); - } + @Test + public void testSetUintInList() { + tx.set(list, 0, NewValue.uint(123)); + Assertions.assertEquals(123, ((AmValue.UInt) doc.get(list, 0).get()).getValue()); + Assertions.assertInstanceOf(AmValue.UInt.class, doc.get(list, 0).get()); + } - @Test - public void testSetStringInList() { - tx.set(list, 0, "hello"); - Assertions.assertEquals("hello", ((AmValue.Str) doc.get(list, 0).get()).getValue()); - } + @Test + public void testSetStringInList() { + tx.set(list, 0, "hello"); + Assertions.assertEquals("hello", ((AmValue.Str) doc.get(list, 0).get()).getValue()); + } - @Test - public void testSetBytesInList() { - byte[] value = "some bytes".getBytes(); - tx.set(list, 0, value); - Assertions.assertArrayEquals(value, ((AmValue.Bytes) doc.get(list, 0).get()).getValue()); - } + @Test + public void testSetBytesInList() { + byte[] value = "some bytes".getBytes(); + tx.set(list, 0, value); + Assertions.assertArrayEquals(value, ((AmValue.Bytes) doc.get(list, 0).get()).getValue()); + } - @Test - public void testSetBooleanInList() { - tx.set(list, 0, true); - Assertions.assertEquals(true, ((AmValue.Bool) doc.get(list, 0).get()).getValue()); - tx.set(list, 0, false); - Assertions.assertEquals(false, ((AmValue.Bool) doc.get(list, 0).get()).getValue()); - } + @Test + public void testSetBooleanInList() { + tx.set(list, 0, true); + Assertions.assertEquals(true, ((AmValue.Bool) doc.get(list, 0).get()).getValue()); + tx.set(list, 0, false); + Assertions.assertEquals(false, ((AmValue.Bool) doc.get(list, 0).get()).getValue()); + } - @Test - public void testSetDateInList() { - Date date = new Date(); - tx.set(list, 0, date); - Assertions.assertEquals(date, ((AmValue.Timestamp) doc.get(list, 0).get()).getValue()); - } + @Test + public void testSetDateInList() { + Date date = new Date(); + tx.set(list, 0, date); + Assertions.assertEquals(date, ((AmValue.Timestamp) doc.get(list, 0).get()).getValue()); + } - @Test - public void testSetCounterInList() { - tx.set(list, 0, new Counter(10)); - Assertions.assertEquals(10, ((AmValue.Counter) doc.get(list, 0).get()).getValue()); - } + @Test + public void testSetCounterInList() { + tx.set(list, 0, new Counter(10)); + Assertions.assertEquals(10, ((AmValue.Counter) doc.get(list, 0).get()).getValue()); + } - @Test - public void testSetNullInList() { - tx.set(list, 0, NewValue.NULL); - Assertions.assertInstanceOf(AmValue.Null.class, doc.get(list, 0).get()); - } + @Test + public void testSetNullInList() { + tx.set(list, 0, NewValue.NULL); + Assertions.assertInstanceOf(AmValue.Null.class, doc.get(list, 0).get()); + } - @Test - public void setObjectInList() { - ObjectId listId = tx.set(list, 0, ObjectType.LIST); - Assertions.assertEquals(listId, ((AmValue.List) doc.get(list, 0).get()).getId()); - ObjectId textId = tx.set(list, 0, ObjectType.TEXT); - Assertions.assertEquals(textId, ((AmValue.Text) doc.get(list, 0).get()).getId()); - ObjectId mapId = tx.set(list, 0, ObjectType.MAP); - Assertions.assertEquals(mapId, ((AmValue.Map) doc.get(list, 0).get()).getId()); - } + @Test + public void setObjectInList() { + ObjectId listId = tx.set(list, 0, ObjectType.LIST); + Assertions.assertEquals(listId, ((AmValue.List) doc.get(list, 0).get()).getId()); + ObjectId textId = tx.set(list, 0, ObjectType.TEXT); + Assertions.assertEquals(textId, ((AmValue.Text) doc.get(list, 0).get()).getId()); + ObjectId mapId = tx.set(list, 0, ObjectType.MAP); + Assertions.assertEquals(mapId, ((AmValue.Map) doc.get(list, 0).get()).getId()); + } } diff --git a/lib/src/test/java/org/automerge/TestSplice.java b/lib/src/test/java/org/automerge/TestSplice.java index 80876f9..9af4372 100644 --- a/lib/src/test/java/org/automerge/TestSplice.java +++ b/lib/src/test/java/org/automerge/TestSplice.java @@ -7,112 +7,113 @@ import org.junit.jupiter.api.Test; public final class TestSplice { - private Document doc; - private Transaction tx; - private ObjectId list; - - @FunctionalInterface - interface InsertedAssertions { - void assertInserted(Object elem1, Object elem2); - } - public TestSplice() { - super(); - } - - @BeforeEach - public void setup() { - doc = new Document(); - tx = doc.startTransaction(); - list = tx.set(ObjectId.ROOT, "list", ObjectType.LIST); - } - - void testSplice2(NewValue val1, NewValue val2, InsertedAssertions assertions) { - tx.insert(list, 0, 1); - tx.insert(list, 1, 2); - tx.insert(list, 2, 3); - NewValue[] toInsert = {val1, val2,}; - tx.splice(list, 1, 1, Arrays.asList(toInsert).iterator()); - Assertions.assertEquals(1l, ((AmValue.Int) tx.get(list, 0).get()).getValue()); - - Object elem1 = tx.get(list, 1).get(); - Object elem2 = tx.get(list, 2).get(); - assertions.assertInserted(elem1, elem2); - - Assertions.assertEquals(3l, ((AmValue.Int) tx.get(list, 3).get()).getValue()); - } - - @Test - public void testSpliceInt() { - testSplice2(NewValue.integer(4), NewValue.integer(5), (elem1, elem2) -> { - Assertions.assertEquals(4l, ((AmValue.Int) elem1).getValue()); - Assertions.assertEquals(5l, ((AmValue.Int) elem2).getValue()); - }); - } - - @Test - public void testSpliceUint() { - testSplice2(NewValue.uint(1), NewValue.uint(2), (elem1, elem2) -> { - Assertions.assertEquals(1, ((AmValue.UInt) elem1).getValue()); - Assertions.assertEquals(2, ((AmValue.UInt) elem2).getValue()); - }); - } - - @Test - public void testSpliceDouble() { - testSplice2(NewValue.f64(4.0d), NewValue.f64(5.0d), (elem1, elem2) -> { - Assertions.assertEquals(4.0, ((AmValue.F64) elem1).getValue()); - Assertions.assertEquals(5.0, ((AmValue.F64) elem2).getValue()); - }); - } - - @Test - public void testSpliceBytes() { - testSplice2(NewValue.bytes("4".getBytes()), NewValue.bytes("5".getBytes()), (elem1, elem2) -> { - Assertions.assertArrayEquals("4".getBytes(), ((AmValue.Bytes) elem1).getValue()); - Assertions.assertArrayEquals("5".getBytes(), ((AmValue.Bytes) elem2).getValue()); - }); - } - - @Test - public void testInsertBool() { - testSplice2(NewValue.bool(true), NewValue.bool(false), (elem1, elem2) -> { - Assertions.assertEquals(true, ((AmValue.Bool) elem1).getValue()); - Assertions.assertEquals(false, ((AmValue.Bool) elem2).getValue()); - }); - } - - @Test - public void testInsertNull() { - testSplice2(NewValue.NULL, NewValue.NULL, (elem1, elem2) -> { - Assertions.assertInstanceOf(AmValue.Null.class, elem1); - Assertions.assertInstanceOf(AmValue.Null.class, elem2); - }); - } - - @Test - public void testInsertString() { - testSplice2(NewValue.str("4"), NewValue.str("5"), (elem1, elem2) -> { - Assertions.assertEquals("4", ((AmValue.Str) elem1).getValue()); - Assertions.assertEquals("5", ((AmValue.Str) elem2).getValue()); - }); - } - - @Test - public void testSpliceDate() { - Date d1 = new Date(); - Date d2 = new Date(); - testSplice2(NewValue.timestamp(d1), NewValue.timestamp(d2), (elem1, elem2) -> { - Assertions.assertEquals(d1, ((AmValue.Timestamp) elem1).getValue()); - Assertions.assertEquals(d2, ((AmValue.Timestamp) elem2).getValue()); - }); - } - - @Test - public void testSpliceCounter() { - testSplice2(NewValue.counter(1), NewValue.counter(2), (elem1, elem2) -> { - Assertions.assertEquals(1, ((AmValue.Counter) elem1).getValue()); - Assertions.assertEquals(2, ((AmValue.Counter) elem2).getValue()); - }); - } + private Document doc; + private Transaction tx; + private ObjectId list; + + @FunctionalInterface + interface InsertedAssertions { + void assertInserted(Object elem1, Object elem2); + } + + public TestSplice() { + super(); + } + + @BeforeEach + public void setup() { + doc = new Document(); + tx = doc.startTransaction(); + list = tx.set(ObjectId.ROOT, "list", ObjectType.LIST); + } + + void testSplice2(NewValue val1, NewValue val2, InsertedAssertions assertions) { + tx.insert(list, 0, 1); + tx.insert(list, 1, 2); + tx.insert(list, 2, 3); + NewValue[] toInsert = {val1, val2,}; + tx.splice(list, 1, 1, Arrays.asList(toInsert).iterator()); + Assertions.assertEquals(1l, ((AmValue.Int) tx.get(list, 0).get()).getValue()); + + Object elem1 = tx.get(list, 1).get(); + Object elem2 = tx.get(list, 2).get(); + assertions.assertInserted(elem1, elem2); + + Assertions.assertEquals(3l, ((AmValue.Int) tx.get(list, 3).get()).getValue()); + } + + @Test + public void testSpliceInt() { + testSplice2(NewValue.integer(4), NewValue.integer(5), (elem1, elem2) -> { + Assertions.assertEquals(4l, ((AmValue.Int) elem1).getValue()); + Assertions.assertEquals(5l, ((AmValue.Int) elem2).getValue()); + }); + } + + @Test + public void testSpliceUint() { + testSplice2(NewValue.uint(1), NewValue.uint(2), (elem1, elem2) -> { + Assertions.assertEquals(1, ((AmValue.UInt) elem1).getValue()); + Assertions.assertEquals(2, ((AmValue.UInt) elem2).getValue()); + }); + } + + @Test + public void testSpliceDouble() { + testSplice2(NewValue.f64(4.0d), NewValue.f64(5.0d), (elem1, elem2) -> { + Assertions.assertEquals(4.0, ((AmValue.F64) elem1).getValue()); + Assertions.assertEquals(5.0, ((AmValue.F64) elem2).getValue()); + }); + } + + @Test + public void testSpliceBytes() { + testSplice2(NewValue.bytes("4".getBytes()), NewValue.bytes("5".getBytes()), (elem1, elem2) -> { + Assertions.assertArrayEquals("4".getBytes(), ((AmValue.Bytes) elem1).getValue()); + Assertions.assertArrayEquals("5".getBytes(), ((AmValue.Bytes) elem2).getValue()); + }); + } + + @Test + public void testInsertBool() { + testSplice2(NewValue.bool(true), NewValue.bool(false), (elem1, elem2) -> { + Assertions.assertEquals(true, ((AmValue.Bool) elem1).getValue()); + Assertions.assertEquals(false, ((AmValue.Bool) elem2).getValue()); + }); + } + + @Test + public void testInsertNull() { + testSplice2(NewValue.NULL, NewValue.NULL, (elem1, elem2) -> { + Assertions.assertInstanceOf(AmValue.Null.class, elem1); + Assertions.assertInstanceOf(AmValue.Null.class, elem2); + }); + } + + @Test + public void testInsertString() { + testSplice2(NewValue.str("4"), NewValue.str("5"), (elem1, elem2) -> { + Assertions.assertEquals("4", ((AmValue.Str) elem1).getValue()); + Assertions.assertEquals("5", ((AmValue.Str) elem2).getValue()); + }); + } + + @Test + public void testSpliceDate() { + Date d1 = new Date(); + Date d2 = new Date(); + testSplice2(NewValue.timestamp(d1), NewValue.timestamp(d2), (elem1, elem2) -> { + Assertions.assertEquals(d1, ((AmValue.Timestamp) elem1).getValue()); + Assertions.assertEquals(d2, ((AmValue.Timestamp) elem2).getValue()); + }); + } + + @Test + public void testSpliceCounter() { + testSplice2(NewValue.counter(1), NewValue.counter(2), (elem1, elem2) -> { + Assertions.assertEquals(1, ((AmValue.Counter) elem1).getValue()); + Assertions.assertEquals(2, ((AmValue.Counter) elem2).getValue()); + }); + } } // Check that committing the transaction clears Document.transactionPtr diff --git a/lib/src/test/java/org/automerge/TestSync.java b/lib/src/test/java/org/automerge/TestSync.java index aa18654..bf72ba5 100644 --- a/lib/src/test/java/org/automerge/TestSync.java +++ b/lib/src/test/java/org/automerge/TestSync.java @@ -7,179 +7,179 @@ import org.junit.jupiter.api.Test; class TestSync { - @Test - public void testSync() { - Document doc1 = new Document(); - try (Transaction tx = doc1.startTransaction()) { - tx.set(ObjectId.ROOT, "key", "value"); - tx.commit(); - } - - Document doc2 = doc1.fork(); - try (Transaction tx = doc2.startTransaction()) { - tx.set(ObjectId.ROOT, "key2", "value2"); - tx.commit(); - } - try (Transaction tx = doc1.startTransaction()) { - tx.set(ObjectId.ROOT, "key3", "value3"); - tx.commit(); - } - - sync(doc1, doc2); - - Assertions.assertEquals("value", ((AmValue.Str) doc1.get(ObjectId.ROOT, "key").get()).getValue()); - Assertions.assertEquals("value2", ((AmValue.Str) doc1.get(ObjectId.ROOT, "key2").get()).getValue()); - Assertions.assertEquals("value3", ((AmValue.Str) doc1.get(ObjectId.ROOT, "key3").get()).getValue()); - Assertions.assertEquals("value", ((AmValue.Str) doc2.get(ObjectId.ROOT, "key").get()).getValue()); - Assertions.assertEquals("value2", ((AmValue.Str) doc2.get(ObjectId.ROOT, "key2").get()).getValue()); - Assertions.assertEquals("value3", ((AmValue.Str) doc2.get(ObjectId.ROOT, "key3").get()).getValue()); - } - - @Test - public void testGenerateSyncMessageThrowsInTransaction() { - Document doc1 = new Document(); - Transaction tx = doc1.startTransaction(); - SyncState syncState = new SyncState(); - Assertions.assertThrows(TransactionInProgress.class, () -> { - doc1.generateSyncMessage(syncState); - }); - } - - @Test - public void testRecieveSyncMessageThrowsInTransaction() { - Document doc1 = new Document(); - try (Transaction tx = doc1.startTransaction()) { - tx.set(ObjectId.ROOT, "key", "value"); - tx.commit(); - } - SyncState syncState = new SyncState(); - byte[] message = syncState.generateSyncMessage(doc1).get(); - Document doc2 = new Document(); - Transaction tx = doc2.startTransaction(); - SyncState syncState2 = new SyncState(); - Assertions.assertThrows(TransactionInProgress.class, () -> { - doc2.receiveSyncMessage(syncState2, message); - }); - } - - @Test - public void testEncodeDecodeSyncState() { - SyncState state = new SyncState(); - Document doc = new Document(); - try (Transaction tx = doc.startTransaction()) { - tx.set(ObjectId.ROOT, "key", "value"); - tx.commit(); - } - byte[] message = doc.generateSyncMessage(state).get(); - byte[] encodedState = state.encode(); - SyncState state2 = SyncState.decode(encodedState); - byte[] message2 = doc.generateSyncMessage(state2).get(); - Assertions.assertArrayEquals(message, message2); - } - - @Test - public void testFree() { - SyncState state = new SyncState(); - state.free(); - } - - void sync(Document docA, Document docB) { - SyncState atob = new SyncState(); - SyncState btoa = new SyncState(); - sync(atob, btoa, docA, docB); - } - - void sync(SyncState atob, SyncState btoa, Document docA, Document docB) { - int iterations = 0; - while (true) { - Optional message1 = docA.generateSyncMessage(atob); - if (message1.isPresent()) { - docB.receiveSyncMessage(btoa, message1.get()); - } - Optional message2 = docB.generateSyncMessage(btoa); - if (message2.isPresent()) { - docA.receiveSyncMessage(atob, message2.get()); - } - if (!message1.isPresent() && !message2.isPresent()) { - break; - } - iterations += 1; - if (iterations >= 10) { - throw new RuntimeException("Sync failed to converge"); - } - } - } - - @Test - public void testSyncForPatches() { - Document doc1 = new Document(); - try (Transaction tx = doc1.startTransaction()) { - tx.set(ObjectId.ROOT, "key", "value"); - tx.commit(); - } - - Document doc2 = new Document(); - - List patches = syncForPatches(doc1, doc2); - - Assertions.assertEquals(patches.size(), 1); - Patch patch = patches.get(0); - Assertions.assertEquals(patch.getPath(), new ArrayList<>()); - Assertions.assertEquals(patch.getObj(), ObjectId.ROOT); - PatchAction.PutMap action = (PatchAction.PutMap) patch.getAction(); - Assertions.assertEquals(action.getKey(), "key"); - Assertions.assertEquals(((AmValue.Str) action.getValue()).getValue(), "value"); - } - - // Run receiveSyncMessageForPatches on docB and return all patches that were - // generated - List syncForPatches(Document docA, Document docB) { - SyncState atob = new SyncState(); - SyncState btoa = new SyncState(); - PatchLog patchLog = new PatchLog(); - int iterations = 0; - while (true) { - Optional message1 = docA.generateSyncMessage(atob); - if (message1.isPresent()) { - docB.receiveSyncMessage(btoa, patchLog, message1.get()); - } - Optional message2 = docB.generateSyncMessage(btoa); - if (message2.isPresent()) { - docA.receiveSyncMessage(atob, message2.get()); - } - if (!message1.isPresent() && !message2.isPresent()) { - break; - } - iterations += 1; - if (iterations >= 10) { - throw new RuntimeException("Sync failed to converge"); - } - } - return docB.makePatches(patchLog); - } - - @Test - public void testInSync() { - Document doc1 = new Document(); - try (Transaction tx = doc1.startTransaction()) { - tx.set(ObjectId.ROOT, "key", "value"); - tx.commit(); - } - - Document doc2 = doc1.fork(); - try (Transaction tx = doc2.startTransaction()) { - tx.set(ObjectId.ROOT, "key2", "value2"); - tx.commit(); - } - - SyncState atob = new SyncState(); - SyncState btoa = new SyncState(); - Assertions.assertFalse(atob.isInSync(doc1)); - Assertions.assertFalse(btoa.isInSync(doc2)); - - sync(atob, btoa, doc1, doc2); - Assertions.assertTrue(atob.isInSync(doc1)); - Assertions.assertTrue(btoa.isInSync(doc2)); - - } + @Test + public void testSync() { + Document doc1 = new Document(); + try (Transaction tx = doc1.startTransaction()) { + tx.set(ObjectId.ROOT, "key", "value"); + tx.commit(); + } + + Document doc2 = doc1.fork(); + try (Transaction tx = doc2.startTransaction()) { + tx.set(ObjectId.ROOT, "key2", "value2"); + tx.commit(); + } + try (Transaction tx = doc1.startTransaction()) { + tx.set(ObjectId.ROOT, "key3", "value3"); + tx.commit(); + } + + sync(doc1, doc2); + + Assertions.assertEquals("value", ((AmValue.Str) doc1.get(ObjectId.ROOT, "key").get()).getValue()); + Assertions.assertEquals("value2", ((AmValue.Str) doc1.get(ObjectId.ROOT, "key2").get()).getValue()); + Assertions.assertEquals("value3", ((AmValue.Str) doc1.get(ObjectId.ROOT, "key3").get()).getValue()); + Assertions.assertEquals("value", ((AmValue.Str) doc2.get(ObjectId.ROOT, "key").get()).getValue()); + Assertions.assertEquals("value2", ((AmValue.Str) doc2.get(ObjectId.ROOT, "key2").get()).getValue()); + Assertions.assertEquals("value3", ((AmValue.Str) doc2.get(ObjectId.ROOT, "key3").get()).getValue()); + } + + @Test + public void testGenerateSyncMessageThrowsInTransaction() { + Document doc1 = new Document(); + Transaction tx = doc1.startTransaction(); + SyncState syncState = new SyncState(); + Assertions.assertThrows(TransactionInProgress.class, () -> { + doc1.generateSyncMessage(syncState); + }); + } + + @Test + public void testRecieveSyncMessageThrowsInTransaction() { + Document doc1 = new Document(); + try (Transaction tx = doc1.startTransaction()) { + tx.set(ObjectId.ROOT, "key", "value"); + tx.commit(); + } + SyncState syncState = new SyncState(); + byte[] message = syncState.generateSyncMessage(doc1).get(); + Document doc2 = new Document(); + Transaction tx = doc2.startTransaction(); + SyncState syncState2 = new SyncState(); + Assertions.assertThrows(TransactionInProgress.class, () -> { + doc2.receiveSyncMessage(syncState2, message); + }); + } + + @Test + public void testEncodeDecodeSyncState() { + SyncState state = new SyncState(); + Document doc = new Document(); + try (Transaction tx = doc.startTransaction()) { + tx.set(ObjectId.ROOT, "key", "value"); + tx.commit(); + } + byte[] message = doc.generateSyncMessage(state).get(); + byte[] encodedState = state.encode(); + SyncState state2 = SyncState.decode(encodedState); + byte[] message2 = doc.generateSyncMessage(state2).get(); + Assertions.assertArrayEquals(message, message2); + } + + @Test + public void testFree() { + SyncState state = new SyncState(); + state.free(); + } + + void sync(Document docA, Document docB) { + SyncState atob = new SyncState(); + SyncState btoa = new SyncState(); + sync(atob, btoa, docA, docB); + } + + void sync(SyncState atob, SyncState btoa, Document docA, Document docB) { + int iterations = 0; + while (true) { + Optional message1 = docA.generateSyncMessage(atob); + if (message1.isPresent()) { + docB.receiveSyncMessage(btoa, message1.get()); + } + Optional message2 = docB.generateSyncMessage(btoa); + if (message2.isPresent()) { + docA.receiveSyncMessage(atob, message2.get()); + } + if (!message1.isPresent() && !message2.isPresent()) { + break; + } + iterations += 1; + if (iterations >= 10) { + throw new RuntimeException("Sync failed to converge"); + } + } + } + + @Test + public void testSyncForPatches() { + Document doc1 = new Document(); + try (Transaction tx = doc1.startTransaction()) { + tx.set(ObjectId.ROOT, "key", "value"); + tx.commit(); + } + + Document doc2 = new Document(); + + List patches = syncForPatches(doc1, doc2); + + Assertions.assertEquals(patches.size(), 1); + Patch patch = patches.get(0); + Assertions.assertEquals(patch.getPath(), new ArrayList<>()); + Assertions.assertEquals(patch.getObj(), ObjectId.ROOT); + PatchAction.PutMap action = (PatchAction.PutMap) patch.getAction(); + Assertions.assertEquals(action.getKey(), "key"); + Assertions.assertEquals(((AmValue.Str) action.getValue()).getValue(), "value"); + } + + // Run receiveSyncMessageForPatches on docB and return all patches that were + // generated + List syncForPatches(Document docA, Document docB) { + SyncState atob = new SyncState(); + SyncState btoa = new SyncState(); + PatchLog patchLog = new PatchLog(); + int iterations = 0; + while (true) { + Optional message1 = docA.generateSyncMessage(atob); + if (message1.isPresent()) { + docB.receiveSyncMessage(btoa, patchLog, message1.get()); + } + Optional message2 = docB.generateSyncMessage(btoa); + if (message2.isPresent()) { + docA.receiveSyncMessage(atob, message2.get()); + } + if (!message1.isPresent() && !message2.isPresent()) { + break; + } + iterations += 1; + if (iterations >= 10) { + throw new RuntimeException("Sync failed to converge"); + } + } + return docB.makePatches(patchLog); + } + + @Test + public void testInSync() { + Document doc1 = new Document(); + try (Transaction tx = doc1.startTransaction()) { + tx.set(ObjectId.ROOT, "key", "value"); + tx.commit(); + } + + Document doc2 = doc1.fork(); + try (Transaction tx = doc2.startTransaction()) { + tx.set(ObjectId.ROOT, "key2", "value2"); + tx.commit(); + } + + SyncState atob = new SyncState(); + SyncState btoa = new SyncState(); + Assertions.assertFalse(atob.isInSync(doc1)); + Assertions.assertFalse(btoa.isInSync(doc2)); + + sync(atob, btoa, doc1, doc2); + Assertions.assertTrue(atob.isInSync(doc1)); + Assertions.assertTrue(btoa.isInSync(doc2)); + + } } diff --git a/lib/src/test/java/org/automerge/TestText.java b/lib/src/test/java/org/automerge/TestText.java index 193b942..4c94168 100644 --- a/lib/src/test/java/org/automerge/TestText.java +++ b/lib/src/test/java/org/automerge/TestText.java @@ -6,58 +6,58 @@ import org.junit.jupiter.api.Test; class TestText { - private Document doc; - private ObjectId text; - - public TestText() { - super(); - } - - @BeforeEach - public void setup() { - doc = new Document(); - Transaction tx = doc.startTransaction(); - text = tx.set(ObjectId.ROOT, "text", ObjectType.TEXT); - tx.commit(); - } - - @Test - public void testGet() { - Transaction tx = doc.startTransaction(); - tx.spliceText(text, 0, 0, "hello"); - Assertions.assertEquals(Optional.of("hello"), tx.text(text)); - Assertions.assertEquals(Optional.of("hello"), doc.text(text)); - } - - @Test - public void testGetNonTextInDoc() { - Document otherDoc = new Document(); - Transaction otherTx = otherDoc.startTransaction(); - ObjectId map = otherTx.set(ObjectId.ROOT, "map", ObjectType.MAP); - otherTx.commit(); - Assertions.assertEquals(otherDoc.text(map), Optional.empty()); - } - - @Test - public void testGetNonTextInTx() { - Document otherDoc = new Document(); - Transaction otherTx = otherDoc.startTransaction(); - ObjectId map = otherTx.set(ObjectId.ROOT, "map", ObjectType.MAP); - Assertions.assertEquals(otherTx.text(map), Optional.empty()); - } - - @Test - public void testTextAt() { - Transaction tx = doc.startTransaction(); - tx.spliceText(text, 0, 0, "hello"); - tx.commit(); - ChangeHash[] heads = doc.getHeads(); - tx = doc.startTransaction(); - tx.spliceText(text, 5, 0, " world"); - Assertions.assertEquals(Optional.of("hello world"), tx.text(text)); - Assertions.assertEquals(Optional.of("hello"), tx.text(text, heads)); - Assertions.assertEquals(Optional.of("hello"), doc.text(text, heads)); - tx.commit(); - Assertions.assertEquals(Optional.of("hello"), doc.text(text, heads)); - } + private Document doc; + private ObjectId text; + + public TestText() { + super(); + } + + @BeforeEach + public void setup() { + doc = new Document(); + Transaction tx = doc.startTransaction(); + text = tx.set(ObjectId.ROOT, "text", ObjectType.TEXT); + tx.commit(); + } + + @Test + public void testGet() { + Transaction tx = doc.startTransaction(); + tx.spliceText(text, 0, 0, "hello"); + Assertions.assertEquals(Optional.of("hello"), tx.text(text)); + Assertions.assertEquals(Optional.of("hello"), doc.text(text)); + } + + @Test + public void testGetNonTextInDoc() { + Document otherDoc = new Document(); + Transaction otherTx = otherDoc.startTransaction(); + ObjectId map = otherTx.set(ObjectId.ROOT, "map", ObjectType.MAP); + otherTx.commit(); + Assertions.assertEquals(otherDoc.text(map), Optional.empty()); + } + + @Test + public void testGetNonTextInTx() { + Document otherDoc = new Document(); + Transaction otherTx = otherDoc.startTransaction(); + ObjectId map = otherTx.set(ObjectId.ROOT, "map", ObjectType.MAP); + Assertions.assertEquals(otherTx.text(map), Optional.empty()); + } + + @Test + public void testTextAt() { + Transaction tx = doc.startTransaction(); + tx.spliceText(text, 0, 0, "hello"); + tx.commit(); + ChangeHash[] heads = doc.getHeads(); + tx = doc.startTransaction(); + tx.spliceText(text, 5, 0, " world"); + Assertions.assertEquals(Optional.of("hello world"), tx.text(text)); + Assertions.assertEquals(Optional.of("hello"), tx.text(text, heads)); + Assertions.assertEquals(Optional.of("hello"), doc.text(text, heads)); + tx.commit(); + Assertions.assertEquals(Optional.of("hello"), doc.text(text, heads)); + } } diff --git a/lib/src/test/java/org/automerge/TestTransaction.java b/lib/src/test/java/org/automerge/TestTransaction.java index ca9812a..58e132a 100644 --- a/lib/src/test/java/org/automerge/TestTransaction.java +++ b/lib/src/test/java/org/automerge/TestTransaction.java @@ -7,180 +7,180 @@ import org.junit.jupiter.api.Test; public final class TestTransaction { - private Document doc; - private Transaction tx; - - public TestTransaction() { - super(); - } - - @BeforeEach - public void setup() { - doc = new Document(); - tx = doc.startTransaction(); - } - - @Test - public void commitTransaction() { - // Check that committing the transaction clears Document.transactionPtr - tx.set(ObjectId.ROOT, "key", 1.23); - tx.commit(); - tx = doc.startTransaction(); - tx.set(ObjectId.ROOT, "key", 4.56); - tx.commit(); - Optional result = doc.get(ObjectId.ROOT, "key"); - Assertions.assertEquals(4.56, ((AmValue.F64) result.get()).getValue()); - } - - @Test - public final void exceptionCaughtWhenSetting() { - // Create an object in another document, then use that object in this document, - // yielding an error in Rust - // then check the error is caught and thrown as an AutomergeException. - Document otherDoc = new Document(); - final ObjectId otherObj; - try (Transaction tx = otherDoc.startTransaction()) { - otherObj = tx.set(ObjectId.ROOT, "key", ObjectType.MAP); - } - // Close the already running transaction from `setup` - tx.commit(); - Assertions.assertThrows(AutomergeException.class, () -> { - try (Transaction tx = doc.startTransaction()) { - tx.set(otherObj, "key", 1.23); - } - }); - } - - @Test - public void testRollBackRemovesOps() { - tx.set(ObjectId.ROOT, "key", 1.23); - tx.rollback(); - Assertions.assertFalse(doc.get(ObjectId.ROOT, "key").isPresent()); - } - - @Test - public void testRollbackClearsTransaction() { - tx.set(ObjectId.ROOT, "key", 1.23); - tx.rollback(); - doc.startTransaction(); - } - - @Test - public void testCloseWithoutCommitRollsback() { - tx.set(ObjectId.ROOT, "key", 1.23); - tx.close(); - Assertions.assertFalse(doc.get(ObjectId.ROOT, "key").isPresent()); - // Check we can start a new transaction because the last one was closed - doc.startTransaction(); - } - - @Test - public void testCloseAfterCommitIsNoOp() { - tx.set(ObjectId.ROOT, "key", 1.23); - tx.commit(); - tx.close(); - Assertions.assertEquals(1.23, ((AmValue.F64) doc.get(ObjectId.ROOT, "key").get()).getValue()); - // Check we can start a new transaction because the last one was closed - doc.startTransaction(); - } - - // Basic tests for setting properties in maps - @Test - public final void setDoubleInMap() { - tx.set(ObjectId.ROOT, "key", 1.23); - tx.commit(); - Assertions.assertEquals(1.23, ((AmValue.F64) doc.get(ObjectId.ROOT, "key").get()).getValue()); - } - - @Test - public final void setBytesInMap() { - byte[] somebytes = "some bytes".getBytes(); - tx.set(ObjectId.ROOT, "bytes", somebytes); - tx.commit(); - Assertions.assertArrayEquals(somebytes, ((AmValue.Bytes) doc.get(ObjectId.ROOT, "bytes").get()).getValue()); - } - - @Test - public final void setStringInMap() { - tx.set(ObjectId.ROOT, "key", "some string"); - Assertions.assertEquals("some string", ((AmValue.Str) doc.get(ObjectId.ROOT, "key").get()).getValue()); - } - - @Test - public final void setIntInMap() { - tx.set(ObjectId.ROOT, "key", 10); - Assertions.assertEquals(10, ((AmValue.Int) doc.get(ObjectId.ROOT, "key").get()).getValue()); - } - - @Test - public final void setUintInMap() { - tx.set(ObjectId.ROOT, "key", NewValue.uint(10)); - Assertions.assertEquals(10, ((AmValue.UInt) doc.get(ObjectId.ROOT, "key").get()).getValue()); - } - - @Test - public final void setBoolInMap() { - tx.set(ObjectId.ROOT, "false", false); - tx.set(ObjectId.ROOT, "true", true); - Assertions.assertEquals(false, ((AmValue.Bool) doc.get(ObjectId.ROOT, "false").get()).getValue()); - Assertions.assertEquals(true, ((AmValue.Bool) doc.get(ObjectId.ROOT, "true").get()).getValue()); - } - - @Test - public final void setNullInMap() { - tx.set(ObjectId.ROOT, "key", NewValue.NULL); - Assertions.assertInstanceOf(AmValue.Null.class, doc.get(ObjectId.ROOT, "key").get()); - } - - @Test - public final void setCounterInMap() { - tx.set(ObjectId.ROOT, "counter", new Counter(10)); - Assertions.assertEquals(10, ((AmValue.Counter) doc.get(ObjectId.ROOT, "counter").get()).getValue()); - } - - @Test - public final void setDateInMap() { - Date date = new Date(); - tx.set(ObjectId.ROOT, "key", date); - Assertions.assertEquals(date, ((AmValue.Timestamp) doc.get(ObjectId.ROOT, "key").get()).getValue()); - } - - @Test - public final void getNone() { - Assertions.assertFalse(doc.get(ObjectId.ROOT, "nonexistent").isPresent()); - } - - // Test for creating objects - @Test - public final void putMapInMap() { - ObjectId nested = tx.set(ObjectId.ROOT, "nested", ObjectType.MAP); - tx.set(nested, "key", 1.23); - tx.commit(); - Assertions.assertEquals(nested, ((AmValue.Map) doc.get(ObjectId.ROOT, "nested").get()).getId()); - } - - @Test - public final void putListInMap() { - ObjectId nested = tx.set(ObjectId.ROOT, "nested", ObjectType.LIST); - Assertions.assertEquals(nested, ((AmValue.List) doc.get(ObjectId.ROOT, "nested").get()).getId()); - } - - @Test - public void testGetInListInTx() { - ObjectId nested = tx.set(ObjectId.ROOT, "nested", ObjectType.LIST); - tx.insert(nested, 0, 123); - Assertions.assertEquals(123, ((AmValue.Int) doc.get(nested, 0).get()).getValue()); - Assertions.assertEquals(123, ((AmValue.Int) tx.get(nested, 0).get()).getValue()); - tx.commit(); - Assertions.assertEquals(123, ((AmValue.Int) doc.get(nested, 0).get()).getValue()); - } - - @Test - public void testGetInMapInTx() { - tx.set(ObjectId.ROOT, "key", 123); - Assertions.assertEquals(123, ((AmValue.Int) doc.get(ObjectId.ROOT, "key").get()).getValue()); - Assertions.assertEquals(123, ((AmValue.Int) tx.get(ObjectId.ROOT, "key").get()).getValue()); - tx.commit(); - Assertions.assertEquals(123, ((AmValue.Int) doc.get(ObjectId.ROOT, "key").get()).getValue()); - } + private Document doc; + private Transaction tx; + + public TestTransaction() { + super(); + } + + @BeforeEach + public void setup() { + doc = new Document(); + tx = doc.startTransaction(); + } + + @Test + public void commitTransaction() { + // Check that committing the transaction clears Document.transactionPtr + tx.set(ObjectId.ROOT, "key", 1.23); + tx.commit(); + tx = doc.startTransaction(); + tx.set(ObjectId.ROOT, "key", 4.56); + tx.commit(); + Optional result = doc.get(ObjectId.ROOT, "key"); + Assertions.assertEquals(4.56, ((AmValue.F64) result.get()).getValue()); + } + + @Test + public final void exceptionCaughtWhenSetting() { + // Create an object in another document, then use that object in this document, + // yielding an error in Rust + // then check the error is caught and thrown as an AutomergeException. + Document otherDoc = new Document(); + final ObjectId otherObj; + try (Transaction tx = otherDoc.startTransaction()) { + otherObj = tx.set(ObjectId.ROOT, "key", ObjectType.MAP); + } + // Close the already running transaction from `setup` + tx.commit(); + Assertions.assertThrows(AutomergeException.class, () -> { + try (Transaction tx = doc.startTransaction()) { + tx.set(otherObj, "key", 1.23); + } + }); + } + + @Test + public void testRollBackRemovesOps() { + tx.set(ObjectId.ROOT, "key", 1.23); + tx.rollback(); + Assertions.assertFalse(doc.get(ObjectId.ROOT, "key").isPresent()); + } + + @Test + public void testRollbackClearsTransaction() { + tx.set(ObjectId.ROOT, "key", 1.23); + tx.rollback(); + doc.startTransaction(); + } + + @Test + public void testCloseWithoutCommitRollsback() { + tx.set(ObjectId.ROOT, "key", 1.23); + tx.close(); + Assertions.assertFalse(doc.get(ObjectId.ROOT, "key").isPresent()); + // Check we can start a new transaction because the last one was closed + doc.startTransaction(); + } + + @Test + public void testCloseAfterCommitIsNoOp() { + tx.set(ObjectId.ROOT, "key", 1.23); + tx.commit(); + tx.close(); + Assertions.assertEquals(1.23, ((AmValue.F64) doc.get(ObjectId.ROOT, "key").get()).getValue()); + // Check we can start a new transaction because the last one was closed + doc.startTransaction(); + } + + // Basic tests for setting properties in maps + @Test + public final void setDoubleInMap() { + tx.set(ObjectId.ROOT, "key", 1.23); + tx.commit(); + Assertions.assertEquals(1.23, ((AmValue.F64) doc.get(ObjectId.ROOT, "key").get()).getValue()); + } + + @Test + public final void setBytesInMap() { + byte[] somebytes = "some bytes".getBytes(); + tx.set(ObjectId.ROOT, "bytes", somebytes); + tx.commit(); + Assertions.assertArrayEquals(somebytes, ((AmValue.Bytes) doc.get(ObjectId.ROOT, "bytes").get()).getValue()); + } + + @Test + public final void setStringInMap() { + tx.set(ObjectId.ROOT, "key", "some string"); + Assertions.assertEquals("some string", ((AmValue.Str) doc.get(ObjectId.ROOT, "key").get()).getValue()); + } + + @Test + public final void setIntInMap() { + tx.set(ObjectId.ROOT, "key", 10); + Assertions.assertEquals(10, ((AmValue.Int) doc.get(ObjectId.ROOT, "key").get()).getValue()); + } + + @Test + public final void setUintInMap() { + tx.set(ObjectId.ROOT, "key", NewValue.uint(10)); + Assertions.assertEquals(10, ((AmValue.UInt) doc.get(ObjectId.ROOT, "key").get()).getValue()); + } + + @Test + public final void setBoolInMap() { + tx.set(ObjectId.ROOT, "false", false); + tx.set(ObjectId.ROOT, "true", true); + Assertions.assertEquals(false, ((AmValue.Bool) doc.get(ObjectId.ROOT, "false").get()).getValue()); + Assertions.assertEquals(true, ((AmValue.Bool) doc.get(ObjectId.ROOT, "true").get()).getValue()); + } + + @Test + public final void setNullInMap() { + tx.set(ObjectId.ROOT, "key", NewValue.NULL); + Assertions.assertInstanceOf(AmValue.Null.class, doc.get(ObjectId.ROOT, "key").get()); + } + + @Test + public final void setCounterInMap() { + tx.set(ObjectId.ROOT, "counter", new Counter(10)); + Assertions.assertEquals(10, ((AmValue.Counter) doc.get(ObjectId.ROOT, "counter").get()).getValue()); + } + + @Test + public final void setDateInMap() { + Date date = new Date(); + tx.set(ObjectId.ROOT, "key", date); + Assertions.assertEquals(date, ((AmValue.Timestamp) doc.get(ObjectId.ROOT, "key").get()).getValue()); + } + + @Test + public final void getNone() { + Assertions.assertFalse(doc.get(ObjectId.ROOT, "nonexistent").isPresent()); + } + + // Test for creating objects + @Test + public final void putMapInMap() { + ObjectId nested = tx.set(ObjectId.ROOT, "nested", ObjectType.MAP); + tx.set(nested, "key", 1.23); + tx.commit(); + Assertions.assertEquals(nested, ((AmValue.Map) doc.get(ObjectId.ROOT, "nested").get()).getId()); + } + + @Test + public final void putListInMap() { + ObjectId nested = tx.set(ObjectId.ROOT, "nested", ObjectType.LIST); + Assertions.assertEquals(nested, ((AmValue.List) doc.get(ObjectId.ROOT, "nested").get()).getId()); + } + + @Test + public void testGetInListInTx() { + ObjectId nested = tx.set(ObjectId.ROOT, "nested", ObjectType.LIST); + tx.insert(nested, 0, 123); + Assertions.assertEquals(123, ((AmValue.Int) doc.get(nested, 0).get()).getValue()); + Assertions.assertEquals(123, ((AmValue.Int) tx.get(nested, 0).get()).getValue()); + tx.commit(); + Assertions.assertEquals(123, ((AmValue.Int) doc.get(nested, 0).get()).getValue()); + } + + @Test + public void testGetInMapInTx() { + tx.set(ObjectId.ROOT, "key", 123); + Assertions.assertEquals(123, ((AmValue.Int) doc.get(ObjectId.ROOT, "key").get()).getValue()); + Assertions.assertEquals(123, ((AmValue.Int) tx.get(ObjectId.ROOT, "key").get()).getValue()); + tx.commit(); + Assertions.assertEquals(123, ((AmValue.Int) doc.get(ObjectId.ROOT, "key").get()).getValue()); + } } diff --git a/lib/src/test/java/org/automerge/TestTransactionAt.java b/lib/src/test/java/org/automerge/TestTransactionAt.java index 96f55cd..d6c926d 100644 --- a/lib/src/test/java/org/automerge/TestTransactionAt.java +++ b/lib/src/test/java/org/automerge/TestTransactionAt.java @@ -7,36 +7,36 @@ public final class TestTransactionAt { - @Test - public void testTransactionAt() { - Document doc = new Document(); - ChangeHash firstHeads; - - try (Transaction tx = doc.startTransaction()) { - tx.set(ObjectId.ROOT, "key", 1.23); - firstHeads = tx.commit().get(); - } - - try (Transaction tx = doc.startTransaction()) { - tx.set(ObjectId.ROOT, "key", 4.56); - tx.commit().get(); - } - - PatchLog patchLog = new PatchLog(); - try (Transaction tx = doc.startTransactionAt(patchLog, new ChangeHash[]{firstHeads})) { - Optional resulut = tx.get(ObjectId.ROOT, "key"); - Assertions.assertEquals(1.23, ((AmValue.F64) resulut.get()).getValue()); - tx.set(ObjectId.ROOT, "key", 7.89); - tx.commit(); - } - - List patches = doc.makePatches(patchLog); - - Assertions.assertEquals(patches.size(), 1); - Assertions.assertEquals(patches.get(0).getObj(), ObjectId.ROOT); - PatchAction.PutMap action = (PatchAction.PutMap) patches.get(0).getAction(); - Assertions.assertEquals(action.getKey(), "key"); - AmValue.F64 value = ((AmValue.F64) action.getValue()); - Assertions.assertEquals(value.getValue(), 7.89); - } + @Test + public void testTransactionAt() { + Document doc = new Document(); + ChangeHash firstHeads; + + try (Transaction tx = doc.startTransaction()) { + tx.set(ObjectId.ROOT, "key", 1.23); + firstHeads = tx.commit().get(); + } + + try (Transaction tx = doc.startTransaction()) { + tx.set(ObjectId.ROOT, "key", 4.56); + tx.commit().get(); + } + + PatchLog patchLog = new PatchLog(); + try (Transaction tx = doc.startTransactionAt(patchLog, new ChangeHash[]{firstHeads})) { + Optional resulut = tx.get(ObjectId.ROOT, "key"); + Assertions.assertEquals(1.23, ((AmValue.F64) resulut.get()).getValue()); + tx.set(ObjectId.ROOT, "key", 7.89); + tx.commit(); + } + + List patches = doc.makePatches(patchLog); + + Assertions.assertEquals(patches.size(), 1); + Assertions.assertEquals(patches.get(0).getObj(), ObjectId.ROOT); + PatchAction.PutMap action = (PatchAction.PutMap) patches.get(0).getAction(); + Assertions.assertEquals(action.getKey(), "key"); + AmValue.F64 value = ((AmValue.F64) action.getValue()); + Assertions.assertEquals(value.getValue(), 7.89); + } } diff --git a/spotless.eclipseformat.xml b/spotless.eclipseformat.xml new file mode 100644 index 0000000..05a27c8 --- /dev/null +++ b/spotless.eclipseformat.xml @@ -0,0 +1,313 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +