package com.kickstarter.libs; import android.content.res.Resources; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.text.TextUtils; import java.util.HashMap; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; public final class KSString { private final String packageName; private final Resources resources; public KSString(final @NonNull String packageName, final @NonNull Resources resources) { this.packageName = packageName; this.resources = resources; } /** * Replace each key found in the string with its corresponding value. */ public @NonNull String format(final @NonNull String string, final @NonNull String key1, final @Nullable String value1) { final Map<String, String> substitutions = new HashMap<String, String>() { { put(key1, value1); } }; return replace(string, substitutions); } /** * Replace each key found in the string with its corresponding value. */ public @NonNull String format(final @NonNull String string, final @NonNull String key1, final @Nullable String value1, final @NonNull String key2, final @Nullable String value2) { final Map<String, String> substitutions = new HashMap<String, String>() { { put(key1, value1); put(key2, value2); } }; return replace(string, substitutions); } /** * Replace each key found in the string with its corresponding value. */ public @NonNull String format(final @NonNull String string, final @NonNull String key1, final @Nullable String value1, final @NonNull String key2, final @Nullable String value2, final @NonNull String key3, final @Nullable String value3) { final Map<String, String> substitutions = new HashMap<String, String>() { { put(key1, value1); put(key2, value2); put(key3, value3); } }; return replace(string, substitutions); } /** * Replace each key found in the string with its corresponding value. */ public @NonNull String format(final @NonNull String string, final @NonNull String key1, final @Nullable String value1, final @NonNull String key2, final @Nullable String value2, final @NonNull String key3, final @Nullable String value3, final @NonNull String key4, final @Nullable String value4) { final Map<String, String> substitutions = new HashMap<String, String>() { { put(key1, value1); put(key2, value2); put(key3, value3); put(key4, value4); } }; return replace(string, substitutions); } /** * Given a base key path and count, find the appropriate string resource and replace each key * found in the string resource with its corresponding value. For example, given a base key of `foo`, * a count of 0 would give the string resource `foo_zero`, a count of 1 `foo_one`, and so on. * * This particular version is for strings that have no replaceable sections */ public @NonNull String format(final @NonNull String baseKeyPath, final int count) { return stringFromKeyPath(baseKeyPath, keyPathComponentForCount(count)); } /** * Given a base key path and count, find the appropriate string resource and replace each key * found in the string resource with its corresponding value. For example, given a base key of `foo`, * a count of 0 would give the string resource `foo_zero`, a count of 1 `foo_one`, and so on. */ public @NonNull String format(final @NonNull String baseKeyPath, final int count, final @NonNull String key1, final @Nullable String value1) { final String string = stringFromKeyPath(baseKeyPath, keyPathComponentForCount(count)); return format(string, key1, value1); } /** * Given a base key path and count, find the appropriate string resource and replace each key * found in the string resource with its corresponding value. For example, given a base key of `foo`, * a count of 0 would give the string resource `foo_zero`, a count of 1 `foo_one`, and so on. */ public @NonNull String format(final @NonNull String baseKeyPath, final int count, final @NonNull String key1, final @Nullable String value1, final @NonNull String key2, final @Nullable String value2) { final String string = stringFromKeyPath(baseKeyPath, keyPathComponentForCount(count)); return format(string, key1, value1, key2, value2); } /** * Given a base key path and count, find the appropriate string resource and replace each key * found in the string resource with its corresponding value. For example, given a base key of `foo`, * a count of 0 would give the string resource `foo_zero`, a count of 1 `foo_one`, and so on. */ public @NonNull String format(final @NonNull String baseKeyPath, final int count, final @NonNull String key1, final @Nullable String value1, final @NonNull String key2, final @Nullable String value2, final @NonNull String key3, final @Nullable String value3) { final String string = stringFromKeyPath(baseKeyPath, keyPathComponentForCount(count)); return format(string, key1, value1, key2, value2, key3, value3); } /** * Takes a variable length of {@link String} arguments, joins them together to form a single path, then * looks up a string resource given that path. If the resource cannot be found, returns an empty string. */ private @NonNull String stringFromKeyPath(final @NonNull String... keyPathComponents) { final String keyPath = TextUtils.join("_", keyPathComponents); try { final int resourceId = resources.getIdentifier(keyPath, "string", packageName); return resources.getString(resourceId); } catch (final @NonNull Resources.NotFoundException e) { return ""; } } private @Nullable String keyPathComponentForCount(final int count) { if (count == 0) { return "zero"; } else if (count == 1) { return "one"; } else if (count == 2) { return "two"; } else if (count > 2 && count <= 5) { return "few"; } else if (count > 5) { return "many"; } return null; } /** * For a given string, replaces occurrences of each key with its corresponding value. In the string, keys are wrapped * with `%{}`, e.g. `%{backers_count} backers`. In this instance, the substitutions hash might contain one entry with the key * `backers_count` and value `2`. */ private @NonNull String replace(final @NonNull String string, final @NonNull Map<String, String> substitutions) { final StringBuilder builder = new StringBuilder(); for (final String key : substitutions.keySet()) { if (builder.length() > 0) { builder.append("|"); } builder .append("(%\\{") .append(key) .append("\\})"); } final Pattern pattern = Pattern.compile(builder.toString()); final Matcher matcher = pattern.matcher(string); final StringBuffer buffer = new StringBuffer(); while (matcher.find()) { final String key = NON_WORD_REGEXP.matcher(matcher.group()).replaceAll(""); final String value = substitutions.get(key); final String replacement = Matcher.quoteReplacement(value != null ? value : ""); matcher.appendReplacement(buffer, replacement); } matcher.appendTail(buffer); return buffer.toString(); } private static final Pattern NON_WORD_REGEXP = Pattern.compile("[^\\w]"); }