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