Skip to content

Commit

Permalink
Revised UriUtils and added test cases
Browse files Browse the repository at this point in the history
  • Loading branch information
Aklakan committed Dec 3, 2023
1 parent f0209d4 commit 2157b62
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 25 deletions.
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
package org.aksw.commons.io.util;

import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.AbstractMap.SimpleImmutableEntry;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.function.BiConsumer;
import java.util.function.Supplier;
import java.util.stream.Collectors;

import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.Multimap;

public class UriUtils {
Expand Down Expand Up @@ -43,7 +49,13 @@ public static Optional<URI> tryNewURI(String uri) {
return result;
}

public static String encodeUtf8(String str) {
return URLEncoder.encode(str, StandardCharsets.UTF_8);
}

public static String decodeUft8(String str) {
return URLDecoder.decode(str, StandardCharsets.UTF_8);
}

/**
* Only retains first value
Expand All @@ -58,7 +70,7 @@ public static Optional<URI> tryNewURI(String uri) {
* @return
*/
public static Map<String, String> parseQueryStringAsMap(String queryString) {
Multimap<String, String> multimap = parseQueryString(queryString);
Multimap<String, String> multimap = parseQueryStringAsMultimap(queryString);
return toMap(multimap, LinkedHashMap::new);
}

Expand All @@ -67,34 +79,56 @@ public static <K, V> Map<K, V> toMap(Multimap<K, V> mm, Supplier<? extends Map<K
.collect(Collectors.toMap(Entry::getKey, Entry::getValue, (u, v) -> u, mapSupplier));
}


public static Multimap<String, String> parseQueryString(String queryString) {
try {
return parseQueryStringEx(queryString);
} catch(Exception e) {
throw new RuntimeException(e);
}
public static Multimap<String, String> parseQueryStringAsMultimap(String queryString) {
Multimap<String, String> result = LinkedHashMultimap.create();
parseQueryStringAsEntries(queryString, result::put);
return result;
}

public static Multimap<String, String> parseQueryStringEx(String queryString)
throws UnsupportedEncodingException
{
Multimap<String, String> result = ArrayListMultimap.create();

if(queryString == null) {
return result;
}
public static List<Entry<String, String>> parseQueryStringAsList(String queryString) {
List<Entry<String, String>> result = new ArrayList<>();
parseQueryStringAsEntries(queryString, (k, v) -> result.add(new SimpleImmutableEntry<>(k, v)));
return result;
}

for (String param : queryString.split("&")) {
String pair[] = param.split("=");
String key = URLDecoder.decode(pair[0], "UTF-8");
String value = "";
if (pair.length > 1) {
value = URLDecoder.decode(pair[1], "UTF-8");
public static void parseQueryStringAsEntries(String queryString, BiConsumer<String, String> sink) {
if (queryString != null && !queryString.isBlank()) {
for (String param : queryString.split("&")) {
String pair[] = param.split("=", 2);
String key = decodeUft8(pair[0]);
String value = null;
if (pair.length > 1) {
value = decodeUft8(pair[1]);
}
sink.accept(key, value);
}
result.put(new String(key), new String(value));
}
}

public static String toQueryString(Multimap<String, String> args) {
return toQueryString(args.entries());
}

public static String toQueryString(Collection<Entry<String, String>> entries) {
String tmp = entries.stream().map(e -> {
String k = e.getKey();
String v = e.getValue();
String encodedK = encodeUtf8(k);
String r = v == null
? encodedK
: encodedK + "=" + encodeUtf8(v);
return r;
}).collect(Collectors.joining("&"));
String result = !tmp.isEmpty() ? tmp : null;
return result;
}

/** Returns a new URI with its query string replaced directly with the given argument */
public static URI replaceQueryString(URI uri, String newQueryString) throws URISyntaxException {
return new URI(uri.getScheme(),
uri.getAuthority(),
uri.getPath(),
newQueryString,
uri.getFragment());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package org.aksw.commons.io.util;

import java.util.ArrayList;
import java.util.List;

import org.junit.Assert;
import org.junit.Test;

import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.Multimap;

public class TestUriUtils {
@Test
public void test01() {
Multimap<String, String> expected = ImmutableMultimap.<String, String>builder()
.put("a", "b").put("c", "d").build();
// The actual map is a LinkedHashMultiMap so the order should always be as given
Multimap<String, String> actual = UriUtils.parseQueryStringAsMultimap("a=b&c=d");
// The entry collections differ in type so we need array lists
Assert.assertEquals(new ArrayList<>(expected.entries()), new ArrayList<>(actual.entries()));
}

/** null query string must result in empty list */
@Test
public void testNullQueryStringResultsInEmptyList() {
Assert.assertEquals(List.of(), UriUtils.parseQueryStringAsList(null));
}

/** empty query string must result in empty list */
@Test
public void testEmptyQueryStringResultsInEmptyList() {
Assert.assertEquals(List.of(), UriUtils.parseQueryStringAsList(""));
}

/** empty list result in null query string */
@Test
public void testEmptyListResultsInNullQueryString() {
Assert.assertEquals(null, UriUtils.toQueryString(List.of()));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.util.Arrays;
import java.util.Objects;
import java.util.Optional;
import java.util.function.BinaryOperator;
import java.util.function.Supplier;

import org.apache.commons.lang3.ClassUtils;
Expand Down Expand Up @@ -54,6 +55,20 @@ public static <T> T castAsOrNull(Class<T> clazz, Object o) {
}


/**
* If both arguments are non-null then invoke the merger with them and return its result.
* If both arguments are null return null;
* Otherwise return the non-null argument.
*/
public static <T> T mergeNonNull(T a, T b, BinaryOperator<T> merger) {
T result = a == null
? b
: b == null
? a
: merger.apply(a, b);
return result;
}

/**
* Supplier-based coalesce function as described in
* https://benjiweber.co.uk/blog/2013/12/08/null-coalescing-in-java-8/
Expand Down

0 comments on commit 2157b62

Please sign in to comment.