|
|
|
|
@ -577,8 +577,7 @@ else if (messageType.equals(Chat.STATUS_MESSAGE))
|
|
|
|
|
chatString +=
|
|
|
|
|
GuiUtils.formatTime(date)
|
|
|
|
|
+ " "
|
|
|
|
|
+ processLinksAndHTMLChars(contactName, true,
|
|
|
|
|
ChatHtmlUtils.TEXT_CONTENT_TYPE) + " "
|
|
|
|
|
+ StringEscapeUtils.escapeHtml4(contactName) + " "
|
|
|
|
|
+ formatMessageAsHTML(message, contentType, keyword)
|
|
|
|
|
+ endHeaderTag;
|
|
|
|
|
}
|
|
|
|
|
@ -589,8 +588,7 @@ else if (messageType.equals(Chat.ACTION_MESSAGE))
|
|
|
|
|
endHeaderTag = "</p>";
|
|
|
|
|
|
|
|
|
|
chatString += "* " + GuiUtils.formatTime(date)
|
|
|
|
|
+ " " + processLinksAndHTMLChars(contactName, true,
|
|
|
|
|
ChatHtmlUtils.TEXT_CONTENT_TYPE) + " "
|
|
|
|
|
+ " " + StringEscapeUtils.escapeHtml4(contactName) + " "
|
|
|
|
|
+ formatMessageAsHTML(message, contentType, keyword)
|
|
|
|
|
+ endHeaderTag;
|
|
|
|
|
}
|
|
|
|
|
@ -623,10 +621,8 @@ else if (messageType.equals(Chat.ERROR_MESSAGE))
|
|
|
|
|
if (messageTitle != null)
|
|
|
|
|
{
|
|
|
|
|
chatString +=
|
|
|
|
|
errorIcon
|
|
|
|
|
+ processLinksAndHTMLChars(messageTitle, true,
|
|
|
|
|
ChatHtmlUtils.TEXT_CONTENT_TYPE) + endHeaderTag
|
|
|
|
|
+ "<h5>"
|
|
|
|
|
errorIcon + StringEscapeUtils.escapeHtml4(messageTitle)
|
|
|
|
|
+ endHeaderTag + "<h5>"
|
|
|
|
|
+ formatMessageAsHTML(message, contentType, keyword)
|
|
|
|
|
+ "</h5>";
|
|
|
|
|
}
|
|
|
|
|
@ -1021,55 +1017,13 @@ private void deleteAllMessagesWithoutHeader()
|
|
|
|
|
deleteAllMessagesWithoutHeader();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Highlights keywords searched in the history.
|
|
|
|
|
*
|
|
|
|
|
* @param message the source message
|
|
|
|
|
* @param contentType the content type
|
|
|
|
|
* @param keyword the searched keyword
|
|
|
|
|
* @return the formatted message
|
|
|
|
|
*/
|
|
|
|
|
private String processKeyword(final String message,
|
|
|
|
|
final String contentType, final String keyword)
|
|
|
|
|
{
|
|
|
|
|
if(message == null)
|
|
|
|
|
return message;
|
|
|
|
|
|
|
|
|
|
Matcher m
|
|
|
|
|
= Pattern.compile(Pattern.quote(keyword), Pattern.CASE_INSENSITIVE)
|
|
|
|
|
.matcher(message);
|
|
|
|
|
StringBuffer msgBuffer = new StringBuffer();
|
|
|
|
|
int prevEnd = 0;
|
|
|
|
|
|
|
|
|
|
while (m.find())
|
|
|
|
|
{
|
|
|
|
|
msgBuffer.append(StringEscapeUtils.escapeHtml4(message.substring(
|
|
|
|
|
prevEnd, m.start())));
|
|
|
|
|
prevEnd = m.end();
|
|
|
|
|
|
|
|
|
|
String keywordMatch = m.group().trim();
|
|
|
|
|
|
|
|
|
|
msgBuffer.append("<b>");
|
|
|
|
|
msgBuffer.append(StringEscapeUtils.escapeHtml4(keywordMatch));
|
|
|
|
|
msgBuffer.append("</b>");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* If the keyword didn't match, let the outside world be able to
|
|
|
|
|
* discover it.
|
|
|
|
|
*/
|
|
|
|
|
if (prevEnd == 0)
|
|
|
|
|
return message;
|
|
|
|
|
|
|
|
|
|
msgBuffer.append(StringEscapeUtils.escapeHtml4(message
|
|
|
|
|
.substring(prevEnd)));
|
|
|
|
|
return msgBuffer.toString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Formats the given message. Processes all smiley chars, new lines and
|
|
|
|
|
* links.
|
|
|
|
|
*
|
|
|
|
|
* TODO correctly name this method: we only expect content in either HTML or
|
|
|
|
|
* PLAIN TEXT format. We DON'T WANT THE HEADER STUFF OF A HTML MESSAGE!!!
|
|
|
|
|
*
|
|
|
|
|
* @param message the message to be formatted
|
|
|
|
|
* @param contentType the content type of the message to be formatted
|
|
|
|
|
* @param keyword the word to be highlighted
|
|
|
|
|
@ -1079,182 +1033,320 @@ private String formatMessageAsHTML(final String original,
|
|
|
|
|
final String contentType,
|
|
|
|
|
final String keyword)
|
|
|
|
|
{
|
|
|
|
|
if(original == null)
|
|
|
|
|
if (original == null)
|
|
|
|
|
{
|
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
String message = original;
|
|
|
|
|
// prepare initial message source
|
|
|
|
|
String source;
|
|
|
|
|
if (ChatHtmlUtils.HTML_CONTENT_TYPE.equals(contentType))
|
|
|
|
|
{
|
|
|
|
|
source = original;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
source = StringEscapeUtils.escapeHtml4(original);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If the message content type is HTML we won't process links and
|
|
|
|
|
// new lines, but only the smileys.
|
|
|
|
|
if (!ChatHtmlUtils.HTML_CONTENT_TYPE.equals(contentType))
|
|
|
|
|
final Replacer[] replacers = new Replacer[]
|
|
|
|
|
{
|
|
|
|
|
new NewlineReplacer(),
|
|
|
|
|
new URLReplacer(),
|
|
|
|
|
new KeywordReplacer(keyword),
|
|
|
|
|
new BrTagReplacer(),
|
|
|
|
|
new ImgTagReplacer()
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* We disallow HTML in plain-text messages. But processKeyword
|
|
|
|
|
* introduces HTML. So we'll allow HTML if processKeyword has
|
|
|
|
|
* introduced it in order to not break highlighting.
|
|
|
|
|
*/
|
|
|
|
|
boolean processHTMLChars;
|
|
|
|
|
return processReplacers(source, replacers);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((keyword != null) && (keyword.length() != 0))
|
|
|
|
|
// FIXME decent comments
|
|
|
|
|
private String processReplacers(final String content, final Replacer... replacers)
|
|
|
|
|
{
|
|
|
|
|
StringBuilder source = new StringBuilder(content);
|
|
|
|
|
for (Replacer replacer : replacers)
|
|
|
|
|
{
|
|
|
|
|
final StringBuilder target = new StringBuilder();
|
|
|
|
|
if (replacer.expectsPlainText())
|
|
|
|
|
{
|
|
|
|
|
// TODO Doesn't replacing keywords first cause hyperlinks to be
|
|
|
|
|
// broken if the keyword is in the hyperlink? Maybe we should
|
|
|
|
|
// insert anchors first, highlighting keywords.
|
|
|
|
|
String messageWithProcessedKeyword
|
|
|
|
|
= processKeyword(message, contentType, keyword);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* The same String instance will be returned if there was no
|
|
|
|
|
* keyword match. Calling #equals() is expensive so == is
|
|
|
|
|
* intentional.
|
|
|
|
|
*/
|
|
|
|
|
processHTMLChars = (messageWithProcessedKeyword == message);
|
|
|
|
|
message = messageWithProcessedKeyword;
|
|
|
|
|
int startPos = 0;
|
|
|
|
|
final Matcher plainTextInHtmlMatcher =
|
|
|
|
|
TEXT_TO_REPLACE_PATTERN.matcher(source);
|
|
|
|
|
while (plainTextInHtmlMatcher.find())
|
|
|
|
|
{
|
|
|
|
|
final String plainTextAsHtml =
|
|
|
|
|
plainTextInHtmlMatcher.group(1);
|
|
|
|
|
final int startMatchPosition =
|
|
|
|
|
plainTextInHtmlMatcher.start(1);
|
|
|
|
|
final int endMatchPosition = plainTextInHtmlMatcher.end(1);
|
|
|
|
|
target.append(source
|
|
|
|
|
.substring(startPos, startMatchPosition));
|
|
|
|
|
final String plaintext =
|
|
|
|
|
StringEscapeUtils.unescapeHtml4(plainTextAsHtml);
|
|
|
|
|
|
|
|
|
|
// Invoke replacer.
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
replacer.replace(target, plaintext);
|
|
|
|
|
}
|
|
|
|
|
catch (RuntimeException e)
|
|
|
|
|
{
|
|
|
|
|
logger.error("An error occurred in replacer: "
|
|
|
|
|
+ replacer.getClass().getName(), e);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
startPos = endMatchPosition;
|
|
|
|
|
}
|
|
|
|
|
target.append(source.substring(startPos));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
processHTMLChars = true;
|
|
|
|
|
{
|
|
|
|
|
// Invoke replacer.
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
replacer.replace(target, source.toString());
|
|
|
|
|
}
|
|
|
|
|
catch (RuntimeException e)
|
|
|
|
|
{
|
|
|
|
|
logger.error("An error occurred in replacer: "
|
|
|
|
|
+ replacer.getClass().getName(), e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
source = target;
|
|
|
|
|
}
|
|
|
|
|
return source.toString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO check all processors for correct html escaping!
|
|
|
|
|
// FIXME decent comments
|
|
|
|
|
private static abstract class Replacer
|
|
|
|
|
{
|
|
|
|
|
/**
|
|
|
|
|
* If a replacer expects plain text strings, then html content is
|
|
|
|
|
* automatically unescaped. The replacer is responsible for correctly
|
|
|
|
|
* escaping normal text.
|
|
|
|
|
*
|
|
|
|
|
* @return returns true if it needs plain text or false if it wants html
|
|
|
|
|
* content
|
|
|
|
|
*/
|
|
|
|
|
abstract boolean expectsPlainText();
|
|
|
|
|
// FIXME javadoc
|
|
|
|
|
abstract void replace(StringBuilder target, String piece);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
message = processNewLines(processLinksAndHTMLChars(
|
|
|
|
|
message, processHTMLChars, contentType), contentType);
|
|
|
|
|
private static final class URLReplacer
|
|
|
|
|
extends Replacer
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
boolean expectsPlainText()
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
// If the message content is HTML, we process br and img tags.
|
|
|
|
|
else
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
void replace(final StringBuilder target, final String piece)
|
|
|
|
|
{
|
|
|
|
|
// For HTML message, also check for hyperlinks.
|
|
|
|
|
int startPos = 0;
|
|
|
|
|
final StringBuilder buff = new StringBuilder();
|
|
|
|
|
final Matcher plainTextInHtmlMatcher =
|
|
|
|
|
TEXT_TO_REPLACE_PATTERN.matcher(message);
|
|
|
|
|
while (plainTextInHtmlMatcher.find())
|
|
|
|
|
final Matcher m = URL_PATTERN.matcher(piece);
|
|
|
|
|
int prevEnd = 0;
|
|
|
|
|
|
|
|
|
|
while (m.find())
|
|
|
|
|
{
|
|
|
|
|
final String plainTextAsHtml = plainTextInHtmlMatcher.group(1);
|
|
|
|
|
final int startMatchPosition = plainTextInHtmlMatcher.start(1);
|
|
|
|
|
final int endMatchPosition = plainTextInHtmlMatcher.end(1);
|
|
|
|
|
target.append(StringEscapeUtils.escapeHtml4(piece.substring(
|
|
|
|
|
prevEnd, m.start())));
|
|
|
|
|
prevEnd = m.end();
|
|
|
|
|
|
|
|
|
|
if (!StringUtils.isNullOrEmpty(plainTextAsHtml))
|
|
|
|
|
String url = m.group().trim();
|
|
|
|
|
target.append("<A href=\"");
|
|
|
|
|
if (url.startsWith("www"))
|
|
|
|
|
{
|
|
|
|
|
// always add from the end of previous match, to current one
|
|
|
|
|
// or from the start to the first match
|
|
|
|
|
buff.append(
|
|
|
|
|
message.substring(startPos, startMatchPosition));
|
|
|
|
|
|
|
|
|
|
final String plaintext =
|
|
|
|
|
StringEscapeUtils.unescapeHtml4(plainTextAsHtml);
|
|
|
|
|
buff.append(processLinksAndHTMLChars(plaintext, true,
|
|
|
|
|
ChatHtmlUtils.TEXT_CONTENT_TYPE));
|
|
|
|
|
|
|
|
|
|
startPos = endMatchPosition;
|
|
|
|
|
target.append("http://");
|
|
|
|
|
}
|
|
|
|
|
target.append(url);
|
|
|
|
|
target.append("\">");
|
|
|
|
|
target.append(StringEscapeUtils.escapeHtml4(url));
|
|
|
|
|
target.append("</A>");
|
|
|
|
|
}
|
|
|
|
|
buff.append(message.substring(startPos));
|
|
|
|
|
message = buff.toString();
|
|
|
|
|
target.append(StringEscapeUtils.escapeHtml4(piece
|
|
|
|
|
.substring(prevEnd)));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static final class NewlineReplacer
|
|
|
|
|
extends Replacer
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
if ((keyword != null) && (keyword.length() != 0))
|
|
|
|
|
message = processKeyword(message, contentType, keyword);
|
|
|
|
|
message = processImgTags(processBrTags(message));
|
|
|
|
|
@Override
|
|
|
|
|
boolean expectsPlainText()
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return message;
|
|
|
|
|
@Override
|
|
|
|
|
void replace(final StringBuilder target, final String piece)
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* <br> tags are needed to visualize a new line in the html format,
|
|
|
|
|
* but when copied to the clipboard they are exported to the plain
|
|
|
|
|
* text format as ' ' and not as '\n'.
|
|
|
|
|
*
|
|
|
|
|
* See bug N4988885:
|
|
|
|
|
* http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4988885
|
|
|
|
|
*
|
|
|
|
|
* To fix this we need " " - the HTML-Code for ASCII-Character
|
|
|
|
|
* No.10 (Line feed).
|
|
|
|
|
*/
|
|
|
|
|
target.append(piece.replaceAll("\n", "<BR/> "));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Formats all links in a given message and optionally escapes special HTML
|
|
|
|
|
* characters such as <, >, & and " in order to prevent HTML
|
|
|
|
|
* injection in plain-text messages such as writing
|
|
|
|
|
* <code></PLAINTEXT></code>, HTML which is going to be rendered as
|
|
|
|
|
* such and <code><PLAINTEXT></code>. The two procedures are carried
|
|
|
|
|
* out in one call in order to not break URLs which contain special HTML
|
|
|
|
|
* characters such as &.
|
|
|
|
|
*
|
|
|
|
|
* @param message The source message string.
|
|
|
|
|
* @param processHTMLChars <tt>true</tt> to escape the special HTML chars;
|
|
|
|
|
* otherwise, <tt>false</tt>
|
|
|
|
|
* @param contentType the message content type (html or plain text)
|
|
|
|
|
* @return The message string with properly formatted links.
|
|
|
|
|
*/
|
|
|
|
|
private String processLinksAndHTMLChars(final String message,
|
|
|
|
|
final boolean processHTMLChars,
|
|
|
|
|
final String contentType)
|
|
|
|
|
private static final class KeywordReplacer
|
|
|
|
|
extends Replacer
|
|
|
|
|
{
|
|
|
|
|
Matcher m = URL_PATTERN.matcher(message);
|
|
|
|
|
StringBuffer msgBuffer = new StringBuffer();
|
|
|
|
|
int prevEnd = 0;
|
|
|
|
|
private final String keyword;
|
|
|
|
|
|
|
|
|
|
while (m.find())
|
|
|
|
|
private KeywordReplacer(final String keyword)
|
|
|
|
|
{
|
|
|
|
|
this.keyword = keyword;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
boolean expectsPlainText()
|
|
|
|
|
{
|
|
|
|
|
final String rawMessage = message.substring(prevEnd, m.start());
|
|
|
|
|
final String fromPrevEndToStart;
|
|
|
|
|
if (processHTMLChars)
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
void replace(final StringBuilder target, final String piece)
|
|
|
|
|
{
|
|
|
|
|
if (this.keyword == null || this.keyword.isEmpty())
|
|
|
|
|
{
|
|
|
|
|
fromPrevEndToStart = StringEscapeUtils.escapeHtml4(rawMessage);
|
|
|
|
|
target.append(StringEscapeUtils.escapeHtml4(piece));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
|
|
|
|
|
final Matcher m =
|
|
|
|
|
Pattern.compile(Pattern.quote(keyword),
|
|
|
|
|
Pattern.CASE_INSENSITIVE).matcher(piece);
|
|
|
|
|
int prevEnd = 0;
|
|
|
|
|
while (m.find())
|
|
|
|
|
{
|
|
|
|
|
fromPrevEndToStart = rawMessage;
|
|
|
|
|
target.append(StringEscapeUtils.escapeHtml4(piece.substring(
|
|
|
|
|
prevEnd, m.start())));
|
|
|
|
|
prevEnd = m.end();
|
|
|
|
|
final String keywordMatch = m.group().trim();
|
|
|
|
|
target.append("<b>");
|
|
|
|
|
target.append(StringEscapeUtils.escapeHtml4(keywordMatch));
|
|
|
|
|
target.append("</b>");
|
|
|
|
|
}
|
|
|
|
|
msgBuffer.append(fromPrevEndToStart);
|
|
|
|
|
prevEnd = m.end();
|
|
|
|
|
target.append(StringEscapeUtils.escapeHtml4(piece
|
|
|
|
|
.substring(prevEnd)));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
String url = m.group().trim();
|
|
|
|
|
private static final class BrTagReplacer extends Replacer {
|
|
|
|
|
|
|
|
|
|
msgBuffer.append("<A href=\"");
|
|
|
|
|
if (url.startsWith("www"))
|
|
|
|
|
msgBuffer.append("http://");
|
|
|
|
|
msgBuffer.append(url);
|
|
|
|
|
msgBuffer.append("\">");
|
|
|
|
|
msgBuffer.append(StringEscapeUtils.escapeHtml4(url));
|
|
|
|
|
msgBuffer.append("</A>");
|
|
|
|
|
@Override
|
|
|
|
|
boolean expectsPlainText()
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
final String rawMessage = message.substring(prevEnd);
|
|
|
|
|
final String fromPrevEndToEnd;
|
|
|
|
|
if (processHTMLChars)
|
|
|
|
|
@Override
|
|
|
|
|
void replace(final StringBuilder target, final String piece)
|
|
|
|
|
{
|
|
|
|
|
fromPrevEndToEnd = StringEscapeUtils.escapeHtml4(rawMessage);
|
|
|
|
|
// Compile the regex to match something like <br .. /> or <BR .. />.
|
|
|
|
|
// This regex is case sensitive and keeps the style or other
|
|
|
|
|
// attributes of the <br> tag.
|
|
|
|
|
Matcher m =
|
|
|
|
|
Pattern.compile("<\\s*[bB][rR](.*?)(/\\s*>)").matcher(piece);
|
|
|
|
|
int start = 0;
|
|
|
|
|
|
|
|
|
|
// while we find some <br /> closing tags with a slash inside.
|
|
|
|
|
while (m.find())
|
|
|
|
|
{
|
|
|
|
|
// First, we have to copy all the message preceding the <br>
|
|
|
|
|
// tag.
|
|
|
|
|
target.append(piece.substring(start, m.start()));
|
|
|
|
|
// Then, we find the position of the slash inside the tag.
|
|
|
|
|
final int slashIndex = m.group().lastIndexOf("/");
|
|
|
|
|
// We copy the <br> tag till the slash exclude.
|
|
|
|
|
target.append(m.group().substring(0, slashIndex));
|
|
|
|
|
// We copy all the end of the tag following the slash exclude.
|
|
|
|
|
target.append(m.group().substring(slashIndex + 1));
|
|
|
|
|
start = m.end();
|
|
|
|
|
}
|
|
|
|
|
// Finally, we have to add the end of the message following the last
|
|
|
|
|
// <br> tag, or the whole message if there is no <br> tag.
|
|
|
|
|
target.append(piece.substring(start));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static final class ImgTagReplacer
|
|
|
|
|
extends Replacer
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
boolean expectsPlainText()
|
|
|
|
|
{
|
|
|
|
|
fromPrevEndToEnd = rawMessage;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
msgBuffer.append(fromPrevEndToEnd);
|
|
|
|
|
@Override
|
|
|
|
|
void replace(final StringBuilder target, final String piece)
|
|
|
|
|
{
|
|
|
|
|
// Compile the regex to match something like <img ... /> or
|
|
|
|
|
// <IMG ... />. This regex is case sensitive and keeps the style,
|
|
|
|
|
// src or other attributes of the <img> tag.
|
|
|
|
|
final Pattern p = Pattern.compile("<\\s*[iI][mM][gG](.*?)(/\\s*>)");
|
|
|
|
|
final Matcher m = p.matcher(piece);
|
|
|
|
|
int slashIndex;
|
|
|
|
|
int start = 0;
|
|
|
|
|
|
|
|
|
|
return msgBuffer.toString();
|
|
|
|
|
// while we find some <img /> self-closing tags with a slash inside.
|
|
|
|
|
while (m.find())
|
|
|
|
|
{
|
|
|
|
|
// First, we have to copy all the message preceding the <img>
|
|
|
|
|
// tag.
|
|
|
|
|
target.append(piece.substring(start, m.start()));
|
|
|
|
|
// Then, we find the position of the slash inside the tag.
|
|
|
|
|
slashIndex = m.group().lastIndexOf("/");
|
|
|
|
|
// We copy the <img> tag till the slash exclude.
|
|
|
|
|
target.append(m.group().substring(0, slashIndex));
|
|
|
|
|
// We copy all the end of the tag following the slash exclude.
|
|
|
|
|
target.append(m.group().substring(slashIndex + 1));
|
|
|
|
|
// We close the tag with a separate closing tag.
|
|
|
|
|
target.append("</img>");
|
|
|
|
|
start = m.end();
|
|
|
|
|
}
|
|
|
|
|
// Finally, we have to add the end of the message following the last
|
|
|
|
|
// <img> tag, or the whole message if there is no <img> tag.
|
|
|
|
|
target.append(piece.substring(start));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// FIXME delete this after everything works
|
|
|
|
|
/**
|
|
|
|
|
* Formats message new lines.
|
|
|
|
|
* Formats all links in a given message and optionally escapes special HTML
|
|
|
|
|
* characters such as <, >, & and " in order to prevent HTML
|
|
|
|
|
* injection in plain-text messages such as writing
|
|
|
|
|
* <code></PLAINTEXT></code>, HTML which is going to be rendered as
|
|
|
|
|
* such and <code><PLAINTEXT></code>. The two procedures are carried
|
|
|
|
|
* out in one call in order to not break URLs which contain special HTML
|
|
|
|
|
* characters such as &.
|
|
|
|
|
*
|
|
|
|
|
* @param message The source message string.
|
|
|
|
|
* @param contentType message contentType (html or plain text)
|
|
|
|
|
* @return The message string with properly formatted new lines.
|
|
|
|
|
* @param processHTMLChars <tt>true</tt> to escape the special HTML chars;
|
|
|
|
|
* otherwise, <tt>false</tt>
|
|
|
|
|
* @param contentType the message content type (html or plain text)
|
|
|
|
|
* @return The message string with properly formatted links.
|
|
|
|
|
*/
|
|
|
|
|
private String processNewLines(String message, String contentType)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* <br> tags are needed to visualize a new line in the html format, but
|
|
|
|
|
* when copied to the clipboard they are exported to the plain text
|
|
|
|
|
* format as ' ' and not as '\n'.
|
|
|
|
|
*
|
|
|
|
|
* See bug N4988885:
|
|
|
|
|
* http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4988885
|
|
|
|
|
*
|
|
|
|
|
* To fix this we need " " - the HTML-Code for ASCII-Character No.10
|
|
|
|
|
* (Line feed).
|
|
|
|
|
*/
|
|
|
|
|
Matcher divMatcher = DIV_PATTERN.matcher(message);
|
|
|
|
|
String openingTag = "";
|
|
|
|
|
String closingTag = "";
|
|
|
|
|
if (divMatcher.find())
|
|
|
|
|
{
|
|
|
|
|
openingTag = divMatcher.group(1);
|
|
|
|
|
message = divMatcher.group(2);
|
|
|
|
|
closingTag = divMatcher.group(3);
|
|
|
|
|
}
|
|
|
|
|
return openingTag + message.replaceAll("\n", "<BR/> ") + closingTag;
|
|
|
|
|
}
|
|
|
|
|
// processLinksAndHTMLChars(final String message,
|
|
|
|
|
// final boolean processHTMLChars,
|
|
|
|
|
// final String contentType)
|
|
|
|
|
// {
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Opens a link in the default browser when clicked and shows link url in a
|
|
|
|
|
@ -1552,6 +1644,7 @@ public Date getPageLastMsgTimestamp()
|
|
|
|
|
return timestamp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// FIXME delete this after everything works
|
|
|
|
|
/**
|
|
|
|
|
* Formats HTML tags <br/> to <br> or <BR/> to <BR>.
|
|
|
|
|
* The reason of this function is that the ChatPanel does not support
|
|
|
|
|
@ -1560,38 +1653,11 @@ public Date getPageLastMsgTimestamp()
|
|
|
|
|
* @param message The source message string.
|
|
|
|
|
* @return The message string with properly formatted <br> tags.
|
|
|
|
|
*/
|
|
|
|
|
private String processBrTags(String message)
|
|
|
|
|
{
|
|
|
|
|
// The resulting message after being processed by this function.
|
|
|
|
|
StringBuffer processedMessage = new StringBuffer();
|
|
|
|
|
|
|
|
|
|
// Compile the regex to match something like <br .. /> or <BR .. />.
|
|
|
|
|
// This regex is case sensitive and keeps the style or other
|
|
|
|
|
// attributes of the <br> tag.
|
|
|
|
|
Matcher m
|
|
|
|
|
= Pattern.compile("<\\s*[bB][rR](.*?)(/\\s*>)").matcher(message);
|
|
|
|
|
int start = 0;
|
|
|
|
|
|
|
|
|
|
// while we find some <br /> closing tags with a slash inside.
|
|
|
|
|
while(m.find())
|
|
|
|
|
{
|
|
|
|
|
// First, we have to copy all the message preceding the <br> tag.
|
|
|
|
|
processedMessage.append(message.substring(start, m.start()));
|
|
|
|
|
// Then, we find the position of the slash inside the tag.
|
|
|
|
|
int slash_index = m.group().lastIndexOf("/");
|
|
|
|
|
// We copy the <br> tag till the slash exclude.
|
|
|
|
|
processedMessage.append(m.group().substring(0, slash_index));
|
|
|
|
|
// We copy all the end of the tag following the slash exclude.
|
|
|
|
|
processedMessage.append(m.group().substring(slash_index+1));
|
|
|
|
|
start = m.end();
|
|
|
|
|
}
|
|
|
|
|
// Finally, we have to add the end of the message following the last
|
|
|
|
|
// <br> tag, or the whole message if there is no <br> tag.
|
|
|
|
|
processedMessage.append(message.substring(start));
|
|
|
|
|
|
|
|
|
|
return processedMessage.toString();
|
|
|
|
|
}
|
|
|
|
|
// processBrTags(String message)
|
|
|
|
|
// {
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// FIXME delete this after everything works
|
|
|
|
|
/**
|
|
|
|
|
* Formats HTML tags <img ... /> to < img ... ></img> or
|
|
|
|
|
* <IMG ... /> to <IMG></IMG>.
|
|
|
|
|
@ -1602,40 +1668,9 @@ private String processBrTags(String message)
|
|
|
|
|
* @param message The source message string.
|
|
|
|
|
* @return The message string with properly formatted <img> tags.
|
|
|
|
|
*/
|
|
|
|
|
private String processImgTags(String message)
|
|
|
|
|
{
|
|
|
|
|
// The resulting message after being processed by this function.
|
|
|
|
|
StringBuffer processedMessage = new StringBuffer();
|
|
|
|
|
|
|
|
|
|
// Compile the regex to match something like <img ... /> or
|
|
|
|
|
// <IMG ... />. This regex is case sensitive and keeps the style,
|
|
|
|
|
// src or other attributes of the <img> tag.
|
|
|
|
|
Pattern p = Pattern.compile("<\\s*[iI][mM][gG](.*?)(/\\s*>)");
|
|
|
|
|
Matcher m = p.matcher(message);
|
|
|
|
|
int slash_index;
|
|
|
|
|
int start = 0;
|
|
|
|
|
|
|
|
|
|
// while we find some <img /> self-closing tags with a slash inside.
|
|
|
|
|
while(m.find())
|
|
|
|
|
{
|
|
|
|
|
// First, we have to copy all the message preceding the <img> tag.
|
|
|
|
|
processedMessage.append(message.substring(start, m.start()));
|
|
|
|
|
// Then, we find the position of the slash inside the tag.
|
|
|
|
|
slash_index = m.group().lastIndexOf("/");
|
|
|
|
|
// We copy the <img> tag till the slash exclude.
|
|
|
|
|
processedMessage.append(m.group().substring(0, slash_index));
|
|
|
|
|
// We copy all the end of the tag following the slash exclude.
|
|
|
|
|
processedMessage.append(m.group().substring(slash_index+1));
|
|
|
|
|
// We close the tag with a separate closing tag.
|
|
|
|
|
processedMessage.append("</img>");
|
|
|
|
|
start = m.end();
|
|
|
|
|
}
|
|
|
|
|
// Finally, we have to add the end of the message following the last
|
|
|
|
|
// <img> tag, or the whole message if there is no <img> tag.
|
|
|
|
|
processedMessage.append(message.substring(start));
|
|
|
|
|
|
|
|
|
|
return processedMessage.toString();
|
|
|
|
|
}
|
|
|
|
|
// processImgTags(String message)
|
|
|
|
|
// {
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Extend Editor pane to add URL tooltips.
|
|
|
|
|
@ -1748,20 +1783,6 @@ public void loadSkin()
|
|
|
|
|
getRightButtonMenu().loadSkin();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Highlights the string in multi user chat.
|
|
|
|
|
*
|
|
|
|
|
* @param message the message to process
|
|
|
|
|
* @param contentType the content type of the message
|
|
|
|
|
* @param keyWord the keyword to highlight
|
|
|
|
|
* @return the message string with the keyword highlighted
|
|
|
|
|
*/
|
|
|
|
|
public String processChatRoomHighlight(String message, String contentType,
|
|
|
|
|
String keyWord)
|
|
|
|
|
{
|
|
|
|
|
return processKeyword(message, contentType, keyWord);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Processes /me command in group chats.
|
|
|
|
|
*
|
|
|
|
|
|