From 727ad04763b8287e9423c1f02565e65831a7415e Mon Sep 17 00:00:00 2001 From: Danny van Heumen Date: Sat, 29 Mar 2014 16:50:19 +0100 Subject: [PATCH] Implemented separate text formatter for IRC control codes. --- .../impl/protocol/irc/ControlChar.java | 2 +- .../protocol/irc/FormattedTextBuilder.java | 63 ++++++++++++++----- .../communicator/impl/protocol/irc/Utils.java | 53 +++++----------- .../irc/FormattedTextBuilderTest.java | 61 +++++++++++++++++- .../impl/protocol/irc/UtilsTest.java | 10 +-- 5 files changed, 129 insertions(+), 60 deletions(-) diff --git a/src/net/java/sip/communicator/impl/protocol/irc/ControlChar.java b/src/net/java/sip/communicator/impl/protocol/irc/ControlChar.java index 46bb61687..761850f07 100644 --- a/src/net/java/sip/communicator/impl/protocol/irc/ControlChar.java +++ b/src/net/java/sip/communicator/impl/protocol/irc/ControlChar.java @@ -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"); /** diff --git a/src/net/java/sip/communicator/impl/protocol/irc/FormattedTextBuilder.java b/src/net/java/sip/communicator/impl/protocol/irc/FormattedTextBuilder.java index 67c0d63fd..d59e5fe92 100644 --- a/src/net/java/sip/communicator/impl/protocol/irc/FormattedTextBuilder.java +++ b/src/net/java/sip/communicator/impl/protocol/irc/FormattedTextBuilder.java @@ -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 unwind = new Stack(); + final Stack rewind = new Stack(); 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(); } } diff --git a/src/net/java/sip/communicator/impl/protocol/irc/Utils.java b/src/net/java/sip/communicator/impl/protocol/irc/Utils.java index cbade22b3..ac7a54bed 100644 --- a/src/net/java/sip/communicator/impl/protocol/irc/Utils.java +++ b/src/net/java/sip/communicator/impl/protocol/irc/Utils.java @@ -42,9 +42,7 @@ public static String parse(String text) if (text == null) return null; - final Stack formatting = new Stack(); - - 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(); } /** diff --git a/test/net/java/sip/communicator/impl/protocol/irc/FormattedTextBuilderTest.java b/test/net/java/sip/communicator/impl/protocol/irc/FormattedTextBuilderTest.java index 5f064e74e..69a15bd1b 100644 --- a/test/net/java/sip/communicator/impl/protocol/irc/FormattedTextBuilderTest.java +++ b/test/net/java/sip/communicator/impl/protocol/irc/FormattedTextBuilderTest.java @@ -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("", 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("Hello world!!!", + formatted.done()); + } + + public void testToStringIntermediateResult() + { + FormattedTextBuilder formatted = new FormattedTextBuilder(); + formatted.apply(ControlChar.BOLD); + formatted.append("Hello "); + formatted.apply(ControlChar.ITALICS); + Assert.assertEquals("Hello ", formatted.toString()); + formatted.append("world"); + formatted.apply(ControlChar.BOLD); + formatted.append("!!!"); + Assert.assertEquals("Hello world!!!", + formatted.toString()); + Assert.assertEquals("Hello world!!!", + formatted.done()); + Assert.assertEquals("Hello world!!!", + 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)); } } diff --git a/test/net/java/sip/communicator/impl/protocol/irc/UtilsTest.java b/test/net/java/sip/communicator/impl/protocol/irc/UtilsTest.java index 83cfc0c81..c1934dda8 100644 --- a/test/net/java/sip/communicator/impl/protocol/irc/UtilsTest.java +++ b/test/net/java/sip/communicator/impl/protocol/irc/UtilsTest.java @@ -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 italics message ITALICS!."; 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 RED on Light Blue 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 RED on Light Blue message."; 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 = "Hello World!"; 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 = "Hello World!"; Assert.assertEquals(htmlMessage, Utils.parse(ircMessage)); }