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