View Javadoc

1   package org.apache.maven.doxia.module.twiki.parser;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
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   * Generic list parser
33   *
34   * @author Juan F. Codagnone
35   * @version $Id: GenericListBlockParser.java 1090706 2011-04-09 23:15:28Z hboutemy $
36   */
37  public class GenericListBlockParser
38      implements BlockParser
39  {
40      static final String EOL = System.getProperty( "line.separator" );
41  
42      /**
43       * parser used to create text blocks
44       */
45      private FormatedTextParser formatedTextParser;
46  
47      /**
48       * supported patterns
49       */
50      private final Pattern[] patterns = new Pattern[TYPES.length];
51  
52      /**
53       * Creates the GenericListBlockParser.
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      /** {@inheritDoc} */
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       * {@inheritDoc}
78       */
79      public final Block visit( final String line, final ByLineSource source )
80          throws ParseException
81      {
82          final TreeListBuilder treeListBuilder = new TreeListBuilder( formatedTextParser );
83          // new TreeListBuilder(formatedTextParser);
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      * Sets the formatTextParser.
118      *
119      * @param textParser <code>FormatedTextParser</code> with the formatTextParser.
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          * @return the pattern of the item part of the list regex
134          */
135         String getItemPattern();
136 
137         /**
138          * @param items children of the new listblock
139          * @return a new ListBlock
140          */
141         ListBlock createList( final ListItemBlock[] items );
142 
143     }
144 
145     /**
146      * unordered list
147      */
148     private static final Type LIST = new Type()
149     {
150         /** {@inheritDoc} */
151         public String getItemPattern()
152         {
153             return "[*]";
154         }
155 
156         /** {@inheritDoc} */
157         public ListBlock createList( final ListItemBlock[] items )
158         {
159             return new UnorderedListBlock( items );
160         }
161     };
162 
163     /**
164      * a.
165      */
166     private static final Type ORDERED_LOWER_ALPHA = new Type()
167     {
168         /** {@inheritDoc} */
169         public String getItemPattern()
170         {
171             return "[a-hj-z][.]";
172         }
173 
174         /** {@inheritDoc} */
175         public ListBlock createList( final ListItemBlock[] items )
176         {
177             return new NumeratedListBlock( Sink.NUMBERING_LOWER_ALPHA, items );
178         }
179     };
180 
181     /**
182      * A.
183      */
184     private static final Type ORDERED_UPPER_ALPHA = new Type()
185     {
186         /** {@inheritDoc} */
187         public String getItemPattern()
188         {
189             return "[A-HJ-Z][.]";
190         }
191 
192         /** {@inheritDoc} */
193         public ListBlock createList( final ListItemBlock[] items )
194         {
195             return new NumeratedListBlock( Sink.NUMBERING_UPPER_ALPHA, items );
196         }
197     };
198 
199     /**
200      * 1.
201      */
202     private static final Type ORDERERED_DECIMAL = new Type()
203     {
204         /** {@inheritDoc} */
205         public String getItemPattern()
206         {
207             return "[0123456789][.]";
208         }
209 
210         /** {@inheritDoc} */
211         public ListBlock createList( final ListItemBlock[] items )
212         {
213             return new NumeratedListBlock( Sink.NUMBERING_DECIMAL, items );
214         }
215     };
216 
217     /**
218      * i.
219      */
220     private static final Type ORDERERED_LOWER_ROMAN = new Type()
221     {
222         /** {@inheritDoc} */
223         public String getItemPattern()
224         {
225             return "[i][.]";
226         }
227 
228         /** {@inheritDoc} */
229         public ListBlock createList( final ListItemBlock[] items )
230         {
231             return new NumeratedListBlock( Sink.NUMBERING_LOWER_ROMAN, items );
232         }
233     };
234 
235     /**
236      * I.
237      */
238     private static final Type ORDERERED_UPPER_ROMAN = new Type()
239     {
240         /** {@inheritDoc} */
241         public String getItemPattern()
242         {
243             return "[I][.]";
244         }
245 
246         /** {@inheritDoc} */
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  * It helps to build
261  *
262  * @author Juan F. Codagnone
263  * @version $Id: GenericListBlockParser.java 1090706 2011-04-09 23:15:28Z hboutemy $
264  */
265 class TreeListBuilder
266 {
267     /**
268      * parser that create text blocks
269      */
270     private final FormatedTextParser textParser;
271 
272     /**
273      * tree root
274      */
275     private final TreeComponent root;
276 
277     /**
278      * the current element of the tree
279      */
280     private TreeComponent current;
281 
282     /**
283      * Creates the TreeListBuilder.
284      *
285      * @param formatTextParser parser that create text blocks
286      * @throws IllegalArgumentException if <code>formatTextParser</code> is null
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      * recibe un nivel y un texto y armar magicamente (manteniendo estado)
302      * el �rbol
303      *
304      * @param type  type of list
305      * @param level indentation level of the item
306      * @param text  text of the item
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             // nothing to move
316         }
317         else if ( incomingLevel > currentDepth )
318         {
319             // el actual ahora es el �ltimo que insert�
320             final TreeComponent[] components = current.getChildren();
321             if ( components.length == 0 )
322             {
323                 /* for example:
324                  *        * item1
325                  *     * item2
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      * @return a Block for the list that we received
354      */
355     ListBlock getBlock()
356     {
357         return getList( root );
358     }
359 
360     /**
361      * Wrapper
362      *
363      * @param tc tree
364      * @return list Block for this tree
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      * @param tc tree
374      * @return list Block for this tree
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      * A bidirectional tree node
405      *
406      * @author Juan F. Codagnone
407      * @version $Id: GenericListBlockParser.java 1090706 2011-04-09 23:15:28Z hboutemy $
408      */
409     class TreeComponent
410     {
411         /**
412          * childrens
413          */
414         private List<TreeComponent> children = new ArrayList<TreeComponent>();
415 
416         /**
417          * node text
418          */
419         private String text;
420 
421         /**
422          * the father
423          */
424         private TreeComponent father;
425 
426         /**
427          * type of the list
428          */
429         private GenericListBlockParser.Type type;
430 
431         /**
432          * Creates the TreeComponent.
433          *
434          * @param father Component father
435          * @param text   component text
436          * @param type   component type
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          * @return my childrens
447          */
448         TreeComponent[] getChildren()
449         {
450             return (TreeComponent[]) children.toArray( new TreeComponent[] {} );
451         }
452 
453         /**
454          * adds a children node
455          *
456          * @param t     text of the children
457          * @param ttype component type
458          * @return the new node created
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          * @return the father
474          */
475         TreeComponent getFather()
476         {
477             return father;
478         }
479 
480         /**
481          * @return the node depth in the tree
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         /** {@inheritDoc} */
498         public String toString()
499         {
500             return toString( "" );
501         }
502 
503         /** {@inheritDoc} */
504         public String toString( final String indent )
505         {
506             final StringBuffer sb = new StringBuffer();
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          * Returns the text.
524          *
525          * @return <code>String</code> with the text.
526          */
527         String getText()
528         {
529             return text;
530         }
531 
532         /**
533          * Returns the type.
534          *
535          * @return <code>Type</code> with the text.
536          */
537         GenericListBlockParser.Type getType()
538         {
539             return type;
540         }
541     }
542 }