Allow multiple changes per ChangeMessage
This commit is contained in:
parent
a64a685e75
commit
872e2842b9
@ -2,4 +2,4 @@ package me.mrletsplay.shareclientcore.connection;
|
|||||||
|
|
||||||
import me.mrletsplay.shareclientcore.document.Char;
|
import me.mrletsplay.shareclientcore.document.Char;
|
||||||
|
|
||||||
public record Change(String documentPath, ChangeType type, Char character) {}
|
public record Change(ChangeType type, Char character) {}
|
||||||
|
@ -3,12 +3,14 @@ package me.mrletsplay.shareclientcore.connection.message;
|
|||||||
import java.io.DataInputStream;
|
import java.io.DataInputStream;
|
||||||
import java.io.DataOutputStream;
|
import java.io.DataOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
import me.mrletsplay.shareclientcore.connection.Change;
|
import me.mrletsplay.shareclientcore.connection.Change;
|
||||||
import me.mrletsplay.shareclientcore.connection.ChangeType;
|
import me.mrletsplay.shareclientcore.connection.ChangeType;
|
||||||
import me.mrletsplay.shareclientcore.document.Char;
|
import me.mrletsplay.shareclientcore.document.Char;
|
||||||
|
|
||||||
public record ChangeMessage(Change change) implements Message {
|
public record ChangeMessage(String documentPath, Change[] changes) implements Message {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MessageType getType() {
|
public MessageType getType() {
|
||||||
@ -17,14 +19,43 @@ public record ChangeMessage(Change change) implements Message {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void serialize(DataOutputStream out) throws IOException {
|
public void serialize(DataOutputStream out) throws IOException {
|
||||||
out.writeUTF(change.documentPath());
|
out.writeUTF(documentPath);
|
||||||
out.writeUTF(change.type().name());
|
out.writeInt(changes.length);
|
||||||
change.character().serialize(out);
|
for(int i = 0; i < changes.length; i++) {
|
||||||
|
out.writeUTF(changes[i].type().name());
|
||||||
|
changes[i].character().serialize(out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = prime * result + Arrays.hashCode(changes);
|
||||||
|
result = prime * result + Objects.hash(documentPath);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj)
|
||||||
|
return true;
|
||||||
|
if (obj == null)
|
||||||
|
return false;
|
||||||
|
if (getClass() != obj.getClass())
|
||||||
|
return false;
|
||||||
|
ChangeMessage other = (ChangeMessage) obj;
|
||||||
|
return Arrays.equals(changes, other.changes) && Objects.equals(documentPath, other.documentPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ChangeMessage deserialize(DataInputStream in) throws IOException {
|
public static ChangeMessage deserialize(DataInputStream in) throws IOException {
|
||||||
try {
|
try {
|
||||||
return new ChangeMessage(new Change(in.readUTF(), ChangeType.valueOf(in.readUTF()), Char.deserialize(in)));
|
String documentPath = in.readUTF();
|
||||||
|
Change[] changes = new Change[in.readInt()];
|
||||||
|
for(int i = 0; i < changes.length; i++) {
|
||||||
|
changes[i] = new Change(ChangeType.valueOf(in.readUTF()), Char.deserialize(in));
|
||||||
|
}
|
||||||
|
return new ChangeMessage(documentPath, changes);
|
||||||
}catch(IllegalArgumentException e) {
|
}catch(IllegalArgumentException e) {
|
||||||
throw new IOException("Invalid change type", e);
|
throw new IOException("Invalid change type", e);
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,7 @@ public class SharedDocument implements MessageListener {
|
|||||||
lamport++;
|
lamport++;
|
||||||
Char ch = new Char(newPos, lamport, bytes[i]);
|
Char ch = new Char(newPos, lamport, bytes[i]);
|
||||||
if(charBag.add(ch) == -1) throw new IllegalStateException("Couldn't insert newly created char");
|
if(charBag.add(ch) == -1) throw new IllegalStateException("Couldn't insert newly created char");
|
||||||
changes[i] = new Change(path, ChangeType.ADD, ch);
|
changes[i] = new Change(ChangeType.ADD, ch);
|
||||||
charBefore = ch;
|
charBefore = ch;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,14 +87,12 @@ public class SharedDocument implements MessageListener {
|
|||||||
public void localInsert(int index, String str) {
|
public void localInsert(int index, String str) {
|
||||||
Change[] changes = insert(index, str, site);
|
Change[] changes = insert(index, str, site);
|
||||||
|
|
||||||
for(Change c : changes) {
|
|
||||||
try {
|
try {
|
||||||
connection.send(new ChangeMessage(c));
|
connection.send(new ChangeMessage(path, changes));
|
||||||
} catch (ConnectionException e) {
|
} catch (ConnectionException e) {
|
||||||
e.printStackTrace(); // TODO: throw error
|
e.printStackTrace(); // TODO: throw error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes n characters from a document, starting at the specified index
|
* Deletes n characters from a document, starting at the specified index
|
||||||
@ -109,18 +107,16 @@ public class SharedDocument implements MessageListener {
|
|||||||
while(n-- > 0) {
|
while(n-- > 0) {
|
||||||
// TODO: more efficient implementation (e.g. range delete in CharBag)
|
// TODO: more efficient implementation (e.g. range delete in CharBag)
|
||||||
Char toRemove = charBag.get(index + 1);
|
Char toRemove = charBag.get(index + 1);
|
||||||
changes[n] = new Change(path, ChangeType.REMOVE, toRemove);
|
changes[n] = new Change(ChangeType.REMOVE, toRemove);
|
||||||
if(charBag.remove(toRemove) == -1) throw new IllegalStateException("Couldn't remove existing char");
|
if(charBag.remove(toRemove) == -1) throw new IllegalStateException("Couldn't remove existing char");
|
||||||
}
|
}
|
||||||
|
|
||||||
for(Change c : changes) {
|
|
||||||
try {
|
try {
|
||||||
connection.send(new ChangeMessage(c));
|
connection.send(new ChangeMessage(path, changes));
|
||||||
} catch (ConnectionException e) {
|
} catch (ConnectionException e) {
|
||||||
e.printStackTrace(); // TODO: throw error
|
e.printStackTrace(); // TODO: throw error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inserts a remote change into the document and updates internal parameters accordingly
|
* Inserts a remote change into the document and updates internal parameters accordingly
|
||||||
@ -191,14 +187,15 @@ public class SharedDocument implements MessageListener {
|
|||||||
@Override
|
@Override
|
||||||
public void onMessage(Message message) {
|
public void onMessage(Message message) {
|
||||||
if(message instanceof ChangeMessage change) {
|
if(message instanceof ChangeMessage change) {
|
||||||
Change c = change.change();
|
if(!change.documentPath().equals(path)) return;
|
||||||
if(!c.documentPath().equals(path)) return;
|
|
||||||
|
|
||||||
|
for(Change c : change.changes()) {
|
||||||
switch(c.type()) {
|
switch(c.type()) {
|
||||||
case ADD -> remoteInsert(c.character());
|
case ADD -> remoteInsert(c.character());
|
||||||
case REMOVE -> remoteDelete(c.character());
|
case REMOVE -> remoteDelete(c.character());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,6 @@ import org.junit.jupiter.params.ParameterizedTest;
|
|||||||
import org.junit.jupiter.params.provider.MethodSource;
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
|
|
||||||
import me.mrletsplay.shareclientcore.document.ArrayCharBag;
|
import me.mrletsplay.shareclientcore.document.ArrayCharBag;
|
||||||
import me.mrletsplay.shareclientcore.document.BinaryTreeCharBag;
|
|
||||||
import me.mrletsplay.shareclientcore.document.Char;
|
import me.mrletsplay.shareclientcore.document.Char;
|
||||||
import me.mrletsplay.shareclientcore.document.CharBag;
|
import me.mrletsplay.shareclientcore.document.CharBag;
|
||||||
import me.mrletsplay.shareclientcore.document.Identifier;
|
import me.mrletsplay.shareclientcore.document.Identifier;
|
||||||
@ -17,7 +16,7 @@ import me.mrletsplay.shareclientcore.document.Identifier;
|
|||||||
public class CharBagTest {
|
public class CharBagTest {
|
||||||
|
|
||||||
private static List<CharBag> getBags() {
|
private static List<CharBag> getBags() {
|
||||||
return List.of(new ArrayCharBag(), new BinaryTreeCharBag());
|
return List.of(new ArrayCharBag()/*, new BinaryTreeCharBag() TODO implement BinaryTreeCharBag */);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
|
@ -42,8 +42,12 @@ public class MessageTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testChangeMessage() throws IOException {
|
public void testChangeMessage() throws IOException {
|
||||||
Change change = new Change("Project:src/test.txt", ChangeType.ADD, new Char(new Identifier[] {new Identifier(0, 1), new Identifier(1, 3)}, 42, (byte) 'e'));
|
Change[] changes = {
|
||||||
ChangeMessage m = new ChangeMessage(change);
|
new Change(ChangeType.ADD, new Char(new Identifier[] {new Identifier(0, 1), new Identifier(1, 3)}, 42, (byte) 'e')),
|
||||||
|
new Change(ChangeType.REMOVE, new Char(new Identifier[] {new Identifier(2, 1), new Identifier(1, 4)}, 314, (byte) 'q')),
|
||||||
|
|
||||||
|
};
|
||||||
|
ChangeMessage m = new ChangeMessage("Project:src/test.txt", changes);
|
||||||
assertEquals(deserialize(serialize(m)), m, "Deserialized message must equal message");
|
assertEquals(deserialize(serialize(m)), m, "Deserialized message must equal message");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user