package com.aggrepoint.winlet.utils;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Supports syntax of BBCode editor: http://www.wysibb.com/
*
* @author: Yang Jiang Ming
*/
public class BBCode {
static final int TYPE_ROOT = 0;
static final int TYPE_TEXT = 1;
static final int TYPE_TAG_NESTED = 2;
static final int TYPE_TAG_SIMPLE = 3;
int type;
String text;
ArrayList<BBCode> content;
static String[] TO_REPLACE = {
"(?is)\\[\\s*img\\s*\\]([^\\[]*)\\[\\s*/\\s*img\\s*\\]",
"(?is)\\[\\s*url\\s*\\]([^\\[]*)\\[\\s*/\\s*url\\s*\\]",
"(?is)\\[\\s*list\\s*=\\s*1\\s*\\](.*)\\[\\s*/\\s*list\\s*\\]" };
static String[] REPLACE_TO = { "[img=$1][/img]", "[url=$1]$1[/url]",
"[olist]$1[/olist]" };
private BBCode(String str) {
this(TYPE_ROOT);
for (int i = 0; i < TO_REPLACE.length; i++)
str = str.replaceAll(TO_REPLACE[i], REPLACE_TO[i]);
parse(str.toCharArray(), new int[1]);
simplify();
}
private BBCode(int type) {
this.type = type;
if (type == TYPE_TEXT)
text = "";
else
content = new ArrayList<BBCode>();
}
private void addText(String text) {
if (text == null || "".equals(text))
return;
BBCode p = new BBCode(TYPE_TEXT);
p.text = text;
content.add(p);
}
private BBCode addTag() {
BBCode p = new BBCode(TYPE_TAG_NESTED);
content.add(p);
return p;
}
private void parse(char[] chars, int[] idx) {
StringBuffer sb = new StringBuffer();
while (idx[0] < chars.length) {
switch (chars[idx[0]]) {
case '[':
addText(sb.toString());
sb = new StringBuffer();
idx[0]++;
addTag().parse(chars, idx);
break;
case ']':
if (type == TYPE_ROOT)
sb.append(chars[idx[0]]);
else {
addText(sb.toString());
return;
}
break;
default:
sb.append(chars[idx[0]]);
break;
}
idx[0]++;
}
addText(sb.toString());
}
private void simplify() {
if (type != TYPE_TAG_NESTED && type != TYPE_ROOT)
return;
for (BBCode p : content) {
if (p.type == TYPE_TAG_NESTED) {
if (p.content.size() == 0) {
p.type = TYPE_TAG_SIMPLE;
p.text = "";
} else if (p.content.size() == 1
&& p.content.get(0).type == TYPE_TEXT) {
p.type = TYPE_TAG_SIMPLE;
p.text = p.content.get(0).text;
} else
p.simplify();
}
}
}
static final String BRACKET_ST = EncodeUtils.html("[");
static final String BRACKET_ED = EncodeUtils.html("]");
static final String[][] MAP = {
// [center]
{ "(?i)^\\s*center\\s*$", "<p style=\"text-align:center\">" },
// [/center]
{ "(?i)^\\s*/\\s*center\\s*$", "</p>" },
// [left]
{ "(?i)^\\s*left\\s*$", "<p style=\"text-align:left\">" },
// [/left]
{ "(?i)^\\s*/\\s*left\\s*$", "</p>" },
// [right]
{ "(?i)^\\s*right\\s*$", "<p style=\"text-align:right\">" },
// [/right]
{ "(?i)^\\s*/\\s*right\\s*$", "</p>" },
// [size=?]
{ "(?i)^\\s*size\\s*=\\s*([\\d\\w]+)\\s*$",
"<span style=\"font-size: $1%\">" },
// [/size]
{ "(?i)^\\s*/\\s*size\\s*$", "</span>" },
// [font=?]
{ "(?i)^\\s*font\\s*=\\s*([\\w\\s]+)\\s*$",
"<span style=\"font-family: $1\">" },
// [/font]
{ "(?i)^\\s*/\\s*font\\s*$", "</span>" },
// [b]
{ "(?i)^\\s*b\\s*$", "<b>" },
// [/b]
{ "(?i)^\\s*/\\s*b\\s*$", "</b>" },
// [i]
{ "(?i)^\\s*i\\s*$", "<i>" },
// [/i]
{ "(?i)^\\s*/\\s*i\\s*$", "</i>" },
// [u]
{ "(?i)^\\s*u\\s*$", "<u>" },
// [/u]
{ "(?i)^\\s*/\\s*u\\s*$", "</u>" },
// [s]
{ "(?i)^\\s*s\\s*$", "<strike>" },
// [/s]
{ "(?i)^\\s*/\\s*s\\s*$", "</strike>" },
// [sup]
{ "(?i)^\\s*sup\\s*$", "<sup>" },
// [/sup]
{ "(?i)^\\s*/\\s*sup\\s*$", "</sup>" },
// [sub]
{ "(?i)^\\s*sub\\s*$", "<sub>" },
// [/sub]
{ "(?i)^\\s*/\\s*sub\\s*$", "</sub>" },
// [quote]
{ "(?i)^\\s*quote\\s*$", "<blockquote>" },
// [/quote]
{ "(?i)^\\s*/\\s*quote\\s*$", "</blockquote>" },
// [list]
{ "(?i)^\\s*list\\s*$", "<ul>" },
// [/list]
{ "(?i)^\\s*/\\s*list\\s*$", "</ul>" },
// [olist]
{ "(?i)^\\s*olist\\s*$", "<ol>" },
// [/olist]
{ "(?i)^\\s*/\\s*olist\\s*$", "</ol>" },
// [*]
{ "(?i)^\\s*\\*\\s*$", "<li>" },
// [/*]
{ "(?i)^\\s*/\\s*\\*\\s*$", "</li>" },
// [code]
{ "(?i)^\\s*code\\s*$", "<pre>" },
// [/code]
{ "(?i)^\\s*/\\s*code\\s*$", "</pre>" },
// [table]
{ "(?i)^\\s*table\\s*$", "<table>" },
// [/table]
{ "(?i)^\\s*/\\s*table\\s*$", "</table>" },
// [tr]
{ "(?i)^\\s*tr\\s*$", "<tr>" },
// [/tr]
{ "(?i)^\\s*/\\s*tr\\s*$", "</tr>" },
// [td]
{ "(?i)^\\s*td\\s*$", "<td>" },
// [/td]
{ "(?i)^\\s*/\\s*td\\s*$", "</td>" },
// [color=?]
{ "(?i)^\\s*color\\s*=\\s*(\\S+)\\s*$", "<font color=\"$1\">" },
// [/color]
{ "(?i)^\\s*/\\s*color\\s*$", "</font>" },
// [url=?]
{
"(?i)^\\s*url\\s*=\\s*((ht|f)tp(s?)\\:\\/\\/[0-9a-zA-Z]([-.\\w]*[0-9a-zA-Z])*(:(0-9)*)*(\\/?)([a-zA-Z0-9\\-\\.\\?\\,\\:\\/\\\\\\+=&%\\$#_]*)?)\\s*$",
"<a href=\"$1\" target=\"_blank\">" },
// [/url]
{ "(?i)^\\s*/\\s*url\\s*$", "</a>" },
// [img=?]
{
"(?i)^\\s*img\\s*=\\s*((ht|f)tp(s?)\\:\\/\\/[0-9a-zA-Z]([-.\\w]*[0-9a-zA-Z])*(:(0-9)*)*(\\/?)([a-zA-Z0-9\\-\\.\\?\\,\\:\\/\\\\\\+=&%\\$#_]*)?)\\s*$",
"<img src=\"$1\" />" },
// [/img]
{ "(?i)^\\s*/\\s*img\\s*$", "" } };
static final Pattern[] BBC_TAGS;
static {
BBC_TAGS = new Pattern[MAP.length];
for (int i = 0; i < MAP.length; i++)
BBC_TAGS[i] = Pattern.compile(MAP[i][0]);
}
static final String[][] DISPLAY_FORMAT = { { "\\
\\
", "<br />" },
{ "\\
", "<br />" }, { "\\
", "<br />" } };
static String displayFormat(String str) {
for (int i = 0; i < DISPLAY_FORMAT.length; i++)
str = str.replaceAll(DISPLAY_FORMAT[i][0], DISPLAY_FORMAT[i][1]);
return str;
}
private void toHtml(StringBuffer sb) {
switch (type) {
case TYPE_ROOT:
for (BBCode p : content)
p.toHtml(sb);
break;
case TYPE_TEXT:
sb.append(displayFormat(EncodeUtils.html(text)));
break;
case TYPE_TAG_SIMPLE:
int i = 0;
for (; i < BBC_TAGS.length; i++) {
Matcher m = BBC_TAGS[i].matcher(text);
if (m.find()) {
sb.append(m.replaceAll(MAP[i][1]));
break;
}
}
if (i < BBC_TAGS.length)
break;
sb.append(BRACKET_ST);
sb.append(displayFormat(EncodeUtils.html(text)));
sb.append(BRACKET_ED);
break;
case TYPE_TAG_NESTED:
sb.append(BRACKET_ST);
for (BBCode p : content)
p.toHtml(sb);
sb.append(BRACKET_ED);
break;
}
}
public static String toHtml(String bbcode) {
BBCode code = new BBCode(bbcode);
StringBuffer sb = new StringBuffer();
code.toHtml(sb);
return sb.toString();
}
}