Accessing Map elements via Enum key in Freemarker - java

I'm trying to access elements in a HashMap.
The keys of this HashMap are defined by an Enum.
After going through the documentation, I figured that in order to be able to access the Enum, I have to send it to Freemarker like so:
BeansWrapper wrapper = BeansWrapper.getDefaultInstance();
TemplateHashModel enumModels = wrapper.getEnumModels();
TemplateHashModel fieldTypeModel = (TemplateHashModel)enumModels.get("com.example.MinisiteFieldType");
root.put("fieldtypes", fieldTypeModel);
In my .ftl I tried the following:
${myelement.mymap[fieldtypes.SEOTEXT]}
However I get:
Expression myelement.mymap[fieldtypes.SEOTEXT] is undefined on line X...
I wanted to make sure I mapped the enum correctly, so I tried:
${fieldtypes.SEOTEXT}
This didn't print anything, leaving me wondering wether it got through, or simply couldn't be printed.
In Java debug, just before inserting the TemplateHashModel in my root Map, 'fieldTypeModel' is filled with the correct data...
Any help would be appreciated!
Bart

I suspect your problem is that by using the [] syntax to access your map, you're implicitly telling Freemarker to treat it as a hash. That may not work as you'd expect -- the hash will probably represent the Map object, mapping 'size' and 'containsKey' and so forth to Java methods. Instead, try:
${myelement.mymap.get(fieldtypes.SEOTEXT)}
As a side note, I've never tried accessing enums by the method you describe, but there's another way to access Java constants from a template that doesn't require Java code, so the following should also work:
${myelement.mymap.get(stack.findValue("#com.example.MinisiteFieldType#SEOTEXT"))}

Related

Can a YAML value string be evaluated in Java?

Is it possible to pass Java code as a value in a YAML file. For example, something like this:
---
dueDate: "DueDateCalc()"
DueDateCalc() might be a method defined in the Java code that is parsing the YAML. It would then set the Java dueDate property to the return of the predefined DueDateCalc() method.
This is possible within the constraints of Java runtime reflection, however you need to implement it yourself.
For example, your YAML could look like this:
---
dueDate: !call DueDateCalc
!call is a local tag for telling the loading code that the scalar value DueDateCalc should be interpreted as method to be called (this is chosen by you, not something predefined). You can implement this with a custom constructor for the !calc tag that searches for a method with the given name within some given class, and then calls it on some given object.
What about parameters? Well, still possible, but will get ugly fast. First problem is how you define the paramaters:
with nested YAML sequences: !call [MyMethod, [1, 2, 3]]
with a scalar that needs to be parsed: !call MyMethod(1, 2, 3)
The former option lets YAML parse the parameters and you'll get a list; the latter option requires you to parse the method call yourself from the string you get from YAML.
The second problem is to load the values into Java variables so that you can give them as argument list. Java reflection lets you get the method's parameter types and you can use those to load the parameter values. For example, if the first parameter's type is a String, you would parse 1 as a "1", while if it's an int, you can parse 1 as int. This is possible with SnakeYAML's builtin facilities if you're using nested YAML sequences for method call encoding.
This would even work if parameters are class objects with complex structure, you'd just use normal YAML syntax and the objects will be loaded properly. Referring to variables in your code is not directly possible, but you could define another tag !lookup which retrieves values from a given Map structure.
While reflection lets you make method calls, you can not directly evaluate an expression like 6*9. So before you try and implement anything, evaluate which functionality you need and check whether it's doable via reflection.

Setting properties using strings

Is it possible to set a property on an object using a string as the property name? I am quite new to Java but in JavaScript I would just do something like this:
Object[propertyNameAsString] = value
After searching around I found how to get a property using a string, but not how to set one. I also came across something called a Properties object, is that what I need to be using?
Basically I just want to loop through an array filled with image names and then set their respective properties onto an object which I will use to store the actual images. Is this possible in Java or should I do it another way?
Edit:
This is what my array looks like:
static String pngs[] = {"staminaBox", "staminaBoxR", "healthBox", "healthBoxR", "moveBox", "goButton"};
Ideally I want to loop through them and add properties to an assets class like so:
for (int i = 0; i < pngs.length; i++) {
Assets.pngs[i] = createImageFromUrl(pngs[i] + ".png");
}
But now I'm realizing this wouldn't work because in my assets class I have to previously define the properties like:
public static Image staminaBox;
And the whole point of me wanting to do this in the first place was so that I didn't have to write out declarations for every single image I wanted to add. I'm used to the loose nature of JavaScript, but does this mean there is no way of getting around these explicit declarations of properties? Will a HashMap let me accomplish this?
You might be talking about a couple different things. Are you talking about setting the value of variables defined inside a class? If so, you might be able to hack something together using reflection, but it's not a "gimme" in Java like it is in javascript.
You might also look into using a HashMap of properties to their values, or like you said, the Properties object (which is pretty much just a Map itself).
You can use the Reflection API. Keep in mind that it will get messy very quickly.
Because Java uses the Getter and Setter pattern, you likely won't be assigning a value to a member variable but calling a method which itself assigns the value.
How do I invoke a Java method when given the method name as a string?

Spring spel - how to parse values into a hashmap

I'm writing an expression to get a values out of a GenericRecord to one object.
Tried to work with hashmap (the number of values is dynamic).
example:
expression.parse("new HashSet<String>(Arrays.asList(get('accountId'), get('anotherField'))");
Map result = expression.applyOn(someGenericRecord);
but it doesn't work.
I know all classes in expressions should be fully qualified (so that might be the problem) but I think there should be a cleaner way of extracting the values for using them later.
thanks

Flexjson unable to deserialize LinkedHashMap

I am deserialising a json object as below
{
"b":"value1",
"a":"value2",
"d":"value3",
"c":"value4",
"f":"value5",
"e":"value6"
}
But i am getting ClassCastException as below
java.lang.ClassCastException: java.util.HashMap cannot be cast to java.util.LinkedHashMap
My deserialisation code is
LinkedHashMap<String, String> map = new JSONDeserializer<LinkedHashMap<String, String>>().deserialize(JSONstring);
But when i use HashMap instead of LinkedHashMap it works but output gets sorted as below (Its not original order).
{
a=value2,
b=value1,
c=value4,
d=value3,
e=value6,
f=value5
}
I want to get the output in original order.
I found this related link
Flex JSON unable to properly serialize/deserialize LinkedHashMap
but didn't get how to use ObjectFactory.
I would appreciate any help!
Thanks in advance!
(Disclaimer: I just downloaded flexjson and debugged through its source code, so my answer might be slightly incomplete.)
In short:
You generally cannot get the entries in original order using flexjson.
Even writing a custom ObjectFactory will not work.
This is most likely intended and correct.
In detail:
First, the use of generics in new JSONDeserializer<LinkedHashMap<String,String>>() only affects the compilable code, flexjson cannot use this information to actually return a LinkedHashMap<String,String> (this is because the compiler removes the generic and the implementing class has no information of this generic type at runtime).
So, looking deeper into what happens during deserialization, it seems that during parsing the input string, the data is automatically converted to the correct type (string, date, number, list, etc.). This is done using some kind of autodetection of the required data type, because JSON does not provide type information in its data, so flexjson has a build-in list to support data types. It can also use custom mappings to assign values to object properties when proper class information is given (on serialization, flexjson adds a field class to the data to store this type information; or you can manually set this, see documentation).
But the main point is that - according to http://json.org -
An object is an unordered set of name/value pairs.
flexjson internally uses an (unordered) map to store the temporary object keys and values. Even if you tell flexjson to return the data as a LinkedHashMap the data is yet put into a HashMap before it will be converted to a LinkedHashMap, so the original order is not available at that point. (This might be fixed by replacing the map creation in flexjson.JSONTokener, line 442 with a LinkedHashMap but I didn't try that myself.)
Conclusion:
It looks like this behaviour even cannot be changed by providing a custom ObjectFactory, so as far as I understand the code, you cannot retain the original field order. (May I ask why this is important for your project?)
So, if anybody finds a solution anyway, don't hesitate to correct me.

Session atrribute is missing a List

I have a very strange situation. I´m working on a pretty big Java application with many bugs, and I found this one today.
I´ll try to explain the situation without posting code because the methods are way too long and I have identified and isolated the specific problem. Here it is:
I have a session attribute set on a Controller class. The attribute has several fields, a couple of Strings, a couple of int and an ArrayList of a certain object type. It is set like this:
request.getSession().setAttribute(Constants.SESSION_LIST_SEARCH, beanList);
Then there is another Controller class where I need to read this attribute, it goes like this:
request.getSession().getAttribute(Constants.SESSION_LIST_SEARCH);
When the controller gets the attribute (with the proper cast) the Strings and the int fields are there, but the ArrayList it´s empty.
I couldn't find an answer so in an act of desperation I tried to "clone" the list to see what happened, so it goes like this:
request.getSession().setAttribute(Constants.SESSION_LIST_SEARCH, beanList);
/* Desperate developer */
ArrayList<ActivityBean> duplicatedList = new ArrayList<ActivityBean>();
for(ActivityBean foo:beanList.getActivityBean()){
duplicatedList.add(foo);
}
request.getSession().setAttribute("duplicatedList",duplicatedList);
This workaround does the trick (now I can read the bean "duplicatedList" from the session correctly) but it just doesn't seem right that the original bean loses the ArrayList on some point and still maintain the other fields.
Thanks in advance
It seems that somewhere else in your application, some code is modifying the List (since you said that it's not null but empty - if it was null I'd expect that it was removed from context at all by some other part of code). Maybe after putting List to the context some code still maintains reference and operates on it?
You can try to do the following:
request.getSession().setAttribute(Constants.SESSION_LIST_SEARCH, Collections.unmodifiableList(beanList));

Categories