1 /** 2 * Copyright (C) 2013 KO GmbH <copyright@kogmbh.com> 3 * 4 * @licstart 5 * This file is part of WebODF. 6 * 7 * WebODF is free software: you can redistribute it and/or modify it 8 * under the terms of the GNU Affero General Public License (GNU AGPL) 9 * as published by the Free Software Foundation, either version 3 of 10 * the License, or (at your option) any later version. 11 * 12 * WebODF is distributed in the hope that it will be useful, but 13 * WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU Affero General Public License for more details. 16 * 17 * You should have received a copy of the GNU Affero General Public License 18 * along with WebODF. If not, see <http://www.gnu.org/licenses/>. 19 * @licend 20 * 21 * @source: http://www.webodf.org/ 22 * @source: https://github.com/kogmbh/WebODF/ 23 */ 24 25 /*global runtime, ops */ 26 27 /* 28 * create specific operation instances. 29 */ 30 31 32 /** 33 * @constructor 34 */ 35 ops.OperationTransformer = function OperationTransformer() { 36 "use strict"; 37 38 var operationTransformMatrix = new ops.OperationTransformMatrix(); 39 40 /** 41 * TODO: priority could be read from op spec, here be an attribute from-server 42 * @param {!{optype:string}} opSpecA op with lower priority in case of tie breaking 43 * @param {!{optype:string}} opSpecB op with higher priority in case of tie breaking 44 * @return {?{opSpecsA:!Array.<!{optype:string}>, 45 * opSpecsB:!Array.<!{optype:string}>}} 46 */ 47 function transformOpVsOp(opSpecA, opSpecB) { 48 return operationTransformMatrix.transformOpspecVsOpspec(opSpecA, opSpecB); 49 } 50 51 /** 52 * @param {!Array.<!{optype:string}>} opSpecsA sequence of ops with lower priority in case of tie breaking 53 * @param {?{optype:string}} opSpecB op with higher priority in case of tie breaking 54 * @return {?{opSpecsA:!Array.<!{optype:string}>, 55 * opSpecsB:!Array.<!Object>}} 56 */ 57 function transformOpListVsOp(opSpecsA, opSpecB) { 58 var transformResult, transformListResult, 59 transformedOpspecsA = [], 60 transformedOpspecsB = []; 61 62 while (opSpecsA.length > 0 && opSpecB) { 63 transformResult = transformOpVsOp(opSpecsA.shift(), opSpecB); 64 // unresolvable operation conflicts? 65 if (!transformResult) { 66 return null; 67 } 68 69 // take transformed ops of the list A 70 transformedOpspecsA = transformedOpspecsA.concat(transformResult.opSpecsA); 71 72 // handle transformed ops of the single op B 73 // opB became a noop? 74 if (transformResult.opSpecsB.length === 0) { 75 // so rest of opsAs stay unchanged, nothing else to do 76 transformedOpspecsA = transformedOpspecsA.concat(opSpecsA); 77 opSpecB = null; 78 break; 79 } 80 // in case of opspecB transformed into multiple ops, 81 // transform the remaining opsAs against any additional opsBs 82 // so we can continue as if there is only one opB 83 while (transformResult.opSpecsB.length > 1) { 84 transformListResult = transformOpListVsOp(opSpecsA, transformResult.opSpecsB.shift()); 85 // unresolvable operation conflicts? 86 if (!transformListResult) { 87 return null; 88 } 89 // take transformed ops of the single b 90 transformedOpspecsB = transformedOpspecsB.concat(transformListResult.opSpecsB); 91 opSpecsA = transformListResult.opSpecsA; 92 } 93 // continue with last of transformed opsB 94 opSpecB = transformResult.opSpecsB.pop(); 95 } 96 97 if (opSpecB) { 98 transformedOpspecsB.push(opSpecB); 99 } 100 return { 101 opSpecsA: transformedOpspecsA, 102 opSpecsB: transformedOpspecsB 103 }; 104 } 105 106 /** 107 * @return {!ops.OperationTransformMatrix} 108 */ 109 this.getOperationTransformMatrix = function () { 110 return operationTransformMatrix; 111 }; 112 113 /** 114 * @param {!Array.<!Object>} opSpecsA sequence of opspecs with lower priority in case of tie breaking 115 * @param {!Array.<!{optype:string}>} opSpecsB opspecs with higher priority in case of tie breaking 116 * @return {?{opSpecsA:!Array.<!Object>, 117 * opSpecsB:!Array.<!Object>}} 118 */ 119 this.transform = function (opSpecsA, opSpecsB) { 120 var transformResult, 121 transformedOpspecsB = []; 122 123 // transform all opSpecsB vs. all unsent client ops 124 while (opSpecsB.length > 0) { 125 transformResult = transformOpListVsOp(opSpecsA, opSpecsB.shift()); 126 // unresolvable operation conflicts? 127 if (!transformResult) { 128 return null; 129 } 130 131 opSpecsA = transformResult.opSpecsA; 132 transformedOpspecsB = transformedOpspecsB.concat(transformResult.opSpecsB); 133 } 134 135 return { 136 opSpecsA: /**@type{!Array.<!Object>}*/(opSpecsA), 137 opSpecsB: /**@type{!Array.<!Object>}*/(transformedOpspecsB) 138 }; 139 }; 140 }; 141