LinkUtilities.java
// © 2026 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
package com.ibm.icu.util;
import com.ibm.icu.impl.links.LinkHandlingUtilities;
import com.ibm.icu.impl.links.LinkHandlingUtilities.UrlInternals;
import com.ibm.icu.impl.links.LinkHandlingUtilities.UrlInternals.EndStatus;
/**
* Utility class for assisting with detecting links (URLs or emails) in text, and formatting them
* for display, implementing the algorithms in https://www.unicode.org/reports/tr58/ to handle
* Unicode characters properly. It supplies lower level APIs for use in augmenting existing scanners
* and formatters.
*
* @draft ICU 79
*/
public class LinkUtilities {
// private constructor to prevent default construction
private LinkUtilities() {}
/**
* Lower level utility for finding the end of a PathQueryFragment (PQF) in text. It assumes that
* the start position is immediately after an identified domain name. The purpose of this
* routine is for fitting into algorithms that are already in use, just taking over for the PQF
* scanning. For more information, see https://www.unicode.org/reports/tr58/.
*
* @param source the text to be scanned
* @param start the position in the text to be scanned from. It should be immediately after a
* domain name.
* @return the exclusive-end position of the PQF, or the start value if there is none.
* @draft ICU 79
*/
public static int scanPathQueryFragment(CharSequence source, int start, int limit) {
return LinkHandlingUtilities.parsePathQueryFragment(source.toString(), start);
}
/**
* Lower level utility for finding the start of an email address in text. It assumes that the
* limit position is at an '@' + identified domain name, and will scan backwards from there. The
* purpose of this routine is for fitting into algorithms that are already in use, just handling
* the email {@code local-part}. It does not scan back through "mailto:".
*
* @param source the text to be scanned
* @param start the position that is the earliest that should be considered in a backwards scan
* @param limit the position to start scanning backwards from — should be the position of the
* '@' (which is just before the domain_name)
* @return the start of the email local part, or limit if no email local part is found
* @draft ICU 79
*/
public static int scanBackEmailLocalPart(CharSequence source, int start, int limit) {
return LinkHandlingUtilities.scanEmailBackwards(source, start, limit);
}
/**
* Enum for determining whether any percent-escaping is minimal or maximal, for use with {@link
* #escapePathQueryFragment()}.
*
* @draft ICU 79
*/
public enum Extent {
/**
* Minimal percent-escaping only percent-escapes non-ASCII where necessary.
*
* @draft ICU 79
*/
MINIMAL,
/**
* Maximal percent-escaping percent-escapes all non-ASCII.
*
* @draft ICU 79
*/
MAXIMAL
}
/**
* Escapes a URL according to the Extent parameter
*
* @param source In the source, it is assumed that ASCII syntax characters requiring escaping
* have already been escaped. For example, a literal / in a path segment would already be
* percent-escaped. For more information, see https://www.unicode.org/reports/tr58/.
* @param extent either MINIMAL or MAXIMAL
* @return an escaped string according to the extent parameter.
* @draft ICU 79
*/
public static String escapePathQueryFragment(String source, Extent extent) {
UrlInternals ui = UrlInternals.from(source);
switch (extent) {
case MINIMAL:
return ui.minimalEscape(EndStatus.FINAL);
case MAXIMAL:
return ui.fullEscape();
default:
throw new InternalError();
}
}
}