1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.doxia.siterenderer.sink;
20
21 import java.io.StringWriter;
22 import java.io.Writer;
23 import java.util.ArrayList;
24 import java.util.List;
25
26 import org.apache.maven.doxia.markup.HtmlMarkup;
27 import org.apache.maven.doxia.module.xhtml5.Xhtml5Sink;
28 import org.apache.maven.doxia.sink.SinkEventAttributes;
29 import org.apache.maven.doxia.sink.impl.SinkEventAttributeSet;
30 import org.apache.maven.doxia.sink.impl.SinkUtils;
31 import org.apache.maven.doxia.site.MermaidConfiguration;
32 import org.apache.maven.doxia.siterenderer.DefaultSiteRenderer;
33 import org.apache.maven.doxia.siterenderer.DocumentContent;
34 import org.apache.maven.doxia.siterenderer.DocumentRenderingContext;
35
36
37
38
39
40
41
42
43 @SuppressWarnings("checkstyle:methodname")
44 public class SiteRendererSink extends Xhtml5Sink implements DocumentContent {
45 private String date;
46
47 private String title;
48
49 private List<String> authors = new ArrayList<>();
50
51 private final StringWriter headWriter;
52
53
54 private StringBuilder verbatimBuffer;
55
56 private final Writer writer;
57
58 private final MermaidConfiguration mermaidConfig;
59
60 private DocumentRenderingContext docRenderingContext;
61
62 private boolean containsMermaidDiagram = false;
63
64 private boolean insideMermaidCodeElement = false;
65
66
67
68
69
70 public SiteRendererSink(DocumentRenderingContext docRenderingContext) {
71 this(docRenderingContext, null);
72 }
73
74 public SiteRendererSink(DocumentRenderingContext docRenderingContext, MermaidConfiguration mermaid) {
75 this(new StringWriter(), docRenderingContext, mermaid);
76 }
77
78 private SiteRendererSink(
79 StringWriter writer, DocumentRenderingContext docRenderingContext, MermaidConfiguration mermaid) {
80 super(writer);
81
82 this.writer = writer;
83 this.headWriter = new StringWriter();
84 this.docRenderingContext = docRenderingContext;
85 this.mermaidConfig = mermaid;
86
87
88 super.contentStack.push(HtmlMarkup.MAIN);
89 }
90
91
92 @Override
93 public void title_() {
94 if (getTextBuffer().length() > 0) {
95 title = getTextBuffer().toString();
96 }
97
98 resetTextBuffer();
99 }
100
101
102
103
104
105
106
107 @Override
108 public void title(SinkEventAttributes attributes) {
109 resetTextBuffer();
110 }
111
112
113 @Override
114 public void author(SinkEventAttributes attributes) {
115 resetTextBuffer();
116 }
117
118
119 @Override
120 public void author_() {
121 if (getTextBuffer().length() > 0) {
122 String text = getTextBuffer().toString().trim();
123 authors.add(text);
124 }
125
126 resetTextBuffer();
127 }
128
129
130 @Override
131 public void date(SinkEventAttributes attributes) {
132 resetTextBuffer();
133 }
134
135
136 @Override
137 public void date_() {
138 if (getTextBuffer().length() > 0) {
139 date = getTextBuffer().toString().trim();
140 }
141
142 resetTextBuffer();
143 }
144
145 @Override
146 public void verbatim(SinkEventAttributes attributes) {
147 if (mermaidConfig != null && normalizeClassAttributesForMermaid(attributes)) {
148 containsMermaidDiagram = true;
149
150
151 SinkEventAttributes filteredAttributes = (SinkEventAttributes)
152 SinkUtils.filterAttributes(attributes, new String[] {SinkEventAttributes.DECORATION});
153 super.verbatim(filteredAttributes);
154 } else {
155
156
157 verbatimBuffer = new StringBuilder();
158 super.verbatim(attributes);
159 }
160 }
161
162 @Override
163 public void verbatim_() {
164 flushVerbatimBuffer(false);
165 super.verbatim_();
166 }
167
168 @Override
169 public void inline(SinkEventAttributes attributes) {
170 if (attributes.containsAttributes(SinkEventAttributeSet.Semantics.CODE)
171 && mermaidConfig != null
172 && normalizeClassAttributesForMermaid(attributes)) {
173 containsMermaidDiagram = true;
174
175 super.inline(attributes);
176
177 inlineStack.pop();
178 insideMermaidCodeElement = true;
179
180
181 flushVerbatimBuffer(true);
182 } else {
183 flushVerbatimBuffer(false);
184 super.inline(attributes);
185 }
186 }
187
188 @Override
189 public void inline_() {
190 if (insideMermaidCodeElement) {
191
192 insideMermaidCodeElement = false;
193 } else {
194 super.inline_();
195 }
196 }
197
198 private void flushVerbatimBuffer(boolean stripCodeElement) {
199 if (verbatimBuffer != null) {
200 String buffer = verbatimBuffer.toString();
201 if (stripCodeElement) {
202
203
204 buffer = buffer.replaceFirst("<pre><code([^>]*)>", "<pre$1>");
205 }
206 verbatimBuffer = null;
207 write(buffer);
208 }
209 }
210
211
212
213
214
215
216
217 boolean normalizeClassAttributesForMermaid(SinkEventAttributes attributes) {
218 String lang = attributes != null ? (String) attributes.getAttribute(SinkEventAttributes.CLASS) : null;
219 if ("language-mermaid"
220 .equals(lang)) {
221 attributes.addAttribute(SinkEventAttributes.CLASS, "mermaid");
222 return true;
223 } else if ("mermaid".equals(lang)) {
224 return true;
225 }
226 return false;
227 }
228
229
230
231
232
233 private void writeMermaidScript() {
234 if (mermaidConfig.getExternalJs() != null) {
235 write(mermaidConfig.getExternalJs().asScriptTag());
236 } else {
237 write("\n<script src=\"");
238 write(docRenderingContext.getRelativePath());
239
240 if (mermaidConfig.isUseTiny()) {
241
242
243 write("/js/mermaid-" + DefaultSiteRenderer.MERMAID_VERSION + ".tiny.min.js");
244 } else {
245
246 write("/js/mermaid-" + DefaultSiteRenderer.MERMAID_VERSION + ".min.js");
247 }
248 write("\"></script>\n");
249 }
250 write("\n<script>\n");
251 if (mermaidConfig.getConfig() != null) {
252 write("mermaid.initialize(" + mermaidConfig.getConfig() + ");\n");
253 } else {
254
255
256 write("mermaid.initialize({startOnLoad:true, securityLevel: 'loose'});\n");
257 }
258 write("</script>\n");
259 }
260
261
262
263
264
265
266
267 @Override
268 public void body_() {
269 if (containsMermaidDiagram && mermaidConfig != null) {
270 writeMermaidScript();
271 }
272 }
273
274
275
276
277
278
279
280 @Override
281 public void body(SinkEventAttributes attributes) {
282
283 }
284
285
286 @Override
287 public void head_() {
288 setHeadFlag(false);
289 }
290
291
292 @Override
293 public void head(SinkEventAttributes attributes) {
294 setHeadFlag(true);
295 }
296
297
298 @Override
299 protected void write(String text) {
300 String txt = text;
301
302 if (isHeadFlag()) {
303 headWriter.write(unifyEOLs(txt));
304
305 return;
306 }
307
308 if (docRenderingContext != null) {
309 String relativePathToBasedir = docRenderingContext.getRelativePath();
310
311 if (relativePathToBasedir == null) {
312 txt = txt == null || txt.isEmpty() ? txt : txt.replace("$relativePath", ".");
313 } else {
314 txt = txt == null || txt.isEmpty() || relativePathToBasedir == null
315 ? txt
316 : txt.replace("$relativePath", relativePathToBasedir);
317 }
318 }
319
320 if (verbatimBuffer != null) {
321 verbatimBuffer.append(unifyEOLs(txt));
322 return;
323 }
324 super.write(txt);
325 }
326
327
328
329
330 public String getTitle() {
331 return title;
332 }
333
334
335 public List<String> getAuthors() {
336 return authors;
337 }
338
339
340 public String getDate() {
341 return date;
342 }
343
344
345 public String getBody() {
346 String body = writer.toString();
347
348 return body.length() > 0 ? body : null;
349 }
350
351
352 public String getHead() {
353 String head = headWriter.toString();
354
355 return head.length() > 0 ? head : null;
356 }
357
358
359 public DocumentRenderingContext getRenderingContext() {
360 return docRenderingContext;
361 }
362 }