Created
November 14, 2018 17:45
-
-
Save attacco/42c806061df3f20931825e49e505a5de to your computer and use it in GitHub Desktop.
This file contains hidden or 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
public class ResourceUtils { | |
// example: ${date:DD.MM} | |
private static final Pattern DATE_TIME_PLACEHOLDER_PATTERN = | |
Pattern.compile("\\$\\{(?<value>date:[\\w.:\\-\\s/\\\\]+)}"); | |
// examples: ${number:N} | |
private static final Pattern NUMBER_PLACEHOLDER_PATTERN = | |
Pattern.compile("\\$\\{(?<value>number:[\\w.:\\-\\s/\\\\?]+)}"); | |
private static final Map<String, Formatter> FORMATTERS = Stream.of( | |
new AbstractMap.SimpleEntry<>("date:DD.MM", new DateTimeFormatter("dd.MM")), | |
new AbstractMap.SimpleEntry<>("date:DD.MM.YYYY", new DateTimeFormatter("dd.MM.yyyy")), | |
new AbstractMap.SimpleEntry<>("date:DD.MM.YYYY HH:MM", new DateTimeFormatter("dd.MM.yyyy HH:mm")), | |
new AbstractMap.SimpleEntry<>("number:N", new NumberFormatter("0")), | |
new AbstractMap.SimpleEntry<>("number:N.00", new NumberFormatter("0.00")), | |
new AbstractMap.SimpleEntry<>("number:N.??", new NumberFormatter("0.##")) | |
).collect(Collectors.toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue)); | |
private static final Formatter NULL_FORMATTER = arg -> "null"; | |
public static String formatString(String str, Object[] args) { | |
if (StringUtils.isEmpty(str)) { | |
return str; | |
} | |
List<Operation> operations = new ArrayList<>(); | |
collectOperations(operations, DATE_TIME_PLACEHOLDER_PATTERN, str); | |
collectOperations(operations, NUMBER_PLACEHOLDER_PATTERN, str); | |
if (operations.isEmpty()) { | |
return str; | |
} else if (operations.size() != args.length) { | |
throw new IllegalArgumentException(String.format( | |
"Arguments count [%d] doesn't matches to placeholders count [%d]", | |
args.length, operations.size())); | |
} | |
operations.sort(Comparator.comparing(Operation::getRangeStart)); | |
StringBuilder sb = new StringBuilder(); | |
Operation lastOperation = null; | |
for (int i = 0; i < operations.size(); i++) { | |
Operation operation = operations.get(i); | |
Object arg = args[i]; | |
Formatter formatter = arg == null ? NULL_FORMATTER : FORMATTERS.get(operation.getPattern()); | |
if (formatter == null) { | |
throw new IllegalArgumentException(String.format( | |
"Unable to find appropriate formatter for pattern [%s]", operation.getPattern())); | |
} | |
String formattedValue; | |
try { | |
formattedValue = formatter.format(arg); | |
} catch (RuntimeException e) { | |
throw new IllegalArgumentException(String.format("Unable to format arg [%s] using pattern [%s]", | |
arg, operation.getPattern()), e); | |
} | |
int startIndex = lastOperation == null ? 0 : lastOperation.getRangeEnd() + 1; | |
if (operation.getRangeStart() > startIndex) { | |
sb.append(str.substring(startIndex, operation.getRangeStart())); | |
} | |
sb.append(formattedValue); | |
lastOperation = operation; | |
} | |
if (lastOperation != null && lastOperation.getRangeEnd() < str.length() - 1) { | |
sb.append(str.substring(lastOperation.getRangeEnd() + 1, str.length())); | |
} | |
return sb.toString(); | |
} | |
private static void collectOperations(List<Operation> operations, Pattern pattern, String str) { | |
Matcher matcher = pattern.matcher(str); | |
while (matcher.find()) { | |
operations.add(new Operation(matcher.start(), matcher.end() - 1, matcher.group("value"))); | |
} | |
} | |
private interface Formatter { | |
String format(Object arg); | |
} | |
private static class DateTimeFormatter implements Formatter { | |
private final java.time.format.DateTimeFormatter dateTimeFormatter; | |
public DateTimeFormatter(String pattern) { | |
this.dateTimeFormatter = java.time.format.DateTimeFormatter.ofPattern(pattern); | |
} | |
@Override | |
public String format(Object arg) { | |
if (!(arg instanceof TemporalAccessor)) { | |
throw new IllegalArgumentException( | |
String.format("Argument [%s] can not be formatted as date-time value", arg)); | |
} | |
return dateTimeFormatter.format((TemporalAccessor) arg); | |
} | |
} | |
private static class NumberFormatter implements Formatter { | |
private final DecimalFormat decimalFormat; | |
public NumberFormatter(String pattern) { | |
DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(); | |
dfs.setDecimalSeparator('.'); | |
decimalFormat = new DecimalFormat(pattern); | |
decimalFormat.setDecimalFormatSymbols(dfs); | |
} | |
@Override | |
public synchronized String format(Object arg) { | |
return decimalFormat.format(arg); | |
} | |
} | |
private static class Operation { | |
private final int rangeStart; | |
private final int rangeEnd; | |
private final String pattern; | |
public Operation(int rangeStart, int rangeEnd, String pattern) { | |
this.rangeStart = rangeStart; | |
this.rangeEnd = rangeEnd; | |
this.pattern = pattern; | |
} | |
public int getRangeStart() { | |
return rangeStart; | |
} | |
public int getRangeEnd() { | |
return rangeEnd; | |
} | |
public String getPattern() { | |
return pattern; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment