1 package org.apache.maven.doxia.module.xdoc;
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.sink.SinkEventAttributes;
28 import org.apache.maven.doxia.sink.impl.SinkEventAttributeSet;
29 import org.apache.maven.doxia.sink.impl.SinkUtils;
30 import org.apache.maven.doxia.sink.impl.XhtmlBaseSink;
31 import org.apache.maven.doxia.util.HtmlTools;
32
33 import org.codehaus.plexus.util.StringUtils;
34
35 /**
36 * <a href="http://maven.apache.org/doxia/references/xdoc-format.html">Xdoc</a> Sink implementation.
37 * <br/>
38 * It uses the Xdoc XSD <a href="http://maven.apache.org/xsd/xdoc-2.0.xsd">
39 * http://maven.apache.org/xsd/xdoc-2.0.xsd</a>.
40 *
41 * @author <a href="mailto:james@jamestaylor.org">James Taylor</a>
42 * @version $Id: XdocSink.java 1726411 2016-01-23 16:34:09Z hboutemy $
43 * @since 1.0
44 */
45 public class XdocSink
46 extends XhtmlBaseSink
47 implements XdocMarkup
48 {
49 // ----------------------------------------------------------------------
50 // Instance fields
51 // ----------------------------------------------------------------------
52
53 /** An indication on if we're inside a box (verbatim). */
54 private boolean boxedFlag;
55
56 private String encoding;
57
58 private String languageId;
59
60 // ----------------------------------------------------------------------
61 // Constructors
62 // ----------------------------------------------------------------------
63
64 /**
65 * Constructor, initialize the Writer.
66 *
67 * @param writer not null writer to write the result. <b>Should</b> be an UTF-8 Writer.
68 * You could use <code>newXmlWriter</code> methods from {@link org.codehaus.plexus.util.WriterFactory}.
69 */
70 protected XdocSink( Writer writer )
71 {
72 super( writer );
73 }
74
75 /**
76 * Constructor, initialize the Writer and tells which encoding is used.
77 *
78 * @param writer not null writer to write the result.
79 * @param encoding the encoding used, that should be written to the generated HTML content
80 * if not <code>null</code>.
81 * @since 1.1
82 */
83 protected XdocSink( Writer writer, String encoding )
84 {
85 this( writer );
86 this.encoding = encoding;
87 }
88
89 /**
90 * Constructor, initialize the Writer and tells which encoding and languageId are used.
91 *
92 * @param writer not null writer to write the result.
93 * @param encoding the encoding used, that should be written to the generated HTML content
94 * if not <code>null</code>.
95 * @param languageId language identifier for the root element as defined by
96 * <a href="ftp://ftp.isi.edu/in-notes/bcp/bcp47.txt">IETF BCP 47</a>, Tags for the Identification of Languages;
97 * in addition, the empty string may be specified.
98 * @since 1.1
99 */
100 protected XdocSink( Writer writer, String encoding, String languageId )
101 {
102 this( writer, encoding );
103
104 this.languageId = languageId;
105 }
106
107 // ----------------------------------------------------------------------
108 // Public protected methods
109 // ----------------------------------------------------------------------
110
111 /** {@inheritDoc} */
112 protected void init()
113 {
114 super.init();
115
116 boxedFlag = false;
117 }
118
119 /**
120 * {@inheritDoc}
121 * @see #head(org.apache.maven.doxia.sink.SinkEventAttributes)
122 */
123 public void head()
124 {
125 head( null );
126 }
127
128 /**
129 * {@inheritDoc}
130 * @see XdocMarkup#DOCUMENT_TAG
131 * @see XdocMarkup#PROPERTIES_TAG
132 */
133 public void head( SinkEventAttributes attributes )
134 {
135 init();
136
137 setHeadFlag( true );
138
139 write( "<?xml version=\"1.0\"" );
140 if ( encoding != null )
141 {
142 write( " encoding=\"" + encoding + "\"" );
143 }
144 write( "?>" );
145
146 MutableAttributeSet atts = new SinkEventAttributeSet();
147 atts.addAttribute( "xmlns", XDOC_NAMESPACE );
148 atts.addAttribute( "xmlns:xsi", XML_NAMESPACE );
149 atts.addAttribute( "xsi:schemaLocation", XDOC_NAMESPACE + " " + XDOC_SYSTEM_ID );
150
151 if ( languageId != null )
152 {
153 atts.addAttribute( Attribute.LANG.toString(), languageId );
154 atts.addAttribute( "xml:lang", languageId );
155 }
156
157 if ( attributes != null )
158 {
159 atts.addAttributes( attributes );
160 }
161
162 writeStartTag( DOCUMENT_TAG, atts );
163
164 writeStartTag( PROPERTIES_TAG );
165 }
166
167 /**
168 * {@inheritDoc}
169 * @see XdocMarkup#DOCUMENT_TAG
170 * @see XdocMarkup#PROPERTIES_TAG
171 */
172 public void head_()
173 {
174 setHeadFlag( false );
175
176 writeEndTag( PROPERTIES_TAG );
177 }
178
179 /**
180 * {@inheritDoc}
181 * @see javax.swing.text.html.HTML.Tag#TITLE
182 */
183 public void title()
184 {
185 writeStartTag( TITLE );
186 }
187
188 /**
189 * {@inheritDoc}
190 * @see javax.swing.text.html.HTML.Tag#TITLE
191 */
192 public void title_()
193 {
194 content( getTextBuffer().toString() );
195
196 writeEndTag( TITLE );
197
198 resetTextBuffer();
199 }
200
201 /**
202 * {@inheritDoc}
203 * @see XdocMarkup#AUTHOR_TAG
204 */
205 public void author_()
206 {
207 if ( getTextBuffer().length() > 0 )
208 {
209 writeStartTag( AUTHOR_TAG );
210 String text = HtmlTools.escapeHTML( getTextBuffer().toString() );
211 // hack: un-escape numerical entities that have been escaped above
212 // note that numerical entities should really be written as one unicode character in the first place
213 text = StringUtils.replace( text, "&#", "&#" );
214 write( text );
215 writeEndTag( AUTHOR_TAG );
216 resetTextBuffer();
217 }
218 }
219
220 /**
221 * {@inheritDoc}
222 * @see XdocMarkup#DATE_TAG
223 */
224 public void date_()
225 {
226 if ( getTextBuffer().length() > 0 )
227 {
228 writeStartTag( DATE_TAG );
229 content( getTextBuffer().toString() );
230 writeEndTag( DATE_TAG );
231 resetTextBuffer();
232 }
233 }
234
235 /**
236 * {@inheritDoc}
237 * @see #body(org.apache.maven.doxia.sink.SinkEventAttributes)
238 */
239 public void body()
240 {
241 body( null );
242 }
243
244 /**
245 * {@inheritDoc}
246 * @see javax.swing.text.html.HTML.Tag#BODY
247 */
248 public void body( SinkEventAttributes attributes )
249 {
250 writeStartTag( BODY, attributes );
251 }
252
253 /**
254 * {@inheritDoc}
255 * @see javax.swing.text.html.HTML.Tag#BODY
256 * @see XdocMarkup#DOCUMENT_TAG
257 */
258 public void body_()
259 {
260 writeEndTag( BODY );
261
262 writeEndTag( DOCUMENT_TAG );
263
264 flush();
265
266 init();
267 }
268
269 // ----------------------------------------------------------------------
270 // Sections
271 // ----------------------------------------------------------------------
272
273 /**
274 * {@inheritDoc}
275 *
276 * Starts a section.
277 * @see XdocMarkup#SECTION_TAG
278 * @see XdocMarkup#SUBSECTION_TAG
279 */
280 protected void onSection( int depth, SinkEventAttributes attributes )
281 {
282 if ( depth == SECTION_LEVEL_1 )
283 {
284 write( String.valueOf( LESS_THAN ) + SECTION_TAG.toString()
285 + SinkUtils.getAttributeString(
286 SinkUtils.filterAttributes( attributes, SinkUtils.SINK_BASE_ATTRIBUTES ) )
287 + String.valueOf( SPACE ) + Attribute.NAME + String.valueOf( EQUAL ) + String.valueOf( QUOTE ) );
288 }
289 else if ( depth == SECTION_LEVEL_2 )
290 {
291 write( String.valueOf( LESS_THAN ) + SUBSECTION_TAG.toString()
292 + SinkUtils.getAttributeString(
293 SinkUtils.filterAttributes( attributes, SinkUtils.SINK_BASE_ATTRIBUTES ) )
294 + String.valueOf( SPACE ) + Attribute.NAME + String.valueOf( EQUAL ) + String.valueOf( QUOTE ) );
295 }
296 }
297
298 /**
299 * {@inheritDoc}
300 *
301 * Ends a section.
302 * @see XdocMarkup#SECTION_TAG
303 * @see XdocMarkup#SUBSECTION_TAG
304 */
305 protected void onSection_( int depth )
306 {
307 if ( depth == SECTION_LEVEL_1 )
308 {
309 writeEndTag( SECTION_TAG );
310 }
311 else if ( depth == SECTION_LEVEL_2 )
312 {
313 writeEndTag( SUBSECTION_TAG );
314 }
315 }
316
317 /**
318 * {@inheritDoc}
319 *
320 * Starts a section title.
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, SinkEventAttributes attributes )
326 {
327 MutableAttributeSet atts = SinkUtils.filterAttributes(
328 attributes, SinkUtils.SINK_SECTION_ATTRIBUTES );
329
330 if ( depth == SECTION_LEVEL_3 )
331 {
332 writeStartTag( H4, atts );
333 }
334 else if ( depth == SECTION_LEVEL_4 )
335 {
336 writeStartTag( H5, atts );
337 }
338 else if ( depth == SECTION_LEVEL_5 )
339 {
340 writeStartTag( H6, atts );
341 }
342 }
343
344 /**
345 * {@inheritDoc}
346 *
347 * Ends a section title.
348 * @see javax.swing.text.html.HTML.Tag#H4
349 * @see javax.swing.text.html.HTML.Tag#H5
350 * @see javax.swing.text.html.HTML.Tag#H6
351 */
352 protected void onSectionTitle_( int depth )
353 {
354 if ( depth == SECTION_LEVEL_1 || depth == SECTION_LEVEL_2 )
355 {
356 write( String.valueOf( QUOTE ) + String.valueOf( GREATER_THAN ) );
357 }
358 else if ( depth == SECTION_LEVEL_3 )
359 {
360 writeEndTag( H4 );
361 }
362 else if ( depth == SECTION_LEVEL_4 )
363 {
364 writeEndTag( H5 );
365 }
366 else if ( depth == SECTION_LEVEL_5 )
367 {
368 writeEndTag( H6 );
369 }
370 }
371
372 // -----------------------------------------------------------------------
373 //
374 // -----------------------------------------------------------------------
375
376 /**
377 * {@inheritDoc}
378 * @see XdocMarkup#SOURCE_TAG
379 * @see javax.swing.text.html.HTML.Tag#PRE
380 */
381 public void verbatim( SinkEventAttributes attributes )
382 {
383 setVerbatimFlag( true );
384
385 MutableAttributeSet atts = SinkUtils.filterAttributes(
386 attributes, SinkUtils.SINK_VERBATIM_ATTRIBUTES );
387
388
389 if ( atts == null )
390 {
391 atts = new SinkEventAttributeSet();
392 }
393
394 boolean boxed = false;
395
396 if ( atts.isDefined( SinkEventAttributes.DECORATION ) )
397 {
398 boxed = "boxed".equals(
399 (String) atts.getAttribute( SinkEventAttributes.DECORATION ) );
400 }
401
402 boxedFlag = boxed;
403 atts.removeAttribute( SinkEventAttributes.DECORATION );
404
405 if ( boxed )
406 {
407 writeStartTag( SOURCE_TAG, atts );
408 }
409 else
410 {
411 atts.removeAttribute( Attribute.ALIGN.toString() );
412 writeStartTag( PRE, atts );
413 }
414 }
415
416 /**
417 * {@inheritDoc}
418 * @see XdocMarkup#SOURCE_TAG
419 * @see javax.swing.text.html.HTML.Tag#PRE
420 */
421 public void verbatim_()
422 {
423 if ( boxedFlag )
424 {
425 writeEndTag( SOURCE_TAG );
426 }
427 else
428 {
429 writeEndTag( PRE );
430 }
431
432 setVerbatimFlag( false );
433
434 boxedFlag = false;
435 }
436
437 /**
438 * The default align is <code>center</code>.
439 *
440 * {@inheritDoc}
441 * @see javax.swing.text.html.HTML.Tag#TABLE
442 */
443 public void tableRows( int[] justification, boolean grid )
444 {
445 // similar to super.tableRows( justification, grid ) but without class.
446
447 this.tableRows = true;
448
449 setCellJustif( justification );
450
451 if ( this.tableAttributes == null )
452 {
453 this.tableAttributes = new SinkEventAttributeSet( 0 );
454 }
455
456 MutableAttributeSet att = new SinkEventAttributeSet();
457
458 if ( !tableAttributes.isDefined( Attribute.BORDER.toString() ) )
459 {
460 att.addAttribute( Attribute.BORDER, ( grid ? "1" : "0" ) );
461 }
462
463 att.addAttributes( tableAttributes );
464
465 tableAttributes.removeAttributes( tableAttributes );
466
467 writeStartTag( TABLE, att );
468 }
469
470 /**
471 * The default valign is <code>top</code>.
472 *
473 * {@inheritDoc}
474 * @see javax.swing.text.html.HTML.Tag#TR
475 */
476 public void tableRow()
477 {
478 MutableAttributeSet att = new SinkEventAttributeSet();
479 att.addAttribute( Attribute.VALIGN, "top" );
480
481 writeStartTag( TR, att );
482
483 setCellCount( 0 );
484 }
485
486 public void close()
487 {
488 super.close();
489
490 init();
491 }
492
493 /**
494 * Adds a link with an optional target.
495 *
496 * @param name the link name.
497 * @param target the link target, may be null.
498 */
499 public void link( String name, String target )
500 {
501 if ( isHeadFlag() )
502 {
503 return;
504 }
505
506 MutableAttributeSet att = new SinkEventAttributeSet();
507
508 att.addAttribute( Attribute.HREF, HtmlTools.escapeHTML( name ) );
509
510 if ( target != null )
511 {
512 att.addAttribute( Attribute.TARGET, target );
513 }
514
515 writeStartTag( A, att );
516 }
517
518 // ----------------------------------------------------------------------
519 //
520 // ----------------------------------------------------------------------
521
522 /**
523 * Write text to output, preserving white space.
524 *
525 * @param text The text to write.
526 * @deprecated use write(String)
527 */
528 protected void markup( String text )
529 {
530 write( text );
531 }
532 }