1
22
23 package com.liferay.portlet.messageboards.util;
24
25 import com.liferay.portal.kernel.util.GetterUtil;
26 import com.liferay.portal.kernel.util.HtmlUtil;
27 import com.liferay.portal.kernel.util.StringPool;
28 import com.liferay.portal.kernel.util.StringUtil;
29
30 import java.util.ArrayList;
31 import java.util.HashMap;
32 import java.util.List;
33 import java.util.Map;
34
35 import org.apache.commons.logging.Log;
36 import org.apache.commons.logging.LogFactory;
37
38
44 public class BBCodeUtil {
45
46 static Map<Integer, String> fontSizes = new HashMap<Integer, String>();
47
48 static Map<String, String> listStyles = new HashMap<String, String>();
49
50 static String[][] emoticons = {
51 {"angry.gif", ":angry:"},
52 {"bashful.gif", ":bashful:"},
53 {"big_grin.gif", ":grin:"},
54 {"blink.gif", ":blink:"},
55 {"blush.gif", ":*)"},
56 {"bored.gif", ":bored:"},
57 {"closed_eyes.gif", "-_-"},
58 {"cold.gif", ":cold:"},
59 {"cool.gif", "B)"},
60 {"darth_vader.gif", ":vader:"},
61 {"dry.gif", "<_<"},
62 {"exclamation.gif", ":what:"},
63 {"girl.gif", ":girl:"},
64 {"glare.gif", ">_>"},
65 {"happy.gif", ":)"},
66 {"huh.gif", ":huh:"},
67 {"in_love.gif", "<3"},
68 {"karate_kid.gif", ":kid:"},
69 {"kiss.gif", ":#"},
70 {"laugh.gif", ":lol:"},
71 {"mad.gif", ":mad:"},
72 {"mellow.gif", ":mellow:"},
73 {"ninja.gif", ":ph34r:"},
74 {"oh_my.gif", ":O"},
75 {"pac_man.gif", ":V"},
76 {"roll_eyes.gif", ":rolleyes:"},
77 {"sad.gif", ":("},
78 {"sleep.gif", ":sleep:"},
79 {"smile.gif", ":D"},
80 {"smug.gif", ":smug:"},
81 {"suspicious.gif", "8o"},
82 {"tongue.gif", ":P"},
83 {"unsure.gif", ":unsure:"},
84 {"wacko.gif", ":wacko:"},
85 {"wink.gif", ":wink:"},
86 {"wub.gif", ":wub:"}
87 };
88
89 static {
90 fontSizes.put(new Integer(1), "<span style='font-size: 0.7em';>");
91 fontSizes.put(new Integer(2), "<span style='font-size: 0.8em';>");
92 fontSizes.put(new Integer(3), "<span style='font-size: 0.9em';>");
93 fontSizes.put(new Integer(4), "<span style='font-size: 1.0em';>");
94 fontSizes.put(new Integer(5), "<span style='font-size: 1.1em';>");
95 fontSizes.put(new Integer(6), "<span style='font-size: 1.3em';>");
96 fontSizes.put(new Integer(7), "<span style='font-size: 1.5em';>");
97
98 listStyles.put("1", "<ol style='list-style-type: decimal';>");
99 listStyles.put("i", "<ol style='list-style-type: lower-roman';>");
100 listStyles.put("I", "<ol style='list-style-type: upper-roman';>");
101 listStyles.put("a", "<ol style='list-style-type: lower-alpha';>");
102 listStyles.put("A", "<ol style='list-style-type: upper-alpha';>");
103
104 for (int i = 0; i < emoticons.length; i++) {
105 String[] emoticon = emoticons[i];
106
107 String image = emoticon[0];
108 String code = emoticon[1];
109
110 emoticon[0] =
111 "<img alt='emoticon' src='@theme_images_path@/emoticons/" +
112 image + "' />";
113 emoticon[1] = HtmlUtil.escape(code);
114 }
115 }
116
117 public static final String[][] EMOTICONS = emoticons;
118
119 public static String getHTML(String bbcode) {
120 String html = HtmlUtil.escape(bbcode);
121
122 html = StringUtil.replace(html, _BBCODE_TAGS, _HTML_TAGS);
123
124 for (int i = 0; i < emoticons.length; i++) {
125 String[] emoticon = emoticons[i];
126
127 html = StringUtil.replace(html, emoticon[1], emoticon[0]);
128 }
129
130 BBCodeTag tag = null;
131
132 StringBuilder sb = null;
133
134 while ((tag = getFirstTag(html, "code")) != null) {
135 String preTag = html.substring(0, tag.getStartPos());
136 String postTag = html.substring(tag.getEndPos());
137
138 String code = tag.getElement().replaceAll(
139 "\t", StringPool.FOUR_SPACES);
140 String[] lines = code.split("\\n");
141 int digits = String.valueOf(lines.length + 1).length();
142
143 sb = new StringBuilder(preTag);
144
145 sb.append("<div class='code'>");
146
147 for (int i = 0; i < lines.length; i++) {
148 String index = String.valueOf(i + 1);
149 int ld = index.length();
150
151 sb.append("<span class='code-lines'>");
152
153 for (int j = 0; j < digits - ld; j++) {
154 sb.append(" ");
155 }
156
157 lines[i] = StringUtil.replace(lines[i], " ",
158 StringPool.NBSP + StringPool.SPACE + StringPool.NBSP);
159 lines[i] = StringUtil.replace(lines[i], " ",
160 StringPool.NBSP + StringPool.SPACE);
161
162 sb.append(index + "</span>");
163 sb.append(lines[i]);
164
165 if (index.length() < lines.length) {
166 sb.append("<br />");
167 }
168 }
169
170 sb.append("</div>");
171 sb.append(postTag);
172
173 html = sb.toString();
174 }
175
176 while ((tag = getFirstTag(html, "color")) != null) {
177 String preTag = html.substring(0, tag.getStartPos());
178 String postTag = html.substring(tag.getEndPos());
179
180 sb = new StringBuilder(preTag);
181
182 if (tag.hasParameter()) {
183 sb.append("<span style='color: ");
184 sb.append(tag.getParameter() + ";'>");
185 sb.append(tag.getElement() + "</span>");
186 }
187 else {
188 sb.append(tag.getElement());
189 }
190
191 sb.append(postTag);
192
193 html = sb.toString();
194 }
195
196 while ((tag = getFirstTag(html, "email")) != null) {
197 String preTag = html.substring(0, tag.getStartPos());
198 String postTag = html.substring(tag.getEndPos());
199
200 String mailto = GetterUtil.getString(
201 tag.getParameter(), tag.getElement().trim());
202
203 sb = new StringBuilder(preTag);
204
205 sb.append("<a href='mailto: " + mailto + "'>");
206 sb.append(tag.getElement() + "</a>");
207 sb.append(postTag);
208
209 html = sb.toString();
210 }
211
212 while ((tag = getFirstTag(html, "font")) != null) {
213 String preTag = html.substring(0, tag.getStartPos());
214 String postTag = html.substring(tag.getEndPos());
215
216 sb = new StringBuilder(preTag);
217
218 if (tag.hasParameter()) {
219 sb.append("<span style='font-family: ");
220 sb.append(tag.getParameter() + "';>");
221 sb.append(tag.getElement() + "</span>");
222 }
223 else {
224 sb.append(tag.getElement());
225 }
226
227 sb.append(postTag);
228
229 html = sb.toString();
230 }
231
232 while ((tag = getFirstTag(html, "img")) != null) {
233 String preTag = html.substring(0, tag.getStartPos());
234 String postTag = html.substring(tag.getEndPos());
235
236 sb = new StringBuilder(preTag);
237
238 sb.append("<img alt='' src='" + tag.getElement().trim() + "' />");
239 sb.append(postTag);
240
241 html = sb.toString();
242 }
243
244 while ((tag = getFirstTag(html, "list")) != null) {
245 String preTag = html.substring(0, tag.getStartPos());
246 String postTag = html.substring(tag.getEndPos());
247
248 String[] items = _getListItems(tag.getElement());
249
250 sb = new StringBuilder(preTag);
251
252 if (tag.hasParameter() &&
253 listStyles.containsKey(tag.getParameter())) {
254
255 sb.append(listStyles.get(tag.getParameter()));
256
257 for (int i = 0; i < items.length; i++) {
258 if (items[i].trim().length() > 0) {
259 sb.append("<li>" + items[i].trim() + "</li>");
260 }
261 }
262
263 sb.append("</ol>");
264 }
265 else {
266 sb.append("<ul style='list-style-type: disc';>");
267
268 for (int i = 0; i < items.length; i++) {
269 if (items[i].trim().length() > 0) {
270 sb.append("<li>" + items[i].trim() + "</li>");
271 }
272 }
273
274 sb.append("</ul>");
275 }
276
277 sb.append(postTag);
278
279 html = sb.toString();
280 }
281
282 while ((tag = getFirstTag(html, "quote")) != null) {
283 String preTag = html.substring(0, tag.getStartPos());
284 String postTag = html.substring(tag.getEndPos());
285
286 sb = new StringBuilder(preTag);
287
288 if (tag.hasParameter()) {
289 sb.append("<div class='quote-title'>");
290 sb.append(tag.getParameter() + ":</div>");
291 }
292
293 sb.append("<div class='quote'>");
294 sb.append("<div class='quote-content'>");
295 sb.append(tag.getElement());
296 sb.append("</div></div>");
297 sb.append(postTag);
298
299 html = sb.toString();
300 }
301
302 while ((tag = getFirstTag(html, "size")) != null) {
303 String preTag = html.substring(0, tag.getStartPos());
304 String postTag = html.substring(tag.getEndPos());
305
306 sb = new StringBuilder(preTag);
307
308 if (tag.hasParameter()) {
309 Integer size = new Integer(
310 GetterUtil.getInteger(tag.getParameter()));
311
312 if (size.intValue() > 7) {
313 size = new Integer(7);
314 }
315
316 if (fontSizes.containsKey(size)) {
317 sb.append(fontSizes.get(size));
318 sb.append(tag.getElement() + "</span>");
319 }
320 else {
321 sb.append(tag.getElement());
322 }
323 }
324 else {
325 sb.append(tag.getElement());
326 }
327
328 sb.append(postTag);
329
330 html = sb.toString();
331 }
332
333 while ((tag = getFirstTag(html, "url")) != null) {
334 String preTag = html.substring(0, tag.getStartPos());
335 String postTag = html.substring(tag.getEndPos());
336
337 String url = GetterUtil.getString(
338 tag.getParameter(), tag.getElement().trim());
339
340 sb = new StringBuilder(preTag);
341
342 sb.append("<a href='" + url + "'>");
343 sb.append(tag.getElement() + "</a>");
344 sb.append(postTag);
345
346 html = sb.toString();
347 }
348
349 html = StringUtil.replace(html, "\n", "<br />");
350
351 return html;
352 }
353
354 public static BBCodeTag getFirstTag(String bbcode, String name) {
355 BBCodeTag tag = new BBCodeTag();
356
357 String begTag = "[" + name;
358 String endTag = "[/" + name + "]";
359
360 String preTag = StringUtil.extractFirst(bbcode, begTag);
361
362 if (preTag == null) {
363 return null;
364 }
365
366 if (preTag.length() != bbcode.length()) {
367 tag.setStartPos(preTag.length());
368
369 String remainder = bbcode.substring(
370 preTag.length() + begTag.length());
371
372 int cb = remainder.indexOf("]");
373 int end = _getEndTagPos(remainder, begTag, endTag);
374
375 if (cb > 0 && remainder.startsWith("=")) {
376 tag.setParameter(remainder.substring(1, cb));
377 tag.setElement(remainder.substring(cb + 1, end));
378 }
379 else if (cb == 0) {
380 try {
381 tag.setElement(remainder.substring(1, end));
382 }
383 catch (StringIndexOutOfBoundsException sioobe) {
384 _log.error(bbcode);
385
386 throw sioobe;
387 }
388 }
389 }
390
391 if (tag.hasElement()) {
392 int length =
393 begTag.length() + 1 + tag.getElement().length() +
394 endTag.length();
395
396 if (tag.hasParameter()) {
397 length += 1 + tag.getParameter().length();
398 }
399
400 tag.setEndPos(tag.getStartPos() + length);
401
402 return tag;
403 }
404
405 return null;
406 }
407
408 private static int _getEndTagPos(
409 String remainder, String begTag, String endTag) {
410
411 int nextBegTagPos = remainder.indexOf(begTag);
412 int nextEndTagPos = remainder.indexOf(endTag);
413
414 while ((nextBegTagPos < nextEndTagPos) && (nextBegTagPos >= 0)) {
415 nextBegTagPos = remainder.indexOf(
416 begTag, nextBegTagPos + begTag.length());
417 nextEndTagPos = remainder.indexOf(
418 endTag, nextEndTagPos + endTag.length());
419 }
420
421 return nextEndTagPos;
422 }
423
424 private static String[] _getListItems(String tagElement) {
425 List<String> items = new ArrayList<String>();
426
427 StringBuilder sb = new StringBuilder();
428
429 int nestLevel = 0;
430
431 for (String item : StringUtil.split(tagElement, "[*]")) {
432 item = item.trim();
433
434 if (item.length() == 0) {
435 continue;
436 }
437
438 int begTagCount = StringUtil.count(item, "[list");
439
440 if (begTagCount > 0) {
441 nestLevel += begTagCount;
442 }
443
444 int endTagCount = StringUtil.count(item, "[/list]");
445
446 if (endTagCount > 0) {
447 nestLevel -= endTagCount;
448 }
449
450 if (nestLevel == 0) {
451 if ((begTagCount == 0) && (endTagCount == 0)) {
452 items.add(item);
453 }
454 else if (endTagCount > 0) {
455 if (sb.length() > 0) {
456 sb.append("[*]");
457 }
458
459 sb.append(item);
460
461 items.add(sb.toString());
462
463 sb.delete(0, sb.length());
464 }
465 }
466 else {
467 if (sb.length() > 0) {
468 sb.append("[*]");
469 }
470
471 sb.append(item);
472 }
473 }
474
475 return items.toArray(new String[items.size()]);
476 }
477
478 private static final String[] _BBCODE_TAGS = {
479 "[b]", "[/b]", "[i]", "[/i]", "[u]", "[/u]", "[s]", "[/s]",
480 "[img]", "[/img]",
481 "[left]", "[center]", "[right]", "[indent]",
482 "[/left]", "[/center]", "[/right]", "[/indent]", "[tt]", "[/tt]"
483 };
484
485 private static final String[] _HTML_TAGS = {
486 "<b>", "</b>", "<i>", "</i>", "<u>", "</u>", "<strike>", "</strike>",
487 "<img alt='' src='", "' />",
488 "<div style='text-align: left'>", "<div style='text-align: center'>",
489 "<div style='text-align: right'>", "<div style='margin-left: 15px'>",
490 "</div>", "</div>", "</div>", "</div>", "<tt>", "</tt>"
491 };
492
493 private static Log _log = LogFactory.getLog(BBCodeUtil.class);
494
495 }