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;
import com.ibm.icu.text.UnicodeSet;
/**
* 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.
*/
public class 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 end position of the PQF, or the start value if there is none.
*/
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 immediately before an '@' + identified domain name. The purpose of this
* routine is for fitting into algorithms that are already in use, just handling for the email
* `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 just after @ and just
* before the domain_name.
* @return the start of the email locale part, or limit if no email local part is found
*/
public static int scanBackEmailLocalPart(CharSequence s, int start, int limit) {
return LinkHandlingUtilities.scanEmailBackwards(s, start, limit);
}
/** Enum for determining whether any percent-escaping is minimal or maximal, for use */
public enum Extent {
/** Minimal percent-escaping only percent-escapes non-ASCII where necessary. */
MINIMAL,
/** Maximal percent-escaping percent-escapes all non-ASCII. */
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.
*/
public static String escapePathQueryFragment(String source, Extent extent) {
UrlInternals ui = UrlInternals.from(source.toString());
switch (extent) {
case MINIMAL:
return ui.minimalEscape(EndStatus.FINAL);
case MAXIMAL:
return ui.fullEscape();
default:
throw new InternalError();
}
}
// NOTE for reviewers: These are only temporary, until ICU supplies \p{Link_Term=X} and
// \p{Idn_Status=X}
/**
* Returns a frozen set of Unicode characters that are guaranteed to never be part of a URL or
* email address. This allows implementations to make various optimizations because URLs and
* email addresses can never span these characters. For example, a span of characters between
* safe characters that doesn't have a sequence of domain-character + . + domain-character can
* be skipped in processing.
*
* @internal
*/
@Deprecated
public static UnicodeSet getSafeCharacters() {
return null;
}
/**
* Returns a frozen set of Unicode characters that are possible characters in a domain name
* (pre-mapping) This allows implementations to make various optimizations because URLs and
* email addresses must contain a sequence of domain-character + . + domain-character. It is the
* same as the set of IDNA Mapping Table character with values ≠ disallowed
*
* @internal
*/
@Deprecated
public static UnicodeSet getDomainCharacters() {
return null;
}
}