alluxio.util.URIUtils Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of alluxio-core-common Show documentation
Show all versions of alluxio-core-common Show documentation
Common utilities shared in Alluxio core modules
/*
* 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.AlluxioURI;
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.Nullable;
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 static final int TO_LOWER_OFFSET = 'a' - 'A';
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} 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
*/
@Nullable
public static String generateQueryString(Map queryMap) {
if (queryMap == null || queryMap.isEmpty()) {
return null;
}
ArrayList pairs = new ArrayList<>(queryMap.size());
try {
for (Map.Entry 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 parseQueryString(String query) {
Map 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;
}
/**
* @param uri a uri to check
* @return whether the uri represents a local filesystem UFS path
*/
public static boolean isLocalFilesystem(String uri) {
return uri.startsWith(AlluxioURI.SEPARATOR)
|| uri.startsWith("file://")
|| AlluxioURI.hasWindowsDrive(uri, false);
}
/**
* @param s1 first string (can be null)
* @param s2 second string (can be null)
* @return negative integer, zero, or positive integer, if the first string is less
* than, equal to, or greater than the second string
*/
public static int compare(String s1, String s2) {
if (s1 == null && s2 == null) {
return 0;
}
if (s1 == null) {
return -1;
}
if (s2 == null) {
return 1;
}
return s1.compareTo(s2);
}
/**
* Checks if two (nullable) strings are equal. Handles `%` codes in URIs.
*
* @param s1 the first string to compare (can be null)
* @param s2 the second string to compare (can be null)
* @return true if the strings are equal
*/
public static boolean equals(String s1, String s2) {
if (s1 == null && s2 == null) {
return true;
}
if ((s1 != null) && (s2 != null)) {
if (s1.length() != s2.length()) {
return false;
}
if (s1.indexOf('%') < 0) {
return s1.equals(s2);
}
int length = s1.length();
for (int i = 0; i < length; i++) {
char c1 = s1.charAt(i);
char c2 = s2.charAt(i);
if (c1 != '%') {
if (c1 != c2) {
return false;
}
continue;
}
// c1 is '%'
if (c2 != '%') {
return false;
}
// check next 2 characters
i++;
if (toLower(s1.charAt(i)) != toLower(s2.charAt(i))) {
return false;
}
i++;
if (toLower(s1.charAt(i)) != toLower(s2.charAt(i))) {
return false;
}
}
return true;
}
return false;
}
/**
* @param c the character to convert
* @return the lowercase counterpart to the input character
*/
public static int toLower(char c) {
if (c >= 'A' && c <= 'Z') {
return c + TO_LOWER_OFFSET;
}
return c;
}
/**
* Returns true if the path requires normalization. A path needs normalization if it has segments
* of: ".", "..", or consecutive "/".
* @param path path to check
* @return true if the path requires normalization
*/
public static boolean needsNormalization(String path) {
int last = path.length() - 1;
int curr = 0;
// Skip initial slashes
while (curr <= last) {
if (path.charAt(curr) != '/') {
break;
}
curr++;
}
if (curr > 1) {
return true;
}
while (curr <= last) {
// segment is "." or "./"
if ((path.charAt(curr) == '.') && ((curr == last) || (path.charAt(curr + 1) == '/'))) {
return true;
}
// segment is ".." or "../"
if ((path.charAt(curr) == '.') && (path.charAt(curr + 1) == '.') && ((curr + 1 == last) || (
path.charAt(curr + 2) == '/'))) {
return true;
}
while (curr <= last) {
// Find next '/'
if (path.charAt(curr++) != '/') {
continue;
}
// Previous 'curr' was a '/'. Check for redundant slashes.
if (path.charAt(curr) != '/') {
break;
}
// multiple slashes
return true;
}
}
return false;
}
/**
* Hashes a string for a URI hash. Handles octets.
*
* @param hash the input hash
* @param s the string to hash and combine with the input hash
* @return the resulting hash
*/
public static int hash(int hash, String s) {
if (s == null) {
return hash;
}
// similar to Arrays.hashCode
return s.indexOf('%') < 0 ? 31 * hash + s.hashCode() : normalizedHash(hash, s);
}
/**
* Hashes a string for a URI hash, while ignoring the case.
*
* @param hash the input hash
* @param s the string to hash and combine with the input hash
* @return the resulting hash
*/
public static int hashIgnoreCase(int hash, String s) {
if (s == null) {
return hash;
}
int length = s.length();
for (int i = 0; i < length; i++) {
hash = 31 * hash + URIUtils.toLower(s.charAt(i));
}
return hash;
}
/**
* Hashes a string (which includes octets) for a URI hash.
*
* @param hash the input hash
* @param s the string to hash and combine with the input hash
* @return the resulting hash
*/
private static int normalizedHash(int hash, String s) {
int nextHash = 0;
for (int index = 0; index < s.length(); index++) {
char ch = s.charAt(index);
nextHash = 31 * nextHash + ch;
if (ch == '%') {
// hash next 2 characters
index++;
if (index >= s.length()) {
break;
}
nextHash = 31 * nextHash + URIUtils.toLower(s.charAt(index));
index++;
if (index >= s.length()) {
break;
}
nextHash = 31 * nextHash + URIUtils.toLower(s.charAt(index));
}
}
// similar to Arrays.hashCode
return hash * 31 + nextHash;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy