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