diff --git a/pom.xml b/pom.xml index aaa93ac..e60641d 100644 --- a/pom.xml +++ b/pom.xml @@ -9,11 +9,48 @@ maven-compiler-plugin - 3.8.1 + 3.11.0 17 + UTF-8 + + org.apache.maven.plugins + maven-jar-plugin + 3.3.0 + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.6.2 + + 17 + UTF-8 + src/main/java + + + + attach-javadocs + + jar + + + + + + org.apache.maven.plugins + maven-source-plugin + 3.3.0 + + + attach-sources + + jar + + + + diff --git a/src/main/java/me/mrletsplay/shareclientcore/document/Util.java b/src/main/java/me/mrletsplay/shareclientcore/document/Util.java index 01d8e5e..ea883a6 100644 --- a/src/main/java/me/mrletsplay/shareclientcore/document/Util.java +++ b/src/main/java/me/mrletsplay/shareclientcore/document/Util.java @@ -5,6 +5,8 @@ import java.util.List; public class Util { + // Source for a lot of the code: https://digitalfreepen.com/2017/10/06/simple-real-time-collaborative-text-editor.html + public static final int BASE = 10; public static int comparePositions(Identifier[] a, Identifier[] b) { @@ -17,14 +19,17 @@ public class Util { } public static Identifier[] generatePositionBetween(Identifier[] before, Identifier[] after, int site) { + if(comparePositions(before, after) != -1) throw new IllegalArgumentException("before must be strictly less than after"); + List newPosition = new ArrayList<>(); for(int i = 0; i < Math.min(before.length, after.length) + 1; i++) { Identifier c1 = i >= before.length ? new Identifier(0, site) : before[i]; - Identifier c2 = i >= after.length ? new Identifier(BASE, site) : after[i]; + Identifier c2 = i >= after.length ? new Identifier(BASE - 1, site) : after[i]; if(c1.digit() != c2.digit()) { - // TODO: generate delta, then pick a value between + int[] incremented = getIncremented(before, after, i); + return constructPosition(incremented, before, after, site); } if(c1.site() == c2.site()) { @@ -34,7 +39,8 @@ public class Util { if(c1.site() < c2.site()) { // Anything starting with before will be sorted before after - newPosition.add(new Identifier(BASE, site)); + newPosition.add(c1); + newPosition.add(new Identifier(1, site)); return newPosition.toArray(Identifier[]::new); } @@ -44,9 +50,9 @@ public class Util { return null; } - public static int[] getIncremented(Identifier[] i1, Identifier[] i2, int offset) { + public static int[] getIncremented(Identifier[] before, Identifier[] after, int offset) { // TODO: can potentially be optimized by just calculating the index of the first non-zero digit (e.g. don't return array and discard it -> just return the index) - int[] delta = subtract(i2, i1, offset); + int[] delta = subtract(after, before, offset); int firstNonZero = 0; for(int i = 0; i < delta.length; i++) { if(delta[i] != 0) { @@ -57,16 +63,38 @@ public class Util { // Because of math, (firstNonZero + 1) >= i1.length (I think) // then make the array 1 longer so we have space for the increment (which is one order of magnitude smaller) - int[] incremented = new int[firstNonZero + 2]; + int[] incremented = new int[offset + firstNonZero + 2]; for(int i = 0; i < incremented.length; i++) { - incremented[i] = i <= i1.length ? i1[i].digit() : 0; + incremented[i] = i < before.length ? before[i].digit() : 0; } - add1AtIndex(incremented, firstNonZero + 1); // last digit might be zero, which is ambigious, inc again - if(incremented[incremented.length - 1] == 0) add1AtIndex(incremented, firstNonZero + 1); + add1AtIndex(incremented, offset + firstNonZero + 1); // last digit might be zero, which is ambigious, inc again + if(incremented[incremented.length - 1] == 0) add1AtIndex(incremented, offset + firstNonZero + 1); return incremented; } + public static Identifier[] constructPosition(int[] num, Identifier[] before, Identifier[] after, int site) { + // Implements rules according to constructPosition from https://inria.hal.science/inria-00432368/document + + Identifier[] ident = new Identifier[num.length]; + + for(int i = 0; i < num.length; i++) { + int digit = num[i]; + + if(i == num.length - 1) { + ident[i] = new Identifier(digit, site); + }else if(i < before.length && digit == before[i].digit()) { + ident[i] = before[i]; + }else if(i < after.length && digit == after[i].digit()) { + ident[i] = after[i]; + }else { + ident[i] = new Identifier(digit, site); + } + } + + return ident; + } + /** * Subtract b from a, where a > b */ diff --git a/src/test/java/me/mrletsplay/shareclientcore/DecimalTest.java b/src/test/java/me/mrletsplay/shareclientcore/DecimalTest.java index 4bcd748..99bf09f 100644 --- a/src/test/java/me/mrletsplay/shareclientcore/DecimalTest.java +++ b/src/test/java/me/mrletsplay/shareclientcore/DecimalTest.java @@ -12,58 +12,122 @@ public class DecimalTest { @Test public void testSimpleSubtraction() { - Identifier[] a = new Identifier[] { new Identifier(1, 0), new Identifier(2, 0), new Identifier(3, 0) }; - Identifier[] b = new Identifier[] { new Identifier(1, 0), new Identifier(1, 0), new Identifier(3, 0) }; + Identifier[] a = { new Identifier(1, 0), new Identifier(2, 0), new Identifier(3, 0) }; + Identifier[] b = { new Identifier(1, 0), new Identifier(1, 0), new Identifier(3, 0) }; assertArrayEquals(new int[] { 0, 1, 0 }, Util.subtract(a, b, 0)); } @Test public void testCarrySubtraction() { - Identifier[] a = new Identifier[] { new Identifier(2, 0), new Identifier(0, 0), new Identifier(4, 0) }; - Identifier[] b = new Identifier[] { new Identifier(1, 0), new Identifier(0, 0), new Identifier(5, 0) }; + Identifier[] a = { new Identifier(2, 0), new Identifier(0, 0), new Identifier(4, 0) }; + Identifier[] b = { new Identifier(1, 0), new Identifier(0, 0), new Identifier(5, 0) }; assertArrayEquals(new int[] { 0, Util.BASE - 1, Util.BASE - 1 }, Util.subtract(a, b, 0)); } @Test public void testOffsetSubtraction() { - Identifier[] a = new Identifier[] { new Identifier(1, 0), new Identifier(0, 0), new Identifier(5, 0) }; - Identifier[] b = new Identifier[] { new Identifier(1, 0), new Identifier(0, 0), new Identifier(4, 0) }; + Identifier[] a = { new Identifier(1, 0), new Identifier(0, 0), new Identifier(5, 0) }; + Identifier[] b = { new Identifier(1, 0), new Identifier(0, 0), new Identifier(4, 0) }; assertArrayEquals(new int[] { 0, 1 }, Util.subtract(a, b, 1)); } @Test public void testReversedInputFails() { - Identifier[] a = new Identifier[] { new Identifier(1, 0), new Identifier(1, 0), new Identifier(3, 0) }; - Identifier[] b = new Identifier[] { new Identifier(1, 0), new Identifier(2, 0), new Identifier(3, 0) }; + Identifier[] a = { new Identifier(1, 0), new Identifier(1, 0), new Identifier(3, 0) }; + Identifier[] b = { new Identifier(1, 0), new Identifier(2, 0), new Identifier(3, 0) }; assertThrows(RuntimeException.class, () -> Util.subtract(a, b, 0)); } @Test public void testSimpleAdd1() { - int[] a = new int[] { 0, 0 }; + int[] a = { 0, 0 }; Util.add1AtIndex(a, 1); assertArrayEquals(new int[] { 0, 1 }, a); } @Test public void testCarryAdd1() { - int[] a = new int[] { 0, Util.BASE - 1 }; + int[] a = { 0, Util.BASE - 1 }; Util.add1AtIndex(a, 1); assertArrayEquals(new int[] { 1, 0 }, a); } @Test public void testCarryAdd1_2() { - int[] a = new int[] { 0, Util.BASE - 1, Util.BASE - 1 }; + int[] a = { 0, Util.BASE - 1, Util.BASE - 1 }; Util.add1AtIndex(a, 2); assertArrayEquals(new int[] { 1, 0, 0 }, a); } @Test public void testCarryAdd1_3() { - int[] a = new int[] { 0, Util.BASE - 1, Util.BASE - 1 }; + int[] a = { 0, Util.BASE - 1, Util.BASE - 1 }; Util.add1AtIndex(a, 1); assertArrayEquals(new int[] { 1, 0, Util.BASE - 1 }, a); } + @Test + public void testIncrement_1() { + Identifier[] a = { new Identifier(1, 0), new Identifier(2, 0) }; + Identifier[] b = { new Identifier(1, 0), new Identifier(3, 0) }; + assertArrayEquals(new int[] { 1, 2, 1 }, Util.getIncremented(a, b, 1)); + } + + @Test + public void testIncrement_2() { + Identifier[] a = { new Identifier(1, 0), new Identifier(2, 0) }; + Identifier[] b = { new Identifier(1, 0), new Identifier(2, 0), new Identifier(Util.BASE - 1, 0) }; + assertArrayEquals(new int[] { 1, 2, 0, 1 }, Util.getIncremented(a, b, 1)); + } + + @Test + public void testCarryIncrement() { + Identifier[] a = { new Identifier(1, 0), new Identifier(2, 0), new Identifier(Util.BASE - 1, 0) }; + Identifier[] b = { new Identifier(1, 0), new Identifier(4, 0) }; + assertArrayEquals(new int[] { 1, 3, 1 }, Util.getIncremented(a, b, 1)); + } + + @Test + public void testConstructPosition_1() { + Identifier[] a = { new Identifier(1, 0), new Identifier(2, 0), new Identifier(Util.BASE - 1, 0) }; + Identifier[] b = { new Identifier(1, 0), new Identifier(4, 0) }; + int[] newIdent = Util.getIncremented(a, b, 1); + Identifier[] expected = { new Identifier(1, 0), new Identifier(3, 1), new Identifier(1, 1) }; + assertArrayEquals(expected, Util.constructPosition(newIdent, a, b, 1)); + } + + @Test + public void testConstructPosition_2() { + Identifier[] a = { new Identifier(1, 0), new Identifier(2, 2), new Identifier(2, 2) }; + Identifier[] b = { new Identifier(1, 0), new Identifier(4, 1), new Identifier(3, 1) }; + int[] newIdent = Util.getIncremented(a, b, 1); + Identifier[] expected = { new Identifier(1, 0), new Identifier(2, 2), new Identifier(3, 3) }; + assertArrayEquals(expected, Util.constructPosition(newIdent, a, b, 3)); + } + + @Test + public void testGeneratePosition_1() { + Identifier[] a = { new Identifier(1, 0), new Identifier(2, 1) }; + Identifier[] b = { new Identifier(1, 0), new Identifier(3, 2) }; + Identifier[] newIdent = Util.generatePositionBetween(a, b, 3); + Identifier[] expected = { new Identifier(1, 0), new Identifier(2, 1), new Identifier(1, 3) }; + assertArrayEquals(expected, newIdent); + } + + @Test + public void testGeneratePosition_2() { + Identifier[] a = { new Identifier(1, 0), new Identifier(2, 1) }; + Identifier[] b = { new Identifier(1, 0), new Identifier(2, 2) }; + Identifier[] newIdent = Util.generatePositionBetween(a, b, 3); + Identifier[] expected = { new Identifier(1, 0), new Identifier(2, 1), new Identifier(1, 3) }; + assertArrayEquals(expected, newIdent); + } + + @Test + public void testGeneratePositionInvalidInputFails() { + Identifier[] a = { new Identifier(1, 0), new Identifier(2, 2) }; + Identifier[] b = { new Identifier(1, 0), new Identifier(2, 1) }; + assertThrows(IllegalArgumentException.class, () -> Util.generatePositionBetween(a, b, 3)); + } + }