Created
May 5, 2022 15:24
-
-
Save ammmze/c1bf44265a4d4eb42c3985934e1f5085 to your computer and use it in GitHub Desktop.
Java Handlebars helpers matching mandrill
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.github.gist.ammmze.handlebars.helpers; | |
import com.github.jknack.handlebars.Options; | |
import com.github.jknack.handlebars.helper.IfHelper; | |
import java.io.IOException; | |
import java.text.NumberFormat; | |
import java.text.ParseException; | |
import java.util.ArrayList; | |
import java.util.List; | |
import java.util.regex.Matcher; | |
import java.util.regex.Pattern; | |
import org.apache.commons.lang3.StringUtils; | |
import org.springframework.expression.ExpressionParser; | |
import org.springframework.expression.spel.standard.SpelExpressionParser; | |
public class ConditionalExpressionHelper extends IfHelper { | |
private enum ExpressionComparator { | |
EQUALS("=="), | |
NOT_EQUALS("!="), | |
LESS_THAN_OR_EQUALS("<="), | |
GREATER_THAN_OR_EQUALS(">="), | |
LESS_THAN("<"), | |
GREATER_THAN(">"), | |
; | |
private String comparator; | |
ExpressionComparator(String comparator) { | |
this.comparator = comparator; | |
} | |
public String getComparator() { | |
return comparator; | |
} | |
public static ExpressionComparator fromComparator(String comparator) { | |
for (ExpressionComparator exp : values()) { | |
if (exp.getComparator().equals(comparator)) { | |
return exp; | |
} | |
} | |
return null; | |
} | |
public static String[] comparators() { | |
List<String> comparators = new ArrayList<>(); | |
for (ExpressionComparator exp : values()) { | |
comparators.add(exp.getComparator()); | |
} | |
return comparators.toArray(new String[values().length]); | |
} | |
} | |
private final Pattern expressionPattern = Pattern.compile("(.*)(" + StringUtils.join(ExpressionComparator.comparators(), "|") + ")(.*)"); | |
private Boolean comparison; | |
public ConditionalExpressionHelper() { | |
comparison = Boolean.TRUE; | |
} | |
public ConditionalExpressionHelper(Boolean comparison) { | |
this.comparison = comparison; | |
} | |
@Override | |
public Object apply(Object context, Options options) throws IOException { | |
Expression expression = getExpression(context, options); | |
if (expression == null){ | |
return super.apply(context, options); | |
} | |
return super.apply(comparison.equals(expression.evaluate()), options); | |
} | |
private String unquote(String str) { | |
return str.replaceAll("^'(.*)'$", "$1") | |
.replaceAll("^\"(.*)\"$", "$1"); | |
} | |
private Expression getExpression(Object context, Options options) { | |
if (!(context instanceof String)) { | |
return null; | |
} | |
Matcher matcher = expressionPattern.matcher((String) context); | |
if (!matcher.matches()) { | |
return null; | |
} | |
return new Expression(parseParameter(matcher.group(1).trim(), options), | |
ExpressionComparator.fromComparator(matcher.group(2).trim()), | |
parseParameter(matcher.group(3).trim(), options)); | |
} | |
private Object parseParameter(String param, Options options) { | |
if (param.startsWith("\"") || param.startsWith("'")) { | |
return unquote(param); | |
} | |
try { | |
return NumberFormat.getInstance().parse(param); | |
} catch (ParseException e) { | |
return options.get(param); | |
} | |
} | |
public static class Expression { | |
private Object lh; | |
private ExpressionComparator comparator; | |
private Object rh; | |
public Expression(Object lh, ExpressionComparator comparator, Object rh) { | |
this.lh = lh; | |
this.comparator = comparator; | |
this.rh = rh; | |
} | |
public Object getLh() { | |
return lh; | |
} | |
public void setLh(Object lh) { | |
this.lh = lh; | |
} | |
public ExpressionComparator getComparator() { | |
return comparator; | |
} | |
public void setComparator(ExpressionComparator comparator) { | |
this.comparator = comparator; | |
} | |
public Object getRh() { | |
return rh; | |
} | |
public void setRh(Object rh) { | |
this.rh = rh; | |
} | |
public boolean evaluate() { | |
String exp = String.format("lh %s rh", comparator.getComparator()); | |
ExpressionParser parser = new SpelExpressionParser(); | |
return Boolean.TRUE.equals(parser.parseExpression(exp).getValue(this)); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.github.gist.ammmze.handlebars.helpers; | |
import com.github.jknack.handlebars.Helper; | |
import com.github.jknack.handlebars.Options; | |
import java.io.IOException; | |
import java.text.SimpleDateFormat; | |
import java.util.Date; | |
import java.util.HashMap; | |
import java.util.Map; | |
import org.apache.commons.lang3.StringUtils; | |
public class DateHelper implements Helper { | |
public static final String DEFAULT_FORMAT = "d/m/Y"; | |
@Override | |
public Object apply(Object context, Options options) throws IOException { | |
String format = context instanceof String && StringUtils.isNotBlank((String) context) ? (String) context : DEFAULT_FORMAT; | |
return new SimpleDateFormat(convertPhpFormatToJavaFormat(format)).format(new Date()); | |
} | |
private final static char ESC = '\\'; | |
private final static Map<Character, String> FORMAT_MAP = new HashMap<Character, String>(){{ | |
put('d', "dd"); // Day of the month, 2 digits with leading zeros. Example: 01 to 31 | |
put('j', "d"); // Day of the month without leading zeros. Example: 1 to 31 | |
put('D', "EEE"); // A textual representation of a day, three letters. Example: Mon through Sun | |
put('l', "EEEE"); // (lowercase 'L'). Example: A full textual representation of the day of the week. Example: Sunday through Saturday | |
put('N', "u"); // ISO-8601 numeric representation of the day of the week (added in PHP 5.1.0). Example: 1 (for Monday) through 7 (for Sunday) | |
put('W', "w"); // ISO-8601 week number of year, weeks starting on Monday. Example: Example: 42 (the 42nd week in the year) | |
put('F', "MMMM"); // A full textual representation of a month, such as January or March. Example: January through December | |
put('m', "MM"); // Numeric representation of a month, with leading zeros. Example: 01 through 12 | |
put('M', "MMM"); // A short textual representation of a month, three letters. Example: Jan through Dec | |
put('n', "M"); // Numeric representation of a month, without leading zeros. Example: 1 through 12 | |
put('o', "YYYY"); // ISO-8601 week-numbering year. This has the same value as Y, except that if the ISO week number (W) belongs to the previous or next year, that year is used instead. (added in PHP 5.1.0). Example: Examples: 1999 or 2003 | |
put('y', "yy"); // A two digit representation of a year. Example: Examples: 99 or 03 | |
put('Y', "yyyy"); // A full numeric representation of a year, 4 digits. Example: Examples: 1999 or 2003 | |
put('A', "a"); // Uppercase Ante meridiem and Post meridiem. Example: AM or PM | |
put('g', "h"); // 12-hour format of an hour without leading zeros. Example: 1 through 12 | |
put('h', "hh"); // 12-hour format of an hour with leading zeros. Example: 01 through 12 | |
put('G', "H"); // 24-hour format of an hour without leading zeros. Example: 0 through 23 | |
put('H', "HH"); // 24-hour format of an hour with leading zeros. Example: 00 through 23 | |
put('i', "mm"); // Minutes with leading zeros. Example: 00 to 59 | |
put('s', "ss"); // Seconds, with leading zeros. Example: 00 through 59 | |
put('v', "SSS"); // Milliseconds (added in PHP 7.0.0). Same note applies as for u.. Example: Example: 654 | |
put('e', "zzz"); // Timezone identifier (added in PHP 5.1.0). Example: Examples: UTC, GMT, Atlantic/Azores | |
put('O', "Z"); // Difference to Greenwich time (GMT) in hours. Example: Example: +0200 | |
put('P', "XXX"); // Difference to Greenwich time (GMT) with colon between hours and minutes (added in PHP 5.1.3). Example: Example: +02:00 | |
put('T', "zzz"); // Timezone abbreviation. Example: Examples: EST, MDT ... | |
put('c', "yyyy-MM-dd'T'HH:mm:ssXXX"); // ISO 8601 date (added in PHP 5). Example: 2004-02-12T15:19:21+00:00 | |
put('r', "EEE, dd MMM yyyy HH:mm:ss Z"); // » RFC 2822 formatted date. Example: Example: Thu, 21 Dec 2000 16:01:07 +0200 | |
// partially supported | |
put('a', "a"); // Lowercase Ante meridiem and Post meridiem. Example: am or pm ... EXCEPT: it ends up the same as A (upper case AM/PM instead of lower case am/pm) | |
put('z', "D"); // The day of the year (starting from 0). Example: 0 through 365 ... EXCEPT: in the java mapping, it starts with 1 instead of 0 | |
// unsupported | |
put('S', ""); // English ordinal suffix for the day of the month, 2 characters. Example: st, nd, rd or th. Works well with j | |
put('w', ""); // Numeric representation of the day of the week. Example: 0 (for Sunday) through 6 (for Saturday) | |
put('t', ""); // Number of days in the given month. Example: 28 through 31 | |
put('L', ""); // Whether it's a leap year. Example: 1 if it is a leap year, 0 otherwise. | |
put('B', ""); // Swatch Internet time. Example: 000 through 999 | |
put('u', ""); // Microseconds (added in PHP 5.2.2). Note that date() will always generate 000000 since it takes an integer parameter, whereas DateTime::format() does support microseconds if DateTime was created with microseconds.. Example: Example: 654321 | |
put('I', ""); // (capital i). Example: Whether or not the date is in daylight saving time. Example: 1 if Daylight Saving Time, 0 otherwise. | |
put('Z', ""); // Timezone offset in seconds. The offset for timezones west of UTC is always negative, and for those east of UTC is always positive.. Example: -43200 through 50400 | |
put('U', ""); // Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT). Example: See also time() | |
}}; | |
public static String convertPhpFormatToJavaFormat(String in) { | |
StringBuilder format = new StringBuilder(); | |
boolean escaping = false; | |
for (int i = 0; i < in.length(); i++) { | |
boolean escaped = i > 0 && in.charAt(i - 1) == ESC; | |
char curr = in.charAt(i); | |
if (!escaping && escaped) { | |
format.append("'"); | |
escaping = true; | |
} | |
if (escaping && !escaped && curr != ESC) { | |
format.append("'"); | |
escaping = false; | |
} | |
if (!escaped && FORMAT_MAP.containsKey((curr))) { | |
format.append(FORMAT_MAP.get(curr)); | |
} else if (escaped || curr != ESC) { | |
format.append(curr); | |
} else { | |
} | |
} | |
return format.toString(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.github.gist.ammmze.handlebars; | |
import com.github.jknack.handlebars.Handlebars; | |
import com.github.gist.ammmze.handlebars.helpers.ConditionalExpressionHelper; | |
import com.github.gist.ammmze.handlebars.helpers.DateHelper; | |
import com.github.gist.ammmze.handlebars.helpers.LowerHelper; | |
import com.github.gist.ammmze.handlebars.helpers.StripTagsHelper; | |
import com.github.gist.ammmze.handlebars.helpers.TitleHelper; | |
import com.github.gist.ammmze.handlebars.helpers.UnsubHelper; | |
import com.github.gist.ammmze.handlebars.helpers.UpperHelper; | |
import com.github.gist.ammmze.handlebars.helpers.UrlEncodeHelper; | |
import javax.annotation.PostConstruct; | |
import org.springframework.stereotype.Component; | |
@Component | |
public class HandlebarsImpl extends Handlebars { | |
@PostConstruct | |
public void postConstruct() { | |
registerHelper("if", new ConditionalExpressionHelper()); | |
registerHelper("else if", new ConditionalExpressionHelper()); | |
registerHelper("unless", new ConditionalExpressionHelper(Boolean.FALSE)); | |
registerHelper("upper", new UpperHelper()); | |
registerHelper("lower", new LowerHelper()); | |
registerHelper("title", new TitleHelper()); | |
registerHelper("url", new UrlEncodeHelper()); | |
registerHelper("date", new DateHelper()); | |
registerHelper("striptags", new StripTagsHelper()); | |
registerHelper("unsub", new UnsubHelper()); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.github.gist.ammmze.handlebars.helpers; | |
import com.github.jknack.handlebars.Helper; | |
import com.github.jknack.handlebars.Options; | |
import java.io.IOException; | |
public class LowerHelper implements Helper<String> { | |
@Override | |
public Object apply(String context, Options options) throws IOException { | |
return context == null ? null : context.toLowerCase(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.github.gist.ammmze.handlebars.helpers; | |
import com.github.jknack.handlebars.Helper; | |
import com.github.jknack.handlebars.Options; | |
import com.ibm.icu.lang.UCharacter; | |
import java.io.IOException; | |
public class TitleHelper implements Helper<String> { | |
@Override | |
public Object apply(String context, Options options) throws IOException { | |
return UCharacter.toTitleCase(context, null); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.github.gist.ammmze.handlebars.helpers; | |
import com.github.jknack.handlebars.Helper; | |
import com.github.jknack.handlebars.Options; | |
import java.io.IOException; | |
public class UnsubHelper implements Helper { | |
public final static String DEFAULT_URL = ""; | |
@Override | |
public Object apply(Object context, Options options) throws IOException { | |
return context instanceof String ? context : DEFAULT_URL; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.github.gist.ammmze.handlebars.helpers; | |
import com.github.jknack.handlebars.Helper; | |
import com.github.jknack.handlebars.Options; | |
import java.io.IOException; | |
public class UpperHelper implements Helper<String> { | |
@Override | |
public Object apply(String context, Options options) throws IOException { | |
return context == null ? null : context.toUpperCase(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.github.gist.ammmze.handlebars.helpers; | |
import com.github.jknack.handlebars.Helper; | |
import com.github.jknack.handlebars.Options; | |
import java.io.IOException; | |
import java.net.URLEncoder; | |
public class UrlEncodeHelper implements Helper<String> { | |
@Override | |
public Object apply(String context, Options options) throws IOException { | |
return context == null ? null : URLEncoder.encode(context, "UTF-8"); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment