Skip to main content
summaryrefslogtreecommitdiffstats
blob: fe5d11c4d3444d8fa13617edda750d66f0d9b25b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
/****************************************************************************
 * Copyright (c) 2008 Mustafa K. Isik and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Mustafa K. Isik - initial API and implementation
 *****************************************************************************/

package org.eclipse.ecf.docshare.cola;

import org.eclipse.ecf.core.util.Trace;
import org.eclipse.ecf.docshare.messages.UpdateMessage;
import org.eclipse.ecf.internal.docshare.Activator;
import org.eclipse.ecf.internal.docshare.DocshareDebugOptions;

public class ColaDeletion implements TransformationStrategy {

	private static final long serialVersionUID = -7430435392915553959L;
	private static ColaDeletion INSTANCE;

	private ColaDeletion() {
		// default constructor is private to enforce singleton property via
		// static factory method
	}

	public static TransformationStrategy getInstance() {
		if (INSTANCE == null) {
			INSTANCE = new ColaDeletion();
		}
		return INSTANCE;
	}

	public ColaUpdateMessage getOperationalTransform(ColaUpdateMessage remoteIncomingMsg, ColaUpdateMessage localAppliedMsg, boolean localMsgHighPrio) {

		Trace.entering(Activator.PLUGIN_ID, DocshareDebugOptions.METHODS_ENTERING, this.getClass(), "getOperationalTransform", new Object[] {remoteIncomingMsg, localAppliedMsg, new Boolean(localMsgHighPrio)}); //$NON-NLS-1$

		ColaUpdateMessage remoteTransformedMsg = remoteIncomingMsg;

		if (localAppliedMsg.isDeletion()) {
			final int noOpLength = 0;

			if (remoteTransformedMsg.getOffset() < localAppliedMsg.getOffset()) {

				if ((remoteTransformedMsg.getOffset() + remoteTransformedMsg.getLengthOfReplacedText()) < localAppliedMsg.getOffset()) {

					//no overlap - remote OK as is, local needs modification
					localAppliedMsg.setOffset(localAppliedMsg.getOffset() - remoteTransformedMsg.getLengthOfReplacedText());

				} else if ((remoteTransformedMsg.getOffset() + remoteTransformedMsg.getLengthOfReplacedText()) < (localAppliedMsg.getOffset() + localAppliedMsg.getLengthOfReplacedText())) {

					//remote del at lower index reaches into locally applied del, local del reaches further out
					//--> shorten remote del appropriately, move and shorten local del left
					remoteTransformedMsg.setLengthOfReplacedText((remoteTransformedMsg.getOffset() + remoteTransformedMsg.getLengthOfReplacedText()) - localAppliedMsg.getOffset());
					localAppliedMsg.setLengthOfReplacedText((remoteTransformedMsg.getOffset() + remoteTransformedMsg.getLengthOfReplacedText()) - localAppliedMsg.getOffset());
					localAppliedMsg.setOffset(remoteTransformedMsg.getOffset());//TODO verify!

				} else if ((remoteTransformedMsg.getOffset() + remoteTransformedMsg.getLengthOfReplacedText()) >= (localAppliedMsg.getOffset() + localAppliedMsg.getLengthOfReplacedText())) {

					//remote del at lower index, remote del fully extends over local del
					//--> shorten remote by local.lengthOfReplacedText, make local no-op
					remoteTransformedMsg.setLengthOfReplacedText(remoteTransformedMsg.getLengthOfReplacedText() - localAppliedMsg.getLengthOfReplacedText());
					//TODO verify whether this is equivalent to setting no-op, otherwise declare a noop boolean field for ColaDeletions
					localAppliedMsg.setOffset(remoteTransformedMsg.getOffset());
					localAppliedMsg.setLengthOfReplacedText(noOpLength);
				}
			} else if (remoteTransformedMsg.getOffset() == localAppliedMsg.getOffset()) {

				if ((remoteTransformedMsg.getOffset() + remoteTransformedMsg.getLengthOfReplacedText()) < (localAppliedMsg.getOffset() + localAppliedMsg.getLengthOfReplacedText())) {
					//start indices are equal, remote is shorter --> make remote no-op
					remoteTransformedMsg.setLengthOfReplacedText(noOpLength);
					//--> shorten localOp to only delete non-overlapping region
					localAppliedMsg.setLengthOfReplacedText(localAppliedMsg.getLengthOfReplacedText() - remoteTransformedMsg.getLengthOfReplacedText());
				} else if ((remoteTransformedMsg.getOffset() + remoteTransformedMsg.getLengthOfReplacedText()) == (localAppliedMsg.getOffset() + localAppliedMsg.getLengthOfReplacedText())) {
					//same endIndex, i.e. same deletion
					//--> make remote op be no-op
					remoteTransformedMsg.setLengthOfReplacedText(noOpLength);
					//--> make local op appear as no-op
					localAppliedMsg.setLengthOfReplacedText(noOpLength);
				} else if ((remoteTransformedMsg.getOffset() + remoteTransformedMsg.getLengthOfReplacedText()) > (localAppliedMsg.getOffset() + localAppliedMsg.getLengthOfReplacedText())) {
					//remote del extends over local del
					//-->shorten remote del by length of local del, index/offset does not need to be updated
					remoteTransformedMsg.setLengthOfReplacedText(remoteTransformedMsg.getLengthOfReplacedText() - localAppliedMsg.getLengthOfReplacedText());
					//-->make local del appear as no-op
					localAppliedMsg.setLengthOfReplacedText(noOpLength);
				}
			} else if (remoteTransformedMsg.getOffset() > localAppliedMsg.getOffset()) {

				if (remoteTransformedMsg.getOffset() > (localAppliedMsg.getOffset() + localAppliedMsg.getLengthOfReplacedText())) {

					//move remote deletion left by length of local deletion
					remoteTransformedMsg.setOffset(remoteTransformedMsg.getOffset() - localAppliedMsg.getLengthOfReplacedText());

				} else if ((remoteTransformedMsg.getOffset() + remoteTransformedMsg.getLengthOfReplacedText()) < (localAppliedMsg.getOffset() + localAppliedMsg.getLengthOfReplacedText())) {

					//remote is fully contained in/overlapping with local del
					//--> remote is no-op
					remoteTransformedMsg.setLengthOfReplacedText(noOpLength);
					//-->local needs to be shortened by length of remote
					localAppliedMsg.setLengthOfReplacedText(localAppliedMsg.getLengthOfReplacedText() - remoteTransformedMsg.getLengthOfReplacedText());

				} else if (remoteTransformedMsg.getOffset() < (localAppliedMsg.getOffset() + localAppliedMsg.getLengthOfReplacedText())) {

					//remote del starts within local del, but extends further
					//-->shorten remote by overlap and move left to index of local del
					remoteTransformedMsg.setLengthOfReplacedText(remoteTransformedMsg.getLengthOfReplacedText() - (localAppliedMsg.getOffset() + localAppliedMsg.getLengthOfReplacedText()) - remoteTransformedMsg.getOffset());
					remoteTransformedMsg.setOffset(localAppliedMsg.getOffset());
					//-->shorten local applied message
					localAppliedMsg.setLengthOfReplacedText(localAppliedMsg.getLengthOfReplacedText() - (localAppliedMsg.getOffset() + localAppliedMsg.getLengthOfReplacedText()) - remoteTransformedMsg.getOffset());
				}
			}
		} else if (localAppliedMsg.isInsertion()) {
			if (remoteTransformedMsg.getOffset() < localAppliedMsg.getOffset()) {
				if ((remoteTransformedMsg.getOffset() + remoteTransformedMsg.getLengthOfReplacedText()) < localAppliedMsg.getOffset()) {
					//remote remains unchanged, deletion happens fully before local insertion
					//local insertion needs to be moved left by full length of deletion
					localAppliedMsg.setOffset(localAppliedMsg.getOffset() - remoteTransformedMsg.getLengthOfReplacedText());
				} else if ((remoteTransformedMsg.getOffset() + remoteTransformedMsg.getLengthOfReplacedText()) >= localAppliedMsg.getOffset()) { //TODO optimize away, "if" just here for clarity, "else" would be enough
					//remote deletion reaches into local insertion and potentially over it
					//remote deletion needs to be split apart
					UpdateMessage deletionFirstMsg = new UpdateMessage(remoteTransformedMsg.getOffset(), localAppliedMsg.getOffset() - remoteTransformedMsg.getOffset(), remoteTransformedMsg.getText());
					ColaUpdateMessage deletionFirstPart = new ColaUpdateMessage(deletionFirstMsg, remoteTransformedMsg.getLocalOperationsCount(), remoteTransformedMsg.getRemoteOperationsCount());
					remoteTransformedMsg.addToSplitUpRepresentation(deletionFirstPart);

					UpdateMessage deletionSecondMsg = new UpdateMessage(localAppliedMsg.getOffset() + localAppliedMsg.getLengthOfInsertedText(), remoteTransformedMsg.getLengthOfReplacedText() - deletionFirstPart.getLengthOfReplacedText(), remoteTransformedMsg.getText());
					ColaUpdateMessage deletionSecondPart = new ColaUpdateMessage(deletionSecondMsg, remoteTransformedMsg.getLocalOperationsCount(), remoteTransformedMsg.getRemoteOperationsCount());
					remoteTransformedMsg.addToSplitUpRepresentation(deletionSecondPart);

					remoteTransformedMsg.setSplitUp(true);

					//local insertion needs to be moved left by overlap
					localAppliedMsg.setOffset(remoteTransformedMsg.getOffset());

				}
			} else if (remoteTransformedMsg.getOffset() >= localAppliedMsg.getOffset()) {
				//remote del needs to be moved right by full length of insertion
				remoteTransformedMsg.setOffset(remoteTransformedMsg.getOffset() + localAppliedMsg.getLengthOfInsertedText());
			}
		}

		Trace.exiting(Activator.PLUGIN_ID, DocshareDebugOptions.METHODS_EXITING, this.getClass(), "getOperationalTransform", null); //$NON-NLS-1$

		return remoteTransformedMsg;
	}

}

Back to the top