package com.hubspot.jinjava.lib.filter; import java.io.Serializable; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.apache.commons.lang3.BooleanUtils; import com.google.common.collect.Lists; import com.hubspot.jinjava.doc.annotations.JinjavaDoc; import com.hubspot.jinjava.doc.annotations.JinjavaParam; import com.hubspot.jinjava.doc.annotations.JinjavaSnippet; import com.hubspot.jinjava.interpret.JinjavaInterpreter; @JinjavaDoc( value = "Sort a dict and yield (key, value) pairs.", params = { @JinjavaParam(value = "value", desc = "Dict to sort"), @JinjavaParam(value = "case_sensitive", type = "boolean", defaultValue = "False", desc = "Determines whether or not the sorting is case sensitive"), @JinjavaParam(value = "by", type = "enum key|value", defaultValue = "key", desc = "Sort by dict key or value") }, snippets = { @JinjavaSnippet( desc = "Sort the dict by value, case insensitive", code = "{% for item in contact|dictsort(false, 'value') %}\n" + " {{item}}\n" + "{% endfor %}") }) public class DictSortFilter implements Filter { @Override public String getName() { return "dictsort"; } @Override public Object filter(Object var, JinjavaInterpreter interpreter, String... args) { if (var == null || !Map.class.isAssignableFrom(var.getClass())) { return var; } boolean caseSensitive = false; if (args.length > 0) { caseSensitive = BooleanUtils.toBoolean(args[0]); } boolean sortByKey = true; if (args.length > 1) { sortByKey = "value".equalsIgnoreCase(args[1]); } @SuppressWarnings("unchecked") Map<String, Object> dict = (Map<String, Object>) var; List<Map.Entry<String, Object>> sorted = Lists.newArrayList(dict.entrySet()); Collections.sort(sorted, new MapEntryComparator(caseSensitive, sortByKey)); return sorted; } private static class MapEntryComparator implements Comparator<Map.Entry<String, Object>>, Serializable { private static final long serialVersionUID = 1L; private final boolean caseSensitive; private final boolean sortByKey; public MapEntryComparator(boolean caseSensitive, boolean sortByKey) { this.caseSensitive = caseSensitive; this.sortByKey = sortByKey; } @SuppressWarnings("unchecked") @Override public int compare(Entry<String, Object> o1, Entry<String, Object> o2) { Object sVal1 = sortByKey ? o1.getKey() : o1.getValue(); Object sVal2 = sortByKey ? o2.getKey() : o2.getValue(); int result = 0; if (!caseSensitive && sVal1 instanceof String && sVal2 instanceof String) { result = ((String) sVal1).compareToIgnoreCase((String) sVal2); } else if (Comparable.class.isAssignableFrom(sVal1.getClass()) && Comparable.class.isAssignableFrom(sVal2.getClass())) { result = ((Comparable<Object>) sVal1).compareTo(sVal2); } return result; } } }