1   /**
2    * Copyright (c) 2000-2008 Liferay, Inc. All rights reserved.
3    *
4    * Permission is hereby granted, free of charge, to any person obtaining a copy
5    * of this software and associated documentation files (the "Software"), to deal
6    * in the Software without restriction, including without limitation the rights
7    * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8    * copies of the Software, and to permit persons to whom the Software is
9    * furnished to do so, subject to the following conditions:
10   *
11   * The above copyright notice and this permission notice shall be included in
12   * all copies or substantial portions of the Software.
13   *
14   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20   * SOFTWARE.
21   */
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.StringMaker;
28  import com.liferay.portal.kernel.util.StringPool;
29  import com.liferay.portal.kernel.util.StringUtil;
30  
31  import java.util.HashMap;
32  import java.util.Map;
33  
34  import org.apache.commons.logging.Log;
35  import org.apache.commons.logging.LogFactory;
36  
37  /**
38   * <a href="BBCodeUtil.java.html"><b><i>View Source</i></b></a>
39   *
40   * @author Alexander Chow
41   *
42   */
43  public class BBCodeUtil {
44  
45      static Map<Integer, String> fontSizes = new HashMap<Integer, String>();
46  
47      static Map<String, String> listStyles = new HashMap<String, String>();
48  
49      static String[][] emoticons = {
50          {"angry.gif", ":angry:"},
51          {"bashful.gif", ":bashful:"},
52          {"big_grin.gif", ":grin:"},
53          {"blink.gif", ":blink:"},
54          {"blush.gif", ":*)"},
55          {"bored.gif", ":bored:"},
56          {"closed_eyes.gif", "-_-"},
57          {"cold.gif", ":cold:"},
58          {"cool.gif", "B)"},
59          {"darth_vader.gif", ":vader:"},
60          {"dry.gif", "<_<"},
61          {"exclamation.gif", ":what:"},
62          {"girl.gif", ":girl:"},
63          {"glare.gif", ">_>"},
64          {"happy.gif", ":)"},
65          {"huh.gif", ":huh:"},
66          {"in_love.gif", "<3"},
67          {"karate_kid.gif", ":kid:"},
68          {"kiss.gif", ":#"},
69          {"laugh.gif", ":lol:"},
70          {"mad.gif", ":mad:"},
71          {"mellow.gif", ":mellow:"},
72          {"ninja.gif", ":ph34r:"},
73          {"oh_my.gif", ":O"},
74          {"pac_man.gif", ":V"},
75          {"roll_eyes.gif", ":rolleyes:"},
76          {"sad.gif", ":("},
77          {"sleep.gif", ":sleep:"},
78          {"smile.gif", ":D"},
79          {"smug.gif", ":smug:"},
80          {"suspicious.gif", "8o"},
81          {"tongue.gif", ":P"},
82          {"unsure.gif", ":unsure:"},
83          {"wacko.gif", ":wacko:"},
84          {"wink.gif", ":wink:"},
85          {"wub.gif", ":wub:"}
86      };
87  
88      static {
89          fontSizes.put(new Integer(1), "<span style='font-size: 0.7em';>");
90          fontSizes.put(new Integer(2), "<span style='font-size: 0.8em';>");
91          fontSizes.put(new Integer(3), "<span style='font-size: 0.9em';>");
92          fontSizes.put(new Integer(4), "<span style='font-size: 1.0em';>");
93          fontSizes.put(new Integer(5), "<span style='font-size: 1.1em';>");
94          fontSizes.put(new Integer(6), "<span style='font-size: 1.3em';>");
95          fontSizes.put(new Integer(7), "<span style='font-size: 1.5em';>");
96  
97          listStyles.put("1", "<ol style='list-style-type: decimal';>");
98          listStyles.put("i", "<ol style='list-style-type: lower-roman';>");
99          listStyles.put("I", "<ol style='list-style-type: upper-roman';>");
100         listStyles.put("a", "<ol style='list-style-type: lower-alpha';>");
101         listStyles.put("A", "<ol style='list-style-type: upper-alpha';>");
102 
103         for (int i = 0; i < emoticons.length; i++) {
104             String[] emoticon = emoticons[i];
105 
106             String image = emoticon[0];
107             String code = emoticon[1];
108 
109             emoticon[0] =
110                 "<img src='@theme_images_path@/emoticons/" + image + "' />";
111             emoticon[1] = HtmlUtil.escape(code);
112         }
113     }
114 
115     public static final String[][] EMOTICONS = emoticons;
116 
117     public static String getHTML(String bbcode) {
118         String html = HtmlUtil.escape(bbcode);
119 
120         html = StringUtil.replace(html, _BBCODE_TAGS, _HTML_TAGS);
121 
122         for (int i = 0; i < emoticons.length; i++) {
123             String[] emoticon = emoticons[i];
124 
125             html = StringUtil.replace(html, emoticon[1], emoticon[0]);
126         }
127 
128         BBCodeTag tag = null;
129 
130         StringMaker sm = null;
131 
132         while ((tag = getFirstTag(html, "code")) != null) {
133             String preTag = html.substring(0, tag.getStartPos());
134             String postTag = html.substring(tag.getEndPos());
135 
136             String code = tag.getElement().replaceAll("\t", "    ");
137             String[] lines = code.split("\\n");
138             int digits = Integer.toString(lines.length + 1).length();
139 
140             sm = new StringMaker(preTag);
141 
142             sm.append("<div class='code'>");
143 
144             for (int i = 0; i < lines.length; i++) {
145                 String index = Integer.toString(i + 1);
146                 int ld = index.length();
147 
148                 sm.append("<span class='code-lines'>");
149 
150                 for (int j = 0; j < digits - ld; j++) {
151                     sm.append("&nbsp;");
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                 sm.append(index + "</span>");
160                 sm.append(lines[i]);
161 
162                 if (index.length() < lines.length) {
163                     sm.append("<br />");
164                 }
165             }
166 
167             sm.append("</div>");
168             sm.append(postTag);
169 
170             html = sm.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             sm = new StringMaker(preTag);
178 
179             if (tag.hasParameter()) {
180                 sm.append("<span style='color: ");
181                 sm.append(tag.getParameter() + ";'>");
182                 sm.append(tag.getElement() + "</span>");
183             }
184             else {
185                 sm.append(tag.getElement());
186             }
187 
188             sm.append(postTag);
189 
190             html = sm.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             sm = new StringMaker(preTag);
201 
202             sm.append("<a href='mailto: " + mailto + "'>");
203             sm.append(tag.getElement() + "</a>");
204             sm.append(postTag);
205 
206             html = sm.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             sm = new StringMaker(preTag);
214 
215             if (tag.hasParameter()) {
216                 sm.append("<span style='font-family: ");
217                 sm.append(tag.getParameter() + "';>");
218                 sm.append(tag.getElement() + "</span>");
219             }
220             else {
221                 sm.append(tag.getElement());
222             }
223 
224             sm.append(postTag);
225 
226             html = sm.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             sm = new StringMaker(preTag);
234 
235             sm.append("<img src='" + tag.getElement().trim() + "' />");
236             sm.append(postTag);
237 
238             html = sm.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 = StringUtil.split(tag.getElement(), "[*]");
246 
247             sm = new StringMaker(preTag);
248 
249             if (tag.hasParameter() &&
250                 listStyles.containsKey(tag.getParameter())) {
251 
252                 sm.append(listStyles.get(tag.getParameter()));
253 
254                 for (int i = 0; i < items.length; i++) {
255                     if (items[i].trim().length() > 0) {
256                         sm.append("<li>" + items[i].trim() + "</li>");
257                     }
258                 }
259 
260                 sm.append("</ol>");
261             }
262             else {
263                 sm.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                         sm.append("<li>" + items[i].trim() + "</li>");
268                     }
269                 }
270 
271                 sm.append("</ul>");
272             }
273 
274             sm.append(postTag);
275 
276             html = sm.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             sm = new StringMaker(preTag);
284 
285             if (tag.hasParameter()) {
286                 sm.append("<div class='quote-title'>");
287                 sm.append(tag.getParameter() + ":</div>");
288             }
289 
290             sm.append("<div class='quote'>");
291             sm.append("<div class='quote-content'>");
292             sm.append(tag.getElement());
293             sm.append("</div></div>");
294             sm.append(postTag);
295 
296             html = sm.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             sm = new StringMaker(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                     sm.append(fontSizes.get(size));
315                     sm.append(tag.getElement() + "</span>");
316                 }
317                 else {
318                     sm.append(tag.getElement());
319                 }
320             }
321             else {
322                 sm.append(tag.getElement());
323             }
324 
325             sm.append(postTag);
326 
327             html = sm.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             sm = new StringMaker(preTag);
338 
339             sm.append("<a href='" + url + "'>");
340             sm.append(tag.getElement() + "</a>");
341             sm.append(postTag);
342 
343             html = sm.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 = remainder.indexOf(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 final String[] _BBCODE_TAGS = {
406         "[b]", "[/b]", "[i]", "[/i]", "[u]", "[/u]", "[s]", "[/s]",
407         "[img]", "[/img]",
408         "[left]", "[center]", "[right]", "[indent]",
409         "[/left]", "[/center]", "[/right]", "[/indent]", "[tt]", "[/tt]"
410     };
411 
412     private static final String[] _HTML_TAGS = {
413         "<b>", "</b>", "<i>", "</i>", "<u>", "</u>", "<strike>", "</strike>",
414         "<img src='", "' />",
415         "<div style='text-align: left'>", "<div style='text-align: center'>",
416         "<div style='text-align: right'>", "<div style='margin-left: 15px'>",
417         "</div>", "</div>", "</div>", "</div>", "<tt>", "</tt>"
418     };
419 
420     private static Log _log = LogFactory.getLog(BBCodeUtil.class);
421 
422 }