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.commons.lang3.StringUtils;
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.Xhtml5BaseSink;
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       * You could use <code>newXmlWriter</code> methods from {@link org.codehaus.plexus.util.WriterFactory}.
60       */
61      protected XdocSink(Writer writer) {
62          super(writer);
63      }
64  
65      /**
66       * Constructor, initialize the Writer and tells which encoding is used.
67       *
68       * @param writer not null writer to write the result.
69       * @param encoding the encoding used, that should be written to the generated HTML content
70       * if not <code>null</code>.
71       * @since 1.1
72       */
73      protected XdocSink(Writer writer, String encoding) {
74          this(writer);
75          this.encoding = encoding;
76      }
77  
78      /**
79       * Constructor, initialize the Writer and tells which encoding and languageId are used.
80       *
81       * @param writer not null writer to write the result.
82       * @param encoding the encoding used, that should be written to the generated HTML content
83       * if not <code>null</code>.
84       * @param languageId language identifier for the root element as defined by
85       * <a href="ftp://ftp.isi.edu/in-notes/bcp/bcp47.txt">IETF BCP 47</a>, Tags for the Identification of Languages;
86       * in addition, the empty string may be specified.
87       * @since 1.1
88       */
89      protected XdocSink(Writer writer, String encoding, String languageId) {
90          this(writer, encoding);
91  
92          this.languageId = languageId;
93      }
94  
95      // ----------------------------------------------------------------------
96      // Public protected methods
97      // ----------------------------------------------------------------------
98  
99      /**
100      * {@inheritDoc}
101      */
102     protected void init() {
103         super.init();
104     }
105 
106     /**
107      * {@inheritDoc}
108      * @see XdocMarkup#DOCUMENT_TAG
109      * @see XdocMarkup#PROPERTIES_TAG
110      */
111     public void head(SinkEventAttributes attributes) {
112         init();
113 
114         setHeadFlag(true);
115 
116         write("<?xml version=\"1.0\"");
117         if (encoding != null) {
118             write(" encoding=\"" + encoding + "\"");
119         }
120         write("?>");
121 
122         MutableAttributeSet atts = new SinkEventAttributeSet();
123         atts.addAttribute("xmlns", XDOC_NAMESPACE);
124         atts.addAttribute("xmlns:xsi", XML_NAMESPACE);
125         atts.addAttribute("xsi:schemaLocation", XDOC_NAMESPACE + " " + XDOC_SYSTEM_ID);
126 
127         if (languageId != null) {
128             atts.addAttribute(Attribute.LANG.toString(), languageId);
129             atts.addAttribute("xml:lang", languageId);
130         }
131 
132         if (attributes != null) {
133             atts.addAttributes(attributes);
134         }
135 
136         writeStartTag(DOCUMENT_TAG, atts);
137 
138         writeStartTag(PROPERTIES_TAG);
139     }
140 
141     /**
142      * {@inheritDoc}
143      *
144      * @see XdocMarkup#DOCUMENT_TAG
145      * @see XdocMarkup#PROPERTIES_TAG
146      */
147     public void head_() {
148         setHeadFlag(false);
149 
150         writeEndTag(PROPERTIES_TAG);
151     }
152 
153     /**
154      * {@inheritDoc}
155      *
156      * @see javax.swing.text.html.HTML.Tag#TITLE
157      */
158     @Override
159     public void title(SinkEventAttributes attributes) {
160         writeStartTag(TITLE);
161     }
162 
163     /**
164      * {@inheritDoc}
165      *
166      * @see javax.swing.text.html.HTML.Tag#TITLE
167      */
168     public void title_() {
169         content(getTextBuffer().toString());
170 
171         writeEndTag(TITLE);
172 
173         resetTextBuffer();
174     }
175 
176     /**
177      * {@inheritDoc}
178      *
179      * @see XdocMarkup#AUTHOR_TAG
180      */
181     public void author_() {
182         if (getTextBuffer().length() > 0) {
183             writeStartTag(AUTHOR_TAG);
184             String text = HtmlTools.escapeHTML(getTextBuffer().toString());
185             // hack: un-escape numerical entities that have been escaped above
186             // note that numerical entities should really be written as one unicode character in the first place
187             text = StringUtils.replace(text, "&amp;#", "&#");
188             write(text);
189             writeEndTag(AUTHOR_TAG);
190             resetTextBuffer();
191         }
192     }
193 
194     /**
195      * {@inheritDoc}
196      *
197      * @see XdocMarkup#DATE_TAG
198      */
199     public void date_() {
200         if (getTextBuffer().length() > 0) {
201             writeStartTag(DATE_TAG);
202             content(getTextBuffer().toString());
203             writeEndTag(DATE_TAG);
204             resetTextBuffer();
205         }
206     }
207 
208     /**
209      * {@inheritDoc}
210      * @see javax.swing.text.html.HTML.Tag#BODY
211      */
212     public void body(SinkEventAttributes attributes) {
213         writeStartTag(BODY, attributes);
214     }
215 
216     /**
217      * {@inheritDoc}
218      *
219      * @see javax.swing.text.html.HTML.Tag#BODY
220      * @see XdocMarkup#DOCUMENT_TAG
221      */
222     public void body_() {
223         writeEndTag(BODY);
224 
225         writeEndTag(DOCUMENT_TAG);
226 
227         flush();
228 
229         init();
230     }
231 
232     // ----------------------------------------------------------------------
233     // Sections
234     // ----------------------------------------------------------------------
235 
236     /**
237      * {@inheritDoc}
238      *
239      * Starts a section.
240      * @see XdocMarkup#SECTION_TAG
241      * @see XdocMarkup#SUBSECTION_TAG
242      */
243     protected void onSection(int depth, SinkEventAttributes attributes) {
244         if (depth == SECTION_LEVEL_1) {
245             write(LESS_THAN
246                     + SECTION_TAG.toString()
247                     + SinkUtils.getAttributeString(
248                             SinkUtils.filterAttributes(attributes, SinkUtils.SINK_BASE_ATTRIBUTES))
249                     + SPACE
250                     + Attribute.NAME
251                     + EQUAL
252                     + QUOTE);
253         } else if (depth == SECTION_LEVEL_2) {
254             write(LESS_THAN
255                     + SUBSECTION_TAG.toString()
256                     + SinkUtils.getAttributeString(
257                             SinkUtils.filterAttributes(attributes, SinkUtils.SINK_BASE_ATTRIBUTES))
258                     + SPACE
259                     + Attribute.NAME
260                     + EQUAL
261                     + QUOTE);
262         }
263     }
264 
265     /**
266      * {@inheritDoc}
267      *
268      * Ends a section.
269      * @see XdocMarkup#SECTION_TAG
270      * @see XdocMarkup#SUBSECTION_TAG
271      */
272     protected void onSection_(int depth) {
273         if (depth == SECTION_LEVEL_1) {
274             writeEndTag(SECTION_TAG);
275         } else if (depth == SECTION_LEVEL_2) {
276             writeEndTag(SUBSECTION_TAG);
277         }
278     }
279 
280     /**
281      * {@inheritDoc}
282      *
283      * Starts a section title.
284      * @see #H3
285      * @see #H4
286      * @see #H5
287      * @see #H6
288      */
289     protected void onSectionTitle(int depth, SinkEventAttributes attributes) {
290         MutableAttributeSet atts = SinkUtils.filterAttributes(attributes, SinkUtils.SINK_SECTION_ATTRIBUTES);
291 
292         if (depth == SECTION_LEVEL_3) {
293             writeStartTag(H3, atts);
294         } else if (depth == SECTION_LEVEL_4) {
295             writeStartTag(H4, atts);
296         } else if (depth == SECTION_LEVEL_5) {
297             writeStartTag(H5, atts);
298         } else if (depth == SECTION_LEVEL_6) {
299             writeStartTag(H6, atts);
300         }
301     }
302 
303     /**
304      * {@inheritDoc}
305      *
306      * Ends a section title.
307      * @see #H3
308      * @see #H4
309      * @see #H5
310      * @see #H6
311      */
312     protected void onSectionTitle_(int depth) {
313         if (depth == SECTION_LEVEL_1 || depth == SECTION_LEVEL_2) {
314             write(String.valueOf(QUOTE) + GREATER_THAN);
315         } else if (depth == SECTION_LEVEL_3) {
316             writeEndTag(H3);
317         } else if (depth == SECTION_LEVEL_4) {
318             writeEndTag(H4);
319         } else if (depth == SECTION_LEVEL_5) {
320             writeEndTag(H5);
321         } else if (depth == SECTION_LEVEL_6) {
322             writeEndTag(H6);
323         }
324     }
325 
326     // -----------------------------------------------------------------------
327     //
328     // -----------------------------------------------------------------------
329 
330     /**
331      * {@inheritDoc}
332      *
333      * @see XdocMarkup#SOURCE_TAG
334      * @see javax.swing.text.html.HTML.Tag#PRE
335      * @param attributes a {@link org.apache.maven.doxia.sink.SinkEventAttributes} object.
336      */
337     public void verbatim(SinkEventAttributes attributes) {
338 
339         MutableAttributeSet atts = SinkUtils.filterAttributes(attributes, SinkUtils.SINK_VERBATIM_ATTRIBUTES);
340 
341         if (atts == null) {
342             atts = new SinkEventAttributeSet();
343         }
344 
345         this.setVerbatimMode(VerbatimMode.ON);
346         if (atts.isDefined(SinkEventAttributes.DECORATION)) {
347             if ("source".equals(atts.getAttribute(SinkEventAttributes.DECORATION))) {
348                 this.setVerbatimMode(VerbatimMode.ON_WITH_CODE);
349             }
350         }
351 
352         atts.removeAttribute(SinkEventAttributes.DECORATION);
353 
354         if (getVerbatimMode() == VerbatimMode.ON_WITH_CODE) {
355             writeStartTag(SOURCE_TAG, atts);
356         } else {
357             writeStartTag(PRE, atts);
358         }
359     }
360 
361     /**
362      * {@inheritDoc}
363      *
364      * @see XdocMarkup#SOURCE_TAG
365      * @see javax.swing.text.html.HTML.Tag#PRE
366      */
367     public void verbatim_() {
368         if (getVerbatimMode() == VerbatimMode.ON_WITH_CODE) {
369             writeEndTag(SOURCE_TAG);
370         } else {
371             writeEndTag(PRE);
372         }
373 
374         this.setVerbatimMode(VerbatimMode.OFF);
375     }
376 
377     /**
378      * {@inheritDoc}
379      *
380      * @see javax.swing.text.html.HTML.Tag#TABLE
381      */
382     public void tableRows(int[] justification, boolean grid) {
383         // similar to super.tableRows(justification, grid) but without class.
384 
385         setCellJustif(justification);
386 
387         MutableAttributeSet att = new SinkEventAttributeSet();
388 
389         if (!tableAttributes.isDefined(Attribute.BORDER.toString())) {
390             att.addAttribute(Attribute.BORDER, (grid ? "1" : "0"));
391         }
392 
393         att.addAttributes(tableAttributes);
394 
395         tableAttributes.removeAttributes(tableAttributes);
396 
397         writeStartTag(TABLE, att);
398     }
399 
400     /**
401      * {@inheritDoc}
402      *
403      * @see javax.swing.text.html.HTML.Tag#TR
404      */
405     @Override
406     public void tableRow(SinkEventAttributes attributes) {
407 
408         writeStartTag(TR, attributes);
409 
410         setCellCount(0);
411     }
412 
413     /**
414      * <p>close.</p>
415      */
416     public void close() {
417         super.close();
418 
419         init();
420     }
421 
422     // ----------------------------------------------------------------------
423     //
424     // ----------------------------------------------------------------------
425 
426 }