417
|
1 package test.editortest;
|
|
2
|
|
3 import java.io.IOException;
|
|
4 import java.net.InetSocketAddress;
|
|
5 import java.nio.channels.SelectionKey;
|
|
6 import java.util.LinkedList;
|
|
7 import rep.REP;
|
|
8 import rep.REPCommand;
|
|
9 import rep.REPCommandPacker;
|
|
10 import rep.channel.REPSelectionKey;
|
|
11 import rep.channel.REPSelector;
|
|
12 import rep.channel.REPSocketChannel;
|
|
13
|
|
14
|
|
15 public class REPEditor extends Thread implements REPTextListener{
|
|
16
|
|
17 private REPSocketChannel<REPCommand> channel;
|
|
18 REPSelector<REPCommand> selector;
|
|
19 private boolean running = true;
|
|
20 private boolean inputLock = false;
|
|
21 private long timeout = 1;
|
|
22 private int syncCounter = 0;
|
|
23 private LinkedList<REPCommand> userCommand = new LinkedList<REPCommand>();
|
|
24 private LinkedList<Runnable> runners = new LinkedList<Runnable>();
|
|
25 private String name = "test";
|
|
26 private int seq;
|
|
27 private int eid;
|
|
28 private int sid;
|
|
29 private REPText repText;
|
419
|
30 private boolean hasInputLock = false;
|
417
|
31 private boolean master;
|
|
32 private boolean syncEnable = true;
|
|
33 private LogTarget logTarget;
|
420
|
34 private int tempseq = -1;
|
417
|
35
|
|
36 public REPEditor(REPText repText, boolean master){
|
|
37 this.repText = repText;
|
|
38 this.master = master;
|
|
39 repText.addTextListener(this);
|
|
40 if(master){
|
|
41 userCommand.add(new REPCommand(REP.SMCMD_PUT,0,0,0,0,name +"-file"));
|
|
42 }else{
|
|
43 userCommand.add(new REPCommand(REP.SMCMD_JOIN, 0, 0, 0, 0, name));
|
|
44 }
|
|
45 }
|
|
46
|
|
47 public void textDeleted(REPTextEvent event) {
|
|
48 Logger.print(event.getText());
|
|
49 addUserInput(new REPCommand(REP.REPCMD_DELETE_USER, 0, 0, 0, event.getLineno(), event.getText()));
|
|
50 }
|
|
51
|
|
52 public void textInserted(REPTextEvent event) {
|
|
53 Logger.print(event.getText());
|
|
54 addUserInput(new REPCommand(REP.REPCMD_INSERT_USER, 0, 0, 0, event.getLineno(), event.getText()));
|
|
55 }
|
|
56
|
419
|
57 public void addUserInput(final REPCommand command) {
|
417
|
58 Runnable runner = new Runnable(){
|
|
59 public void run(){
|
|
60 userCommand.add(command);
|
|
61 timeout = 1;
|
|
62 }
|
|
63 };
|
|
64 synchronized(runners){
|
|
65 runners.add(runner);
|
|
66 }
|
|
67 if(selector != null){
|
|
68 selector.wakeup();
|
|
69 }
|
|
70 }
|
|
71
|
|
72 public void run(){
|
|
73 /*
|
|
74 * Create Socket and connect to the session manager
|
|
75 */
|
|
76 try {
|
|
77 channel = REPSocketChannel.<REPCommand>create(new REPCommandPacker());
|
|
78 } catch (IOException e) {
|
|
79 e.printStackTrace();
|
|
80 return;
|
|
81 }
|
|
82 try {
|
|
83 InetSocketAddress semaIP = new InetSocketAddress("localhost", 8766);
|
|
84 while (!channel.connect(semaIP)){
|
|
85 Logger.print("SeMa not listen to socket yet, wait");
|
|
86 }
|
|
87 } catch (IOException e) {
|
|
88 e.printStackTrace();
|
|
89 return;
|
|
90 }
|
|
91 /*
|
|
92 * Start editor main loop
|
|
93 * public REPCommand(REP cmd,int sid,int eid, int seq, int lineno, String string)
|
|
94 */
|
|
95 try {
|
|
96 mainloop();
|
|
97 } catch (IOException e) {
|
|
98 }
|
|
99 }
|
|
100
|
|
101 /*
|
|
102 * Editor main loop with input lock
|
|
103 */
|
|
104 private void mainloop() throws IOException {
|
|
105
|
|
106 channel.configureBlocking(false);
|
|
107 selector = REPSelector.create();
|
|
108 channel.register(selector, SelectionKey.OP_READ);
|
|
109 while(running) {
|
|
110
|
|
111 synchronized(runners){
|
|
112 for(Runnable runner : runners){
|
|
113 runner.run();
|
|
114 }
|
|
115 runners.clear();
|
|
116 }
|
|
117
|
|
118 if (inputLock) {
|
|
119 // No user input during merge mode (optional)
|
|
120 if (selector.select(0)>0) {
|
|
121 handle(channel.read());
|
|
122 }
|
|
123 continue;
|
|
124 } else if (selector.select(timeout)<=0) {
|
|
125 if (syncCounter>0) {
|
|
126 syncText(); // send the master editor buffer to clients.
|
|
127 }
|
|
128 userInput();
|
|
129 }
|
|
130 // selector(timeout) returns 0, but it may contain readable channel..
|
|
131 for(REPSelectionKey<REPCommand> key : selector.selectedKeys1()) {
|
|
132 REPSocketChannel<REPCommand> ch = key.channel1();
|
|
133 handle(ch.read());
|
|
134 }
|
|
135 }
|
|
136 }
|
|
137
|
|
138 private void handle(REPCommand command) {
|
|
139 Logger.print(logTarget, command);
|
420
|
140
|
|
141 //check seq
|
|
142 if(eid == 1){
|
|
143 if(command.eid == 3){
|
|
144 if(tempseq > command.seq){
|
|
145 System.err.println(command);
|
|
146 }
|
|
147 tempseq = command.seq;
|
|
148 }
|
|
149 }
|
|
150
|
419
|
151 // if(inputLock) Logger.print(logTarget, command);
|
417
|
152 if(command == null) return;
|
|
153 switch(command.cmd){
|
|
154 case REPCMD_DELETE:
|
|
155 if(command.eid != eid){
|
|
156 String del = repText.delete(command.lineno);
|
|
157 command.setString(del);
|
|
158 }
|
|
159 forward(command);
|
|
160 break;
|
|
161 case REPCMD_INSERT:
|
|
162 if(command.eid != eid){
|
|
163 repText.insert(command.lineno, command.string);
|
|
164 }
|
|
165 forward(command);
|
|
166 break;
|
|
167 case REPCMD_NOP:
|
|
168 case REPCMD_INSERT_ACK:
|
|
169 case REPCMD_DELETE_ACK:
|
|
170 forward(command);
|
|
171 break;
|
|
172 case SMCMD_PUT_ACK:
|
|
173 sid = command.sid;
|
|
174 eid = command.eid;
|
|
175 name += "(eid="+eid+",sid="+sid+")";
|
|
176 inputLock = false;
|
|
177 break;
|
|
178 case SMCMD_JOIN_ACK :
|
|
179 sid = command.sid;
|
|
180 eid = command.eid;
|
|
181 name += "(eid="+eid+",sid="+sid+")";
|
|
182 inputLock = false;
|
|
183 break;
|
|
184 case SMCMD_START_MERGE :
|
|
185 // lock user input during merge (optional)
|
|
186 inputLock = hasInputLock;
|
|
187 command.cmd = REP.SMCMD_START_MERGE_ACK;
|
|
188 send(command);
|
|
189 break;
|
|
190 case SMCMD_END_MERGE :
|
|
191 inputLock = false;
|
|
192 break;
|
|
193 case SMCMD_SYNC:
|
|
194 // start contents sync with newly joined editor
|
|
195 command.cmd = REP.SMCMD_SYNC_ACK;
|
|
196 forward(command);
|
|
197 //if (cmd.eid==eid) {
|
|
198 if (master && syncEnable ) {
|
|
199 syncCounter = 1;
|
|
200 timeout = 1;
|
|
201 }
|
|
202 break;
|
|
203 }
|
|
204 }
|
|
205
|
|
206 private void userInput() {
|
|
207 Logger.print();
|
|
208 REPCommand command = userCommand.poll();
|
|
209 if(command != null){
|
|
210 switch(command.cmd){
|
|
211 case REPCMD_DELETE_USER:
|
|
212 send(command);
|
|
213 break;
|
|
214 case REPCMD_INSERT_USER:
|
|
215 send(command);
|
|
216 break;
|
|
217 case SMCMD_PUT:
|
|
218 case SMCMD_JOIN:
|
|
219 send(command);
|
|
220 break;
|
|
221 }
|
|
222 }else{
|
|
223 if(syncCounter == 0){
|
|
224 timeout = 0;
|
|
225 }
|
|
226 }
|
|
227 }
|
|
228
|
|
229 private void forward(REPCommand command) {
|
|
230 REPCommand cmd = new REPCommand(command);
|
|
231 channel.write(cmd);
|
|
232 }
|
|
233
|
|
234 private void send(REPCommand command) {
|
|
235 REPCommand cmd = new REPCommand(command);
|
|
236 cmd.setSEQID(seq++);
|
|
237 cmd.setEID(eid);
|
|
238 cmd.setSID(sid);
|
|
239 channel.write(cmd);
|
|
240 }
|
|
241
|
|
242 private void syncText() {
|
|
243 if(syncCounter>repText.size()){
|
|
244 syncCounter = 0;
|
|
245 }else {
|
|
246 if(inputLock) return;
|
|
247 int i = syncCounter - 1;
|
|
248 REPCommand del = new REPCommand(REP.REPCMD_DELETE_USER, sid, eid, 0, i, repText.get(i));
|
|
249 REPCommand ins = new REPCommand(REP.REPCMD_INSERT_USER, sid, eid, 0, i, repText.get(i));
|
|
250 send(del);
|
|
251 send(ins);
|
|
252 syncCounter++;
|
|
253 }
|
|
254 }
|
|
255
|
|
256 public void setLogTarget(LogTarget target){
|
|
257 logTarget = target;
|
|
258 }
|
|
259
|
419
|
260 public REPText getREPText() {
|
|
261 return repText;
|
|
262 }
|
|
263
|
417
|
264 }
|