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