jknack / handlebars.java

Logic-less and semantic Mustache templates with Java
http://jknack.github.io/handlebars.java
Other
1.48k stars 383 forks source link

Broken feature: `EnumMap` lookup #1083

Open TWiStErRob opened 1 year ago

TWiStErRob commented 1 year ago

I have a model property Map<Line, StatusChange> statuses = new EnumMap(Line.class); where these are the enum definitions:

public enum Line {
    unknown /*Line$1*/, Bakerloo /*Line$2*/, ...
}
public enum StatusChange {
    Better("status-better"), ...
    private final String cssClass;
    StatusChange(String cssClass) {
        this.cssClass = cssClass;
    }
    public String getCssClass() { return cssClass; }
}

and this template: {{statuses.[lineStatus.line].cssClass}}, which now I understand is wrong, I think I need lookup.

However I did not get that understanding from the error message, since it was:

Caused by: com.github.jknack.handlebars.HandlebarsException: /views/LineStatus.hbs:99:24:
java.lang.IllegalArgumentException: net.twisterrob.blt.model.Line$2 is not an enum class
    at java.base/java.lang.Class.enumConstantDirectory(Class.java:3862)
    at java.base/java.lang.Enum.valueOf(Enum.java:267)
    at com.github.jknack.handlebars.context.MapValueResolver.resolve(MapValueResolver.java:52)

I had a look at the sources:

https://github.com/jknack/handlebars.java/blob/683c5e885d5dcdf3d17b33e9667f3fb153952016/handlebars/src/main/java/com/github/jknack/handlebars/context/MapValueResolver.java#L52

and debugged, where I confirmed that the code is passing in the wrong class:

-Enum.valueOf(first.getClass(), name)
+Enum.valueOf(first.getDeclaringClass(), name)

then the error becomes a more meaningful:

java.lang.IllegalArgumentException: No enum constant net.twisterrob.blt.model.Line.lineStatus.line

Sadly this not only affects the error cases, the feature to be able to index into an EnumMap via a String in [] is broken too, because it won't find the valueOf given the wrong class. I tried {{statuses.[Bakerloo].cssClass}} and got the same is not an enum class exception, even though according to the MapValueResolver it should've "just worked".

TWiStErRob commented 1 year ago

Caused by original implementation: https://github.com/jknack/handlebars.java/issues/237

Also affects {{lookup statuses lineStatus.line}} syntax.

Workaround:

handlebars.registerHelpers(new HelperSource());
public static class HelperSource {
    /**
     * Workaround for `myEnumMap.[Foo]` and `lookup myEnumMap Foo` not working.
     * Usage: Replace {@code (lookup someEnumMap someEnumKey)} with {@code (lookupEnumMap someEnumMap someEnumKey)}.
     * @see https://github.com/jknack/handlebars.java/issues/1083
     */
    public static <E extends Enum<E>> Object lookupEnumMap(EnumMap<E, ?> map, E key) {
        return map.get(key);
    }
}