001/* 002 * Portions of this software was developed by employees of the National Institute 003 * of Standards and Technology (NIST), an agency of the Federal Government and is 004 * being made available as a public service. Pursuant to title 17 United States 005 * Code Section 105, works of NIST employees are not subject to copyright 006 * protection in the United States. This software may be subject to foreign 007 * copyright. Permission in the United States and in foreign countries, to the 008 * extent that NIST may hold copyright, to use, copy, modify, create derivative 009 * works, and distribute this software and its documentation without fee is hereby 010 * granted on a non-exclusive basis, provided that this notice and disclaimer 011 * of warranty appears in all copies. 012 * 013 * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER 014 * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY 015 * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF 016 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM 017 * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE 018 * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE. IN NO EVENT 019 * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT, 020 * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM, 021 * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY, 022 * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR 023 * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT 024 * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. 025 */ 026 027package gov.nist.secauto.metaschema.core.datatype.markup.flexmark; 028 029import com.vladsch.flexmark.ast.AutoLink; 030import com.vladsch.flexmark.ast.BlockQuote; 031import com.vladsch.flexmark.ast.BulletList; 032import com.vladsch.flexmark.ast.Code; 033import com.vladsch.flexmark.ast.CodeBlock; 034import com.vladsch.flexmark.ast.Emphasis; 035import com.vladsch.flexmark.ast.FencedCodeBlock; 036import com.vladsch.flexmark.ast.HardLineBreak; 037import com.vladsch.flexmark.ast.Heading; 038import com.vladsch.flexmark.ast.HtmlBlock; 039import com.vladsch.flexmark.ast.HtmlCommentBlock; 040import com.vladsch.flexmark.ast.HtmlEntity; 041import com.vladsch.flexmark.ast.HtmlInline; 042import com.vladsch.flexmark.ast.Image; 043import com.vladsch.flexmark.ast.IndentedCodeBlock; 044import com.vladsch.flexmark.ast.Link; 045import com.vladsch.flexmark.ast.LinkRef; 046import com.vladsch.flexmark.ast.ListItem; 047import com.vladsch.flexmark.ast.MailLink; 048import com.vladsch.flexmark.ast.OrderedList; 049import com.vladsch.flexmark.ast.Paragraph; 050import com.vladsch.flexmark.ast.Reference; 051import com.vladsch.flexmark.ast.SoftLineBreak; 052import com.vladsch.flexmark.ast.StrongEmphasis; 053import com.vladsch.flexmark.ast.Text; 054import com.vladsch.flexmark.ast.TextBase; 055import com.vladsch.flexmark.ast.ThematicBreak; 056import com.vladsch.flexmark.ext.gfm.strikethrough.Subscript; 057import com.vladsch.flexmark.ext.superscript.Superscript; 058import com.vladsch.flexmark.ext.tables.TableBlock; 059import com.vladsch.flexmark.ext.typographic.TypographicQuotes; 060import com.vladsch.flexmark.ext.typographic.TypographicSmarts; 061import com.vladsch.flexmark.util.ast.Block; 062import com.vladsch.flexmark.util.ast.Document; 063import com.vladsch.flexmark.util.ast.Node; 064 065import gov.nist.secauto.metaschema.core.datatype.markup.flexmark.InsertAnchorExtension.InsertAnchorNode; 066import gov.nist.secauto.metaschema.core.util.ObjectUtils; 067 068import edu.umd.cs.findbugs.annotations.NonNull; 069import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 070 071/** 072 * 073 * This implementation is stateless. 074 * 075 * @param <T> 076 * the type of stream to write to 077 * @param <E> 078 * the type of exception that can be thrown when a writing error occurs 079 */ 080@SuppressFBWarnings("THROWS_METHOD_THROWS_CLAUSE_THROWABLE") 081public class MarkupVisitor<T, E extends Throwable> implements IMarkupVisitor<T, E> { 082 private final boolean handleBlockElements; 083 084 public MarkupVisitor(boolean handleBlockElements) { 085 this.handleBlockElements = handleBlockElements; 086 } 087 088 protected boolean isHandleBlockElements() { 089 return handleBlockElements; 090 } 091 092 @Override 093 public void visitDocument(Document document, IMarkupWriter<T, E> writer) throws E { 094 visitChildren(document, writer); 095 } 096 097 protected void visitChildren(@NonNull Node parentNode, @NonNull IMarkupWriter<T, E> writer) throws E { 098 for (Node node : parentNode.getChildren()) { 099 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}