Implementation of minimalist template language

Keywords: Java

In the project, the user needs to input a string template with parameters, and then the server will parse it according to a context. I checked some data and found out StrSubstitutor This little tool. Its own examples are as follows:

 Map valuesMap = HashMap();
 valuesMap.put("animal", "quick brown fox");
 valuesMap.put("target", "lazy dog");
 String templateString = "The ${animal} jumped over the ${target}.";
 StrSubstitutor sub = new StrSubstitutor(valuesMap);
 String resolvedString = sub.replace(templateString);

Now I want to run such a template from a row in a two-dimensional table data structure (list < map < string, Object > >), so that the above example is unnecessary. I even want to extend the results, such as formatting. Related by query data , found the class strookup. Combined with regular expressions, we have the following implementation

public class ExtraFormatLookup extends StrLookup {

    private static final String FMT = "fmt";
    private final List<Map<String, Object>> result;
    private final Map<String, MetricDO> metrics;

    ExtraFormatLookup(List<Map<String, Object>> result, Map<String, MetricDO> metrics) {
        this.result = result;
        this.metrics = metrics;
    }

    @Override
    public String lookup(String s) {
       s = s.toLowerCase();
        String regex = "([^\\[.]+)(?:\\[(-?\\d+)])?(?:\\.([a-zA-z]+)(?::-(.*))?)?";
        Matcher matcher = Pattern.compile(regex).matcher(s);
        if (matcher.find()) {
            String key = matcher.group(1);
            String indexStr = matcher.group(2);
            String suffix = matcher.group(3);
            String defaultValue = matcher.group(4) == null ? "" : matcher.group(4);
            if (result == null || result.isEmpty()) {
                return defaultValue;
            }
            int index = 0;
            if (!Strings.isNullOrEmpty(indexStr)) {
                index = Integer.parseInt(indexStr);
                if (index < 0 - result.size() || index >= result.size()) {
                    return defaultValue;
                }
                if (index < 0) {
                    index = result.size() + index;
                }
            }
            String r = String.valueOf(result.get(index).getOrDefault(key, defaultValue));

            if (metrics.containsKey(key) && FMT.equalsIgnoreCase(suffix)) {
                r = CommonUtil.getValueFormatStr(metrics.get(key), r);
            }
            return r;
        } else {
            return "";
        }
    }
}

The test code is as follows:

 @Test
    public void lookup1() {
       List<Map<String, Object>> result = Lists.newArrayList(
            ImmutableMap.of("abc", "1111111"),
            ImmutableMap.of("abc", "222222"),
            ImmutableMap.of("abc", "33333", "def", "4444")
        );

        Map<String, MetricDO> metrics = Maps.newHashMap();
        metrics.put("abc", new MetricDO().setCode("abc").setType("abs").setPattern(",###,###"));

        StrSubstitutor sub = new StrSubstitutor(new ExtraFormatLookup(result, metrics));

        assertThat(sub.replace("test ${abc}"), Matchers.equalTo("test 1111111"));
        assertThat(sub.replace("test ${abc.fmt}"), Matchers.equalTo("test 1,111,111"));
        assertThat(sub.replace("test ${abc[0]}"), Matchers.equalTo("test 1111111"));
        assertThat(sub.replace("test ${abc[1]}"), Matchers.equalTo("test 222222"));
        assertThat(sub.replace("test ${abc[2]}"), Matchers.equalTo("test 33333"));
        assertThat(sub.replace("test ${abc[1].fmt}"), Matchers.equalTo("test 222,222"));
        assertThat(sub.replace("test ${abc[2].fmt}"), Matchers.equalTo("test 33,333"));
        assertThat(sub.replace("test ${def[2].fmt}"), Matchers.equalTo("test 4444"));
        assertThat(sub.replace("test ${def.fmt}"), Matchers.equalTo("test "));
        assertThat(sub.replace("test ${abc[-1].fmt}"), Matchers.equalTo("test 33,333"));
        assertThat(sub.replace("test ${abc[-2].fmt}"), Matchers.equalTo("test 222,222"));
        assertThat(sub.replace("test ${abc[-5].fmt}"), Matchers.equalTo("test "));
        assertThat(sub.replace("test ${abcd[-5].fmt:-efg}"), Matchers.equalTo("test efg"));
        assertThat(sub.replace("test ${abc[-789].fmt:-efg}"), Matchers.equalTo("test efg"));
        assertThat(sub.replace("test ${abcd[1111].fmt:-efg}"), Matchers.equalTo("test efg"));
    }

In fact, the logic is very simple, that is, get the key to be accessed, row index, extension method field and default value through regular expression. It's also relatively simple to expand later. It's better to implement the method in advance and then add the supported extension fields.

In addition, Amway is a website to verify Java regular expressions https://www.freeformatter.com/java-regex-tester.html#ad-output

Posted by johnnyv on Wed, 04 Dec 2019 08:37:18 -0800