Library for evaluating logical expressions on Java objects - java

Let's say I have the following class:
class Person {
int age;
String city;
Collection<Person> friends;
Person spouse;
}
I need a library which would allow me to evaluate whether a logical expression is true on a given Person object. The expression would look something like this:
((age>25 OR spouse.age>27) AND city=="New-York" AND size(friends)>100)
So, the requirements are:
Ability to use basic logical operators
Access properties of the given object
Access properties of an internal object
Use simple mathematical\sql-like functions such as size,max,sum
Suggestions?

You could use a ScriptEngine + reflection:
access all the fields in your object and create variable that have those values
evaluate the expression
Here is a contrived example which outputs:
age = 35
city = "London"
age > 32 && city == "London" => true
age > 32 && city == "Paris" => false
age < 32 && city == "London" => false
It could become quite messy if you want to deal with non primitive types, such as collections.
public class Test1 {
public static void main(String[] args) throws Exception{
Person p = new Person();
p.age = 35;
p.city = "London";
ScriptEngineManager factory = new ScriptEngineManager();
ScriptEngine engine = factory.getEngineByName("JavaScript");
Class<Person> c = Person.class;
for (Field f : c.getDeclaredFields()) {
Object o = f.get(p);
String assignement = null;
if (o instanceof String) {
assignement = f.getName() + " = \"" + String.valueOf(o) + "\"";
} else {
assignement = f.getName() + " = " + String.valueOf(o);
}
engine.eval(assignement);
System.out.println(assignement);
}
String condition = "age > 32 && city == \"London\"";
System.out.println(condition + " => " + engine.eval(condition));
condition = "age > 32 && city == \"Paris\"";
System.out.println(condition + " => " + engine.eval(condition));
condition = "age < 32 && city == \"London\"";
System.out.println(condition + " => " + engine.eval(condition));
}
public static class Person {
int age;
String city;
}
}

OK, think I found what I wanted, it's a variation on assylias' answer but I prefer it since it's more standardized (woudn't want to depend specifically on Javascript expressions, or run Javascript at all for that matter).
Apparently there is a Unified Expression Language for evaluating expressions, it was originally designed for JSP but can be used for other stuff as well. There are several parser implementations, I'll probably go either with Spring EL or JUEL

You can use rhino library from Mozilla. https://github.com/mozilla/rhino
Example :
#Test
public void shouldCheckLogicalOperations(){
Context cx = Context.enter();
try {
Scriptable scope = cx.initStandardObjects();
ScriptableObject.putProperty(scope, "form_admin_checked", true);
ScriptableObject.putProperty(scope, "form_limit_value", 50000);
String script = "((form_admin_checked && form_limit_value<2000) || (form_admin_checked && !form_admin_checked) || form_admin_checked && form_limit_value==50000)";
String expectedResponse = "true";
Object result = cx.evaluateString(scope, script, "<cmd>", 1, null);
Assert.assertEquals(expectedResponse, result.toString());
} finally {
// Exit from the context.
Context.exit();
}
}

Related

How to use a loop in a toString?

I need to print the name of the shop (this method is in the Shop class) as well as the product name (which is an arraylist) and the price of the product ( which is done using a getter from the class Product). I want to loop through all the products in the arraylist, how can I do that ?
Please if something is not clear comment it and I will edit the question to make it clearer.
public String toString()
{
for(int i=0; i<products.size();i++)
{
return "Shop"+"["+"name"+" = "+name+","+"Product name"+" = "+products.get(i).getName()+", "+"Price"+" = "+products.get(i).getPrice()+"]";
}
return null;
}
This is wrong as the i++ is a dead code which means that the loop will execute once. Any help please ?
Thanks for your time
Use StringBuilder:
public String toString() {
StringBuilder sb = new StringBuilder("Shop").append("[name = ").append(name)
for (Product product : products) {
sb.append(",")
.append("Product name = ").append(product.getName())
.append(", Price = ").append(product.getPrice())
.append("]");
}
return sb.toString();
}
Note:
Do not use + inside the loop to join strings (as accepted answer did) as it will create a stringbuilder in each iteration to join the strings, and then append it to the outer stringbuilder.
Using a return statement will end the function's execution. Here's what you want to do instead.
public String toString()
{
StringBuilder result = new StringBuilder();
result.append("Shop [ name = "+name+" ");
for(Product p : products)
{
result.append("Product name"+" = "+p.getName()+", "+"Price"+" = "+p.getPrice()+" ");
}
result.append("]");
return result.toString();
}
You could use StringJoiner ( https://docs.oracle.com/javase/8/docs/api/java/util/StringJoiner.html )
public String toString(){
StringJoiner string = new StringJoiner(", ", name + "[", "]");
for (Product p : list) {
string.add("Product name:" + p.getName()).add("Product value:"+p.getValue());
}
return string.toString();
}
Output:
Shop[Product name:Teste, Product value:1.0, Product name:teste2, Product value:2.0]
You can use Guava Objects Class it facilitates to build the object toString().They changed it recently to MoreObjects.ToStringHelper
public String toString(){
ToStringHelper toStringHelper = MoreObjects.toStringHelper(this);
for (Product p : products)
{
toStringHelper.add("name", p.name);
toStringHelper.add("Product name", p.getName();
}
return toStringHelper.toString();
}
If you have a small collection, you can always just concatenate strings with +.
Creating a StringBuilder provides unnecessary overhead for small examples.
Other than that, compiler will optimise that part of code If it provides improvement to the performance.
public String toString() {
String str = "Shop [name = " + name + " ";
for (Product product : products) {
str += ", Product name = " + product.getName();
str += ", Price = " + product.getPrice() + "]";
}
return str;
}

Multiple string replacements without affecting substituted text in subsequent iterations

I've posted about letters earlier, but this is an another topic, I have a json response that contain 2 objects, from and to , from is what to change, and to is what it will be changed to .
My code is :
// for example, the EnteredText is "ab b test a b" .
EnteredString = EnteredText.getText().toString();
for (int i = 0; i < m_jArry.length(); i++) {
JSONObject jo_inside = m_jArry.getJSONObject(i);
String Original = jo_inside.getString("from");
String To = jo_inside.getString("to");
if(isMethodConvertingIn){
EnteredString = EnteredString.replace(" ","_");
EnteredString = EnteredString.replace(Original,To + " ");
} else {
EnteredString = EnteredString.replace("_"," ");
EnteredString = EnteredString.replace(To + " ", Original);
}
}
LoadingProgress.setVisibility(View.GONE);
SetResultText(EnteredString);
ShowResultCardView();
For example, the json response is :
{
"Response":[
{"from":"a","to":"bhduh"},{"from":"b","to":"eieja"},{"from":"tes","to":"neesj"}
]
}
String.replace() method won't work here, because first it will replace a to bhduh, then b to eieja, BUT here's the problem, it will convert b in bhduh to eieja, which i don't want to.
I want to perfectly convert the letters and "words" in the String according the Json, but that what i'm failing at .
New Code :
if(m_jArry.length() > 0){
HashMap<String, String> m_li;
EnteredString = EnteredText.getText().toString();
Log.i("TestAf_","Before Converting: " + EnteredString);
HashMap<String,String> replacements = new HashMap<String,String>();
for (int i = 0; i < m_jArry.length(); i++) {
JSONObject jo_inside = m_jArry.getJSONObject(i);
String Original = jo_inside.getString("from");
String To = jo_inside.getString("to");
if(isMethodConvertingIn){
//EnteredString = EnteredString.replace(" ","_");
replacements.put(Original,To);
Log.i("TestAf_","From: " + Original + " - To: " + To + " - Loop: " + i);
//EnteredString = EnteredString.replace(" ","_");
//EnteredString = EnteredString.replace(Original,To + " ");
} else {
EnteredString = EnteredString.replace("_"," ");
EnteredString = EnteredString.replace("'" + To + "'", Original);
}
}
Log.i("TestAf_","After Converting: " + replaceTokens(EnteredString,replacements));
// Replace Logic Here
// When Finish, Do :
LoadingProgress.setVisibility(View.GONE);
SetResultText(replaceTokens(EnteredString,replacements));
ShowResultCardView();
Output :
10-10 19:51:19.757 12113-12113/? I/TestAf_: Before Converting: ab a ba
10-10 19:51:19.757 12113-12113/? I/TestAf_: From: a - To: bhduh - Loop: 0
10-10 19:51:19.757 12113-12113/? I/TestAf_: From: b - To: eieja - Loop: 1
10-10 19:51:19.757 12113-12113/? I/TestAf_: From: o - To: neesj - Loop: 2
10-10 19:51:19.758 12113-12113/? I/TestAf_: After Converting: ab a ba
You question would be clearer if you gave the expected output for the function.
Assuming it is: ab b test a b >>>> bhduheieja eieja neesjt bhduh eieja
then see the following, the key point in the Javadoc being "This will not repeat"
http://commons.apache.org/proper/commons-lang/javadocs/api-release/org/apache/commons/lang3/StringUtils.html#replaceEach(java.lang.String,%20java.lang.String[],%20java.lang.String[])
Replaces all occurrences of Strings within another String.
A null reference passed to this method is a no-op, or if any "search
string" or "string to replace" is null, that replace will be ignored.
This will not repeat. For repeating replaces, call the overloaded
method.
Example 1
import org.apache.commons.lang3.StringUtils;
public class StringReplacer {
public static void main(String[] args) {
String input = "ab b test a b";
String output = StringUtils.replaceEach(input, new String[] { "a", "b", "tes" },
new String[] { "bhduh", "eieja", "neesj" });
System.out.println(input + " >>>> " + output);
}
}
Example 2
import org.apache.commons.lang3.StringUtils;
public class StringReplacer {
public static void main(String[] args) {
String input = "this is a test string with foo";
String output = StringUtils.replaceEach(input, new String[] { "a", "foo" },
new String[] { "foo", "bar"});
System.out.println(input + " >>>> " + output);
}
}
Try following:
Solution 1:
Traverse the String characters one by one and move the new String to a new StringBuffer or StringBuilder, then call toString() to get the result. This will need you to implement string matching algorithm.
Solution 2 (Using Regex):
For this, you must know the domain of your string. For example, it is [a-zA-Z] then other arbitrary characters (not part of domain) can be used for intermediate step. First replace the actual characters with arbitrary one then arbitrary ones with the target. In example below, [!##] are the arbitrary characters. These can be any random \uxxxx value as well.
String input = "a-b-c";
String output = input.replaceAll("[a]", "!").replaceAll("[b]", "#").replaceAll("[c]", "#");
output = output.replaceAll("[!]", "bcd").replaceAll("[#]", "cde").replaceAll("[#]", "def");
System.out.println("input: " + input);
System.out.println("Expected: bcd-cde-def");
System.out.println("Actual: " + output);
Your issue is quite common. To sum things up :
String test = "this is a test string with foo";
System.out.println(test.replace("a", "foo").replace("foo", "bar"));
Gives : this is bar test string with bar
Expected by you : this is foo test string with bar
You can use StrSubstitutor from Apache Commons Lang
But first you will have to inject placeholders in your string :
String test = "this is a test string with foo";
Map<String, String> valuesMap = new HashMap<>();
valuesMap.put("a", "foo");
valuesMap.put("foo", "bar");
String testWithPlaceholder = test;
// Preparing the placeholders
for (String value : valuesMap.keySet())
{
testWithPlaceholder = testWithPlaceholder.replace(value, "${"+value+"}");
}
And then, use StrSubstitutor
System.out.println(StrSubstitutor.replace(testWithPlaceholder, valuesMap));
It gives : this is foo test string with bar
Here is an method which is strictly just Java. I tried not to use any Java 8 methods here.
public static String translate(final String str, List<String> from, List<String> to, int index) {
StringBuilder components = new StringBuilder();
String token, replace;
int p;
if (index < from.size()) {
token = from.get(index);
replace = to.get(index);
p = 0;
for (int i = str.indexOf(token, p); i != -1; i = str.indexOf(token, p)) {
if (i != p) {
components.append(translate(str.substring(p, i), from, to, index + 1));
}
components.append(replace);
p = i + token.length();
}
return components.append(translate(str.substring(p), from, to, index + 1)).toString();
}
return str;
}
public static String translate(final String str, List<String> from, List<String> to) {
if (null == str) {
return null;
}
return translate(str, from, to, 0);
}
Sample test program
public static void main(String []args) {
String EnteredString = "aa hjkyu batesh a";
List<String> from = new ArrayList<>(Arrays.asList("a", "b", "tes"));
List<String> to = new ArrayList<>(Arrays.asList("bhduh", "eieja", "neesj"));
System.out.println(translate(EnteredString, from, to));
}
Output:
bhduhbhduh hjkyu eiejabhduhneesjh bhduh
Explaination
The algorithm is recursive, and it simply does the following
If a pattern found in the string matches a pattern in the from list
if there is any string before that pattern, apply the algorithm to that string
replace the found pattern with the corresponding pattern in the to list
append the replacement to the new string
discard the pattern in the from list and repeat the algorithm for the rest of the string
Otherwise append the rest of the string to the new string
You could use split like:
String[] pieces = jsonResponse.split("},{");
then you just parse the from and to in each piece and apply them with replace() then put the string back together again. (and please get your capitalization of your variables/methods right - makes it very hard to read the way you have it)
Apache Commons StringUtils::replaceEach does this.
String[] froms = new String[] {"a", "b"};
String[] tos = new String[] {"b","c"};
String result = StringUtils.replaceEach("ab", froms, tos);
// result is "bc"
Why not keep it very simple (if the JSON is always in same format, EG: from the same system). Instead of replacing from with to, replace the entire markup:
replace "from":"*from*" with "from":"*to*"
Why not just change the actual "to" and "from" labels? That way, you don't run into a situation where "bhudh" becomes "eieja". Just do a string replace on "from" and "to".

Building Hibernate query depending of parameters that can be nulls

so i am working on a project right now
1st time using Hibernate
in this projet i am using Swing too
i have a form with multiple jTextFields
public List<Object[]> getoperations(String a,String c,String n,String e,String d) {
SessionDao s=new SessionDao();
session=s.getSession();
Query q;
q=session.createQuery("select idTiers,beneficiaire,emetteur,montant,numcompte,t_param_nature_operation.libelleNature,dateValidite,dateCreation where");
if (a != null && !a.isEmpty()) { q+= " and codeBanque='" + a + "'"; }
if (c != null && !c.isEmpty()) { q += " and numCompte='" + c + "'"; }
if (n != null && !n.isEmpty()) { q += " and t_param_nature_operation_.libelleNature='" + n + "'"; }
if (e != null && !e.isEmpty()) { q += " and decision='" + e + "'"; }
if (d != null && !d.isEmpty()) { q += " and dateCreation='" + d + "'"; }
q+= " order by idTiers" ;
return q.list();
}
As you see I am making a test on the values to add them in the query.
My question is there a way to add those values?
since query +="" isn't working.
Personally, I would add Guava utils to my project and use isNotBlank()
function. Anyway, you can write your own static function that would
return true if not null and not empty and false otherwise, and later
use it. It'll make your code much clearer.
The above was my comment and I decided to show you this little piece of code.
public static boolean isBlank(String s) {
if (s == null)
return true;
if (s.isEmpty())
return true;
return false;
}
Now you can simply write:
//static import your isBlank() method
//import static package.classInWhichIsBlankIsDeclared;
if (!isBlank(a) { q+= " and codeBanque='" + a + "'"; }
if (!isBlank(b) { q+= " and codeBanque='" + b + "'"; }
if (!isBlank(c) { q+= " and codeBanque='" + c + "'"; }
if (!isBlank(d) { q+= " and codeBanque='" + d + "'"; }
It's much more readable so it'll be much easier to debug in case of errors in the future.
Please, have a look at DRY principle and follow it. If your issue require checking same condition 4 or 5 times (2 times should be enough to use DRY) consider writing a function. call it the way that it'll be human-friendly instead of combination of different logical statements.
DRY. Don't Repeat Yourself.
"Every piece of knowledge must have a single, unambiguous, authoritative representation within a system"
Wikipedia article about DRY
you should consider using Criteria. it's more clean when dealing with multiple where statements.
eg
Criteria cr = session.createCriteria(YourEntityClass.class);
cr.add(Restrictions.eq("property1", value1));
cr.add(Restrictions.eq("property2", value2));
List results = cr.list();
have a look at these examples here

Erasing String that contains null

I have a null value in my message or println that i want to delete, all succeed when i just using code like this.
the message before :
message = 2014-06-02 14:53:37.103 null tes
Here is the code that delete the null word.
public static void main(String[] args) {
//final int month = Integer.parseInt(period[0]), year = Integer.parseInt(period[1]);
Date x = new Date();
Timestamp t = new Timestamp(x.getTime());
String a = "null";
String b = t+" " +a + " tes";
String tes = (b.trim()!= null && b.trim().length()>=23) ? b.trim().replaceFirst(b.trim().substring(0,28), ""+t) : b;
System.out.println("message = " + tes);
}
The printout is right. its like this :
message = 2014-06-02 14:53:37.103 tes
But when i insert this | the printout gone wrong. i'm using that as a separator.
This is the code that went wrong.
public static void main(String[] args) {
//final int month = Integer.parseInt(period[0]), year = Integer.parseInt(period[1]);
Date x = new Date();
Timestamp t = new Timestamp(x.getTime());
String a = "null";
String b = t+"| " +a + " tes";
String tes = (b.trim()!= null && b.trim().length()>=23) ? b.trim().replaceFirst(b.trim().substring(0,28), ""+t) : b;
System.out.println("message = " + tes);
}
And this is the print out :
message = 2014-06-02 14:58:03.148| null tes
What happen to the second code actually?
Thanks
As Boris said there are other ways, but the main problem is that replaceFirst takes a regex and pipe is a special character in regex.
Below with the introduction of Pattern.quote the code should work:
b.trim().replaceFirst(Pattern.quote(b.trim().substring(0,28)), ""+t)
If you want to simply strip out the 'null' text string without the regEx issues above, and quickly and cleanly, just replace the 'null' String directly.
b.replace("null", "")
What is exactly the main purpose of the program? if it is just to remove the null in the String, I would do as JamasA says. Otherwise if you want to get the timestamp and the "tes" string in two clean strings I would do it in this way:
String tes = b.replace("null", "");
String[] aux = tes.split("\\|");
for (int i = 0; i < aux.length; i++) {
System.out.println(aux[i].trim());
}
In this way you get both information separated.I hope it will helpful for you :)

for loop attaches wrong variable to output from pascal compiler to MIPS generator (I'm programming in Java)

I'm trying to do some code generation from Pascal to MIPS (via programming in Java). I'm currently just working on initializing the MIPS integer values. The code I've programmed to initialize the variables is:
for (StatementNode sn : statements) {
VariableNode var = null;
ValueNode value = new ValueNode("0");
if (sn instanceof AssignmentStatementNode) {
AssignmentStatementNode asn = (AssignmentStatementNode) sn;
for (VariableNode vr : vars) {
var = vr;
if (asn.getExpression() instanceof ValueNode) {
value = (ValueNode) asn.getExpression();
break;
}
}
answer += var.getName() + ": .word " + value.getAttribute()
+ "\n";
}
}
Based on the pascal code:
program assignSt;
var one, two, sum : integer;
begin
one := 1;
two := 2;
sum := one + two
end
.
It should be outputting:
.data
one: .word 1
two: .word 2
sum: .word 0
but instead I'm getting
.data
one: .word 1
one: .word 2
sum: .word 0
I've been programming all evening and I'd really like some fresh eyes on the problem. All help is much appreciated. I'm using QtSpim for my MIPS assembly(version 9.1.9) and Eclipse IDE for Java Developers (Juno service release 2).
I figured out how to answer my own question, however, I decided to take a different approach which involves a lot less code. Here's the answer I came up with, if anyone's curious:
public String writeCode(DeclarationsNode node, CompoundStatementNode node1) {
ArrayList<VariableNode> vars = node.getVars();
ArrayList<StatementNode> statements = node1.getStatements();
VariableNode var = null;
String answer = "";
for (StatementNode sn : statements) {
ValueNode value = new ValueNode("0");
if (sn instanceof AssignmentStatementNode) {
AssignmentStatementNode asn = (AssignmentStatementNode) sn;
for (VariableNode vr : vars) {
var = vr;
if (asn.getExpression() instanceof ValueNode) {
if (asn.getLvalue().getName().equals(var.getName())) {
value = (ValueNode) asn.getExpression();
answer += var.getName() + ": .word "
+ value.getAttribute() + "\n";
var.setDone(true);
break;
}
}
if (!var.isDone()) {
answer += var.getName() + ": .word "
+ value.getAttribute() + "\n";
var.setDone(true);
break;
}
}
}
}
return answer;
}
You should move the line that increments answer up inside the prior loop, before the 'break' statement. At present you're using a stale value for var. In fact you don't need the var variable at all: just use vr.

Categories

Resources