View Javadoc

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.SinkEventAttributeSet;
28  import org.apache.maven.doxia.sink.SinkEventAttributes;
29  import org.apache.maven.doxia.sink.SinkUtils;
30  import org.apache.maven.doxia.sink.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 926772 2010-03-23 20:52:37Z ltheussl $
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, "&amp;#", "&#" );
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 }