1 package org.apache.maven.doxia.module.twiki.parser;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import org.apache.maven.doxia.util.ByLineSource;
23 import org.apache.maven.doxia.parser.ParseException;
24 import org.apache.maven.doxia.sink.Sink;
25
26 import java.util.ArrayList;
27 import java.util.List;
28 import java.util.regex.Matcher;
29 import java.util.regex.Pattern;
30
31
32
33
34
35
36
37 public class GenericListBlockParser
38 implements BlockParser
39 {
40 static final String EOL = System.getProperty( "line.separator" );
41
42
43
44
45 private FormatedTextParser formatedTextParser;
46
47
48
49
50 private final Pattern[] patterns = new Pattern[TYPES.length];
51
52
53
54
55 public GenericListBlockParser()
56 {
57 for ( int i = 0; i < TYPES.length; i++ )
58 {
59 patterns[i] = Pattern.compile( "^(( )+)" + TYPES[i].getItemPattern() + "(.*)$" );
60 }
61 }
62
63
64 public final boolean accept( final String line )
65 {
66 boolean ret = false;
67
68 for ( int i = 0; !ret && i < patterns.length; i++ )
69 {
70 ret |= patterns[i].matcher( line ).lookingAt();
71 }
72
73 return ret;
74 }
75
76
77
78
79 public final Block visit( final String line, final ByLineSource source )
80 throws ParseException
81 {
82 final TreeListBuilder treeListBuilder = new TreeListBuilder( formatedTextParser );
83
84 String l = line;
85 do
86 {
87 if ( !accept( l ) )
88 {
89 break;
90 }
91
92 for ( int i = 0; i < patterns.length; i++ )
93 {
94 final Matcher m = patterns[i].matcher( l );
95 if ( m.lookingAt() )
96 {
97 final int numberOfSpaces = 3;
98 final int textGroup = 3;
99 assert m.group( 1 ).length() % numberOfSpaces == 0;
100 final int level = m.group( 1 ).length() / numberOfSpaces;
101 treeListBuilder.feedEntry( TYPES[i], level, m.group( textGroup ).trim() );
102 break;
103 }
104 }
105 }
106 while ( ( l = source.getNextLine() ) != null );
107
108 if ( l != null )
109 {
110 source.ungetLine();
111 }
112
113 return treeListBuilder.getBlock();
114 }
115
116
117
118
119
120
121 public final void setTextParser( final FormatedTextParser textParser )
122 {
123 if ( textParser == null )
124 {
125 throw new IllegalArgumentException( "formatTextParser can't be null" );
126 }
127 this.formatedTextParser = textParser;
128 }
129
130 interface Type
131 {
132
133
134
135 String getItemPattern();
136
137
138
139
140
141 ListBlock createList( final ListItemBlock[] items );
142
143 }
144
145
146
147
148 private static final Type LIST = new Type()
149 {
150
151 public String getItemPattern()
152 {
153 return "[*]";
154 }
155
156
157 public ListBlock createList( final ListItemBlock[] items )
158 {
159 return new UnorderedListBlock( items );
160 }
161 };
162
163
164
165
166 private static final Type ORDERED_LOWER_ALPHA = new Type()
167 {
168
169 public String getItemPattern()
170 {
171 return "[a-hj-z][.]";
172 }
173
174
175 public ListBlock createList( final ListItemBlock[] items )
176 {
177 return new NumeratedListBlock( Sink.NUMBERING_LOWER_ALPHA, items );
178 }
179 };
180
181
182
183
184 private static final Type ORDERED_UPPER_ALPHA = new Type()
185 {
186
187 public String getItemPattern()
188 {
189 return "[A-HJ-Z][.]";
190 }
191
192
193 public ListBlock createList( final ListItemBlock[] items )
194 {
195 return new NumeratedListBlock( Sink.NUMBERING_UPPER_ALPHA, items );
196 }
197 };
198
199
200
201
202 private static final Type ORDERERED_DECIMAL = new Type()
203 {
204
205 public String getItemPattern()
206 {
207 return "[0123456789][.]";
208 }
209
210
211 public ListBlock createList( final ListItemBlock[] items )
212 {
213 return new NumeratedListBlock( Sink.NUMBERING_DECIMAL, items );
214 }
215 };
216
217
218
219
220 private static final Type ORDERERED_LOWER_ROMAN = new Type()
221 {
222
223 public String getItemPattern()
224 {
225 return "[i][.]";
226 }
227
228
229 public ListBlock createList( final ListItemBlock[] items )
230 {
231 return new NumeratedListBlock( Sink.NUMBERING_LOWER_ROMAN, items );
232 }
233 };
234
235
236
237
238 private static final Type ORDERERED_UPPER_ROMAN = new Type()
239 {
240
241 public String getItemPattern()
242 {
243 return "[I][.]";
244 }
245
246
247 public ListBlock createList( final ListItemBlock[] items )
248 {
249 return new NumeratedListBlock( Sink.NUMBERING_UPPER_ROMAN, items );
250 }
251 };
252
253 private static final Type[] TYPES =
254 { LIST, ORDERED_LOWER_ALPHA, ORDERED_UPPER_ALPHA, ORDERERED_DECIMAL, ORDERERED_LOWER_ROMAN,
255 ORDERERED_UPPER_ROMAN };
256
257 }
258
259
260
261
262
263
264
265 class TreeListBuilder
266 {
267
268
269
270 private final FormatedTextParser textParser;
271
272
273
274
275 private final TreeComponent root;
276
277
278
279
280 private TreeComponent current;
281
282
283
284
285
286
287
288 TreeListBuilder( final FormatedTextParser formatTextParser )
289 throws IllegalArgumentException
290 {
291 if ( formatTextParser == null )
292 {
293 throw new IllegalArgumentException( "argument is null" );
294 }
295 this.textParser = formatTextParser;
296 root = new TreeComponent( null, "root", null );
297 current = root;
298 }
299
300
301
302
303
304
305
306
307
308 void feedEntry( final GenericListBlockParser.Type type, final int level, final String text )
309 {
310 final int currentDepth = current.getDepth();
311 final int incomingLevel = level - 1;
312
313 if ( incomingLevel == currentDepth )
314 {
315
316 }
317 else if ( incomingLevel > currentDepth )
318 {
319
320 final TreeComponent[] components = current.getChildren();
321 if ( components.length == 0 )
322 {
323
324
325
326
327 for ( int i = 0, n = incomingLevel - currentDepth; i < n; i++ )
328 {
329 current = current.addChildren( "", type );
330 }
331 }
332 else
333 {
334 current = components[components.length - 1];
335 }
336
337 }
338 else
339 {
340 for ( int i = 0, n = currentDepth - incomingLevel; i < n; i++ )
341 {
342 current = current.getFather();
343 if ( current == null )
344 {
345 throw new IllegalStateException();
346 }
347 }
348 }
349 current.addChildren( text, type );
350 }
351
352
353
354
355 ListBlock getBlock()
356 {
357 return getList( root );
358 }
359
360
361
362
363
364
365
366 private ListBlock getList( final TreeComponent tc )
367 {
368 ListItemBlock[] li = getListItems( tc ).toArray( new ListItemBlock[] {} );
369 return tc.getChildren()[0].getType().createList( li );
370 }
371
372
373
374
375
376 private List<ListItemBlock> getListItems( final TreeComponent tc )
377 {
378 final List<ListItemBlock> blocks = new ArrayList<ListItemBlock>();
379
380 for ( int i = 0; i < tc.getChildren().length; i++ )
381 {
382 final TreeComponent child = tc.getChildren()[i];
383
384 Block[] text = new Block[] {};
385 if ( child.getFather() != null )
386 {
387 text = textParser.parse( child.getText() );
388 }
389
390 if ( child.getChildren().length != 0 )
391 {
392 blocks.add( new ListItemBlock( text, getList( child ) ) );
393 }
394 else
395 {
396 blocks.add( new ListItemBlock( text ) );
397 }
398 }
399
400 return blocks;
401 }
402
403
404
405
406
407
408
409 class TreeComponent
410 {
411
412
413
414 private List<TreeComponent> children = new ArrayList<TreeComponent>();
415
416
417
418
419 private String text;
420
421
422
423
424 private TreeComponent father;
425
426
427
428
429 private GenericListBlockParser.Type type;
430
431
432
433
434
435
436
437
438 TreeComponent( final TreeComponent father, final String text, final GenericListBlockParser.Type type )
439 {
440 this.text = text;
441 this.father = father;
442 this.type = type;
443 }
444
445
446
447
448 TreeComponent[] getChildren()
449 {
450 return (TreeComponent[]) children.toArray( new TreeComponent[] {} );
451 }
452
453
454
455
456
457
458
459
460 TreeComponent addChildren( final String t, final GenericListBlockParser.Type ttype )
461 {
462 if ( t == null || ttype == null )
463 {
464 throw new IllegalArgumentException( "argument is null" );
465 }
466 final TreeComponent ret = new TreeComponent( this, t, ttype );
467 children.add( ret );
468
469 return ret;
470 }
471
472
473
474
475 TreeComponent getFather()
476 {
477 return father;
478 }
479
480
481
482
483 int getDepth()
484 {
485 int ret = 0;
486
487 TreeComponent c = this;
488
489 while ( ( c = c.getFather() ) != null )
490 {
491 ret++;
492 }
493
494 return ret;
495 }
496
497
498 public String toString()
499 {
500 return toString( "" );
501 }
502
503
504 public String toString( final String indent )
505 {
506 final StringBuilder sb = new StringBuilder();
507
508 if ( father != null )
509 {
510 sb.append( indent );
511 sb.append( "- " );
512 sb.append( text );
513 sb.append( GenericListBlockParser.EOL );
514 }
515 for ( TreeComponent lc : children )
516 {
517 sb.append( lc.toString( indent + " " ) );
518 }
519 return sb.toString();
520 }
521
522
523
524
525
526
527 String getText()
528 {
529 return text;
530 }
531
532
533
534
535
536
537 GenericListBlockParser.Type getType()
538 {
539 return type;
540 }
541 }
542 }