001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.parsers.bbcode;
016    
017    import com.liferay.portal.kernel.log.Log;
018    import com.liferay.portal.kernel.log.LogFactoryUtil;
019    import com.liferay.portal.kernel.parsers.bbcode.BBCodeTranslator;
020    import com.liferay.portal.kernel.security.pacl.DoPrivileged;
021    import com.liferay.portal.kernel.util.GetterUtil;
022    import com.liferay.portal.kernel.util.HtmlUtil;
023    import com.liferay.portal.kernel.util.IntegerWrapper;
024    import com.liferay.portal.kernel.util.StringBundler;
025    import com.liferay.portal.kernel.util.StringPool;
026    import com.liferay.portal.kernel.util.StringUtil;
027    import com.liferay.portal.kernel.util.Validator;
028    
029    import java.util.Collection;
030    import java.util.HashMap;
031    import java.util.List;
032    import java.util.Map;
033    import java.util.Stack;
034    import java.util.regex.Matcher;
035    import java.util.regex.Pattern;
036    
037    /**
038     * @author Iliyan Peychev
039     */
040    @DoPrivileged
041    public class HtmlBBCodeTranslatorImpl implements BBCodeTranslator {
042    
043            public HtmlBBCodeTranslatorImpl() {
044                    _listStyles = new HashMap<String, String>();
045    
046                    _listStyles.put("a", "list-style: lower-alpha outside;");
047                    _listStyles.put("A", "list-style: upper-alpha outside;");
048                    _listStyles.put("1", "list-style: decimal outside;");
049                    _listStyles.put("i", "list-style: lower-roman outside;");
050                    _listStyles.put("I", "list-style: upper-roman outside;");
051    
052                    _excludeNewLineTypes = new HashMap<String, Integer>();
053    
054                    _excludeNewLineTypes.put("*", BBCodeParser.TYPE_TAG_START_END);
055                    _excludeNewLineTypes.put("li", BBCodeParser.TYPE_TAG_START_END);
056                    _excludeNewLineTypes.put("table", BBCodeParser.TYPE_TAG_END);
057                    _excludeNewLineTypes.put("td", BBCodeParser.TYPE_TAG_START_END);
058                    _excludeNewLineTypes.put("th", BBCodeParser.TYPE_TAG_START_END);
059                    _excludeNewLineTypes.put("tr", BBCodeParser.TYPE_TAG_START_END);
060    
061                    _bbCodeCharacters = new HashMap<String, String>();
062    
063                    _bbCodeCharacters.put("&", "&amp;");
064                    _bbCodeCharacters.put("<", "&lt;");
065                    _bbCodeCharacters.put(">", "&gt;");
066                    _bbCodeCharacters.put("\"", "&#034;");
067                    _bbCodeCharacters.put("'", "&#039;");
068                    _bbCodeCharacters.put("/", "&#047;");
069                    _bbCodeCharacters.put("`", "&#096;");
070                    _bbCodeCharacters.put("[", "&#91;");
071                    _bbCodeCharacters.put("]", "&#93;");
072                    _bbCodeCharacters.put("(", "&#40;");
073                    _bbCodeCharacters.put(")", "&#41;");
074    
075                    for (int i = 0; i < _EMOTICONS.length; i++) {
076                            String[] emoticon = _EMOTICONS[i];
077    
078                            _emoticonDescriptions[i] = emoticon[2];
079                            _emoticonFiles[i] = emoticon[0];
080                            _emoticonSymbols[i] = emoticon[1];
081    
082                            String image = emoticon[0];
083    
084                            emoticon[0] =
085                                    "<img alt=\"emoticon\" src=\"@theme_images_path@/emoticons/" +
086                                            image + "\" >";
087                    }
088            }
089    
090            @Override
091            public String[] getEmoticonDescriptions() {
092                    return _emoticonDescriptions;
093            }
094    
095            @Override
096            public String[] getEmoticonFiles() {
097                    return _emoticonFiles;
098            }
099    
100            @Override
101            public String[][] getEmoticons() {
102                    return _EMOTICONS;
103            }
104    
105            @Override
106            public String[] getEmoticonSymbols() {
107                    return _emoticonSymbols;
108            }
109    
110            @Override
111            public String getHTML(String bbcode) {
112                    try {
113                            bbcode = parse(bbcode);
114                    }
115                    catch (Exception e) {
116                            _log.error("Unable to parse: " + bbcode, e);
117    
118                            bbcode = HtmlUtil.escape(bbcode);
119                    }
120    
121                    return bbcode;
122            }
123    
124            @Override
125            public String parse(String text) {
126                    StringBundler sb = new StringBundler();
127    
128                    List<BBCodeItem> bbCodeItems = _bbCodeParser.parse(text);
129                    Stack<String> tags = new Stack<String>();
130                    IntegerWrapper marker = new IntegerWrapper();
131    
132                    for (; marker.getValue() < bbCodeItems.size(); marker.increment()) {
133                            BBCodeItem bbCodeItem = bbCodeItems.get(marker.getValue());
134    
135                            int type = bbCodeItem.getType();
136    
137                            if (type == BBCodeParser.TYPE_DATA) {
138                                    handleData(sb, bbCodeItems, tags, marker, bbCodeItem);
139                            }
140                            else if (type == BBCodeParser.TYPE_TAG_END) {
141                                    handleTagEnd(sb, tags, bbCodeItem);
142                            }
143                            else if (type == BBCodeParser.TYPE_TAG_START) {
144                                    handleTagStart(sb, bbCodeItems, tags, marker, bbCodeItem);
145                            }
146                    }
147    
148                    return sb.toString();
149            }
150    
151            protected String escapeQuote(String quote) {
152                    StringBuilder sb = new StringBuilder();
153    
154                    int index = 0;
155    
156                    Matcher matcher = _bbCodePattern.matcher(quote);
157    
158                    Collection<String> values = _bbCodeCharacters.values();
159    
160                    while (matcher.find()) {
161                            String match = matcher.group();
162    
163                            int matchStartIndex = matcher.start();
164    
165                            int nextSemicolonIndex = quote.indexOf(
166                                    StringPool.SEMICOLON, matchStartIndex);
167    
168                            sb.append(quote.substring(index, matchStartIndex));
169    
170                            boolean entityFound = false;
171    
172                            if (nextSemicolonIndex >= 0) {
173                                    String value = quote.substring(
174                                            matchStartIndex, nextSemicolonIndex + 1);
175    
176                                    if (values.contains(value)) {
177                                            sb.append(value);
178    
179                                            index = matchStartIndex + value.length();
180    
181                                            entityFound = true;
182                                    }
183                            }
184    
185                            if (!entityFound) {
186                                    String escapedValue = _bbCodeCharacters.get(match);
187    
188                                    sb.append(escapedValue);
189    
190                                    index = matchStartIndex + match.length();
191                            }
192                    }
193    
194                    if (index < quote.length()) {
195                            sb.append(quote.substring(index, quote.length()));
196                    }
197    
198                    return sb.toString();
199            }
200    
201            protected String extractData(
202                    List<BBCodeItem> bbCodeItems, IntegerWrapper marker, String tag,
203                    int type, boolean consume) {
204    
205                    StringBundler sb = new StringBundler();
206    
207                    int index = marker.getValue() + 1;
208    
209                    BBCodeItem bbCodeItem = null;
210    
211                    do {
212                            bbCodeItem = bbCodeItems.get(index++);
213    
214                            if ((bbCodeItem.getType() & type) > 0) {
215                                    sb.append(bbCodeItem.getValue());
216                            }
217    
218                    }
219                    while ((bbCodeItem.getType() != BBCodeParser.TYPE_TAG_END) &&
220                               !tag.equals(bbCodeItem.getValue()));
221    
222                    if (consume) {
223                            marker.setValue(index - 1);
224                    }
225    
226                    return sb.toString();
227            }
228    
229            protected void handleBold(StringBundler sb, Stack<String> tags) {
230                    handleSimpleTag(sb, tags, "strong");
231            }
232    
233            protected void handleCode(
234                    StringBundler sb, List<BBCodeItem> bbCodeItems, IntegerWrapper marker) {
235    
236                    sb.append("<div class=\"lfr-code\">");
237                    sb.append("<table>");
238                    sb.append("<tbody>");
239                    sb.append("<tr>");
240                    sb.append("<td class=\"line-numbers\">");
241    
242                    String code = extractData(
243                            bbCodeItems, marker, "code", BBCodeParser.TYPE_DATA, true);
244    
245                    code = HtmlUtil.escape(code);
246                    code = code.replaceAll(StringPool.TAB, StringPool.FOUR_SPACES);
247    
248                    String[] lines = code.split("\r?\n");
249    
250                    for (int i = 0; i < lines.length; i++) {
251                            String index = String.valueOf(i + 1);
252    
253                            sb.append("<span class=\"number\">");
254                            sb.append(index);
255                            sb.append("</span>");
256                    }
257    
258                    sb.append("</td>");
259                    sb.append("<td class=\"lines\">");
260    
261                    for (int i = 0; i < lines.length; i++) {
262                            String line = lines[i];
263    
264                            line = StringUtil.replace(
265                                    line, StringPool.THREE_SPACES, "&nbsp; &nbsp;");
266                            line = StringUtil.replace(line, StringPool.DOUBLE_SPACE, "&nbsp; ");
267    
268                            if (Validator.isNull(line)) {
269                                    line = "<br />";
270                            }
271    
272                            sb.append("<div class=\"line\">");
273                            sb.append(line);
274                            sb.append("</div>");
275                    }
276    
277                    sb.append("</td>");
278                    sb.append("</tr>");
279                    sb.append("</tbody>");
280                    sb.append("</table>");
281                    sb.append("</div>");
282            }
283    
284            protected void handleColor(
285                    StringBundler sb, Stack<String> tags, BBCodeItem bbCodeItem) {
286    
287                    sb.append("<span style=\"color: ");
288    
289                    String color = bbCodeItem.getAttribute();
290    
291                    if (color == null) {
292                            color = "inherit";
293                    }
294                    else {
295                            Matcher matcher = _colorPattern.matcher(color);
296    
297                            if (!matcher.matches()) {
298                                    color = "inherit";
299                            }
300                    }
301    
302                    sb.append(color);
303    
304                    sb.append("\">");
305    
306                    tags.push("</span>");
307            }
308    
309            protected void handleData(
310                    StringBundler sb, List<BBCodeItem> bbCodeItems, Stack<String> tags,
311                    IntegerWrapper marker, BBCodeItem bbCodeItem) {
312    
313                    String value = HtmlUtil.escape(bbCodeItem.getValue());
314    
315                    value = handleNewLine(bbCodeItems, tags, marker, value);
316    
317                    for (int i = 0; i < _EMOTICONS.length; i++) {
318                            String[] emoticon = _EMOTICONS[i];
319    
320                            value = StringUtil.replace(value, emoticon[1], emoticon[0]);
321                    }
322    
323                    sb.append(value);
324            }
325    
326            protected void handleEmail(
327                    StringBundler sb, List<BBCodeItem> bbCodeItems, Stack<String> tags,
328                    IntegerWrapper marker, BBCodeItem bbCodeItem) {
329    
330                    sb.append("<a href=\"");
331    
332                    String href = bbCodeItem.getAttribute();
333    
334                    if (href == null) {
335                            href = extractData(
336                                    bbCodeItems, marker, "email", BBCodeParser.TYPE_DATA, false);
337                    }
338    
339                    if (!href.startsWith("mailto:")) {
340                            href = "mailto:" + href;
341                    }
342    
343                    sb.append(HtmlUtil.escapeHREF(href));
344    
345                    sb.append("\">");
346    
347                    tags.push("</a>");
348            }
349    
350            protected void handleFontFamily(
351                    StringBundler sb, Stack<String> tags, BBCodeItem bbCodeItem) {
352    
353                    sb.append("<span style=\"font-family: ");
354                    sb.append(HtmlUtil.escapeAttribute(bbCodeItem.getAttribute()));
355                    sb.append("\">");
356    
357                    tags.push("</span>");
358            }
359    
360            protected void handleFontSize(
361                    StringBundler sb, Stack<String> tags, BBCodeItem bbCodeItem) {
362    
363                    sb.append("<span style=\"font-size: ");
364    
365                    int size = GetterUtil.getInteger(bbCodeItem.getAttribute());
366    
367                    if ((size >= 1) && (size <= _fontSizes.length)) {
368                            sb.append(_fontSizes[size - 1]);
369                    }
370                    else {
371                            sb.append(_fontSizes[1]);
372                    }
373    
374                    sb.append("px\">");
375    
376                    tags.push("</span>");
377            }
378    
379            protected void handleImage(
380                    StringBundler sb, List<BBCodeItem> bbCodeItems, IntegerWrapper marker) {
381    
382                    sb.append("<img src=\"");
383    
384                    String src = extractData(
385                            bbCodeItems, marker, "img", BBCodeParser.TYPE_DATA, true);
386    
387                    Matcher matcher = _imagePattern.matcher(src);
388    
389                    if (matcher.matches()) {
390                            sb.append(HtmlUtil.escapeAttribute(src));
391                    }
392    
393                    sb.append("\" />");
394            }
395    
396            protected void handleItalic(StringBundler sb, Stack<String> tags) {
397                    handleSimpleTag(sb, tags, "em");
398            }
399    
400            protected void handleList(
401                    StringBundler sb, Stack<String> tags, BBCodeItem bbCodeItem) {
402    
403                    String listStyle = null;
404    
405                    String tag = null;
406    
407                    String listAttribute = bbCodeItem.getAttribute();
408    
409                    if (listAttribute != null) {
410                            listStyle = _listStyles.get(listAttribute);
411    
412                            tag = "ol";
413                    }
414                    else {
415                            tag = "ul style=\"list-style: disc outside;\"";
416                    }
417    
418                    if (listStyle == null) {
419                            sb.append("<");
420                            sb.append(tag);
421                            sb.append(">");
422                    }
423                    else {
424                            sb.append("<");
425                            sb.append(tag);
426                            sb.append(" style=\"");
427                            sb.append(listStyle);
428                            sb.append("\">");
429                    }
430    
431                    tags.push("</" + tag + ">");
432            }
433    
434            protected void handleListItem(StringBundler sb, Stack<String> tags) {
435                    handleSimpleTag(sb, tags, "li");
436            }
437    
438            protected String handleNewLine(
439                    List<BBCodeItem> bbCodeItems, Stack<String> tags, IntegerWrapper marker,
440                    String data) {
441    
442                    BBCodeItem bbCodeItem = null;
443    
444                    if ((marker.getValue() + 1) < bbCodeItems.size()) {
445                            if (data.matches("\\A\r?\n\\z")) {
446                                    bbCodeItem = bbCodeItems.get(marker.getValue() + 1);
447    
448                                    if (bbCodeItem != null) {
449                                            String value = bbCodeItem.getValue();
450    
451                                            if (_excludeNewLineTypes.containsKey(value)) {
452                                                    int type = bbCodeItem.getType();
453    
454                                                    int excludeNewLineType = _excludeNewLineTypes.get(
455                                                            value);
456    
457                                                    if ((type & excludeNewLineType) > 0) {
458                                                            data = StringPool.BLANK;
459                                                    }
460                                            }
461                                    }
462                            }
463                            else if (data.matches("(?s).*\r?\n\\z")) {
464                                    bbCodeItem = bbCodeItems.get(marker.getValue() + 1);
465    
466                                    if ((bbCodeItem != null) &&
467                                            (bbCodeItem.getType() == BBCodeParser.TYPE_TAG_END)) {
468    
469                                            String value = bbCodeItem.getValue();
470    
471                                            if (value.equals("*")) {
472                                                    data = data.substring(0, data.length() - 1);
473                                            }
474                                    }
475                            }
476                    }
477    
478                    if (data.length() > 0) {
479                            data = data.replaceAll("\r?\n", "<br />");
480                    }
481    
482                    return data;
483            }
484    
485            protected void handleQuote(
486                    StringBundler sb, Stack<String> tags, BBCodeItem bbCodeItem) {
487    
488                    String quote = bbCodeItem.getAttribute();
489    
490                    if ((quote != null) && (quote.length() > 0)) {
491                            sb.append("<div class=\"quote-title\">");
492                            sb.append(escapeQuote(quote));
493                            sb.append(":</div>");
494                    }
495    
496                    sb.append("<div class=\"quote\"><div class=\"quote-content\">");
497    
498                    tags.push("</div></div>");
499            }
500    
501            protected void handleSimpleTag(
502                    StringBundler sb, Stack<String> tags, BBCodeItem bbCodeItem) {
503    
504                    handleSimpleTag(sb, tags, bbCodeItem.getValue());
505            }
506    
507            protected void handleSimpleTag(
508                    StringBundler sb, Stack<String> tags, String tag) {
509    
510                    sb.append("<");
511                    sb.append(tag);
512                    sb.append(">");
513    
514                    tags.push("</" + tag + ">");
515            }
516    
517            protected void handleStrikeThrough(StringBundler sb, Stack<String> tags) {
518                    handleSimpleTag(sb, tags, "strike");
519            }
520    
521            protected void handleTable(StringBundler sb, Stack<String> tags) {
522                    handleSimpleTag(sb, tags, "table");
523            }
524    
525            protected void handleTableCell(StringBundler sb, Stack<String> tags) {
526                    handleSimpleTag(sb, tags, "td");
527            }
528    
529            protected void handleTableHeader(StringBundler sb, Stack<String> tags) {
530                    handleSimpleTag(sb, tags, "th");
531            }
532    
533            protected void handleTableRow(StringBundler sb, Stack<String> tags) {
534                    handleSimpleTag(sb, tags, "tr");
535            }
536    
537            protected void handleTagEnd(
538                    StringBundler sb, Stack<String> tags, BBCodeItem bbCodeItem) {
539    
540                    String tag = bbCodeItem.getValue();
541    
542                    if (isValidTag(tag)) {
543                            sb.append(tags.pop());
544                    }
545            }
546    
547            protected void handleTagStart(
548                    StringBundler sb, List<BBCodeItem> bbCodeItems, Stack<String> tags,
549                    IntegerWrapper marker, BBCodeItem bbCodeItem) {
550    
551                    String tag = bbCodeItem.getValue();
552    
553                    if (!isValidTag(tag)) {
554                            return;
555                    }
556    
557                    if (tag.equals("b")) {
558                            handleBold(sb, tags);
559                    }
560                    else if (tag.equals("center") || tag.equals("justify") ||
561                                     tag.equals("left") || tag.equals("right")) {
562    
563                            handleTextAlign(sb, tags, bbCodeItem);
564                    }
565                    else if (tag.equals("code")) {
566                            handleCode(sb, bbCodeItems, marker);
567                    }
568                    else if (tag.equals("color") || tag.equals("colour")) {
569                            handleColor(sb, tags, bbCodeItem);
570                    }
571                    else if (tag.equals("email")) {
572                            handleEmail(sb, bbCodeItems, tags, marker, bbCodeItem);
573                    }
574                    else if (tag.equals("font")) {
575                            handleFontFamily(sb, tags, bbCodeItem);
576                    }
577                    else if (tag.equals("i")) {
578                            handleItalic(sb, tags);
579                    }
580                    else if (tag.equals("img")) {
581                            handleImage(sb, bbCodeItems, marker);
582                    }
583                    else if (tag.equals("li") || tag.equals("*")) {
584                            handleListItem(sb, tags);
585                    }
586                    else if (tag.equals("list")) {
587                            handleList(sb, tags, bbCodeItem);
588                    }
589                    else if (tag.equals("q") || tag.equals("quote")) {
590                            handleQuote(sb, tags, bbCodeItem);
591                    }
592                    else if (tag.equals("s")) {
593                            handleStrikeThrough(sb, tags);
594                    }
595                    else if (tag.equals("size")) {
596                            handleFontSize(sb, tags, bbCodeItem);
597                    }
598                    else if (tag.equals("table")) {
599                            handleTable(sb, tags);
600                    }
601                    else if (tag.equals("td")) {
602                            handleTableCell(sb, tags);
603                    }
604                    else if (tag.equals("th")) {
605                            handleTableHeader(sb, tags);
606                    }
607                    else if (tag.equals("tr")) {
608                            handleTableRow(sb, tags);
609                    }
610                    else if (tag.equals("url")) {
611                            handleURL(sb, bbCodeItems, tags, marker, bbCodeItem);
612                    }
613                    else {
614                            handleSimpleTag(sb, tags, bbCodeItem);
615                    }
616            }
617    
618            protected void handleTextAlign(
619                    StringBundler sb, Stack<String> tags, BBCodeItem bbCodeItem) {
620    
621                    sb.append("<p style=\"text-align: ");
622                    sb.append(bbCodeItem.getValue());
623                    sb.append("\">");
624    
625                    tags.push("</p>");
626            }
627    
628            protected void handleURL(
629                    StringBundler sb, List<BBCodeItem> bbCodeItems, Stack<String> tags,
630                    IntegerWrapper marker, BBCodeItem bbCodeItem) {
631    
632                    sb.append("<a href=\"");
633    
634                    String href = bbCodeItem.getAttribute();
635    
636                    if (href == null) {
637                            href = extractData(
638                                    bbCodeItems, marker, "url", BBCodeParser.TYPE_DATA, false);
639                    }
640    
641                    Matcher matcher = _urlPattern.matcher(href);
642    
643                    if (matcher.matches()) {
644                            sb.append(HtmlUtil.escapeHREF(href));
645                    }
646    
647                    sb.append("\">");
648    
649                    tags.push("</a>");
650            }
651    
652            protected boolean isValidTag(String tag) {
653                    if ((tag != null) && (tag.length() > 0)) {
654                            Matcher matcher = _tagPattern.matcher(tag);
655    
656                            return matcher.matches();
657                    }
658    
659                    return false;
660            }
661    
662            private static final String[][] _EMOTICONS = {
663                    {"happy.gif", ":)", "happy"},
664                    {"smile.gif", ":D", "smile"},
665                    {"cool.gif", "B)", "cool"},
666                    {"sad.gif", ":(", "sad"},
667                    {"tongue.gif", ":P", "tongue"},
668                    {"laugh.gif", ":lol:", "laugh"},
669                    {"kiss.gif", ":#", "kiss"},
670                    {"blush.gif", ":*)", "blush"},
671                    {"bashful.gif", ":bashful:", "bashful"},
672                    {"smug.gif", ":smug:", "smug"},
673                    {"blink.gif", ":blink:", "blink"},
674                    {"huh.gif", ":huh:", "huh"},
675                    {"mellow.gif", ":mellow:", "mellow"},
676                    {"unsure.gif", ":unsure:", "unsure"},
677                    {"mad.gif", ":mad:", "mad"},
678                    {"oh_my.gif", ":O", "oh-my-goodness"},
679                    {"roll_eyes.gif", ":rolleyes:", "roll-eyes"},
680                    {"angry.gif", ":angry:", "angry"},
681                    {"suspicious.gif", "8o", "suspicious"},
682                    {"big_grin.gif", ":grin:", "grin"},
683                    {"in_love.gif", ":love:", "in-love"},
684                    {"bored.gif", ":bored:", "bored"},
685                    {"closed_eyes.gif", "-_-", "closed-eyes"},
686                    {"cold.gif", ":cold:", "cold"},
687                    {"sleep.gif", ":sleep:", "sleep"},
688                    {"glare.gif", ":glare:", "glare"},
689                    {"darth_vader.gif", ":vader:", "darth-vader"},
690                    {"dry.gif", ":dry:", "dry"},
691                    {"exclamation.gif", ":what:", "what"},
692                    {"girl.gif", ":girl:", "girl"},
693                    {"karate_kid.gif", ":kid:", "karate-kid"},
694                    {"ninja.gif", ":ph34r:", "ninja"},
695                    {"pac_man.gif", ":V", "pac-man"},
696                    {"wacko.gif", ":wacko:", "wacko"},
697                    {"wink.gif", ":wink:", "wink"},
698                    {"wub.gif", ":wub:", "wub"}
699            };
700    
701            private static Log _log = LogFactoryUtil.getLog(
702                    HtmlBBCodeTranslatorImpl.class);
703    
704            private Map<String, String> _bbCodeCharacters;
705            private BBCodeParser _bbCodeParser = new BBCodeParser();
706            private Pattern _bbCodePattern = Pattern.compile("[]&<>'\"`\\[()]");
707            private Pattern _colorPattern = Pattern.compile(
708                    "^(:?aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|purple" +
709                            "|red|silver|teal|white|yellow|#(?:[0-9a-f]{3})?[0-9a-f]{3})$",
710                    Pattern.CASE_INSENSITIVE);
711            private String[] _emoticonDescriptions = new String[_EMOTICONS.length];
712            private String[] _emoticonFiles = new String[_EMOTICONS.length];
713            private String[] _emoticonSymbols = new String[_EMOTICONS.length];
714            private Map<String, Integer> _excludeNewLineTypes;
715            private int[] _fontSizes = {10, 12, 16, 18, 24, 32, 48};
716            private Pattern _imagePattern = Pattern.compile(
717                    "^(?:https?://|/)[-;/?:@&=+$,_.!~*'()%0-9a-z]{1,512}$",
718                    Pattern.CASE_INSENSITIVE);
719            private Map<String, String> _listStyles;
720            private Pattern _tagPattern = Pattern.compile(
721                    "^/?(?:b|center|code|colou?r|email|i|img|justify|left|pre|q|quote|" +
722                            "right|\\*|s|size|table|tr|th|td|li|list|font|u|url)$",
723                    Pattern.CASE_INSENSITIVE);
724            private Pattern _urlPattern = Pattern.compile(
725                    "^[-;/?:@&=+$,_.!~*'()%0-9a-z#]{1,512}$", Pattern.CASE_INSENSITIVE);
726    
727    }