55
66public abstract class TextChange <S , Self extends TextChange <S , Self >> {
77
8+ public static enum ChangeType {
9+ /** Indicates that the change will insert something but not remove anything */
10+ INSERTION ,
11+ /** Indicates that the change will delete something but not insert anything */
12+ DELETION ,
13+ /** Indicates that the change will remove something and insert something as its replacement */
14+ REPLACEMENT
15+ }
16+
17+ private ChangeType type ;
18+ public final ChangeType getType () {
19+ if (type == null ) {
20+ if (insertedLength () == 0 ) {
21+ if (removedLength () == 0 ) {
22+ throw new IllegalStateException ("Cannot get the type of a change that neither inserts nor deletes anything." );
23+ } else {
24+ type = ChangeType .DELETION ;
25+ }
26+ } else if (removedLength () == 0 ) {
27+ type = ChangeType .INSERTION ;
28+ } else {
29+ type = ChangeType .REPLACEMENT ;
30+ }
31+ }
32+ return type ;
33+ }
34+
835 protected final int position ;
936 protected final S removed ;
1037 protected final S inserted ;
@@ -29,35 +56,21 @@ public TextChange(int position, S removed, S inserted) {
2956 protected abstract Self create (int position , S removed , S inserted );
3057
3158 /**
32- * Merges this change with the given change, if possible.
33- * This change is considered to be the former and the given
34- * change is considered to be the latter.
35- * Changes can be merged if either
36- * <ul>
37- * <li>the latter's start matches the former's added text end; or</li>
38- * <li>the latter's removed text end matches the former's added text end.</li>
39- * </ul>
59+ * Merges this change with the given change only if the end of this change's inserted text
60+ * equals the latter's position and both are either insertion or deletion changes.
61+ *
4062 * @param latter change to merge with this change.
4163 * @return a new merged change if changes can be merged,
4264 * {@code null} otherwise.
4365 */
4466 public Optional <Self > mergeWith (Self latter ) {
45- if (latter .position == this .position + this .insertedLength ()) {
67+ if (this .getType () != ChangeType .REPLACEMENT
68+ && this .getType () == latter .getType ()
69+ && this .getInsertionEnd () == latter .position ) {
4670 S removedText = concat (this .removed , latter .removed );
4771 S addedText = concat (this .inserted , latter .inserted );
4872 return Optional .of (create (this .position , removedText , addedText ));
49- }
50- else if (latter .position + latter .removedLength () == this .position + this .insertedLength ()) {
51- if (this .position <= latter .position ) {
52- S addedText = concat (sub (this .inserted , 0 , latter .position - this .position ), latter .inserted );
53- return Optional .of (create (this .position , this .removed , addedText ));
54- }
55- else {
56- S removedText = concat (sub (latter .removed , 0 , this .position - latter .position ), this .removed );
57- return Optional .of (create (latter .position , removedText , latter .inserted ));
58- }
59- }
60- else {
73+ } else {
6174 return Optional .empty ();
6275 }
6376 }
@@ -83,9 +96,10 @@ public int hashCode() {
8396 public final String toString () {
8497 return
8598 this .getClass ().getSimpleName () + "{\n " +
86- "\t position: " + position + "\n " +
87- "\t removed: " + removed + "\n " +
88- "\t inserted: " + inserted + "\n " +
99+ "\t position: " + position + "\n " +
100+ "\t type: " + getType () + "\n " +
101+ "\t removed: " + removed + "\n " +
102+ "\t inserted: " + inserted + "\n " +
89103 "}" ;
90104 }
91105}
0 commit comments