View Javadoc
1   package org.apache.maven.doxia.module.xhtml5;
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.Writer;
23  
24  import javax.swing.text.MutableAttributeSet;
25  import javax.swing.text.html.HTML.Attribute;
26  
27  import org.apache.maven.doxia.markup.HtmlMarkup;
28  import org.apache.maven.doxia.sink.SinkEventAttributes;
29  import org.apache.maven.doxia.sink.impl.SinkEventAttributeSet;
30  import org.apache.maven.doxia.sink.impl.SinkUtils;
31  import org.apache.maven.doxia.sink.impl.Xhtml5BaseSink;
32  import org.apache.maven.doxia.util.HtmlTools;
33  import org.codehaus.plexus.util.StringUtils;
34  
35  /**
36   * <a href="https://www.w3.org/TR/html52/">XHTML 5.2</a> sink implementation.
37   */
38  public class Xhtml5Sink
39      extends Xhtml5BaseSink
40      implements Xhtml5Markup
41  {
42      // ----------------------------------------------------------------------
43      // Instance fields
44      // ----------------------------------------------------------------------
45  
46      private String encoding;
47  
48      private String languageId;
49  
50      /** An indication on if we're inside a head title. */
51      private boolean headTitleFlag;
52  
53      // ----------------------------------------------------------------------
54      // Constructors
55      // ----------------------------------------------------------------------
56  
57      /**
58       * Constructor, initialize the Writer.
59       *
60       * @param writer not null writer to write the result.
61       */
62      protected Xhtml5Sink( Writer writer )
63      {
64          super( writer );
65      }
66  
67      /**
68       * Constructor, initialize the Writer and tells which encoding is used.
69       *
70       * @param writer not null writer to write the result.
71       * @param encoding the encoding used, that should be written to the generated HTML content
72       * if not <code>null</code>.
73       */
74      protected Xhtml5Sink( Writer writer, String encoding )
75      {
76          super( writer );
77  
78          this.encoding = encoding;
79      }
80  
81      /**
82       * Constructor, initialize the Writer and tells which encoding and languageId are used.
83       *
84       * @param writer not null writer to write the result.
85       * @param encoding the encoding used, that should be written to the generated HTML content
86       * if not <code>null</code>.
87       * @param languageId language identifier for the root element as defined by
88       * <a href="ftp://ftp.isi.edu/in-notes/bcp/bcp47.txt">IETF BCP 47</a>, Tags for the Identification of Languages;
89       * in addition, the empty string may be specified.
90       */
91      protected Xhtml5Sink( Writer writer, String encoding, String languageId )
92      {
93          this( writer, encoding );
94  
95          this.languageId = languageId;
96      }
97  
98      /**
99       * {@inheritDoc}
100      */
101     public void head()
102     {
103         init();
104 
105         setHeadFlag( true );
106 
107         write( "<!DOCTYPE html>" );
108 
109         MutableAttributeSet atts = new SinkEventAttributeSet();
110         atts.addAttribute( "xmlns", XHTML5_NAMESPACE );
111 
112         if ( languageId != null )
113         {
114             atts.addAttribute( Attribute.LANG.toString(), languageId );
115             atts.addAttribute( "xml:lang", languageId );
116         }
117 
118         writeStartTag( HTML, atts );
119 
120         writeStartTag( HEAD );
121     }
122 
123     /**
124      * {@inheritDoc}
125      */
126     public void head_()
127     {
128         if ( !isHeadTitleFlag() )
129         {
130             // The content of element type "head" must match
131             // "((script|style|meta|link|object|isindex)*,
132             //  ((title,(script|style|meta|link|object|isindex)*,
133             //  (base,(script|style|meta|link|object|isindex)*)?)|(base,(script|style|meta|link|object|isindex)*,
134             //  (title,(script|style|meta|link|object|isindex)*))))"
135             writeStartTag( TITLE );
136             writeEndTag( TITLE );
137         }
138 
139         setHeadFlag( false );
140         setHeadTitleFlag( false );
141 
142         if ( encoding != null )
143         {
144             write( "<meta charset=\"" + encoding + "\"/>" );
145         }
146 
147         writeEndTag( HEAD );
148     }
149 
150     /**
151      * {@inheritDoc}
152      *
153      * @see javax.swing.text.html.HTML.Tag#TITLE
154      */
155     public void title()
156     {
157         setHeadTitleFlag( true );
158 
159         writeStartTag( TITLE );
160     }
161 
162     /**
163      * {@inheritDoc}
164      *
165      * @see javax.swing.text.html.HTML.Tag#TITLE
166      */
167     public void title_()
168     {
169         content( getTextBuffer().toString() );
170 
171         writeEndTag( TITLE );
172 
173         resetTextBuffer();
174 
175     }
176 
177     /**
178      * {@inheritDoc}
179      *
180      * @see javax.swing.text.html.HTML.Tag#META
181      */
182     public void author_()
183     {
184         if ( getTextBuffer().length() > 0 )
185         {
186             MutableAttributeSet att = new SinkEventAttributeSet();
187             att.addAttribute( Attribute.NAME, "author" );
188             String text = HtmlTools.escapeHTML( getTextBuffer().toString() );
189             // hack: un-escape numerical entities that have been escaped above
190             // note that numerical entities should really be added as one unicode character in the first place
191             text = StringUtils.replace( text, "&amp;#", "&#" );
192             att.addAttribute( Attribute.CONTENT, text );
193 
194             writeSimpleTag( META, att );
195 
196             resetTextBuffer();
197         }
198     }
199 
200     /**
201      * {@inheritDoc}
202      *
203      * @see javax.swing.text.html.HTML.Tag#META
204      */
205     public void date_()
206     {
207         if ( getTextBuffer().length() > 0 )
208         {
209             MutableAttributeSet att = new SinkEventAttributeSet();
210             att.addAttribute( Attribute.NAME, "date" );
211             att.addAttribute( Attribute.CONTENT, getTextBuffer().toString() );
212 
213             writeSimpleTag( META, att );
214 
215             resetTextBuffer();
216         }
217     }
218 
219     /**
220      * {@inheritDoc}
221      *
222      * @see javax.swing.text.html.HTML.Tag#BODY
223      */
224     public void body()
225     {
226         writeStartTag( BODY );
227     }
228 
229     /**
230      * {@inheritDoc}
231      *
232      * @see javax.swing.text.html.HTML.Tag#BODY
233      * @see javax.swing.text.html.HTML.Tag#HTML
234      */
235     public void body_()
236     {
237         writeEndTag( BODY );
238 
239         writeEndTag( HTML );
240 
241         flush();
242 
243         init();
244     }
245 
246     /**
247      * {@inheritDoc}
248      *
249      * Starts a section..
250      * @see javax.swing.text.html.HTML.Tag#DIV
251      */
252     protected void onSection( int depth, SinkEventAttributes attributes )
253     {
254         if ( depth >= SECTION_LEVEL_1 && depth <= SECTION_LEVEL_5 )
255         {
256             MutableAttributeSet att = new SinkEventAttributeSet();
257             att.addAttributes( SinkUtils.filterAttributes(
258                     attributes, SinkUtils.SINK_BASE_ATTRIBUTES  ) );
259 
260             writeStartTag( HtmlMarkup.SECTION, att );
261         }
262     }
263 
264     /**
265      * {@inheritDoc}
266      *
267      * Ends a section.
268      * @see javax.swing.text.html.HTML.Tag#DIV
269      */
270     protected void onSection_( int depth )
271     {
272         if ( depth >= SECTION_LEVEL_1 && depth <= SECTION_LEVEL_5 )
273         {
274             writeEndTag( HtmlMarkup.SECTION );
275         }
276     }
277 
278     /**
279      * {@inheritDoc}
280      *
281      * Starts a section title.
282      * @see javax.swing.text.html.HTML.Tag#H2
283      * @see javax.swing.text.html.HTML.Tag#H3
284      * @see javax.swing.text.html.HTML.Tag#H4
285      * @see javax.swing.text.html.HTML.Tag#H5
286      * @see javax.swing.text.html.HTML.Tag#H6
287      */
288     protected void onSectionTitle( int depth, SinkEventAttributes attributes )
289     {
290         MutableAttributeSet atts = SinkUtils.filterAttributes(
291                 attributes, SinkUtils.SINK_SECTION_ATTRIBUTES  );
292 
293         if ( depth == SECTION_LEVEL_1 )
294         {
295             writeStartTag( HtmlMarkup.H2, atts );
296         }
297         else if ( depth == SECTION_LEVEL_2 )
298         {
299             writeStartTag( HtmlMarkup.H3, atts );
300         }
301         else if ( depth == SECTION_LEVEL_3 )
302         {
303             writeStartTag( HtmlMarkup.H4, atts );
304         }
305         else if ( depth == SECTION_LEVEL_4 )
306         {
307             writeStartTag( HtmlMarkup.H5, atts );
308         }
309         else if ( depth == SECTION_LEVEL_5 )
310         {
311             writeStartTag( HtmlMarkup.H6, atts );
312         }
313     }
314 
315     /**
316      * {@inheritDoc}
317      *
318      * Ends a section title.
319      * @see javax.swing.text.html.HTML.Tag#H2
320      * @see javax.swing.text.html.HTML.Tag#H3
321      * @see javax.swing.text.html.HTML.Tag#H4
322      * @see javax.swing.text.html.HTML.Tag#H5
323      * @see javax.swing.text.html.HTML.Tag#H6
324      */
325     protected void onSectionTitle_( int depth )
326     {
327         if ( depth == SECTION_LEVEL_1 )
328         {
329             writeEndTag( HtmlMarkup.H2 );
330         }
331         else if ( depth == SECTION_LEVEL_2 )
332         {
333             writeEndTag( HtmlMarkup.H3 );
334         }
335         else if ( depth == SECTION_LEVEL_3 )
336         {
337             writeEndTag( HtmlMarkup.H4 );
338         }
339         else if ( depth == SECTION_LEVEL_4 )
340         {
341             writeEndTag( HtmlMarkup.H5 );
342         }
343         else if ( depth == SECTION_LEVEL_5 )
344         {
345             writeEndTag( HtmlMarkup.H6 );
346         }
347     }
348 
349     // ----------------------------------------------------------------------
350     // Public protected methods
351     // ----------------------------------------------------------------------
352 
353     /**
354      * <p>Setter for the field <code>headTitleFlag</code>.</p>
355      *
356      * @param headTitleFlag an header title flag.
357      * @since 1.1
358      */
359     protected void setHeadTitleFlag( boolean headTitleFlag )
360     {
361         this.headTitleFlag = headTitleFlag;
362     }
363 
364     /**
365      * <p>isHeadTitleFlag.</p>
366      *
367      * @return the current headTitleFlag.
368      * @since 1.1
369      */
370     protected boolean isHeadTitleFlag()
371     {
372         return this.headTitleFlag ;
373     }
374 }