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