Skip to content

Commit

Permalink
Add new MapELResolver with type coercion to support accessing enum ke…
Browse files Browse the repository at this point in the history
…ys (#688)

* enum dict test case

* Add EnumMapElResolver to handle accessing enum variables by name

* Use CollectionMembershipOperator

* Add logic to handle custom enum toString

Co-authored-by: Matt Coley <[email protected]>
  • Loading branch information
liamrharwood and Matt Coley authored Jun 17, 2021
1 parent 2e8821a commit bbf37df
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@
import javax.el.CompositeELResolver;
import javax.el.ELContext;
import javax.el.ELResolver;
import javax.el.MapELResolver;
import javax.el.PropertyNotFoundException;
import javax.el.ResourceBundleELResolver;
import org.apache.commons.lang3.LocaleUtils;
Expand All @@ -57,7 +56,7 @@ public class JinjavaInterpreterResolver extends SimpleResolver {
{
add(new ArrayELResolver(true));
add(new JinjavaListELResolver(true));
add(new MapELResolver(true));
add(new TypeConvertingMapELResolver(true));
add(new ResourceBundleELResolver());
add(new JinjavaBeanELResolver(true));
}
Expand All @@ -68,7 +67,7 @@ public class JinjavaInterpreterResolver extends SimpleResolver {
{
add(new ArrayELResolver(false));
add(new JinjavaListELResolver(false));
add(new MapELResolver(false));
add(new TypeConvertingMapELResolver(false));
add(new ResourceBundleELResolver());
add(new JinjavaBeanELResolver(false));
}
Expand Down
16 changes: 15 additions & 1 deletion src/main/java/com/hubspot/jinjava/el/TruthyTypeConverter.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.EnumSet;
import javax.el.ELException;

public class TruthyTypeConverter extends TypeConverterImpl {
private static final long serialVersionUID = 1L;
Expand Down Expand Up @@ -103,7 +104,20 @@ protected <T extends Enum<T>> T coerceToEnum(Object value, Class<T> type) {
return enumSet.iterator().next();
}
}
return super.coerceToEnum(value, type);

try {
return super.coerceToEnum(value, type);
} catch (ELException e) {
if (value instanceof String) {
for (T enumVal : type.getEnumConstants()) {
String enumStr = enumVal.toString();
if (enumStr != null && enumStr.equalsIgnoreCase((String) value)) {
return enumVal;
}
}
}
throw e;
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.hubspot.jinjava.el;

import java.util.Map;
import javax.el.ELContext;
import javax.el.MapELResolver;

public class TypeConvertingMapELResolver extends MapELResolver {
private static final TruthyTypeConverter TYPE_CONVERTER = new TruthyTypeConverter();

public TypeConvertingMapELResolver(boolean readOnly) {
super(readOnly);
}

@Override
public Object getValue(ELContext context, Object base, Object property) {
Object value = super.getValue(context, base, property);

if (value != null) {
return value;
}

if (base instanceof Map && !((Map) base).isEmpty()) {
Class<?> keyClass = ((Map) base).keySet().iterator().next().getClass();
value = ((Map) base).get(TYPE_CONVERTER.convert(property, keyClass));
if (value != null) {
context.setPropertyResolved(true);
}
}

return value;
}
}
82 changes: 82 additions & 0 deletions src/test/java/com/hubspot/jinjava/el/ext/AstDictTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package com.hubspot.jinjava.el.ext;

import static org.assertj.core.api.Assertions.assertThat;

import com.google.common.collect.ImmutableMap;
import com.hubspot.jinjava.Jinjava;
import com.hubspot.jinjava.interpret.JinjavaInterpreter;
import com.hubspot.jinjava.interpret.TemplateError.ErrorType;
import java.util.Map;
import org.junit.Before;
import org.junit.Test;

public class AstDictTest {
private JinjavaInterpreter interpreter;

@Before
public void setup() {
interpreter = new Jinjava().newInterpreter();
}

@Test
public void itGetsDictValues() {
interpreter.getContext().put("foo", ImmutableMap.of("bar", "test"));
assertThat(interpreter.resolveELExpression("foo.bar", -1)).isEqualTo("test");
}

@Test
public void itGetsDictValuesWithEnumKeys() {
interpreter.getContext().put("foo", ImmutableMap.of(ErrorType.FATAL, "test"));
assertThat(interpreter.resolveELExpression("foo.FATAL", -1)).isEqualTo("test");
}

@Test
public void itGetsDictValuesWithEnumKeysUsingToString() {
interpreter.getContext().put("foo", ImmutableMap.of(TestEnum.BAR, "test"));
assertThat(interpreter.resolveELExpression("foo.barName", -1)).isEqualTo("test");
}

@Test
public void itHandlesEmptyMaps() {
interpreter.getContext().put("foo", ImmutableMap.of());
assertThat(interpreter.resolveELExpression("foo.FATAL", -1)).isNull();
assertThat(interpreter.getErrors()).isEmpty();
}

@Test
public void itGetsDictValuesWithEnumKeysInObjects() {
interpreter
.getContext()
.put("test", new TestClass(ImmutableMap.of(ErrorType.FATAL, "test")));
assertThat(interpreter.resolveELExpression("test.my_map.FATAL", -1))
.isEqualTo("test");
}

public class TestClass {
private Map<ErrorType, String> myMap;

public TestClass(Map<ErrorType, String> myMap) {
this.myMap = myMap;
}

public Map<ErrorType, String> getMyMap() {
return myMap;
}
}

public enum TestEnum {
FOO("fooName"),
BAR("barName");

private String name;

TestEnum(String name) {
this.name = name;
}

@Override
public String toString() {
return name;
}
}
}

0 comments on commit bbf37df

Please sign in to comment.