StringExt { public static <A> Map<String, A> parseKeyValueList(String text, Pattern entrySeparator, Pattern pairSeparator, BiFunction<A, String, A> accumulator) { String trimmed = safeTrim(text); if (isEmpty(trimmed)) { return Collections.emptyMap(); } List<String> keyValuePairs = asList(entrySeparator.split(trimmed)); if (keyValuePairs.isEmpty()) { return Collections.emptyMap(); } Map<String, A> result = new HashMap<>(); for (String keyValuePair : keyValuePairs) { int idx = indexOf(pairSeparator, keyValuePair); String key; String value; if (idx == -1) { key = keyValuePair; value = ""; } else if (idx == keyValuePair.length() - 1) { key = keyValuePair.substring(0, idx); value = ""; } else { key = keyValuePair.substring(0, idx); value = keyValuePair.substring(idx + 1); } result.put(key, accumulator.apply(result.get(key), value)); } return result; } private  StringExt(); static boolean isNotEmpty(String s); static boolean isEmpty(String s); static String safeTrim(String s); static String nonNull(String value); static String takeUntil(String value, String endMarker); static void applyIfNonEmpty(String value, Consumer<String> consumer); static void runIfEmpty(String value, Runnable action); static void trimAndApplyIfNonEmpty(String value, Consumer<String> consumer); static String concatenate(Collection<String> stringCollection, String delimiter); static String concatenate(T[] array, String delimiter, Function<T, String> mapping); static String concatenate(Object[] array, String delimiter); static String concatenate(E[] array, String delimiter); static String concatenate(Class<E> enumType, String delimiter, Function<E, Boolean> filter); static String getNonEmptyOrDefault(String value, String defaultValue); static Optional<String> firstNonEmpty(String... values); static List<String> trim(List<String> list); static Pattern getCommaSeparator(); static Pattern getDotSeparator(); static Pattern getSemicolonSeparator(); static List<String> splitByComma(String value); static Set<String> splitByCommaIntoSet(String value); static List<String> splitByDot(String value); static E parseEnumIgnoreCase(String enumName, Class<E> enumType); static List<E> parseEnumListIgnoreCase(String enumNames, Class<E> enumType); static List<E> parseEnumListIgnoreCase(String enumNames, Class<E> enumType, Function<String, List<E>> groupings); static Map<String, A> parseKeyValueList(String text, Pattern entrySeparator, Pattern pairSeparator,
                                                       BiFunction<A, String, A> accumulator); static Map<String, String> parseKeyValueList(String text); static Map<String, Set<String>> parseKeyValuesList(String text); static boolean isAsciiLetter(char c); static boolean isAsciiDigit(char c); static boolean isAsciiLetterOrDigit(char c); static String removeNonAlphanumeric(String text); static int indexOf(Pattern pattern, String text); static String doubleQuotes(String text); static String removeSurroundingQuotes(String text); static String appendToEndIfMissing(String text, String value); static String startWithLowercase(String text); static Optional<Integer> parseInt(String s); static Optional<Long> parseLong(String s); static Optional<Double> parseDouble(String s); static Optional<List<Duration>> parseDurationMsList(String s); static String gzipAndBase64Encode(String s); static Either<String, IllegalArgumentException> nameFromJavaBeanGetter(String getterName); final static Pattern COMMA_SPLIT_RE; final static Pattern DOT_SPLIT_RE; final static Pattern SEMICOLON_SPLIT_RE; final static Pattern COLON_SPLIT_RE; final static Pattern EQUAL_SPLIT_RE; }
@Test public void testParseKeyValueList() { Map<String, String> actual = parseKeyValueList("key1:value1,key2:value2,key3:,key4"); assertThat(actual).containsAllEntriesOf(ImmutableMap.<String, String> builder().put("key1", "value1").put("key2", "value2").put("key3", "").put("key4", "").build() ); }