1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 package gov.nist.secauto.metaschema.core.datatype.markup.flexmark;
28
29 import com.vladsch.flexmark.ext.typographic.TypographicQuotes;
30 import com.vladsch.flexmark.html.HtmlRenderer;
31 import com.vladsch.flexmark.html.HtmlWriter;
32 import com.vladsch.flexmark.html.renderer.NodeRenderer;
33 import com.vladsch.flexmark.html.renderer.NodeRendererContext;
34 import com.vladsch.flexmark.html.renderer.NodeRendererFactory;
35 import com.vladsch.flexmark.html.renderer.NodeRenderingHandler;
36 import com.vladsch.flexmark.html2md.converter.FlexmarkHtmlConverter;
37 import com.vladsch.flexmark.html2md.converter.HtmlMarkdownWriter;
38 import com.vladsch.flexmark.html2md.converter.HtmlNodeConverterContext;
39 import com.vladsch.flexmark.html2md.converter.HtmlNodeRenderer;
40 import com.vladsch.flexmark.html2md.converter.HtmlNodeRendererFactory;
41 import com.vladsch.flexmark.html2md.converter.HtmlNodeRendererHandler;
42 import com.vladsch.flexmark.parser.Parser;
43 import com.vladsch.flexmark.parser.block.NodePostProcessor;
44 import com.vladsch.flexmark.parser.block.NodePostProcessorFactory;
45 import com.vladsch.flexmark.util.ast.DoNotDecorate;
46 import com.vladsch.flexmark.util.ast.Document;
47 import com.vladsch.flexmark.util.ast.Node;
48 import com.vladsch.flexmark.util.ast.NodeTracker;
49 import com.vladsch.flexmark.util.data.DataHolder;
50 import com.vladsch.flexmark.util.data.MutableDataHolder;
51
52 import org.jsoup.nodes.Element;
53
54 import java.util.Collections;
55 import java.util.Set;
56
57 import edu.umd.cs.findbugs.annotations.NonNull;
58 import edu.umd.cs.findbugs.annotations.Nullable;
59
60 public class HtmlQuoteTagExtension
61 implements Parser.ParserExtension, HtmlRenderer.HtmlRendererExtension,
62 FlexmarkHtmlConverter.HtmlConverterExtension {
63
64 public static HtmlQuoteTagExtension create() {
65 return new HtmlQuoteTagExtension();
66 }
67
68 @Override
69 public void rendererOptions(MutableDataHolder options) {
70
71 }
72
73 @Override
74 public void parserOptions(MutableDataHolder options) {
75
76 }
77
78 @Override
79 public void extend(HtmlRenderer.Builder rendererBuilder, String rendererType) {
80 rendererBuilder.nodeRendererFactory(new QTagNodeRenderer.Factory());
81 }
82
83 @Override
84 public void extend(Parser.Builder parserBuilder) {
85 parserBuilder.postProcessorFactory(new QuoteReplacingPostProcessor.Factory());
86 }
87
88 @Override
89 public void extend(FlexmarkHtmlConverter.Builder builder) {
90 builder.htmlNodeRendererFactory(new QTagHtmlNodeRenderer.Factory());
91 }
92
93 static class QTagNodeRenderer implements NodeRenderer {
94
95 @Override
96 public @Nullable Set<NodeRenderingHandler<?>> getNodeRenderingHandlers() {
97 return Collections.singleton(
98 new NodeRenderingHandler<>(DoubleQuoteNode.class, this::render));
99 }
100
101 protected void render(@NonNull DoubleQuoteNode node, @NonNull NodeRendererContext context,
102 @NonNull HtmlWriter html) {
103 html.withAttr().tag("q");
104 context.renderChildren(node);
105 html.tag("/q");
106 }
107
108 public static class Factory implements NodeRendererFactory {
109
110 @Override
111 public NodeRenderer apply(DataHolder options) {
112 return new QTagNodeRenderer();
113 }
114
115 }
116 }
117
118 static class QuoteReplacingPostProcessor
119 extends NodePostProcessor {
120
121 @Override
122 public void process(NodeTracker state, Node node) {
123 if (node instanceof TypographicQuotes) {
124 TypographicQuotes typographicQuotes = (TypographicQuotes) node;
125 if (typographicQuotes.getOpeningMarker().matchChars("\"")) {
126 DoubleQuoteNode quoteNode = new DoubleQuoteNode(typographicQuotes);
127 node.insertAfter(quoteNode);
128 state.nodeAdded(quoteNode);
129 node.unlink();
130 state.nodeRemoved(node);
131 }
132 }
133 }
134
135 public static class Factory
136 extends NodePostProcessorFactory {
137 public Factory() {
138 super(false);
139 addNodeWithExclusions(TypographicQuotes.class, DoNotDecorate.class);
140 }
141
142 @NonNull
143 @Override
144 public NodePostProcessor apply(Document document) {
145 return new QuoteReplacingPostProcessor();
146 }
147 }
148 }
149
150 static class QTagHtmlNodeRenderer implements HtmlNodeRenderer {
151
152 @Override
153 public Set<HtmlNodeRendererHandler<?>> getHtmlNodeRendererHandlers() {
154 return Collections.singleton(new HtmlNodeRendererHandler<>("q", Element.class, this::renderMarkdown));
155 }
156
157 protected void renderMarkdown(Element element, HtmlNodeConverterContext context,
158 @SuppressWarnings("unused") HtmlMarkdownWriter out) {
159 context.wrapTextNodes(element, "\"", element.nextElementSibling() != null);
160 }
161
162 public static class Factory implements HtmlNodeRendererFactory {
163
164 @Override
165 public HtmlNodeRenderer apply(DataHolder options) {
166 return new QTagHtmlNodeRenderer();
167 }
168 }
169
170 }
171
172 public static class DoubleQuoteNode
173 extends TypographicQuotes {
174
175 public DoubleQuoteNode(TypographicQuotes node) {
176 super(node.getOpeningMarker(), node.getText(), node.getClosingMarker());
177 setTypographicOpening(node.getTypographicOpening());
178 setTypographicClosing(node.getTypographicClosing());
179 for (Node child : node.getChildren()) {
180 appendChild(child);
181 }
182 }
183 }
184 }