Create account / Log in

Using Java Integer and String functions

Talk about module design ideas and techniques.

Moderators: uckelman, Tim M

Using Java Integer and String functions

Postby m3tan » July 31st, 2020, 4:13 pm

Not sure if many people are aware, but Beanshell fully supports Java math and string functions:

http://www.vassalengine.org/wiki/How_to ... _in_VASSAL
http://www.vassalengine.org/wiki/How_to ... _in_VASSAL

I've already updated the wiki articles above with some hard-earned discoveries made by experimenting with those functions. One area I was encountering nothing but problems with was the usage of GetProperty in conjunction with those functions. So I thought I'd solicit opinions of those who are more familiar with the Vassal engine source code. I've had no problems working with Dynamic or Global Properties. Those are clearly defined as integers or strings, so virtually any Java manipulation worked. However, it seems like anything generated by GetProperty is neither a string nor an integer (maybe some other non-primitive type). For instance:

Integer.toString(Prague_Units).replace("0","").length()

Prague_Units is a numeric Global Property so Vassal reports it as an integer to Java. The above function converts that integer to a string, then replaces any "0" characters with a blank, and finally reports the length of that string. It works great. However, the same exact function fails when evaluated using GetProperty in place of a numeric Global Property:

Integer.toString(GetProperty(LocationName+"_Units")).replace("0","").length()
LocationName="Prague"

I got Java errors telling me it was expecting an integer, but that GetProperty(LocationName+"_Units") wasn't an integer. OK fine, it's a string. So then I changed the expression to:

GetProperty(LocationName+"_Units").replace("0","").length()

Then, I got Java errors telling me it was expecting a string, but that GetProperty(LocationName+"_Units") wasn't a string. So I thought "hmm, maybe it's undefined" and forcing it to be a string before passing it off to Java would work:

(GetProperty(LocationName+"_Units")+"").replace("0","").length()

Nope. Lastly, I tried:

Integer.toString(GetProperty(LocationName+"_Units"+0)).replace("0","").length()

Low and behold that worked!

So I've been able to unlock virtually every Java function through trial and error like this, but just curious if anybody knows what non-primitive type Vassal is handing off to Java with GetProperty...
User avatar
m3tan
 
Posts: 189
Joined: August 12th, 2018, 11:49 pm

Re: Using Java Integer and String functions

Postby Cattlesquat » July 31st, 2020, 4:55 pm

I discovered last night that (States).contains("Arkansas") works just as well as GetProperty("States").contains("Arkansas").

And (States).contains(State) works to check a property against a property.
User avatar
Cattlesquat
 
Posts: 890
Joined: December 2nd, 2019, 4:57 pm
Location: Baltimore, Maryland, USA

Re: Using Java Integer and String functions

Postby m3tan » July 31st, 2020, 5:31 pm

Yeah those wiki articles were not quite accurate and that's why I edited them. You don't need to use GetProperty, you just need parenthesis for math functions, and frankly, GetProperty just seems to cause problems so I'd avoid it unless you need it, for instance, to dynamically concatenate a property name.
User avatar
m3tan
 
Posts: 189
Joined: August 12th, 2018, 11:49 pm

Re: Using Java Integer and String functions

Postby Brent Easton » August 1st, 2020, 3:43 am

M3, good timing as I am working through the Beanshell stuff right now. I have already added the String and math functions to the Expression Builder, added some new functions and have been working with Brian on the Documentation.

I will work through reproducing your examples and investigating what is happening, but most problems come down to a fundamental 'impedance mismatch' between Vassal and Beanshell/Java. Beanshell/java are strongly typed. A variable is a String or it is an integer or it it is a float or it is a boolean. You can't multiply two strings together, even if they are "1" and "2". Vassal on the other hand, is untyped. Everything is String. Integers are stored as Strings, booleans as "true" or "false". You can multiply two Strings together if they are "1" and "2".

GetProperty tries to return the property value as a suitable type based on inspection of it's value. Integers are returned as int's. The String "true" or "false" are returned as boolean. Anything else is returned as a String.

Anyway, I will follow through your examples above and see where that leads us.

Rgds.
User avatar
Brent Easton
 
Posts: 3168
Joined: December 21st, 2007, 3:06 am
Location: Berry, NSW, Australia

Re: Using Java Integer and String functions

Postby Brent Easton » August 1st, 2020, 6:56 am

Integer.toString(Prague_Units).replace("0","").length()


This also works: (Prague_Units).toString().replace("0","").length()

However, I have reworked part of the Beanshell evaluator, so that the following will also work:

Prague_Units.replace("0","").length()

Essentially, at the time of the initial Parsing of the expression, I can locate any calls to String functions on unknown variables in the expression. I then modified the evaluator to force these variables to have the correct type of String when being evaluated and to force the value passed in to be a String.

This has 2 effects:
1. It allows String functions to be called directly on Property Names
2. It force the Property Name value to be a String, even if it looks like a number or a boolean.

This should simplify things and make String functions easier to use.

(Brian, I found an efficient method to implement the idea I suggested just for the property names that call String functions).

GetProperty(LocationName+"_Units").replace("0","").length()


The reason that this doesn't work is the the value returned from the Prague_Units property in your module looks like a number, so gets returned as a number and you can can't call replace() on a number.

(GetProperty(LocationName+"_Units")+"").replace("0","").length()


The reason this didn't work is that we made a specific change to Beanshell so that adding an empty String to a number is the same as adding 0, so you still ended up trying to call replace() on a number.

If you want to call String functions on the results of a GetProperty() call and there is a possibility that the property may contain a number or "true" or "false", then you need to add in a .toString() call:

GetProperty(LocationName+"_Units").toString().replace("0","").length()

Rgds.
User avatar
Brent Easton
 
Posts: 3168
Joined: December 21st, 2007, 3:06 am
Location: Berry, NSW, Australia


Return to Module Design

Who is online

Users browsing this forum: No registered users and 4 guests