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.Code; 030import com.vladsch.flexmark.ast.Text; 031import com.vladsch.flexmark.html.HtmlRenderer; 032import com.vladsch.flexmark.html.HtmlRendererOptions; 033import com.vladsch.flexmark.html.HtmlWriter; 034import com.vladsch.flexmark.html.renderer.NodeRenderer; 035import com.vladsch.flexmark.html.renderer.NodeRendererContext; 036import com.vladsch.flexmark.html.renderer.NodeRendererFactory; 037import com.vladsch.flexmark.html.renderer.NodeRenderingHandler; 038import com.vladsch.flexmark.parser.Parser; 039import com.vladsch.flexmark.util.ast.Node; 040import com.vladsch.flexmark.util.data.DataHolder; 041import com.vladsch.flexmark.util.data.MutableDataHolder; 042import com.vladsch.flexmark.util.sequence.Escaping; 043import com.vladsch.flexmark.util.sequence.Range; 044import com.vladsch.flexmark.util.sequence.SequenceUtils; 045 046import gov.nist.secauto.metaschema.core.util.ObjectUtils; 047 048import java.util.Collections; 049import java.util.Set; 050import java.util.regex.Pattern; 051 052import edu.umd.cs.findbugs.annotations.NonNull; 053import edu.umd.cs.findbugs.annotations.Nullable; 054import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 055 056public class HtmlCodeRenderExtension 057 implements HtmlRenderer.HtmlRendererExtension { 058 private static final Pattern EOL_PATTERN = Pattern.compile("\r\n|\r|\n"); 059 060 public static HtmlCodeRenderExtension create() { 061 return new HtmlCodeRenderExtension(); 062 } 063 064 @Override 065 public void rendererOptions(MutableDataHolder options) { 066 // do nothing 067 } 068 069 @Override 070 public void extend(HtmlRenderer.Builder rendererBuilder, String rendererType) { 071 rendererBuilder.nodeRendererFactory(new CodeNodeHtmlRenderer.Factory()); 072 } 073 074 static final class CodeNodeHtmlRenderer implements NodeRenderer { 075 private final boolean codeSoftLineBreaks; 076 077 private CodeNodeHtmlRenderer(DataHolder options) { 078 codeSoftLineBreaks = Parser.CODE_SOFT_LINE_BREAKS.get(options); 079 } 080 081 @Override 082 public @Nullable Set<NodeRenderingHandler<?>> getNodeRenderingHandlers() { 083 return Collections.singleton( 084 new NodeRenderingHandler<>(Code.class, this::render)); 085 } 086 087 @SuppressFBWarnings(value = "RV_RETURN_VALUE_IGNORED_NO_SIDE_EFFECT", justification = "false positive") 088 private void render( // NOPMD actually used in lambda 089 @NonNull Code node, 090 @NonNull NodeRendererContext context, 091 @NonNull HtmlWriter html) { 092 HtmlRendererOptions htmlOptions = context.getHtmlOptions(); 093 094 boolean customTag = htmlOptions.codeStyleHtmlOpen != null || htmlOptions.codeStyleHtmlClose != null; 095 if (customTag) { 096 html.raw(ObjectUtils.notNull(htmlOptions.codeStyleHtmlOpen)); 097 } else { 098 if (context.getHtmlOptions().sourcePositionParagraphLines) { 099 html.withAttr().tag("code"); 100 } else { 101 html.srcPos(node.getText()).withAttr().tag("code"); 102 } 103 } 104 105 if (codeSoftLineBreaks && !htmlOptions.isSoftBreakAllSpaces) { 106 for (Node child : node.getChildren()) { 107 if (child instanceof Text) { 108 html.text(Escaping.collapseWhitespace(child.getChars(), false)); 109 } else { 110 context.render(child); 111 } 112 } 113 } else { 114 String text = EOL_PATTERN.matcher(node.getText()).replaceAll(" "); 115 if (!text.isBlank() && SequenceUtils.startsWithWhitespace(text) && SequenceUtils.endsWithWhitespace(text)) { 116 html.text(SequenceUtils.subSequence(text, Range.of(1, text.length() - 1))); 117 } else { 118 html.raw(Escaping.escapeHtml(text, false)); 119 } 120 // html.text(Escaping.collapseWhitespace(node.getText(), true)); 121 } 122 123 if (customTag) { 124 html.raw(ObjectUtils.notNull(htmlOptions.codeStyleHtmlClose)); 125 } else { 126 html.tag("/code"); 127 } 128 } 129 130 public static class Factory implements NodeRendererFactory { 131 132 @Override 133 public NodeRenderer apply(DataHolder options) { 134 return new CodeNodeHtmlRenderer(options); 135 } 136 137 } 138 } 139}