Customize Interactive Brokers' reqIds() and reqMktData() Java methods - java

I am trying to write customized code within Interactive Brokers' Java API. There are a bunch of methods that get sent to TWS via the eClientSocket object. Two examples are reqIds() and reqMktData(). These are both void methods, so they do not return anything. Instead, they 'activate' methods written within the class that invokes them (in this case, SampleFrame). These methods are also void, in that they don't return any data. Instead, code is written within these methods (nextValidId() and tickPrice() respectively) to handle the data that is sent back from TWS (trader workstation).
I am having trouble creating a modified version of the nextValidId() and tickPrice() methods because reqIds() and reqMktData() don't actually specify these method names in their own code. I therefore cannot write a method called "tickPriceBlackBox()" which is called from within reqMktData(), or from within a copy of reqMktData() called reqMktDataBlackBox(). Again, there is no specific code within reqMktData() that can be modified to call a specific tickPriceBlackBox() method. It's as if code within TWS itself is hardwired to call the tickPrice() method, making it impossible for me to create a new method for returning price information.
Can anyone explain what is going on, or how to create a solution?
Here's some code:
void onReqMktData() {//requests market data from TWS / Interactive Brokers
// run m_orderDlg
m_orderDlg.init("Mkt Data Options", true, "Market Data Options", m_mktDataOptions);
m_orderDlg.show();
if( !m_orderDlg.m_rc ) {
return;
}
m_mktDataOptions = m_orderDlg.getOptions();
// req mkt data
m_client.reqMktData( m_orderDlg.m_id, m_orderDlg.m_contract,
m_orderDlg.m_genericTicks, m_orderDlg.m_snapshotMktData, m_mktDataOptions);
}
//Here is the reqMktData() method
public synchronized void reqMktData(int tickerId, Contract contract,
String genericTickList, boolean snapshot, List mktDataOptions) {
if (!m_connected) {
error(EClientErrors.NO_VALID_ID, EClientErrors.NOT_CONNECTED, "");
return;
}
if (m_serverVersion < MIN_SERVER_VER_SNAPSHOT_MKT_DATA && snapshot) {
error(tickerId, EClientErrors.UPDATE_TWS,
" It does not support snapshot market data requests.");
return;
}
if (m_serverVersion < MIN_SERVER_VER_UNDER_COMP) {
if (contract.m_underComp != null) {
error(tickerId, EClientErrors.UPDATE_TWS,
" It does not support delta-neutral orders.");
return;
}
}
if (m_serverVersion < MIN_SERVER_VER_REQ_MKT_DATA_CONID) {
if (contract.m_conId > 0) {
error(tickerId, EClientErrors.UPDATE_TWS,
" It does not support conId parameter.");
return;
}
}
if (m_serverVersion < MIN_SERVER_VER_TRADING_CLASS) {
if (!IsEmpty(contract.m_tradingClass)) {
error(tickerId, EClientErrors.UPDATE_TWS,
" It does not support tradingClass parameter in reqMarketData.");
return;
}
}
final int VERSION = 11;
try {
// send req mkt data msg
send(REQ_MKT_DATA);
send(VERSION);
send(tickerId);
// send contract fields
if (m_serverVersion >= MIN_SERVER_VER_REQ_MKT_DATA_CONID) {
send(contract.m_conId);
}
send(contract.m_symbol);
send(contract.m_secType);
send(contract.m_expiry);
send(contract.m_strike);
send(contract.m_right);
if (m_serverVersion >= 15) {
send(contract.m_multiplier);
}
send(contract.m_exchange);
if (m_serverVersion >= 14) {
send(contract.m_primaryExch);
}
send(contract.m_currency);
if(m_serverVersion >= 2) {
send( contract.m_localSymbol);
}
if(m_serverVersion >= MIN_SERVER_VER_TRADING_CLASS) {
send( contract.m_tradingClass);
}
if(m_serverVersion >= 8 && BAG_SEC_TYPE.equalsIgnoreCase(contract.m_secType)) {
if ( contract.m_comboLegs == null ) {
send( 0);
}
else {
send( contract.m_comboLegs.size());
ComboLeg comboLeg;
for (int i=0; i < contract.m_comboLegs.size(); i ++) {
comboLeg = contract.m_comboLegs.get(i);
send( comboLeg.m_conId);
send( comboLeg.m_ratio);
send( comboLeg.m_action);
send( comboLeg.m_exchange);
}
}
}
if (m_serverVersion >= MIN_SERVER_VER_UNDER_COMP) {
if (contract.m_underComp != null) {
UnderComp underComp = contract.m_underComp;
send( true);
send( underComp.m_conId);
send( underComp.m_delta);
send( underComp.m_price);
}
else {
send( false);
}
}
if (m_serverVersion >= 31) {
/*
* Note: Even though SHORTABLE tick type supported only
* starting server version 33 it would be relatively
* expensive to expose this restriction here.
*
* Therefore we are relying on TWS doing validation.
*/
send( genericTickList);
}
if (m_serverVersion >= MIN_SERVER_VER_SNAPSHOT_MKT_DATA) {
send (snapshot);
}
// send mktDataOptions parameter
if(m_serverVersion >= MIN_SERVER_VER_LINKING) {
StringBuilder mktDataOptionsStr = new StringBuilder();
int mktDataOptionsCount = mktDataOptions == null ? 0 : mktDataOptions.size();
if( mktDataOptionsCount > 0) {
for( int i = 0; i < mktDataOptionsCount; ++i) {
TagValue tagValue = (TagValue)mktDataOptions.get(i);
mktDataOptionsStr.append( tagValue.m_tag);
mktDataOptionsStr.append( "=");
mktDataOptionsStr.append( tagValue.m_value);
mktDataOptionsStr.append( ";");
}
}
send( mktDataOptionsStr.toString());
}
}
catch( Exception e) {
error( tickerId, EClientErrors.FAIL_SEND_REQMKT, "" + e);
close();
}
}
//The key piece of this code, REQ_MKT_DATA, leads to a final int variable within the EClientSocket.java object, equal to 1. tickPrice() is not mentioned anywhere.
//This method provides stock price, but doesn't return a value. You have to put executable code within this one method. I cannot duplicate and change the name of this method (tickprice();) because none of my accessible code calls it, to my knowledge. It feels as if TWS is calling tickPrice from its end.
public void tickPrice( int tickerId, int field, double price, int canAutoExecute) {
// received price tick
String msg = EWrapperMsgGenerator.tickPrice( tickerId, field, price, canAutoExecute);
m_tickers.add( msg );
}

The tickPrice method is called from EReader which gets created in EClientSocket which knows the EWrapper implementation.
Basically you call the socket reqMktData method and it will send it to TWS. EReader will see the response on the socket as a tickPrice message and will send it to the Wrapper implementation.
If you want to handle it yourself then you do it inside the tickPrice method. It could be just as simple as passing the data to a method you define.
public void tickPrice( int tickerId, int field, double price, int canAutoExecute) {
handleTick(tickerId,field,price);
}
And then write your own handleTick method

Finally may have found an answer. I'm new to Java...these appear to be callback methods. A callback method receives information from some other source. Because the method is part of an object in OOP, the returned information (stock info in this case) is returned into the callback method. Any other code that is contained within the callback method is executed when the method is replied to.
I am still not clear on how these methods are activated if they haven't been specifically executed in the code on my machine. Does Interactive Broker's know to feed information back to this method inside my Java program? Seems logical.

Related

Erase console text java

I am making a console chat program in java. Which accepts user text input and sends to server, server then broadcast this to all clients. I want to erase the text entered by user, on his console.
I would prefer platform independent solution.
import java.io.*;
class Test
{
public static void main(String args[])
{
System.out.print("you: ");
String t=getString();
System.out.println("We accepted : " + t);
}
static String getString()
{
String s;
try{
BufferedReader bufferRead = new BufferedReader(new InputStreamReader(System.in));
s = bufferRead.readLine();
//int count = 1;
//System.out.print(String.format("\033[%dA",count)); // Move up
//System.out.print("\033[2K"); // Erase line content, works on terminal but not on cmd
for(int i=0;i<s.length();i++)
System.out.print("\b"); //dont know, why it doesnt works??
}
catch(IOException e)
{e.printStackTrace(); s="Error";}
return s;
}
}
This is my interpretation of your question:
I am a client and I type a message, "Hello." Everybody else sees "Hello". However, by typing "Hello" into my console, the message is already there. I don't want to see another "Hello" appear on my screen from the announcer.
Is that the functionality you are trying to achieve? If so, I would suggest that you do not erase the message. If you've printed something to console, it might not be so easy to erase it. Rather you could just never send it in the first place.
The following is what I have generally used for messaging systems in which you don't want the sender to see his message twice:
Conveniently, you have a member object for each person connected to the server, and each member has a name. Hopefully names will be unique. Now, if you ensure that every member gets identified with a name upon connecting to your server, then when you announce the message to everyone, you just make sure that you don't announce it to the sender.
Specifically, I would modify the constructor and the run() method of the announcer class:
String message;
String senderName;
announcer( member x , String msg ) {
senderName = x.name;
message= x.name + " : " + msg ;
}
public void run() {
for ( int i=0 ; i<server.group.size() ; i++ ) {
if ( !server.group.get( i ).name.equals( senderName ) ) {
send( server.group.get( i ).sck );
}
}
}
Now, the message is sent to everyone except the sender, which achieves the equivalent result of sending it to the sender and then erasing it. I'm sorry if you will have to write more code to get everyone's name set up correctly, but I haven't heard of any implementations where duplicate messages were just "erased" from standard output.
Alternatively, if your server.group objects are the same as the member objects you pass into the announcer constructor, then this will work
String message;
member sender;
announcer( member x , String msg ) {
sender = x;
message= x.name + " : " + msg ;
}
public void run() {
for ( int i=0 ; i<server.group.size() ; i++ ) {
if ( server.group.get( i ) != sender ) {
send( server.group.get( i ).sck );
}
}
}

reducing number of return statements in a method

I have a java code in which there are multiple return statements in a single method. But for code cleaning purpose, I can have only one return statement per method. What can be done to overcome this.
Here is a method from my code:-
public ActionForward login(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
// Kill any old sessions
//request.getSession().invalidate();
DynaValidatorForm dynaform = (DynaValidatorForm)form;
// validate the form
ActionErrors errors = form.validate(mapping, request);
if(!errors.isEmpty()) {
this.saveErrors(request, errors);
return mapping.getInputForward();
}
// first check if token is set
if(!isTokenValid(request, true)) {
String errmsg="There was a problem with your login. Please close your browser then reopen it and try again. Make sure to click the Login button only ONCE.";
request.setAttribute("errormessage", errmsg);
saveToken(request);
return mapping.findForward(ConstantLibrary.FWD_CONTINUE);
}
// check the form for input errors
String errmsg = checkInput(form);
if (errmsg != null) {
request.setAttribute("errormessage", errmsg);
saveToken(request);
return mapping.findForward(ConstantLibrary.FWD_CONTINUE);
}
// no input errors detected
String resumekey = null;
// check for valid login
ObjectFactory objFactory = ObjectFactory.getInstance();
DataAccessor dataAccessor = objFactory.getDataAccessor();
request.setCharacterEncoding("UTF-8");
String testcode = dynaform.getString("testcode").trim();
String studentname = dynaform.getString("yourname").trim();
String password = dynaform.getString("password").trim();
// 4/3/07 - passwords going forward are ALL lower case
if (!CaslsUtils.isEmpty(password)) {
password = password.toLowerCase();
}
try{
resumekey = new String(studentname.getBytes("ISO-8859-1"),"UTF-8");
} catch (Exception e) {
log_.error("Error converting item content data to UTF-8 encoding. ",e);
}
String hashWord = CaslsUtils.encryptString(password);
// Make sure this is short enough to fit in the db
if (hashWord.length() > ConstantLibrary.MAX_PASSWORD_LENGTH) {
hashWord = hashWord.substring(0, ConstantLibrary.MAX_PASSWORD_LENGTH);
}
Login login = dataAccessor.getLogin(testcode, hashWord, false);
if (login == null || !login.getUsertype().equals(Login.USERTYPE_SUBJECT)) {
request.setAttribute("errormessage", "Incorrect test code or password.");
saveToken(request);
return mapping.findForward(ConstantLibrary.FWD_CONTINUE);
}
// Check if the login has expired
if (login.getLoginexpires() != null && login.getLoginexpires().before(new Date())) {
request.setAttribute("errormessage", "Your login has expired.");
saveToken(request);
return mapping.findForward(ConstantLibrary.FWD_CONTINUE);
}
// Check if the password has expired
if (login.getPasswordexpires() != null && login.getPasswordexpires().before(new Date())) {
request.setAttribute("errormessage", "Your login password has expired.");
saveToken(request);
return mapping.findForward(ConstantLibrary.FWD_CONTINUE);
}
HttpSession session = request.getSession();
session.setAttribute(ConstantLibrary.SESSION_LOGIN, login);
session.setAttribute(ConstantLibrary.SESSION_STUDENTNAME, studentname);
List<Testtaker> testtakers = null;
try {
//invalidate the old session if the incoming user is already logged in.
synchronized(this){
invalidateExistingSessionOfCurrentUser(request, studentname, testcode);
testtakers = dataAccessor.getTesttakersByResumeKey(studentname, login);// Adding this code to call getTesttakersByResumeKey instead of getTesttakers to improve the performance of the application during student login
}
} catch (Exception e) {
log.error("Exception when calling getTesttakers");
CaslsUtils.outputLoggingData(log_, request);
throw e;
}
session = request.getSession();
if(testtakers!=null)
{
if(testtakers.size() == 0) {
// new student -> start fresh
log_.debug("starting a fresh test");
// if this is a demo test, skip the consent pages and dump them directly to the select test page
if (login.getTestengine().equals(Itemmaster.TESTENGINE_DEMO)) {
return mapping.findForward("continue-panel");
}
}
// send them to fill out the profile
// check for custom profiles
String[] surveynames = new String[4];
List<Logingroup> logingroups = dataAccessor.getLoginGroupsByLogin(login.getLoginid());
for(Logingroup logingroup : logingroups) {
Groupmaster group = logingroup.getGroupmaster();
log_.debug(String.format("group: {groupid: %d, grouptype: %s, groupname: %s}", new Object[] {group.getGroupid(), group.getGrouptype(), group.getName()}));
Set<Groupsurvey> surveys = group.getGroupsurveys();
if(surveys.size() > 0) {
// grab the first (and only) one
Groupsurvey survey = surveys.toArray(new Groupsurvey[0])[0];
if(group.getGrouptype().equalsIgnoreCase(Groupmaster.GROUPTYPE_CLASS)) {
surveynames[0] = survey.getSurveyname();
} else if (group.getGrouptype().equalsIgnoreCase(Groupmaster.GROUPTYPE_SCHOOL)){
surveynames[1] = survey.getSurveyname();
} else if (group.getGrouptype().equalsIgnoreCase(Groupmaster.GROUPTYPE_DISTRICT)){
surveynames[2] = survey.getSurveyname();
} else if (group.getGrouptype().equalsIgnoreCase(Groupmaster.GROUPTYPE_STATE)){
surveynames[3] = survey.getSurveyname();
}
}
}
// match the most grandular survey
for(int i=0; i < surveynames.length; ++i) {
if(surveynames[i] != null) {
saveToken(request);
return mapping.findForward("student-profile-"+surveynames[i]);
}
}
// no custom profile, send them to the default
saveToken(request);
return mapping.findForward("student-profile");
}
// get the set of availible panels
Set<Panel> availiblePanels = dataAccessor.getAvailiblePanels(login, studentname);
if(availiblePanels.size() == 0) {
// no panels availible. send to all done!
log_.debug(String.format("No panels availible for Login:%s with resumekey:%s", login.toString(), studentname));
session.setAttribute("logoutpage", true);
resetToken(request);
return mapping.findForward("continue-alldone");
}
//Eventum #427 - Prevent test takers from retaking a finished test.
TestSubjectResult testSubjecResult=dataAccessor.getTestSubjectResult(login, resumekey);
if(testSubjecResult != null){
if(testSubjecResult.getRdscore() != null && testSubjecResult.getWrscore() != null && testSubjecResult.getLsscore() != null && testSubjecResult.getOlscore() != null){
if(testSubjecResult.getRdscore().getFinishtime() != null && testSubjecResult.getWrscore().getFinishtime() != null && testSubjecResult.getLsscore().getFinishtime() != null && testSubjecResult.getOlscore().getFinishtime() != null){
log_.debug(String.format("Already completed all the Skill Tests.", login.toString(), studentname));
session.setAttribute("logoutpage", true);
resetToken(request);
return mapping.findForward("continue-alldone");
}
}
}
// get a list of resumeable testtakers
List<Testtaker> resumeableTesttakers = new ArrayList<Testtaker>();
for(Testtaker testtaker : testtakers) {
if(testtaker.getPhase().equals(ConstantLibrary.PHASE_GOODBYE)) {
// testtaker is done with test. skip.
continue;
}
if(testtaker.getCurrentpanelid() == null) {
// testtaker is the profile testtaker
continue;
}
resumeableTesttakers.add(testtaker);
}
// sort them from least recent to latest
Collections.sort(resumeableTesttakers, new Comparator<Testtaker>() {
#Override
public int compare(Testtaker o1, Testtaker o2) {
// TODO Auto-generated method stub
//return 0;
return new CompareToBuilder()
.append(o1.getLasttouched(), o2.getLasttouched())
.toComparison();
}
});
if(resumeableTesttakers.size() == 0 && availiblePanels.size() > 0) {
// nobody is resumeable but there are panels left to take
// send them to the panel choice
// TODO: This is probably a misuse of Struts.
log_.info("No resumeable testtakers. Sending to panel select");
saveToken(request);
ActionForward myForward = (new ActionForward("/do/capstartpanel?capStartPanelAction=retest&lasttesttakerid="
+ testtakers.get(0).getTesttakerid(), true));
return myForward;// mapping.findForward(ConstantLibrary.FWD_CONTINUE + "-panel");
} else {
// grab the one most recently created and take their test
log_.info(String.format("Resuming with choice of %d testtakers", resumeableTesttakers.size()));
// we're forwarding to resume at this point, so we should do the some of the initialization
// that would have happened if we were still using getTesttaker() instead of getTesttakers() above.
session.setAttribute(ConstantLibrary.SESSION_LOGIN, login);
session.setAttribute(ConstantLibrary.SESSION_TESTTAKER, resumeableTesttakers.get(resumeableTesttakers.size()-1));
saveToken(request);
return mapping.findForward(ConstantLibrary.FWD_RESUME);
}
}
It's not a worth changing multiple returns to a single return statement per method. Actually, that will unnecessarily increase the burden of storing the result in a local variable and then making the return finally,
ActionForward result = null;
//scenario 1
result = ...
//scenario 2
result = ...
//scenario 3
result = ...
//finally
return result;
Hope this helps, but, it doesn't make much sense to me
As pointed out by others, having a single return statement does not necessarily make your code cleaner. However, in this case splitting up the method in smaller pieces probably makes the code more readable.
For example, this part:
// first check if token is set
if(!isTokenValid(request, true)) {
String errmsg="There was a problem with your login. Please close your browser then reopen it and try again. Make sure to click the Login button only ONCE.";
request.setAttribute("errormessage", errmsg);
saveToken(request);
return mapping.findForward(ConstantLibrary.FWD_CONTINUE);
}
// check the form for input errors
String errmsg = checkInput(form);
if (errmsg != null) {
request.setAttribute("errormessage", errmsg);
saveToken(request);
return mapping.findForward(ConstantLibrary.FWD_CONTINUE);
}
could be replaced by introducing two methods and using those to write:
If(tokenNotSet() || formHasErrors()){
return mapping.findForward(ConstantLibrary.FWD_CONTINUE);
}
By doing this on multiple places the structure of the algorithm becomes more clear, possibly giving you more insight in how this code could be refactored to adhere to your coding guidelines.
I would set a an action forward variable at the start of the method.
ActionForward actionForwardToReturn = null;
Then replace each of these two lines
return mapping.getInputForward();
return mapping.findForward(ConstantLibrary.FWD_CONTINUE);
with these two lines :
actionForwardToReturn = mapping.getInputForward()
actionForwardToReturn = mapping.findForward(ConstantLibrary.FWD_CONTINUE);
finally return the variable.
return actionForwardToReturn;
This shouldn't be too difficult :)
On a side note... (actually the orginal answer to the question) :
Multiple return statements can make it hard to debug code.
I personally would have just one action object that you return at the end of the method. The benefit of this, is that i can put a break point right on the return statement and look at exactly what that object is.
Any logging or other cross cutting concern I would want to add later, would only have to be done at one point. Otherwise I would have to add a log statement to every line where you are returning.
The complexity added to a method in an attempt to remove multiple return statements is many a times not worth it, especially in a method such as yours.There's nothing wrong with using them in this case.
Like user3580294 there's nothing wrong with multiple return statements. However you could combine the last two if statements since they are essentially returning the same thing.
Use #Octopus 's method if you absolutely have to have one return statement

Luaj attempt to index ? (a function value)

I am trying to compile Lua code that has two functions which I want to invoke and get some information from but when I use invokemethod on the LuaValue object, I get this error
LuaError: attempt to index ? (a function value)
The code is inside a LuaScript class I created for convenience
This method is first called to compile the file
public void compile(File file) {
try {
Globals globals = JmePlatform.standardGlobals();
compiledcode = globals.load(new FileReader(file), "script");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
And then this is used to invoke the function getSameTiles from my lua script
public Object invoke(String func, Object... parameters) {
if (parameters != null && parameters.length > 0) {
LuaValue[] values = new LuaValue[parameters.length];
for (int i = 0; i < parameters.length; i++)
values[i] = CoerceJavaToLua.coerce(parameters[i]);
return compiledcode.invokemethod(func, LuaValue.listOf(values));
} else
return compiledcode.invokemethod(func);
}
The error LuaError: attempt to index ? (a function value) occurs at the line return compiledcode.invokemethod(func); where "getSameTiles" is passed as the string for func
This is my Lua code
function getSameTiles()
--My code here
end
There are a couple of issues that needed fixing.
Firstly, in lua, load() returns a function which you'd then need to call to execute the script.
Secondly, what the script does is add a function to the global table _G. In order to invoke that function you'll need to get the function from the Globals table and call that.
The following code does this
Globals globals = JmePlatform.standardGlobals();
public void compile(File file) {
try {
globals.load(new FileReader(file), "script").call();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
public Object invoke(String func, Object... parameters) {
if (parameters != null && parameters.length > 0) {
LuaValue[] values = new LuaValue[parameters.length];
for (int i = 0; i < parameters.length; i++)
values[i] = CoerceJavaToLua.coerce(parameters[i]);
return globals.get(func).call(LuaValue.listOf(values));
} else
return globals.get(func).call();
}

How to convert from JSObject to Map (viceversa) or list JSObject members

In Javascript i have the following code:
var r=applet.foo({var0:99,var1:'foo',var2:applet});
In my Java applet i have the following:
public JSObject foo(JSObject args){
System.out.println("The function is correctly invoked");
//In fact, the following works perfectly:
System.out.println("var1 is:"+(String)args.getMember("var1"));
JSObject w=JSObject.getWindow(this);
JSObject j=(JSObject)w.eval("new Object();");
Map m=new Hashmap();
//TODO here all the keys and values of args should be added to m
m.put("hello","world");
//TODO here all the keys and values of m should be added to j
return j;
}
How can this be done? (TODOs)
Reading http://docstore.mik.ua/orelly/web/jscript/ch19_06.html, i noticed theres a getSlot method for JSObject but if i do
args.getSlot(0)
all i have is one Exception:
netscape.javascript.JSException: No such slot 0 on JavaScript object
...
Unfortunately, Errandir's solution works only when you know a name of global variable that can be used to access an object you want to get properties' names of. You need to know this name to be able to add keys method to the object, and invoke it using JSObject's call method later. Of course, you can pass a global name of your object to Java if you have it. This solution doesn't look so good especially when you can't refer to your object in global context.
As an alternative, I proposed to use this of JSObject's eval method in the comment supposing that it will do all the work. And it does. But a big disappointent was that it works as expected only in Mozilla Firefox and Opera. In Internet Explorer 9 and Google Chrome (tested under Windows 7 and Ubuntu 12.04 LTS) this of eval method always refers to applet's document window ignoring which JavaScript object JSObject instance actually represents. I don't know whether it's a bug or simply LiveConnect is supported in these browsers very poorly.
The good news is that call method of JSObject executes specified function on the proper context. Keeping that in mind I finally found a solution how a list of names of JavaScript object's properties can be retrieved. The idea is to define a temporary function in global context using eval method. This function has to receive a JavaScript object we want to get properties of and to return names of these properties as an array. After that we can invoke the temporary function through JSObject's call method passing a Java representation of concerned JavaScript object (jsObject in my method below or args as it sounds in the question). At last, temporary function can be removed.
public static ArrayList<String> getJsObjectPropertiesNames(Applet applet, JSObject jsObject) {
if (applet == null || jsObject == null)
return null;
// Retrieving global context - a JSObject representing a window applet belongs to
JSObject globalContext;
try {
globalContext = JSObject.getWindow(applet);
}
catch (JSException ex) {
return null;
}
// Checking whether passed object is not an array
try {
jsObject.getSlot(0);
return null;
}
catch (JSException e) {
}
String keysFunctionName = String.format("_getKeys%d", Calendar.getInstance().getTimeInMillis());
jsObject.eval("window['" + keysFunctionName + "'] = function(jsObject) { return Object.keys(jsObject) }");
JSObject propertiesNamesJsObject = (JSObject)globalContext.call(keysFunctionName, new Object[] { jsObject });
jsObject.eval("delete(window['" + keysFunctionName + "'])");
ArrayList<String> propertiesNames = new ArrayList<>();
try {
int slotIndex = 0;
while (true) {
Object propertyName = propertiesNamesJsObject.getSlot(slotIndex);
if (propertyName instanceof String)
propertiesNames.add((String)propertyName);
slotIndex++;
}
}
catch (JSException e) {
}
return propertiesNames;
}
As a solution, you could define method keys as proposed here (You can do it within your java-code using JSObject.eval(...)). Then you could get keys like:
JSObject keys = (JSObject)args.call("keys", Collections.EMPTY_LIST);
keys.getSlot(0);
Here below I print a String, please modify it to get whatever you need.
public final static String getKeys = "{var keys = [];for (var key in this) {keys.push(key);} keys;}";
private static String printProperties(final Object o,
final boolean printType,
final int level,
final String tab) {
final StringBuilder sb = new StringBuilder(100);
if (printType) {
sb.append("(");
sb.append(o.getClass().getSimpleName());
sb.append(") ");
}
if (o instanceof JSObject) {
sb.append("{\n");
final JSObject js = (JSObject) o;
final JSObject keys = (JSObject) js.eval(getKeys);
boolean needComma = false;
for (int i = 0;; i++) {
final String key = (String) keys.getSlot(i);
if ((key != null) && !(key.equals("undefined"))) {
final Object val = js.getMember(key);
if (!needComma) {
needComma = true;
} else {
sb.append(",\n");
}
sb.append(multitab(tab, level));
sb.append(key);
sb.append(":");
sb.append(printProperties(val, printType, level + 1, tab));
} else {
break;
}
}
sb.append("\n");
sb.append(multitab(tab, level - 1));
sb.append("}");
} else {
sb.append(o);
}
return sb.toString();
}
private final static String tab = " ";
private static String multitab(final String tab,
int i) {
final StringBuilder sb = new StringBuilder();
while (i-- > 0) {
sb.append(tab);
}
return sb.toString();
}

how to convert java object to plain string in j2me Bluetooth application?

I'm making a j2me Bluetooth application. I'm also new in java world. Where I have to display a Bluetooth service name to the user. So far it seems all is working correctly except service name. I verified my Bluetooth server is advertising service name correctly by other client (done by qt). I tried as follows:-
public void commandAction(Command command, Item item) {
if (item == deviceChoiceGroup) {
if (command == servicesDiscoverCommand) {
if(deviceList.size()==0) {
return;
}
UUID[] searchList = new UUID[1];
searchList[0] = new UUID("11111111111111111111111111111111",false);
int[] attrSet = new int[1];
attrSet[0] = 0x100;
RemoteDevice currentDevice =
(RemoteDevice) deviceList.elementAt(
getDeviceChoiceGroup().getSelectedIndex());
if(currentDevice == null) {
return;
}
try {
transactionID = bluetoothDiscoveryAgent.searchServices(
new int[] {0x100}, searchList, currentDevice, this);
printToForm("Start services under L2CAP searching...");
form.addCommand(cancelServicesDiscoverCommand);
} catch (BluetoothStateException e) {
//TODO: write handler code
}
}
}
}
public void servicesDiscovered(int transID, ServiceRecord[] serviceRecords){
if (serviceRecords.length>0 && serviceRecords!=null)
{
connectionURL=serviceRecords[0].getConnectionURL(0, false);
int[] ids=serviceRecords[0].getAttributeIDs();
DataElement ServiceName=serviceRecords[0].getAttributeValue(ids[1]);
// tried to convert objedct to string.
String str = (ServiceName.getValue()).toString();
// out is put is like java.util.vector$1#3c60cd14c
printToForm("#Service name: "+str);
printToForm("The Service name is: "+ServiceName.getValue());
}
}
"DataElement.getValue()" which returns object. Thus I can see service name as "java.util.vector$1#3c60cd14c". I tried to convert object to string as "String str = (ServiceName.getValue()).toString();" It doesn't convert correctly.
So how to convert object to string. So that I could see the service name in plain text. Thanks!
By seeing the result : java.util.vector$1#3c60cd14c i guess the returned object's type is Vector.
So try to cast to the Vector and iterate through it to get the values.
Iterator itr = serviceName.getValue().iterator();//do something here
System.out.println("Iterating through Vector elements...");
while(itr.hasNext())
System.out.println(itr.next());

Categories