{
Iterator bookmarks = folder.getBookmarks().iterator();
String workingText = text;
while (bookmarks.hasNext())
{
BookmarkData bookmark = (BookmarkData)bookmarks.next();
String bkDescription = bookmark.getDescription();
if (bkDescription == null) bkDescription = "";
String bookmarkLink = "<a href=\"" +
bookmark.getUrl() + "\" title=\"" +
bkDescription + "\">" +
bookmark.getName() + "</a>";
try
{
// Replace all occurrences of bookmark name that don't occur within the bounds of an anchor tag
// Notes:
// - use reluctant quantifiers on the tags to avoid gobbling more than desired
// - use non-capturing groups for boundaries to avoid replacing the boundary as well as the bookmark name.
// - we depend on the numbering of the specific groups in this expression in the replacement code below.
// TODO: should escape the bookmark name
String regEx = "(<a(?:\\s.*?)??/>)|(<a(?:\\s.*?)??>)|(</a(?:\\s.*?)??>)|(?:\\b)(" + bookmark.getName() + ")(?:\\b)";
Matcher m = Pattern.compile(regEx).matcher(workingText);
StringBuffer textBuf = new StringBuffer(workingText.length());
int inLink = 0;
while (m.find())
{
if (m.group(1) != null)
{
// self-closed anchor tag <a ... /> -- ignore
}
else if (m.group(2) != null)
{
// matched opening anchor tag <a ...>
inLink++;
}
else if (m.group(3) != null)
{
// closing anchor tag </a>, but ignore nonmatching ones
if (inLink > 0) inLink--;
}
else if (m.group(4) != null)
{
// matched the bookmark -- replace, but only if not within a link tag.
if (inLink == 0) m.appendReplacement(textBuf, bookmarkLink);
}
// Any remaining case indicates a bug. One could add an else with assertion here. Conservatively don't substitute.
}
m.appendTail(textBuf);
workingText = textBuf.toString();
}
catch (PatternSyntaxException e)
{
// Can happen since we don't escape pattern the bookmark name to protect pattern characters.
mLogger.warn("Failed to substitute for bookmark [" + bookmark.getName() + "] due to regular expression characters.");
}
}
return workingText.toString();
}