View Javadoc
1   /*
2    * Portions of this software was developed by employees of the National Institute
3    * of Standards and Technology (NIST), an agency of the Federal Government and is
4    * being made available as a public service. Pursuant to title 17 United States
5    * Code Section 105, works of NIST employees are not subject to copyright
6    * protection in the United States. This software may be subject to foreign
7    * copyright. Permission in the United States and in foreign countries, to the
8    * extent that NIST may hold copyright, to use, copy, modify, create derivative
9    * works, and distribute this software and its documentation without fee is hereby
10   * granted on a non-exclusive basis, provided that this notice and disclaimer
11   * of warranty appears in all copies.
12   *
13   * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER
14   * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY
15   * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF
16   * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM
17   * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE
18   * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE.  IN NO EVENT
19   * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT,
20   * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM,
21   * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY,
22   * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR
23   * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT
24   * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER.
25   */
26  
27  package gov.nist.secauto.metaschema.core.datatype.markup.flexmark;
28  
29  import com.vladsch.flexmark.ast.AutoLink;
30  import com.vladsch.flexmark.ast.BlockQuote;
31  import com.vladsch.flexmark.ast.BulletList;
32  import com.vladsch.flexmark.ast.Code;
33  import com.vladsch.flexmark.ast.CodeBlock;
34  import com.vladsch.flexmark.ast.Emphasis;
35  import com.vladsch.flexmark.ast.FencedCodeBlock;
36  import com.vladsch.flexmark.ast.HardLineBreak;
37  import com.vladsch.flexmark.ast.Heading;
38  import com.vladsch.flexmark.ast.HtmlBlock;
39  import com.vladsch.flexmark.ast.HtmlCommentBlock;
40  import com.vladsch.flexmark.ast.HtmlEntity;
41  import com.vladsch.flexmark.ast.HtmlInline;
42  import com.vladsch.flexmark.ast.Image;
43  import com.vladsch.flexmark.ast.IndentedCodeBlock;
44  import com.vladsch.flexmark.ast.Link;
45  import com.vladsch.flexmark.ast.LinkRef;
46  import com.vladsch.flexmark.ast.ListItem;
47  import com.vladsch.flexmark.ast.MailLink;
48  import com.vladsch.flexmark.ast.OrderedList;
49  import com.vladsch.flexmark.ast.Paragraph;
50  import com.vladsch.flexmark.ast.Reference;
51  import com.vladsch.flexmark.ast.SoftLineBreak;
52  import com.vladsch.flexmark.ast.StrongEmphasis;
53  import com.vladsch.flexmark.ast.Text;
54  import com.vladsch.flexmark.ast.TextBase;
55  import com.vladsch.flexmark.ast.ThematicBreak;
56  import com.vladsch.flexmark.ext.gfm.strikethrough.Subscript;
57  import com.vladsch.flexmark.ext.superscript.Superscript;
58  import com.vladsch.flexmark.ext.tables.TableBlock;
59  import com.vladsch.flexmark.ext.typographic.TypographicQuotes;
60  import com.vladsch.flexmark.ext.typographic.TypographicSmarts;
61  import com.vladsch.flexmark.util.ast.Block;
62  import com.vladsch.flexmark.util.ast.Document;
63  import com.vladsch.flexmark.util.ast.Node;
64  
65  import gov.nist.secauto.metaschema.core.datatype.markup.flexmark.InsertAnchorExtension.InsertAnchorNode;
66  import gov.nist.secauto.metaschema.core.util.ObjectUtils;
67  
68  import edu.umd.cs.findbugs.annotations.NonNull;
69  import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
70  
71  /**
72   *
73   * This implementation is stateless.
74   *
75   * @param <T>
76   *          the type of stream to write to
77   * @param <E>
78   *          the type of exception that can be thrown when a writing error occurs
79   */
80  @SuppressFBWarnings("THROWS_METHOD_THROWS_CLAUSE_THROWABLE")
81  public class MarkupVisitor<T, E extends Throwable> implements IMarkupVisitor<T, E> {
82    private final boolean handleBlockElements;
83  
84    public MarkupVisitor(boolean handleBlockElements) {
85      this.handleBlockElements = handleBlockElements;
86    }
87  
88    protected boolean isHandleBlockElements() {
89      return handleBlockElements;
90    }
91  
92    @Override
93    public void visitDocument(Document document, IMarkupWriter<T, E> writer) throws E {
94      visitChildren(document, writer);
95    }
96  
97    protected void visitChildren(@NonNull Node parentNode, @NonNull IMarkupWriter<T, E> writer) throws E {
98      for (Node node : parentNode.getChildren()) {
99        assert node != null;
100       visit(node, writer);
101     }
102   }
103 
104   protected void visit(@NonNull Node node, @NonNull IMarkupWriter<T, E> writer) throws E {
105     boolean handled = processInlineElements(node, writer);
106     if (!handled && node instanceof Block) {
107       if (isHandleBlockElements()) {
108         handled = processBlockElements(node, writer);
109       } else {
110         visitChildren(node, writer);
111         handled = true;
112       }
113     }
114 
115     if (!handled) {
116       throw new UnsupportedOperationException(
117           String.format("Node '%s' not handled. AST: %s", node.getNodeName(), node.toAstString(true)));
118     }
119   }
120 
121   protected boolean processInlineElements( // NOPMD dispatch method
122       @NonNull Node node,
123       @NonNull IMarkupWriter<T, E> writer) throws E { // NOPMD - acceptable
124     boolean retval = true;
125     if (node instanceof Text) {
126       writer.writeText((Text) node);
127     } else if (node instanceof TextBase) {
128       writer.writeText((TextBase) node);
129     } else if (node instanceof HtmlEntity) {
130       writer.writeHtmlEntity((HtmlEntity) node);
131     } else if (node instanceof TypographicSmarts) {
132       writer.writeHtmlEntity((TypographicSmarts) node);
133     } else if (node instanceof TypographicQuotes) {
134       writer.writeTypographicQuotes((TypographicQuotes) node, this::visit);
135     } else if (node instanceof Code) {
136       writer.writeCode((Code) node, this::visit);
137     } else if (node instanceof StrongEmphasis) {
138       writer.writeElement("strong", node, this::visit);
139     } else if (node instanceof Emphasis) {
140       writer.writeElement("em", node, this::visit);
141     } else if (node instanceof ListItem) {
142       writer.writeElement("li", node, this::visit);
143     } else if (node instanceof Link) {
144       writer.writeLink((Link) node, this::visit);
145     } else if (node instanceof AutoLink) {
146       writer.writeLink((AutoLink) node);
147     } else if (node instanceof MailLink) {
148       writer.writeLink((MailLink) node);
149     } else if (node instanceof Subscript) {
150       writer.writeElement("sub", node, this::visit);
151     } else if (node instanceof Superscript) {
152       writer.writeElement("sup", node, this::visit);
153     } else if (node instanceof Image) {
154       writer.writeImage((Image) node);
155     } else if (node instanceof InsertAnchorNode) {
156       writer.writeInsertAnchor((InsertAnchorNode) node);
157     } else if (node instanceof SoftLineBreak) {
158       writer.writeText("\n");
159     } else if (node instanceof HardLineBreak) {
160       writer.writeBreak((HardLineBreak) node);
161     } else if (node instanceof HtmlInline) {
162       writer.writeInlineHtml((HtmlInline) node);
163     } else if (node instanceof LinkRef || node instanceof Reference) {
164       throw new UnsupportedOperationException(
165           String.format(
166               "Link references are not supported by Metaschema."
167                   + " Perhaps you have an unescaped bracket in the following string? %s",
168               ObjectUtils.notNull(node.getParent()).getChars()));
169     } else {
170       retval = false;
171     }
172     return retval;
173   }
174 
175   protected boolean processBlockElements( // NOPMD dispatch method
176       @NonNull Node node,
177       @NonNull IMarkupWriter<T, E> writer) throws E {
178     boolean retval = true;
179     if (node instanceof Paragraph) {
180       writer.writeParagraph((Paragraph) node, this::visit);
181     } else if (node instanceof Heading) {
182       writer.writeHeading((Heading) node, this::visit);
183     } else if (node instanceof OrderedList) {
184       writer.writeList("ol", (OrderedList) node, this::visit);
185     } else if (node instanceof BulletList) {
186       writer.writeList("ul", (BulletList) node, this::visit);
187     } else if (node instanceof TableBlock) {
188       writer.writeTable((TableBlock) node, this::visit);
189     } else if (node instanceof HtmlBlock) {
190       writer.writeBlockHtml((HtmlBlock) node);
191     } else if (node instanceof HtmlCommentBlock) {
192       writer.writeComment((HtmlCommentBlock) node);
193     } else if (node instanceof IndentedCodeBlock) {
194       writer.writeCodeBlock((IndentedCodeBlock) node, this::visit);
195     } else if (node instanceof FencedCodeBlock) {
196       writer.writeCodeBlock((FencedCodeBlock) node, this::visit);
197     } else if (node instanceof CodeBlock) {
198       writer.writeCodeBlock((CodeBlock) node, this::visit);
199     } else if (node instanceof BlockQuote) {
200       writer.writeBlockQuote((BlockQuote) node, this::visit);
201     } else if (node instanceof ThematicBreak) {
202       writer.writeBreak((ThematicBreak) node);
203     } else {
204       retval = false;
205     }
206 
207     // if (retval && node.getNextAny(Block.class) != null) {
208     // writer.writeText("\n");
209     // }
210     return retval;
211   }
212 }