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, "&#", "&#" );
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 }