blob: fc3ffebfc689b735de491c963e2c770c7f86cac8 [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
package org.openoffice.test.vcl.client;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* The class is used to send a statement to the automation server.
*
*
*/
public class CommandCaller implements CommunicationListener, Constant {
private ByteArrayOutputStream dataOutput = new ByteArrayOutputStream(1024);
private ByteArrayInputStream dataInput = null;
private CommunicationManager communicationManager = null;
private int sequence = 0;
private WinInfoReceiver winInfoReceiver = null;
private boolean receivingWinInfo = false;
/**Store the response**/
private Object response = null;
private SmartId responseExceptionId = null;
private String responseExceptionMessage = null;
/**A variable to indicate if the server answered the request **/
private boolean answered = false;
public CommandCaller(CommunicationManager communicationManager) {
this.communicationManager = communicationManager;
communicationManager.addListener(this);
}
private void write(byte[] bytes) {
try {
dataOutput.write(bytes);
} catch (IOException e) {
e.printStackTrace();
}
}
private byte[] read(int len) {
byte[] bytes = new byte[len];
try {
dataInput.read(bytes);
} catch (IOException e) {
e.printStackTrace();
}
return bytes;
}
/**
* Write the data type
* @param i
*/
private void writeChar(int i) {
byte[] bytes = new byte[2];
bytes[1] = (byte) ((i & 0xFF00) >> 8);
bytes[0] = (byte) (i & 0x00FF);
write(bytes);
}
private int readChar() {
byte[] bytes = read(2);
return (bytes[0] & 0x00FF) + ((bytes[1] & 0x00FF) << 8);
}
/**
* Check the type of next data
* The bytes used to identify the type will not be read out from the stream
* @return
*/
private int nextType() {
dataInput.mark(0);
int type = readChar();
dataInput.reset();
return type;
}
/**
* Write an unsigned 16-bit integer
* @param i
*/
private void writeUShort(int i) {
writeChar(BinUSHORT);
writeChar(i);
}
/**
* Read an unsigned 16-bit integer
* @return
*/
private int readUShort() {
if (readChar() != BinUSHORT)
throw new RuntimeException("Bad data!");
byte[] bytes = read(2);
return (bytes[0] & 0x00FF) + ((bytes[1] & 0x00FF) << 8);
}
/**
* Write an unsigned 32-bit integer
* @param i
*/
private void writeULong(long i) {
writeChar(BinULONG);
byte[] bytes = new byte[4];
bytes[3] = (byte) ((i & 0xFF000000L) >> 24);
bytes[2] = (byte) ((i & 0x00FF0000L) >> 16);
bytes[1] = (byte) ((i & 0x0000FF00L) >> 8);
bytes[0] = (byte) ((i & 0x000000FFL));
write(bytes);
}
/**
* Read an unsigned 32-bit integer
* @return
*/
private long readULong() {
if (readChar() != BinULONG)
throw new RuntimeException("Bad data!");
byte[] bytes = read(4);
return (bytes[0] & 0x00FFL) + ((bytes[1] & 0x00FFL) << 8) + ((bytes[2] & 0x00FFL) << 16) + ((bytes[3] & 0x00FFL) << 24);
}
/**
* Write boolean
* @param bBool
*/
private void writeBoolean(boolean bBool) {
writeChar(BinBool);
write(new byte[] { bBool ? (byte) 1 : 0 });
}
/**
* Read boolean
* @return
*/
private boolean readBoolean() {
if (readChar() != BinBool)
throw new RuntimeException("Bad data!");
byte[] bytes = read(1);
return bytes[0] != 0;
}
/**
* Write a string
* @param str
*/
private void writeString(String str) {
writeChar(BinString);
int len = str.length();
if (len > 0xFFFF) {
throw new RuntimeException("String is too long.");
}
writeChar(len);
char[] chars = str.toCharArray();
for (int i = 0 ; i < len; i++)
writeChar(chars[i]);
}
/**
* Read a string
* @return
*/
private String readString() {
if (readChar() != BinString)
throw new RuntimeException("Bad data!");
int len = readChar();
char[] chars = new char[len];
for (int i = 0; i < len; i++) {
chars[i] = (char) readChar();
}
return new String(chars);
}
private void writeParams(Object[] args) {
int nParams = PARAM_NONE;
int nNr1=0;
int nNr2=0;
int nNr3=0;
int nNr4=0;
long nLNr1=0;
String aString1=null;
String aString2=null;
boolean bBool1=false;
boolean bBool2=false;
if (args != null) {
for (int i = 0; i < args.length; i++) {
if (args[i] instanceof Short || args[i] instanceof Integer) {
int c = ((Number) args[i]).intValue();
if ((nParams & PARAM_USHORT_1) == 0) {
nParams |= PARAM_USHORT_1;
nNr1 = c;
} else if ((nParams & PARAM_USHORT_2) == 0) {
nParams |= PARAM_USHORT_2;
nNr2 = c;
} else if ((nParams & PARAM_USHORT_3) == 0) {
nParams |= PARAM_USHORT_3;
nNr3 = c;
} else if ((nParams & PARAM_USHORT_4) == 0) {
nParams |= PARAM_USHORT_4;
nNr4 = c;
} else {
//TODO error
}
} else if (args[i] instanceof Long) {
long l = ((Long) args[i]).longValue();
nParams |= PARAM_ULONG_1;
nLNr1 = l;
} else if (args[i] instanceof Boolean) {
if ((nParams & PARAM_BOOL_1) == 0) {
nParams |= PARAM_BOOL_1;
bBool1 = ((Boolean) args[i]).booleanValue();
} else if ((nParams & PARAM_BOOL_2) == 0) {
nParams |= PARAM_BOOL_2;
bBool2 = ((Boolean) args[i]).booleanValue();
} else {
//TODO error
}
} else if (args[i] instanceof String) {
if ((nParams & PARAM_STR_1) == 0) {
nParams |= PARAM_STR_1;
aString1 = (String) args[i];
} else if ((nParams & PARAM_STR_2) == 0) {
nParams |= PARAM_STR_2;
aString2 = (String) args[i];
} else {
//TODO error
}
}
}
}
writeUShort(nParams);
if ((nParams & PARAM_USHORT_1) != 0) {
writeUShort(nNr1);
}
if ((nParams & PARAM_USHORT_2) != 0) {
writeUShort(nNr2);
}
if ((nParams & PARAM_USHORT_3) != 0) {
writeUShort(nNr3);
}
if ((nParams & PARAM_USHORT_4) != 0) {
writeUShort(nNr4);
}
if ((nParams & PARAM_ULONG_1) != 0) {
writeULong(nLNr1);
}
if ((nParams & PARAM_STR_1) != 0) {
writeString(aString1);
}
if ((nParams & PARAM_STR_2) != 0) {
writeString(aString2);
}
if ((nParams & PARAM_BOOL_1) != 0) {
writeBoolean(bBool1);
}
if ((nParams & PARAM_BOOL_2) != 0) {
writeBoolean(bBool2);
}
}
private void send() {
byte[] data = dataOutput.toByteArray();
dataOutput.reset();
int protocal = CM_PROTOCOL_OLDSTYLE;
byte[] header = new byte[]{(byte)((protocal >>> 8) & 0xFF), (byte) ((protocal >>> 0) & 0xFF)};
communicationManager.sendPackage(CH_SimpleMultiChannel, header, data);
}
/**
* The data arrives
*/
public synchronized void received(int headerType, byte[] header, byte[] data) {
if (headerType != CommunicationManager.CH_Handshake) {
dataInput = new ByteArrayInputStream(data);
handleResponse();
dataInput = null;
answered = true;
notifyAll();
}
}
/**
* This method is called when the communication is started.
*/
public void start() {
}
/**
* This method is called when the communication is closed
*/
public synchronized void stop() {
answered = true;
notifyAll();
}
private SmartId readId() {
int type = nextType();
if ( type == BinString) {
return new SmartId(readString());
} else if (type == BinULONG) {
return new SmartId(readULong());
}
throw new RuntimeException("Bad data!");
}
private void handleResponse() {
this.response = null;
this.responseExceptionId = null;
this.responseExceptionMessage = null;
while (dataInput.available() >= 2) {
int id = readUShort();
switch (id) {
case SIReturn:
int returnType = readUShort();
SmartId sid = readId();
int nNr1 = 0;
long nLNr1 = 0;
String aString1 = null;
boolean bBool1 = false;
int params = readUShort();
if ((params & PARAM_USHORT_1) != 0)
nNr1 = readUShort();
if ((params & PARAM_ULONG_1) != 0)
nLNr1 = readULong();
if ((params & PARAM_STR_1) != 0)
aString1 = readString();
if ((params & PARAM_BOOL_1) != 0)
bBool1 = readBoolean();
if ((params & PARAM_SBXVALUE_1) != 0) {
// Don't support????
}
switch (returnType) {
case RET_Sequence:
if (sid.getId() != sequence)
this.responseExceptionMessage = "Bad sequence of command.";
break;
case RET_Value:
List<Object> ret = new ArrayList<Object>();
if ((params & PARAM_USHORT_1) != 0)
ret.add(new Integer(nNr1));
if ((params & PARAM_ULONG_1) != 0)
ret.add(new Long(nLNr1));
if ((params & PARAM_STR_1) != 0)
ret.add(aString1);
if ((params & PARAM_BOOL_1) != 0)
ret.add(new Boolean(bBool1));
this.response = ret.size() == 1 ? ret.get(0): ret;
break;
case RET_WinInfo:
if (bBool1) {
receivingWinInfo = true;
if (winInfoReceiver != null)
winInfoReceiver.onStartReceiving();
} else {
if (winInfoReceiver != null)
winInfoReceiver.addWinInfo(sid, nLNr1, aString1);
}
break;
}
break;
case SIReturnError:
this.responseExceptionId = readId();
this.responseExceptionMessage = readString();
break;
}
}
if (receivingWinInfo) {
if (winInfoReceiver != null)
winInfoReceiver.onFinishReceiving();
receivingWinInfo = false;
}
}
public void setWinInfoReceiver(WinInfoReceiver receiver) {
this.winInfoReceiver = receiver;
}
private void callFlow(int nArt) {
writeUShort(SIFlow);
writeUShort(nArt);
writeUShort(PARAM_NONE);
}
private void callFlow(int nArt, long nLNr1) {
writeUShort(SIFlow);
writeUShort(nArt);
writeUShort(PARAM_ULONG_1);
writeULong(nLNr1);
}
/**
* Tell automation server to execute a 'StatementCommand'
* @param methodId The method ID
* @param args the arguments. The arguments can be Integer, Long, Boolean and String.
* @return The return can be Integer, Long, String and Boolean or an Object[] includes these types of object.
*/
public synchronized Object callCommand(int methodId, Object... args) {
beginBlock();
writeUShort(SICommand);
writeUShort(methodId);
writeParams(args);
endBlock();
if ((methodId & M_WITH_RETURN) != 0) {
return response;
}
return null;
}
public synchronized Object callCommand(int methodId) {
return callCommand(methodId, (Object)null);
}
/**
* Tell automation server to execute a 'StatementControl'
* @param id the control ID
* @param methodId the method ID defined Constant class
* @param args the arguments. The arguments can be Integer, Long, Boolean and String.
* @return The return can be Integer, Long, String and Boolean or an Object[] includes these types of object.
*/
public synchronized Object callControl(String id, int methodId, Object... args){
beginBlock();
try {
long noId = Long.parseLong(id);
writeUShort(SIControl);
writeULong(noId);
} catch (NumberFormatException e) {
writeUShort(SIStringControl);
writeString(id);
}
writeUShort(methodId);
writeParams(args);
endBlock();
if ((methodId & M_WITH_RETURN) != 0) {
return response;
}
return null;
}
/**
* Tell automation server to execute a 'StatementUNOSlot'
* @param url the UNO slot url
*/
public synchronized void callUNOSlot(String url) {
beginBlock();
writeUShort(SIUnoSlot);
writeString(url);
endBlock();
}
/**
* Tell automation server to execute a 'StatementSlot'
* @param id the slot ID
* @param args the slot args
*/
public synchronized void callSlot(int id, Object... args) {
beginBlock();
writeUShort(SISlot);
writeUShort(id);
if (args.length % 2 != 0)
throw new RuntimeException("bad arg");
writeUShort(args.length / 2);
for (int i = 0; i < args.length; i++) {
if (!(args[i] instanceof String))
throw new RuntimeException("bad arg");
writeString((String)args[i]);
i++;
if (args[i] instanceof Boolean) {
writeBoolean((Boolean)args[i]);
} else if (args[i] instanceof String) {
writeString((String)args[i]);
} else if (args[i] instanceof Short || args[i] instanceof Integer) {
writeUShort(((Number) args[i]).intValue());
} else if (args[i] instanceof Long) {
writeULong((Long) args[i]);
} else {
throw new RuntimeException("bad arg");
}
}
endBlock();
}
private void beginBlock() {
callFlow(F_Sequence, ++sequence);
}
private void endBlock() {
callFlow(F_EndCommandBlock);
answered = false;
send();
int MAX_RETRY = 240;//max waiting time is two minutes.
for (int i = 0; !answered && i < MAX_RETRY; i++) {
try {
wait(500);
} catch (InterruptedException e) {
}
}
// Still answered
if (!answered) {
communicationManager.stop();
throw new CommunicationException("Failed to get data from automation server!");
}
if (responseExceptionId != null || responseExceptionMessage != null)
throw new VclHookException(responseExceptionId, responseExceptionMessage);
}
}