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