Access Map<Enum, Object> in JSTL - java

I have:
public enum MyEnum{
One, Two, Three
}
From controller, I put in the model:
HashMap<MyEnum, Long> map = new HashMap<MyEnum, Long>();
map.put(MyEnum.One, 1L);
mav.addObject( "map", map);
How do I in my JSTL access the object in the map for key enum MyEnum.One, in a neat way?
${map['One']} //does not seem to work...
nor does
${map[MyEnum.One]}

It's not exactly true that you can't do it, but the solution isn't completely straight forward. The issue is EL is not converting the string you pass in as the map key to the corresponding enum for you, so putting ${map['One']} does not use the enum constant MyEnum.One in the map lookup.
I ran into the same issue and didn't want to revert to going with a String keyed map, so the challenge then was in JSTL how to get the actual enum reference to use in the map lookup.
What is required is to get the Enum constants into the scope of the JSP so that you can then use the actual Enum itself as the key. To do this, in the controller you do something like this:
for (MyEnum e : MyEnum.values()) {
request.putAttribute(e.toString(), e);
}
What you've done here is added variables into the scope named as the string representation of the enum. (you could of course avoid naming issues by prepending the e.toSring() with some value)
Now, when you do the following
${map[ONE]}
You will be using the actual enum constant as the key and will therefore get back the proper corresponding value from the map. (notice there are no quotes around ONE, that is because you are referencing the request attribute ONE in this case, that was added above)

You can't. Your best bet is to change your map to use enum.name() as key:
HashMap<String, Long> map = new HashMap<String, Long>();
map.put(MyEnum.One.name, 1L);
map.addObject( "map", map);
Your first approach would work then:
${map['One']} // works now
Or you can write a custom EL function to do the above for you if you can't / don't want to change the map.

I usually use this solution:
<%#page import="my.package.MyEnum"%>
<c:set var="One" value="<%=MyEnum.One %>" />
<c:set var="MyEnum_values" value="${map[One]}" />
First, I import the enum. Then, I save the enum value I want into JSTL variable. Then I can access the map with this variable as the key.

${map[MyEnum.One]}
It works for me. But you have to write the complete name of your class: my.package.MyEnum or to import MyEnum class:
<%#page import="my.package.MyEnum"%>

Related

Creating a map in config files

We have one application where we use configuration files and they have fields as arrays and normal variables:
metadata {
array=["val1", "val2"]
singleValue=2.0
}
Now, I know how to extract these above values like
config.getStringList("metadata.array").asScala.toArray
and config.getString("metadata.singleValue)
But, is there any way I can define maps here so that I can find value wrt desired key from that map.
This config is an object of
public interface Config extends com.typesafe.config.ConfigMergeable
You can use config.getConfig("metadata") to obtain a (sub)config object.
Converting the (sub)config to a map is something you'll have to do yourself. I would use config.entrySet() to obtain the entries as key-values, and load it into a map that way.
I haven't tried compiling/testing this code, but something like this should work:
Map<String,Object> metadata = new HashMap<>();
for (Map.Entry<String,ConfigValue> entry : config.entrySet()) {
metadata.put(entry.getKey(), entry.getValue().unwrapped());
}

Assert that map contains not null vales by given keys

I have a code that consumes map of properties with string keys which represents some kind of context. And I want this code to fail if the map does not contain some of the required properties.
The corresponding code might look like this:
SomeResultType businessMethod(Map<String, String> context) {
Assert.isTrue(context.containsKey("A"), "A property must not be empty");
Assert.isTrue(context.containsKey("B"), "B property must not be empty");
Assert.isTrue(context.containsKey("C"), "C property must not be empty");
// ...
}
I wrote a simple replacement by myself with signature like this
public static <K, V> void mapContainsKeys(Map<K, V> map, K... keys)
But I think that something like this must be already implemented somewhere. So I'm searching for library replacement of this code.
It would be great if Spring guys implemented something like this in org.springframework.util.Assert.
if you want to just check that a map contains a list of keys, use this:
map.keySet().containsAll(keys)
if you want more details, to know which ones were missing:
Set<String> missingKeys = new HashSet<>(keys);
missingKeys.removeAll(map.keySet());
and then:
if (!missingKeys.isEmpty()) {
throw new IllegalArgumentException(missingKeys + " keys missing");
}
The fact that you had to write a helper method means that you are probably manipulating maps all over your code.
Looks like a code smell to me, you should probably map your properties to an object that you can validate once and pass everywhere.
Since you are using Spring, and if you are using Spring Boot (most people do nowadays), you can use #ConfigurationProperties to map your configuration properties to an object.
This way you can also add validation like #javax.validation.constraints.NotNull and make sure your properties are valid.

Problems calling Java methods in velocity template with Jira

I´m developing a plugin for jira which contains a custom field type.
The field is a select and has to be filled with options. I supply these options via the Jira method getVelocityParameters().
#Override
public Map<String, Object> getVelocityParameters(Issue issue,
CustomField field, FieldLayoutItem fieldLayoutItem) {
Map<String, Object> map = super.getVelocityParameters(issue, field, fieldLayoutItem);
map.put("customOptions", getCustomOptions());
return map;
}
getCustomOptions() returns a Hashtable with the Options i need.
To access and display these options i used a #foreach loop in the template:
#foreach($customOption in $customOptions)
<option id="$customOption.Id" value="$customOption.Value">
$customOption.Label
</option>
#end
Instead of showing the returned Objects it simply just display the text itself, only the "$customOption.Id" is displayed correctly.
And writing only "$customOption" shows the whole reference to the object.
So i CAN access the object and its id but not the other properties.
Id is an int, while label and value are Strings.
i searched for solutions and tried different things to solve this problem, e.g.:
$!customOption.Label, ${!customOption.Label}, ${customOption.Label}, $customOption.getLabel()
I can´t find the problem here, because the id is working properly.
Sry for the broken english.
Because you use Map. So try the following:
#foreach($customOption in $customOptions)
#if ($customOption)
#foreach ($co in $customOption.keySet())
$customOption[$co]
#end
#end
#end
Velocity will display the source if there is no value for your issue.
E.g. if you want to obtain a customfield value, you should check the value and if its not set you can either load the default value or just ignore it.
I think you should look into the fact that Velocity will only display a field value if its class has a public get method for that field.
Say customOption is an object of class X, then class X must have a public get() method that returns the label.
It does not matter if the label field is a public field of class X, public get() method is necessary.
You can have a look at this for reference:

How to retrieve hashmap values in velocity template

How to retrieve values from the following hashmap in velocity template? Please help..
LinkedHashMap<String, LinkedHashMap<Integer, Object>> hashmap = new LinkedHashMap<String, LinkedHashMap<Integer,Object>>();
First, add the hashmap to your backing Java class (reference here).
context.put("myhashmap", hashmap);
Then you can reference anywhere in your Velocity template, e.g:
<span>$myhashmap.get("foo").get(1).toString()</span>
This worked for me:
$!myhashmap.get($!foo).toString();
Note: foo is a variable in my case.

Spring bean initialize maps using external property file

I have a class which has a Map as its member variable. Something like this -
public Clas Engine{
private Map<String,List<String>> filesByKey;
public void setFilesByKey(Map<String,List<String>> map) {
this.filesByKey = map;
}
public Map<String,List<String>> getFilesByKey() {
return filesByKey;
}
}
User can specify any number of keys in the map and its not predefined concept. They can basically group any number of files into one key and provider the map Value.
I was using PropertyOverrideConfigurer and in the properties file, I was trying to do something like this -
engine.filesByKey[key1][0]=file1
engine.filesByKey[key1][1]=file2
engine.filesByKey[key2][0]=anotherfile1
engine.filesByKey[key2][1]=anotherfile2
Now this is not working because the the List value corresponding to key1 or key2 is null to being with. So Spring Bean creation fails with the message that it can not set value to a property which is NULL.
What is the best way to handle this situation?
You should be able to use the LazyMap & LazyList from commons collections to achieve this.
Try to initialize your filesByKey variable with DefaultedMap from commons collections. It can return default value instead of null if map doesn't contain required key.

Categories