From bf63ca5bd5470a455ec8322c1d6264c5341ebb5e Mon Sep 17 00:00:00 2001 From: Xiang Fu Date: Thu, 12 Dec 2024 01:18:36 -0800 Subject: [PATCH] Add URL scalar functions --- .../common/function/scalar/UrlFunctions.java | 730 ++++++++++++++++++ .../function/scalar/UrlFunctionsTest.java | 530 +++++++++++++ 2 files changed, 1260 insertions(+) create mode 100644 pinot-common/src/main/java/org/apache/pinot/common/function/scalar/UrlFunctions.java create mode 100644 pinot-common/src/test/java/org/apache/pinot/common/function/scalar/UrlFunctionsTest.java diff --git a/pinot-common/src/main/java/org/apache/pinot/common/function/scalar/UrlFunctions.java b/pinot-common/src/main/java/org/apache/pinot/common/function/scalar/UrlFunctions.java new file mode 100644 index 000000000000..b82519242861 --- /dev/null +++ b/pinot-common/src/main/java/org/apache/pinot/common/function/scalar/UrlFunctions.java @@ -0,0 +1,730 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.pinot.common.function.scalar; + +import java.net.URI; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import org.apache.pinot.spi.annotations.ScalarFunction; + + +/** + * URL Transformation Functions + * The functions can be used as UDFs in Query when added in the FunctionRegistry. + * {@code @ScalarFunction} annotation is used with each method for the registration + */ +public class UrlFunctions { + private UrlFunctions() { + } + + /** + * Extracts the protocol (scheme) from the URL. + * + * @param url URL string + * @return Protocol or null if invalid + */ + @ScalarFunction + public static String urlProtocol(String url) { + try { + return URI.create(url).getScheme(); + } catch (Exception e) { + return null; + } + } + + /** + * Extracts the domain from the URL. + * + * @param url URL string + * @return Domain or null if invalid + */ + @ScalarFunction + public static String urlDomain(String url) { + try { + return URI.create(url).getHost(); + } catch (Exception e) { + return null; + } + } + + /** + * Extracts the domain without the leading "www." if present. + * + * @param url URL string + * @return Domain without "www." or null if invalid + */ + @ScalarFunction + public static String urlDomainWithoutWWW(String url) { + try { + String domain = URI.create(url).getHost(); + if (domain != null && domain.startsWith("www.")) { + return domain.substring(4); + } + return domain; + } catch (Exception e) { + return null; + } + } + + /** + * Extracts the top-level domain (TLD) from the URL. + * + * @param url URL string + * @return Top-level domain or null if invalid + */ + @ScalarFunction + public static String urlTopLevelDomain(String url) { + try { + String domain = URI.create(url).getHost(); + if (domain != null) { + String[] domainParts = domain.split("\\."); + return domainParts[domainParts.length - 1]; + } + return null; + } catch (Exception e) { + return null; + } + } + + /** + * Extracts the first significant subdomain from the URL. + * + * @param url URL string + * @return First significant subdomain or null if invalid + */ + @ScalarFunction + public static String urlFirstSignificantSubdomain(String url) { + try { + String domain = URI.create(url).getHost(); + if (domain != null) { + String[] domainParts = domain.split("\\."); + if (domainParts.length <= 2) { + return domainParts[0]; + } + String tld = domainParts[domainParts.length - 1]; + if (tld.equals("com") || tld.equals("net") || tld.equals("org") || tld.equals("co")) { + return domainParts[domainParts.length - 2]; + } + return domainParts[domainParts.length - 3]; + } + return null; + } catch (Exception e) { + return null; + } + } + + /** + * Extracts the first significant subdomain and the top-level domain from the URL. + * + * @param url URL string + * @return First significant subdomain and top-level domain or null if invalid + */ + @ScalarFunction + public static String cutToFirstSignificantSubdomain(String url) { + try { + String domain = URI.create(url).getHost(); + if (domain != null) { + String[] domainParts = domain.split("\\."); + if (domainParts.length <= 2) { + return domain; + } + String tld = domainParts[domainParts.length - 1]; + if (tld.equals("com") || tld.equals("net") || tld.equals("org") || tld.equals("co")) { + return domainParts[domainParts.length - 2] + "." + domainParts[domainParts.length - 1]; + } + return domainParts[domainParts.length - 3] + "." + domainParts[domainParts.length - 2] + "." + domainParts[ + domainParts.length - 1]; + } + return null; + } catch (Exception e) { + return null; + } + } + + /** + * Returns the part of the domain that includes top-level subdomains up to the "first significant subdomain", + * without stripping www. + * + * @param url URL string + * @return First significant subdomain and top-level domain or null if invalid + */ + @ScalarFunction + public static String cutToFirstSignificantSubdomainWithWWW(String url) { + try { + String domain = URI.create(url).getHost(); + if (domain != null) { + String[] domainParts = domain.split("\\."); + if (domainParts.length <= 2) { + return domain; + } + String tld = domainParts[domainParts.length - 1]; + if (tld.equals("com") || tld.equals("net") || tld.equals("org") || tld.equals("co")) { + String subDomain = domainParts[domainParts.length - 2] + "." + domainParts[domainParts.length - 1]; + if (domainParts[0].equals("www") && domainParts.length == 3) { + return "www." + subDomain; + } + return subDomain; + } + String subDomain = + domainParts[domainParts.length - 3] + "." + domainParts[domainParts.length - 2] + "." + domainParts[ + domainParts.length - 1]; + if (domainParts[0].equals("www") && domainParts.length == 4) { + return "www." + subDomain; + } + return subDomain; + } + return null; + } catch (Exception e) { + return null; + } + } + + /** + * Extracts the port from the URL. + * + * @param url URL string + * @return Port or -1 if invalid or not specified + */ + @ScalarFunction + public static int urlPort(String url) { + try { + return URI.create(url).getPort(); + } catch (Exception e) { + return -1; + } + } + + /** + * Extracts the path from the URL without the query string. + * + * @param url URL string + * @return Path or null if invalid + */ + @ScalarFunction + public static String urlPath(String url) { + try { + URI uri = new URI(url); + if (uri.getScheme() == null || uri.getHost() == null) { + return null; // Ensure the URL has a valid scheme and host + } + return uri.getPath(); + } catch (Exception e) { + return null; // Return null for any invalid input + } + } + + /** + * Function to extract the path from the URL with query string. + * + * @param url + * @return path + */ + @ScalarFunction + public static String urlPathWithQuery(String url) { + try { + URI uri = new URI(url); + if (uri.getScheme() == null || uri.getHost() == null) { + return null; // Ensure the URL has a valid scheme and host + } + return URI.create(url).getRawPath(); + } catch (Exception e) { + return null; // Return null for any invalid input + } + } + + /** + * Extracts the query string without the initial question mark (`?`) and excludes + * the fragment (`#`) and everything after it. + * + * @param url URL string + * @return Query string without `?` or null if invalid or not present + */ + @ScalarFunction + public static String urlQueryString(String url) { + try { + if (url == null) { + return null; + } + + URI uri = new URI(url); + String query = uri.getRawQuery(); // Extract the query string (raw, undecoded) + return query; // Return the query string directly (excluding `?`) + } catch (Exception e) { + return null; // Return null for invalid URLs + } + } + + /** + * Extracts the fragment identifier (without the hash symbol) from the URL. + * + * @param url URL string + * @return Fragment or null if invalid or not present + */ + @ScalarFunction + public static String urlFragment(String url) { + try { + return URI.create(url).getFragment(); + } catch (Exception e) { + return null; + } + } + + /** + * Extracts the query string and fragment identifier from the URL. + * + * Example: + * Input: "https://example.com/path?page=1#section" + * Output: "page=1#section" + * + * @param url URL string + * @return Query string and fragment identifier, or null if invalid or not present + */ + @ScalarFunction + public static String urlQueryStringAndFragment(String url) { + try { + if (url == null) { + return null; + } + + URI uri = new URI(url); + String query = uri.getQuery(); + String fragment = uri.getFragment(); + + if (query == null && fragment == null) { + return null; + } + + StringBuilder result = new StringBuilder(); + if (query != null) { + result.append(query); + } + if (fragment != null) { + if (result.length() > 0) { + result.append("#"); + } + result.append(fragment); + } + + return result.toString(); + } catch (Exception e) { + return null; + } + } + + /** + * Extracts the value of a specific query parameter from the URL. + * If multiple parameters with the same name exist, the first one is returned. + * + * Example: + * Input: ("https://example.com/path?page=1&lr=213", "page") + * Output: "1" + * + * @param url URL string + * @param name Name of the parameter to extract + * @return Value of the parameter, or an empty string if not found or invalid + */ + @ScalarFunction + public static String extractURLParameter(String url, String name) { + try { + if (url == null || name == null) { + return ""; + } + + URI uri = new URI(url); + String query = uri.getQuery(); + if (query == null) { + return ""; + } + + for (String param : query.split("&")) { + String[] keyValue = param.split("=", 2); + if (keyValue[0].equals(name)) { + return keyValue.length > 1 ? keyValue[1] : ""; + } + } + + return ""; + } catch (Exception e) { + return ""; + } + } + + /** + * Extracts all query parameters from the URL as an array of name=value pairs. + * + * Example: + * Input: "https://example.com/path?page=1&lr=213" + * Output: ["page=1", "lr=213"] + * + * @param url URL string + * @return Array of name=value pairs, or an empty array if no query parameters are present + */ + @ScalarFunction + public static String[] extractURLParameters(String url) { + try { + if (url == null) { + return new String[0]; + } + + URI uri = new URI(url); + String query = uri.getQuery(); + if (query == null) { + return new String[0]; + } + + return query.split("&"); + } catch (Exception e) { + return new String[0]; + } + } + + /** + * Extracts all parameter names from the URL query string. + * + * Example: + * Input: "https://example.com/path?page=1&lr=213" + * Output: ["page", "lr"] + * + * @param url URL string + * @return Array of parameter names, or an empty array if no query parameters are present + */ + @ScalarFunction + public static String[] extractURLParameterNames(String url) { + try { + if (url == null) { + return new String[0]; + } + + URI uri = new URI(url); + String query = uri.getQuery(); + if (query == null) { + return new String[0]; + } + + String[] params = query.split("&"); + String[] names = new String[params.length]; + for (int i = 0; i < params.length; i++) { + names[i] = params[i].split("=", 2)[0]; + } + return names; + } catch (Exception e) { + return new String[0]; + } + } + + /** + * Generates a hierarchy of URLs truncated at path and query separators. + * Each level of the path is included in the hierarchy. + * + * @param url URL string + * @return Array of truncated URLs representing the hierarchy, or an empty array if invalid + */ + @ScalarFunction + public static String[] urlHierarchy(String url) { + try { + if (url == null) { + return new String[0]; + } + + URI uri = new URI(url); + if (uri.getScheme() == null || uri.getHost() == null) { + return new String[0]; // Ensure the URL has a valid scheme and host + } + String baseUrl = uri.getScheme() + "://" + uri.getHost(); + String path = uri.getPath(); + + if (path == null || path.isEmpty()) { + return new String[]{baseUrl}; // Return only the base URL + } + + String[] pathParts = path.split("/"); + String[] hierarchy = new String[pathParts.length]; + hierarchy[0] = baseUrl; + StringBuilder currentPath = new StringBuilder(baseUrl); + + for (int i = 1; i < pathParts.length; i++) { + currentPath.append("/").append(pathParts[i]); + hierarchy[i] = currentPath.toString(); + } + + return hierarchy; + } catch (Exception e) { + return new String[0]; + } + } + + /** + * Generates a hierarchy of path elements from the URL. + * The protocol and host are excluded. The root ("/") is not included. + * + * Example: + * Input: "https://example.com/browse/CONV-6788" + * Output: ["/browse/", "/browse/CONV-6788"] + * + * @param url URL string + * @return Array of truncated path elements, or an empty array if invalid + */ + @ScalarFunction + public static String[] urlPathHierarchy(String url) { + try { + if (url == null) { + return new String[0]; + } + + URI uri = new URI(url); + String path = uri.getPath(); + if (path == null || path.isEmpty()) { + return new String[0]; + } + + String[] pathParts = path.split("/"); + String[] hierarchy = new String[pathParts.length - 1]; + for (int i = 1; i < pathParts.length; i++) { + StringBuilder part = new StringBuilder(); + for (int j = 1; j <= i; j++) { + part.append("/").append(pathParts[j]); + } + hierarchy[i - 1] = part.toString(); + } + + return hierarchy; + } catch (Exception e) { + return new String[0]; + } + } + + /** + * Encodes a string into a URL-safe format. + * + * @param url String to encode + * @return URL-encoded string or null if invalid + */ + @ScalarFunction + public static String encodeUrl(String url) { + try { + return URLEncoder.encode(url, StandardCharsets.UTF_8).replace("%20", "+"); + } catch (Exception e) { + return null; + } + } + + /** + * Decodes a URL-encoded string. + * + * @param url URL-encoded string + * @return Decoded string or null if invalid + */ + @ScalarFunction + public static String decodeUrl(String url) { + try { + return URLDecoder.decode(url, StandardCharsets.UTF_8); + } catch (Exception e) { + return null; + } + } + + /** + * Encodes the URL string following RFC-1866 standards. + * Spaces are encoded as `+`. + * + * @param url URL string to encode + * @return Encoded URL string + */ + @ScalarFunction + public static String encodeURLFormComponent(String url) { + try { + if (url == null) { + return null; + } + return URLEncoder.encode(url, StandardCharsets.UTF_8.name()); + } catch (Exception e) { + return null; + } + } + + /** + * Decodes the URL string following RFC-1866 standards. + * `+` is decoded as a space. + * + * @param url Encoded URL string + * @return Decoded URL string + */ + @ScalarFunction + public static String decodeURLFormComponent(String url) { + try { + if (url == null) { + return null; + } + return URLDecoder.decode(url.replace("+", "%20"), StandardCharsets.UTF_8.name()); + } catch (Exception e) { + return null; + } + } + + /** + * Extracts the network locality (username:password@host:port) from the URL. + * + * @param url URL string + * @return Network locality string, or null if invalid + */ + @ScalarFunction + public static String urlNetloc(String url) { + try { + if (url == null) { + return null; + } + + URI uri = new URI(url); + + // Extract user info (username:password) + String userInfo = uri.getUserInfo(); + + // Extract host + String host = uri.getHost(); + + // Extract port + int port = uri.getPort(); + String portStr = (port == -1) ? "" : ":" + port; + + // Combine into netloc format + StringBuilder netloc = new StringBuilder(); + if (userInfo != null && !userInfo.isEmpty()) { + netloc.append(userInfo).append("@"); + } + if (host != null) { + netloc.append(host); + } + netloc.append(portStr); + + return netloc.toString(); + } catch (Exception e) { + return null; + } + } + + /** + * Removes the leading www. from a URL’s domain. + */ + @ScalarFunction + public static String cutWWW(String url) { + try { + URI uri = new URI(url); + String host = uri.getHost(); + if (host != null && host.startsWith("www.")) { + host = host.substring(4); + } + return new URI(uri.getScheme(), uri.getUserInfo(), host, uri.getPort(), uri.getPath(), uri.getQuery(), + uri.getFragment()).toString(); + } catch (Exception e) { + return url; // Return unchanged if there's an error + } + } + + /** + * Removes the query string, including the question mark. + */ + @ScalarFunction + public static String cutQueryString(String url) { + try { + URI uri = new URI(url); + return new URI(uri.getScheme(), uri.getUserInfo(), uri.getHost(), uri.getPort(), uri.getPath(), null, + uri.getFragment()).toString(); + } catch (Exception e) { + return url; // Return unchanged if there's an error + } + } + + /** + * Removes the fragment identifier, including the number sign. + */ + @ScalarFunction + public static String cutFragment(String url) { + try { + URI uri = new URI(url); + return new URI(uri.getScheme(), uri.getUserInfo(), uri.getHost(), uri.getPort(), uri.getPath(), uri.getQuery(), + null).toString(); + } catch (Exception e) { + return url; // Return unchanged if there's an error + } + } + + /** + * Removes both the query string and fragment identifier. + */ + @ScalarFunction + public static String cutQueryStringAndFragment(String url) { + try { + URI uri = new URI(url); + return new URI(uri.getScheme(), uri.getUserInfo(), uri.getHost(), uri.getPort(), uri.getPath(), null, + null).toString(); + } catch (Exception e) { + return url; // Return unchanged if there's an error + } + } + + /** + * Removes specific query parameters from a URL. + */ + @ScalarFunction + public static String cutURLParameter(String url, String name) { + try { + URI uri = new URI(url); + String query = uri.getQuery(); + if (query == null || query.isEmpty()) { + return url; // No query string to process + } + + StringBuilder newQuery = new StringBuilder(); + for (String param : query.split("&")) { + String[] pair = param.split("=", 2); + String key = URLDecoder.decode(pair[0], "UTF-8"); + if (!key.equals(name)) { + if (newQuery.length() > 0) { + newQuery.append("&"); + } + newQuery.append(pair[0]); + if (pair.length > 1) { + newQuery.append("=").append(pair[1]); + } + } + } + + return new URI(uri.getScheme(), uri.getUserInfo(), uri.getHost(), uri.getPort(), uri.getPath(), + newQuery.length() > 0 ? newQuery.toString() : null, uri.getFragment()).toString(); + } catch (Exception e) { + return url; // Return unchanged if there's an error + } + } + + /** + * Removes specific query parameters from a URL. + * + * @param url The URL string from which the query parameters should be removed. + * @param names An array of query parameter names to remove. + * @return The URL string with the specified query parameters removed. + */ + @ScalarFunction + public static String cutURLParameters(String url, String[] names) { + for (String name : names) { + url = cutURLParameter(url, name); + } + return url; + } +} diff --git a/pinot-common/src/test/java/org/apache/pinot/common/function/scalar/UrlFunctionsTest.java b/pinot-common/src/test/java/org/apache/pinot/common/function/scalar/UrlFunctionsTest.java new file mode 100644 index 000000000000..7fddb3df749c --- /dev/null +++ b/pinot-common/src/test/java/org/apache/pinot/common/function/scalar/UrlFunctionsTest.java @@ -0,0 +1,530 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.pinot.common.function.scalar; + +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNull; +import static org.testng.internal.junit.ArrayAsserts.assertArrayEquals; + + +public class UrlFunctionsTest { + + @Test + public void testUrlProtocol() { + //test cases for http, https, ftp, mailto, tel, magnet. + assertEquals(UrlFunctions.urlProtocol("http://example.com"), "http"); + assertEquals(UrlFunctions.urlProtocol("https://example.com"), "https"); + assertEquals(UrlFunctions.urlProtocol("ftp://example.com"), "ftp"); + assertEquals(UrlFunctions.urlProtocol("mailto:name@email.com"), "mailto"); + assertEquals(UrlFunctions.urlProtocol("tel:1234567890"), "tel"); + assertEquals(UrlFunctions.urlProtocol("magnet:?xt=urn:btih:1234567890"), "magnet"); + //test case for invalid url + assertNull(UrlFunctions.urlProtocol("invalid_url")); + } + + @Test + public void testUrlDomain() { + assertEquals(UrlFunctions.urlDomain("https://example.com"), "example.com"); + assertEquals(UrlFunctions.urlDomain("http://example.com"), "example.com"); + assertEquals(UrlFunctions.urlDomain("https://sub.example.com"), "sub.example.com"); + assertEquals(UrlFunctions.urlDomain("https://example.co.uk"), "example.co.uk"); + assertNull(UrlFunctions.urlDomain("invalid_url")); + assertNull(UrlFunctions.urlDomain("http://")); + assertNull(UrlFunctions.urlDomain("https://")); + assertNull(UrlFunctions.urlDomain("")); + assertNull(UrlFunctions.urlDomain(null)); + } + + @Test + public void testUrlDomainWithoutWWW() { + assertEquals(UrlFunctions.urlDomainWithoutWWW("https://www.example.com"), "example.com"); + assertEquals(UrlFunctions.urlDomainWithoutWWW("https://example.com"), "example.com"); + assertEquals(UrlFunctions.urlDomainWithoutWWW("https://sub.example.com"), "sub.example.com"); + assertEquals(UrlFunctions.urlDomainWithoutWWW("https://www.sub.example.com"), "sub.example.com"); + assertEquals(UrlFunctions.urlDomainWithoutWWW("https://example.co.uk"), "example.co.uk"); + assertEquals(UrlFunctions.urlDomainWithoutWWW("https://www.example.co.uk"), "example.co.uk"); + assertNull(UrlFunctions.urlDomainWithoutWWW("invalid_url")); + assertNull(UrlFunctions.urlDomainWithoutWWW("http://")); + assertNull(UrlFunctions.urlDomainWithoutWWW("https://")); + assertNull(UrlFunctions.urlDomainWithoutWWW("")); + assertNull(UrlFunctions.urlDomainWithoutWWW(null)); + } + + @Test + public void testTopLevelUrlDomain() { + assertEquals(UrlFunctions.urlTopLevelDomain("https://example.com"), "com"); + assertEquals(UrlFunctions.urlTopLevelDomain("http://example.org"), "org"); + assertEquals(UrlFunctions.urlTopLevelDomain("https://example.co.uk"), "uk"); + assertEquals(UrlFunctions.urlTopLevelDomain("https://sub.example.com"), "com"); + assertEquals(UrlFunctions.urlTopLevelDomain("https://example.travel"), "travel"); + assertNull(UrlFunctions.urlTopLevelDomain("invalid_url")); + assertNull(UrlFunctions.urlTopLevelDomain("http://")); + assertNull(UrlFunctions.urlTopLevelDomain("https://")); + assertNull(UrlFunctions.urlTopLevelDomain("")); + assertNull(UrlFunctions.urlTopLevelDomain(null)); + } + + @Test + public void testUrlFirstSignificantSubdomain() { + assertEquals(UrlFunctions.urlFirstSignificantSubdomain("https://news.example.com"), "example"); + assertEquals(UrlFunctions.urlFirstSignificantSubdomain("https://example.com"), "example"); + assertEquals(UrlFunctions.urlFirstSignificantSubdomain("https://sub.example.co.uk"), "example"); + assertEquals(UrlFunctions.urlFirstSignificantSubdomain("https://www.sub.example.com"), "example"); + assertEquals(UrlFunctions.urlFirstSignificantSubdomain("https://example.travel"), "example"); + assertNull(UrlFunctions.urlFirstSignificantSubdomain("invalid_url")); + assertNull(UrlFunctions.urlFirstSignificantSubdomain("http://")); + assertNull(UrlFunctions.urlFirstSignificantSubdomain("https://")); + assertNull(UrlFunctions.urlFirstSignificantSubdomain("")); + assertNull(UrlFunctions.urlFirstSignificantSubdomain(null)); + } + + @Test + public void testCutToFirstSignificantSubdomain() { + assertEquals(UrlFunctions.cutToFirstSignificantSubdomain("https://news.example.com"), "example.com"); + assertEquals(UrlFunctions.cutToFirstSignificantSubdomain("https://news.example.com.cn"), "example.com.cn"); + assertEquals(UrlFunctions.cutToFirstSignificantSubdomain("https://example.com"), "example.com"); + assertEquals(UrlFunctions.cutToFirstSignificantSubdomain("https://sub.example.co.uk"), "example.co.uk"); + assertEquals(UrlFunctions.cutToFirstSignificantSubdomain("https://www.sub.example.com"), "example.com"); + assertEquals(UrlFunctions.cutToFirstSignificantSubdomain("https://example.travel"), "example.travel"); + assertNull(UrlFunctions.cutToFirstSignificantSubdomain("www.cn")); + assertNull(UrlFunctions.cutToFirstSignificantSubdomain("cn")); + assertNull(UrlFunctions.cutToFirstSignificantSubdomain("invalid_url")); + assertNull(UrlFunctions.cutToFirstSignificantSubdomain("http://")); + assertNull(UrlFunctions.cutToFirstSignificantSubdomain("https://")); + assertNull(UrlFunctions.cutToFirstSignificantSubdomain("")); + assertNull(UrlFunctions.cutToFirstSignificantSubdomain(null)); + } + + @Test + public void testCutToFirstSignificantSubdomainWithWWW() { + assertEquals(UrlFunctions.cutToFirstSignificantSubdomainWithWWW("https://news.example.com"), "example.com"); + assertEquals(UrlFunctions.cutToFirstSignificantSubdomainWithWWW("https://example.com"), "example.com"); + assertEquals(UrlFunctions.cutToFirstSignificantSubdomainWithWWW("https://www.example.com"), "www.example.com"); + assertEquals(UrlFunctions.cutToFirstSignificantSubdomainWithWWW("https://sub.example.co.uk"), "example.co.uk"); + assertEquals(UrlFunctions.cutToFirstSignificantSubdomainWithWWW("https://www.example.co.uk"), "www.example.co.uk"); + assertEquals(UrlFunctions.cutToFirstSignificantSubdomainWithWWW("https://www.sub.example.com"), + "example.com"); + assertEquals(UrlFunctions.cutToFirstSignificantSubdomainWithWWW("https://example.travel"), "example.travel"); + assertNull(UrlFunctions.cutToFirstSignificantSubdomainWithWWW("invalid_url")); + assertNull(UrlFunctions.cutToFirstSignificantSubdomainWithWWW("http://")); + assertNull(UrlFunctions.cutToFirstSignificantSubdomainWithWWW("https://")); + assertNull(UrlFunctions.cutToFirstSignificantSubdomainWithWWW("")); + assertNull(UrlFunctions.cutToFirstSignificantSubdomainWithWWW(null)); + } + + @Test + public void testUrlPort() { + assertEquals(UrlFunctions.urlPort("https://example.com:8080"), 8080); + assertEquals(UrlFunctions.urlPort("http://example.com:80"), 80); + assertEquals(UrlFunctions.urlPort("https://example.com"), -1); + assertEquals(UrlFunctions.urlPort("http://example.com"), -1); + assertEquals(UrlFunctions.urlPort("https://example.com:invalid"), -1); + assertEquals(UrlFunctions.urlPort("invalid_url"), -1); + assertEquals(UrlFunctions.urlPort("http://"), -1); + assertEquals(UrlFunctions.urlPort("https://"), -1); + assertEquals(UrlFunctions.urlPort(""), -1); + assertEquals(UrlFunctions.urlPort(null), -1); + } + + @Test + public void testUrlPath() { + assertEquals(UrlFunctions.urlPath("https://example.com/path"), "/urlPath"); + assertEquals(UrlFunctions.urlPath("https://example.com/path/to/resource"), "/urlPath/to/resource"); + assertEquals(UrlFunctions.urlPath("https://example.com/"), "/"); + assertEquals(UrlFunctions.urlPath("https://example.com"), ""); + assertEquals(UrlFunctions.urlPath("https://example.com/path/to/resource?query=param"), "/urlPath/to/resource"); + assertEquals(UrlFunctions.urlPath("https://example.com/path/to/resource#fragment"), "/urlPath/to/resource"); + assertNull(UrlFunctions.urlPath("invalid_url")); + assertNull(UrlFunctions.urlPath("http://")); + assertNull(UrlFunctions.urlPath("https://")); + assertNull(UrlFunctions.urlPath("")); + assertNull(UrlFunctions.urlPath(null)); + } + + @Test + public void testUrlPathWithQuery() { + assertEquals(UrlFunctions.urlPathWithQuery("https://example.com/path?query=value"), "/urlPath"); + assertEquals(UrlFunctions.urlPathWithQuery("https://example.com/?query=value"), "/"); + assertEquals(UrlFunctions.urlPathWithQuery("https://example.com/path/to/resource?query=param"), + "/urlPath/to/resource"); + assertEquals(UrlFunctions.urlPathWithQuery("https://example.com/path/to/resource#fragment"), + "/urlPath/to/resource"); + assertNull(UrlFunctions.urlPathWithQuery("invalid_url")); + assertNull(UrlFunctions.urlPathWithQuery("http://")); + assertNull(UrlFunctions.urlPathWithQuery("https://")); + assertNull(UrlFunctions.urlPathWithQuery("")); + assertNull(UrlFunctions.urlPathWithQuery(null)); + } + + @Test + public void testUrlQueryString() { + assertEquals(UrlFunctions.urlQueryString("https://example.com/path?query=value"), "query=value"); + assertEquals(UrlFunctions.urlQueryString("https://example.com/path?param1=value1¶m2=value2"), + "param1=value1¶m2=value2"); + assertEquals(UrlFunctions.urlQueryString("https://example.com/path?param=value#fragment"), "param=value"); + assertNull(UrlFunctions.urlQueryString("https://example.com/path")); + assertNull(UrlFunctions.urlQueryString("invalid_url")); + assertNull(UrlFunctions.urlQueryString("http://")); + assertNull(UrlFunctions.urlQueryString("https://")); + assertNull(UrlFunctions.urlQueryString("")); + assertNull(UrlFunctions.urlQueryString(null)); + } + + @Test + public void testUrlFragment() { + assertEquals(UrlFunctions.urlFragment("https://example.com/path#fragment"), "urlFragment"); + assertEquals(UrlFunctions.urlFragment("https://example.com/path/to/resource#section"), "section"); + assertEquals(UrlFunctions.urlFragment("https://example.com/#top"), "top"); + assertNull(UrlFunctions.urlFragment("https://example.com/path")); + assertNull(UrlFunctions.urlFragment("invalid_url")); + assertNull(UrlFunctions.urlFragment("http://")); + assertNull(UrlFunctions.urlFragment("https://")); + assertNull(UrlFunctions.urlFragment("")); + assertNull(UrlFunctions.urlFragment(null)); + } + + @Test + public void testUrlQueryStringAndFragment() { + assertEquals(UrlFunctions.urlQueryStringAndFragment("https://example.com/path?query=value#fragment"), + "query=value#urlFragment"); + assertEquals(UrlFunctions.urlQueryStringAndFragment("https://example.com/path?param1=value1¶m2=value2#section"), + "param1=value1¶m2=value2#section"); + assertEquals(UrlFunctions.urlQueryStringAndFragment("https://example.com/path?param=value"), "param=value"); + assertEquals(UrlFunctions.urlQueryStringAndFragment("https://example.com/path#fragment"), "urlFragment"); + assertNull(UrlFunctions.urlQueryStringAndFragment("https://example.com/path")); + assertNull(UrlFunctions.urlQueryStringAndFragment("invalid_url")); + assertNull(UrlFunctions.urlQueryStringAndFragment("http://")); + assertNull(UrlFunctions.urlQueryStringAndFragment("https://")); + assertNull(UrlFunctions.urlQueryStringAndFragment("")); + assertNull(UrlFunctions.urlQueryStringAndFragment(null)); + } + + @Test + public void testExtractURLParameter() { + assertEquals(UrlFunctions.extractURLParameter("https://example.com/path?param=value", "param"), "value"); + assertEquals(UrlFunctions.extractURLParameter("https://example.com/path?param1=value1¶m2=value2", "param2"), + "value2"); + assertEquals(UrlFunctions.extractURLParameter("https://example.com/path?param=value#fragment", "param"), "value"); + assertEquals(UrlFunctions.extractURLParameter("https://example.com/path?param=value¶m=", "param"), "value"); + assertEquals(UrlFunctions.extractURLParameter("https://example.com/path?param=value¶m2=", "param2"), ""); + assertEquals(UrlFunctions.extractURLParameter("https://example.com/path?param=value¶m2", "param2"), ""); + assertEquals(UrlFunctions.extractURLParameter("https://example.com/path?param=value¶m2", "param3"), ""); + assertEquals(UrlFunctions.extractURLParameter("https://example.com/path", "param"), ""); + assertEquals(UrlFunctions.extractURLParameter("https://example.com/path?", "param"), ""); + assertEquals(UrlFunctions.extractURLParameter("https://example.com/path?param", "param"), ""); + assertEquals(UrlFunctions.extractURLParameter("https://example.com/path?param=", "param"), ""); + assertEquals(UrlFunctions.extractURLParameter("https://example.com/path?param=value¶m2=value2", "param2"), + "value2"); + assertEquals(UrlFunctions.extractURLParameter("https://example.com/path?param=value¶m2=value2", "param"), + "value"); + assertEquals(UrlFunctions.extractURLParameter("https://example.com/path?param=value¶m2=value2", "param3"), ""); + assertEquals(UrlFunctions.extractURLParameter("https://example.com/path?param=value¶m2=value2", ""), ""); + assertEquals(UrlFunctions.extractURLParameter("https://example.com/path?param=value¶m2=value2", null), ""); + } + + @Test + public void testExtractURLParameters() { + String[] expected = {"param1=value1", "param2=value2"}; + assertArrayEquals(expected, + UrlFunctions.extractURLParameters("https://example.com/path?param1=value1¶m2=value2")); + assertArrayEquals(new String[0], UrlFunctions.extractURLParameters("https://example.com/path")); + assertArrayEquals(new String[0], UrlFunctions.extractURLParameters("invalid_url")); + assertArrayEquals(new String[0], UrlFunctions.extractURLParameters("http://")); + assertArrayEquals(new String[0], UrlFunctions.extractURLParameters("https://")); + assertArrayEquals(new String[0], UrlFunctions.extractURLParameters("")); + assertArrayEquals(new String[0], UrlFunctions.extractURLParameters(null)); + } + + @Test + public void testExtractURLParameterNames() { + String[] expected = {"param1", "param2"}; + assertArrayEquals(expected, + UrlFunctions.extractURLParameterNames("https://example.com/path?param1=value1¶m2=value2")); + assertArrayEquals(new String[0], UrlFunctions.extractURLParameterNames("https://example.com/path")); + } + + @Test + public void testUrlHierarchy() { + assertArrayEquals(new String[]{"https://example.com", "https://example.com/path", "https://example.com/path/to"}, + UrlFunctions.urlHierarchy("https://example.com/path/to")); + + assertArrayEquals(new String[]{"https://example.com"}, UrlFunctions.urlHierarchy("https://example.com")); + + assertArrayEquals(new String[]{"https://example.com", "https://example.com/path"}, + UrlFunctions.urlHierarchy("https://example.com/path")); + + assertArrayEquals(new String[0], UrlFunctions.urlHierarchy("invalid_url")); + assertArrayEquals(new String[0], UrlFunctions.urlHierarchy("http://")); + assertArrayEquals(new String[0], UrlFunctions.urlHierarchy("https://")); + assertArrayEquals(new String[0], UrlFunctions.urlHierarchy("")); + assertArrayEquals(new String[0], UrlFunctions.urlHierarchy(null)); + } + + @Test + public void testUrlPathHierarchy() { + assertArrayEquals(new String[]{"/urlPath", "/urlPath/to"}, + UrlFunctions.urlPathHierarchy("https://example.com/path/to")); + assertArrayEquals(new String[0], UrlFunctions.urlPathHierarchy("https://example.com/")); + assertArrayEquals(new String[0], UrlFunctions.urlPathHierarchy("https://example.com")); + assertArrayEquals(new String[0], UrlFunctions.urlPathHierarchy("invalid_url")); + assertArrayEquals(new String[0], UrlFunctions.urlPathHierarchy(null)); + assertArrayEquals(new String[]{"/urlPath", "/urlPath/to", "/urlPath/to/resource"}, + UrlFunctions.urlPathHierarchy("https://example.com/path/to/resource")); + } + + @Test + public void testUrlEncode() { + assertEquals(UrlFunctions.encodeUrl("https://example.com/path to resource"), + "https%3A%2F%2Fexample.com%2Fpath+to+resource"); + assertEquals(UrlFunctions.encodeUrl("https://example.com/path/to/resource"), + "https%3A%2F%2Fexample.com%2Fpath%2Fto%2Fresource"); + assertEquals(UrlFunctions.encodeUrl("https://example.com/path?query=value"), + "https%3A%2F%2Fexample.com%2Fpath%3Fquery%3Dvalue"); + assertEquals(UrlFunctions.encodeUrl("https://example.com/path#fragment"), + "https%3A%2F%2Fexample.com%2Fpath%23fragment"); + assertEquals(UrlFunctions.encodeUrl("https://example.com/path?query=value#fragment"), + "https%3A%2F%2Fexample.com%2Fpath%3Fquery%3Dvalue%23fragment"); + + assertEquals(UrlFunctions.encodeUrl("invalid_url"), "invalid_url"); + assertEquals(UrlFunctions.encodeUrl(""), ""); + assertNull(UrlFunctions.encodeUrl(null)); + } + + @Test + public void testUrlDecode() { + assertEquals(UrlFunctions.decodeUrl("https%3A%2F%2Fexample.com%2Fpath%20to%20resource"), + "https://example.com/path to resource"); + assertEquals(UrlFunctions.decodeUrl("https%3A%2F%2Fexample.com%2Fpath%2Fto%2Fresource"), + "https://example.com/path/to/resource"); + assertEquals(UrlFunctions.decodeUrl("https%3A%2F%2Fexample.com%2Fpath%3Fquery%3Dvalue"), + "https://example.com/path?query=value"); + assertEquals(UrlFunctions.decodeUrl("https%3A%2F%2Fexample.com%2Fpath%23fragment"), + "https://example.com/path#fragment"); + assertEquals(UrlFunctions.decodeUrl("https%3A%2F%2Fexample.com%2Fpath%3Fquery%3Dvalue%23fragment"), + "https://example.com/path?query=value#fragment"); + assertEquals(UrlFunctions.decodeUrl("random_path"), "random_path"); + assertEquals(UrlFunctions.decodeUrl(""), ""); + assertNull(UrlFunctions.decodeUrl(null)); + } + + @Test + public void testEncodeURLFormComponent() { + assertEquals(UrlFunctions.encodeURLFormComponent("https://example.com/path to resource"), + "https%3A%2F%2Fexample.com%2Fpath+to+resource"); + assertEquals(UrlFunctions.encodeURLFormComponent("https://example.com/path/to/resource"), + "https%3A%2F%2Fexample.com%2Fpath%2Fto%2Fresource"); + assertEquals(UrlFunctions.encodeURLFormComponent("https://example.com/path?query=value"), + "https%3A%2F%2Fexample.com%2Fpath%3Fquery%3Dvalue"); + assertEquals(UrlFunctions.encodeURLFormComponent("https://example.com/path#fragment"), + "https%3A%2F%2Fexample.com%2Fpath%23fragment"); + assertEquals(UrlFunctions.encodeURLFormComponent("https://example.com/path?query=value#fragment"), + "https%3A%2F%2Fexample.com%2Fpath%3Fquery%3Dvalue%23fragment"); + assertNull(UrlFunctions.encodeURLFormComponent(null)); + assertEquals(UrlFunctions.encodeURLFormComponent(""), ""); + } + + @Test + public void testDecodeURLFormComponent() { + assertEquals(UrlFunctions.decodeURLFormComponent("https%3A%2F%2Fexample.com%2Fpath+to+resource"), + "https://example.com/path to resource"); + assertEquals(UrlFunctions.decodeURLFormComponent("https%3A%2F%2Fexample.com%2Fpath%2Fto%2Fresource"), + "https://example.com/path/to/resource"); + assertEquals(UrlFunctions.decodeURLFormComponent("https%3A%2F%2Fexample.com%2Fpath%3Fquery%3Dvalue"), + "https://example.com/path?query=value"); + assertEquals(UrlFunctions.decodeURLFormComponent("https%3A%2F%2Fexample.com%2Fpath%23fragment"), + "https://example.com/path#fragment"); + assertEquals(UrlFunctions.decodeURLFormComponent("https%3A%2F%2Fexample.com%2Fpath%3Fquery%3Dvalue%23fragment"), + "https://example.com/path?query=value#fragment"); + assertNull(UrlFunctions.decodeURLFormComponent(null)); + assertEquals(UrlFunctions.decodeURLFormComponent(""), ""); + } + + @Test + public void testUrlNetloc() { + assertEquals(UrlFunctions.urlNetloc("https://user@example.com:8080/path"), "user@example.com:8080"); + assertEquals(UrlFunctions.urlNetloc("https://user:pass@example.com:8080/path"), "user:pass@example.com:8080"); + assertEquals(UrlFunctions.urlNetloc("https://example.com:8080/path"), "example.com:8080"); + assertEquals(UrlFunctions.urlNetloc("https://example.com/path"), "example.com"); + assertEquals(UrlFunctions.urlNetloc("https://user@example.com/path"), "user@example.com"); + assertEquals(UrlFunctions.urlNetloc("https://example.com"), "example.com"); + assertEquals(UrlFunctions.urlNetloc("random"), ""); + assertEquals(UrlFunctions.urlNetloc(""), ""); + assertNull(UrlFunctions.urlNetloc("http://")); + assertNull(UrlFunctions.urlNetloc("https://")); + assertNull(UrlFunctions.urlNetloc(null)); + } + + @Test + public void testCutWWW() { + assertEquals(UrlFunctions.cutWWW("https://www.example.com"), "https://example.com"); + assertEquals(UrlFunctions.cutWWW("http://www.example.com"), "http://example.com"); + assertEquals(UrlFunctions.cutWWW("https://www.sub.example.com"), "https://sub.example.com"); + assertEquals(UrlFunctions.cutWWW("http://www.sub.example.com"), "http://sub.example.com"); + + assertEquals(UrlFunctions.cutWWW("https://example.com"), "https://example.com"); + assertEquals(UrlFunctions.cutWWW("http://example.com"), "http://example.com"); + assertEquals(UrlFunctions.cutWWW("https://sub.example.com"), "https://sub.example.com"); + assertEquals(UrlFunctions.cutWWW("http://sub.example.com"), "http://sub.example.com"); + + assertEquals(UrlFunctions.cutWWW("invalid_url"), "invalid_url"); + assertEquals(UrlFunctions.cutWWW("http://"), "http://"); + assertEquals(UrlFunctions.cutWWW("https://"), "https://"); + assertEquals(UrlFunctions.cutWWW(""), ""); + assertNull(UrlFunctions.cutWWW(null)); + } + + @Test + public void testCutQueryString() { + assertEquals(UrlFunctions.cutQueryString("https://example.com/path?query=value"), "https://example.com/path"); + assertEquals(UrlFunctions.cutQueryString("https://example.com/path?param1=value1¶m2=value2"), + "https://example.com/path"); + assertEquals(UrlFunctions.cutQueryString("https://example.com/path?param=value#fragment"), + "https://example.com/path#fragment"); + + assertEquals(UrlFunctions.cutQueryString("https://example.com/path"), "https://example.com/path"); + assertEquals(UrlFunctions.cutQueryString("https://example.com/path#fragment"), "https://example.com/path#fragment"); + assertEquals(UrlFunctions.cutQueryString("https://example.com"), "https://example.com"); + + assertEquals(UrlFunctions.cutQueryString("invalid_url"), "invalid_url"); + assertEquals(UrlFunctions.cutQueryString("http://"), "http://"); + assertEquals(UrlFunctions.cutQueryString("https://"), "https://"); + assertEquals(UrlFunctions.cutQueryString(""), ""); + assertNull(UrlFunctions.cutQueryString(null)); + } + + @Test + public void testCutFragment() { + assertEquals(UrlFunctions.cutFragment("https://example.com/path#fragment"), "https://example.com/path"); + assertEquals(UrlFunctions.cutFragment("https://example.com/path/to/resource#section"), + "https://example.com/path/to/resource"); + assertEquals(UrlFunctions.cutFragment("https://example.com/#top"), "https://example.com/"); + assertEquals(UrlFunctions.cutFragment("https://example.com/path"), "https://example.com/path"); + assertEquals(UrlFunctions.cutFragment("https://example.com"), "https://example.com"); + assertEquals(UrlFunctions.cutFragment("invalid_url"), "invalid_url"); + assertEquals(UrlFunctions.cutFragment("http://"), "http://"); + assertEquals(UrlFunctions.cutFragment("https://"), "https://"); + assertEquals(UrlFunctions.cutFragment(""), ""); + assertNull(UrlFunctions.cutFragment(null)); + } + + @Test + public void testCutQueryStringAndFragment() { + assertEquals(UrlFunctions.cutQueryStringAndFragment("https://example.com/path?query=value#fragment"), + "https://example.com/path"); + assertEquals(UrlFunctions.cutQueryStringAndFragment("https://example.com/path?param1=value1¶m2=value2#section"), + "https://example.com/path"); + assertEquals(UrlFunctions.cutQueryStringAndFragment("https://example.com/path?param=value"), + "https://example.com/path"); + assertEquals(UrlFunctions.cutQueryStringAndFragment("https://example.com/path#fragment"), + "https://example.com/path"); + + assertEquals(UrlFunctions.cutQueryStringAndFragment("https://example.com/path"), "https://example.com/path"); + assertEquals(UrlFunctions.cutQueryStringAndFragment("https://example.com"), "https://example.com"); + + assertEquals(UrlFunctions.cutQueryStringAndFragment("invalid_url"), "invalid_url"); + assertEquals(UrlFunctions.cutQueryStringAndFragment("http://"), "http://"); + assertEquals(UrlFunctions.cutQueryStringAndFragment("https://"), "https://"); + assertEquals(UrlFunctions.cutQueryStringAndFragment(""), ""); + assertNull(UrlFunctions.cutQueryStringAndFragment(null)); + } + + @Test + public void testCutURLParameter() { + assertEquals( + UrlFunctions.cutURLParameter("https://example.com/path?param1=value1¶m2=value2", "param1"), + "https://example.com/path?param2=value2"); + assertEquals( + UrlFunctions.cutURLParameter("https://example.com/path?param1=value1¶m2=value2", "param2"), + "https://example.com/path?param1=value1"); + assertEquals(UrlFunctions.cutURLParameter("https://example.com/path?param=value", "param"), + "https://example.com/path"); + assertEquals(UrlFunctions.cutURLParameter("https://example.com/path?param1=value1¶m2=value2¶m3=value3", + "param2"), "https://example.com/path?param1=value1¶m3=value3"); + assertEquals( + UrlFunctions.cutURLParameter("https://example.com/path?param1=value1¶m2=value2¶m3=value3#fragment", + "param3"), "https://example.com/path?param1=value1¶m2=value2#fragment"); + assertEquals(UrlFunctions.cutURLParameter( + "https://example.com/path?param1=value1¶m2=value2¶m4¶m3=value3#fragment", "param4"), + "https://example.com/path?param1=value1¶m2=value2¶m3=value3#fragment"); + + assertEquals(UrlFunctions.cutURLParameter("https://example.com/path", "param"), + "https://example.com/path"); + assertEquals(UrlFunctions.cutURLParameter("https://example.com", "param"), "https://example.com"); + + assertEquals(UrlFunctions.cutURLParameter("invalid_url", "param"), "invalid_url"); + assertEquals(UrlFunctions.cutURLParameter("http://", "param"), "http://"); + assertEquals(UrlFunctions.cutURLParameter("https://", "param"), "https://"); + assertEquals(UrlFunctions.cutURLParameter("", "param"), ""); + assertNull(UrlFunctions.cutURLParameter(null, "param")); + + assertEquals(UrlFunctions.cutURLParameter("https://example.com/path?param=value", ""), + "https://example.com/path?param=value"); + assertEquals(UrlFunctions.cutURLParameter("https://example.com/path", ""), + "https://example.com/path"); + + assertEquals(UrlFunctions.cutURLParameter("https://example.com/path?param=value", null), + "https://example.com/path?param=value"); + assertEquals(UrlFunctions.cutURLParameter("https://example.com/path", null), "https://example.com/path"); + } + + @Test + public void testCutURLParameters() { + assertEquals( + UrlFunctions.cutURLParameters("https://example.com/path?param1=value1¶m2=value2", new String[]{"param1"}), + "https://example.com/path?param2=value2"); + assertEquals( + UrlFunctions.cutURLParameters("https://example.com/path?param1=value1¶m2=value2", new String[]{"param2"}), + "https://example.com/path?param1=value1"); + assertEquals(UrlFunctions.cutURLParameters("https://example.com/path?param=value", new String[]{"param"}), + "https://example.com/path"); + assertEquals(UrlFunctions.cutURLParameters("https://example.com/path?param1=value1¶m2=value2¶m3=value3", + new String[]{"param2", "param3"}), + "https://example.com/path?param1=value1"); + assertEquals( + UrlFunctions.cutURLParameters("https://example.com/path?param1=value1¶m2=value2¶m3=value3#fragment", + new String[]{"param2", "param3"}), + "https://example.com/path?param1=value1#fragment"); + assertEquals( + UrlFunctions.cutURLParameters( + "https://example.com/path?param1=value1¶m2=value2¶m4¶m3=value3#fragment", + new String[]{"param2", "param3"}), + "https://example.com/path?param1=value1¶m4#fragment"); + + assertEquals(UrlFunctions.cutURLParameters("https://example.com/path", new String[]{"param"}), + "https://example.com/path"); + assertEquals(UrlFunctions.cutURLParameters("https://example.com", new String[]{"param"}), "https://example.com"); + + assertEquals(UrlFunctions.cutURLParameters("invalid_url", new String[]{"param"}), "invalid_url"); + assertEquals(UrlFunctions.cutURLParameters("http://", new String[]{"param"}), "http://"); + assertEquals(UrlFunctions.cutURLParameters("https://", new String[]{"param"}), "https://"); + assertEquals(UrlFunctions.cutURLParameters("", new String[]{"param"}), ""); + assertNull(UrlFunctions.cutURLParameters(null, new String[]{"param"})); + + assertEquals(UrlFunctions.cutURLParameters("https://example.com/path?param=value", new String[]{""}), + "https://example.com/path?param=value"); + assertEquals(UrlFunctions.cutURLParameters("https://example.com/path", new String[]{""}), + "https://example.com/path"); + + assertEquals(UrlFunctions.cutURLParameters("https://example.com/path?param=value", new String[0]), + "https://example.com/path?param=value"); + assertEquals(UrlFunctions.cutURLParameters("https://example.com/path", new String[0]), "https://example.com/path"); + } +}