View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.doxia.module.xdoc;
20  
21  import javax.swing.text.MutableAttributeSet;
22  import javax.swing.text.html.HTML.Attribute;
23  
24  import java.io.Writer;
25  
26  import org.apache.maven.doxia.sink.SinkEventAttributes;
27  import org.apache.maven.doxia.sink.impl.SinkEventAttributeSet;
28  import org.apache.maven.doxia.sink.impl.SinkUtils;
29  import org.apache.maven.doxia.sink.impl.Xhtml5BaseSink;
30  import org.apache.maven.doxia.util.DoxiaStringUtils;
31  import org.apache.maven.doxia.util.HtmlTools;
32  
33  /**
34   * <a href="https://maven.apache.org/doxia/references/xdoc-format.html">Xdoc</a> Sink implementation.
35   * <br>
36   * It uses the Xdoc XSD <a href="https://maven.apache.org/xsd/xdoc-2.0.xsd">
37   * https://maven.apache.org/xsd/xdoc-2.0.xsd</a>.
38   *
39   * @author <a href="mailto:james@jamestaylor.org">James Taylor</a>
40   * @since 1.0
41   */
42  public class XdocSink extends Xhtml5BaseSink implements XdocMarkup {
43      // ----------------------------------------------------------------------
44      // Instance fields
45      // ----------------------------------------------------------------------
46  
47      private String encoding;
48  
49      private String languageId;
50  
51      // ----------------------------------------------------------------------
52      // Constructors
53      // ----------------------------------------------------------------------
54  
55      /**
56       * Constructor, initialize the Writer.
57       *
58       * @param writer not null writer to write the result. <b>Should</b> be an UTF-8 Writer.
59       */
60      protected XdocSink(Writer writer) {
61          super(writer);
62      }
63  
64      /**
65       * Constructor, initialize the Writer and tells which encoding is used.
66       *
67       * @param writer not null writer to write the result.
68       * @param encoding the encoding used, that should be written to the generated HTML content
69       * if not <code>null</code>.
70       * @since 1.1
71       */
72      protected XdocSink(Writer writer, String encoding) {
73          this(writer);
74          this.encoding = encoding;
75      }
76  
77      /**
78       * Constructor, initialize the Writer and tells which encoding and languageId are used.
79       *
80       * @param writer not null writer to write the result.
81       * @param encoding the encoding used, that should be written to the generated HTML content
82       * if not <code>null</code>.
83       * @param languageId language identifier for the root element as defined by
84       * <a href="ftp://ftp.isi.edu/in-notes/bcp/bcp47.txt">IETF BCP 47</a>, Tags for the Identification of Languages;
85       * in addition, the empty string may be specified.
86       * @since 1.1
87       */
88      protected XdocSink(Writer writer, String encoding, String languageId) {
89          this(writer, encoding);
90  
91          this.languageId = languageId;
92      }
93  
94      // ----------------------------------------------------------------------
95      // Public protected methods
96      // ----------------------------------------------------------------------
97  
98      /**
99       * {@inheritDoc}
100      */
101     protected void init() {
102         super.init();
103     }
104 
105     /**
106      * {@inheritDoc}
107      * @see XdocMarkup#DOCUMENT_TAG
108      * @see XdocMarkup#PROPERTIES_TAG
109      */
110     public void head(SinkEventAttributes attributes) {
111         init();
112 
113         setHeadFlag(true);
114 
115         write("<?xml version=\"1.0\"");
116         if (encoding != null) {
117             write(" encoding=\"" + encoding + "\"");
118         }
119         write("?>");
120 
121         MutableAttributeSet atts = new SinkEventAttributeSet();
122         atts.addAttribute("xmlns", XDOC_NAMESPACE);
123         atts.addAttribute("xmlns:xsi", XML_NAMESPACE);
124         atts.addAttribute("xsi:schemaLocation", XDOC_NAMESPACE + " " + XDOC_SYSTEM_ID);
125 
126         if (languageId != null) {
127             atts.addAttribute(Attribute.LANG.toString(), languageId);
128             atts.addAttribute("xml:lang", languageId);
129         }
130 
131         if (attributes != null) {
132             atts.addAttributes(attributes);
133         }
134 
135         writeStartTag(DOCUMENT_TAG, atts);
136 
137         writeStartTag(PROPERTIES_TAG);
138     }
139 
140     /**
141      * {@inheritDoc}
142      *
143      * @see XdocMarkup#DOCUMENT_TAG
144      * @see XdocMarkup#PROPERTIES_TAG
145      */
146     public void head_() {
147         setHeadFlag(false);
148 
149         writeEndTag(PROPERTIES_TAG);
150     }
151 
152     /**
153      * {@inheritDoc}
154      *
155      * @see javax.swing.text.html.HTML.Tag#TITLE
156      */
157     @Override
158     public void title(SinkEventAttributes attributes) {
159         writeStartTag(TITLE);
160     }
161 
162     /**
163      * {@inheritDoc}
164      *
165      * @see javax.swing.text.html.HTML.Tag#TITLE
166      */
167     public void title_() {
168         content(getTextBuffer().toString());
169 
170         writeEndTag(TITLE);
171 
172         resetTextBuffer();
173     }
174 
175     /**
176      * {@inheritDoc}
177      *
178      * @see XdocMarkup#AUTHOR_TAG
179      */
180     public void author_() {
181         if (getTextBuffer().length() > 0) {
182             writeStartTag(AUTHOR_TAG);
183             String text = HtmlTools.escapeHTML(getTextBuffer().toString());
184             // hack: un-escape numerical entities that have been escaped above
185             // note that numerical entities should really be written as one unicode character in the first place
186             text = DoxiaStringUtils.replace(text, "&amp;#", "&#");
187             write(text);
188             writeEndTag(AUTHOR_TAG);
189             resetTextBuffer();
190         }
191     }
192 
193     /**
194      * {@inheritDoc}
195      *
196      * @see XdocMarkup#DATE_TAG
197      */
198     public void date_() {
199         if (getTextBuffer().length() > 0) {
200             writeStartTag(DATE_TAG);
201             content(getTextBuffer().toString());
202             writeEndTag(DATE_TAG);
203             resetTextBuffer();
204         }
205     }
206 
207     /**
208      * {@inheritDoc}
209      * @see javax.swing.text.html.HTML.Tag#BODY
210      */
211     public void body(SinkEventAttributes attributes) {
212         writeStartTag(BODY, attributes);
213     }
214 
215     /**
216      * {@inheritDoc}
217      *
218      * @see javax.swing.text.html.HTML.Tag#BODY
219      * @see XdocMarkup#DOCUMENT_TAG
220      */
221     public void body_() {
222         writeEndTag(BODY);
223 
224         writeEndTag(DOCUMENT_TAG);
225 
226         flush();
227 
228         init();
229     }
230 
231     // ----------------------------------------------------------------------
232     // Sections
233     // ----------------------------------------------------------------------
234 
235     /**
236      * {@inheritDoc}
237      *
238      * Starts a section.
239      * @see XdocMarkup#SECTION_TAG
240      * @see XdocMarkup#SUBSECTION_TAG
241      */
242     protected void onSection(int depth, SinkEventAttributes attributes) {
243         if (depth == SECTION_LEVEL_1) {
244             write(LESS_THAN
245                     + SECTION_TAG.toString()
246                     + SinkUtils.getAttributeString(
247                             SinkUtils.filterAttributes(attributes, SinkUtils.SINK_BASE_ATTRIBUTES))
248                     + SPACE
249                     + Attribute.NAME
250                     + EQUAL
251                     + QUOTE);
252         } else if (depth == SECTION_LEVEL_2) {
253             write(LESS_THAN
254                     + SUBSECTION_TAG.toString()
255                     + SinkUtils.getAttributeString(
256                             SinkUtils.filterAttributes(attributes, SinkUtils.SINK_BASE_ATTRIBUTES))
257                     + SPACE
258                     + Attribute.NAME
259                     + EQUAL
260                     + QUOTE);
261         }
262     }
263 
264     /**
265      * {@inheritDoc}
266      *
267      * Ends a section.
268      * @see XdocMarkup#SECTION_TAG
269      * @see XdocMarkup#SUBSECTION_TAG
270      */
271     protected void onSection_(int depth) {
272         if (depth == SECTION_LEVEL_1) {
273             writeEndTag(SECTION_TAG);
274         } else if (depth == SECTION_LEVEL_2) {
275             writeEndTag(SUBSECTION_TAG);
276         }
277     }
278 
279     /**
280      * {@inheritDoc}
281      *
282      * Starts a section title.
283      * @see #H3
284      * @see #H4
285      * @see #H5
286      * @see #H6
287      */
288     protected void onSectionTitle(int depth, SinkEventAttributes attributes) {
289         MutableAttributeSet atts = SinkUtils.filterAttributes(attributes, SinkUtils.SINK_SECTION_ATTRIBUTES);
290 
291         if (depth == SECTION_LEVEL_3) {
292             writeStartTag(H3, atts);
293         } else if (depth == SECTION_LEVEL_4) {
294             writeStartTag(H4, atts);
295         } else if (depth == SECTION_LEVEL_5) {
296             writeStartTag(H5, atts);
297         } else if (depth == SECTION_LEVEL_6) {
298             writeStartTag(H6, atts);
299         }
300     }
301 
302     /**
303      * {@inheritDoc}
304      *
305      * Ends a section title.
306      * @see #H3
307      * @see #H4
308      * @see #H5
309      * @see #H6
310      */
311     protected void onSectionTitle_(int depth) {
312         if (depth == SECTION_LEVEL_1 || depth == SECTION_LEVEL_2) {
313             write(String.valueOf(QUOTE) + GREATER_THAN);
314         } else if (depth == SECTION_LEVEL_3) {
315             writeEndTag(H3);
316         } else if (depth == SECTION_LEVEL_4) {
317             writeEndTag(H4);
318         } else if (depth == SECTION_LEVEL_5) {
319             writeEndTag(H5);
320         } else if (depth == SECTION_LEVEL_6) {
321             writeEndTag(H6);
322         }
323     }
324 
325     // -----------------------------------------------------------------------
326     //
327     // -----------------------------------------------------------------------
328 
329     /**
330      * {@inheritDoc}
331      *
332      * @see XdocMarkup#SOURCE_TAG
333      * @see javax.swing.text.html.HTML.Tag#PRE
334      * @param attributes a {@link org.apache.maven.doxia.sink.SinkEventAttributes} object.
335      */
336     public void verbatim(SinkEventAttributes attributes) {
337 
338         MutableAttributeSet atts = SinkUtils.filterAttributes(attributes, SinkUtils.SINK_VERBATIM_ATTRIBUTES);
339 
340         if (atts == null) {
341             atts = new SinkEventAttributeSet();
342         }
343 
344         this.setVerbatimMode(VerbatimMode.ON);
345         if (atts.isDefined(SinkEventAttributes.DECORATION)) {
346             if ("source".equals(atts.getAttribute(SinkEventAttributes.DECORATION))) {
347                 this.setVerbatimMode(VerbatimMode.ON_WITH_CODE);
348             }
349         }
350 
351         atts.removeAttribute(SinkEventAttributes.DECORATION);
352 
353         if (getVerbatimMode() == VerbatimMode.ON_WITH_CODE) {
354             writeStartTag(SOURCE_TAG, atts);
355         } else {
356             writeStartTag(PRE, atts);
357         }
358     }
359 
360     /**
361      * {@inheritDoc}
362      *
363      * @see XdocMarkup#SOURCE_TAG
364      * @see javax.swing.text.html.HTML.Tag#PRE
365      */
366     public void verbatim_() {
367         if (getVerbatimMode() == VerbatimMode.ON_WITH_CODE) {
368             writeEndTag(SOURCE_TAG);
369         } else {
370             writeEndTag(PRE);
371         }
372 
373         this.setVerbatimMode(VerbatimMode.OFF);
374     }
375 
376     /**
377      * {@inheritDoc}
378      *
379      * @see javax.swing.text.html.HTML.Tag#TABLE
380      */
381     public void tableRows(int[] justification, boolean grid) {
382         // similar to super.tableRows(justification, grid) but without class.
383 
384         setCellJustif(justification);
385 
386         MutableAttributeSet att = new SinkEventAttributeSet();
387 
388         if (!tableAttributes.isDefined(Attribute.BORDER.toString())) {
389             att.addAttribute(Attribute.BORDER, (grid ? "1" : "0"));
390         }
391 
392         att.addAttributes(tableAttributes);
393 
394         tableAttributes.removeAttributes(tableAttributes);
395 
396         writeStartTag(TABLE, att);
397     }
398 
399     /**
400      * {@inheritDoc}
401      *
402      * @see javax.swing.text.html.HTML.Tag#TR
403      */
404     @Override
405     public void tableRow(SinkEventAttributes attributes) {
406 
407         writeStartTag(TR, attributes);
408 
409         setCellCount(0);
410     }
411 
412     /**
413      * <p>close.</p>
414      */
415     public void close() {
416         super.close();
417 
418         init();
419     }
420 
421     // ----------------------------------------------------------------------
422     //
423     // ----------------------------------------------------------------------
424 
425 }