390
|
1 package rep.handler;
|
|
2
|
|
3 import java.util.Comparator;
|
|
4 import java.util.LinkedList;
|
|
5 import java.util.List;
|
|
6 import java.util.TreeSet;
|
|
7
|
|
8 import rep.REP;
|
|
9 import rep.REPCommand;
|
407
|
10 import rep.SessionManager;
|
421
|
11 import rep.channel.REPLogger;
|
390
|
12 import rep.optimizers.REPCommandOptimizer;
|
|
13
|
|
14 public class Translator {
|
|
15 public int eid;
|
|
16
|
|
17 public REPCommandOptimizer optimizer;
|
|
18 private LinkedList<REPCommand> unMergedCmds;
|
438
|
19 private LinkedList<REPCommand> sentMergedList;
|
445
|
20 private TreeSet<REPCommand> sortedEditCmds;
|
431
|
21 boolean mergeAgain;
|
421
|
22 public REPLogger logger = SessionManager.logger;
|
390
|
23 boolean merge_mode = false;
|
|
24
|
|
25 public Translator(int _eid,REPCommandOptimizer opt){
|
|
26 eid = _eid;
|
|
27 optimizer = opt;
|
|
28 unMergedCmds = new LinkedList<REPCommand>();
|
431
|
29 mergeAgain = false;
|
390
|
30 sentMergedList = new LinkedList<REPCommand>();
|
|
31 }
|
|
32
|
|
33 /**
|
|
34 * New command from an editor
|
|
35 * The command is sent to the next editor
|
|
36 * @param cmd
|
|
37 * @return translated command.
|
|
38 */
|
|
39 public REPCommand transSendCmd(REPCommand cmd){
|
|
40 assert(cmd.eid==eid);
|
431
|
41 unMergedCmds.addLast(cmd);
|
390
|
42
|
|
43 //マージ中にユーザから割り込みがあった場合
|
|
44 if(isMerging()){
|
431
|
45 mergeAgain = true;
|
390
|
46 }
|
|
47
|
|
48 return cmd;
|
|
49 }
|
431
|
50
|
390
|
51 /**
|
|
52 * My command is returned from the session ring, and START_MERGE_ACK
|
|
53 * is returned. At this
|
|
54 * stage my writeQueue is empty, our editor is waiting for me.
|
|
55 * Start merge process.
|
|
56 * @param cmd
|
|
57 */
|
435
|
58 public boolean catchOwnCommand(REPNode editor, REPCommand prev){
|
442
|
59 logger.writeLog("beforeMerge"+eid+":"+unMergedCmds);
|
390
|
60 LinkedList<REPCommand> output = new LinkedList<REPCommand>();
|
427
|
61 // merge queue上にあるコマンドを全部undoコマンドするのと同時に
|
|
62 // sort したコマンド列を生成する
|
|
63 for( REPCommand cmd0 : unMergedCmds) {
|
390
|
64 output.add( createUndo(cmd0) );
|
431
|
65 }
|
434
|
66
|
445
|
67 sortedEditCmds = new TreeSet<REPCommand>(new REPCommandComparator(1));
|
446
|
68 logger.writeLog("sentList"+eid+":"+editor.getSentList());
|
449
|
69 boolean merged = true;
|
431
|
70 for( REPCommand cmd0 : editor.getSentList()) {
|
449
|
71 if (cmd0.cmd==REP.SMCMD_START_MERGE) {
|
|
72 merged = false;
|
|
73 continue;
|
|
74 }
|
|
75 if (merged) continue;
|
433
|
76 if (cmd0.cmd==REP.REPCMD_INSERT || cmd0.cmd==REP.REPCMD_DELETE)
|
445
|
77 sortedEditCmds.add(cmd0);
|
390
|
78 }
|
445
|
79 logger.writeLog("sortedMerge"+eid+":"+sortedEditCmds);
|
442
|
80 // logger.writeLog("Ediotr"+eid+" Merge:: sorted sent list => Eid="+eid+cmds+" ack="+prev);
|
445
|
81 output.addAll(sortedEditCmds);
|
427
|
82 // ACKが来たものは必ず先頭
|
431
|
83
|
|
84 // unMerged command のdeleteのundo string は、この時点で使えない。
|
|
85 // Editor 側から送り返して来たものを使う必要がある。
|
|
86 unMergedCmds.clear();
|
442
|
87 logger.writeLog("outputMerge"+eid+":"+output);
|
390
|
88 return optimizedSend(editor,output);
|
|
89 }
|
|
90
|
|
91 /**
|
446
|
92 * Received all merge command ack
|
|
93 */
|
|
94 public void endMerge() {
|
|
95 sortedEditCmds = null;
|
449
|
96 unMergedCmds = new LinkedList<REPCommand>();
|
446
|
97 }
|
|
98 /**
|
390
|
99 * Sent optimized merged command list
|
|
100 * @param editor
|
|
101 * @param output
|
|
102 * @return if any sent commands output
|
|
103 */
|
|
104 public boolean optimizedSend(REPNode editor, LinkedList<REPCommand> output) {
|
447
|
105 /*
|
|
106 * Optimized send の場合は、unMergedCommand のつじつまを合わせる必要がある。
|
|
107 */
|
439
|
108 sentMergedList.clear();
|
390
|
109 List<REPCommand> output1 = optimizer.optimize(output);
|
|
110 if (output1.size()==0) {
|
|
111 merge_mode = false;
|
|
112 return false;
|
|
113 }
|
|
114 for(REPCommand c:output1) {
|
|
115 REPCommand m = new REPCommand(c);
|
|
116 m.setEID(REP.MERGE_EID.id);
|
|
117 m.setSEQID(editor.seq());
|
441
|
118 sentMergedList.addLast(m);
|
450
|
119 editor.sendToEditor(m);
|
441
|
120 }
|
442
|
121 logger.writeLog("OptimizedOutputMerge"+eid+":"+sentMergedList);
|
390
|
122 merge_mode = true;
|
|
123 return true;
|
|
124 }
|
|
125
|
|
126 private REPCommand createUndo(REPCommand cmd){
|
|
127 REPCommand retCmd = new REPCommand(cmd);
|
445
|
128 if (cmd.cmd==REP.REPCMD_INSERT) {
|
|
129 retCmd.cmd=REP.REPCMD_DELETE;
|
|
130 retCmd.string="";
|
|
131 }
|
390
|
132 else if (cmd.cmd==REP.REPCMD_DELETE) retCmd.cmd=REP.REPCMD_INSERT;
|
|
133 return retCmd;
|
|
134 }
|
|
135
|
|
136 class REPCommandComparator implements Comparator<REPCommand>{
|
431
|
137 int base;
|
|
138 REPCommandComparator(int base) {
|
|
139 this.base = base;
|
|
140 }
|
390
|
141 public int compare(REPCommand o1, REPCommand o2) {
|
431
|
142 int eid1 = o1.eid-base; if (eid1<0) eid1 += Integer.MAX_VALUE;
|
|
143 int eid2 = o2.eid-base; if (eid2<0) eid2 += Integer.MAX_VALUE;
|
|
144 if ( eid1<eid2 ) return -1;
|
|
145 if ( eid1>eid2 ) return 1;
|
427
|
146 if ( o1.seq<o2.seq ) return -1;
|
|
147 if ( o1.seq>o2.seq ) return 1;
|
445
|
148 // assert(false); // this can happen in MergedAgain case
|
427
|
149 return 0;
|
390
|
150 }
|
|
151 }
|
|
152
|
|
153 /**
|
439
|
154 * Translate Command that was received from SeMa.
|
390
|
155 * @param cmd the command to be translated.
|
439
|
156 * @return translated command.
|
390
|
157 */
|
|
158 public void transReceiveCmd(REPNode nextEditor,REPCommand cmd){
|
|
159 assert (cmd.eid != eid);
|
433
|
160 unMergedCmds.addFirst(cmd);
|
390
|
161 }
|
|
162
|
|
163 public void setEid(int _eid){
|
|
164 eid = _eid;
|
|
165 }
|
|
166
|
|
167 public boolean checkMergeConflict(REPCommand command) {
|
433
|
168 unMergedCmds.addFirst(command);
|
390
|
169
|
441
|
170 REPCommand prev = sentMergedList.getFirst();
|
|
171 if (prev.seq==command.seq) {
|
442
|
172 // logger.writeLog("Input eid="+eid+"SentMergedList = "+sentMergedList);
|
441
|
173 sentMergedList.removeFirst();
|
442
|
174 }
|
|
175 // previous merge command may be returned
|
441
|
176
|
|
177 if(sentMergedList.size()==0 && !mergeAgain) {
|
390
|
178 merge_mode=false;
|
|
179 }
|
441
|
180 return mergeAgain;
|
390
|
181 }
|
|
182
|
|
183 public void getMergeAgain(REPNode editor) {
|
441
|
184 if (sentMergedList.size()>0) return; // wait for previous merge completion
|
|
185
|
390
|
186 LinkedList<REPCommand> returnCommand = new LinkedList<REPCommand>();
|
431
|
187 LinkedList<REPCommand> merge = new LinkedList<REPCommand>();
|
|
188 LinkedList<REPCommand> conflict = new LinkedList<REPCommand>();
|
|
189 for(REPCommand command : unMergedCmds) {
|
|
190 returnCommand.add(createUndo(command));
|
390
|
191 }
|
431
|
192 for(REPCommand command : unMergedCmds) {
|
390
|
193 if(command.eid == REP.MERGE_EID.id){
|
431
|
194 merge.addLast(command);
|
445
|
195 sortedEditCmds.add(command);
|
390
|
196 }
|
|
197 }
|
431
|
198 for(REPCommand command : unMergedCmds){
|
390
|
199 if(command.eid == eid){
|
|
200 command.eid = REP.MERGE_EID.id;
|
431
|
201 conflict.addLast(command);
|
390
|
202 }
|
|
203 }
|
431
|
204 unMergedCmds.clear();
|
|
205 returnCommand.addAll(merge);
|
|
206 returnCommand.addAll(conflict);
|
441
|
207 logger.writeLog("MergeAgain "+eid+" ret="+returnCommand.size());
|
431
|
208 mergeAgain = false;
|
390
|
209 optimizedSend(editor, returnCommand);
|
|
210 }
|
445
|
211 //
|
|
212 // public boolean isFinished() {
|
|
213 // if(unMergedCmds.size() > 0) return false;
|
|
214 // if(sentMergedList.size() > 0) return false;
|
|
215 // return true;
|
|
216 // }
|
390
|
217
|
|
218 public boolean isMerging() {
|
|
219 return merge_mode;
|
|
220 }
|
|
221
|
|
222 public void startMerge(REPCommand cmd) {
|
|
223 }
|
|
224
|
445
|
225 /**
|
|
226 * receive SMCMD_START_MERGE_ACK
|
|
227 */
|
390
|
228 public void mergeAck() {
|
456
|
229 logger.writeLog("Editor"+eid+": START MERGE "+
|
|
230 ((unMergedCmds.size()>0)?" and top of unMergedCmds = "+ unMergedCmds.getLast():""));
|
|
231 merge_mode = true;
|
390
|
232 }
|
|
233
|
445
|
234
|
390
|
235
|
|
236
|
|
237 }
|