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