View Javadoc
1   package org.apache.maven.doxia.siterenderer.sink;
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.StringWriter;
23  import java.io.Writer;
24  import java.util.ArrayList;
25  import java.util.HashSet;
26  import java.util.List;
27  import java.util.Set;
28  
29  import javax.swing.text.html.HTML.Attribute;
30  
31  import org.apache.maven.doxia.markup.HtmlMarkup;
32  import org.apache.maven.doxia.module.xhtml5.Xhtml5Sink;
33  import org.apache.maven.doxia.sink.Sink;
34  import org.apache.maven.doxia.sink.SinkEventAttributes;
35  import org.apache.maven.doxia.siterenderer.DocumentContent;
36  import org.apache.maven.doxia.siterenderer.RenderingContext;
37  import org.apache.maven.doxia.util.HtmlTools;
38  import org.codehaus.plexus.util.StringUtils;
39  
40  /**
41   * Sink for site rendering of a document, to allow later merge document's output with a template.
42   * During raw Doxia rendering, content is stored in multiple fields for later use when incorporating
43   * into skin or template: title, date, authors, head, body 
44   *
45   * @author <a href="mailto:evenisse@codehaus.org">Emmanuel Venisse</a>
46   */
47  @SuppressWarnings( "checkstyle:methodname" )
48  public class SiteRendererSink
49      extends Xhtml5Sink
50      implements Sink, org.codehaus.doxia.sink.Sink, DocumentContent
51  {
52      private String date = "";
53  
54      private String title = "";
55  
56      private List<String> authors = new ArrayList<String>();
57  
58      private final StringWriter headWriter;
59  
60      private StringBuilder sectionTitleBuffer;
61  
62      private StringBuilder sectionTitleWriteBuffer;
63  
64      private boolean sectionHasID;
65  
66      private boolean isSectionTitle;
67  
68      private Set<String> anchorsInSectionTitle;
69  
70      private final Writer writer;
71  
72      private RenderingContext renderingContext;
73  
74      /**
75       * Construct a new SiteRendererSink for a document.
76       *
77       * @param renderingContext the document's RenderingContext.
78       */
79      public SiteRendererSink( RenderingContext renderingContext )
80      {
81          this( new StringWriter(), renderingContext );
82      }
83  
84      /**
85       * Construct a new SiteRendererSink for a document.
86       *
87       * @param writer the writer for the sink.
88       * @param renderingContext the document's RenderingContext.
89       */
90      private SiteRendererSink( StringWriter writer, RenderingContext renderingContext )
91      {
92          super( writer );
93  
94          this.writer = writer;
95          this.headWriter = new StringWriter();
96          this.renderingContext = renderingContext;
97  
98          /* the template is expected to have used the main tag, which can be used only once */
99          super.contentStack.push( HtmlMarkup.MAIN );
100     }
101 
102     /** {@inheritDoc} */
103     @Override
104     public void title_()
105     {
106         if ( getTextBuffer().length() > 0 )
107         {
108             title = getTextBuffer().toString();
109         }
110 
111         resetTextBuffer();
112     }
113 
114     /**
115      * {@inheritDoc}
116      *
117      * Reset text buffer, since text content before title mustn't be in title.
118      * @see org.apache.maven.doxia.module.xhtml5.Xhtml5Sink#title()
119      */
120     @Override
121     public void title()
122     {
123         resetTextBuffer();
124     }
125 
126     /** {@inheritDoc} */
127     @Override
128     public void author()
129     {
130         resetTextBuffer();
131     }
132 
133     /** {@inheritDoc} */
134     @Override
135     public void author_()
136     {
137         if ( getTextBuffer().length() > 0 )
138         {
139             String text = HtmlTools.escapeHTML( getTextBuffer().toString() );
140             text = StringUtils.replace( text, "&amp;#", "&#" );
141             authors.add( text.trim() );
142         }
143 
144         resetTextBuffer();
145     }
146 
147     /** {@inheritDoc} */
148     @Override
149     public void date()
150     {
151         resetTextBuffer();
152     }
153 
154     /** {@inheritDoc} */
155     @Override
156     public void date_()
157     {
158         if ( getTextBuffer().length() > 0 )
159         {
160             date = getTextBuffer().toString().trim();
161         }
162 
163         resetTextBuffer();
164     }
165 
166     /**
167      * {@inheritDoc}
168      *
169      * Do nothing.
170      * @see org.apache.maven.doxia.module.xhtml5.Xhtml5Sink#body_()
171      */
172     @Override
173     public void body_()
174     {
175         // nop
176     }
177 
178     /**
179      * {@inheritDoc}
180      *
181      * Do nothing.
182      * @see org.apache.maven.doxia.module.xhtml5.Xhtml5Sink#body()
183      */
184     @Override
185     public void body()
186     {
187         // nop
188     }
189 
190     /** {@inheritDoc} */
191     @Override
192     public void head_()
193     {
194         setHeadFlag( false );
195     }
196 
197     /** {@inheritDoc} */
198     @Override
199     public void head()
200     {
201         setHeadFlag( true );
202     }
203 
204     /** {@inheritDoc} */
205     @Override
206     public void anchor( String name, SinkEventAttributes attributes )
207     {
208         super.anchor( name, attributes );
209         if ( isSectionTitle )
210         {
211             if ( anchorsInSectionTitle == null )
212             {
213                 anchorsInSectionTitle = new HashSet<String>();
214             }
215             anchorsInSectionTitle.add( name );
216         }
217     }
218 
219     /** {@inheritDoc} */
220     @Override
221     protected void onSectionTitle( int depth, SinkEventAttributes attributes )
222     {
223         sectionHasID = ( attributes != null && attributes.isDefined ( Attribute.ID.toString() ) );
224         isSectionTitle = true;
225 
226         super.onSectionTitle( depth, attributes );
227 
228         this.sectionTitleBuffer = new StringBuilder();
229         this.sectionTitleWriteBuffer = new StringBuilder();
230     }
231 
232     /** {@inheritDoc} */
233     @Override
234     protected void onSectionTitle_( int depth )
235     {
236         String sectionTitle = sectionTitleBuffer.toString();
237         this.sectionTitleBuffer = null;
238         String sectionWriteTitle = sectionTitleWriteBuffer.toString();
239         this.sectionTitleWriteBuffer = null;
240 
241         if ( !StringUtils.isEmpty( sectionTitle ) )
242         {
243             if ( sectionHasID )
244             {
245                 sectionHasID = false;
246             }
247             else
248             {
249                 String id = HtmlTools.encodeId( sectionTitle );
250                 if ( ( anchorsInSectionTitle == null ) || ( !anchorsInSectionTitle.contains( id ) ) )
251                 {
252                     anchor( id );
253                     anchor_();
254                 }
255             }
256         }
257 
258         super.write( sectionWriteTitle );
259 
260         this.isSectionTitle = false;
261         anchorsInSectionTitle = null;
262         super.onSectionTitle_( depth );
263     }
264 
265     /** {@inheritDoc} */
266     @Override
267     public void text( String text )
268     {
269         if ( sectionTitleBuffer != null )
270         {
271             // this implies we're inside a section title, collect text events for anchor generation
272             sectionTitleBuffer.append( text );
273         }
274 
275         super.text( text );
276     }
277 
278     /** {@inheritDoc} */
279     @Override
280     protected void write( String text )
281     {
282         String txt = text;
283 
284         if ( isHeadFlag() )
285         {
286             headWriter.write( unifyEOLs( txt ) );
287 
288             return;
289         }
290 
291         if ( renderingContext != null )
292         {
293             String relativePathToBasedir = renderingContext.getRelativePath();
294 
295             if ( relativePathToBasedir == null )
296             {
297                 txt = StringUtils.replace( txt, "$relativePath", "." );
298             }
299             else
300             {
301                 txt = StringUtils.replace( txt, "$relativePath", relativePathToBasedir );
302             }
303         }
304 
305         if ( sectionTitleWriteBuffer != null )
306         {
307             // this implies we're inside a section title, collect text events for anchor generation
308             sectionTitleWriteBuffer.append( txt );
309         }
310         else
311         {
312             super.write( txt );
313         }
314     }
315 
316     // DocumentContent interface
317 
318     /** {@inheritDoc} */
319     public String getTitle()
320     {
321         return title;
322     }
323 
324     /** {@inheritDoc} */
325     public List<String> getAuthors()
326     {
327         return authors;
328     }
329 
330     /** {@inheritDoc} */
331     public String getDate()
332     {
333         return date;
334     }
335 
336     /** {@inheritDoc} */
337     public String getBody()
338     {
339         return writer.toString();
340     }
341 
342     /** {@inheritDoc} */
343     public String getHead()
344     {
345         return headWriter.toString();
346     }
347 
348     /** {@inheritDoc} */
349     public RenderingContext getRenderingContext()
350     {
351         return renderingContext;
352     }
353 }