aboutsummaryrefslogtreecommitdiffstats
path: root/src/net/java/sip/communicator/impl/protocol/irc/Utils.java
blob: 008949d22cdfa185e04c0a10ee96c88cec53eee1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
/*
 * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
 * 
 * Distributable under LGPL license. See terms of license at gnu.org.
 */
package net.java.sip.communicator.impl.protocol.irc;

import java.util.*;

import net.java.sip.communicator.util.*;

/**
 * Some IRC-related utility methods.
 * 
 * @author Danny van Heumen
 */
public final class Utils
{
    /**
     * Logger
     */
    private static final Logger LOGGER = Logger.getLogger(Utils.class);

    /**
     * Private constructor since we do not need to construct anything.
     */
    private Utils()
    {
    }

    /**
     * Parse IRC text message and process possible control codes.
     * 
     * TODO Support reverse (0x16) control code?
     * 
     * @param text the message
     * @return returns the processed message or null if text message was null,
     *         since there is nothing to modify there
     */
    public static String parse(String text)
    {
        if (text == null)
            return null;

        final Stack<ControlChar> formatting = new Stack<ControlChar>();

        final StringBuilder builder = new StringBuilder();
        for (int i = 0; i < text.length(); i++)
        {
            char val = text.charAt(i);
            ControlChar control = ControlChar.byCode(val);
            if (control != null)
            {
                // value is a control character, so do something special
                switch (control)
                {
                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());
                    }
                    break;
                case COLOR:
                    if (formatting.size() > 0 && formatting.peek() == control)
                    {
                        formatting.pop();
                        builder.append(control.getHtmlEnd());
                    }
                    else
                    {
                        final List<String> adds = new LinkedList<String>();
                        try
                        {
                            // parse foreground color code
                            final Color foreground =
                                parseForegroundColor(text.substring(i + 1));
                            adds.add("color=\"" + foreground.getHtml() + "\"");
                            i += 2;

                            // parse background color code
                            final Color background =
                                parseBackgroundColor(text.substring(i + 1));
                            adds.add("bgcolor=\"" + background.getHtml() + "\"");
                            i += 3;
                        }
                        catch (IllegalArgumentException e)
                        {
                            LOGGER.debug("Invalid color code.", e);
                        }
                        formatting.push(control);
                        String htmlTag =
                            control.getHtmlStart(adds.toArray(new String[adds
                                .size()]));
                        builder.append(htmlTag);
                    }
                    break;
                default:
                    LOGGER.warn("Unsupported IRC control code encountered: "
                        + control);
                    break;
                }
            }
            else
            {
                // value is a normal character, just append
                builder.append(val);
            }
        }

        // Finish any outstanding formatting.
        while (formatting.size() > 0)
        {
            builder.append(formatting.pop().getHtmlEnd());
        }

        return builder.toString();
    }

    /**
     * Parse background color code starting with the separator.
     * 
     * @param text the text starting with the background color (separator)
     * @return returns the background color
     */
    private static Color parseBackgroundColor(String text)
    {
        try
        {
            if (text.charAt(0) == ',')
            {
                // if available, also parse background color
                int color =
                    Integer.parseInt("" + text.charAt(1) + text.charAt(2));
                return Color.values()[color];
            }
            throw new IllegalArgumentException(
                "no color separator present, hence no background color present");
        }
        catch (StringIndexOutOfBoundsException e)
        {
            // Abort parsing background color. Assume only
            // foreground color available.
            throw new IllegalArgumentException(
                "text stopped before the background color code was finished");
        }
        catch (NumberFormatException e)
        {
            // No background color defined, ignoring ...
            throw new IllegalArgumentException(
                "value isn't a background color code: " + text.substring(1, 3));
        }
        catch (ArrayIndexOutOfBoundsException e)
        {
            LOGGER.info("Specified IRC color is not a known color code.", e);
            throw new IllegalArgumentException("background color value "
                + text.substring(1, 3) + " is not a known color");
        }
    }

    /**
     * Parse foreground color and return corresponding Color instance.
     * 
     * @param text the text to parse, starting with color code
     * @return returns Color instance
     */
    private static Color parseForegroundColor(String text)
    {
        try
        {
            int color = Integer.parseInt("" + text.charAt(0) + text.charAt(1));
            return Color.values()[color];
        }
        catch (StringIndexOutOfBoundsException e)
        {
            // Invalid control code, since text has ended.
            LOGGER
                .trace("ArrayIndexOutOfBounds during foreground color control code parsing.");
            throw new IllegalArgumentException("missing foreground color code");
        }
        catch (NumberFormatException e)
        {
            // Invalid text color value
            LOGGER.trace("Invalid foreground color code encountered.");
            throw new IllegalArgumentException(
                "invalid foreground color code: " + text.substring(0, 2));
        }
        catch (ArrayIndexOutOfBoundsException e)
        {
            LOGGER.info("Specified IRC color is not a known color code.", e);
            throw new IllegalArgumentException("foreground color value "
                + text.substring(0, 2) + " is not a known color");
        }
    }
}