Следующий код вносит эти изменения в «подход Этвуда»:
- Обнаруживает https в дополнение к http (добавление других схем тривиально)
- Используется флаг CASE_INSENSTIVE, поскольку HtTpS: // является действительным.
- Соответствующие наборы скобок отслаиваются (их можно вкладывать в
любой уровень). Кроме того, любые оставшиеся несопоставленные левые скобки
раздетые, но завершающие правые скобки остаются нетронутыми (для уважения
URL в стиле википедии)
- URL-адрес HTML-кодируется в тексте ссылки.
- Целевой атрибут передается через параметр метода. Другие атрибуты могут быть добавлены по желанию.
- Он не использует \ b для определения разрыва слова перед соответствием URL. URL-адреса могут начинаться с левой круглой скобки или http [s]: // без каких-либо других требований.
Примечания:
- Apache Commons Lang StringUtils используются в приведенном ниже коде
- Вызов HtmlUtil.encode () ниже - это утилита, которая в конечном итоге вызывает
некоторый код томагавка для HTML-кодирования текста ссылки, но подойдет любая подобная утилита.
- См. Комментарий к методу для использования в JSF или других средах, где по умолчанию выводится HTML-кодирование.
Это было написано в ответ на требования нашего клиента, и мы считаем, что оно представляет собой разумный компромисс между допустимыми символами из RFC и обычным использованием. Это предлагается здесь в надежде, что это будет полезно для других.
Возможно дальнейшее расширение, которое позволит вводить любые символы Юникода (т. Е. Не экранировать с помощью% XX (двузначный шестнадцатеричный код) и гиперссылки, но для этого потребуется принять все буквы Юникода плюс ограниченную пунктуацию, а затем разделить на "" допустимые разделители (например,.,%, |, # и т. д.), URL-кодирование каждой части, а затем склейка обратно. Например, http://en.wikipedia.org/wiki/Björn_Andrésen (который не обнаруживает генератор переполнения стека) будет "http://en.wikipedia.org/wiki/Bj%C3%B6rn_Andr%C3%A9sen" в href, но будет содержать Björn_Andrésen в связанном тексте на странице.
<code>// NOTES: 1) \w includes 0-9, a-z, A-Z, _
// 2) The leading '-' is the '-' character. It must go first in character class expression
private static final String VALID_CHARS = "-\\w+&@#/%=~()|";
private static final String VALID_NON_TERMINAL = "?!:,.;";
// Notes on the expression:
// 1) Any number of leading '(' (left parenthesis) accepted. Will be dealt with.
// 2) s? ==> the s is optional so either [http, https] accepted as scheme
// 3) All valid chars accepted and then one or more
// 4) Case insensitive so that the scheme can be hTtPs (for example) if desired
private static final Pattern URI_FINDER_PATTERN = Pattern.compile("\\(*https?://["+ VALID_CHARS + VALID_NON_TERMINAL + "]*[" +VALID_CHARS + "]", Pattern.CASE_INSENSITIVE );
/**
* <p>
* Finds all "URL"s in the given _rawText, wraps them in
* HTML link tags and returns the result (with the rest of the text
* html encoded).
* </p>
* <p>
* We employ the procedure described at:
* http://www.codinghorror.com/blog/2008/10/the-problem-with-urls.html
* which is a <b>must-read</b>.
* </p>
* Basically, we allow any number of left parenthesis (which will get stripped away)
* followed by http:// or https://. Then any number of permitted URL characters
* (based on http://www.ietf.org/rfc/rfc1738.txt) followed by a single character
* of that set (basically, those minus typical punctuation). We remove all sets of
* matching left & right parentheses which surround the URL.
*</p>
* <p>
* This method *must* be called from a tag/component which will NOT
* end up escaping the output. For example:
* <PRE>
* <h:outputText ... escape="false" value="#{core:hyperlinkText(textThatMayHaveURLs, '_blank')}"/>
*
*
*
* Причина: мы добавляем <a href="...">
тегов к выводу * и *
* кодирование остальной части строки. Таким образом, кодирование outupt приведет к
* двойное кодирование данных, которые уже были закодированы - и кодирование a href
* (что сделает его бесполезным).
*
*
*
* @param _rawText - если null
, возвращает ""
(пустая строка).
* @param _target - если не null
или ""
, добавляет цель, присвоенную сгенерированной ссылке, используя _target в качестве значения атрибута.
* /
public static final String hyperlinkText (final String _rawText, final String _target) {
String returnValue = null;
if (! StringUtils.isBlank (_rawText)) {
final Matcher matcher = URI_FINDER_PATTERN.matcher (_rawText);
if (matcher.find ()) {
final int originalLength = _rawText.length ();
final String targetText = (StringUtils.isBlank (_target))? "": "target = \" "+ _target.trim () +" \ "";
final int targetLength = targetText.length ();
// Подсчитано 15 символов помимо цели + 2 URL (максимум, если вся строка является URL)
// Грубое предположение, но мы не должны расширять Builder слишком много раз.
final StringBuilder returnBuffer = new StringBuilder (originalLength * 2 + targetLength + 15);
int currentStart;
int currentEnd;
int lastEnd = 0;
Строка currentURL;
делать {
currentStart = matcher.start ();
currentEnd = matcher.end ();
currentURL = matcher.group ();
// Корректируем для URL-адресов, обернутых в () 's ... переместить маркеры начала / конца
// и подстрока _rawText для нового значения URL.
while (currentURL.startsWith ("(") && currentURL.endsWith (")")) {
currentStart = currentStart + 1;
currentEnd = currentEnd - 1;currentURL = _rawText.substring (currentStart, currentEnd);
}
while (currentURL.startsWith ("(")) {
currentStart = currentStart + 1;
currentURL = _rawText.substring (currentStart, currentEnd);
}
// Текст с последнего совпадения
returnBuffer.append (HtmlUtil.encode (_rawText.substring (lastEnd, currentStart))));
// Обернуть совпавший URL
returnBuffer.append ("" + currentURL + "");
lastEnd = currentEnd;
} while (matcher.find ());
if (lastEnd