This is an automated email from the ASF dual-hosted git repository.

xiangfu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pinot.git


The following commit(s) were added to refs/heads/master by this push:
     new d7505f6986 Add URL scalar functions (#14646)
d7505f6986 is described below

commit d7505f6986f6b1fe6dd8e36e556c7d0d577645f8
Author: Xiang Fu <[email protected]>
AuthorDate: Thu Dec 12 18:47:43 2024 -0800

    Add URL scalar functions (#14646)
---
 .../pinot/common/function/scalar/UrlFunctions.java | 743 +++++++++++++++++++++
 .../common/function/scalar/UrlFunctionsTest.java   | 530 +++++++++++++++
 2 files changed, 1273 insertions(+)

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 0000000000..86051b9675
--- /dev/null
+++ 
b/pinot-common/src/main/java/org/apache/pinot/common/function/scalar/UrlFunctions.java
@@ -0,0 +1,743 @@
+/**
+ * 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 javax.annotation.Nullable;
+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
+   */
+  @Nullable
+  @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
+   */
+  @Nullable
+  @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
+   */
+  @Nullable
+  @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
+   */
+  @Nullable
+  @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
+   */
+  @Nullable
+  @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
+   */
+  @Nullable
+  @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
+   */
+  @Nullable
+  @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
+   */
+  @Nullable
+  @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 URL string
+   * @return path with query string or null if invalid
+   */
+  @Nullable
+  @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
+   */
+  @Nullable
+  @ScalarFunction
+  public static String urlQueryString(String url) {
+    try {
+      if (url == null) {
+        return null;
+      }
+
+      URI uri = new URI(url);
+      // Extract the query string (raw, un-decoded)
+      return uri.getRawQuery(); // 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
+   */
+  @Nullable
+  @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: "<a 
href="https://example.com/path?page=1#section";>https://example.com/path?page=1#section</a>"
+   * Output: "page=1#section"
+   *
+   * @param url URL string
+   * @return Query string and fragment identifier, or null if invalid or not 
present
+   */
+  @Nullable
+  @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: ("<a 
href="https://example.com/path?page=1&lr=213";>https://example.com/path?page=1&lr=213</a>",
 "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: "<a 
href="https://example.com/path?page=1&lr=213";>https://example.com/path?page=1&lr=213</a>"
+   * 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: "<a 
href="https://example.com/path?page=1&lr=213";>https://example.com/path?page=1&lr=213</a>"
+   * 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: "<a 
href="https://example.com/browse/CONV-6788";>https://example.com/browse/CONV-6788</a>"
+   * 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
+   */
+  @Nullable
+  @ScalarFunction
+  public static String urlEncode(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
+   */
+  @Nullable
+  @ScalarFunction
+  public static String urlDecode(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
+   */
+  @Nullable
+  @ScalarFunction
+  public static String urlEncodeFormComponent(String url) {
+    try {
+      if (url == null) {
+        return null;
+      }
+      return URLEncoder.encode(url, StandardCharsets.UTF_8);
+    } 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
+   */
+  @Nullable
+  @ScalarFunction
+  public static String urlDecodeFormComponent(String url) {
+    try {
+      if (url == null) {
+        return null;
+      }
+      return URLDecoder.decode(url.replace("+", "%20"), 
StandardCharsets.UTF_8);
+    } 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
+   */
+  @Nullable
+  @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], StandardCharsets.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 0000000000..ea2d00b968
--- /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:[email protected]";), "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";), "/path");
+    assertEquals(UrlFunctions.urlPath("https://example.com/path/to/resource";), 
"/path/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";),
 "/path/to/resource");
+    
assertEquals(UrlFunctions.urlPath("https://example.com/path/to/resource#fragment";),
 "/path/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";),
 "/path");
+    
assertEquals(UrlFunctions.urlPathWithQuery("https://example.com/?query=value";), 
"/");
+    
assertEquals(UrlFunctions.urlPathWithQuery("https://example.com/path/to/resource?query=param";),
+        "/path/to/resource");
+    
assertEquals(UrlFunctions.urlPathWithQuery("https://example.com/path/to/resource#fragment";),
+        "/path/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&param2=value2";),
+        "param1=value1&param2=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";), 
"fragment");
+    
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#fragment");
+    
assertEquals(UrlFunctions.urlQueryStringAndFragment("https://example.com/path?param1=value1&param2=value2#section";),
+        "param1=value1&param2=value2#section");
+    
assertEquals(UrlFunctions.urlQueryStringAndFragment("https://example.com/path?param=value";),
 "param=value");
+    
assertEquals(UrlFunctions.urlQueryStringAndFragment("https://example.com/path#fragment";),
 "fragment");
+    
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&param2=value2";,
 "param2"),
+        "value2");
+    
assertEquals(UrlFunctions.extractURLParameter("https://example.com/path?param=value#fragment";,
 "param"), "value");
+    
assertEquals(UrlFunctions.extractURLParameter("https://example.com/path?param=value&param=";,
 "param"), "value");
+    
assertEquals(UrlFunctions.extractURLParameter("https://example.com/path?param=value&param2=";,
 "param2"), "");
+    
assertEquals(UrlFunctions.extractURLParameter("https://example.com/path?param=value&param2";,
 "param2"), "");
+    
assertEquals(UrlFunctions.extractURLParameter("https://example.com/path?param=value&param2";,
 "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&param2=value2";,
 "param2"),
+        "value2");
+    
assertEquals(UrlFunctions.extractURLParameter("https://example.com/path?param=value&param2=value2";,
 "param"),
+        "value");
+    
assertEquals(UrlFunctions.extractURLParameter("https://example.com/path?param=value&param2=value2";,
 "param3"), "");
+    
assertEquals(UrlFunctions.extractURLParameter("https://example.com/path?param=value&param2=value2";,
 ""), "");
+    
assertEquals(UrlFunctions.extractURLParameter("https://example.com/path?param=value&param2=value2";,
 null), "");
+  }
+
+  @Test
+  public void testExtractURLParameters() {
+    String[] expected = {"param1=value1", "param2=value2"};
+    assertArrayEquals(expected,
+        
UrlFunctions.extractURLParameters("https://example.com/path?param1=value1&param2=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&param2=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[]{"/path", "/path/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[]{"/path", "/path/to", "/path/to/resource"},
+        UrlFunctions.urlPathHierarchy("https://example.com/path/to/resource";));
+  }
+
+  @Test
+  public void testUrlEncode() {
+    assertEquals(UrlFunctions.urlEncode("https://example.com/path to 
resource"),
+        "https%3A%2F%2Fexample.com%2Fpath+to+resource");
+    
assertEquals(UrlFunctions.urlEncode("https://example.com/path/to/resource";),
+        "https%3A%2F%2Fexample.com%2Fpath%2Fto%2Fresource");
+    
assertEquals(UrlFunctions.urlEncode("https://example.com/path?query=value";),
+        "https%3A%2F%2Fexample.com%2Fpath%3Fquery%3Dvalue");
+    assertEquals(UrlFunctions.urlEncode("https://example.com/path#fragment";),
+        "https%3A%2F%2Fexample.com%2Fpath%23fragment");
+    
assertEquals(UrlFunctions.urlEncode("https://example.com/path?query=value#fragment";),
+        "https%3A%2F%2Fexample.com%2Fpath%3Fquery%3Dvalue%23fragment");
+
+    assertEquals(UrlFunctions.urlEncode("invalid_url"), "invalid_url");
+    assertEquals(UrlFunctions.urlEncode(""), "");
+    assertNull(UrlFunctions.urlEncode(null));
+  }
+
+  @Test
+  public void testUrlDecode() {
+    
assertEquals(UrlFunctions.urlDecode("https%3A%2F%2Fexample.com%2Fpath%20to%20resource"),
+        "https://example.com/path to resource");
+    
assertEquals(UrlFunctions.urlDecode("https%3A%2F%2Fexample.com%2Fpath%2Fto%2Fresource"),
+        "https://example.com/path/to/resource";);
+    
assertEquals(UrlFunctions.urlDecode("https%3A%2F%2Fexample.com%2Fpath%3Fquery%3Dvalue"),
+        "https://example.com/path?query=value";);
+    
assertEquals(UrlFunctions.urlDecode("https%3A%2F%2Fexample.com%2Fpath%23fragment"),
+        "https://example.com/path#fragment";);
+    
assertEquals(UrlFunctions.urlDecode("https%3A%2F%2Fexample.com%2Fpath%3Fquery%3Dvalue%23fragment"),
+        "https://example.com/path?query=value#fragment";);
+    assertEquals(UrlFunctions.urlDecode("random_path"), "random_path");
+    assertEquals(UrlFunctions.urlDecode(""), "");
+    assertNull(UrlFunctions.urlDecode(null));
+  }
+
+  @Test
+  public void testUrlEncodeFormComponent() {
+    assertEquals(UrlFunctions.urlEncodeFormComponent("https://example.com/path 
to resource"),
+        "https%3A%2F%2Fexample.com%2Fpath+to+resource");
+    
assertEquals(UrlFunctions.urlEncodeFormComponent("https://example.com/path/to/resource";),
+        "https%3A%2F%2Fexample.com%2Fpath%2Fto%2Fresource");
+    
assertEquals(UrlFunctions.urlEncodeFormComponent("https://example.com/path?query=value";),
+        "https%3A%2F%2Fexample.com%2Fpath%3Fquery%3Dvalue");
+    
assertEquals(UrlFunctions.urlEncodeFormComponent("https://example.com/path#fragment";),
+        "https%3A%2F%2Fexample.com%2Fpath%23fragment");
+    
assertEquals(UrlFunctions.urlEncodeFormComponent("https://example.com/path?query=value#fragment";),
+        "https%3A%2F%2Fexample.com%2Fpath%3Fquery%3Dvalue%23fragment");
+    assertNull(UrlFunctions.urlEncodeFormComponent(null));
+    assertEquals(UrlFunctions.urlEncodeFormComponent(""), "");
+  }
+
+  @Test
+  public void testUrlDecodeFormComponent() {
+    
assertEquals(UrlFunctions.urlDecodeFormComponent("https%3A%2F%2Fexample.com%2Fpath+to+resource"),
+        "https://example.com/path to resource");
+    
assertEquals(UrlFunctions.urlDecodeFormComponent("https%3A%2F%2Fexample.com%2Fpath%2Fto%2Fresource"),
+        "https://example.com/path/to/resource";);
+    
assertEquals(UrlFunctions.urlDecodeFormComponent("https%3A%2F%2Fexample.com%2Fpath%3Fquery%3Dvalue"),
+        "https://example.com/path?query=value";);
+    
assertEquals(UrlFunctions.urlDecodeFormComponent("https%3A%2F%2Fexample.com%2Fpath%23fragment"),
+        "https://example.com/path#fragment";);
+    
assertEquals(UrlFunctions.urlDecodeFormComponent("https%3A%2F%2Fexample.com%2Fpath%3Fquery%3Dvalue%23fragment"),
+        "https://example.com/path?query=value#fragment";);
+    assertNull(UrlFunctions.urlDecodeFormComponent(null));
+    assertEquals(UrlFunctions.urlDecodeFormComponent(""), "");
+  }
+
+  @Test
+  public void testUrlNetloc() {
+    assertEquals(UrlFunctions.urlNetloc("https://[email protected]:8080/path";), 
"[email protected]:8080");
+    
assertEquals(UrlFunctions.urlNetloc("https://user:[email protected]:8080/path";), 
"user:[email protected]: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://[email protected]/path";), 
"[email protected]");
+    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&param2=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&param2=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&param2=value2";,
 "param1"),
+        "https://example.com/path?param2=value2";);
+    assertEquals(
+        
UrlFunctions.cutURLParameter("https://example.com/path?param1=value1&param2=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&param2=value2&param3=value3";,
+        "param2"), "https://example.com/path?param1=value1&param3=value3";);
+    assertEquals(
+        
UrlFunctions.cutURLParameter("https://example.com/path?param1=value1&param2=value2&param3=value3#fragment";,
+            "param3"), 
"https://example.com/path?param1=value1&param2=value2#fragment";);
+    assertEquals(UrlFunctions.cutURLParameter(
+            
"https://example.com/path?param1=value1&param2=value2&param4&param3=value3#fragment";,
 "param4"),
+        
"https://example.com/path?param1=value1&param2=value2&param3=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&param2=value2";,
 new String[]{"param1"}),
+        "https://example.com/path?param2=value2";);
+    assertEquals(
+        
UrlFunctions.cutURLParameters("https://example.com/path?param1=value1&param2=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&param2=value2&param3=value3";,
+            new String[]{"param2", "param3"}),
+        "https://example.com/path?param1=value1";);
+    assertEquals(
+        
UrlFunctions.cutURLParameters("https://example.com/path?param1=value1&param2=value2&param3=value3#fragment";,
+            new String[]{"param2", "param3"}),
+        "https://example.com/path?param1=value1#fragment";);
+    assertEquals(
+        UrlFunctions.cutURLParameters(
+            
"https://example.com/path?param1=value1&param2=value2&param4&param3=value3#fragment";,
+            new String[]{"param2", "param3"}),
+        "https://example.com/path?param1=value1&param4#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";);
+  }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to