Drools - extract value inside a map and assign - error : unable to resolve method using strict-mode - drools

Thanks to #roddy for his answer to my query here
Copy pasting from earlier to set the context :
here is my data structure :
public class Premium{
private Map<String,Map<String,String>> valuesMap = new HashMap<String,Map<String,String>>();
public Map<String, Map<String, String>> getValuesMap() {
return valuesMap;
}
}
Sample values that will be present inside this 'valuesMap' :
Map<String,String> m1= new HashMap<String,String>();
m1.put("death","100");
m1.put("income","50");
valuesMap.put("Male",m1);
valuesMap.put("Female",m2);
....
Thanks to #Roddy now I can extract the map 'm1' embedded within 'valuesMap' for "Male"
rule "rule#7 testing me 001 "
when
// below line extracts 'valuesMap' from Premium object
$pr:Premium($masterMap:valuesMap)
// now have a handle to the embedded map for 'Male'
Map( $male: this["Male"] ) from $masterMap
// defining an object in which I want to populate the value from map obtained for male
$rulesResponse:RulesResponse();
then
System.out.println("rule#7 map " + $map);
// this is where in below code it is failing
$rulesResponse.abc = $innerMap.get("income");
end
when I am trying to extract the string from map against key 'income' and assign it to the 'RulesResponse' object it fails with :
[Error: unable to resolve method using strict-mode: java.lang.Object.get(java.lang.String)]
[Near : {... nse.abc = $innerMap.get("income"); ....}]
The response object is a simple POJO with getter and setter for attribute : abc
public class RulesResponse {
private String abc = "";
public String getAbc() {
return abc;
}
public void setAbc(String abc) {
this.abc = abc;
}
If I try and assign a hard coded value - it works and also reflects after the rule is executed
// this works
$rulesResponse.abc = "hard coded value";

When you get this["Male"] out of the map, it's an Object, not anything typed. It's basically due to type erasure -- Map<String, ?>.
You can get "income" out by doing Map( $income: this["income"]) from $male. Of course, now $income will too also be an Object so you'll need to cast it again. Could be as simple as a (String)$income on the right-hand side, or a $incomeStr: String() from $income on the left.
rule "Example"
when
$pr: Premium( $masterMap: valuesMap != null )
Map( $male: this["Male"] != null ) from $masterMap
Map( $income: this["income"] != null ) from $male
$rulesResponse: RulesResponse()
then
$rulesResponse.abc = (String)$income; // cast as necessary
end
We lose the nested type identity because of type erasure -- you've got a Map<String, ?> which becomes Map<String, Object> in practice.
Strongly suggest using a properly structured POJO instead of a Map as a rule input. Even if your actual code uses these nested maps (bad practice!), you should leverage a transform before calling the rules -- not only will your rules be a lot simpler and easier to work with, but they'll also be much more performant.
Even converting that inner map into an object will make things easier:
class GenderValues {
String death;
String income;
}
class Premium {
Map<String, GenderValues> valuesByGender;
}
Best practice would be to omit the Map entirely.

Related

Using Class<T> as a Map key in Haxe

I'd like to store instances of models in a common provider using their classes or interfaces as a keys and then pop them up by class references. I have written some code:
class Provider {
public function new() { }
public function set<T:Any>(instance:T, ?type:Class<T>) {
if (type == null)
type = Type.getClass(instance);
if (type != null && instance != null)
map.set(type, instance);
}
public function get<T:Any>(type:Class<T>):Null<T> {
return cast map.get(type);
}
var map = new Map<Class<Any>, Any>();
}
...alas, it's even doesn't compile.
Probably I have to use qualified class name as a key rather than class/interface reference? But I'd like to keep neat get function design that takes type as argument and returns object just of type taken, without additional type casting.
Is it possible or should I change my approach to this problem?
The issue of using Class<T> as a Map key come up every so often, here is a related discussion. The naive approach of Map<Class<T>, T> fails to compile with something like this:
Abstract haxe.ds.Map has no #:to function that accepts haxe.IMap<Class<Main.T>, Main.T>`
There's several different approaches to this problem:
One can use Type reflection to obtain the fully qualified name of a class instance, and then use that as a key in a Map<String, T>:
var map = new Map<String, Any>();
var name = Type.getClassName(Main);
map[name] = value;
For convenience, you would probably want to have a wrapper that does this for you, such as this ClassMap implementation.
A simpler solution is to simply "trick" Haxe into compiling it by using an empty structure type ({}) as the key type. This causes ObjectMap to be chosen as the underlying map implementation.
var map = new Map<{}, Any>();
map[Main] = value;
However, that allows you to use things as keys that are not of type Class<T>, such as:
map[{foo: "bar"}] = value;
The type safety issues of the previous approach can be remedied by using this ClassKey abstract:
#:coreType abstract ClassKey from Class<Dynamic> to {} {}
This still uses ObjectMap as the underlying map implementation due to the to {} implicit cast. However, using a structure as a key now fails at compile time:
var map = new Map<ClassKey, Any>();
map[{foo: "bar"}] = value; // No #:arrayAccess function accepts arguments [...]

Unable to print the values of an instance of my model

I know this is a very fundamental question but answer to this will solve many of my doubts.
val new_parent = ParentDetails(intent.extras.getString("name"),
intent.extras.getString("email"),
intent.extras.getString("parent_relation"),
intent.extras.getString("locationdata"))
println(new_parent.tostring())
The code above doesn't print the various fields and their values present in the class.
The ParentDetails is a model I have created with some fields that are initialized. The ParentDetails model:
class ParentDetails {
var parent_id: Int = 0
var parent_name: String = ""
var parent_email: String = ""
var parent_relation: String = ""
var parent_location: String=""
constructor(parent_name: String, parent_email: String, parent_relation: String,parent_location:String) {
this.parent_name = parent_name
this.parent_email = parent_email
this.parent_relation = parent_relation
this.parent_location = parent_location
}
public fun getparentId(): Int {
return parent_id
}
fun ParentDetailsprintme() {
println(parent_name)
println(parent_email)
println(parent_relation)
println(parent_location)
}
}
In fact, it prints null and accessing individual fields, it prints empty string(the way it was initialized).
How do we explain this?
As I understand your problem is that calling println(new_parent.tostring()) does not print what you would like to print in function ParentDetailsprintme.
First of all, you have a typo, the correct call would be new_parent.toString().
Note that it could have been simplified as println(new_parent).
It does not print that you defined in the ParentDetailsprintme method, as the method is not called.
What println(new_parent.toString()) prints, is actually the hashcode of the object, as this is the default behaviour of every object.
To make it work call it like println(new_parent.ParentDetailsprintme()) or override the toString() method for example as:
override fun toString() = "$parent_name $parent_email $parent_relation $parent_location"
then the following
val new_parent = ParentDetails("myName", "myEmail", "myParent_relation", "myLocationdata")
println(new_parent)
should print
myName myEmail myParent_relation myLocationdata
Kotlin's println function simply calls System.out.println(message) under the hood which will call String.valueOf() (e.g. String.valueOf(Object object) for objects, which will call the toString() method of the passed object).
/** Prints the given message and newline to the standard output stream. */
#kotlin.internal.InlineOnly
public inline fun println(message: CharArray) {
System.out.println(message)
}
Update ("Using data class method also works"):
If you make the class to be a data class:
data class ParentDetails(
val parent_id: Int = 0,
val parent_name: String = "",
val parent_email: String = "",
val parent_relation: String = "",
val parent_location: String = ""
)
and then you execute
val new_parent = ParentDetails(0, "myName", "myEmail", "myParent_relation", "myLocationdata")
println(new_parent)
you will receive as result
ParentDetails(parent_id=0, parent_name=myName, parent_email=myEmail, parent_relation=myParent_relation, parent_location=myLocationdata)
This is because data classes override the toString() function:
The compiler automatically derives the following members from all
properties declared in the primary constructor:
equals()/hashCode() pair;
toString() of the form "User(name=John, age=42)";
Did you check that you receive valid data from your intent.extras?
Also I suggest you use data class for your models.
It will look something like this:
data class ParentDetails(
var parent_id: Int = 0,
var parent_name: String = "",
var parent_email: String = "",
var parent_relation: String = "",
var parent_location: String = ""
)
You will be able to use it like this :
val new_parent = ParentDetails(
parent_name = intent.extras.getString("name"),
parent_email = intent.extras.getString("email"),
parent_relation = intent.extras.getString("parent_relation"),
parent_location = intent.extras.getString("locationdata")
)
println(new_parent.tostring())
As already mentioned, you have a typo. toString returns the hashcode of an object unless it's overridden to return something else. Look up the original implementation for more details.
By overriding the toString method, you change what it returns, and through that, what is printed when you print(someClass). DVarga showed that in their answer.
Data classes auto-generate a toString method containing the content of the class. So creating a data class is a shortcut to getting output containing the data.
The reason the method you had didn't work is because you didn't call it. if you call it instead of toString, you would get the data printed.
Also, toString is explicitly called when you print a class. You don't need to call print(someInstance.toString()), print(someInstance) is enough.
And while I'm writing an answer, you don't need to use secondary constructors in Kotlin. Primary constructors would shorten your code significantly, whether it's a data class or not. Also, you should look into naming conventions.

NTriplesParser extract textual value from string

I am using dotnetrdf and trying to parse some triples with NTriplesParser. I have my own handler RobHandler in which I process each triple in turn.
public class RobHandler : BaseRdfHandler
{
protected override bool HandleTripleInternal(Triple t)
{
string predicateUrl = ((BaseUriNode)(t.Predicate)).Uri.AbsoluteUri;
string value = t.Object.ToString();
}
}
This works fine but I want to get the object minus the language. My objects look like "Lincoln"#en. I could obviously write some code to remove the #en bit, but I'd rather use some library code rather than my own that hard-coded strings like #en. To do this I think I need to create a LiteralNode but there doesn't seem to be a way to get from a string which is what I have (my variable value) to a LiteralNode.
How can I extract just the textual value from an object string?
Actually I think I have the answer myself:
if (t.Object.NodeType == NodeType.Literal)
{
var node = (ILiteralNode)t.Object;
}

method based on variable type

I just have the following scenario
i want to return string from method but the method should be based on variable type which is (Type CType)
i need to make the render class like this
public string render(TextBox ctype){
return "its text box";
}
public string render(DropDown ctype){
return "its drop down";
}
you know TextBox is a Type thats why i can declare the Type variable like this
var CType = typeof(TextBox)
and i need to call the render method like this
render(Ctype);
so if the Ctype is type of TextBox it should call the render(TextBox ctype)
and so on
How can i make it ?
you should use a template function
public customRender<T>(T ctype)
{
if(ctype is TextBox){
//render textbox
}
else if(ctype is DropDown){
//render dropdown
}
}
hope it will help
First of all, even if you don't see an if or a switch, there will still be one somewhere hidden inside some functions. Distinguishing types at runtime that are not known at compile-time simply will not be possible without any such kind of branching of the control flow.
You can use one of the collection classes to build a map at runtime that maps Type instances to Func<T, TResult> methods. For example, you can use the Dictionary type to create such a map:
var rendererFuncs = new Dictionary<Type, Func<object, string>>();
You could then add some entries to that dictionary like this:
rendererFuncs[typeof(TextBox)] = ctype => "its text box";
rendererFuncs[typeof(DropDown)] = ctype => "its drop down";
Later on, you can call the appropriate function like this:
string renderedValue = rendererFuncs[Ctype.GetType()](Ctype);
Or, if you want to be on the safe side (in case there are Ctype values that have no appropriate renderer):
string renderedValue;
Func<object, string> renderer;
if (rendererFuncs.TryGetValue(Ctype.GetType(), out renderer)) {
renderedValue = renderer(Ctype);
} else {
renderedValue = "(no renderer found)";
}
Note that this will only work for as long as Ctype is of the exact type used as a key in the dictionary; if you want any subtypes to be correctly recognized as well, drop the dictionary and build your own map that traverses the inheritance hierarchy of the type being searched (by using the Type.BaseType property).

Can't use string.Format() in Anonymous Type

I'm hoping to achieve something as follows:
var comboBoxItems = from state in states
select new
{
Key = state.Code,
Value = string.Format("{0} ({1})", state.Name, state.Code)
};
this.stateComboBox.DisplayMember = "Value";
this.stateComboBox.ValueMember = "Key";
this.stateComboBox.DataSource = new BindingSource(comboBoxItems, null);
However, it gives me the following error when it attempts to bind to the DataSource:
"LINQ to Entities does not recognize the method 'System.String
Format(System.String, System.Object, System.Object)' method, and this
method cannot be translated into a store expression."
Is there any way to include a method like string.Format() in the Anonymous Type?
var comboBoxItems = from state in states.ToList()
select new
{
Key = state.Code,
Value = string.Format("{0} ({1})", state.Name, state.Code)
};
You cannot use Format in LINQ 2 Entities as it cannot be translated to SQL. A call to ToList will cause the items to be loaded from DB and your format will now execute properly.