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, "&#", "&#");
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 }