001package org.apache.maven.doxia.macro.toc; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import javax.inject.Named; 023 024import java.io.StringReader; 025 026import org.apache.maven.doxia.index.IndexEntry; 027import org.apache.maven.doxia.index.IndexingSink; 028import org.apache.maven.doxia.macro.AbstractMacro; 029import org.apache.maven.doxia.macro.MacroExecutionException; 030import org.apache.maven.doxia.macro.MacroRequest; 031import org.apache.maven.doxia.util.HtmlTools; 032import org.apache.maven.doxia.parser.ParseException; 033import org.apache.maven.doxia.parser.Parser; 034import org.apache.maven.doxia.sink.Sink; 035 036import org.codehaus.plexus.util.StringUtils; 037 038/** 039 * Macro to display a <code>Table Of Content</code> in a given <code>Sink</code>. 040 * The input parameters for this macro are: 041 * <dl> 042 * <dt>section</dt> 043 * <dd>Display a TOC for the specified section only, or all sections if 0.<br> 044 * Positive int, not mandatory, 0 by default.</dd> 045 * <dt>fromDepth</dt> 046 * <dd>Minimal depth of entries to display in the TOC. 047 * Sections are depth 1, sub-sections depth 2, etc.<br> 048 * Positive int, not mandatory, 0 by default.</dd> 049 * <dt>toDepth</dt> 050 * <dd>Maximum depth of entries to display in the TOC.<br> 051 * Positive int, not mandatory, 5 by default.</dd> 052 * </dl> 053 * For instance, in an APT file, you could write: 054 * <dl> 055 * <dt>%{toc|section=2|fromDepth=2|toDepth=3}</dt> 056 * <dd>Display a TOC for the second section in the document, including all 057 * subsections (depth 2) and sub-subsections (depth 3).</dd> 058 * <dt>%{toc}</dt> 059 * <dd>display a TOC with all section and subsections 060 * (similar to %{toc|section=0} )</dd> 061 * </dl> 062 * Moreover, you need to write APT link for section to allow anchor, 063 * for instance: 064 * <pre> 065 * * {SubSection 1} 066 * </pre> 067 * 068 * Similarly, in an XDOC file, you could write: 069 * <pre> 070 * <macro name="toc"> 071 * <param name="section" value="1" /> 072 * <param name="fromDepth" value="1" /> 073 * <param name="toDepth" value="2" /> 074 * </macro> 075 * </pre> 076 * 077 * @author <a href="mailto:vincent.siveton@gmail.com">Vincent Siveton</a> 078 */ 079@Named( "toc" ) 080public class TocMacro 081 extends AbstractMacro 082{ 083 /** The section to display. */ 084 private int section; 085 086 /** Start depth. */ 087 private int fromDepth; 088 089 /** End depth. */ 090 private int toDepth; 091 092 /** The default end depth. */ 093 private static final int DEFAULT_DEPTH = 5; 094 095 /** {@inheritDoc} */ 096 public void execute( Sink sink, MacroRequest request ) 097 throws MacroExecutionException 098 { 099 String source = request.getSourceContent(); 100 Parser parser = request.getParser(); 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( 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}