package io.divolte.server.recordmapping;
import static java.net.URLDecoder.*;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import javax.annotation.concurrent.ThreadSafe;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
@ParametersAreNonnullByDefault
@ThreadSafe
public final class QueryStringParser {
public static Map<String,List<String>> parseQueryString(@Nullable final String string) {
if (string == null) {
return Collections.emptyMap();
}
Map<String,List<String>> result = Maps.newHashMapWithExpectedSize(10);
try {
int stringStart = 0;
String attrName = null;
for (int i = 0; i < string.length(); ++i) {
char c = string.charAt(i);
if (c == '=' && attrName == null) {
attrName = string.substring(stringStart, i);
stringStart = i + 1;
} else if (c == '&') {
if (attrName != null) {
final String key = decode(attrName, StandardCharsets.UTF_8.name());
final String value = decode(string.substring(stringStart, i), StandardCharsets.UTF_8.name());
mergeIntoMap(result, key, value);
} else {
final String key = decode(string.substring(stringStart, i), StandardCharsets.UTF_8.name());
final String value = "";
mergeIntoMap(result, key, value);
}
stringStart = i + 1;
attrName = null;
}
}
if (attrName != null) {
final String key = decode(attrName, StandardCharsets.UTF_8.name());
final String value = decode(string.substring(stringStart, string.length()), StandardCharsets.UTF_8.name());
mergeIntoMap(result, key, value);
} else if (string.length() != stringStart) {
final String key = decode(string.substring(stringStart, string.length()), StandardCharsets.UTF_8.name());
final String value = "";
mergeIntoMap(result, key, value);
}
} catch (UnsupportedEncodingException e) {
return Collections.emptyMap();
}
return result;
}
private static void mergeIntoMap(final Map<String,List<String>> map, final String key, final String value) {
map.compute(key, (ignored,existing) -> {
if (existing != null) {
existing.add(value);
return existing;
}
return Lists.newArrayList(value);
});
}
}