Drool Rules: Arithmetic Operations - drools

I'm trying to write a simple rule. To perform a set of calculations. I understand that the calculations can be handled via functions. But, The business logic involves calculations.
package com.sample.rules
import com.sample.rules.Test
rule "Calculate"
when
Test(calculate == true)
then
finalValue = Test(InitialValue) + 10;
Test.setFinalValue(finalValue)
System.out.println("FinalValue=" + Test.getFinalValue();
end
The Class file with variable
public class Test {
private int finalValue;
private int initialValue;
private boolean calculate;
public int getFinalValue() {
return FinalValue;
}
public void setFinalValue(int FinalValue) {
this.FinalValue = FinalValue;
}
public int getInitialValue() {
return InitialValue;
}
public void setInitialValue(int InitialValue) {
this.InitialValue = InitialValue;
}
public boolean isCalculate() {
return calculate;
}
public void setCalculate(boolean calculate) {
this.calculate = calculate;
}
}
The App file is as shown below
public class CalculationApp {
public static void main(String[] args) {
System.out.println( "Bootstrapping the Rule Engine ..." );
KieServices ks = KieServices.Factory.get();
KieContainer kContainer = ks.getKieClasspathContainer();
KieSession kSession = kContainer.newKieSession("ksession-rules");
Test test = new Test();
test.setInitialValue(20);
test.setCalculate(true);
kSession.insert(test);
int fired = kSession.fireAllRules();
System.out.println( "Number of Rules executed = " + fired );
}
}
The rule file throws an error :
Rule Compilation error finalValue cannot be resolved to a variable
Cannot make a static reference to the non-static method getInitialValue() from the type Test
finalValue cannot be resolved to a variable
Cannot make a static reference to the non-static method getFinalValue() from the type Test
While I found the answer by Trial and Error: I'm trying to understand the logic behind it. I mean, Assigning a variable (I understand variable has a different meaning) /assignment in the when condition is understandable, but the same the how does the Then part work. I mean, the item.setFinalValue(...) part.

Your rule has several problems, as indicated by the error.
Rule Compilation error finalValue cannot be resolved to a variable
finalValue cannot be resolved to a variable
The first problem is that you never declare finalValue, just attempt to assign to it on the right hand side. To fix this, simply declare finalValue. Assuming it's an integer:
int finalValue = ...
This is identical to how it works in Java.
However given what the original rule looks like, I think you're trying to pull the initial value from the Test object. To do that, you would do something like this:
rule "Calculate"
when
Test( calculate == true,
$initialValue: initialValue ) // assign the $initialValue variable
then
int finalValue = $initialValue + 10; // set finalValue by adding 10 to $initialValue
Cannot make a static reference to the non-static method getInitialValue() from the type Test
Cannot make a static reference to the non-static method getFinalValue() from the type Test
Both of these errors indicate that you're trying to call instance methods without an object instance. This is once again the same as in Java, where if you have an instance method you must call it against an instance.
Assuming these are methods on the Test class, you would change your rule to be:
package com.sample.rules
import com.sample.rules.Test
rule "Calculate"
when
$test : Test( calculate == true,
$initialValue: initialValue )
then
int finalValue = $initialValue + 10;
$test.setFinalValue(finalValue); // call against $test instance
System.out.println("FinalValue=" + $test.getFinalValue()); // you were also missing a parenthesis
end

What worked was :
rule "Calculate"
when
item: Test(calculate == true)
then
item.setFinalValue(item.getInitialValue() + 10)
System.out.println("FinalValue=" + item.getFinalValue();
end

Related

Setting and getting a Global variable in Drools

I have something like this in drl file:
import java.lang.String
global String result;
rule ''Rule 1'' when some condition
then
result = "PASS";
kcontext.getKnowledgeRuntime().setGlobal("Result", result); // I got an "Unexpected global" exception.
System.out.println("result = "+ Result);
Also, I don't know how to access this global variable from my MyService.java class.
I was trying to set a global variable from the drl file not my java class like Service class.
All I had to do was the following and it worked successfully
import java.lang.String
global String result;
rule ''Rule 1''
when
some condition
then
String grade = "PASS";
kcontext.getKnowledgeRuntime().setGlobal("result", grade);
end
Also, the global variable name should match what I pass on the setGlobal("result",...).
And then get the global variable using the session I have in the Service class. like:
session.getGlobal("result");
Your rule should not be touching the 'kcontext'. What in the world are you trying to do? result = "PASS" is sufficient for setting the value of the global.
global String result
rule "Rule 1"
when
// some condition
then
result = "PASS";
end
Of course it's not going to work like you want it to because you need to change the value of the existing object; you can't overwrite it like that. Some options might be a "ResultsHolder" sort of class with a boolean variable you can set; or maybe even an AtomicBoolean that you can call set on.
To fire rules with a global, you need to add the global objects to the KieBase before invoking your rules:
var value = ...; // some OBJECT which you are going to pass in as a global
KieSession session = ruleBase.newStatefulSession();
session.insert(...); // insert data
session.setGlobal( "myGlobalFoo", value ); // sets the global; note the name must match the rule file!
session.fireAllRules();
After the rules are fired, you'll have your reference to value that you can use. This is also why you can't pass strings as globals and expect them to capture changes -- Java is pass-by-value, not pass-by-reference.
Here's an example for passing results out of the rules. This toy app will check the student's score on a test and then decide if they passed or failed.
Classes:
class Student {
private String name;
public String getName() { return this.name; }
public void setName(String name) { this.name = name; }
}
class Exam {
private String name;
private Double score;
public String getName() { return this.name; }
public void setName(String name) { this.name = name; }
public Double getScore() { return this.score; }
public void setScore(String score) { this.score = score; }
}
class ExamResults {
private List<String> results = new ArrayList<>();
public void logResults( String name, Double score, boolean passed ) {
this.results.add(name + " scored " + score + "%, which is a " + (passed ? "passing": "failing") + " grade.");
}
public List<String> getResults() { return this.results; }
}
Rule:
global ExamResults results;
rule "Evaluate exam"
when
Student( $name: name )
Exam ( $score: score, name == $name )
then
boolean passed = $score > 60.0;
results.logResults( $name, $score, passed );
end
Invocation:
List<Student> students = ...;
List<Exam> exams = ... ;
ExamResults results = new ExamResults();
KieSession session = ruleBase.newStatefulSession();
students.forEach( student -> session.insert(students) );
exams.forEach( exam -> session.insert(exam) );
session.setGlobal( "results", results);
session.fireAllRules();
// Print the results:
results.getResults().forEach(System.out::println);
If all you're trying to do is to get some data out of your rules (eg whether certain conditions match), I've written up answers about how to do that previously here and here. If you just want to know what rules triggered, you should write a listener which logs rule hits ("afterMatchFired").

Addition program in Drools

just out of curiosity i am trying to write a very simple rule in Drools of addition.(I know its not a very mature thing, but still...)
But getting error that $firstNum and $secondNum can't be assign to a variable. help me out...
rule "Addition rule"
when
act : CalcOperation(CalcOperation.ADD, $firstNum : firstNum, $secondNum : secondNum)
then
$out : $firstNum + $secondNum;;
//logger.info("Result of addition is : "+$out);
end
...............................................................................
Here is the CalcOperation class.
public class CalcOperation {
Double firstNum;
Double secondNum;
public static Boolean ADD;
public static Boolean SUB;
public static Boolean MUL;
public static Boolean DIV;
public CalcOperation(Boolean operation, Double m, Double n){
this.firstNum = m;
this.secondNum = n;
}
You'll need to dig more into Java, and a lot more into Drools documentation.
A static variable isn't useful in the context you are using it. If you want to have separate objects for addition, subtraction etc. you could subclass Operation. Instance variables should be private, and at least have a getter.
In a rule, you cannot bind a variable ($firstNum etc.) to a field unless it has a proper getter (or is public - not recommended).
The then-part or consequence must be written in Java. $out : $firstNum + $secondNum;; is not a valid Java statement.
Here's a Java class:
public enum Operator { ADD, SUB, MUL, DIV };
public class Operation {
private double op1;
private double op2;
private Operator op;
public Operation( Operator op, double op1, double op2 ){
this.op = op;
//...
}
public Operator getOp(){ return op; }
//...
}
And here's the rule:
rule "exec op"
when
$op: Operation( $op: Operator.ADD, $op1: op1, $op2: op2 )
then
System.out.writeln( "result: " + ($op1 + $op2) );
end

How to get down to StringLiterals with Eclipse AST?

I need to create an Eclipse plugin that displays a tooltip when I hover the mouse over a String literal.
But only if that String literal is the first parameter of a special method.
Here is the Test.java file I use to test my plugin:
package test;
public class Test {
public static void main(String[] args) {
String hello = "Hello";
String world = Translator.get("Test.worldLabel");
System.out.println(hello + " " + world);
}
}
I created a class implementing IJavaEditorTextHover and I need to compile the currently edited Java file to compute if the cursor is hovering a String that needs to be translated or not.
Hovering "Hello" will do nothing.
Hovering "Test.worldLabel" will display my tooltip because that literal is included inside a Translator.get() method call.
At first I used this (170 is inside "Test.worldLabel"):
ITypeRoot typeRoot = (ITypeRoot)
JavaUI.getEditorInputJavaElement(editorPart.getEditorInput());
JavaElement foundElement = (JavaElement) typeRoot.getElementAt(170);
But the foundElement contains the whole main() method: it is not fine-grained enough.
Then, the correct way is, I think:
private static ASTNode parse(ICompilationUnit unit, int position) {
ASTParser parser = ASTParser.newParser(AST.JLS3);
parser.setKind(ASTParser.K_COMPILATION_UNIT);
parser.setSource(unit);
parser.setResolveBindings(true);
parser.setIgnoreMethodBodies(false);
// TODO Future optimisation: parser.setFocalPosition(position);
return parser.createAST((IProgressMonitor) null); // parse
}
And in my IJavaEditorTextHover.getHoverInfo(...) implementation:
ICompilationUnit compilationUnit = (ICompilationUnit)
JavaUI.getEditorInputJavaElement(editor.getEditorInput())
int position = 170/*hoverRegion.getOffset()*/;
ASTNode ast = parse(compilationUnit, position);
And now, here is my question:
How, from this ast node, do I get the ASTNode reprensenting the StringLiteral at position 170 in the source code (the "Test.worldLabel" String)?
Bonus question: did I choose the right solution? On a performance basis.
Edit:
Well, here is a solution I found:
private StringLiteral findStringLiteralAtPosition(final ASTNode parent, final int position) {
final List<StringLiteral> stringLiterals = new ArrayList<StringLiteral>();
parent.accept(new ASTVisitor() {
#Override
public boolean visit(StringLiteral stringLiteral) {
int start = stringLiteral.getStartPosition();
int end = start + stringLiteral.getLength();
if (start <= position && position <= end) {
stringLiterals.add(stringLiteral);
}
return super.visit(stringLiteral);
}
});
return (stringLiterals.size() > 0 ? stringLiterals.get(0) : null);
}
Does it seam OK?
Or is it an easier way or a more performant one?
One solution will be not using the offset logic at all.
You can generalise the solution by using a node parent check.
Here is a sample code:
public boolean visit(StringLiteral stringLiteral) {
// Check if parent is a method inovacation.
if (stringLiteral.getParent().getNodeType() == ASTNode.METHOD_INVOCATION) {
// get the parent method inovacation.
MethodInvocation miNode = (MethodInvocation) stringLiteral.getParent();
//To do: null and empty check on argument list.
// Check if is the special method and this is the 1st argument
if (miNode.getName().toString().equals("SpecialMethod")
&& miNode.arguments().get(0).toString().equals(stringLiteral.toString())) {
System.out.println("Found it : " + stringLiteral.toString());
}
}
return true;
}

Non-static methods used in a static context error

I keep getting two errors for this bit of code about non-static methods used in a static context. This code uses an ArrayList of different objects of birds, cats, and dogs and puts them in the ArrayList called petList using an interface called Pet.
I get the same errors on the 4th and 6th line.
public static void Feed(ArrayList petList){
Scanner input = new Scanner(System.in);
String petName = input.next();
contains(petName, petList);
if(ifThere == true){
String feed = Pet.feed();
System.out.println(petName + feed);
}
else{
System.out.println("Unknown pet");
}
}
public boolean contains (String petName, ArrayList petList){
boolean ifThere = false;
int sizeList = petList.size() -1;
for(int i=0; sizeList > i; i++){
Pet booleanPet = petList.get(i);
String booleanName = booleanPet.getName();
if (booleanName.equals(petName)){
ifThere = true;
}
}
return ifThere;
}
In short : You can not call non static method from static method.
Solution : 1) Make your "contains" method as Static and it will solve the problem.
OR
2) (Assume the name of the class is Pet then create an instance of Pet class and call contains method :
your line 4 can be replaced by below code(C# style code) :
Pet somePet = new Pet ();
somePet.contains(petName, petList);
-- Extra Details:
Static method is a method which is never specific to any object. e.g. Addtion of 2 number. You
dont need to instantiate any class to call Math.Add() method because Add is static method.
You can also say that Static is a method which is not a virtual meaning you definitely know
which method is being called.

Writing methods and constructors

OK, i need someone to explain to me where to start on this project.
First I need to overload the constructor by adding a default (no-args) constructor to Person that defines an object to have the name "N/A" and an id of -1.
Then i need to add a setter method named reset that can be used to reset the two private instance variables of this class to two values passed in as parameters.
Then I need to add a getter method named getName and getId that can be used to retrieve these two private variables
Here is the code:
public class Person
{
private String name;
private int id;
private static int personCount = 0;
// constructor
public Person(String pname)
{
name = pname;
personCount++;
id = 100 + personCount;
}
public String toString()
{
return "name: " + name + " id: " + id
+ " (Person count: " + personCount + ")";
}
// static/class method
public static int getCount()
{
return personCount;
}
////////////////////////////////////////////////
public class StaticTest
{
public static void main(String args[])
{
Person tom = new Person("Tom Jones");
System.out.println("Person.getCount(): " + Person.getCount());
System.out.println(tom);
System.out.println();
Person sue = new Person("Susan Top");
System.out.println("Person.getCount(): " + Person.getCount());
System.out.println(sue);
System.out.println("sue.getCount(): " + sue.getCount());
System.out.println();
Person fred = new Person("Fred Shoe");
System.out.println("Person.getCount(): " + Person.getCount());
System.out.println(fred);
System.out.println();
System.out.println("tom.getCount(): " + tom.getCount());
System.out.println("sue.getCount(): " + sue.getCount());
System.out.println("fred.getCount(): " + fred.getCount());
}
}
I'm not exactly sure where to start and I don't want just the answer. I'm looking for someone to explain this clearly.
First I need to overload the constructor by adding a default (no-args) constructor to Person that defines an object to have the name "N/A" and an id of -1.
Read about constructors here.
The Person class already contains a ctor that takes 1 argument. What you need to do is create a "default ctor" which is typically a ctor w/out any parameters.
Example:
class x
{
// ctor w/ parameter
//
x(int a)
{
// logic here
}
// default ctor (contains no parameter)
//
x()
{
// logic here
}
}
Then i need to add a setter method named reset that can be used to reset the two private instance variables of this class to two values passed in as parameters.
Setter methods are used to "encapsulate" member variables by "setting" their value via public function. See here.
Example:
class x
{
private int _number;
// Setter, used to set the value of '_number'
//
public void setNumber(int value)
{
_number = value;
}
}
Then I need to add a getter method named getName and getId that can be used to retrieve these two private variables
Getters do the opposite. Instead of "setting" the value of a private member variable, they are used to "get" the value from the member variable.
Example:
class x
{
private int _number;
// Getter, used to return the value of _number
//
public int getNumber()
{
return _number;
}
}
Hope this helps
I highly recommend consulting the Java Tutorials, which should be very helpful here. For example, there is a section on constructors which details how they work, even giving an example of a no-argument form:
Although Bicycle only has one constructor, it could have others, including a no-argument constructor:
public Bicycle() {
gear = 1;
cadence = 10;
speed = 0;
}
Bicycle yourBike = new Bicycle(); invokes the no-argument constructor to create a new Bicycle object called yourBike.
Similarly, there are sections dedicated to defining methods and passing information to them. There's even a section on returning values from your method.
Read the above and you should be able to complete your homework :-)