Spring expression language - referencing variables - spring-el

I am able to do the following :-
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("'hi'.equals('hi')");
it evaluates to true.
Im failing at using variables in the expression ? for example:
If i want to :
Case 1 : use local variables
String str1 = "hi";
String str2 = "hi";
Expression exp = parser.parseExpression("str1.equals(str2)");
and Case2 : use field from a pojo :
Expression exp = parser.parseExpression("pojo1.getName().equals(pojo2.getName())");
How do i achieve case1 and 2 ? The above 2 examples are failing for me.. Case 1 evaluates to false and Case 2 throws an exception

You could do it like this:
Case 1:
String str1 = "hi";
String str2 = "hi";
StandardEvaluationContext evaluationContext = new StandardEvaluationContext();
evaluationContext.setVariable("str1", str1);
evaluationContext.setVariable("str2", str2);
ExpressionParser parser = new SpelExpressionParser();
boolean result = parser.parseExpression("#str1 eq #str2").getValue(evaluationContext, Boolean.class);
// result will be true
// also "#str1 == #str2" can be used
Case 2:
StandardEvaluationContext evaluationContext = new StandardEvaluationContext();
evaluationContext.setVariable("pojo1Name", pojo1.getName());
evaluationContext.setVariable("pojo2Name", pojo2.getName());
ExpressionParser parser = new SpelExpressionParser();
boolean result = parser.parseExpression("#pojo1Name eq #pojo2Name").getValue(evaluationContext, Boolean.class);

Case 1.
You have to register variables with evaluation context, then use #str1 in the expression.
/**
* Set a named variable within this evaluation context to a specified value.
* #param name the name of the variable to set
* #param value the value to be placed in the variable
*/
void setVariable(String name, #Nullable Object value);
Case 2:
Use a BeanExpressionResolver wired up with the bean factory. If you implement BeanFactoryAware, use
#Override
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
if (beanFactory instanceof ConfigurableListableBeanFactory) {
this.resolver = ((ConfigurableListableBeanFactory) beanFactory).getBeanExpressionResolver();
this.expressionContext = new BeanExpressionContext((ConfigurableListableBeanFactory) beanFactory, null);
}
}
then call evaluate.
Beans are referenced with # - #someBean.someMethod().

Related

Test if string contains anything from an array of strings (kotlin)

I'm new to Kotlin (I have a Java background) and I can't seem to figure out how to check whether a string contains a match from a list of keywords.
What I want to do is check if a string contains a match from an array of keywords (case-insensitive please). If so, print out the keyword(s) that was matched and the string that contained the keyword. (I will be looping over a bunch of strings in a file).
Here's an MVE for starters:
val keywords = arrayOf("foo", "bar", "spam")
fun search(content: String) {
var match = <return an array of the keywords that content contained>
if(match.size > 0) {
println("Found match(es): " + match + "\n" + content)
}
}
fun main(args: Array<String>) {
var str = "I found food in the barn"
search(str) //should print out that foo and bar were a match
}
As a start (this ignores the 'match' variable and getting-a-list-of-keywords-matched), I tried using the following if statement according with what I found at this question,
if(Arrays.stream(keywords).parallel().anyMatch(content::contains))
but it put a squiggly line under "content" and gave me this error
None of the following functions can be called with the arguments
supplied: public operator fun CharSequence.contains(char: Char,
ignoreCase: Boolean = ...): Boolean defined in kotlin.text public
operator fun CharSequence.contains(other: CharSequence, ignoreCase:
Boolean = ...): Boolean defined in kotlin.text #InlineOnly public
inline operator fun CharSequence.contains(regex: Regex): Boolean
defined in kotlin.text
You can use the filter function to leave only those keywords contained in content:
val match = keywords.filter { it in content }
Here match is a List<String>. If you want to get an array in the result, you can add .toTypedArray() call.
in operator in the expression it in content is the same as content.contains(it).
If you want to have case insensitive match, you need to specify ignoreCase parameter when calling contains:
val match = keywords.filter { content.contains(it, ignoreCase = true) }
Another obvious choice is using a regex doing case-insensitive matching:
arrayOf("foo", "bar", "spam").joinToString(prefix = "(?i)", separator = "|").toRegex())
Glues together a pattern with a prefixed inline (?i) incase-sensitive modifier, and alternations between the keywords: (?i)foo|bar|spam
Sample Code:
private val keywords = arrayOf("foo", "bar", "spam")
private val pattern = keywords.joinToString(prefix = "(?i)", separator = "|")
private val rx = pattern.toRegex()
fun findKeyword(content: String): ArrayList<String> {
var result = ArrayList<String>()
rx.findAll(content).forEach { result.add(it.value) }
return result
}
fun main(args: Array<String>) {
println(findKeyword("Some spam and a lot of bar"));
}
The regex approach could be handy if you are after some more complex matching, e.g. non-/overlapping matches adding word boundaries \b, etc.
Here is my approach without Streams:
fun String.containsAnyOfIgnoreCase(keywords: List<String>): Boolean {
for (keyword in keywords) {
if (this.contains(keyword, true)) return true
}
return false
}
Usage:
"test string".containsAnyOfIgnoreCase(listOf("abc","test"))
I think Any is the efficient way.
fun findMatch(s: String, strings: List<String>): Boolean {
return strings.any { s.contains(it) }
}
fun main() {
val today = "Wednesday"
val weekend = listOf("Sat", "Sun")
println(if (findMatch(today, weekend)) "Yes" else "No") // No
}
reference: click here

Convert a byte Array to Type from String

I would like to find an elegant solution to solve my problem.
I would like to do a dynamic byte conversion, in a type value defined in String ex. "Int32", "Byte", "UInt16" derived from xml file.
This is an example. is it possible to automate the conversion to remove the Switch Case?
public static void Main(string[] args)
{
byte[] data = {77,4}; // (256 * 4) + 77 --> 1101
object value = ToType(data, "Unt16");
Console.WriteLine(value);
}
unsafe public static dynamic ToType(byte[] data, String typeStr)
{
fixed (byte* p = &data[0])
{
switch(typeStr)
{
case "Int16":
return *((Int16*)p);
case "Unt16":
return *((UInt16*)p);
case "Int32":
return *((Int32*)p);
case "... ...":
....
default:
return null;
}
}
}

When i running the multiple execute statement service i got the error "Cannot read property "parameters" from null "

I want to insert the below default values when i am running the service i got this below error any one please tell me how to resolve.
Runtime error in script ("Process: 'CustomPersonalGS Practice' ProcessItem: 'Initialize' Type: 'ITEM'" -1:-1).TypeError: Cannot read property "parameters" from null
//Initialise SQL Query List
tw.local.sqlQueries = new tw.object.listOf.SQLStatement();
tw.local.sql = "";
tw.local.customerPD = new tw.object.customerPD1BO();
tw.local.customerPD.customerPersonalDetailsList = new tw.object.listOf.customerSpecificPersonalDetailsListBO();
var custPersonalDetails = new tw.object.customerSpecificPersonalDetailsListBO();
custPersonalDetails.customerId = "8467";
custPersonalDetails.custPersonalDetailsId = "8";
custPersonalDetails.isBPMEnabled = true;
custPersonalDetails.isCCPEnabled = true;
custPersonalDetails.isCCPMandatory = true;
custPersonalDetails.isLatestVersion = true
tw.local.customerPD.customerPersonalDetailsList.insertIntoList(tw.local.customerPD.customerPersonalDetailsList.listLength, custPersonalDetails);
tw.local.sql = "INSERT INTO CUSTOMPERSONALDETAILSQUESTION(CUSTOMERID,CUSTPERSONLADETAILSID,ISBPMENABLED,ISCCPENABLED,ISCCPMANDATORY,ISLATESTVERSION) VALUES (?,?,?,?,?,?) ";
function addSQLStatement() {
tw.local.sqlQueries[tw.local.sqlQueries.listLength] = new tw.object.SQLStatement();
}
function addParam(value,type,mode) {
log.info("VALUE :" + value);
var newParam = new tw.object.SQLParameter();
newParam.value = value;
newParam.type = type;
newParam.mode = mode;
if( tw.local.sqlQueries == null){
tw.local.sqlQueries = new tw.object.listOf.SQLStatement();
}
if( tw.local.sqlQueries[tw.local.sqlQueries.listLength] == null ){
tw.local.sqlQueries.insertIntoList(tw.local.sqlQueries.listLength, new tw.object.SQLStatement());
}
if(tw.local.sqlQueries[tw.local.sqlQueries.listLength].parameters == null ){
tw.local.sqlQueries[tw.local.sqlQueries.listLength].parameters = new tw.object.listOf.SQLParameter();
}
var paramsLength = tw.local.sqlQueries[tw.local.sqlQueries.listLength].parameters.listLength;
tw.local.sqlQueries[tw.local.sqlQueries.listLength].parameters[paramsLength] = newParam;
}
for(var i=0;i<tw.local.customerPD.customerPersonalDetailsList.listLength;i++){
addSQLStatement(tw.local.sql);
addParam(tw.local.customerPD.customerPersonalDetailsList[i].customerId,"VARCHAR","IN");
addParam(tw.local.customerPD.customerPersonalDetailsList[i].custPersonalDetailsId,"VARCHAR","IN");
var yesNoFlag = "N";
if(tw.local.customerPD.customerPersonalDetailsList[i].isBPMEnabled){
yesNoFlag="Y";
addParam(yesNoFlag,"CHAR","IN");
}
yesNoFlag = "N";
if(tw.local.customerPD.customerPersonalDetailsList[i].isCCPEnabled){
yesNoFlag="Y";
addParam(yesNoFlag,"CHAR","IN");
}
yesNoFlag = "N";
if(tw.local.customerPD.customerPersonalDetailsList[i].isCCPMandatory){
yesNoFlag="Y";
addParam(yesNoFlag,"CHAR","IN");
}
yesNoFlag = "N";
if(tw.local.customerPD.customerPersonalDetailsList[i].isLatestVersion){
yesNoFlag="Y";
addParam(yesNoFlag,"CHAR","IN");
}
}
You didn't initialize the parameter list in your SQL as far as I can tell. That is on line 38 you call -
var paramsLength = tw.local.sqlQueries[tw.local.sqlQueries.listLength].parameters.listLength;
However when you create the entry in the tw.local.sqlQueries, you didn't initialize the parameter array. I'll also note that your addSQLStatement() function ignore the sql input (and that value is hard coded, so really you don't need to pass it in). I think if you change addSQLStatement to be something like -
function addSQLStatement(query) {
var targetQuery = new tw.object.SQLStatement();
targetQuery.sql = query;
tagetQuery.params = new tw.object.listOf.SQLParameter();
tw.local.sqlQueries[tw.local.sqlQueries.listLength] = targetQuery;
}
then your code will work. Additionally you could actually return targetQuery from this function and then pass it to the "addParams" method eliminating the need to find the last one in the array. Alternatively insert it in the beginning of the array instead and just update the 0th item instead of the last.
-AP
This comparison will never work properly. array[array.length] will always be null (line 35).
if (tw.local.sqlQueries[tw.local.sqlQueries.listLength].parameters == null ){
In addition, in the next lines, if you want to work with the last element of the list, you might want to use something like array[array.length - 1]. Personally, I'd use some temporary variable, do some stuff with it and insert it into the list at the end (similar to #Drux's answer).

Evaluating null varible in boolean expression

When I try to evaluate a boolean expression that contain a variable with null value or evaluate a undefined variable, the parser not work as I expected, it does not fail, rather than, it assume the null variable (or the undefined variable) as big negative number (I guess...).
Here the Test class I wrote to show this:
public class SpELTest {
#Test(expected = Exception.class)
public void evaluateNullVariable() {
ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
context.setVariable("PERFORMANCE", null);
Boolean result = (Boolean)parser.parseExpression("#PERFORMANCE < 100").getValue(context);
assertTrue(result); // no expected result
}
#Test(expected = Exception.class)
public void evaluateUndefinedVariable() {
ExpressionParser parser = new SpelExpressionParser();
Boolean result = (Boolean)parser.parseExpression("#UNDEFINED < 100").getValue();
assertTrue(result); // no expected result
}
}
any idea of this behavior or how to avoid it?
"#PERFORMANCE == null ? false : #PERFORMANCE < 100"

substring from an array as3 **Error #1009: Cannot access a property or method of a null object reference

I am trying to create a matching game where one object in the array hitBoxes is matched to one object in the array hitBoxes2. I have tried to convert the instance name into a string and then used the substring method to match the LAST number in the instance name, if its a match they win. Right now I'm getting the error
TypeError: Error #1009: Cannot access a property or method of a null
object reference. at MethodInfo-499()
I'm wondering if anyone can help me. Thanks!
var left:String;
var correct:MovieClip = new Correct;
var isClicked:Boolean = false;
var leftClicked:int = 0;
p3.nextPage.buttonMode = true;
p3.nextPage.addEventListener(MouseEvent.CLICK, nextPage);
function nextPage(MouseEvent):void{
removeChild(p3);
}
var hitBoxes:Array = [p3.a1, p3.a2, p3.a3, p3.a4, p3.a5, p3.a6, p3.a7, p3.a8];
var hitBoxes2:Array = [p3.b1, p3.b2, p3.b3, p3.b4, p3.b5, p3.b6, p3.b7, p3.b8];
for (var h:int = 0; h < hitBoxes.length; h++){
hitBoxes[h].buttonMode = true;
hitBoxes[h].addEventListener(MouseEvent.CLICK, matchingLeft);
}
for (var h2:int = 0; h2 < hitBoxes2.length; h2++){
hitBoxes2[h2].buttonMode = true;
hitBoxes2[h2].addEventListener(MouseEvent.CLICK, matchingRight);
}
function matchingLeft(e:MouseEvent):void{
var left = String(e.currentTarget.name);
isClicked = true;
trace(left);
}
function matchingRight(e:MouseEvent):void{
var right:String = String(e.currentTarget.name);
trace(right);
if(isClicked == true && left.substring(3,3) == right.substring(3,3)){
trace("matched");
}
}
According to your code variable "left" is null at matchingRight method, because matchingLeft uses its local variable with name "left", and top-level "left" still has its default value.
also String.substring method is used incorrectly:
var name:String="p3.a1";
trace(name.substring(3, 3)); // this will always output empty string ""
trace(name.substring(4, 5)); // this will output "1" string
in conclusion I'd advise to use array indices (integers) instead of strings when calculating "matched" condition, substring operation and string comparison are CPU intensive.

Resources