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