KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > portal > format > parser > bbcode > BBCodeParser


1 /*****************************************
2  * *
3  * JBoss Portal: The OpenSource Portal *
4  * *
5  * Distributable under LGPL license. *
6  * See terms of license at gnu.org. *
7  * *
8  *****************************************/

9
10 package org.jboss.portal.format.parser.bbcode;
11
12 import java.io.IOException JavaDoc;
13 import java.io.Reader JavaDoc;
14 import java.util.Iterator JavaDoc;
15
16 import org.jboss.portal.format.parser.AbstractParser;
17 import org.jboss.portal.format.parser.ParseEvent;
18 import org.jboss.portal.format.parser.TextEvent;
19 import org.jboss.portal.format.parser.Token;
20 import org.jboss.portal.format.parser.chars.MutableChars;
21 import org.jboss.portal.format.util.Stack;
22
23 /**
24  * This nasty class parse BB code and create events.
25  *
26  * @author <a HREF="mailto:julien@jboss.org">Julien Viet</a>
27  * @version $Revision: 1.2 $
28  */

29 public class BBCodeParser
30    extends AbstractParser
31 {
32
33    // Public constants
34

35    public static final int EVENT_NORMAL = 0;
36    public static final int EVENT_BOLD = 1;
37    public static final int EVENT_ITALIC = 2;
38    public static final int EVENT_UNDERLINE = 3;
39    public static final int EVENT_COLOR = 4;
40    public static final int EVENT_SIZE = 5;
41    public static final int EVENT_QUOTE = 6;
42    public static final int EVENT_CODE = 7;
43    public static final int EVENT_ITEM = 8;
44    public static final int EVENT_LINK = 9;
45    public static final int EVENT_UNORDERED_LIST = 10;
46    public static final int EVENT_ALPHABETICALLY_ORDERED_LIST = 11;
47    public static final int EVENT_NUMERICALLY_ORDERED_LIST = 12;
48
49    // The parser state
50

51    public static final Reader JavaDoc NULL_READER = new Reader JavaDoc()
52    {
53       public int read(char cbuf[], int off, int len) throws IOException JavaDoc
54       {
55          return 0;
56       }
57       public void close() throws IOException JavaDoc
58       {
59       }
60    };
61
62    private Analyzer analyzer = new Analyzer(BBCodeParser.NULL_READER);
63    private CodeKey myKey = new CodeKey();
64    private MutableChars buffer = new MutableChars();
65    private TextEvent textEvent = new TextEvent();
66    private OpenEvent openEvent = new OpenEvent();
67
68    private Stack stack = new Stack(10)
69    {
70       protected Stack.Key createKey()
71       {
72          return new CloseEvent();
73       }
74       protected boolean equals(Stack.Key key1, Stack.Key key2)
75       {
76          return ((CodeKey)key1).getType() == ((CodeKey)key2).getType();
77       }
78    };
79
80    public BBCodeParser()
81    {
82    }
83
84    public void parse(char[] chars, int offset, int length)
85    {
86       // First we initialize the parser state
87
stack.reset();
88       analyzer.reset(chars, offset, length);
89       buffer.reset();
90
91       // First
92
_start(EVENT_NORMAL, null);
93
94       // Enter the switch loop
95
while (true)
96       {
97          // Get the next token
98
Token t = analyzer.next();
99
100          if (t == null)
101          {
102             // No more tokens to read
103
break;
104          }
105
106          // According to the token we fire the approriate events
107
switch (t.type)
108          {
109          case Analyzer.OPEN_B:
110             _start(EVENT_BOLD, null);
111             break;
112          case Analyzer.CLOSE_B:
113             _end(EVENT_BOLD);
114             break;
115
116          case Analyzer.OPEN_I:
117             _start(EVENT_ITALIC, null);
118             break;
119          case Analyzer.CLOSE_I:
120             _end(EVENT_ITALIC);
121             break;
122
123          case Analyzer.OPEN_U:
124             _start(EVENT_UNDERLINE, null);
125             break;
126          case Analyzer.CLOSE_U:
127             _end(EVENT_UNDERLINE);
128             break;
129
130          case Analyzer.OPEN_COLOR:
131             _start(EVENT_COLOR, t.value);
132             break;
133          case Analyzer.CLOSE_COLOR:
134             _end(EVENT_COLOR);
135             break;
136
137          case Analyzer.OPEN_SIZE:
138             _start(EVENT_SIZE, t.value);
139             break;
140          case Analyzer.CLOSE_SIZE:
141             _end(EVENT_SIZE);
142             break;
143
144          case Analyzer.OPEN_QUOTE_ANONYMOUS:
145             _start(EVENT_QUOTE, null);
146             break;
147          case Analyzer.OPEN_QUOTE:
148             _start(EVENT_QUOTE, t.value);
149             break;
150          case Analyzer.CLOSE_QUOTE:
151             _end(EVENT_QUOTE);
152             break;
153
154          case Analyzer.OPEN_CODE:
155             _start(EVENT_CODE, null);
156             break;
157          case Analyzer.CLOSE_CODE:
158             _end(EVENT_CODE);
159             break;
160
161          case Analyzer.OPEN_LIST_UNORDERED:
162             _start(EVENT_UNORDERED_LIST, null);
163             break;
164          case Analyzer.OPEN_LIST_ORDERED_NUMERICAL:
165             _start(EVENT_NUMERICALLY_ORDERED_LIST, null);
166             break;
167          case Analyzer.OPEN_LIST_ORDERED_ALPHABETICAL:
168             _start(EVENT_ALPHABETICALLY_ORDERED_LIST, null);
169             break;
170
171          case Analyzer.CLOSE_LIST:
172             // If we match a list token on the stack at
173
// level -1 (because of the list item that should
174
// be pushed on the stack) we close
175
CodeKey tmp = (CodeKey)stack.peek(2);
176             if (tmp != null)
177             {
178                int type = tmp.type;
179                if (type == EVENT_UNORDERED_LIST ||
180                    type == EVENT_NUMERICALLY_ORDERED_LIST ||
181                    type == EVENT_ALPHABETICALLY_ORDERED_LIST)
182                {
183                   _end(type);
184                }
185             }
186             break;
187
188          case Analyzer.LIST_ITEM:
189             // If we have a sibling item close it
190
CodeKey tmp2 = (CodeKey)stack.peek(1);
191             if (tmp2 != null && tmp2.type == EVENT_ITEM)
192             {
193                _end(EVENT_ITEM);
194             }
195             _start(EVENT_ITEM, null);
196             break;
197
198          case Analyzer.LINK:
199             int bracket = t.value.indexOf(']');
200             if (bracket > 0)
201             {
202                boolean hasEquals = t.value.charAt(0) == '=';
203                if (hasEquals)
204                {
205                   // todo add support for the link value which is not used yet
206
String JavaDoc url = t.value.substring(1, bracket);
207                   String JavaDoc link = t.value.substring(bracket + 1);
208                   _start(EVENT_LINK, url);
209                   _end(EVENT_LINK);
210                }
211             }
212             else
213             {
214                _start(EVENT_LINK, t.value.substring(1));
215                _end(EVENT_LINK);
216             }
217
218             break;
219
220          case Analyzer.TEXT:
221             buffer.append(t.value.charAt(0));
222             break;
223
224          default:
225             throw new IllegalStateException JavaDoc("This should not be possible");
226          }
227       }
228
229       // Last
230
_end(EVENT_NORMAL);
231    }
232
233    private void _text()
234    {
235       if (buffer.length() > 0)
236       {
237          textEvent.setText(buffer.chars(), 0, buffer.length());
238          buffer.reset();
239          handler.handle(textEvent);
240       }
241    }
242
243    private void _start(int type, String JavaDoc string)
244    {
245       _text();
246       CloseEvent closeEvent = (CloseEvent)stack.push();
247       openEvent.type = closeEvent.type = type;
248       openEvent.string = string;
249       handler.handle(openEvent);
250    }
251
252    private void _end(int type)
253    {
254       myKey.type = type;
255       Iterator JavaDoc i = stack.pop(myKey);
256       if (i.hasNext())
257       {
258          _text();
259          do
260          {
261             handler.handle((CloseEvent)i.next());
262          }
263          while (i.hasNext());
264       }
265    }
266
267    private static class CodeKey implements Stack.Key
268    {
269       protected int type;
270       protected String JavaDoc string;
271       public CodeKey()
272       {
273          type = -1;
274          string = null;
275       }
276       public int getType()
277       {
278          return type;
279       }
280       public String JavaDoc getString()
281       {
282          return string;
283       }
284    }
285
286    public static class OpenEvent extends CodeKey implements ParseEvent
287    {
288       public String JavaDoc toString()
289       {
290          return "open: " + type + " " + string;
291       }
292    }
293
294    public static class CloseEvent extends CodeKey implements ParseEvent
295    {
296       public String JavaDoc toString()
297       {
298          return "close: " + type + " " + string;
299       }
300    }
301 }
302
Popular Tags