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