41 lines
1.6 KiB
Java
41 lines
1.6 KiB
Java
package de.neitzel.fxdesigner.contracts;
|
|
|
|
import java.util.*;
|
|
|
|
/** One named undo transaction. All UTF-16 ranges address the same expected revision. */
|
|
public record EditBatch(long expectedRevision, String name, List<Replacement> replacements) {
|
|
public record Replacement(int start, int end, String text) {
|
|
public Replacement {
|
|
if (start < 0 || end < start) throw new IllegalArgumentException("Invalid range");
|
|
Objects.requireNonNull(text);
|
|
}
|
|
}
|
|
|
|
public EditBatch {
|
|
if (expectedRevision < 0) throw new IllegalArgumentException("Negative revision");
|
|
if (name == null || name.isBlank()) throw new IllegalArgumentException("Missing command name");
|
|
replacements = List.copyOf(replacements);
|
|
}
|
|
|
|
/** Validates every range before computing a result; never partially edits a host draft. */
|
|
public String applyTo(DocumentSnapshot draft) {
|
|
if (!draft.writable()) throw new IllegalStateException("Document is read-only");
|
|
if (draft.revision() != expectedRevision)
|
|
throw new ConcurrentModificationException("Stale document revision");
|
|
var ordered = new ArrayList<>(replacements);
|
|
ordered.sort(Comparator.comparingInt(Replacement::start));
|
|
int previousEnd = -1;
|
|
for (var r : ordered) {
|
|
if (r.end() > draft.text().length() || r.start() < previousEnd)
|
|
throw new IllegalArgumentException("Overlapping/out-of-bounds edits");
|
|
previousEnd = r.end();
|
|
}
|
|
var result = new StringBuilder(draft.text());
|
|
for (int i = ordered.size() - 1; i >= 0; i--) {
|
|
var r = ordered.get(i);
|
|
result.replace(r.start(), r.end(), r.text());
|
|
}
|
|
return result.toString();
|
|
}
|
|
}
|