View Javadoc

1   package org.apache.maven.doxia.macro.toc;
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 java.io.StringReader;
23  
24  import org.apache.maven.doxia.index.IndexEntry;
25  import org.apache.maven.doxia.index.IndexingSink;
26  import org.apache.maven.doxia.macro.AbstractMacro;
27  import org.apache.maven.doxia.macro.MacroExecutionException;
28  import org.apache.maven.doxia.macro.MacroRequest;
29  import org.apache.maven.doxia.util.HtmlTools;
30  import org.apache.maven.doxia.parser.ParseException;
31  import org.apache.maven.doxia.parser.Parser;
32  import org.apache.maven.doxia.sink.Sink;
33  
34  import org.codehaus.plexus.util.StringUtils;
35  
36  /**
37   * Macro to display a <code>Table Of Content</code> in a given <code>Sink</code>.
38   * The input parameters for this macro are:
39   * <dl>
40   * <dt>section</dt>
41   * <dd>Display a TOC for the specified section only, or all sections if 0.<br/>
42   * Positive int, not mandatory, 0 by default.</dd>
43   * <dt>fromDepth</dt>
44   * <dd>Minimal depth of entries to display in the TOC.
45   * Sections are depth 1, sub-sections depth 2, etc.<br/>
46   * Positive int, not mandatory, 0 by default.</dd>
47   * <dt>toDepth</dt>
48   * <dd>Maximum depth of entries to display in the TOC.<br/>
49   * Positive int, not mandatory, 5 by default.</dd>
50   * </dl>
51   * For instance, in an APT file, you could write:
52   * <dl>
53   * <dt>%{toc|section=2|fromDepth=2|toDepth=3}</dt>
54   * <dd>Display a TOC for the second section in the document, including all
55   * subsections (depth 2) and  sub-subsections (depth 3).</dd>
56   * <dt>%{toc}</dt>
57   * <dd>display a TOC with all section and subsections
58   * (similar to %{toc|section=0} )</dd>
59   * </dl>
60   * Moreover, you need to write APT link for section to allow anchor,
61   * for instance:
62   * <pre>
63   * * {SubSection 1}
64   * </pre>
65   *
66   * Similarly, in an XDOC file, you could write:
67   * <pre>
68   * &lt;macro name="toc"&gt;
69   *   &lt;param name="section" value="1" /&gt;
70   *   &lt;param name="fromDepth" value="1" /&gt;
71   *   &lt;param name="toDepth" value="2" /&gt;
72   * &lt;/macro&gt;
73   * </pre>
74   *
75   * @plexus.component role-hint="toc"
76   *
77   * @author <a href="mailto:vincent.siveton@gmail.com">Vincent Siveton</a>
78   * @version $Id: TocMacro.java 1090706 2011-04-09 23:15:28Z hboutemy $
79   */
80  public class TocMacro
81      extends AbstractMacro
82  {
83      /** The section to display. */
84      private int section;
85  
86      /** Start depth. */
87      private int fromDepth;
88  
89      /** End depth. */
90      private int toDepth;
91  
92      /** The default end depth. */
93      private static final int DEFAULT_DEPTH = 5;
94  
95      /** {@inheritDoc} */
96      public void execute( Sink sink, MacroRequest request )
97          throws MacroExecutionException
98      {
99          String source = (String) request.getParameter( "sourceContent" );
100         Parser parser = (Parser) request.getParameter( "parser" );
101 
102         section = getInt( request, "section", 0 );
103         fromDepth = getInt( request, "fromDepth", 0 );
104         toDepth = getInt( request, "toDepth", DEFAULT_DEPTH );
105 
106         if ( fromDepth > toDepth )
107         {
108             return;
109         }
110 
111         IndexEntry index = new IndexEntry( "index" );
112         IndexingSink tocSink = new IndexingSink( index );
113 
114         try
115         {
116             parser.parse( new StringReader( source ), tocSink );
117         }
118         catch ( ParseException e )
119         {
120             throw new MacroExecutionException( "ParseException: " + e.getMessage(), e );
121         }
122 
123         if ( index.getChildEntries().size() > 0 )
124         {
125             sink.list( getAttributesFromMap( request.getParameters() ) );
126 
127             int i = 1;
128 
129             for ( IndexEntry sectionIndex : index.getChildEntries() )
130             {
131                 if ( ( i == section ) || ( section == 0 ) )
132                 {
133                     writeSubSectionN( sink, sectionIndex, 1 );
134                 }
135 
136                 i++;
137             }
138 
139             sink.list_();
140         }
141     }
142 
143     /**
144      * @param sink The sink to write to.
145      * @param sectionIndex The section index.
146      * @param n The toc depth.
147      */
148     private void writeSubSectionN( Sink sink, IndexEntry sectionIndex, int n )
149     {
150         if ( fromDepth <= n )
151         {
152             sink.listItem();
153             sink.link( "#" + HtmlTools.encodeId( sectionIndex.getId() ) );
154             sink.text( sectionIndex.getTitle() );
155             sink.link_();
156         }
157 
158         if ( toDepth > n )
159         {
160             if ( sectionIndex.getChildEntries().size() > 0 )
161             {
162                 if ( fromDepth <= n )
163                 {
164                     sink.list();
165                 }
166 
167                 for ( IndexEntry subsectionIndex : sectionIndex.getChildEntries() )
168                 {
169                     if ( n == toDepth - 1 )
170                     {
171                         sink.listItem();
172                         sink.link( "#" + HtmlTools.encodeId( subsectionIndex.getId() ) );
173                         sink.text( subsectionIndex.getTitle() );
174                         sink.link_();
175                         sink.listItem_();
176                     }
177                     else
178                     {
179                         writeSubSectionN( sink, subsectionIndex, n + 1 );
180                     }
181                 }
182 
183                 if ( fromDepth <= n )
184                 {
185                     sink.list_();
186                 }
187             }
188         }
189 
190         if ( fromDepth <= n )
191         {
192             sink.listItem_();
193         }
194     }
195 
196     /**
197      * @param request The MacroRequest.
198      * @param parameter The parameter.
199      * @param defaultValue the default value.
200      * @return the int value of a parameter in the request.
201      * @throws MacroExecutionException if something goes wrong.
202      */
203     private static int getInt( MacroRequest request, String parameter, int defaultValue )
204         throws MacroExecutionException
205     {
206         String value = (String) request.getParameter( parameter );
207 
208         if ( StringUtils.isEmpty( value ) )
209         {
210             return defaultValue;
211         }
212 
213         int i;
214 
215         try
216         {
217             i = Integer.parseInt( value );
218         }
219         catch ( NumberFormatException e )
220         {
221             return defaultValue;
222         }
223 
224         if ( i < 0 )
225         {
226             throw new MacroExecutionException( "The " + parameter + "=" + i + " should be positive." );
227         }
228 
229         return i;
230     }
231 }