1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 package org.openoffice.test.vcl.client;
25 
26 import java.io.ByteArrayInputStream;
27 import java.io.ByteArrayOutputStream;
28 import java.io.IOException;
29 import java.util.ArrayList;
30 import java.util.List;
31 
32 
33 /**
34  * The class is used to send a statement to the automation server.
35  *
36  *
37  */
38 public class CommandCaller implements CommunicationListener, Constant {
39 
40 	private ByteArrayOutputStream dataOutput = new ByteArrayOutputStream(1024);
41 
42 	private ByteArrayInputStream dataInput = null;
43 
44 	private CommunicationManager communicationManager = null;
45 
46 	private int sequence = 0;
47 
48 	private WinInfoReceiver winInfoReceiver = null;
49 
50 	private boolean receivingWinInfo = false;
51 
52 	/**Store the response**/
53 	private Object response = null;
54 
55 	private SmartId responseExceptionId = null;
56 
57 	private String responseExceptionMessage = null;
58 
59 	/**A variable to indicate if the server answered the request **/
60 	private boolean answered = false;
61 
CommandCaller(CommunicationManager communicationManager)62 	public CommandCaller(CommunicationManager communicationManager) {
63 		this.communicationManager = communicationManager;
64 		communicationManager.addListener(this);
65 	}
66 
write(byte[] bytes)67 	private void write(byte[] bytes) {
68 		try {
69 			dataOutput.write(bytes);
70 		} catch (IOException e) {
71 			e.printStackTrace();
72 		}
73 	}
74 
read(int len)75 	private byte[] read(int len) {
76 		byte[] bytes = new byte[len];
77 		try {
78 			dataInput.read(bytes);
79 		} catch (IOException e) {
80 			e.printStackTrace();
81 		}
82 		return bytes;
83 	}
84 
85 
86 	/**
87 	 * Write the data type
88 	 * @param i
89 	 */
writeChar(int i)90 	private void writeChar(int i) {
91 		byte[] bytes = new byte[2];
92 		bytes[1] = (byte) ((i & 0xFF00) >> 8);
93 		bytes[0] = (byte) (i & 0x00FF);
94 		write(bytes);
95 	}
96 
readChar()97 	private int readChar() {
98 		byte[] bytes = read(2);
99 		return (bytes[0] & 0x00FF) + ((bytes[1] & 0x00FF) << 8);
100 	}
101 
102 
103 	/**
104 	 * Check the type of next data
105 	 * The bytes used to identify the type will not be read out from the stream
106 	 * @return
107 	 */
nextType()108 	private int nextType() {
109 		dataInput.mark(0);
110 		int type = readChar();
111 		dataInput.reset();
112 		return type;
113 	}
114 
115 	/**
116 	 * Write an unsigned 16-bit integer
117 	 * @param i
118 	 */
writeUShort(int i)119 	private void writeUShort(int i) {
120 		writeChar(BinUSHORT);
121 		writeChar(i);
122 	}
123 
124 	/**
125 	 * Read an unsigned 16-bit integer
126 	 * @return
127 	 */
readUShort()128 	private int readUShort() {
129 		if (readChar() != BinUSHORT)
130 			throw new RuntimeException("Bad data!");
131 
132 		byte[] bytes = read(2);
133 		return (bytes[0] & 0x00FF) + ((bytes[1] & 0x00FF) << 8);
134 	}
135 
136 	/**
137 	 * Write an unsigned 32-bit integer
138 	 * @param i
139 	 */
writeULong(long i)140 	private void writeULong(long i)  {
141 		writeChar(BinULONG);
142 		byte[] bytes = new byte[4];
143 		bytes[3] = (byte) ((i & 0xFF000000L) >> 24);
144 		bytes[2] = (byte) ((i & 0x00FF0000L) >> 16);
145 		bytes[1] = (byte) ((i & 0x0000FF00L) >> 8);
146 		bytes[0] = (byte) ((i & 0x000000FFL));
147 		write(bytes);
148 	}
149 
150 	/**
151 	 * Read an unsigned 32-bit integer
152 	 * @return
153 	 */
readULong()154 	private long readULong() {
155 		if (readChar() != BinULONG)
156 			throw new RuntimeException("Bad data!");
157 		byte[] bytes = read(4);
158 		return (bytes[0] & 0x00FFL) + ((bytes[1] & 0x00FFL) << 8) + ((bytes[2] & 0x00FFL) << 16) + ((bytes[3] & 0x00FFL) << 24);
159 	}
160 
161 	/**
162 	 * Write boolean
163 	 * @param bBool
164 	 */
writeBoolean(boolean bBool)165 	private void writeBoolean(boolean bBool) {
166 		writeChar(BinBool);
167 		write(new byte[] { bBool ? (byte) 1 : 0 });
168 	}
169 
170 	/**
171 	 * Read boolean
172 	 * @return
173 	 */
readBoolean()174 	private boolean readBoolean() {
175 		if (readChar() != BinBool)
176 			throw new RuntimeException("Bad data!");
177 		byte[] bytes = read(1);
178 		return bytes[0] != 0;
179 	}
180 
181 	/**
182 	 * Write a string
183 	 * @param str
184 	 */
writeString(String str)185 	private void writeString(String str) {
186 		writeChar(BinString);
187 		int len =  str.length();
188 		if (len > 0xFFFF) {
189 			throw new RuntimeException("String is too long.");
190 		}
191 		writeChar(len);
192 		char[] chars = str.toCharArray();
193 		for (int i = 0 ; i < len; i++)
194 			writeChar(chars[i]);
195 	}
196 
197 	/**
198 	 * Read a string
199 	 * @return
200 	 */
readString()201 	private String readString() {
202 		if (readChar() != BinString)
203 			throw new RuntimeException("Bad data!");
204 		int len = readChar();
205 		char[] chars = new char[len];
206 		for (int i = 0; i < len; i++) {
207 			chars[i] = (char) readChar();
208 		}
209 		return new String(chars);
210 	}
211 
writeParams(Object[] args)212 	private void writeParams(Object[] args) {
213 		int nParams = PARAM_NONE;
214 		int nNr1=0;
215 		int nNr2=0;
216 		int nNr3=0;
217 		int nNr4=0;
218 		long nLNr1=0;
219 		String aString1=null;
220 		String aString2=null;
221 		boolean bBool1=false;
222 		boolean bBool2=false;
223 
224 		if (args != null) {
225 			for (int i = 0; i < args.length; i++) {
226 				if (args[i] instanceof Short || args[i] instanceof Integer) {
227 					int c =  ((Number) args[i]).intValue();
228 					if ((nParams & PARAM_USHORT_1) == 0) {
229 						nParams |= PARAM_USHORT_1;
230 						nNr1 = c;
231 					} else if ((nParams & PARAM_USHORT_2) == 0) {
232 						nParams |= PARAM_USHORT_2;
233 						nNr2 = c;
234 					} else if ((nParams & PARAM_USHORT_3) == 0) {
235 						nParams |= PARAM_USHORT_3;
236 						nNr3 = c;
237 					} else if ((nParams & PARAM_USHORT_4) == 0) {
238 						nParams |= PARAM_USHORT_4;
239 						nNr4 = c;
240 					} else {
241 						//TODO error
242 					}
243 				} else if (args[i] instanceof Long) {
244 					long l = ((Long) args[i]).longValue();
245 					nParams |= PARAM_ULONG_1;
246 					nLNr1 = l;
247 				} else if (args[i] instanceof Boolean) {
248 					if ((nParams & PARAM_BOOL_1) == 0) {
249 						nParams |= PARAM_BOOL_1;
250 						bBool1 = ((Boolean) args[i]).booleanValue();
251 					} else if ((nParams & PARAM_BOOL_2) == 0) {
252 						nParams |= PARAM_BOOL_2;
253 						bBool2 = ((Boolean) args[i]).booleanValue();
254 					} else {
255 						//TODO error
256 					}
257 
258 				} else if (args[i] instanceof String) {
259 					if ((nParams & PARAM_STR_1) == 0) {
260 						nParams |= PARAM_STR_1;
261 						aString1 = (String) args[i];
262 					} else if ((nParams & PARAM_STR_2) == 0) {
263 						nParams |= PARAM_STR_2;
264 						aString2 = (String) args[i];
265 					} else {
266 						//TODO error
267 					}
268 				}
269 			}
270 		}
271 
272 		writeUShort(nParams);
273 		if ((nParams & PARAM_USHORT_1) != 0) {
274 			writeUShort(nNr1);
275 		}
276 
277 		if ((nParams & PARAM_USHORT_2) != 0) {
278 			writeUShort(nNr2);
279 		}
280 
281 		if ((nParams & PARAM_USHORT_3) != 0) {
282 			writeUShort(nNr3);
283 		}
284 
285 		if ((nParams & PARAM_USHORT_4) != 0) {
286 			writeUShort(nNr4);
287 		}
288 
289 		if ((nParams & PARAM_ULONG_1) != 0) {
290 			writeULong(nLNr1);
291 		}
292 
293 		if ((nParams & PARAM_STR_1) != 0) {
294 			writeString(aString1);
295 		}
296 
297 		if ((nParams & PARAM_STR_2) != 0) {
298 			writeString(aString2);
299 		}
300 
301 		if ((nParams & PARAM_BOOL_1) != 0) {
302 			writeBoolean(bBool1);
303 		}
304 		if ((nParams & PARAM_BOOL_2) != 0) {
305 			writeBoolean(bBool2);
306 		}
307 
308 	}
309 
send()310 	private void send() {
311 		byte[] data = dataOutput.toByteArray();
312 		dataOutput.reset();
313 		int protocal = CM_PROTOCOL_OLDSTYLE;
314 		byte[] header = new byte[]{(byte)((protocal >>> 8) & 0xFF), (byte) ((protocal >>> 0) & 0xFF)};
315 		communicationManager.sendPackage(CH_SimpleMultiChannel, header, data);
316 	}
317 
318 	/**
319 	 * The data arrives
320 	 */
received(int headerType, byte[] header, byte[] data)321 	public synchronized void received(int headerType, byte[] header, byte[] data) {
322 		if (headerType != CommunicationManager.CH_Handshake) {
323 			dataInput = new ByteArrayInputStream(data);
324 			handleResponse();
325 			dataInput = null;
326 			answered = true;
327 			notifyAll();
328 		}
329 	}
330 
331 	/**
332 	 * This method is called when the communication is started.
333 	 */
start()334 	public void start() {
335 	}
336 
337 	/**
338 	 * This method is called when the communication is closed
339 	 */
stop()340 	public synchronized void stop() {
341 		answered = true;
342 		notifyAll();
343 	}
344 
345 
readId()346 	private SmartId readId() {
347 		int type = nextType();
348 		if ( type == BinString) {
349 			return new SmartId(readString());
350 		} else if (type == BinULONG) {
351 			return new SmartId(readULong());
352 		}
353 
354 		throw new RuntimeException("Bad data!");
355 	}
356 
handleResponse()357 	private void handleResponse() {
358 		this.response = null;
359 		this.responseExceptionId = null;
360 		this.responseExceptionMessage = null;
361 
362 		while (dataInput.available() >= 2) {
363 			int id = readUShort();
364 			switch (id) {
365 			case SIReturn:
366 				int returnType = readUShort();
367 				SmartId sid = readId();
368 				int nNr1 = 0;
369 				long nLNr1 = 0;
370 				String aString1 = null;
371 				boolean bBool1 = false;
372 				int params = readUShort();
373 				if ((params & PARAM_USHORT_1) != 0)
374 					nNr1 = readUShort();
375 				if ((params & PARAM_ULONG_1) != 0)
376 					nLNr1 = readULong();
377 				if ((params & PARAM_STR_1) != 0)
378 					aString1 = readString();
379 				if ((params & PARAM_BOOL_1) != 0)
380 					bBool1 = readBoolean();
381 				if ((params & PARAM_SBXVALUE_1) != 0) {
382 					// Don't support????
383 				}
384 
385 				switch (returnType) {
386 				case RET_Sequence:
387 					if (sid.getId() != sequence)
388 						this.responseExceptionMessage = "Bad sequence of command.";
389 					break;
390 				case RET_Value:
391 					List<Object> ret = new ArrayList<Object>();
392 					if ((params & PARAM_USHORT_1) != 0)
393 						ret.add(new Integer(nNr1));
394 					if ((params & PARAM_ULONG_1) != 0)
395 						ret.add(new Long(nLNr1));
396 					if ((params & PARAM_STR_1) != 0)
397 						ret.add(aString1);
398 					if ((params & PARAM_BOOL_1) != 0)
399 						ret.add(new Boolean(bBool1));
400 					this.response = ret.size() == 1 ? ret.get(0): ret;
401 					break;
402 				case RET_WinInfo:
403 					if (bBool1) {
404 						receivingWinInfo = true;
405 						if (winInfoReceiver != null)
406 							winInfoReceiver.onStartReceiving();
407 					} else {
408 						if (winInfoReceiver != null)
409 							winInfoReceiver.addWinInfo(sid, nLNr1, aString1);
410 					}
411 					break;
412 				}
413 
414 				break;
415 			case SIReturnError:
416 				this.responseExceptionId = readId();
417 				this.responseExceptionMessage = readString();
418 				break;
419 			}
420 		}
421 
422 		if (receivingWinInfo) {
423 			if (winInfoReceiver != null)
424 				winInfoReceiver.onFinishReceiving();
425 			receivingWinInfo = false;
426 		}
427 	}
428 
429 
setWinInfoReceiver(WinInfoReceiver receiver)430 	public void setWinInfoReceiver(WinInfoReceiver receiver) {
431 		this.winInfoReceiver = receiver;
432 	}
433 
callFlow(int nArt)434 	private void callFlow(int nArt) {
435 		writeUShort(SIFlow);
436 		writeUShort(nArt);
437 		writeUShort(PARAM_NONE);
438 	}
439 
440 
callFlow(int nArt, long nLNr1)441 	private void callFlow(int nArt, long nLNr1) {
442 		writeUShort(SIFlow);
443 		writeUShort(nArt);
444 		writeUShort(PARAM_ULONG_1);
445 		writeULong(nLNr1);
446 	}
447 
448 	/**
449 	 * Tell automation server to execute a 'StatementCommand'
450 	 * @param methodId The method ID
451 	 * @param args the arguments. The arguments can be Integer, Long, Boolean and String.
452 	 * @return The return can be Integer, Long, String and Boolean or an Object[] includes these types of object.
453 	 */
callCommand(int methodId, Object... args)454 	public synchronized Object callCommand(int methodId, Object... args) {
455 		beginBlock();
456 		writeUShort(SICommand);
457 		writeUShort(methodId);
458 		writeParams(args);
459 		endBlock();
460 
461 		if ((methodId & M_WITH_RETURN) != 0) {
462 			return response;
463 		}
464 
465 		return null;
466 	}
467 
callCommand(int methodId)468 	public synchronized Object callCommand(int methodId) {
469 		return callCommand(methodId, (Object)null);
470 	}
471 
472 	/**
473 	 *  Tell automation server to execute a 'StatementControl'
474 	 * @param id the control ID
475 	 * @param methodId the method ID defined Constant class
476 	 * @param args the arguments. The arguments can be Integer, Long, Boolean and String.
477 	 * @return The return can be Integer, Long, String and Boolean or an Object[] includes these types of object.
478 	 */
callControl(String id, int methodId, Object... args)479 	public synchronized Object callControl(String id, int methodId, Object... args){
480 		beginBlock();
481 		try {
482 			long noId = Long.parseLong(id);
483 			writeUShort(SIControl);
484 			writeULong(noId);
485 		} catch (NumberFormatException e) {
486 			writeUShort(SIStringControl);
487 			writeString(id);
488 		}
489 		writeUShort(methodId);
490 		writeParams(args);
491 		endBlock();
492 
493 		if ((methodId & M_WITH_RETURN) != 0) {
494 			return response;
495 		}
496 
497 		return null;
498 	}
499 
500 	/**
501 	 * Tell automation server to execute a 'StatementUNOSlot'
502 	 * @param url the UNO slot url
503 	 */
callUNOSlot(String url)504 	public synchronized void callUNOSlot(String url) {
505 		beginBlock();
506 		writeUShort(SIUnoSlot);
507 		writeString(url);
508 		endBlock();
509 	}
510 
511 	/**
512 	 * Tell automation server to execute a 'StatementSlot'
513 	 * @param id the slot ID
514 	 * @param args the slot args
515 	 */
callSlot(int id, Object... args)516 	public synchronized void callSlot(int id, Object... args) {
517 		beginBlock();
518 		writeUShort(SISlot);
519 		writeUShort(id);
520 		if (args.length % 2 != 0)
521 			throw new RuntimeException("bad arg");
522 		writeUShort(args.length / 2);
523 		for (int i = 0; i < args.length; i++) {
524 			if (!(args[i] instanceof String))
525 				throw new RuntimeException("bad arg");
526 			writeString((String)args[i]);
527 			i++;
528 			if (args[i] instanceof Boolean) {
529 				writeBoolean((Boolean)args[i]);
530 			} else if (args[i] instanceof String) {
531 				writeString((String)args[i]);
532 			} else if (args[i] instanceof Short || args[i] instanceof Integer) {
533 				writeUShort(((Number) args[i]).intValue());
534 			} else if (args[i] instanceof Long) {
535 				writeULong((Long) args[i]);
536 			} else {
537 				throw new RuntimeException("bad arg");
538 			}
539 		}
540 		endBlock();
541 	}
542 
543 
beginBlock()544 	private void beginBlock() {
545 		callFlow(F_Sequence, ++sequence);
546 	}
547 
endBlock()548 	private void endBlock() {
549 		callFlow(F_EndCommandBlock);
550 		answered = false;
551 		send();
552 		int MAX_RETRY = 240;//max waiting time is two minutes.
553 		for (int i = 0; !answered && i < MAX_RETRY; i++) {
554 			try {
555 				wait(500);
556 			} catch (InterruptedException e) {
557 
558 			}
559 		}
560 
561 		// Still answered
562 		if (!answered) {
563 			communicationManager.stop();
564 			throw new CommunicationException("Failed to get data from automation server!");
565 		}
566 
567 		if (responseExceptionId != null || responseExceptionMessage != null)
568 			throw new VclHookException(responseExceptionId, responseExceptionMessage);
569 
570 	}
571 }
572