/* * The Alluxio Open Foundation licenses this work under the Apache License, version 2.0 * (the "License"). You may not use this work except in compliance with the License, which is * available at www.apache.org/licenses/LICENSE-2.0 * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, * either express or implied, as more fully set forth in the License. * * See the NOTICE file distributed with this work for information regarding copyright ownership. */ package alluxio.util; import alluxio.util.io.PathUtils; import com.google.common.base.Joiner; import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; import java.net.URLDecoder; import java.net.URLEncoder; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import javax.annotation.concurrent.ThreadSafe; /** * Utility methods for working with URIs. */ @ThreadSafe public final class URIUtils { public static final char QUERY_SEPARATOR = '&'; public static final char QUERY_KEY_VALUE_SEPARATOR = '='; private URIUtils() {} // prevent instantiation /** * Appends the given path to the given base URI. * * @param base the base URI * @param path the path to append * @return the URI resulting from appending the base and the path * @throws URISyntaxException if URI syntax error is encountered */ public static URI appendPath(URI base, String path) throws URISyntaxException { return new URI(base.getScheme(), base.getAuthority(), PathUtils.concatPath(base.getPath(), path), base.getQuery(), base.getFragment()); } /** * Appends the given path to the given base URI. It throws an {@link RuntimeException} if * the inputs are malformed. * * @param base the base URI * @param path the path to append * @return the URI resulting from appending the base and the path */ public static URI appendPathOrDie(URI base, String path) { try { return appendPath(base, path); } catch (URISyntaxException e) { throw new RuntimeException(e); } } /** * Generates a query string from a {@link Map <String, String>} of key/value pairs. * * @param queryMap the map of query key/value pairs * @return the generated query string, null if the input map is null or empty */ public static String generateQueryString(Map<String, String> queryMap) { if (queryMap == null || queryMap.isEmpty()) { return null; } ArrayList<String> pairs = new ArrayList<>(queryMap.size()); try { for (Map.Entry<String, String> entry : queryMap.entrySet()) { pairs.add( URLEncoder.encode(entry.getKey(), "UTF-8") + QUERY_KEY_VALUE_SEPARATOR + URLEncoder .encode(entry.getValue(), "UTF-8")); } } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } Joiner joiner = Joiner.on(QUERY_SEPARATOR); return joiner.join(pairs); } /** * Parses the given query string, and returns a map of the query parameters. * * @param query the query string to parse * @return the map of query keys and values */ public static Map<String, String> parseQueryString(String query) { Map<String, String> queryMap = new HashMap<>(); if (query == null || query.isEmpty()) { return queryMap; } // The query string should escape '&'. String[] entries = query.split(String.valueOf(QUERY_SEPARATOR)); try { for (String entry : entries) { // There should be at most 2 parts, since key and value both should escape '='. String[] parts = entry.split(String.valueOf(QUERY_KEY_VALUE_SEPARATOR)); if (parts.length == 0) { // Skip this empty entry. } else if (parts.length == 1) { // There is no value part. Just use empty string as the value. String key = URLDecoder.decode(parts[0], "UTF-8"); queryMap.put(key, ""); } else { // Save the key and value. String key = URLDecoder.decode(parts[0], "UTF-8"); String value = URLDecoder.decode(parts[1], "UTF-8"); queryMap.put(key, value); } } } catch (UnsupportedEncodingException e) { // This is unexpected. throw new RuntimeException(e); } return queryMap; } }