Implemented separate text formatter for IRC control codes.

fix-message-formatting
Danny van Heumen 12 years ago
parent c3c6e23203
commit 727ad04763

@ -15,7 +15,7 @@ public enum ControlChar
BOLD('\u0002', "b"),
COLOR('\u0003', "font"),
NORMAL('\u000F', null),
ITALICS('\u001D', "i"),
ITALICS('\u0016', "i"),
UNDERLINE('\u001F', "u");
/**

@ -28,26 +28,57 @@ public void append(String text)
{
this.text.append(text);
}
/**
* Append a character.
*
* @param c character
*/
public void append(char c)
{
this.text.append(c);
}
/**
* Apply a control char for formatting.
*
* @param c the control char
*/
public void apply(ControlChar c)
public void apply(ControlChar c, String... addition)
{
if (formatting.contains(c))
if (c == ControlChar.NORMAL)
{
// cancel active control char
cancel(c);
// Special case: NORMAL resets/cancels all active formatting
cancelAll();
}
else
{
// start control char formatting
this.text.append(c.getHtmlStart());
if (formatting.contains(c))
{
// cancel active control char
cancel(c);
}
else
{
// start control char formatting
this.formatting.add(c);
this.text.append(c.getHtmlStart(addition));
}
}
}
/**
* Test whether or not a control character is already active.
*
* @param c the control char
* @return returns true if control char's kind of formatting is active, or
* false otherwise.
*/
public boolean isActive(ControlChar c)
{
return this.formatting.contains(c);
}
/**
* Cancel the specified control char.
*
@ -55,7 +86,7 @@ public void apply(ControlChar c)
*/
private void cancel(ControlChar c)
{
final Stack<ControlChar> unwind = new Stack<ControlChar>();
final Stack<ControlChar> rewind = new Stack<ControlChar>();
while (!this.formatting.empty())
{
// unwind control chars looking for the cancelled control char
@ -67,15 +98,14 @@ private void cancel(ControlChar c)
}
else
{
unwind.push(current);
rewind.push(current);
}
}
while (!unwind.empty())
while (!rewind.empty())
{
// rewind remaining control characters
ControlChar current = unwind.pop();
this.text.append(current.getHtmlStart());
this.formatting.push(current);
// reapply remaining control characters
ControlChar current = rewind.pop();
apply(current);
}
}
@ -102,11 +132,12 @@ public String done()
}
/**
* Return the formatted string. If it is not yet finished (outstanding
* formatting) also finish up remaining control chars.
* Return the formatted string in its current state. (This means that if
* {@link #done()} was not yet called, it will print an intermediate state of
* the formatted text.)
*/
public String toString()
{
return done();
return this.text.toString();
}
}

@ -42,9 +42,7 @@ public static String parse(String text)
if (text == null)
return null;
final Stack<ControlChar> formatting = new Stack<ControlChar>();
final StringBuilder builder = new StringBuilder();
FormattedTextBuilder builder = new FormattedTextBuilder();
for (int i = 0; i < text.length(); i++)
{
char val = text.charAt(i);
@ -57,29 +55,13 @@ public static String parse(String text)
case ITALICS:
case UNDERLINE:
case BOLD:
if (formatting.size() > 0 && formatting.peek() == control)
{
formatting.pop();
builder.append(control.getHtmlEnd());
}
else
{
formatting.push(control);
builder.append(control.getHtmlStart());
}
break;
case NORMAL:
while (formatting.size() > 0)
{
ControlChar c = formatting.pop();
builder.append(c.getHtmlEnd());
}
builder.apply(control);
break;
case COLOR:
if (formatting.size() > 0 && formatting.peek() == control)
if (builder.isActive(control))
{
formatting.pop();
builder.append(control.getHtmlEnd());
builder.apply(control);
}
else
{
@ -95,18 +77,23 @@ public static String parse(String text)
// parse background color code
final Color background =
parseBackgroundColor(text.substring(i + 1));
adds.add("bgcolor=\"" + background.getHtml() + "\"");
adds.add(
"bgcolor=\"" + background.getHtml() + "\"");
i += 3;
}
catch (IllegalArgumentException e)
{
LOGGER.trace("Invalid color code.", e);
LOGGER.debug(
"Invalid color code: "
+ text.substring(i + 1), e);
}
catch (ArrayIndexOutOfBoundsException e)
{
LOGGER.debug("Unknown color code referenced: "
+ text.substring(i + 1), e);
}
formatting.push(control);
String htmlTag =
control.getHtmlStart(adds.toArray(new String[adds
.size()]));
builder.append(htmlTag);
builder.apply(control,
adds.toArray(new String[adds.size()]));
}
break;
default:
@ -122,13 +109,7 @@ public static String parse(String text)
}
}
// Finish any outstanding formatting.
while (formatting.size() > 0)
{
builder.append(formatting.pop().getHtmlEnd());
}
return builder.toString();
return builder.done();
}
/**

@ -24,6 +24,17 @@ public void testPlainText()
Assert.assertEquals("Hello world!", formatted.done());
}
public void testPlainChar()
{
FormattedTextBuilder formatted = new FormattedTextBuilder();
formatted.append('H');
formatted.append('e');
formatted.append('l');
formatted.append('l');
formatted.append('o');
Assert.assertEquals("Hello", formatted.done());
}
public void testDoneWithoutFormatting()
{
FormattedTextBuilder formatted = new FormattedTextBuilder();
@ -43,7 +54,53 @@ public void testDoneRepeatedly()
public void testOnlyFormatting()
{
FormattedTextBuilder formatted = new FormattedTextBuilder();
formatted.append("Hello world!");
Assert.assertEquals("Hello world!", formatted.done());
formatted.apply(ControlChar.BOLD);
Assert.assertEquals("<b></b>", formatted.done());
}
public void testMixedFormattingContent()
{
FormattedTextBuilder formatted = new FormattedTextBuilder();
formatted.apply(ControlChar.BOLD);
formatted.append("Hello ");
formatted.apply(ControlChar.ITALICS);
formatted.append("world");
formatted.apply(ControlChar.BOLD);
formatted.append("!!!");
Assert.assertEquals("<b>Hello <i>world</i></b><i>!!!</i>",
formatted.done());
}
public void testToStringIntermediateResult()
{
FormattedTextBuilder formatted = new FormattedTextBuilder();
formatted.apply(ControlChar.BOLD);
formatted.append("Hello ");
formatted.apply(ControlChar.ITALICS);
Assert.assertEquals("<b>Hello <i>", formatted.toString());
formatted.append("world");
formatted.apply(ControlChar.BOLD);
formatted.append("!!!");
Assert.assertEquals("<b>Hello <i>world</i></b><i>!!!",
formatted.toString());
Assert.assertEquals("<b>Hello <i>world</i></b><i>!!!</i>",
formatted.done());
Assert.assertEquals("<b>Hello <i>world</i></b><i>!!!</i>",
formatted.toString());
}
public void testActiveFormatting()
{
FormattedTextBuilder formatted = new FormattedTextBuilder();
Assert.assertFalse(formatted.isActive(ControlChar.BOLD));
formatted.apply(ControlChar.BOLD);
Assert.assertTrue(formatted.isActive(ControlChar.BOLD));
formatted.append("Hello ");
Assert.assertFalse(formatted.isActive(ControlChar.ITALICS));
formatted.apply(ControlChar.ITALICS);
Assert.assertTrue(formatted.isActive(ControlChar.ITALICS));
formatted.done();
Assert.assertFalse(formatted.isActive(ControlChar.BOLD));
Assert.assertFalse(formatted.isActive(ControlChar.ITALICS));
}
}

@ -51,7 +51,7 @@ public void testParseStringWithBoldCode()
public void testParseStringWithItalicsCode()
{
final String ircMessage =
"My \u001Ditalics\u001D message \u001DITALICS!\u001D.";
"My \u0016italics\u0016 message \u0016ITALICS!\u0016.";
final String htmlMessage = "My <i>italics</i> message <i>ITALICS!</i>.";
Assert.assertEquals(htmlMessage, Utils.parse(ircMessage));
}
@ -125,7 +125,7 @@ public void testParseStringWithIncompleteBackgroundColorControlCode()
public void testParseSringAndNeutralizeWithNormalControlCode()
{
final String ircMessage =
"My \u0002\u001D\u001F\u000304,12RED on Light Blue\u000F message.";
"My \u0002\u0016\u001F\u000304,12RED on Light Blue\u000F message.";
final String htmlMessage =
"My <b><i><u><font color=\"Red\" bgcolor=\"RoyalBlue\">RED on Light Blue</font></u></i></b> message.";
Assert.assertEquals(htmlMessage, Utils.parse(ircMessage));
@ -134,7 +134,7 @@ public void testParseSringAndNeutralizeWithNormalControlCode()
public void testParseStringWithUnclosedFormattingI()
{
final String ircMessage =
"My \u0002\u001D\u001F\u000304,12RED on Light Blue message.";
"My \u0002\u0016\u001F\u000304,12RED on Light Blue message.";
final String htmlMessage =
"My <b><i><u><font color=\"Red\" bgcolor=\"RoyalBlue\">RED on Light Blue message.</font></u></i></b>";
Assert.assertEquals(htmlMessage, Utils.parse(ircMessage));
@ -156,14 +156,14 @@ public void testParseUnknownBackgroundCOlor()
public void testStackIncompatibleFormatToggling()
{
final String ircMessage = "\u0002\u001D\u001FHello\u0002 W\u001Dorld\u001F!";
final String ircMessage = "\u0002\u0016\u001FHello\u0002 W\u0016orld\u001F!";
final String htmlMessage = "<b><i><u>Hello</u></i></b><i><u> W</u></i><u>orld</u>!";
Assert.assertEquals(htmlMessage, Utils.parse(ircMessage));
}
public void testColorSwitch()
{
final String ircMessage = "\u000302,03Hello \u000308,09World\u000F!";
final String ircMessage = "\u000302,03Hello \u0003\u000308,09World\u000F!";
final String htmlMessage = "<font color=\"Navy\" bgcolor=\"Green\">Hello </font><font color=\"Yellow\" bgcolor=\"Lime\">World</font>!";
Assert.assertEquals(htmlMessage, Utils.parse(ircMessage));
}

Loading…
Cancel
Save