| 127 | | TODO: Explain possible transformation cases including the need of SplitOperation and NoOperation. |
| | 127 | ''obby_document:1 1:record:0:0:ins:2:a'' |
| | 128 | |
| | 129 | and get |
| | 130 | |
| | 131 | {{{obby_document:1 1:record:0:0:ins:0:bar}}} |
| | 132 | |
| | 133 | This time, we must include the effect of ins(a@2) into ins(bar@0). Since 'a' was inserted behind 'bar', nothing has to be done. But imagine we now get a second record: |
| | 134 | |
| | 135 | {{{obby_document:1 1:record:1:0:ins:3:baz}}} |
| | 136 | |
| | 137 | The remote operation count is still zero, so when the server wrote 'baz', he did still not get our 'a' and intended to insert 'baz' right behind the 'bar' he inserted beforehand. But when we include the effect of ins(a@2) into ins(baz@3), we get ins(baz@4). So, when the document's initial content was 'foo', we first made 'foao' out of it, then ins(bar@0) is applied resulting in 'barfoao' and after this ins(baz@4), leading to a final content of 'barfbazao'. It is easy to see that this is not what the server intended since it inserted 'baz' right after 'bar' without the 'f' inbetween. So what went wrong here? |
| | 138 | |
| | 139 | So, when the server sent the first record ins(bar@0), we did not transform anything, which was right, because our 'a' has been inserted behind the server's 'bar'. However, this caused the 'a' to move 3 characters forward. So we also should have included the effect of ins(bar@0) into ins(a@2), and not only the other way around. This way, ins(a@2) turns into ins(a@5). As soon as the second record ins(baz@3) arrives and we include the effect of ins(a@5) into ins(baz@3), it still remains ins(baz@3) because ins(a@5) is now behind it. But notice that, again, we have also to include the effect of ins(baz@3) into ins(a@5), resulting in ins(a@8), to be prepared for the case when the server sends a third record with a zero remote operation count. |
| | 140 | |
| | 141 | === Why ins and del is not enough === |
| | 142 | |
| | 143 | In fact, ins and del are not the only required operations. There are two other operations known as NoOperation (noop) and SplitOperation (split). These are not generated directly in response of user input but might result from inclusion transformations. A NoOperation is just an operation that does nothing and therefore has no parameters. A SplitOperation is simply a wrapper around two operations. |
| | 144 | |
| | 145 | Obviously the NoOperation is the simpler case, so I will begin to explain this one. Let's say, we have a document whose content is 'foobar'. We now delete some characters to make 'far' out of it by sending |
| | 146 | |
| | 147 | ''obby_document:1 1:record:0:0:del:1:3'' |
| | 148 | |
| | 149 | to the server. The server decided to delete only 'oo' and sent |
| | 150 | |
| | 151 | {{{obby_document:1 1:record:0:0:del:1:2'' |
| | 152 | |
| | 153 | When we get this operation we notice that the server deleted 'oo' before he got our deletion of 'oob'. Therefore, we have to include the effect of del(1,3) into del(1,2). When we delete three characters starting at index 1, the first to characters starting at index 1 are already deleted, so we have to do nothing, because we already deleted ourselves what the server deleted. The result of this transformation is therefore a noop. This happens every time when we receive a delete operation that deletes a range that is already deleted. |
| | 154 | |
| | 155 | The SplitOperation is required when one inserts text into a range of text that has to deleted. Consider the following example (again, the initial content is 'foobar'): |
| | 156 | |
| | 157 | ''obby_doucment:1 1:record:0:0:ins:3:bal'' |
| | 158 | {{{obby_document:1 1:record:0:0:del:0:6}}} |
| | 159 | |
| | 160 | so the server deletes all six characters in the document, resulting in an empty document while we inserted 'bal' at position 3 which turns to 'foobalbar'. Now we receive the delete record from the server. Obviously the server wants to delete 'foobar', so we cannot just perform del(0,6) because it would be 'bar' what remains and not 'bal'. When transforming the incoming operation, it has to be splitted up into two delete operations: del(0,3) and del(6,9) which deletes the original 'foobar'. |