diff --git a/make/autoconf/spec.gmk.in b/make/autoconf/spec.gmk.in
index 9448cb9b7e885..9fe930bc824a3 100644
--- a/make/autoconf/spec.gmk.in
+++ b/make/autoconf/spec.gmk.in
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
@@ -680,7 +680,7 @@ BUILD_JAR=@FIXPATH@ $(BUILD_JDK)/bin/jar
DOCS_REFERENCE_JAVADOC := @DOCS_REFERENCE_JAVADOC@
# Interim langtools modules and arguments
-INTERIM_LANGTOOLS_BASE_MODULES := java.compiler jdk.compiler jdk.javadoc
+INTERIM_LANGTOOLS_BASE_MODULES := java.compiler jdk.compiler jdk.javadoc jdk.internal.md
INTERIM_LANGTOOLS_MODULES := $(addsuffix .interim, $(INTERIM_LANGTOOLS_BASE_MODULES))
INTERIM_LANGTOOLS_ADD_EXPORTS := \
--add-exports java.base/sun.reflect.annotation=jdk.compiler.interim \
diff --git a/src/jdk.compiler/share/classes/com/sun/source/doctree/DocTree.java b/src/jdk.compiler/share/classes/com/sun/source/doctree/DocTree.java
index 9d457a739da51..35f8554078019 100644
--- a/src/jdk.compiler/share/classes/com/sun/source/doctree/DocTree.java
+++ b/src/jdk.compiler/share/classes/com/sun/source/doctree/DocTree.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -153,6 +153,14 @@ enum Kind {
*/
LITERAL("literal"),
+ /**
+ * Used for instances of {@link MarkdownTree}
+ * representing a fragment of Markdown content.
+ *
+ * @since 21
+ */
+ MARKDOWN,
+
/**
* Used for instances of {@link ParamTree}
* representing an {@code @param} tag.
diff --git a/src/jdk.compiler/share/classes/com/sun/source/doctree/DocTreeVisitor.java b/src/jdk.compiler/share/classes/com/sun/source/doctree/DocTreeVisitor.java
index 7bfaa7acd4626..5b6acac4c1855 100644
--- a/src/jdk.compiler/share/classes/com/sun/source/doctree/DocTreeVisitor.java
+++ b/src/jdk.compiler/share/classes/com/sun/source/doctree/DocTreeVisitor.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -207,6 +207,21 @@ default R visitIndex(IndexTree node, P p) {
*/
R visitLiteral(LiteralTree node, P p);
+ /**
+ * Visits a {@code MarkdownTree} node.
+ * @param node the node being visited
+ * @param p a parameter value
+ * @return a result value
+ *
+ * @implSpec Visits the provided {@code MarkdownTree} node
+ * by calling {@code visitOther(node, p)}.
+ *
+ * @since 21
+ */
+ default R visitMarkdown(MarkdownTree node, P p) {
+ return visitOther(node, p);
+ }
+
/**
* Visits a {@code ParamTree} node.
* @param node the node being visited
diff --git a/src/jdk.compiler/share/classes/com/sun/source/doctree/InheritDocTree.java b/src/jdk.compiler/share/classes/com/sun/source/doctree/InheritDocTree.java
index 304bde3908f9b..a9dd9d84a997f 100644
--- a/src/jdk.compiler/share/classes/com/sun/source/doctree/InheritDocTree.java
+++ b/src/jdk.compiler/share/classes/com/sun/source/doctree/InheritDocTree.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -28,6 +28,11 @@
/**
* A tree node for an {@code @inheritDoc} inline tag.
*
+ * @apiNote
+ * There is no requirement that the comment containing the tag and the comment
+ * containing the inherited documentation should either be both Markdown comments
+ * or both normal (not Markdown) comments.
+ *
*
* {@inheritDoc}
*
diff --git a/src/jdk.compiler/share/classes/com/sun/source/doctree/MarkdownTree.java b/src/jdk.compiler/share/classes/com/sun/source/doctree/MarkdownTree.java
new file mode 100644
index 0000000000000..36b8e450ad20a
--- /dev/null
+++ b/src/jdk.compiler/share/classes/com/sun/source/doctree/MarkdownTree.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.source.doctree;
+
+/**
+ * A tree node for a fragment of Markdown content.
+ *
+ *
+ * The content may contain plain text, entities and HTML elements,
+ * all represented directly in the text of the content,
+ * but not {@linkplain InlineTagTree inline tags}.
+ *
+ * @apiNote
+ * {@code MarkdownTree} nodes will typically exist in a list of
+ * {@code DocTree} nodes, along with other kinds of {@code DocTree}
+ * nodes, such as for inline tags. When processing any such list,
+ * any non-Markdown nodes will be processed recursively first, and then
+ * treated as opaque objects within the remaining stream of Markdown nodes.
+ * Thus, the content of any non-Markdown nodes will not affect how the
+ * Markdown nodes will be processed.
+ *
+ * Note: malformed entities and HTML elements will typically be
+ * processed as literal text, with no warnings or errors being
+ * reported.
+ *
+ * @since 21
+ */
+public interface MarkdownTree extends DocTree {
+ /**
+ * {@return the content}
+ */
+ String getContent();
+}
diff --git a/src/jdk.compiler/share/classes/com/sun/source/doctree/package-info.java b/src/jdk.compiler/share/classes/com/sun/source/doctree/package-info.java
index 30832c062ff7e..410f1ef022550 100644
--- a/src/jdk.compiler/share/classes/com/sun/source/doctree/package-info.java
+++ b/src/jdk.compiler/share/classes/com/sun/source/doctree/package-info.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -27,6 +27,17 @@
* Provides interfaces to represent documentation comments as abstract syntax
* trees (AST).
*
+ *
Markdown
+ *
+ * Various classes defined in this package contain a list of {@link DocTree} nodes,
+ * which may represent {@linkplain TextTree plain text}, {@linkplain EntityTree entities},
+ * {@linkplain InlineTagTree inline} and {@linkplain BlockTagTree block} tags,
+ * {@linkplain StartElementTree start} and {@linkplain EndElementTree end} HTML elements,
+ * and {@linkplain MarkdownTree Markdown content}. In such lists, if there is at least
+ * one Markdown node, the entire list will be treated as Markdown, although all
+ * non-Markdown nodes will be treated as opaque objects and will not be parsed as
+ * part of the Markdown content.
+ *
* @author Jonathan Gibbons
* @since 1.8
*
diff --git a/src/jdk.compiler/share/classes/com/sun/source/util/DocTreeFactory.java b/src/jdk.compiler/share/classes/com/sun/source/util/DocTreeFactory.java
index 78e973d23d45f..8d330589b555f 100644
--- a/src/jdk.compiler/share/classes/com/sun/source/util/DocTreeFactory.java
+++ b/src/jdk.compiler/share/classes/com/sun/source/util/DocTreeFactory.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -49,6 +49,7 @@
import com.sun.source.doctree.InheritDocTree;
import com.sun.source.doctree.LinkTree;
import com.sun.source.doctree.LiteralTree;
+import com.sun.source.doctree.MarkdownTree;
import com.sun.source.doctree.ParamTree;
import com.sun.source.doctree.ProvidesTree;
import com.sun.source.doctree.ReferenceTree;
@@ -235,6 +236,15 @@ DocCommentTree newDocCommentTree(List extends DocTree> fullBody,
*/
LiteralTree newLiteralTree(TextTree text);
+ /**
+ * Creates a new {@code MarkdownTree} object, to represent a fragment of Markdown content.
+ * @param code the code
+ * @return a {@code MarkdownTree} object
+ *
+ * @since 21
+ */
+ MarkdownTree newMarkdownTree(String code);
+
/**
* Creates a new {@code ParamTree} object, to represent a {@code @param} tag.
* @param isTypeParameter {@code true} if this is a type parameter, and {@code false} otherwise
diff --git a/src/jdk.compiler/share/classes/com/sun/source/util/DocTreeScanner.java b/src/jdk.compiler/share/classes/com/sun/source/util/DocTreeScanner.java
index bd5a64324caef..f0172f18617a0 100644
--- a/src/jdk.compiler/share/classes/com/sun/source/util/DocTreeScanner.java
+++ b/src/jdk.compiler/share/classes/com/sun/source/util/DocTreeScanner.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -365,6 +365,22 @@ public R visitLiteral(LiteralTree node, P p) {
return scan(node.getBody(), p);
}
+ /**
+ * {@inheritDoc}
+ *
+ * @implSpec This implementation returns {@code null}.
+ *
+ * @param node {@inheritDoc}
+ * @param p {@inheritDoc}
+ * @return the result of scanning
+ *
+ * @since 21
+ */
+ @Override
+ public R visitMarkdown(MarkdownTree node, P p) {
+ return null;
+ }
+
/**
* {@inheritDoc}
*
diff --git a/src/jdk.compiler/share/classes/com/sun/source/util/SimpleDocTreeVisitor.java b/src/jdk.compiler/share/classes/com/sun/source/util/SimpleDocTreeVisitor.java
index b3a27f9e6b424..c0f0a3cf612ae 100644
--- a/src/jdk.compiler/share/classes/com/sun/source/util/SimpleDocTreeVisitor.java
+++ b/src/jdk.compiler/share/classes/com/sun/source/util/SimpleDocTreeVisitor.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005, 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -320,6 +320,22 @@ public R visitLiteral(LiteralTree node, P p) {
return defaultAction(node, p);
}
+ /**
+ * {@inheritDoc}
+ *
+ * @implSpec This implementation calls {@code defaultAction}.
+ *
+ * @param node {@inheritDoc}
+ * @param p {@inheritDoc}
+ * @return the result of {@code defaultAction}
+ *
+ * @since 21
+ */
+ @Override
+ public R visitMarkdown(MarkdownTree node, P p) {
+ return defaultAction(node, p);
+ }
+
/**
* {@inheritDoc}
*
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/DocCommentParser.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/DocCommentParser.java
index 80dde7fe8a6ca..a2ec714371e7e 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/DocCommentParser.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/DocCommentParser.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -83,6 +83,7 @@ private enum Phase { PREAMBLE, BODY, POSTAMBLE }
private final DocTreeMaker m;
private final Names names;
private final boolean isFileContent;
+ private final boolean isMarkdown;
/** The input buffer, index of most recent character read,
* index of one past last character in buffer.
@@ -101,6 +102,8 @@ private enum Phase { PREAMBLE, BODY, POSTAMBLE }
private final Map tagParsers;
+ private static final String MARKDOWN_PREFIX = "md";
+
public DocCommentParser(ParserFactory fac, DiagnosticSource diagSource,
Comment comment, boolean isFileContent) {
this.fac = fac;
@@ -109,6 +112,8 @@ public DocCommentParser(ParserFactory fac, DiagnosticSource diagSource,
this.comment = comment;
names = fac.names;
this.isFileContent = isFileContent;
+ String t = comment.getText();
+ this.isMarkdown = t.startsWith(MARKDOWN_PREFIX) && Character.isWhitespace(t.charAt(MARKDOWN_PREFIX.length()));
m = fac.docTreeMaker;
tagParsers = createTagParsers();
}
@@ -127,15 +132,16 @@ public DCDocComment parse() {
c.getChars(0, c.length(), buf, 0);
buf[buf.length - 1] = EOI;
buflen = buf.length - 1;
- bp = -1;
+ bp = -1 + (isMarkdown ? MARKDOWN_PREFIX.length() : 0);
nextChar();
- List preamble = isFileContent ? blockContent(Phase.PREAMBLE) : List.nil();
+ List preamble = isFileContent && !isMarkdown ? blockContent(Phase.PREAMBLE) : List.nil();
List body = blockContent(Phase.BODY);
List tags = blockTags();
List postamble = isFileContent ? blockContent(Phase.POSTAMBLE) : List.nil();
- int pos = !preamble.isEmpty() ? preamble.head.pos
+ int pos = isMarkdown ? 0
+ : !preamble.isEmpty() ? preamble.head.pos
: !body.isEmpty() ? body.head.pos
: !tags.isEmpty() ? tags.head.pos
: !postamble.isEmpty() ? postamble.head.pos
@@ -157,9 +163,13 @@ protected List blockContent() {
}
/**
- * Read block content, consisting of text, html and inline tags.
+ * Read block content, consisting of text, and Markdown code or html and inline tags.
* Terminated by the end of input, or the beginning of the next block tag:
* that is, @ as the first non-whitespace character on a line.
+ *
+ * In Markdown mode, {@code &} (introducing an entity) and {@code <} (introducing
+ * an HTML element) are treated as "plain" characters, to be analyzed eventually
+ * by the Markdown processor.
*/
@SuppressWarnings("fallthrough")
protected List blockContent(Phase phase) {
@@ -178,44 +188,52 @@ protected List blockContent(Phase phase) {
break;
case '&':
- entity(trees);
+ if (isMarkdown) {
+ defaultBlockCharacter();
+ } else {
+ entity(trees);
+ }
break;
case '<':
- newline = false;
- if (isFileContent) {
- switch (phase) {
- case PREAMBLE:
- if (isEndPreamble()) {
- trees.add(html());
- if (textStart == -1) {
- textStart = bp;
- lastNonWhite = -1;
+ if (isMarkdown) {
+ defaultBlockCharacter();
+ } else {
+ newline = false;
+ if (isFileContent) {
+ switch (phase) {
+ case PREAMBLE:
+ if (isEndPreamble()) {
+ trees.add(html());
+ if (textStart == -1) {
+ textStart = bp;
+ lastNonWhite = -1;
+ }
+ // mark this as the start, for processing purposes
+ newline = true;
+ break loop;
}
- // mark this as the start, for processing purposes
- newline = true;
- break loop;
- }
- break;
- case BODY:
- if (isEndBody()) {
- addPendingText(trees, lastNonWhite);
- break loop;
- }
- break;
- default:
- // fallthrough
+ break;
+ case BODY:
+ if (isEndBody()) {
+ addPendingText(trees, lastNonWhite);
+ break loop;
+ }
+ break;
+ default:
+ // fallthrough
+ }
}
- }
- addPendingText(trees, bp - 1);
- trees.add(html());
+ addPendingText(trees, bp - 1);
+ trees.add(html());
- if (phase == Phase.PREAMBLE || phase == Phase.POSTAMBLE) {
- break; // Ignore newlines after html tags, in the meta content
- }
- if (textStart == -1) {
- textStart = bp;
- lastNonWhite = -1;
+ if (phase == Phase.PREAMBLE || phase == Phase.POSTAMBLE) {
+ break; // Ignore newlines after html tags, in the meta content
+ }
+ if (textStart == -1) {
+ textStart = bp;
+ lastNonWhite = -1;
+ }
}
break;
@@ -231,11 +249,7 @@ protected List blockContent(Phase phase) {
// fallthrough
default:
- newline = false;
- if (textStart == -1)
- textStart = bp;
- lastNonWhite = bp;
- nextChar();
+ defaultBlockCharacter();
}
}
@@ -245,6 +259,14 @@ protected List blockContent(Phase phase) {
return trees.toList();
}
+ private void defaultBlockCharacter() {
+ newline = false;
+ if (textStart == -1)
+ textStart = bp;
+ lastNonWhite = bp;
+ nextChar();
+ }
+
/**
* Read a series of block tags, including their content.
* Standard tags parse their content appropriately.
@@ -561,9 +583,13 @@ protected DCText inlineWord() {
}
/**
- * Reads general text content of an inline tag, including HTML entities and elements.
+ * Reads general text content of an inline tag, including Markdown or HTML entities and elements.
* Matching pairs of { } are skipped; the text is terminated by the first
* unmatched }. It is an error if the beginning of the next tag is detected.
+ *
+ * In Markdown mode, {@code &} (introducing an entity) and {@code <} (introducing
+ * an HTML element) are treated as "plain" characters, to be analyzed eventually
+ * by the Markdown processor.
*/
@SuppressWarnings("fallthrough")
private List inlineContent() {
@@ -587,15 +613,23 @@ private List inlineContent() {
break;
case '&':
- entity(trees);
+ if (isMarkdown) {
+ defaultInlineCharacter();
+ } else {
+ entity(trees);
+ }
break;
case '<':
- newline = false;
- addPendingText(trees, bp - 1);
- trees.add(html());
- textStart = bp;
- lastNonWhite = -1;
+ if (isMarkdown) {
+ defaultInlineCharacter();
+ } else {
+ newline = false;
+ addPendingText(trees, bp - 1);
+ trees.add(html());
+ textStart = bp;
+ lastNonWhite = -1;
+ }
break;
case '{':
@@ -629,9 +663,7 @@ private List inlineContent() {
// fallthrough
default:
- if (textStart == -1)
- textStart = bp;
- nextChar();
+ defaultInlineCharacter();
break;
}
}
@@ -639,6 +671,12 @@ private List inlineContent() {
return List.of(erroneous("dc.unterminated.inline.tag", pos));
}
+ private void defaultInlineCharacter() {
+ if (textStart == -1)
+ textStart = bp;
+ nextChar();
+ }
+
protected void entity(ListBuffer list) {
newline = false;
addPendingText(list, bp - 1);
@@ -942,7 +980,7 @@ protected List htmlAttrs() {
}
attrValueChar(v);
}
- addPendingText(v, bp - 1);
+ addPendingText(v, bp - 1, false);
nextChar();
} else {
vkind = ValueKind.UNQUOTED;
@@ -950,7 +988,7 @@ protected List htmlAttrs() {
while (bp < buflen && !isUnquotedAttrValueTerminator(ch)) {
attrValueChar(v);
}
- addPendingText(v, bp - 1);
+ addPendingText(v, bp - 1, false);
}
skipWhitespace();
value = v.toList();
@@ -978,9 +1016,17 @@ protected void attrValueChar(ListBuffer list) {
}
protected void addPendingText(ListBuffer list, int textEnd) {
+ addPendingText(list, textEnd, isMarkdown);
+ }
+
+ protected void addPendingText(ListBuffer list, int textEnd, boolean useMarkdown) {
if (textStart != -1) {
if (textStart <= textEnd) {
- list.add(m.at(textStart).newTextTree(newString(textStart, textEnd + 1)));
+ if (useMarkdown) {
+ list.add(m.at(textStart).newMarkdownTree(newString(textStart, textEnd + 1)));
+ } else {
+ list.add(m.at(textStart).newTextTree(newString(textStart, textEnd + 1)));
+ }
}
textStart = -1;
}
@@ -1524,7 +1570,7 @@ private List tagAttrs() {
while (bp < buflen && ch != quote) {
nextChar();
}
- addPendingText(v, bp - 1);
+ addPendingText(v, bp - 1, false);
nextChar();
} else {
vkind = ValueKind.UNQUOTED;
@@ -1533,7 +1579,7 @@ private List tagAttrs() {
while (bp < buflen && (ch != '}' && ch != ':' && !isUnquotedAttrValueTerminator(ch))) {
nextChar();
}
- addPendingText(v, bp - 1);
+ addPendingText(v, bp - 1, false);
}
skipWhitespace();
value = v.toList();
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/DCTree.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/DCTree.java
index 95cb6e0fa3c14..b28aaacbed94f 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/DCTree.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/DCTree.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -158,6 +158,11 @@ public int getEndPosition() {
}
switch (getKind()) {
+ case MARKDOWN -> {
+ DCMarkdown markdown = (DCMarkdown) this;
+ return markdown.pos + markdown.code.length();
+ }
+
case TEXT -> {
DCText text = (DCText) this;
return text.pos + text.text.length();
@@ -791,6 +796,34 @@ public DCText getBody() {
}
}
+ public static class DCMarkdown extends DCTree implements MarkdownTree {
+ public final String code;
+
+ DCMarkdown(String code) {
+ this.code = code;
+ }
+
+ @Override
+ public boolean isBlank() {
+ return code.isBlank();
+ }
+
+ @Override @DefinedBy(Api.COMPILER_TREE)
+ public Kind getKind() {
+ return Kind.MARKDOWN;
+ }
+
+ @Override @DefinedBy(Api.COMPILER_TREE)
+ public R accept(DocTreeVisitor v, D d) {
+ return v.visitMarkdown(this, d);
+ }
+
+ @Override @DefinedBy(Api.COMPILER_TREE)
+ public String getContent() {
+ return code;
+ }
+ }
+
public static class DCParam extends DCBlockTag implements ParamTree {
public final boolean isTypeParameter;
public final DCIdentifier name;
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/DocPretty.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/DocPretty.java
index 37f0fe0e9c0fa..c001291bdb0bd 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/DocPretty.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/DocPretty.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -32,6 +32,7 @@
import com.sun.source.doctree.*;
import com.sun.source.doctree.AttributeTree.ValueKind;
+import com.sun.source.util.DocTreeScanner;
import com.sun.tools.javac.util.Convert;
import com.sun.tools.javac.util.DefinedBy;
import com.sun.tools.javac.util.DefinedBy.Api;
@@ -188,6 +189,9 @@ public Void visitDeprecated(DeprecatedTree node, Void p) {
@Override @DefinedBy(Api.COMPILER_TREE)
public Void visitDocComment(DocCommentTree node, Void p) {
try {
+ if (isMarkdown(node)) {
+ print("md\n");
+ }
List extends DocTree> b = node.getFullBody();
List extends DocTree> t = node.getBlockTags();
print(b);
@@ -200,6 +204,36 @@ public Void visitDocComment(DocCommentTree node, Void p) {
return null;
}
+ private static final DocTreeVisitor isMarkdownVisitor = new DocTreeScanner() {
+ @Override
+ public Boolean scan(Iterable extends DocTree> nodes, Void ignore) {
+ if (nodes != null) {
+ boolean first = true;
+ for (DocTree node : nodes) {
+ Boolean b = scan(node, ignore);
+ if (b == Boolean.TRUE) {
+ return b;
+ }
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public Boolean scan(DocTree node, Void ignore) {
+ return node != null && node.getKind() == DocTree.Kind.MARKDOWN ? Boolean.TRUE : super.scan(node, ignore);
+ }
+
+ @Override
+ public Boolean reduce(Boolean r1, Boolean r2) {
+ return r1 == Boolean.TRUE || r2 == Boolean.TRUE;
+ }
+ };
+
+ private boolean isMarkdown(DocCommentTree node) {
+ return isMarkdownVisitor.visitDocComment(node, null);
+ }
+
@Override @DefinedBy(Api.COMPILER_TREE)
public Void visitDocRoot(DocRootTree node, Void p) {
try {
@@ -345,6 +379,16 @@ public Void visitLiteral(LiteralTree node, Void p) {
return null;
}
+ @Override @DefinedBy(Api.COMPILER_TREE)
+ public Void visitMarkdown(MarkdownTree node, Void p) {
+ try {
+ print(node.getContent());
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ return null;
+ }
+
@Override @DefinedBy(Api.COMPILER_TREE)
public Void visitParam(ParamTree node, Void p) {
try {
@@ -616,8 +660,12 @@ public Void visitUnknownInlineTag(UnknownInlineTagTree node, Void p) {
print("{");
print("@");
print(node.getTagName());
- print(" ");
- print(node.getContent());
+ var content = node.getContent();
+ boolean isEmpty = content.stream().allMatch(n -> (n instanceof TextTree t) && t.getBody().isEmpty());
+ if (!isEmpty) {
+ print(" ");
+ print(content);
+ }
print("}");
} catch (IOException e) {
throw new UncheckedIOException(e);
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/DocTreeMaker.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/DocTreeMaker.java
index cd6fdad70e67d..3000b960d85c8 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/DocTreeMaker.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/DocTreeMaker.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -66,6 +66,7 @@
import com.sun.tools.javac.tree.DCTree.DCInheritDoc;
import com.sun.tools.javac.tree.DCTree.DCLink;
import com.sun.tools.javac.tree.DCTree.DCLiteral;
+import com.sun.tools.javac.tree.DCTree.DCMarkdown;
import com.sun.tools.javac.tree.DCTree.DCParam;
import com.sun.tools.javac.tree.DCTree.DCProvides;
import com.sun.tools.javac.tree.DCTree.DCReference;
@@ -333,6 +334,13 @@ public DCLiteral newLiteralTree(TextTree text) {
return tree;
}
+ @Override @DefinedBy(Api.COMPILER_TREE)
+ public DCMarkdown newMarkdownTree(String text) {
+ DCMarkdown tree = new DCMarkdown(text);
+ tree.pos = pos;
+ return tree;
+ }
+
@Override @DefinedBy(Api.COMPILER_TREE)
public DCParam newParamTree(boolean isTypeParameter, IdentifierTree name, List extends DocTree> description) {
DCParam tree = new DCParam(isTypeParameter, (DCIdentifier) name, cast(description));
@@ -538,11 +546,46 @@ private Pair, List> splitBody(Collection extends DocTree>
continue;
}
switch (dt.getKind()) {
- case RETURN:
- case SUMMARY:
+ case RETURN,
+ SUMMARY ->
foundFirstSentence = true;
- break;
- case TEXT:
+
+ // TODO: merge MARKDOWN and TEXT code, perhaps with generic method
+ case MARKDOWN -> {
+ DCMarkdown mt = (DCMarkdown) dt;
+ String s = mt.getContent();
+ DocTree peekedNext = itr.hasNext()
+ ? alist.get(itr.nextIndex())
+ : null;
+ int sbreak = getSentenceBreak(s, peekedNext);
+ if (sbreak > 0) {
+ s = s.substring(0, sbreak).stripTrailing();
+ DCMarkdown text = this.at(spos).newMarkdownTree(s);
+ fs.add(text);
+ foundFirstSentence = true;
+ int nwPos = skipWhiteSpace(mt.getContent(), sbreak);
+ if (nwPos > 0) {
+ DCMarkdown text2 = this.at(spos + nwPos).newMarkdownTree(mt.getContent().substring(nwPos));
+ body.add(text2);
+ }
+ continue;
+ } else if (itr.hasNext()) {
+ // if the next doctree is a break, remove trailing spaces
+ peekedNext = alist.get(itr.nextIndex());
+ boolean sbrk = isSentenceBreak(peekedNext, false);
+ if (sbrk) {
+ DocTree next = itr.next();
+ s = s.stripTrailing();
+ DCMarkdown text = this.at(spos).newMarkdownTree(s);
+ fs.add(text);
+ body.add((DCMarkdown) next); // TODO: why the cast?
+ foundFirstSentence = true;
+ continue;
+ }
+ }
+ }
+
+ case TEXT -> {
DCText tt = (DCText) dt;
String s = tt.getBody();
DocTree peekedNext = itr.hasNext()
@@ -574,14 +617,15 @@ private Pair, List> splitBody(Collection extends DocTree>
continue;
}
}
- break;
- default:
+ }
+
+ default -> {
if (isSentenceBreak(dt, isFirst)) {
body.add((DCTree) dt);
foundFirstSentence = true;
continue;
}
- break;
+ }
}
fs.add((DCTree) dt);
}
diff --git a/src/jdk.compiler/share/classes/module-info.java b/src/jdk.compiler/share/classes/module-info.java
index 4610474318272..a513f3c258e0a 100644
--- a/src/jdk.compiler/share/classes/module-info.java
+++ b/src/jdk.compiler/share/classes/module-info.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -257,8 +257,6 @@
jdk.jdeps,
jdk.javadoc,
jdk.jshell;
- exports jdk.internal.shellsupport.doc to
- jdk.jshell;
uses javax.annotation.processing.Processor;
uses com.sun.source.util.Plugin;
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/Extension.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/Extension.java
new file mode 100644
index 0000000000000..b0f1ed378f69b
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/Extension.java
@@ -0,0 +1,43 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark;
+
+/**
+ * Base interface for a parser/renderer extension.
+ *
+ * Doesn't have any methods itself, but has specific sub interfaces to
+ * configure parser/renderer. This base interface is for convenience, so that a list of extensions can be built and then
+ * used for configuring both the parser and renderer in the same way.
+ */
+public interface Extension {
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/BlockContent.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/BlockContent.java
new file mode 100644
index 0000000000000..64e9e5f7bf193
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/BlockContent.java
@@ -0,0 +1,61 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.internal;
+
+class BlockContent {
+
+ private final StringBuilder sb;
+
+ private int lineCount = 0;
+
+ public BlockContent() {
+ sb = new StringBuilder();
+ }
+
+ public BlockContent(String content) {
+ sb = new StringBuilder(content);
+ }
+
+ public void add(CharSequence line) {
+ if (lineCount != 0) {
+ sb.append('\n');
+ }
+ sb.append(line);
+ lineCount++;
+ }
+
+ public String getString() {
+ return sb.toString();
+ }
+
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/BlockContinueImpl.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/BlockContinueImpl.java
new file mode 100644
index 0000000000000..47697018e406d
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/BlockContinueImpl.java
@@ -0,0 +1,61 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.internal;
+
+import jdk.internal.org.commonmark.parser.block.BlockContinue;
+
+public class BlockContinueImpl extends BlockContinue {
+
+ private final int newIndex;
+ private final int newColumn;
+ private final boolean finalize;
+
+ public BlockContinueImpl(int newIndex, int newColumn, boolean finalize) {
+ this.newIndex = newIndex;
+ this.newColumn = newColumn;
+ this.finalize = finalize;
+ }
+
+ public int getNewIndex() {
+ return newIndex;
+ }
+
+ public int getNewColumn() {
+ return newColumn;
+ }
+
+ public boolean isFinalize() {
+ return finalize;
+ }
+
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/BlockQuoteParser.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/BlockQuoteParser.java
new file mode 100644
index 0000000000000..c7f30af09d730
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/BlockQuoteParser.java
@@ -0,0 +1,94 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.internal;
+
+import jdk.internal.org.commonmark.internal.util.Parsing;
+import jdk.internal.org.commonmark.node.Block;
+import jdk.internal.org.commonmark.node.BlockQuote;
+import jdk.internal.org.commonmark.parser.block.*;
+
+public class BlockQuoteParser extends AbstractBlockParser {
+
+ private final BlockQuote block = new BlockQuote();
+
+ @Override
+ public boolean isContainer() {
+ return true;
+ }
+
+ @Override
+ public boolean canContain(Block block) {
+ return true;
+ }
+
+ @Override
+ public BlockQuote getBlock() {
+ return block;
+ }
+
+ @Override
+ public BlockContinue tryContinue(ParserState state) {
+ int nextNonSpace = state.getNextNonSpaceIndex();
+ if (isMarker(state, nextNonSpace)) {
+ int newColumn = state.getColumn() + state.getIndent() + 1;
+ // optional following space or tab
+ if (Parsing.isSpaceOrTab(state.getLine().getContent(), nextNonSpace + 1)) {
+ newColumn++;
+ }
+ return BlockContinue.atColumn(newColumn);
+ } else {
+ return BlockContinue.none();
+ }
+ }
+
+ private static boolean isMarker(ParserState state, int index) {
+ CharSequence line = state.getLine().getContent();
+ return state.getIndent() < Parsing.CODE_BLOCK_INDENT && index < line.length() && line.charAt(index) == '>';
+ }
+
+ public static class Factory extends AbstractBlockParserFactory {
+ public BlockStart tryStart(ParserState state, MatchedBlockParser matchedBlockParser) {
+ int nextNonSpace = state.getNextNonSpaceIndex();
+ if (isMarker(state, nextNonSpace)) {
+ int newColumn = state.getColumn() + state.getIndent() + 1;
+ // optional following space or tab
+ if (Parsing.isSpaceOrTab(state.getLine().getContent(), nextNonSpace + 1)) {
+ newColumn++;
+ }
+ return BlockStart.of(new BlockQuoteParser()).atColumn(newColumn);
+ } else {
+ return BlockStart.none();
+ }
+ }
+ }
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/BlockStartImpl.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/BlockStartImpl.java
new file mode 100644
index 0000000000000..bde28136b82b1
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/BlockStartImpl.java
@@ -0,0 +1,83 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.internal;
+
+import jdk.internal.org.commonmark.parser.block.BlockParser;
+import jdk.internal.org.commonmark.parser.block.BlockStart;
+
+public class BlockStartImpl extends BlockStart {
+
+ private final BlockParser[] blockParsers;
+ private int newIndex = -1;
+ private int newColumn = -1;
+ private boolean replaceActiveBlockParser = false;
+
+ public BlockStartImpl(BlockParser... blockParsers) {
+ this.blockParsers = blockParsers;
+ }
+
+ public BlockParser[] getBlockParsers() {
+ return blockParsers;
+ }
+
+ public int getNewIndex() {
+ return newIndex;
+ }
+
+ public int getNewColumn() {
+ return newColumn;
+ }
+
+ public boolean isReplaceActiveBlockParser() {
+ return replaceActiveBlockParser;
+ }
+
+ @Override
+ public BlockStart atIndex(int newIndex) {
+ this.newIndex = newIndex;
+ return this;
+ }
+
+ @Override
+ public BlockStart atColumn(int newColumn) {
+ this.newColumn = newColumn;
+ return this;
+ }
+
+ @Override
+ public BlockStart replaceActiveBlockParser() {
+ this.replaceActiveBlockParser = true;
+ return this;
+ }
+
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/Bracket.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/Bracket.java
new file mode 100644
index 0000000000000..b3473bb666f5b
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/Bracket.java
@@ -0,0 +1,96 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.internal;
+
+import jdk.internal.org.commonmark.internal.inline.Position;
+import jdk.internal.org.commonmark.node.Text;
+
+/**
+ * Opening bracket for links ([) or images (![).
+ */
+public class Bracket {
+
+ public final Text node;
+
+ /**
+ * The position of the marker for the bracket ([ or ![)
+ */
+ public final Position markerPosition;
+
+ /**
+ * The position of the content (after the opening bracket)
+ */
+ public final Position contentPosition;
+
+ /**
+ * Whether this is an image or link.
+ */
+ public final boolean image;
+
+ /**
+ * Previous bracket.
+ */
+ public final Bracket previous;
+
+ /**
+ * Previous delimiter (emphasis, etc) before this bracket.
+ */
+ public final Delimiter previousDelimiter;
+
+ /**
+ * Whether this bracket is allowed to form a link/image (also known as "active").
+ */
+ public boolean allowed = true;
+
+ /**
+ * Whether there is an unescaped bracket (opening or closing) anywhere after this opening bracket.
+ */
+ public boolean bracketAfter = false;
+
+ static public Bracket link(Text node, Position markerPosition, Position contentPosition, Bracket previous, Delimiter previousDelimiter) {
+ return new Bracket(node, markerPosition, contentPosition, previous, previousDelimiter, false);
+ }
+
+ static public Bracket image(Text node, Position markerPosition, Position contentPosition, Bracket previous, Delimiter previousDelimiter) {
+ return new Bracket(node, markerPosition, contentPosition, previous, previousDelimiter, true);
+ }
+
+ private Bracket(Text node, Position markerPosition, Position contentPosition, Bracket previous, Delimiter previousDelimiter, boolean image) {
+ this.node = node;
+ this.markerPosition = markerPosition;
+ this.contentPosition = contentPosition;
+ this.image = image;
+ this.previous = previous;
+ this.previousDelimiter = previousDelimiter;
+ }
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/Delimiter.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/Delimiter.java
new file mode 100644
index 0000000000000..8437594dd6da3
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/Delimiter.java
@@ -0,0 +1,114 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.internal;
+
+import jdk.internal.org.commonmark.node.Text;
+import jdk.internal.org.commonmark.parser.delimiter.DelimiterRun;
+
+import java.util.List;
+
+/**
+ * Delimiter (emphasis, strong emphasis or custom emphasis).
+ */
+public class Delimiter implements DelimiterRun {
+
+ public final List characters;
+ public final char delimiterChar;
+ private final int originalLength;
+
+ // Can open emphasis, see spec.
+ private final boolean canOpen;
+
+ // Can close emphasis, see spec.
+ private final boolean canClose;
+
+ public Delimiter previous;
+ public Delimiter next;
+
+ public Delimiter(List characters, char delimiterChar, boolean canOpen, boolean canClose, Delimiter previous) {
+ this.characters = characters;
+ this.delimiterChar = delimiterChar;
+ this.canOpen = canOpen;
+ this.canClose = canClose;
+ this.previous = previous;
+ this.originalLength = characters.size();
+ }
+
+ @Override
+ public boolean canOpen() {
+ return canOpen;
+ }
+
+ @Override
+ public boolean canClose() {
+ return canClose;
+ }
+
+ @Override
+ public int length() {
+ return characters.size();
+ }
+
+ @Override
+ public int originalLength() {
+ return originalLength;
+ }
+
+ @Override
+ public Text getOpener() {
+ return characters.get(characters.size() - 1);
+ }
+
+ @Override
+ public Text getCloser() {
+ return characters.get(0);
+ }
+
+ @Override
+ public Iterable getOpeners(int length) {
+ if (!(length >= 1 && length <= length())) {
+ throw new IllegalArgumentException("length must be between 1 and " + length() + ", was " + length);
+ }
+
+ return characters.subList(characters.size() - length, characters.size());
+ }
+
+ @Override
+ public Iterable getClosers(int length) {
+ if (!(length >= 1 && length <= length())) {
+ throw new IllegalArgumentException("length must be between 1 and " + length() + ", was " + length);
+ }
+
+ return characters.subList(0, length);
+ }
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/DocumentBlockParser.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/DocumentBlockParser.java
new file mode 100644
index 0000000000000..8f18feed1a0a3
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/DocumentBlockParser.java
@@ -0,0 +1,70 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.internal;
+
+import jdk.internal.org.commonmark.node.Block;
+import jdk.internal.org.commonmark.node.Document;
+import jdk.internal.org.commonmark.parser.SourceLine;
+import jdk.internal.org.commonmark.parser.block.AbstractBlockParser;
+import jdk.internal.org.commonmark.parser.block.BlockContinue;
+import jdk.internal.org.commonmark.parser.block.ParserState;
+
+public class DocumentBlockParser extends AbstractBlockParser {
+
+ private final Document document = new Document();
+
+ @Override
+ public boolean isContainer() {
+ return true;
+ }
+
+ @Override
+ public boolean canContain(Block block) {
+ return true;
+ }
+
+ @Override
+ public Document getBlock() {
+ return document;
+ }
+
+ @Override
+ public BlockContinue tryContinue(ParserState state) {
+ return BlockContinue.atIndex(state.getIndex());
+ }
+
+ @Override
+ public void addLine(SourceLine line) {
+ }
+
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/DocumentParser.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/DocumentParser.java
new file mode 100644
index 0000000000000..714c5ce7ef8bb
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/DocumentParser.java
@@ -0,0 +1,609 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.internal;
+
+import jdk.internal.org.commonmark.internal.util.Parsing;
+import jdk.internal.org.commonmark.node.*;
+import jdk.internal.org.commonmark.parser.*;
+import jdk.internal.org.commonmark.parser.block.*;
+import jdk.internal.org.commonmark.parser.delimiter.DelimiterProcessor;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.Reader;
+import java.util.*;
+
+public class DocumentParser implements ParserState {
+
+ private static final Set> CORE_FACTORY_TYPES = new LinkedHashSet<>(Arrays.asList(
+ BlockQuote.class,
+ Heading.class,
+ FencedCodeBlock.class,
+ HtmlBlock.class,
+ ThematicBreak.class,
+ ListBlock.class,
+ IndentedCodeBlock.class));
+
+ private static final Map, BlockParserFactory> NODES_TO_CORE_FACTORIES;
+
+ static {
+ Map, BlockParserFactory> map = new HashMap<>();
+ map.put(BlockQuote.class, new BlockQuoteParser.Factory());
+ map.put(Heading.class, new HeadingParser.Factory());
+ map.put(FencedCodeBlock.class, new FencedCodeBlockParser.Factory());
+ map.put(HtmlBlock.class, new HtmlBlockParser.Factory());
+ map.put(ThematicBreak.class, new ThematicBreakParser.Factory());
+ map.put(ListBlock.class, new ListBlockParser.Factory());
+ map.put(IndentedCodeBlock.class, new IndentedCodeBlockParser.Factory());
+ NODES_TO_CORE_FACTORIES = Collections.unmodifiableMap(map);
+ }
+
+ private SourceLine line;
+
+ /**
+ * Line index (0-based)
+ */
+ private int lineIndex = -1;
+
+ /**
+ * current index (offset) in input line (0-based)
+ */
+ private int index = 0;
+
+ /**
+ * current column of input line (tab causes column to go to next 4-space tab stop) (0-based)
+ */
+ private int column = 0;
+
+ /**
+ * if the current column is within a tab character (partially consumed tab)
+ */
+ private boolean columnIsInTab;
+
+ private int nextNonSpace = 0;
+ private int nextNonSpaceColumn = 0;
+ private int indent = 0;
+ private boolean blank;
+
+ private final List blockParserFactories;
+ private final InlineParserFactory inlineParserFactory;
+ private final List delimiterProcessors;
+ private final IncludeSourceSpans includeSourceSpans;
+ private final DocumentBlockParser documentBlockParser;
+ private final LinkReferenceDefinitions definitions = new LinkReferenceDefinitions();
+
+ private final List openBlockParsers = new ArrayList<>();
+ private final List allBlockParsers = new ArrayList<>();
+
+ public DocumentParser(List blockParserFactories, InlineParserFactory inlineParserFactory,
+ List delimiterProcessors, IncludeSourceSpans includeSourceSpans) {
+ this.blockParserFactories = blockParserFactories;
+ this.inlineParserFactory = inlineParserFactory;
+ this.delimiterProcessors = delimiterProcessors;
+ this.includeSourceSpans = includeSourceSpans;
+
+ this.documentBlockParser = new DocumentBlockParser();
+ activateBlockParser(new OpenBlockParser(documentBlockParser, 0));
+ }
+
+ public static Set> getDefaultBlockParserTypes() {
+ return CORE_FACTORY_TYPES;
+ }
+
+ public static List calculateBlockParserFactories(List customBlockParserFactories, Set> enabledBlockTypes) {
+ List list = new ArrayList<>();
+ // By having the custom factories come first, extensions are able to change behavior of core syntax.
+ list.addAll(customBlockParserFactories);
+ for (Class extends Block> blockType : enabledBlockTypes) {
+ list.add(NODES_TO_CORE_FACTORIES.get(blockType));
+ }
+ return list;
+ }
+
+ public static void checkEnabledBlockTypes(Set> enabledBlockTypes) {
+ for (Class extends Block> enabledBlockType : enabledBlockTypes) {
+ if (!NODES_TO_CORE_FACTORIES.containsKey(enabledBlockType)) {
+ throw new IllegalArgumentException("Can't enable block type " + enabledBlockType + ", possible options are: " + NODES_TO_CORE_FACTORIES.keySet());
+ }
+ }
+ }
+
+ /**
+ * The main parsing function. Returns a parsed document AST.
+ */
+ public Document parse(String input) {
+ int lineStart = 0;
+ int lineBreak;
+ while ((lineBreak = Parsing.findLineBreak(input, lineStart)) != -1) {
+ String line = input.substring(lineStart, lineBreak);
+ parseLine(line);
+ if (lineBreak + 1 < input.length() && input.charAt(lineBreak) == '\r' && input.charAt(lineBreak + 1) == '\n') {
+ lineStart = lineBreak + 2;
+ } else {
+ lineStart = lineBreak + 1;
+ }
+ }
+ if (input.length() > 0 && (lineStart == 0 || lineStart < input.length())) {
+ String line = input.substring(lineStart);
+ parseLine(line);
+ }
+
+ return finalizeAndProcess();
+ }
+
+ public Document parse(Reader input) throws IOException {
+ BufferedReader bufferedReader;
+ if (input instanceof BufferedReader) {
+ bufferedReader = (BufferedReader) input;
+ } else {
+ bufferedReader = new BufferedReader(input);
+ }
+
+ String line;
+ while ((line = bufferedReader.readLine()) != null) {
+ parseLine(line);
+ }
+
+ return finalizeAndProcess();
+ }
+
+ @Override
+ public SourceLine getLine() {
+ return line;
+ }
+
+ @Override
+ public int getIndex() {
+ return index;
+ }
+
+ @Override
+ public int getNextNonSpaceIndex() {
+ return nextNonSpace;
+ }
+
+ @Override
+ public int getColumn() {
+ return column;
+ }
+
+ @Override
+ public int getIndent() {
+ return indent;
+ }
+
+ @Override
+ public boolean isBlank() {
+ return blank;
+ }
+
+ @Override
+ public BlockParser getActiveBlockParser() {
+ return openBlockParsers.get(openBlockParsers.size() - 1).blockParser;
+ }
+
+ /**
+ * Analyze a line of text and update the document appropriately. We parse markdown text by calling this on each
+ * line of input, then finalizing the document.
+ */
+ private void parseLine(CharSequence ln) {
+ setLine(ln);
+
+ // For each containing block, try to parse the associated line start.
+ // The document will always match, so we can skip the first block parser and start at 1 matches
+ int matches = 1;
+ for (int i = 1; i < openBlockParsers.size(); i++) {
+ OpenBlockParser openBlockParser = openBlockParsers.get(i);
+ BlockParser blockParser = openBlockParser.blockParser;
+ findNextNonSpace();
+
+ BlockContinue result = blockParser.tryContinue(this);
+ if (result instanceof BlockContinueImpl) {
+ BlockContinueImpl blockContinue = (BlockContinueImpl) result;
+ openBlockParser.sourceIndex = getIndex();
+ if (blockContinue.isFinalize()) {
+ addSourceSpans();
+ closeBlockParsers(openBlockParsers.size() - i);
+ return;
+ } else {
+ if (blockContinue.getNewIndex() != -1) {
+ setNewIndex(blockContinue.getNewIndex());
+ } else if (blockContinue.getNewColumn() != -1) {
+ setNewColumn(blockContinue.getNewColumn());
+ }
+ matches++;
+ }
+ } else {
+ break;
+ }
+ }
+
+ int unmatchedBlocks = openBlockParsers.size() - matches;
+ BlockParser blockParser = openBlockParsers.get(matches - 1).blockParser;
+ boolean startedNewBlock = false;
+
+ int lastIndex = index;
+
+ // Unless last matched container is a code block, try new container starts,
+ // adding children to the last matched container:
+ boolean tryBlockStarts = blockParser.getBlock() instanceof Paragraph || blockParser.isContainer();
+ while (tryBlockStarts) {
+ lastIndex = index;
+ findNextNonSpace();
+
+ // this is a little performance optimization:
+ if (isBlank() || (indent < Parsing.CODE_BLOCK_INDENT && Parsing.isLetter(this.line.getContent(), nextNonSpace))) {
+ setNewIndex(nextNonSpace);
+ break;
+ }
+
+ BlockStartImpl blockStart = findBlockStart(blockParser);
+ if (blockStart == null) {
+ setNewIndex(nextNonSpace);
+ break;
+ }
+
+ startedNewBlock = true;
+ int sourceIndex = getIndex();
+
+ // We're starting a new block. If we have any previous blocks that need to be closed, we need to do it now.
+ if (unmatchedBlocks > 0) {
+ closeBlockParsers(unmatchedBlocks);
+ unmatchedBlocks = 0;
+ }
+
+ if (blockStart.getNewIndex() != -1) {
+ setNewIndex(blockStart.getNewIndex());
+ } else if (blockStart.getNewColumn() != -1) {
+ setNewColumn(blockStart.getNewColumn());
+ }
+
+ List replacedSourceSpans = null;
+ if (blockStart.isReplaceActiveBlockParser()) {
+ Block replacedBlock = prepareActiveBlockParserForReplacement();
+ replacedSourceSpans = replacedBlock.getSourceSpans();
+ }
+
+ for (BlockParser newBlockParser : blockStart.getBlockParsers()) {
+ addChild(new OpenBlockParser(newBlockParser, sourceIndex));
+ if (replacedSourceSpans != null) {
+ newBlockParser.getBlock().setSourceSpans(replacedSourceSpans);
+ }
+ blockParser = newBlockParser;
+ tryBlockStarts = newBlockParser.isContainer();
+ }
+ }
+
+ // What remains at the offset is a text line. Add the text to the
+ // appropriate block.
+
+ // First check for a lazy paragraph continuation:
+ if (!startedNewBlock && !isBlank() &&
+ getActiveBlockParser().canHaveLazyContinuationLines()) {
+ openBlockParsers.get(openBlockParsers.size() - 1).sourceIndex = lastIndex;
+ // lazy paragraph continuation
+ addLine();
+
+ } else {
+
+ // finalize any blocks not matched
+ if (unmatchedBlocks > 0) {
+ closeBlockParsers(unmatchedBlocks);
+ }
+
+ if (!blockParser.isContainer()) {
+ addLine();
+ } else if (!isBlank()) {
+ // create paragraph container for line
+ ParagraphParser paragraphParser = new ParagraphParser();
+ addChild(new OpenBlockParser(paragraphParser, lastIndex));
+ addLine();
+ } else {
+ // This can happen for a list item like this:
+ // ```
+ // *
+ // list item
+ // ```
+ //
+ // The first line does not start a paragraph yet, but we still want to record source positions.
+ addSourceSpans();
+ }
+ }
+ }
+
+ private void setLine(CharSequence ln) {
+ lineIndex++;
+ index = 0;
+ column = 0;
+ columnIsInTab = false;
+
+ CharSequence lineContent = Parsing.prepareLine(ln);
+ SourceSpan sourceSpan = null;
+ if (includeSourceSpans != IncludeSourceSpans.NONE) {
+ sourceSpan = SourceSpan.of(lineIndex, 0, lineContent.length());
+ }
+ this.line = SourceLine.of(lineContent, sourceSpan);
+ }
+
+ private void findNextNonSpace() {
+ int i = index;
+ int cols = column;
+
+ blank = true;
+ int length = line.getContent().length();
+ while (i < length) {
+ char c = line.getContent().charAt(i);
+ switch (c) {
+ case ' ':
+ i++;
+ cols++;
+ continue;
+ case '\t':
+ i++;
+ cols += (4 - (cols % 4));
+ continue;
+ }
+ blank = false;
+ break;
+ }
+
+ nextNonSpace = i;
+ nextNonSpaceColumn = cols;
+ indent = nextNonSpaceColumn - column;
+ }
+
+ private void setNewIndex(int newIndex) {
+ if (newIndex >= nextNonSpace) {
+ // We can start from here, no need to calculate tab stops again
+ index = nextNonSpace;
+ column = nextNonSpaceColumn;
+ }
+ int length = line.getContent().length();
+ while (index < newIndex && index != length) {
+ advance();
+ }
+ // If we're going to an index as opposed to a column, we're never within a tab
+ columnIsInTab = false;
+ }
+
+ private void setNewColumn(int newColumn) {
+ if (newColumn >= nextNonSpaceColumn) {
+ // We can start from here, no need to calculate tab stops again
+ index = nextNonSpace;
+ column = nextNonSpaceColumn;
+ }
+ int length = line.getContent().length();
+ while (column < newColumn && index != length) {
+ advance();
+ }
+ if (column > newColumn) {
+ // Last character was a tab and we overshot our target
+ index--;
+ column = newColumn;
+ columnIsInTab = true;
+ } else {
+ columnIsInTab = false;
+ }
+ }
+
+ private void advance() {
+ char c = line.getContent().charAt(index);
+ index++;
+ if (c == '\t') {
+ column += Parsing.columnsToNextTabStop(column);
+ } else {
+ column++;
+ }
+ }
+
+ /**
+ * Add line content to the active block parser. We assume it can accept lines -- that check should be done before
+ * calling this.
+ */
+ private void addLine() {
+ CharSequence content;
+ if (columnIsInTab) {
+ // Our column is in a partially consumed tab. Expand the remaining columns (to the next tab stop) to spaces.
+ int afterTab = index + 1;
+ CharSequence rest = line.getContent().subSequence(afterTab, line.getContent().length());
+ int spaces = Parsing.columnsToNextTabStop(column);
+ StringBuilder sb = new StringBuilder(spaces + rest.length());
+ for (int i = 0; i < spaces; i++) {
+ sb.append(' ');
+ }
+ sb.append(rest);
+ content = sb.toString();
+ } else if (index == 0) {
+ content = line.getContent();
+ } else {
+ content = line.getContent().subSequence(index, line.getContent().length());
+ }
+ SourceSpan sourceSpan = null;
+ if (includeSourceSpans == IncludeSourceSpans.BLOCKS_AND_INLINES) {
+ // Note that if we're in a partially-consumed tab, the length here corresponds to the content but not to the
+ // actual source length. That sounds like a problem, but I haven't found a test case where it matters (yet).
+ sourceSpan = SourceSpan.of(lineIndex, index, content.length());
+ }
+ getActiveBlockParser().addLine(SourceLine.of(content, sourceSpan));
+ addSourceSpans();
+ }
+
+ private void addSourceSpans() {
+ if (includeSourceSpans != IncludeSourceSpans.NONE) {
+ // Don't add source spans for Document itself (it would get the whole source text)
+ for (int i = 1; i < openBlockParsers.size(); i++) {
+ OpenBlockParser openBlockParser = openBlockParsers.get(i);
+ int blockIndex = openBlockParser.sourceIndex;
+ int length = line.getContent().length() - blockIndex;
+ if (length != 0) {
+ openBlockParser.blockParser.addSourceSpan(SourceSpan.of(lineIndex, blockIndex, length));
+ }
+ }
+ }
+ }
+
+ private BlockStartImpl findBlockStart(BlockParser blockParser) {
+ MatchedBlockParser matchedBlockParser = new MatchedBlockParserImpl(blockParser);
+ for (BlockParserFactory blockParserFactory : blockParserFactories) {
+ BlockStart result = blockParserFactory.tryStart(this, matchedBlockParser);
+ if (result instanceof BlockStartImpl) {
+ return (BlockStartImpl) result;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Finalize a block. Close it and do any necessary postprocessing, e.g. setting the content of blocks and
+ * collecting link reference definitions from paragraphs.
+ */
+ private void finalize(BlockParser blockParser) {
+ if (blockParser instanceof ParagraphParser) {
+ addDefinitionsFrom((ParagraphParser) blockParser);
+ }
+
+ blockParser.closeBlock();
+ }
+
+ private void addDefinitionsFrom(ParagraphParser paragraphParser) {
+ for (LinkReferenceDefinition definition : paragraphParser.getDefinitions()) {
+ // Add nodes into document before paragraph.
+ paragraphParser.getBlock().insertBefore(definition);
+
+ definitions.add(definition);
+ }
+ }
+
+ /**
+ * Walk through a block & children recursively, parsing string content into inline content where appropriate.
+ */
+ private void processInlines() {
+ InlineParserContextImpl context = new InlineParserContextImpl(delimiterProcessors, definitions);
+ InlineParser inlineParser = inlineParserFactory.create(context);
+
+ for (BlockParser blockParser : allBlockParsers) {
+ blockParser.parseInlines(inlineParser);
+ }
+ }
+
+ /**
+ * Add block of type tag as a child of the tip. If the tip can't accept children, close and finalize it and try
+ * its parent, and so on until we find a block that can accept children.
+ */
+ private void addChild(OpenBlockParser openBlockParser) {
+ while (!getActiveBlockParser().canContain(openBlockParser.blockParser.getBlock())) {
+ closeBlockParsers(1);
+ }
+
+ getActiveBlockParser().getBlock().appendChild(openBlockParser.blockParser.getBlock());
+ activateBlockParser(openBlockParser);
+ }
+
+ private void activateBlockParser(OpenBlockParser openBlockParser) {
+ openBlockParsers.add(openBlockParser);
+ }
+
+ private OpenBlockParser deactivateBlockParser() {
+ return openBlockParsers.remove(openBlockParsers.size() - 1);
+ }
+
+ private Block prepareActiveBlockParserForReplacement() {
+ // Note that we don't want to parse inlines, as it's getting replaced.
+ BlockParser old = deactivateBlockParser().blockParser;
+
+ if (old instanceof ParagraphParser) {
+ ParagraphParser paragraphParser = (ParagraphParser) old;
+ // Collect any link reference definitions. Note that replacing the active block parser is done after a
+ // block parser got the current paragraph content using MatchedBlockParser#getContentString. In case the
+ // paragraph started with link reference definitions, we parse and strip them before the block parser gets
+ // the content. We want to keep them.
+ // If no replacement happens, we collect the definitions as part of finalizing paragraph blocks.
+ addDefinitionsFrom(paragraphParser);
+ }
+
+ // Do this so that source positions are calculated, which we will carry over to the replacing block.
+ old.closeBlock();
+ old.getBlock().unlink();
+ return old.getBlock();
+ }
+
+ private Document finalizeAndProcess() {
+ closeBlockParsers(openBlockParsers.size());
+ processInlines();
+ return documentBlockParser.getBlock();
+ }
+
+ private void closeBlockParsers(int count) {
+ for (int i = 0; i < count; i++) {
+ BlockParser blockParser = deactivateBlockParser().blockParser;
+ finalize(blockParser);
+ // Remember for inline parsing. Note that a lot of blocks don't need inline parsing. We could have a
+ // separate interface (e.g. BlockParserWithInlines) so that we only have to remember those that actually
+ // have inlines to parse.
+ allBlockParsers.add(blockParser);
+ }
+ }
+
+ private static class MatchedBlockParserImpl implements MatchedBlockParser {
+
+ private final BlockParser matchedBlockParser;
+
+ public MatchedBlockParserImpl(BlockParser matchedBlockParser) {
+ this.matchedBlockParser = matchedBlockParser;
+ }
+
+ @Override
+ public BlockParser getMatchedBlockParser() {
+ return matchedBlockParser;
+ }
+
+ @Override
+ public SourceLines getParagraphLines() {
+ if (matchedBlockParser instanceof ParagraphParser) {
+ ParagraphParser paragraphParser = (ParagraphParser) matchedBlockParser;
+ return paragraphParser.getParagraphLines();
+ }
+ return SourceLines.empty();
+ }
+ }
+
+ private static class OpenBlockParser {
+ private final BlockParser blockParser;
+ private int sourceIndex;
+
+ OpenBlockParser(BlockParser blockParser, int sourceIndex) {
+ this.blockParser = blockParser;
+ this.sourceIndex = sourceIndex;
+ }
+ }
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/FencedCodeBlockParser.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/FencedCodeBlockParser.java
new file mode 100644
index 0000000000000..92de6f34bf952
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/FencedCodeBlockParser.java
@@ -0,0 +1,164 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.internal;
+
+import jdk.internal.org.commonmark.internal.util.Parsing;
+import jdk.internal.org.commonmark.node.Block;
+import jdk.internal.org.commonmark.node.FencedCodeBlock;
+import jdk.internal.org.commonmark.parser.SourceLine;
+import jdk.internal.org.commonmark.parser.block.*;
+
+import static jdk.internal.org.commonmark.internal.util.Escaping.unescapeString;
+
+public class FencedCodeBlockParser extends AbstractBlockParser {
+
+ private final FencedCodeBlock block = new FencedCodeBlock();
+
+ private String firstLine;
+ private StringBuilder otherLines = new StringBuilder();
+
+ public FencedCodeBlockParser(char fenceChar, int fenceLength, int fenceIndent) {
+ block.setFenceChar(fenceChar);
+ block.setFenceLength(fenceLength);
+ block.setFenceIndent(fenceIndent);
+ }
+
+ @Override
+ public Block getBlock() {
+ return block;
+ }
+
+ @Override
+ public BlockContinue tryContinue(ParserState state) {
+ int nextNonSpace = state.getNextNonSpaceIndex();
+ int newIndex = state.getIndex();
+ CharSequence line = state.getLine().getContent();
+ if (state.getIndent() < Parsing.CODE_BLOCK_INDENT && nextNonSpace < line.length() && line.charAt(nextNonSpace) == block.getFenceChar() && isClosing(line, nextNonSpace)) {
+ // closing fence - we're at end of line, so we can finalize now
+ return BlockContinue.finished();
+ } else {
+ // skip optional spaces of fence indent
+ int i = block.getFenceIndent();
+ int length = line.length();
+ while (i > 0 && newIndex < length && line.charAt(newIndex) == ' ') {
+ newIndex++;
+ i--;
+ }
+ }
+ return BlockContinue.atIndex(newIndex);
+ }
+
+ @Override
+ public void addLine(SourceLine line) {
+ if (firstLine == null) {
+ firstLine = line.getContent().toString();
+ } else {
+ otherLines.append(line.getContent());
+ otherLines.append('\n');
+ }
+ }
+
+ @Override
+ public void closeBlock() {
+ // first line becomes info string
+ block.setInfo(unescapeString(firstLine.trim()));
+ block.setLiteral(otherLines.toString());
+ }
+
+ public static class Factory extends AbstractBlockParserFactory {
+
+ @Override
+ public BlockStart tryStart(ParserState state, MatchedBlockParser matchedBlockParser) {
+ int indent = state.getIndent();
+ if (indent >= Parsing.CODE_BLOCK_INDENT) {
+ return BlockStart.none();
+ }
+
+ int nextNonSpace = state.getNextNonSpaceIndex();
+ FencedCodeBlockParser blockParser = checkOpener(state.getLine().getContent(), nextNonSpace, indent);
+ if (blockParser != null) {
+ return BlockStart.of(blockParser).atIndex(nextNonSpace + blockParser.block.getFenceLength());
+ } else {
+ return BlockStart.none();
+ }
+ }
+ }
+
+ // spec: A code fence is a sequence of at least three consecutive backtick characters (`) or tildes (~). (Tildes and
+ // backticks cannot be mixed.)
+ private static FencedCodeBlockParser checkOpener(CharSequence line, int index, int indent) {
+ int backticks = 0;
+ int tildes = 0;
+ int length = line.length();
+ loop:
+ for (int i = index; i < length; i++) {
+ switch (line.charAt(i)) {
+ case '`':
+ backticks++;
+ break;
+ case '~':
+ tildes++;
+ break;
+ default:
+ break loop;
+ }
+ }
+ if (backticks >= 3 && tildes == 0) {
+ // spec: If the info string comes after a backtick fence, it may not contain any backtick characters.
+ if (Parsing.find('`', line, index + backticks) != -1) {
+ return null;
+ }
+ return new FencedCodeBlockParser('`', backticks, indent);
+ } else if (tildes >= 3 && backticks == 0) {
+ // spec: Info strings for tilde code blocks can contain backticks and tildes
+ return new FencedCodeBlockParser('~', tildes, indent);
+ } else {
+ return null;
+ }
+ }
+
+ // spec: The content of the code block consists of all subsequent lines, until a closing code fence of the same type
+ // as the code block began with (backticks or tildes), and with at least as many backticks or tildes as the opening
+ // code fence.
+ private boolean isClosing(CharSequence line, int index) {
+ char fenceChar = block.getFenceChar();
+ int fenceLength = block.getFenceLength();
+ int fences = Parsing.skip(fenceChar, line, index, line.length()) - index;
+ if (fences < fenceLength) {
+ return false;
+ }
+ // spec: The closing code fence [...] may be followed only by spaces, which are ignored.
+ int after = Parsing.skipSpaceTab(line, index + fences, line.length());
+ return after == line.length();
+ }
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/HeadingParser.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/HeadingParser.java
new file mode 100644
index 0000000000000..7568803760c45
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/HeadingParser.java
@@ -0,0 +1,187 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.internal;
+
+import jdk.internal.org.commonmark.internal.inline.Position;
+import jdk.internal.org.commonmark.internal.inline.Scanner;
+import jdk.internal.org.commonmark.internal.util.Parsing;
+import jdk.internal.org.commonmark.node.Block;
+import jdk.internal.org.commonmark.node.Heading;
+import jdk.internal.org.commonmark.parser.InlineParser;
+import jdk.internal.org.commonmark.parser.SourceLine;
+import jdk.internal.org.commonmark.parser.SourceLines;
+import jdk.internal.org.commonmark.parser.block.*;
+
+public class HeadingParser extends AbstractBlockParser {
+
+ private final Heading block = new Heading();
+ private final SourceLines content;
+
+ public HeadingParser(int level, SourceLines content) {
+ block.setLevel(level);
+ this.content = content;
+ }
+
+ @Override
+ public Block getBlock() {
+ return block;
+ }
+
+ @Override
+ public BlockContinue tryContinue(ParserState parserState) {
+ // In both ATX and Setext headings, once we have the heading markup, there's nothing more to parse.
+ return BlockContinue.none();
+ }
+
+ @Override
+ public void parseInlines(InlineParser inlineParser) {
+ inlineParser.parse(content, block);
+ }
+
+ public static class Factory extends AbstractBlockParserFactory {
+
+ @Override
+ public BlockStart tryStart(ParserState state, MatchedBlockParser matchedBlockParser) {
+ if (state.getIndent() >= Parsing.CODE_BLOCK_INDENT) {
+ return BlockStart.none();
+ }
+
+ SourceLine line = state.getLine();
+ int nextNonSpace = state.getNextNonSpaceIndex();
+ if (line.getContent().charAt(nextNonSpace) == '#') {
+ HeadingParser atxHeading = getAtxHeading(line.substring(nextNonSpace, line.getContent().length()));
+ if (atxHeading != null) {
+ return BlockStart.of(atxHeading).atIndex(line.getContent().length());
+ }
+ }
+
+ int setextHeadingLevel = getSetextHeadingLevel(line.getContent(), nextNonSpace);
+ if (setextHeadingLevel > 0) {
+ SourceLines paragraph = matchedBlockParser.getParagraphLines();
+ if (!paragraph.isEmpty()) {
+ return BlockStart.of(new HeadingParser(setextHeadingLevel, paragraph))
+ .atIndex(line.getContent().length())
+ .replaceActiveBlockParser();
+ }
+ }
+
+ return BlockStart.none();
+ }
+ }
+
+ // spec: An ATX heading consists of a string of characters, parsed as inline content, between an opening sequence of
+ // 1\u20136 unescaped # characters and an optional closing sequence of any number of unescaped # characters. The opening
+ // sequence of # characters must be followed by a space or by the end of line. The optional closing sequence of #s
+ // must be preceded by a space and may be followed by spaces only.
+ private static HeadingParser getAtxHeading(SourceLine line) {
+ Scanner scanner = Scanner.of(SourceLines.of(line));
+ int level = scanner.matchMultiple('#');
+
+ if (level == 0 || level > 6) {
+ return null;
+ }
+
+ if (!scanner.hasNext()) {
+ // End of line after markers is an empty heading
+ return new HeadingParser(level, SourceLines.empty());
+ }
+
+ char next = scanner.peek();
+ if (!(next == ' ' || next == '\t')) {
+ return null;
+ }
+
+ scanner.whitespace();
+ Position start = scanner.position();
+ Position end = start;
+ boolean hashCanEnd = true;
+
+ while (scanner.hasNext()) {
+ char c = scanner.peek();
+ switch (c) {
+ case '#':
+ if (hashCanEnd) {
+ scanner.matchMultiple('#');
+ int whitespace = scanner.whitespace();
+ // If there's other characters, the hashes and spaces were part of the heading
+ if (scanner.hasNext()) {
+ end = scanner.position();
+ }
+ hashCanEnd = whitespace > 0;
+ } else {
+ scanner.next();
+ end = scanner.position();
+ }
+ break;
+ case ' ':
+ case '\t':
+ hashCanEnd = true;
+ scanner.next();
+ break;
+ default:
+ hashCanEnd = false;
+ scanner.next();
+ end = scanner.position();
+ }
+ }
+
+ SourceLines source = scanner.getSource(start, end);
+ String content = source.getContent();
+ if (content.isEmpty()) {
+ return new HeadingParser(level, SourceLines.empty());
+ }
+ return new HeadingParser(level, source);
+ }
+
+ // spec: A setext heading underline is a sequence of = characters or a sequence of - characters, with no more than
+ // 3 spaces indentation and any number of trailing spaces.
+ @SuppressWarnings("fallthrough") private static int getSetextHeadingLevel(CharSequence line, int index) {
+ switch (line.charAt(index)) {
+ case '=':
+ if (isSetextHeadingRest(line, index + 1, '=')) {
+ return 1;
+ }
+ case '-':
+ if (isSetextHeadingRest(line, index + 1, '-')) {
+ return 2;
+ }
+ }
+ return 0;
+ }
+
+ private static boolean isSetextHeadingRest(CharSequence line, int index, char marker) {
+ int afterMarker = Parsing.skip(marker, line, index, line.length());
+ int afterSpace = Parsing.skipSpaceTab(line, afterMarker, line.length());
+ return afterSpace >= line.length();
+ }
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/HtmlBlockParser.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/HtmlBlockParser.java
new file mode 100644
index 0000000000000..fe467f825e125
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/HtmlBlockParser.java
@@ -0,0 +1,164 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.internal;
+
+import jdk.internal.org.commonmark.internal.util.Parsing;
+import jdk.internal.org.commonmark.node.Block;
+import jdk.internal.org.commonmark.node.HtmlBlock;
+import jdk.internal.org.commonmark.node.Paragraph;
+import jdk.internal.org.commonmark.parser.SourceLine;
+import jdk.internal.org.commonmark.parser.block.*;
+
+import java.util.regex.Pattern;
+
+public class HtmlBlockParser extends AbstractBlockParser {
+
+ private static final Pattern[][] BLOCK_PATTERNS = new Pattern[][]{
+ {null, null}, // not used (no type 0)
+ {
+ Pattern.compile("^<(?:script|pre|style|textarea)(?:\\s|>|$)", Pattern.CASE_INSENSITIVE),
+ Pattern.compile("(?:script|pre|style|textarea)>", Pattern.CASE_INSENSITIVE)
+ },
+ {
+ Pattern.compile("^")
+ },
+ {
+ Pattern.compile("^<[?]"),
+ Pattern.compile("\\?>")
+ },
+ {
+ Pattern.compile("^")
+ },
+ {
+ Pattern.compile("^")
+ },
+ {
+ Pattern.compile("^?(?:" +
+ "address|article|aside|" +
+ "base|basefont|blockquote|body|" +
+ "caption|center|col|colgroup|" +
+ "dd|details|dialog|dir|div|dl|dt|" +
+ "fieldset|figcaption|figure|footer|form|frame|frameset|" +
+ "h1|h2|h3|h4|h5|h6|head|header|hr|html|" +
+ "iframe|" +
+ "legend|li|link|" +
+ "main|menu|menuitem|" +
+ "nav|noframes|" +
+ "ol|optgroup|option|" +
+ "p|param|" +
+ "section|source|summary|" +
+ "table|tbody|td|tfoot|th|thead|title|tr|track|" +
+ "ul" +
+ ")(?:\\s|[/]?[>]|$)", Pattern.CASE_INSENSITIVE),
+ null // terminated by blank line
+ },
+ {
+ Pattern.compile("^(?:" + Parsing.OPENTAG + '|' + Parsing.CLOSETAG + ")\\s*$", Pattern.CASE_INSENSITIVE),
+ null // terminated by blank line
+ }
+ };
+
+ private final HtmlBlock block = new HtmlBlock();
+ private final Pattern closingPattern;
+
+ private boolean finished = false;
+ private BlockContent content = new BlockContent();
+
+ private HtmlBlockParser(Pattern closingPattern) {
+ this.closingPattern = closingPattern;
+ }
+
+ @Override
+ public Block getBlock() {
+ return block;
+ }
+
+ @Override
+ public BlockContinue tryContinue(ParserState state) {
+ if (finished) {
+ return BlockContinue.none();
+ }
+
+ // Blank line ends type 6 and type 7 blocks
+ if (state.isBlank() && closingPattern == null) {
+ return BlockContinue.none();
+ } else {
+ return BlockContinue.atIndex(state.getIndex());
+ }
+ }
+
+ @Override
+ public void addLine(SourceLine line) {
+ content.add(line.getContent());
+
+ if (closingPattern != null && closingPattern.matcher(line.getContent()).find()) {
+ finished = true;
+ }
+ }
+
+ @Override
+ public void closeBlock() {
+ block.setLiteral(content.getString());
+ content = null;
+ }
+
+ public static class Factory extends AbstractBlockParserFactory {
+
+ @Override
+ public BlockStart tryStart(ParserState state, MatchedBlockParser matchedBlockParser) {
+ int nextNonSpace = state.getNextNonSpaceIndex();
+ CharSequence line = state.getLine().getContent();
+
+ if (state.getIndent() < 4 && line.charAt(nextNonSpace) == '<') {
+ for (int blockType = 1; blockType <= 7; blockType++) {
+ // Type 7 can not interrupt a paragraph (not even a lazy one)
+ if (blockType == 7 && (
+ matchedBlockParser.getMatchedBlockParser().getBlock() instanceof Paragraph ||
+ state.getActiveBlockParser().canHaveLazyContinuationLines())) {
+ continue;
+ }
+ Pattern opener = BLOCK_PATTERNS[blockType][0];
+ Pattern closer = BLOCK_PATTERNS[blockType][1];
+ boolean matches = opener.matcher(line.subSequence(nextNonSpace, line.length())).find();
+ if (matches) {
+ return BlockStart.of(new HtmlBlockParser(closer)).atIndex(state.getIndex());
+ }
+ }
+ }
+ return BlockStart.none();
+ }
+ }
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/IndentedCodeBlockParser.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/IndentedCodeBlockParser.java
new file mode 100644
index 0000000000000..ffbd6a24d8c17
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/IndentedCodeBlockParser.java
@@ -0,0 +1,104 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.internal;
+
+import jdk.internal.org.commonmark.internal.util.Parsing;
+import jdk.internal.org.commonmark.node.Block;
+import jdk.internal.org.commonmark.node.IndentedCodeBlock;
+import jdk.internal.org.commonmark.node.Paragraph;
+import jdk.internal.org.commonmark.parser.SourceLine;
+import jdk.internal.org.commonmark.parser.block.*;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class IndentedCodeBlockParser extends AbstractBlockParser {
+
+ private final IndentedCodeBlock block = new IndentedCodeBlock();
+ private final List lines = new ArrayList<>();
+
+ @Override
+ public Block getBlock() {
+ return block;
+ }
+
+ @Override
+ public BlockContinue tryContinue(ParserState state) {
+ if (state.getIndent() >= Parsing.CODE_BLOCK_INDENT) {
+ return BlockContinue.atColumn(state.getColumn() + Parsing.CODE_BLOCK_INDENT);
+ } else if (state.isBlank()) {
+ return BlockContinue.atIndex(state.getNextNonSpaceIndex());
+ } else {
+ return BlockContinue.none();
+ }
+ }
+
+ @Override
+ public void addLine(SourceLine line) {
+ lines.add(line.getContent());
+ }
+
+ @Override
+ public void closeBlock() {
+ int lastNonBlank = lines.size() - 1;
+ while (lastNonBlank >= 0) {
+ if (!Parsing.isBlank(lines.get(lastNonBlank))) {
+ break;
+ }
+ lastNonBlank--;
+ }
+
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < lastNonBlank + 1; i++) {
+ sb.append(lines.get(i));
+ sb.append('\n');
+ }
+
+ String literal = sb.toString();
+ block.setLiteral(literal);
+ }
+
+ public static class Factory extends AbstractBlockParserFactory {
+
+ @Override
+ public BlockStart tryStart(ParserState state, MatchedBlockParser matchedBlockParser) {
+ // An indented code block cannot interrupt a paragraph.
+ if (state.getIndent() >= Parsing.CODE_BLOCK_INDENT && !state.isBlank() && !(state.getActiveBlockParser().getBlock() instanceof Paragraph)) {
+ return BlockStart.of(new IndentedCodeBlockParser()).atColumn(state.getColumn() + Parsing.CODE_BLOCK_INDENT);
+ } else {
+ return BlockStart.none();
+ }
+ }
+ }
+}
+
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/InlineParserContextImpl.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/InlineParserContextImpl.java
new file mode 100644
index 0000000000000..dab02aeedd16a
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/InlineParserContextImpl.java
@@ -0,0 +1,62 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.internal;
+
+import jdk.internal.org.commonmark.node.LinkReferenceDefinition;
+import jdk.internal.org.commonmark.parser.InlineParserContext;
+import jdk.internal.org.commonmark.parser.delimiter.DelimiterProcessor;
+
+import java.util.List;
+import java.util.Map;
+
+public class InlineParserContextImpl implements InlineParserContext {
+
+ private final List delimiterProcessors;
+ private final LinkReferenceDefinitions linkReferenceDefinitions;
+
+ public InlineParserContextImpl(List delimiterProcessors,
+ LinkReferenceDefinitions linkReferenceDefinitions) {
+ this.delimiterProcessors = delimiterProcessors;
+ this.linkReferenceDefinitions = linkReferenceDefinitions;
+ }
+
+ @Override
+ public List getCustomDelimiterProcessors() {
+ return delimiterProcessors;
+ }
+
+ @Override
+ public LinkReferenceDefinition getLinkReferenceDefinition(String label) {
+ return linkReferenceDefinitions.get(label);
+ }
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/InlineParserImpl.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/InlineParserImpl.java
new file mode 100644
index 0000000000000..14ccbd5ef61d5
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/InlineParserImpl.java
@@ -0,0 +1,786 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.internal;
+
+import jdk.internal.org.commonmark.internal.inline.Scanner;
+import jdk.internal.org.commonmark.internal.inline.*;
+import jdk.internal.org.commonmark.internal.util.Escaping;
+import jdk.internal.org.commonmark.internal.util.LinkScanner;
+import jdk.internal.org.commonmark.internal.util.Parsing;
+import jdk.internal.org.commonmark.node.*;
+import jdk.internal.org.commonmark.parser.InlineParser;
+import jdk.internal.org.commonmark.parser.InlineParserContext;
+import jdk.internal.org.commonmark.parser.SourceLines;
+import jdk.internal.org.commonmark.parser.delimiter.DelimiterProcessor;
+
+import java.util.*;
+
+public class InlineParserImpl implements InlineParser, InlineParserState {
+
+ private final BitSet specialCharacters;
+ private final Map delimiterProcessors;
+ private final InlineParserContext context;
+ private final Map> inlineParsers;
+
+ private Scanner scanner;
+ private boolean includeSourceSpans;
+ private int trailingSpaces;
+
+ /**
+ * Top delimiter (emphasis, strong emphasis or custom emphasis). (Brackets are on a separate stack, different
+ * from the algorithm described in the spec.)
+ */
+ private Delimiter lastDelimiter;
+
+ /**
+ * Top opening bracket ([ or ![)).
+ */
+ private Bracket lastBracket;
+
+ public InlineParserImpl(InlineParserContext inlineParserContext) {
+ this.delimiterProcessors = calculateDelimiterProcessors(inlineParserContext.getCustomDelimiterProcessors());
+
+ this.context = inlineParserContext;
+ this.inlineParsers = new HashMap<>();
+ this.inlineParsers.put('\\', Collections.singletonList(new BackslashInlineParser()));
+ this.inlineParsers.put('`', Collections.singletonList(new BackticksInlineParser()));
+ this.inlineParsers.put('&', Collections.singletonList(new EntityInlineParser()));
+ this.inlineParsers.put('<', Arrays.asList(new AutolinkInlineParser(), new HtmlInlineParser()));
+
+ this.specialCharacters = calculateSpecialCharacters(this.delimiterProcessors.keySet(), inlineParsers.keySet());
+ }
+
+ public static BitSet calculateSpecialCharacters(Set delimiterCharacters, Set characters) {
+ BitSet bitSet = new BitSet();
+ for (Character c : delimiterCharacters) {
+ bitSet.set(c);
+ }
+ for (Character c : characters) {
+ bitSet.set(c);
+ }
+ bitSet.set('[');
+ bitSet.set(']');
+ bitSet.set('!');
+ bitSet.set('\n');
+ return bitSet;
+ }
+
+ public static Map calculateDelimiterProcessors(List delimiterProcessors) {
+ Map map = new HashMap<>();
+ addDelimiterProcessors(Arrays.asList(new AsteriskDelimiterProcessor(), new UnderscoreDelimiterProcessor()), map);
+ addDelimiterProcessors(delimiterProcessors, map);
+ return map;
+ }
+
+ @Override
+ public Scanner scanner() {
+ return scanner;
+ }
+
+ private static void addDelimiterProcessors(Iterable delimiterProcessors, Map map) {
+ for (DelimiterProcessor delimiterProcessor : delimiterProcessors) {
+ char opening = delimiterProcessor.getOpeningCharacter();
+ char closing = delimiterProcessor.getClosingCharacter();
+ if (opening == closing) {
+ DelimiterProcessor old = map.get(opening);
+ if (old != null && old.getOpeningCharacter() == old.getClosingCharacter()) {
+ StaggeredDelimiterProcessor s;
+ if (old instanceof StaggeredDelimiterProcessor) {
+ s = (StaggeredDelimiterProcessor) old;
+ } else {
+ s = new StaggeredDelimiterProcessor(opening);
+ s.add(old);
+ }
+ s.add(delimiterProcessor);
+ map.put(opening, s);
+ } else {
+ addDelimiterProcessorForChar(opening, delimiterProcessor, map);
+ }
+ } else {
+ addDelimiterProcessorForChar(opening, delimiterProcessor, map);
+ addDelimiterProcessorForChar(closing, delimiterProcessor, map);
+ }
+ }
+ }
+
+ private static void addDelimiterProcessorForChar(char delimiterChar, DelimiterProcessor toAdd, Map delimiterProcessors) {
+ DelimiterProcessor existing = delimiterProcessors.put(delimiterChar, toAdd);
+ if (existing != null) {
+ throw new IllegalArgumentException("Delimiter processor conflict with delimiter char '" + delimiterChar + "'");
+ }
+ }
+
+ /**
+ * Parse content in block into inline children, appending them to the block node.
+ */
+ @Override
+ public void parse(SourceLines lines, Node block) {
+ reset(lines);
+
+ while (true) {
+ List extends Node> nodes = parseInline();
+ if (nodes != null) {
+ for (Node node : nodes) {
+ block.appendChild(node);
+ }
+ } else {
+ break;
+ }
+ }
+
+ processDelimiters(null);
+ mergeChildTextNodes(block);
+ }
+
+ void reset(SourceLines lines) {
+ this.scanner = Scanner.of(lines);
+ this.includeSourceSpans = !lines.getSourceSpans().isEmpty();
+ this.trailingSpaces = 0;
+ this.lastDelimiter = null;
+ this.lastBracket = null;
+ }
+
+ private Text text(SourceLines sourceLines) {
+ Text text = new Text(sourceLines.getContent());
+ text.setSourceSpans(sourceLines.getSourceSpans());
+ return text;
+ }
+
+ /**
+ * Parse the next inline element in subject, advancing our position.
+ * On success, return the new inline node.
+ * On failure, return null.
+ */
+ private List extends Node> parseInline() {
+ char c = scanner.peek();
+
+ switch (c) {
+ case '[':
+ return Collections.singletonList(parseOpenBracket());
+ case '!':
+ return Collections.singletonList(parseBang());
+ case ']':
+ return Collections.singletonList(parseCloseBracket());
+ case '\n':
+ return Collections.singletonList(parseLineBreak());
+ case Scanner.END:
+ return null;
+ }
+
+ // No inline parser, delimiter or other special handling.
+ if (!specialCharacters.get(c)) {
+ return Collections.singletonList(parseText());
+ }
+
+ List inlineParsers = this.inlineParsers.get(c);
+ if (inlineParsers != null) {
+ Position position = scanner.position();
+ for (InlineContentParser inlineParser : inlineParsers) {
+ ParsedInline parsedInline = inlineParser.tryParse(this);
+ if (parsedInline instanceof ParsedInlineImpl) {
+ ParsedInlineImpl parsedInlineImpl = (ParsedInlineImpl) parsedInline;
+ Node node = parsedInlineImpl.getNode();
+ scanner.setPosition(parsedInlineImpl.getPosition());
+ if (includeSourceSpans && node.getSourceSpans().isEmpty()) {
+ node.setSourceSpans(scanner.getSource(position, scanner.position()).getSourceSpans());
+ }
+ return Collections.singletonList(node);
+ } else {
+ // Reset position
+ scanner.setPosition(position);
+ }
+ }
+ }
+
+ DelimiterProcessor delimiterProcessor = delimiterProcessors.get(c);
+ if (delimiterProcessor != null) {
+ List extends Node> nodes = parseDelimiters(delimiterProcessor, c);
+ if (nodes != null) {
+ return nodes;
+ }
+ }
+
+ // If we get here, even for a special/delimiter character, we will just treat it as text.
+ return Collections.singletonList(parseText());
+ }
+
+ /**
+ * Attempt to parse delimiters like emphasis, strong emphasis or custom delimiters.
+ */
+ private List extends Node> parseDelimiters(DelimiterProcessor delimiterProcessor, char delimiterChar) {
+ DelimiterData res = scanDelimiters(delimiterProcessor, delimiterChar);
+ if (res == null) {
+ return null;
+ }
+
+ List characters = res.characters;
+
+ // Add entry to stack for this opener
+ lastDelimiter = new Delimiter(characters, delimiterChar, res.canOpen, res.canClose, lastDelimiter);
+ if (lastDelimiter.previous != null) {
+ lastDelimiter.previous.next = lastDelimiter;
+ }
+
+ return characters;
+ }
+
+ /**
+ * Add open bracket to delimiter stack and add a text node to block's children.
+ */
+ private Node parseOpenBracket() {
+ Position start = scanner.position();
+ scanner.next();
+ Position contentPosition = scanner.position();
+
+ Text node = text(scanner.getSource(start, contentPosition));
+
+ // Add entry to stack for this opener
+ addBracket(Bracket.link(node, start, contentPosition, lastBracket, lastDelimiter));
+
+ return node;
+ }
+
+ /**
+ * If next character is [, and ! delimiter to delimiter stack and add a text node to block's children.
+ * Otherwise just add a text node.
+ */
+ private Node parseBang() {
+ Position start = scanner.position();
+ scanner.next();
+ if (scanner.next('[')) {
+ Position contentPosition = scanner.position();
+ Text node = text(scanner.getSource(start, contentPosition));
+
+ // Add entry to stack for this opener
+ addBracket(Bracket.image(node, start, contentPosition, lastBracket, lastDelimiter));
+ return node;
+ } else {
+ return text(scanner.getSource(start, scanner.position()));
+ }
+ }
+
+ /**
+ * Try to match close bracket against an opening in the delimiter stack. Return either a link or image, or a
+ * plain [ character. If there is a matching delimiter, remove it from the delimiter stack.
+ */
+ private Node parseCloseBracket() {
+ Position beforeClose = scanner.position();
+ scanner.next();
+ Position afterClose = scanner.position();
+
+ // Get previous `[` or ``
+ if (scanner.next('(')) {
+ scanner.whitespace();
+ dest = parseLinkDestination(scanner);
+ if (dest == null) {
+ scanner.setPosition(afterClose);
+ } else {
+ int whitespace = scanner.whitespace();
+ // title needs a whitespace before
+ if (whitespace >= 1) {
+ title = parseLinkTitle(scanner);
+ scanner.whitespace();
+ }
+ if (!scanner.next(')')) {
+ // Don't have a closing `)`, so it's not a destination and title -> reset.
+ // Note that something like `[foo](` could be valid, `(` will just be text.
+ scanner.setPosition(afterClose);
+ dest = null;
+ title = null;
+ }
+ }
+ }
+
+ // Maybe a reference link like `[foo][bar]`, `[foo][]` or `[foo]`.
+ // Note that even `[foo](` could be a valid link if there's a reference, which is why this is not just an `else`
+ // here.
+ if (dest == null) {
+ // See if there's a link label like `[bar]` or `[]`
+ String ref = parseLinkLabel(scanner);
+ if (ref == null) {
+ scanner.setPosition(afterClose);
+ }
+ if ((ref == null || ref.isEmpty()) && !opener.bracketAfter) {
+ // If the second label is empty `[foo][]` or missing `[foo]`, then the first label is the reference.
+ // But it can only be a reference when there's no (unescaped) bracket in it.
+ // If there is, we don't even need to try to look up the reference. This is an optimization.
+ ref = scanner.getSource(opener.contentPosition, beforeClose).getContent();
+ }
+
+ if (ref != null) {
+ LinkReferenceDefinition definition = context.getLinkReferenceDefinition(ref);
+ if (definition != null) {
+ dest = definition.getDestination();
+ title = definition.getTitle();
+ }
+ }
+ }
+
+ if (dest != null) {
+ // If we got here, we have a link or image
+ Node linkOrImage = opener.image ? new Image(dest, title) : new Link(dest, title);
+
+ // Add all nodes between the opening bracket and now (closing bracket) as child nodes of the link
+ Node node = opener.node.getNext();
+ while (node != null) {
+ Node next = node.getNext();
+ linkOrImage.appendChild(node);
+ node = next;
+ }
+
+ if (includeSourceSpans) {
+ linkOrImage.setSourceSpans(scanner.getSource(opener.markerPosition, scanner.position()).getSourceSpans());
+ }
+
+ // Process delimiters such as emphasis inside link/image
+ processDelimiters(opener.previousDelimiter);
+ mergeChildTextNodes(linkOrImage);
+ // We don't need the corresponding text node anymore, we turned it into a link/image node
+ opener.node.unlink();
+ removeLastBracket();
+
+ // Links within links are not allowed. We found this link, so there can be no other link around it.
+ if (!opener.image) {
+ Bracket bracket = lastBracket;
+ while (bracket != null) {
+ if (!bracket.image) {
+ // Disallow link opener. It will still get matched, but will not result in a link.
+ bracket.allowed = false;
+ }
+ bracket = bracket.previous;
+ }
+ }
+
+ return linkOrImage;
+
+ } else {
+ // No link or image, parse just the bracket as text and continue
+ removeLastBracket();
+
+ scanner.setPosition(afterClose);
+ return text(scanner.getSource(beforeClose, afterClose));
+ }
+ }
+
+ private void addBracket(Bracket bracket) {
+ if (lastBracket != null) {
+ lastBracket.bracketAfter = true;
+ }
+ lastBracket = bracket;
+ }
+
+ private void removeLastBracket() {
+ lastBracket = lastBracket.previous;
+ }
+
+ /**
+ * Attempt to parse link destination, returning the string or null if no match.
+ */
+ private String parseLinkDestination(Scanner scanner) {
+ char delimiter = scanner.peek();
+ Position start = scanner.position();
+ if (!LinkScanner.scanLinkDestination(scanner)) {
+ return null;
+ }
+
+ String dest;
+ if (delimiter == '<') {
+ // chop off surrounding <..>:
+ String rawDestination = scanner.getSource(start, scanner.position()).getContent();
+ dest = rawDestination.substring(1, rawDestination.length() - 1);
+ } else {
+ dest = scanner.getSource(start, scanner.position()).getContent();
+ }
+
+ return Escaping.unescapeString(dest);
+ }
+
+ /**
+ * Attempt to parse link title (sans quotes), returning the string or null if no match.
+ */
+ private String parseLinkTitle(Scanner scanner) {
+ Position start = scanner.position();
+ if (!LinkScanner.scanLinkTitle(scanner)) {
+ return null;
+ }
+
+ // chop off ', " or parens
+ String rawTitle = scanner.getSource(start, scanner.position()).getContent();
+ String title = rawTitle.substring(1, rawTitle.length() - 1);
+ return Escaping.unescapeString(title);
+ }
+
+ /**
+ * Attempt to parse a link label, returning the label between the brackets or null.
+ */
+ String parseLinkLabel(Scanner scanner) {
+ if (!scanner.next('[')) {
+ return null;
+ }
+
+ Position start = scanner.position();
+ if (!LinkScanner.scanLinkLabelContent(scanner)) {
+ return null;
+ }
+ Position end = scanner.position();
+
+ if (!scanner.next(']')) {
+ return null;
+ }
+
+ String content = scanner.getSource(start, end).getContent();
+ // spec: A link label can have at most 999 characters inside the square brackets.
+ if (content.length() > 999) {
+ return null;
+ }
+
+ return content;
+ }
+
+ private Node parseLineBreak() {
+ scanner.next();
+
+ if (trailingSpaces >= 2) {
+ return new HardLineBreak();
+ } else {
+ return new SoftLineBreak();
+ }
+ }
+
+ /**
+ * Parse the next character as plain text, and possibly more if the following characters are non-special.
+ */
+ private Node parseText() {
+ Position start = scanner.position();
+ scanner.next();
+ char c;
+ while (true) {
+ c = scanner.peek();
+ if (c == Scanner.END || specialCharacters.get(c)) {
+ break;
+ }
+ scanner.next();
+ }
+
+ SourceLines source = scanner.getSource(start, scanner.position());
+ String content = source.getContent();
+
+ if (c == '\n') {
+ // We parsed until the end of the line. Trim any trailing spaces and remember them (for hard line breaks).
+ int end = Parsing.skipBackwards(' ', content, content.length() - 1, 0) + 1;
+ trailingSpaces = content.length() - end;
+ content = content.substring(0, end);
+ } else if (c == Scanner.END) {
+ // For the last line, both tabs and spaces are trimmed for some reason (checked with commonmark.js).
+ int end = Parsing.skipSpaceTabBackwards(content, content.length() - 1, 0) + 1;
+ content = content.substring(0, end);
+ }
+
+ Text text = new Text(content);
+ text.setSourceSpans(source.getSourceSpans());
+ return text;
+ }
+
+ /**
+ * Scan a sequence of characters with code delimiterChar, and return information about the number of delimiters
+ * and whether they are positioned such that they can open and/or close emphasis or strong emphasis.
+ *
+ * @return information about delimiter run, or {@code null}
+ */
+ private DelimiterData scanDelimiters(DelimiterProcessor delimiterProcessor, char delimiterChar) {
+ int before = scanner.peekPreviousCodePoint();
+ Position start = scanner.position();
+
+ // Quick check to see if we have enough delimiters.
+ int delimiterCount = scanner.matchMultiple(delimiterChar);
+ if (delimiterCount < delimiterProcessor.getMinLength()) {
+ scanner.setPosition(start);
+ return null;
+ }
+
+ // We do have enough, extract a text node for each delimiter character.
+ List delimiters = new ArrayList<>();
+ scanner.setPosition(start);
+ Position positionBefore = start;
+ while (scanner.next(delimiterChar)) {
+ delimiters.add(text(scanner.getSource(positionBefore, scanner.position())));
+ positionBefore = scanner.position();
+ }
+
+ int after = scanner.peekCodePoint();
+
+ // We could be more lazy here, in most cases we don't need to do every match case.
+ boolean beforeIsPunctuation = before == Scanner.END || Parsing.isPunctuationCodePoint(before);
+ boolean beforeIsWhitespace = before == Scanner.END || Parsing.isWhitespaceCodePoint(before);
+ boolean afterIsPunctuation = after == Scanner.END || Parsing.isPunctuationCodePoint(after);
+ boolean afterIsWhitespace = after == Scanner.END || Parsing.isWhitespaceCodePoint(after);
+
+ boolean leftFlanking = !afterIsWhitespace &&
+ (!afterIsPunctuation || beforeIsWhitespace || beforeIsPunctuation);
+ boolean rightFlanking = !beforeIsWhitespace &&
+ (!beforeIsPunctuation || afterIsWhitespace || afterIsPunctuation);
+ boolean canOpen;
+ boolean canClose;
+ if (delimiterChar == '_') {
+ canOpen = leftFlanking && (!rightFlanking || beforeIsPunctuation);
+ canClose = rightFlanking && (!leftFlanking || afterIsPunctuation);
+ } else {
+ canOpen = leftFlanking && delimiterChar == delimiterProcessor.getOpeningCharacter();
+ canClose = rightFlanking && delimiterChar == delimiterProcessor.getClosingCharacter();
+ }
+
+ return new DelimiterData(delimiters, canOpen, canClose);
+ }
+
+ private void processDelimiters(Delimiter stackBottom) {
+
+ Map openersBottom = new HashMap<>();
+
+ // find first closer above stackBottom:
+ Delimiter closer = lastDelimiter;
+ while (closer != null && closer.previous != stackBottom) {
+ closer = closer.previous;
+ }
+ // move forward, looking for closers, and handling each
+ while (closer != null) {
+ char delimiterChar = closer.delimiterChar;
+
+ DelimiterProcessor delimiterProcessor = delimiterProcessors.get(delimiterChar);
+ if (!closer.canClose() || delimiterProcessor == null) {
+ closer = closer.next;
+ continue;
+ }
+
+ char openingDelimiterChar = delimiterProcessor.getOpeningCharacter();
+
+ // Found delimiter closer. Now look back for first matching opener.
+ int usedDelims = 0;
+ boolean openerFound = false;
+ boolean potentialOpenerFound = false;
+ Delimiter opener = closer.previous;
+ while (opener != null && opener != stackBottom && opener != openersBottom.get(delimiterChar)) {
+ if (opener.canOpen() && opener.delimiterChar == openingDelimiterChar) {
+ potentialOpenerFound = true;
+ usedDelims = delimiterProcessor.process(opener, closer);
+ if (usedDelims > 0) {
+ openerFound = true;
+ break;
+ }
+ }
+ opener = opener.previous;
+ }
+
+ if (!openerFound) {
+ if (!potentialOpenerFound) {
+ // Set lower bound for future searches for openers.
+ // Only do this when we didn't even have a potential
+ // opener (one that matches the character and can open).
+ // If an opener was rejected because of the number of
+ // delimiters (e.g. because of the "multiple of 3" rule),
+ // we want to consider it next time because the number
+ // of delimiters can change as we continue processing.
+ openersBottom.put(delimiterChar, closer.previous);
+ if (!closer.canOpen()) {
+ // We can remove a closer that can't be an opener,
+ // once we've seen there's no matching opener:
+ removeDelimiterKeepNode(closer);
+ }
+ }
+ closer = closer.next;
+ continue;
+ }
+
+ // Remove number of used delimiters nodes.
+ for (int i = 0; i < usedDelims; i++) {
+ Text delimiter = opener.characters.remove(opener.characters.size() - 1);
+ delimiter.unlink();
+ }
+ for (int i = 0; i < usedDelims; i++) {
+ Text delimiter = closer.characters.remove(0);
+ delimiter.unlink();
+ }
+
+ removeDelimitersBetween(opener, closer);
+
+ // No delimiter characters left to process, so we can remove delimiter and the now empty node.
+ if (opener.length() == 0) {
+ removeDelimiterAndNodes(opener);
+ }
+
+ if (closer.length() == 0) {
+ Delimiter next = closer.next;
+ removeDelimiterAndNodes(closer);
+ closer = next;
+ }
+ }
+
+ // remove all delimiters
+ while (lastDelimiter != null && lastDelimiter != stackBottom) {
+ removeDelimiterKeepNode(lastDelimiter);
+ }
+ }
+
+ private void removeDelimitersBetween(Delimiter opener, Delimiter closer) {
+ Delimiter delimiter = closer.previous;
+ while (delimiter != null && delimiter != opener) {
+ Delimiter previousDelimiter = delimiter.previous;
+ removeDelimiterKeepNode(delimiter);
+ delimiter = previousDelimiter;
+ }
+ }
+
+ /**
+ * Remove the delimiter and the corresponding text node. For used delimiters, e.g. `*` in `*foo*`.
+ */
+ private void removeDelimiterAndNodes(Delimiter delim) {
+ removeDelimiter(delim);
+ }
+
+ /**
+ * Remove the delimiter but keep the corresponding node as text. For unused delimiters such as `_` in `foo_bar`.
+ */
+ private void removeDelimiterKeepNode(Delimiter delim) {
+ removeDelimiter(delim);
+ }
+
+ private void removeDelimiter(Delimiter delim) {
+ if (delim.previous != null) {
+ delim.previous.next = delim.next;
+ }
+ if (delim.next == null) {
+ // top of stack
+ lastDelimiter = delim.previous;
+ } else {
+ delim.next.previous = delim.previous;
+ }
+ }
+
+ private void mergeChildTextNodes(Node node) {
+ // No children, no need for merging
+ if (node.getFirstChild() == null) {
+ return;
+ }
+
+ mergeTextNodesInclusive(node.getFirstChild(), node.getLastChild());
+ }
+
+ private void mergeTextNodesInclusive(Node fromNode, Node toNode) {
+ Text first = null;
+ Text last = null;
+ int length = 0;
+
+ Node node = fromNode;
+ while (node != null) {
+ if (node instanceof Text) {
+ Text text = (Text) node;
+ if (first == null) {
+ first = text;
+ }
+ length += text.getLiteral().length();
+ last = text;
+ } else {
+ mergeIfNeeded(first, last, length);
+ first = null;
+ last = null;
+ length = 0;
+
+ mergeChildTextNodes(node);
+ }
+ if (node == toNode) {
+ break;
+ }
+ node = node.getNext();
+ }
+
+ mergeIfNeeded(first, last, length);
+ }
+
+ private void mergeIfNeeded(Text first, Text last, int textLength) {
+ if (first != null && last != null && first != last) {
+ StringBuilder sb = new StringBuilder(textLength);
+ sb.append(first.getLiteral());
+ SourceSpans sourceSpans = null;
+ if (includeSourceSpans) {
+ sourceSpans = new SourceSpans();
+ sourceSpans.addAll(first.getSourceSpans());
+ }
+ Node node = first.getNext();
+ Node stop = last.getNext();
+ while (node != stop) {
+ sb.append(((Text) node).getLiteral());
+ if (sourceSpans != null) {
+ sourceSpans.addAll(node.getSourceSpans());
+ }
+
+ Node unlink = node;
+ node = node.getNext();
+ unlink.unlink();
+ }
+ String literal = sb.toString();
+ first.setLiteral(literal);
+ if (sourceSpans != null) {
+ first.setSourceSpans(sourceSpans.getSourceSpans());
+ }
+ }
+ }
+
+ private static class DelimiterData {
+
+ final List characters;
+ final boolean canClose;
+ final boolean canOpen;
+
+ DelimiterData(List characters, boolean canOpen, boolean canClose) {
+ this.characters = characters;
+ this.canOpen = canOpen;
+ this.canClose = canClose;
+ }
+ }
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/LinkReferenceDefinitionParser.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/LinkReferenceDefinitionParser.java
new file mode 100644
index 0000000000000..af03efca7de40
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/LinkReferenceDefinitionParser.java
@@ -0,0 +1,311 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.internal;
+
+import jdk.internal.org.commonmark.internal.inline.Position;
+import jdk.internal.org.commonmark.internal.inline.Scanner;
+import jdk.internal.org.commonmark.internal.util.Escaping;
+import jdk.internal.org.commonmark.internal.util.LinkScanner;
+import jdk.internal.org.commonmark.node.LinkReferenceDefinition;
+import jdk.internal.org.commonmark.node.SourceSpan;
+import jdk.internal.org.commonmark.parser.SourceLine;
+import jdk.internal.org.commonmark.parser.SourceLines;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Parser for link reference definitions at the beginning of a paragraph.
+ *
+ * @see Link reference definitions
+ */
+public class LinkReferenceDefinitionParser {
+
+ private State state = State.START_DEFINITION;
+
+ private final List paragraphLines = new ArrayList<>();
+ private final List definitions = new ArrayList<>();
+ private final List sourceSpans = new ArrayList<>();
+
+ private StringBuilder label;
+ private String destination;
+ private char titleDelimiter;
+ private StringBuilder title;
+ private boolean referenceValid = false;
+
+ public void parse(SourceLine line) {
+ paragraphLines.add(line);
+ if (state == State.PARAGRAPH) {
+ // We're in a paragraph now. Link reference definitions can only appear at the beginning, so once
+ // we're in a paragraph, there's no going back.
+ return;
+ }
+
+ Scanner scanner = Scanner.of(SourceLines.of(line));
+ while (scanner.hasNext()) {
+ boolean success;
+ switch (state) {
+ case START_DEFINITION: {
+ success = startDefinition(scanner);
+ break;
+ }
+ case LABEL: {
+ success = label(scanner);
+ break;
+ }
+ case DESTINATION: {
+ success = destination(scanner);
+ break;
+ }
+ case START_TITLE: {
+ success = startTitle(scanner);
+ break;
+ }
+ case TITLE: {
+ success = title(scanner);
+ break;
+ }
+ default: {
+ throw new IllegalStateException("Unknown parsing state: " + state);
+ }
+ }
+ // Parsing failed, which means we fall back to treating text as a paragraph.
+ if (!success) {
+ state = State.PARAGRAPH;
+ return;
+ }
+ }
+ }
+
+ public void addSourceSpan(SourceSpan sourceSpan) {
+ sourceSpans.add(sourceSpan);
+ }
+
+ /**
+ * @return the lines that are normal paragraph content, without newlines
+ */
+ SourceLines getParagraphLines() {
+ return SourceLines.of(paragraphLines);
+ }
+
+ List getParagraphSourceSpans() {
+ return sourceSpans;
+ }
+
+ List getDefinitions() {
+ finishReference();
+ return definitions;
+ }
+
+ State getState() {
+ return state;
+ }
+
+ private boolean startDefinition(Scanner scanner) {
+ scanner.whitespace();
+ if (!scanner.next('[')) {
+ return false;
+ }
+
+ state = State.LABEL;
+ label = new StringBuilder();
+
+ if (!scanner.hasNext()) {
+ label.append('\n');
+ }
+ return true;
+ }
+
+ private boolean label(Scanner scanner) {
+ Position start = scanner.position();
+ if (!LinkScanner.scanLinkLabelContent(scanner)) {
+ return false;
+ }
+
+ label.append(scanner.getSource(start, scanner.position()).getContent());
+
+ if (!scanner.hasNext()) {
+ // label might continue on next line
+ label.append('\n');
+ return true;
+ } else if (scanner.next(']')) {
+ // end of label
+ if (!scanner.next(':')) {
+ return false;
+ }
+
+ // spec: A link label can have at most 999 characters inside the square brackets.
+ if (label.length() > 999) {
+ return false;
+ }
+
+ String normalizedLabel = Escaping.normalizeLabelContent(label.toString());
+ if (normalizedLabel.isEmpty()) {
+ return false;
+ }
+
+ state = State.DESTINATION;
+
+ scanner.whitespace();
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ private boolean destination(Scanner scanner) {
+ scanner.whitespace();
+ Position start = scanner.position();
+ if (!LinkScanner.scanLinkDestination(scanner)) {
+ return false;
+ }
+
+ String rawDestination = scanner.getSource(start, scanner.position()).getContent();
+ destination = rawDestination.startsWith("<") ?
+ rawDestination.substring(1, rawDestination.length() - 1) :
+ rawDestination;
+
+ int whitespace = scanner.whitespace();
+ if (!scanner.hasNext()) {
+ // Destination was at end of line, so this is a valid reference for sure (and maybe a title).
+ // If not at end of line, wait for title to be valid first.
+ referenceValid = true;
+ paragraphLines.clear();
+ } else if (whitespace == 0) {
+ // spec: The title must be separated from the link destination by whitespace
+ return false;
+ }
+
+ state = State.START_TITLE;
+ return true;
+ }
+
+ private boolean startTitle(Scanner scanner) {
+ scanner.whitespace();
+ if (!scanner.hasNext()) {
+ state = State.START_DEFINITION;
+ return true;
+ }
+
+ titleDelimiter = '\0';
+ char c = scanner.peek();
+ switch (c) {
+ case '"':
+ case '\'':
+ titleDelimiter = c;
+ break;
+ case '(':
+ titleDelimiter = ')';
+ break;
+ }
+
+ if (titleDelimiter != '\0') {
+ state = State.TITLE;
+ title = new StringBuilder();
+ scanner.next();
+ if (!scanner.hasNext()) {
+ title.append('\n');
+ }
+ } else {
+ finishReference();
+ // There might be another reference instead, try that for the same character.
+ state = State.START_DEFINITION;
+ }
+ return true;
+ }
+
+ private boolean title(Scanner scanner) {
+ Position start = scanner.position();
+ if (!LinkScanner.scanLinkTitleContent(scanner, titleDelimiter)) {
+ // Invalid title, stop
+ return false;
+ }
+
+ title.append(scanner.getSource(start, scanner.position()).getContent());
+
+ if (!scanner.hasNext()) {
+ // Title ran until the end of line, so continue on next line (until we find the delimiter)
+ title.append('\n');
+ return true;
+ }
+
+ // Skip delimiter character
+ scanner.next();
+ scanner.whitespace();
+ if (scanner.hasNext()) {
+ // spec: No further non-whitespace characters may occur on the line.
+ return false;
+ }
+ referenceValid = true;
+ finishReference();
+ paragraphLines.clear();
+
+ // See if there's another definition.
+ state = State.START_DEFINITION;
+ return true;
+ }
+
+ private void finishReference() {
+ if (!referenceValid) {
+ return;
+ }
+
+ String d = Escaping.unescapeString(destination);
+ String t = title != null ? Escaping.unescapeString(title.toString()) : null;
+ LinkReferenceDefinition definition = new LinkReferenceDefinition(label.toString(), d, t);
+ definition.setSourceSpans(sourceSpans);
+ sourceSpans.clear();
+ definitions.add(definition);
+
+ label = null;
+ referenceValid = false;
+ destination = null;
+ title = null;
+ }
+
+ enum State {
+ // Looking for the start of a definition, i.e. `[`
+ START_DEFINITION,
+ // Parsing the label, i.e. `foo` within `[foo]`
+ LABEL,
+ // Parsing the destination, i.e. `/url` in `[foo]: /url`
+ DESTINATION,
+ // Looking for the start of a title, i.e. the first `"` in `[foo]: /url "title"`
+ START_TITLE,
+ // Parsing the content of the title, i.e. `title` in `[foo]: /url "title"`
+ TITLE,
+
+ // End state, no matter what kind of lines we add, they won't be references
+ PARAGRAPH,
+ }
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/LinkReferenceDefinitions.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/LinkReferenceDefinitions.java
new file mode 100644
index 0000000000000..e1def1de98e9b
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/LinkReferenceDefinitions.java
@@ -0,0 +1,59 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.internal;
+
+import jdk.internal.org.commonmark.internal.util.Escaping;
+import jdk.internal.org.commonmark.node.LinkReferenceDefinition;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+public class LinkReferenceDefinitions {
+
+ // LinkedHashMap for determinism and to preserve document order
+ private final Map definitions = new LinkedHashMap<>();
+
+ public void add(LinkReferenceDefinition definition) {
+ String normalizedLabel = Escaping.normalizeLabelContent(definition.getLabel());
+
+ // spec: When there are multiple matching link reference definitions, the first is used
+ if (!definitions.containsKey(normalizedLabel)) {
+ definitions.put(normalizedLabel, definition);
+ }
+ }
+
+ public LinkReferenceDefinition get(String label) {
+ String normalizedLabel = Escaping.normalizeLabelContent(label);
+ return definitions.get(normalizedLabel);
+ }
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/ListBlockParser.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/ListBlockParser.java
new file mode 100644
index 0000000000000..7635c3d587957
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/ListBlockParser.java
@@ -0,0 +1,288 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.internal;
+
+import jdk.internal.org.commonmark.internal.util.Parsing;
+import jdk.internal.org.commonmark.node.*;
+import jdk.internal.org.commonmark.parser.block.*;
+
+public class ListBlockParser extends AbstractBlockParser {
+
+ private final ListBlock block;
+
+ private boolean hadBlankLine;
+ private int linesAfterBlank;
+
+ public ListBlockParser(ListBlock block) {
+ this.block = block;
+ }
+
+ @Override
+ public boolean isContainer() {
+ return true;
+ }
+
+ @Override
+ public boolean canContain(Block childBlock) {
+ if (childBlock instanceof ListItem) {
+ // Another list item is added to this list block. If the previous line was blank, that means this list block
+ // is "loose" (not tight).
+ //
+ // spec: A list is loose if any of its constituent list items are separated by blank lines
+ if (hadBlankLine && linesAfterBlank == 1) {
+ block.setTight(false);
+ hadBlankLine = false;
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public Block getBlock() {
+ return block;
+ }
+
+ @Override
+ public BlockContinue tryContinue(ParserState state) {
+ if (state.isBlank()) {
+ hadBlankLine = true;
+ linesAfterBlank = 0;
+ } else if (hadBlankLine) {
+ linesAfterBlank++;
+ }
+ // List blocks themselves don't have any markers, only list items. So try to stay in the list.
+ // If there is a block start other than list item, canContain makes sure that this list is closed.
+ return BlockContinue.atIndex(state.getIndex());
+ }
+
+ /**
+ * Parse a list marker and return data on the marker or null.
+ */
+ private static ListData parseList(CharSequence line, final int markerIndex, final int markerColumn,
+ final boolean inParagraph) {
+ ListMarkerData listMarker = parseListMarker(line, markerIndex);
+ if (listMarker == null) {
+ return null;
+ }
+ ListBlock listBlock = listMarker.listBlock;
+
+ int indexAfterMarker = listMarker.indexAfterMarker;
+ int markerLength = indexAfterMarker - markerIndex;
+ // marker doesn't include tabs, so counting them as columns directly is ok
+ int columnAfterMarker = markerColumn + markerLength;
+ // the column within the line where the content starts
+ int contentColumn = columnAfterMarker;
+
+ // See at which column the content starts if there is content
+ boolean hasContent = false;
+ int length = line.length();
+ for (int i = indexAfterMarker; i < length; i++) {
+ char c = line.charAt(i);
+ if (c == '\t') {
+ contentColumn += Parsing.columnsToNextTabStop(contentColumn);
+ } else if (c == ' ') {
+ contentColumn++;
+ } else {
+ hasContent = true;
+ break;
+ }
+ }
+
+ if (inParagraph) {
+ // If the list item is ordered, the start number must be 1 to interrupt a paragraph.
+ if (listBlock instanceof OrderedList && ((OrderedList) listBlock).getStartNumber() != 1) {
+ return null;
+ }
+ // Empty list item can not interrupt a paragraph.
+ if (!hasContent) {
+ return null;
+ }
+ }
+
+ if (!hasContent || (contentColumn - columnAfterMarker) > Parsing.CODE_BLOCK_INDENT) {
+ // If this line is blank or has a code block, default to 1 space after marker
+ contentColumn = columnAfterMarker + 1;
+ }
+
+ return new ListData(listBlock, contentColumn);
+ }
+
+ private static ListMarkerData parseListMarker(CharSequence line, int index) {
+ char c = line.charAt(index);
+ switch (c) {
+ // spec: A bullet list marker is a -, +, or * character.
+ case '-':
+ case '+':
+ case '*':
+ if (isSpaceTabOrEnd(line, index + 1)) {
+ BulletList bulletList = new BulletList();
+ bulletList.setBulletMarker(c);
+ return new ListMarkerData(bulletList, index + 1);
+ } else {
+ return null;
+ }
+ default:
+ return parseOrderedList(line, index);
+ }
+ }
+
+ // spec: An ordered list marker is a sequence of 1\u20139 arabic digits (0-9), followed by either a `.` character or a
+ // `)` character.
+ private static ListMarkerData parseOrderedList(CharSequence line, int index) {
+ int digits = 0;
+ int length = line.length();
+ for (int i = index; i < length; i++) {
+ char c = line.charAt(i);
+ switch (c) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ digits++;
+ if (digits > 9) {
+ return null;
+ }
+ break;
+ case '.':
+ case ')':
+ if (digits >= 1 && isSpaceTabOrEnd(line, i + 1)) {
+ String number = line.subSequence(index, i).toString();
+ OrderedList orderedList = new OrderedList();
+ orderedList.setStartNumber(Integer.parseInt(number));
+ orderedList.setDelimiter(c);
+ return new ListMarkerData(orderedList, i + 1);
+ } else {
+ return null;
+ }
+ default:
+ return null;
+ }
+ }
+ return null;
+ }
+
+ private static boolean isSpaceTabOrEnd(CharSequence line, int index) {
+ if (index < line.length()) {
+ switch (line.charAt(index)) {
+ case ' ':
+ case '\t':
+ return true;
+ default:
+ return false;
+ }
+ } else {
+ return true;
+ }
+ }
+
+ /**
+ * Returns true if the two list items are of the same type,
+ * with the same delimiter and bullet character. This is used
+ * in agglomerating list items into lists.
+ */
+ private static boolean listsMatch(ListBlock a, ListBlock b) {
+ if (a instanceof BulletList && b instanceof BulletList) {
+ return equals(((BulletList) a).getBulletMarker(), ((BulletList) b).getBulletMarker());
+ } else if (a instanceof OrderedList && b instanceof OrderedList) {
+ return equals(((OrderedList) a).getDelimiter(), ((OrderedList) b).getDelimiter());
+ }
+ return false;
+ }
+
+ private static boolean equals(Object a, Object b) {
+ return (a == null) ? (b == null) : a.equals(b);
+ }
+
+ public static class Factory extends AbstractBlockParserFactory {
+
+ @Override
+ public BlockStart tryStart(ParserState state, MatchedBlockParser matchedBlockParser) {
+ BlockParser matched = matchedBlockParser.getMatchedBlockParser();
+
+ if (state.getIndent() >= Parsing.CODE_BLOCK_INDENT) {
+ return BlockStart.none();
+ }
+ int markerIndex = state.getNextNonSpaceIndex();
+ int markerColumn = state.getColumn() + state.getIndent();
+ boolean inParagraph = !matchedBlockParser.getParagraphLines().isEmpty();
+ ListData listData = parseList(state.getLine().getContent(), markerIndex, markerColumn, inParagraph);
+ if (listData == null) {
+ return BlockStart.none();
+ }
+
+ int newColumn = listData.contentColumn;
+ ListItemParser listItemParser = new ListItemParser(newColumn - state.getColumn());
+
+ // prepend the list block if needed
+ if (!(matched instanceof ListBlockParser) ||
+ !(listsMatch((ListBlock) matched.getBlock(), listData.listBlock))) {
+
+ ListBlockParser listBlockParser = new ListBlockParser(listData.listBlock);
+ // We start out with assuming a list is tight. If we find a blank line, we set it to loose later.
+ listData.listBlock.setTight(true);
+
+ return BlockStart.of(listBlockParser, listItemParser).atColumn(newColumn);
+ } else {
+ return BlockStart.of(listItemParser).atColumn(newColumn);
+ }
+ }
+ }
+
+ private static class ListData {
+ final ListBlock listBlock;
+ final int contentColumn;
+
+ ListData(ListBlock listBlock, int contentColumn) {
+ this.listBlock = listBlock;
+ this.contentColumn = contentColumn;
+ }
+ }
+
+ private static class ListMarkerData {
+ final ListBlock listBlock;
+ final int indexAfterMarker;
+
+ ListMarkerData(ListBlock listBlock, int indexAfterMarker) {
+ this.listBlock = listBlock;
+ this.indexAfterMarker = indexAfterMarker;
+ }
+ }
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/ListItemParser.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/ListItemParser.java
new file mode 100644
index 0000000000000..4f4a5a24fc804
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/ListItemParser.java
@@ -0,0 +1,105 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.internal;
+
+import jdk.internal.org.commonmark.node.Block;
+import jdk.internal.org.commonmark.node.ListBlock;
+import jdk.internal.org.commonmark.node.ListItem;
+import jdk.internal.org.commonmark.node.Paragraph;
+import jdk.internal.org.commonmark.parser.block.AbstractBlockParser;
+import jdk.internal.org.commonmark.parser.block.BlockContinue;
+import jdk.internal.org.commonmark.parser.block.ParserState;
+
+public class ListItemParser extends AbstractBlockParser {
+
+ private final ListItem block = new ListItem();
+
+ /**
+ * Minimum number of columns that the content has to be indented (relative to the containing block) to be part of
+ * this list item.
+ */
+ private int contentIndent;
+
+ private boolean hadBlankLine;
+
+ public ListItemParser(int contentIndent) {
+ this.contentIndent = contentIndent;
+ }
+
+ @Override
+ public boolean isContainer() {
+ return true;
+ }
+
+ @Override
+ public boolean canContain(Block childBlock) {
+ if (hadBlankLine) {
+ // We saw a blank line in this list item, that means the list block is loose.
+ //
+ // spec: if any of its constituent list items directly contain two block-level elements with a blank line
+ // between them
+ Block parent = block.getParent();
+ if (parent instanceof ListBlock) {
+ ((ListBlock) parent).setTight(false);
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public Block getBlock() {
+ return block;
+ }
+
+ @Override
+ public BlockContinue tryContinue(ParserState state) {
+ if (state.isBlank()) {
+ if (block.getFirstChild() == null) {
+ // Blank line after empty list item
+ return BlockContinue.none();
+ } else {
+ Block activeBlock = state.getActiveBlockParser().getBlock();
+ // If the active block is a code block, blank lines in it should not affect if the list is tight.
+ hadBlankLine = activeBlock instanceof Paragraph || activeBlock instanceof ListItem;
+ return BlockContinue.atIndex(state.getNextNonSpaceIndex());
+ }
+ }
+
+ if (state.getIndent() >= contentIndent) {
+ return BlockContinue.atColumn(state.getColumn() + contentIndent);
+ } else {
+ // Note: We'll hit this case for lazy continuation lines, they will get added later.
+ return BlockContinue.none();
+ }
+ }
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/ParagraphParser.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/ParagraphParser.java
new file mode 100644
index 0000000000000..7cbc28e2cb228
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/ParagraphParser.java
@@ -0,0 +1,108 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.internal;
+
+import jdk.internal.org.commonmark.node.Block;
+import jdk.internal.org.commonmark.node.LinkReferenceDefinition;
+import jdk.internal.org.commonmark.node.Paragraph;
+import jdk.internal.org.commonmark.node.SourceSpan;
+import jdk.internal.org.commonmark.parser.InlineParser;
+import jdk.internal.org.commonmark.parser.SourceLine;
+import jdk.internal.org.commonmark.parser.SourceLines;
+import jdk.internal.org.commonmark.parser.block.AbstractBlockParser;
+import jdk.internal.org.commonmark.parser.block.BlockContinue;
+import jdk.internal.org.commonmark.parser.block.ParserState;
+
+import java.util.List;
+
+public class ParagraphParser extends AbstractBlockParser {
+
+ private final Paragraph block = new Paragraph();
+ private final LinkReferenceDefinitionParser linkReferenceDefinitionParser = new LinkReferenceDefinitionParser();
+
+ @Override
+ public boolean canHaveLazyContinuationLines() {
+ return true;
+ }
+
+ @Override
+ public Block getBlock() {
+ return block;
+ }
+
+ @Override
+ public BlockContinue tryContinue(ParserState state) {
+ if (!state.isBlank()) {
+ return BlockContinue.atIndex(state.getIndex());
+ } else {
+ return BlockContinue.none();
+ }
+ }
+
+ @Override
+ public void addLine(SourceLine line) {
+ linkReferenceDefinitionParser.parse(line);
+ }
+
+ @Override
+ public void addSourceSpan(SourceSpan sourceSpan) {
+ // Some source spans might belong to link reference definitions, others to the paragraph.
+ // The parser will handle that.
+ linkReferenceDefinitionParser.addSourceSpan(sourceSpan);
+ }
+
+ @Override
+ public void closeBlock() {
+ if (linkReferenceDefinitionParser.getParagraphLines().isEmpty()) {
+ block.unlink();
+ } else {
+ block.setSourceSpans(linkReferenceDefinitionParser.getParagraphSourceSpans());
+ }
+ }
+
+ @Override
+ public void parseInlines(InlineParser inlineParser) {
+ SourceLines lines = linkReferenceDefinitionParser.getParagraphLines();
+ if (!lines.isEmpty()) {
+ inlineParser.parse(lines, block);
+ }
+ }
+
+ public SourceLines getParagraphLines() {
+ return linkReferenceDefinitionParser.getParagraphLines();
+ }
+
+ public List getDefinitions() {
+ return linkReferenceDefinitionParser.getDefinitions();
+ }
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/StaggeredDelimiterProcessor.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/StaggeredDelimiterProcessor.java
new file mode 100644
index 0000000000000..995f9eac27026
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/StaggeredDelimiterProcessor.java
@@ -0,0 +1,108 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.internal;
+
+import jdk.internal.org.commonmark.parser.delimiter.DelimiterProcessor;
+import jdk.internal.org.commonmark.parser.delimiter.DelimiterRun;
+
+import java.util.LinkedList;
+import java.util.ListIterator;
+
+/**
+ * An implementation of DelimiterProcessor that dispatches all calls to two or more other DelimiterProcessors
+ * depending on the length of the delimiter run. All child DelimiterProcessors must have different minimum
+ * lengths. A given delimiter run is dispatched to the child with the largest acceptable minimum length. If no
+ * child is applicable, the one with the largest minimum length is chosen.
+ */
+class StaggeredDelimiterProcessor implements DelimiterProcessor {
+
+ private final char delim;
+ private int minLength = 0;
+ private LinkedList processors = new LinkedList<>(); // in reverse getMinLength order
+
+ StaggeredDelimiterProcessor(char delim) {
+ this.delim = delim;
+ }
+
+
+ @Override
+ public char getOpeningCharacter() {
+ return delim;
+ }
+
+ @Override
+ public char getClosingCharacter() {
+ return delim;
+ }
+
+ @Override
+ public int getMinLength() {
+ return minLength;
+ }
+
+ void add(DelimiterProcessor dp) {
+ final int len = dp.getMinLength();
+ ListIterator it = processors.listIterator();
+ boolean added = false;
+ while (it.hasNext()) {
+ DelimiterProcessor p = it.next();
+ int pLen = p.getMinLength();
+ if (len > pLen) {
+ it.previous();
+ it.add(dp);
+ added = true;
+ break;
+ } else if (len == pLen) {
+ throw new IllegalArgumentException("Cannot add two delimiter processors for char '" + delim + "' and minimum length " + len + "; conflicting processors: " + p + ", " + dp);
+ }
+ }
+ if (!added) {
+ processors.add(dp);
+ this.minLength = len;
+ }
+ }
+
+ private DelimiterProcessor findProcessor(int len) {
+ for (DelimiterProcessor p : processors) {
+ if (p.getMinLength() <= len) {
+ return p;
+ }
+ }
+ return processors.getFirst();
+ }
+
+ @Override
+ public int process(DelimiterRun openingRun, DelimiterRun closingRun) {
+ return findProcessor(openingRun.length()).process(openingRun, closingRun);
+ }
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/ThematicBreakParser.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/ThematicBreakParser.java
new file mode 100644
index 0000000000000..36d32244b8bba
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/ThematicBreakParser.java
@@ -0,0 +1,102 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.internal;
+
+import jdk.internal.org.commonmark.node.Block;
+import jdk.internal.org.commonmark.node.ThematicBreak;
+import jdk.internal.org.commonmark.parser.block.*;
+
+public class ThematicBreakParser extends AbstractBlockParser {
+
+ private final ThematicBreak block = new ThematicBreak();
+
+ @Override
+ public Block getBlock() {
+ return block;
+ }
+
+ @Override
+ public BlockContinue tryContinue(ParserState state) {
+ // a horizontal rule can never container > 1 line, so fail to match
+ return BlockContinue.none();
+ }
+
+ public static class Factory extends AbstractBlockParserFactory {
+
+ @Override
+ public BlockStart tryStart(ParserState state, MatchedBlockParser matchedBlockParser) {
+ if (state.getIndent() >= 4) {
+ return BlockStart.none();
+ }
+ int nextNonSpace = state.getNextNonSpaceIndex();
+ CharSequence line = state.getLine().getContent();
+ if (isThematicBreak(line, nextNonSpace)) {
+ return BlockStart.of(new ThematicBreakParser()).atIndex(line.length());
+ } else {
+ return BlockStart.none();
+ }
+ }
+ }
+
+ // spec: A line consisting of 0-3 spaces of indentation, followed by a sequence of three or more matching -, _, or *
+ // characters, each followed optionally by any number of spaces, forms a thematic break.
+ private static boolean isThematicBreak(CharSequence line, int index) {
+ int dashes = 0;
+ int underscores = 0;
+ int asterisks = 0;
+ int length = line.length();
+ for (int i = index; i < length; i++) {
+ switch (line.charAt(i)) {
+ case '-':
+ dashes++;
+ break;
+ case '_':
+ underscores++;
+ break;
+ case '*':
+ asterisks++;
+ break;
+ case ' ':
+ case '\t':
+ // Allowed, even between markers
+ break;
+ default:
+ return false;
+ }
+ }
+
+ return ((dashes >= 3 && underscores == 0 && asterisks == 0) ||
+ (underscores >= 3 && dashes == 0 && asterisks == 0) ||
+ (asterisks >= 3 && dashes == 0 && underscores == 0));
+ }
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/AsteriskDelimiterProcessor.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/AsteriskDelimiterProcessor.java
new file mode 100644
index 0000000000000..ff205e8bd2204
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/AsteriskDelimiterProcessor.java
@@ -0,0 +1,40 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.internal.inline;
+
+public class AsteriskDelimiterProcessor extends EmphasisDelimiterProcessor {
+
+ public AsteriskDelimiterProcessor() {
+ super('*');
+ }
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/AutolinkInlineParser.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/AutolinkInlineParser.java
new file mode 100644
index 0000000000000..919392b4614ce
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/AutolinkInlineParser.java
@@ -0,0 +1,79 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.internal.inline;
+
+import jdk.internal.org.commonmark.node.Link;
+import jdk.internal.org.commonmark.node.Text;
+import jdk.internal.org.commonmark.parser.SourceLines;
+
+import java.util.regex.Pattern;
+
+/**
+ * Attempt to parse an autolink (URL or email in pointy brackets).
+ */
+public class AutolinkInlineParser implements InlineContentParser {
+
+ private static final Pattern URI = Pattern
+ .compile("^[a-zA-Z][a-zA-Z0-9.+-]{1,31}:[^<>\u0000-\u0020]*$");
+
+ private static final Pattern EMAIL = Pattern
+ .compile("^([a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*)$");
+
+ @Override
+ public ParsedInline tryParse(InlineParserState inlineParserState) {
+ Scanner scanner = inlineParserState.scanner();
+ scanner.next();
+ Position textStart = scanner.position();
+ if (scanner.find('>') > 0) {
+ SourceLines textSource = scanner.getSource(textStart, scanner.position());
+ String content = textSource.getContent();
+ scanner.next();
+
+ String destination = null;
+ if (URI.matcher(content).matches()) {
+ destination = content;
+ } else if (EMAIL.matcher(content).matches()) {
+ destination = "mailto:" + content;
+ }
+
+ if (destination != null) {
+ Link link = new Link(destination, null);
+ Text text = new Text(content);
+ text.setSourceSpans(textSource.getSourceSpans());
+ link.appendChild(text);
+ return ParsedInline.of(link, scanner.position());
+ }
+ }
+ return ParsedInline.none();
+ }
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/BackslashInlineParser.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/BackslashInlineParser.java
new file mode 100644
index 0000000000000..296f5e7e8d610
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/BackslashInlineParser.java
@@ -0,0 +1,67 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.internal.inline;
+
+import jdk.internal.org.commonmark.internal.util.Escaping;
+import jdk.internal.org.commonmark.node.HardLineBreak;
+import jdk.internal.org.commonmark.node.Node;
+import jdk.internal.org.commonmark.node.Text;
+
+import java.util.regex.Pattern;
+
+/**
+ * Parse a backslash-escaped special character, adding either the escaped character, a hard line break
+ * (if the backslash is followed by a newline), or a literal backslash to the block's children.
+ */
+public class BackslashInlineParser implements InlineContentParser {
+
+ private static final Pattern ESCAPABLE = Pattern.compile('^' + Escaping.ESCAPABLE);
+
+ @Override
+ public ParsedInline tryParse(InlineParserState inlineParserState) {
+ Scanner scanner = inlineParserState.scanner();
+ // Backslash
+ scanner.next();
+
+ char next = scanner.peek();
+ if (next == '\n') {
+ scanner.next();
+ return ParsedInline.of(new HardLineBreak(), scanner.position());
+ } else if (ESCAPABLE.matcher(String.valueOf(next)).matches()) {
+ scanner.next();
+ return ParsedInline.of(new Text(String.valueOf(next)), scanner.position());
+ } else {
+ return ParsedInline.of(new Text("\\"), scanner.position());
+ }
+ }
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/BackticksInlineParser.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/BackticksInlineParser.java
new file mode 100644
index 0000000000000..b868046acab90
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/BackticksInlineParser.java
@@ -0,0 +1,80 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.internal.inline;
+
+import jdk.internal.org.commonmark.internal.util.Parsing;
+import jdk.internal.org.commonmark.node.Code;
+import jdk.internal.org.commonmark.node.Text;
+import jdk.internal.org.commonmark.parser.SourceLines;
+
+/**
+ * Attempt to parse backticks, returning either a backtick code span or a literal sequence of backticks.
+ */
+public class BackticksInlineParser implements InlineContentParser {
+
+ @Override
+ public ParsedInline tryParse(InlineParserState inlineParserState) {
+ Scanner scanner = inlineParserState.scanner();
+ Position start = scanner.position();
+ int openingTicks = scanner.matchMultiple('`');
+ Position afterOpening = scanner.position();
+
+ while (scanner.find('`') > 0) {
+ Position beforeClosing = scanner.position();
+ int count = scanner.matchMultiple('`');
+ if (count == openingTicks) {
+ Code node = new Code();
+
+ String content = scanner.getSource(afterOpening, beforeClosing).getContent();
+ content = content.replace('\n', ' ');
+
+ // spec: If the resulting string both begins and ends with a space character, but does not consist
+ // entirely of space characters, a single space character is removed from the front and back.
+ if (content.length() >= 3 &&
+ content.charAt(0) == ' ' &&
+ content.charAt(content.length() - 1) == ' ' &&
+ Parsing.hasNonSpace(content)) {
+ content = content.substring(1, content.length() - 1);
+ }
+
+ node.setLiteral(content);
+ return ParsedInline.of(node, scanner.position());
+ }
+ }
+
+ // If we got here, we didn't find a matching closing backtick sequence.
+ SourceLines source = scanner.getSource(start, afterOpening);
+ Text text = new Text(source.getContent());
+ return ParsedInline.of(text, afterOpening);
+ }
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/EmphasisDelimiterProcessor.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/EmphasisDelimiterProcessor.java
new file mode 100644
index 0000000000000..0a4c3c4514054
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/EmphasisDelimiterProcessor.java
@@ -0,0 +1,98 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.internal.inline;
+
+import jdk.internal.org.commonmark.node.*;
+import jdk.internal.org.commonmark.parser.delimiter.DelimiterProcessor;
+import jdk.internal.org.commonmark.parser.delimiter.DelimiterRun;
+
+public abstract class EmphasisDelimiterProcessor implements DelimiterProcessor {
+
+ private final char delimiterChar;
+
+ protected EmphasisDelimiterProcessor(char delimiterChar) {
+ this.delimiterChar = delimiterChar;
+ }
+
+ @Override
+ public char getOpeningCharacter() {
+ return delimiterChar;
+ }
+
+ @Override
+ public char getClosingCharacter() {
+ return delimiterChar;
+ }
+
+ @Override
+ public int getMinLength() {
+ return 1;
+ }
+
+ @Override
+ public int process(DelimiterRun openingRun, DelimiterRun closingRun) {
+ // "multiple of 3" rule for internal delimiter runs
+ if ((openingRun.canClose() || closingRun.canOpen()) &&
+ closingRun.originalLength() % 3 != 0 &&
+ (openingRun.originalLength() + closingRun.originalLength()) % 3 == 0) {
+ return 0;
+ }
+
+ int usedDelimiters;
+ Node emphasis;
+ // calculate actual number of delimiters used from this closer
+ if (openingRun.length() >= 2 && closingRun.length() >= 2) {
+ usedDelimiters = 2;
+ emphasis = new StrongEmphasis(String.valueOf(delimiterChar) + delimiterChar);
+ } else {
+ usedDelimiters = 1;
+ emphasis = new Emphasis(String.valueOf(delimiterChar));
+ }
+
+ SourceSpans sourceSpans = SourceSpans.empty();
+ sourceSpans.addAllFrom(openingRun.getOpeners(usedDelimiters));
+
+ Text opener = openingRun.getOpener();
+ for (Node node : Nodes.between(opener, closingRun.getCloser())) {
+ emphasis.appendChild(node);
+ sourceSpans.addAll(node.getSourceSpans());
+ }
+
+ sourceSpans.addAllFrom(closingRun.getClosers(usedDelimiters));
+
+ emphasis.setSourceSpans(sourceSpans.getSourceSpans());
+ opener.insertAfter(emphasis);
+
+ return usedDelimiters;
+ }
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/EntityInlineParser.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/EntityInlineParser.java
new file mode 100644
index 0000000000000..c6c0fb7e17e4e
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/EntityInlineParser.java
@@ -0,0 +1,85 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.internal.inline;
+
+import jdk.internal.org.commonmark.internal.util.AsciiMatcher;
+import jdk.internal.org.commonmark.internal.util.Html5Entities;
+import jdk.internal.org.commonmark.node.Text;
+
+/**
+ * Attempts to parse a HTML entity or numeric character reference.
+ */
+public class EntityInlineParser implements InlineContentParser {
+
+ private static final AsciiMatcher hex = AsciiMatcher.builder().range('0', '9').range('A', 'F').range('a', 'f').build();
+ private static final AsciiMatcher dec = AsciiMatcher.builder().range('0', '9').build();
+ private static final AsciiMatcher entityStart = AsciiMatcher.builder().range('A', 'Z').range('a', 'z').build();
+ private static final AsciiMatcher entityContinue = entityStart.newBuilder().range('0', '9').build();
+
+ @Override
+ public ParsedInline tryParse(InlineParserState inlineParserState) {
+ Scanner scanner = inlineParserState.scanner();
+ Position start = scanner.position();
+ // Skip `&`
+ scanner.next();
+
+ char c = scanner.peek();
+ if (c == '#') {
+ // Numeric
+ scanner.next();
+ if (scanner.next('x') || scanner.next('X')) {
+ int digits = scanner.match(hex);
+ if (1 <= digits && digits <= 6 && scanner.next(';')) {
+ return entity(scanner, start);
+ }
+ } else {
+ int digits = scanner.match(dec);
+ if (1 <= digits && digits <= 7 && scanner.next(';')) {
+ return entity(scanner, start);
+ }
+ }
+ } else if (entityStart.matches(c)) {
+ scanner.match(entityContinue);
+ if (scanner.next(';')) {
+ return entity(scanner, start);
+ }
+ }
+
+ return ParsedInline.none();
+ }
+
+ private ParsedInline entity(Scanner scanner, Position start) {
+ String text = scanner.getSource(start, scanner.position()).getContent();
+ return ParsedInline.of(new Text(Html5Entities.entityToString(text)), scanner.position());
+ }
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/HtmlInlineParser.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/HtmlInlineParser.java
new file mode 100644
index 0000000000000..5414f40eb75f6
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/HtmlInlineParser.java
@@ -0,0 +1,232 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.internal.inline;
+
+import jdk.internal.org.commonmark.internal.util.AsciiMatcher;
+import jdk.internal.org.commonmark.node.HtmlInline;
+
+/**
+ * Attempt to parse inline HTML.
+ */
+public class HtmlInlineParser implements InlineContentParser {
+
+ private static final AsciiMatcher asciiLetter = AsciiMatcher.builder().range('A', 'Z').range('a', 'z').build();
+
+ // spec: A tag name consists of an ASCII letter followed by zero or more ASCII letters, digits, or hyphens (-).
+ private static final AsciiMatcher tagNameStart = asciiLetter;
+ private static final AsciiMatcher tagNameContinue = tagNameStart.newBuilder().range('0', '9').c('-').build();
+
+ // spec: An attribute name consists of an ASCII letter, _, or :, followed by zero or more ASCII letters, digits,
+ // _, ., :, or -. (Note: This is the XML specification restricted to ASCII. HTML5 is laxer.)
+ private static final AsciiMatcher attributeStart = asciiLetter.newBuilder().c('_').c(':').build();
+ private static final AsciiMatcher attributeContinue = attributeStart.newBuilder().range('0', '9').c('.').c('-').build();
+ // spec: An unquoted attribute value is a nonempty string of characters not including whitespace, ", ', =, <, >, or `.
+ private static final AsciiMatcher attributeValueEnd = AsciiMatcher.builder()
+ .c(' ').c('\t').c('\n').c('\u000B').c('\f').c('\r')
+ .c('"').c('\'').c('=').c('<').c('>').c('`')
+ .build();
+
+ @Override
+ public ParsedInline tryParse(InlineParserState inlineParserState) {
+ Scanner scanner = inlineParserState.scanner();
+ Position start = scanner.position();
+ // Skip over `<`
+ scanner.next();
+
+ char c = scanner.peek();
+ if (tagNameStart.matches(c)) {
+ if (tryOpenTag(scanner)) {
+ return htmlInline(start, scanner);
+ }
+ } else if (c == '/') {
+ if (tryClosingTag(scanner)) {
+ return htmlInline(start, scanner);
+ }
+ } else if (c == '?') {
+ if (tryProcessingInstruction(scanner)) {
+ return htmlInline(start, scanner);
+ }
+ } else if (c == '!') {
+ // comment, declaration or CDATA
+ scanner.next();
+ c = scanner.peek();
+ if (c == '-') {
+ if (tryComment(scanner)) {
+ return htmlInline(start, scanner);
+ }
+ } else if (c == '[') {
+ if (tryCdata(scanner)) {
+ return htmlInline(start, scanner);
+ }
+ } else if (asciiLetter.matches(c)) {
+ if (tryDeclaration(scanner)) {
+ return htmlInline(start, scanner);
+ }
+ }
+ }
+
+ return ParsedInline.none();
+ }
+
+ private static ParsedInline htmlInline(Position start, Scanner scanner) {
+ String text = scanner.getSource(start, scanner.position()).getContent();
+ HtmlInline node = new HtmlInline();
+ node.setLiteral(text);
+ return ParsedInline.of(node, scanner.position());
+ }
+
+ private static boolean tryOpenTag(Scanner scanner) {
+ // spec: An open tag consists of a < character, a tag name, zero or more attributes, optional whitespace,
+ // an optional / character, and a > character.
+ scanner.next();
+ scanner.match(tagNameContinue);
+ boolean whitespace = scanner.whitespace() >= 1;
+ // spec: An attribute consists of whitespace, an attribute name, and an optional attribute value specification.
+ while (whitespace && scanner.match(attributeStart) >= 1) {
+ scanner.match(attributeContinue);
+ // spec: An attribute value specification consists of optional whitespace, a = character,
+ // optional whitespace, and an attribute value.
+ whitespace = scanner.whitespace() >= 1;
+ if (scanner.next('=')) {
+ scanner.whitespace();
+ char valueStart = scanner.peek();
+ if (valueStart == '\'') {
+ scanner.next();
+ if (scanner.find('\'') < 0) {
+ return false;
+ }
+ scanner.next();
+ } else if (valueStart == '"') {
+ scanner.next();
+ if (scanner.find('"') < 0) {
+ return false;
+ }
+ scanner.next();
+ } else {
+ if (scanner.find(attributeValueEnd) <= 0) {
+ return false;
+ }
+ }
+
+ // Whitespace is required between attributes
+ whitespace = scanner.whitespace() >= 1;
+ }
+ }
+
+ scanner.next('/');
+ return scanner.next('>');
+ }
+
+ private static boolean tryClosingTag(Scanner scanner) {
+ // spec: A closing tag consists of the string , a tag name, optional whitespace, and the character >.
+ scanner.next();
+ if (scanner.match(tagNameStart) >= 1) {
+ scanner.match(tagNameContinue);
+ scanner.whitespace();
+ return scanner.next('>');
+ }
+ return false;
+ }
+
+ private static boolean tryProcessingInstruction(Scanner scanner) {
+ // spec: A processing instruction consists of the string , a string of characters not including the string ?>,
+ // and the string ?>.
+ scanner.next();
+ while (scanner.find('?') > 0) {
+ scanner.next();
+ if (scanner.next('>')) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static boolean tryComment(Scanner scanner) {
+ // spec: An HTML comment consists of , where text does not start with > or ->, does not end
+ // with -, and does not contain --. (See the HTML5 spec.)
+
+ // Skip first `-`
+ scanner.next();
+ if (!scanner.next('-')) {
+ return false;
+ }
+
+ if (scanner.next('>') || scanner.next("->")) {
+ return false;
+ }
+
+ while (scanner.find('-') >= 0) {
+ if (scanner.next("--")) {
+ return scanner.next('>');
+ } else {
+ scanner.next();
+ }
+ }
+
+ return false;
+ }
+
+ private static boolean tryCdata(Scanner scanner) {
+ // spec: A CDATA section consists of the string ,
+ // and the string ]]>.
+
+ // Skip `[`
+ scanner.next();
+
+ if (scanner.next("CDATA[")) {
+ while (scanner.find(']') >= 0) {
+ if (scanner.next("]]>")) {
+ return true;
+ } else {
+ scanner.next();
+ }
+ }
+ }
+
+ return false;
+ }
+
+ private static boolean tryDeclaration(Scanner scanner) {
+ // spec: A declaration consists of the string , and the character >.
+ scanner.match(asciiLetter);
+ if (scanner.whitespace() <= 0) {
+ return false;
+ }
+ if (scanner.find('>') >= 0) {
+ scanner.next();
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/InlineContentParser.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/InlineContentParser.java
new file mode 100644
index 0000000000000..66feff21784db
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/InlineContentParser.java
@@ -0,0 +1,38 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.internal.inline;
+
+public interface InlineContentParser {
+
+ ParsedInline tryParse(InlineParserState inlineParserState);
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/InlineParserState.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/InlineParserState.java
new file mode 100644
index 0000000000000..3e970b0c26a47
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/InlineParserState.java
@@ -0,0 +1,45 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.internal.inline;
+
+public interface InlineParserState {
+
+ /**
+ * Return a scanner for the input for the current position (on the character that the inline parser registered
+ * interest for).
+ *
+ * Note that this always returns the same instance, if you want to backtrack you need to use
+ * {@link Scanner#position()} and {@link Scanner#setPosition(Position)}.
+ */
+ Scanner scanner();
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/ParsedInline.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/ParsedInline.java
new file mode 100644
index 0000000000000..e23b020d674f7
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/ParsedInline.java
@@ -0,0 +1,55 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.internal.inline;
+
+import jdk.internal.org.commonmark.node.Node;
+
+public abstract class ParsedInline {
+
+ protected ParsedInline() {
+ }
+
+ public static ParsedInline none() {
+ return null;
+ }
+
+ public static ParsedInline of(Node node, Position position) {
+ if (node == null) {
+ throw new NullPointerException("node must not be null");
+ }
+ if (position == null) {
+ throw new NullPointerException("position must not be null");
+ }
+ return new ParsedInlineImpl(node, position);
+ }
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/ParsedInlineImpl.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/ParsedInlineImpl.java
new file mode 100644
index 0000000000000..236df3ad52a13
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/ParsedInlineImpl.java
@@ -0,0 +1,53 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.internal.inline;
+
+import jdk.internal.org.commonmark.node.Node;
+
+public class ParsedInlineImpl extends ParsedInline {
+ private final Node node;
+ private final Position position;
+
+ ParsedInlineImpl(Node node, Position position) {
+ this.node = node;
+ this.position = position;
+ }
+
+ public Node getNode() {
+ return node;
+ }
+
+ public Position getPosition() {
+ return position;
+ }
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/Position.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/Position.java
new file mode 100644
index 0000000000000..fa3767083e37e
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/Position.java
@@ -0,0 +1,48 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.internal.inline;
+
+/**
+ * Position within a {@link Scanner}. This is intentionally kept opaque so as not to expose the internal structure of
+ * the Scanner.
+ */
+public class Position {
+
+ final int lineIndex;
+ final int index;
+
+ Position(int lineIndex, int index) {
+ this.lineIndex = lineIndex;
+ this.index = index;
+ }
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/Scanner.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/Scanner.java
new file mode 100644
index 0000000000000..a32d0564f7d49
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/Scanner.java
@@ -0,0 +1,313 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.internal.inline;
+
+import jdk.internal.org.commonmark.internal.util.CharMatcher;
+import jdk.internal.org.commonmark.node.SourceSpan;
+import jdk.internal.org.commonmark.parser.SourceLine;
+import jdk.internal.org.commonmark.parser.SourceLines;
+
+import java.util.List;
+
+public class Scanner {
+
+ /**
+ * Character representing the end of input source (or outside of the text in case of the "previous" methods).
+ *
+ * Can be used to only process certain nodes. If you override a method and want visiting to descend into children,
+ * call {@link #visitChildren}.
+ */
+public abstract class AbstractVisitor implements Visitor {
+
+ @Override
+ public void visit(BlockQuote blockQuote) {
+ visitChildren(blockQuote);
+ }
+
+ @Override
+ public void visit(BulletList bulletList) {
+ visitChildren(bulletList);
+ }
+
+ @Override
+ public void visit(Code code) {
+ visitChildren(code);
+ }
+
+ @Override
+ public void visit(Document document) {
+ visitChildren(document);
+ }
+
+ @Override
+ public void visit(Emphasis emphasis) {
+ visitChildren(emphasis);
+ }
+
+ @Override
+ public void visit(FencedCodeBlock fencedCodeBlock) {
+ visitChildren(fencedCodeBlock);
+ }
+
+ @Override
+ public void visit(HardLineBreak hardLineBreak) {
+ visitChildren(hardLineBreak);
+ }
+
+ @Override
+ public void visit(Heading heading) {
+ visitChildren(heading);
+ }
+
+ @Override
+ public void visit(ThematicBreak thematicBreak) {
+ visitChildren(thematicBreak);
+ }
+
+ @Override
+ public void visit(HtmlInline htmlInline) {
+ visitChildren(htmlInline);
+ }
+
+ @Override
+ public void visit(HtmlBlock htmlBlock) {
+ visitChildren(htmlBlock);
+ }
+
+ @Override
+ public void visit(Image image) {
+ visitChildren(image);
+ }
+
+ @Override
+ public void visit(IndentedCodeBlock indentedCodeBlock) {
+ visitChildren(indentedCodeBlock);
+ }
+
+ @Override
+ public void visit(Link link) {
+ visitChildren(link);
+ }
+
+ @Override
+ public void visit(ListItem listItem) {
+ visitChildren(listItem);
+ }
+
+ @Override
+ public void visit(OrderedList orderedList) {
+ visitChildren(orderedList);
+ }
+
+ @Override
+ public void visit(Paragraph paragraph) {
+ visitChildren(paragraph);
+ }
+
+ @Override
+ public void visit(SoftLineBreak softLineBreak) {
+ visitChildren(softLineBreak);
+ }
+
+ @Override
+ public void visit(StrongEmphasis strongEmphasis) {
+ visitChildren(strongEmphasis);
+ }
+
+ @Override
+ public void visit(Text text) {
+ visitChildren(text);
+ }
+
+ @Override
+ public void visit(LinkReferenceDefinition linkReferenceDefinition) {
+ visitChildren(linkReferenceDefinition);
+ }
+
+ @Override
+ public void visit(CustomBlock customBlock) {
+ visitChildren(customBlock);
+ }
+
+ @Override
+ public void visit(CustomNode customNode) {
+ visitChildren(customNode);
+ }
+
+ /**
+ * Visit the child nodes.
+ *
+ * @param parent the parent node whose children should be visited
+ */
+ protected void visitChildren(Node parent) {
+ Node node = parent.getFirstChild();
+ while (node != null) {
+ // A subclass of this visitor might modify the node, resulting in getNext returning a different node or no
+ // node after visiting it. So get the next node before visiting.
+ Node next = node.getNext();
+ node.accept(this);
+ node = next;
+ }
+ }
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Block.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Block.java
new file mode 100644
index 0000000000000..57603fa5fddb5
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Block.java
@@ -0,0 +1,51 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.node;
+
+/**
+ * Block nodes such as paragraphs, list blocks, code blocks etc.
+ */
+public abstract class Block extends Node {
+
+ public Block getParent() {
+ return (Block) super.getParent();
+ }
+
+ @Override
+ protected void setParent(Node parent) {
+ if (!(parent instanceof Block)) {
+ throw new IllegalArgumentException("Parent of block must also be block (can not be inline)");
+ }
+ super.setParent(parent);
+ }
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/BlockQuote.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/BlockQuote.java
new file mode 100644
index 0000000000000..8a5f1d20c0781
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/BlockQuote.java
@@ -0,0 +1,41 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.node;
+
+public class BlockQuote extends Block {
+
+ @Override
+ public void accept(Visitor visitor) {
+ visitor.visit(this);
+ }
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/BulletList.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/BulletList.java
new file mode 100644
index 0000000000000..7512d56673a14
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/BulletList.java
@@ -0,0 +1,52 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.node;
+
+public class BulletList extends ListBlock {
+
+ private char bulletMarker;
+
+ @Override
+ public void accept(Visitor visitor) {
+ visitor.visit(this);
+ }
+
+ public char getBulletMarker() {
+ return bulletMarker;
+ }
+
+ public void setBulletMarker(char bulletMarker) {
+ this.bulletMarker = bulletMarker;
+ }
+
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Code.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Code.java
new file mode 100644
index 0000000000000..b64ddefa1f1e5
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Code.java
@@ -0,0 +1,58 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.node;
+
+public class Code extends Node {
+
+ private String literal;
+
+ public Code() {
+ }
+
+ public Code(String literal) {
+ this.literal = literal;
+ }
+
+ @Override
+ public void accept(Visitor visitor) {
+ visitor.visit(this);
+ }
+
+ public String getLiteral() {
+ return literal;
+ }
+
+ public void setLiteral(String literal) {
+ this.literal = literal;
+ }
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/CustomBlock.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/CustomBlock.java
new file mode 100644
index 0000000000000..08038848adab8
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/CustomBlock.java
@@ -0,0 +1,41 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.node;
+
+public abstract class CustomBlock extends Block {
+
+ @Override
+ public void accept(Visitor visitor) {
+ visitor.visit(this);
+ }
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/CustomNode.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/CustomNode.java
new file mode 100644
index 0000000000000..733df320afcf8
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/CustomNode.java
@@ -0,0 +1,40 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.node;
+
+public abstract class CustomNode extends Node {
+ @Override
+ public void accept(Visitor visitor) {
+ visitor.visit(this);
+ }
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Delimited.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Delimited.java
new file mode 100644
index 0000000000000..b887aa3f75a26
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Delimited.java
@@ -0,0 +1,49 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.node;
+
+/**
+ * A node that uses delimiters in the source form (e.g. *bold*).
+ */
+public interface Delimited {
+
+ /**
+ * @return the opening (beginning) delimiter, e.g. *
+ */
+ String getOpeningDelimiter();
+
+ /**
+ * @return the closing (ending) delimiter, e.g. *
+ */
+ String getClosingDelimiter();
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Document.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Document.java
new file mode 100644
index 0000000000000..390728318eca5
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Document.java
@@ -0,0 +1,41 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.node;
+
+public class Document extends Block {
+
+ @Override
+ public void accept(Visitor visitor) {
+ visitor.visit(this);
+ }
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Emphasis.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Emphasis.java
new file mode 100644
index 0000000000000..86b09f4f2f6dd
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Emphasis.java
@@ -0,0 +1,64 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.node;
+
+public class Emphasis extends Node implements Delimited {
+
+ private String delimiter;
+
+ public Emphasis() {
+ }
+
+ public Emphasis(String delimiter) {
+ this.delimiter = delimiter;
+ }
+
+ public void setDelimiter(String delimiter) {
+ this.delimiter = delimiter;
+ }
+
+ @Override
+ public String getOpeningDelimiter() {
+ return delimiter;
+ }
+
+ @Override
+ public String getClosingDelimiter() {
+ return delimiter;
+ }
+
+ @Override
+ public void accept(Visitor visitor) {
+ visitor.visit(this);
+ }
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/FencedCodeBlock.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/FencedCodeBlock.java
new file mode 100644
index 0000000000000..43fd68aa06f07
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/FencedCodeBlock.java
@@ -0,0 +1,91 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.node;
+
+public class FencedCodeBlock extends Block {
+
+ private char fenceChar;
+ private int fenceLength;
+ private int fenceIndent;
+
+ private String info;
+ private String literal;
+
+ @Override
+ public void accept(Visitor visitor) {
+ visitor.visit(this);
+ }
+
+ public char getFenceChar() {
+ return fenceChar;
+ }
+
+ public void setFenceChar(char fenceChar) {
+ this.fenceChar = fenceChar;
+ }
+
+ public int getFenceLength() {
+ return fenceLength;
+ }
+
+ public void setFenceLength(int fenceLength) {
+ this.fenceLength = fenceLength;
+ }
+
+ public int getFenceIndent() {
+ return fenceIndent;
+ }
+
+ public void setFenceIndent(int fenceIndent) {
+ this.fenceIndent = fenceIndent;
+ }
+
+ /**
+ * @see CommonMark spec
+ */
+ public String getInfo() {
+ return info;
+ }
+
+ public void setInfo(String info) {
+ this.info = info;
+ }
+
+ public String getLiteral() {
+ return literal;
+ }
+
+ public void setLiteral(String literal) {
+ this.literal = literal;
+ }
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/HardLineBreak.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/HardLineBreak.java
new file mode 100644
index 0000000000000..5c85504a8fbc7
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/HardLineBreak.java
@@ -0,0 +1,41 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.node;
+
+public class HardLineBreak extends Node {
+
+ @Override
+ public void accept(Visitor visitor) {
+ visitor.visit(this);
+ }
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Heading.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Heading.java
new file mode 100644
index 0000000000000..49a8225fca1fa
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Heading.java
@@ -0,0 +1,51 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.node;
+
+public class Heading extends Block {
+
+ private int level;
+
+ @Override
+ public void accept(Visitor visitor) {
+ visitor.visit(this);
+ }
+
+ public int getLevel() {
+ return level;
+ }
+
+ public void setLevel(int level) {
+ this.level = level;
+ }
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/HtmlBlock.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/HtmlBlock.java
new file mode 100644
index 0000000000000..94380c084e12b
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/HtmlBlock.java
@@ -0,0 +1,56 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.node;
+
+/**
+ * HTML block
+ *
+ * @see CommonMark Spec
+ */
+public class HtmlBlock extends Block {
+
+ private String literal;
+
+ @Override
+ public void accept(Visitor visitor) {
+ visitor.visit(this);
+ }
+
+ public String getLiteral() {
+ return literal;
+ }
+
+ public void setLiteral(String literal) {
+ this.literal = literal;
+ }
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/HtmlInline.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/HtmlInline.java
new file mode 100644
index 0000000000000..7402606ac83ec
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/HtmlInline.java
@@ -0,0 +1,56 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.node;
+
+/**
+ * Inline HTML element.
+ *
+ * @see CommonMark Spec
+ */
+public class HtmlInline extends Node {
+
+ private String literal;
+
+ @Override
+ public void accept(Visitor visitor) {
+ visitor.visit(this);
+ }
+
+ public String getLiteral() {
+ return literal;
+ }
+
+ public void setLiteral(String literal) {
+ this.literal = literal;
+ }
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Image.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Image.java
new file mode 100644
index 0000000000000..36a3f2f72212f
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Image.java
@@ -0,0 +1,73 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.node;
+
+public class Image extends Node {
+
+ private String destination;
+ private String title;
+
+ public Image() {
+ }
+
+ public Image(String destination, String title) {
+ this.destination = destination;
+ this.title = title;
+ }
+
+ @Override
+ public void accept(Visitor visitor) {
+ visitor.visit(this);
+ }
+
+ public String getDestination() {
+ return destination;
+ }
+
+ public void setDestination(String destination) {
+ this.destination = destination;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ @Override
+ protected String toStringAttributes() {
+ return "destination=" + destination + ", title=" + title;
+ }
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/IndentedCodeBlock.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/IndentedCodeBlock.java
new file mode 100644
index 0000000000000..d3b4cec41d8a4
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/IndentedCodeBlock.java
@@ -0,0 +1,51 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.node;
+
+public class IndentedCodeBlock extends Block {
+
+ private String literal;
+
+ @Override
+ public void accept(Visitor visitor) {
+ visitor.visit(this);
+ }
+
+ public String getLiteral() {
+ return literal;
+ }
+
+ public void setLiteral(String literal) {
+ this.literal = literal;
+ }
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Link.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Link.java
new file mode 100644
index 0000000000000..448c0b063db19
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Link.java
@@ -0,0 +1,93 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.node;
+
+/**
+ * A link with a destination and an optional title; the link text is in child nodes.
+ *
+ * Example for an inline link in a CommonMark document:
+ *
+ * [link](/uri "title")
+ *
+ *
+ * The corresponding Link node would look like this:
+ *
A {@link Text} child node with {@link Text#getLiteral() getLiteral} that returns {@code "link"}
+ *
+ *
+ * Note that the text in the link can contain inline formatting, so it could also contain an {@link Image} or
+ * {@link Emphasis}, etc.
+ *
+ * @see CommonMark Spec for links
+ */
+public class Link extends Node {
+
+ private String destination;
+ private String title;
+
+ public Link() {
+ }
+
+ public Link(String destination, String title) {
+ this.destination = destination;
+ this.title = title;
+ }
+
+ @Override
+ public void accept(Visitor visitor) {
+ visitor.visit(this);
+ }
+
+ public String getDestination() {
+ return destination;
+ }
+
+ public void setDestination(String destination) {
+ this.destination = destination;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ @Override
+ protected String toStringAttributes() {
+ return "destination=" + destination + ", title=" + title;
+ }
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/LinkReferenceDefinition.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/LinkReferenceDefinition.java
new file mode 100644
index 0000000000000..9f3e7107e789a
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/LinkReferenceDefinition.java
@@ -0,0 +1,89 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.node;
+
+/**
+ * A link reference definition, e.g.:
+ *
+ * [foo]: /url "title"
+ *
+ *
+ * They can be referenced anywhere else in the document to produce a link using [foo]. The definitions
+ * themselves are usually not rendered in the final output.
+ *
+ * @see Link reference definitions
+ */
+public class LinkReferenceDefinition extends Node {
+
+ private String label;
+ private String destination;
+ private String title;
+
+ public LinkReferenceDefinition() {
+ }
+
+ public LinkReferenceDefinition(String label, String destination, String title) {
+ this.label = label;
+ this.destination = destination;
+ this.title = title;
+ }
+
+ public String getLabel() {
+ return label;
+ }
+
+ public void setLabel(String label) {
+ this.label = label;
+ }
+
+ public String getDestination() {
+ return destination;
+ }
+
+ public void setDestination(String destination) {
+ this.destination = destination;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ @Override
+ public void accept(Visitor visitor) {
+ visitor.visit(this);
+ }
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/ListBlock.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/ListBlock.java
new file mode 100644
index 0000000000000..67cccde093a3e
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/ListBlock.java
@@ -0,0 +1,51 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.node;
+
+public abstract class ListBlock extends Block {
+
+ private boolean tight;
+
+ /**
+ * @return whether this list is tight or loose
+ * @see CommonMark Spec for tight lists
+ */
+ public boolean isTight() {
+ return tight;
+ }
+
+ public void setTight(boolean tight) {
+ this.tight = tight;
+ }
+
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/ListItem.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/ListItem.java
new file mode 100644
index 0000000000000..aeb99e7828c1f
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/ListItem.java
@@ -0,0 +1,41 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.node;
+
+public class ListItem extends Block {
+
+ @Override
+ public void accept(Visitor visitor) {
+ visitor.visit(this);
+ }
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Node.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Node.java
new file mode 100644
index 0000000000000..1607ec2ef5f3d
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Node.java
@@ -0,0 +1,193 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.node;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * The base class of all CommonMark AST nodes ({@link Block} and inlines).
+ *
+ * A node can have multiple children, and a parent (except for the root node).
+ */
+public abstract class Node {
+
+ private Node parent = null;
+ private Node firstChild = null;
+ private Node lastChild = null;
+ private Node prev = null;
+ private Node next = null;
+ private List sourceSpans = null;
+
+ public abstract void accept(Visitor visitor);
+
+ public Node getNext() {
+ return next;
+ }
+
+ public Node getPrevious() {
+ return prev;
+ }
+
+ public Node getFirstChild() {
+ return firstChild;
+ }
+
+ public Node getLastChild() {
+ return lastChild;
+ }
+
+ public Node getParent() {
+ return parent;
+ }
+
+ protected void setParent(Node parent) {
+ this.parent = parent;
+ }
+
+ public void appendChild(Node child) {
+ child.unlink();
+ child.setParent(this);
+ if (this.lastChild != null) {
+ this.lastChild.next = child;
+ child.prev = this.lastChild;
+ this.lastChild = child;
+ } else {
+ this.firstChild = child;
+ this.lastChild = child;
+ }
+ }
+
+ public void prependChild(Node child) {
+ child.unlink();
+ child.setParent(this);
+ if (this.firstChild != null) {
+ this.firstChild.prev = child;
+ child.next = this.firstChild;
+ this.firstChild = child;
+ } else {
+ this.firstChild = child;
+ this.lastChild = child;
+ }
+ }
+
+ public void unlink() {
+ if (this.prev != null) {
+ this.prev.next = this.next;
+ } else if (this.parent != null) {
+ this.parent.firstChild = this.next;
+ }
+ if (this.next != null) {
+ this.next.prev = this.prev;
+ } else if (this.parent != null) {
+ this.parent.lastChild = this.prev;
+ }
+ this.parent = null;
+ this.next = null;
+ this.prev = null;
+ }
+
+ public void insertAfter(Node sibling) {
+ sibling.unlink();
+ sibling.next = this.next;
+ if (sibling.next != null) {
+ sibling.next.prev = sibling;
+ }
+ sibling.prev = this;
+ this.next = sibling;
+ sibling.parent = this.parent;
+ if (sibling.next == null) {
+ sibling.parent.lastChild = sibling;
+ }
+ }
+
+ public void insertBefore(Node sibling) {
+ sibling.unlink();
+ sibling.prev = this.prev;
+ if (sibling.prev != null) {
+ sibling.prev.next = sibling;
+ }
+ sibling.next = this;
+ this.prev = sibling;
+ sibling.parent = this.parent;
+ if (sibling.prev == null) {
+ sibling.parent.firstChild = sibling;
+ }
+ }
+
+
+ /**
+ * @return the source spans of this node if included by the parser, an empty list otherwise
+ * @since 0.16.0
+ */
+ public List getSourceSpans() {
+ return sourceSpans != null ? Collections.unmodifiableList(sourceSpans) : Collections.emptyList();
+ }
+
+ /**
+ * Replace the current source spans with the provided list.
+ *
+ * @param sourceSpans the new source spans to set
+ * @since 0.16.0
+ */
+ public void setSourceSpans(List sourceSpans) {
+ if (sourceSpans.isEmpty()) {
+ this.sourceSpans = null;
+ } else {
+ this.sourceSpans = new ArrayList<>(sourceSpans);
+ }
+ }
+
+ /**
+ * Add a source span to the end of the list.
+ *
+ * @param sourceSpan the source span to add
+ * @since 0.16.0
+ */
+ public void addSourceSpan(SourceSpan sourceSpan) {
+ if (sourceSpans == null) {
+ this.sourceSpans = new ArrayList<>();
+ }
+ this.sourceSpans.add(sourceSpan);
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + "{" + toStringAttributes() + "}";
+ }
+
+ protected String toStringAttributes() {
+ return "";
+ }
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Nodes.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Nodes.java
new file mode 100644
index 0000000000000..5be07004284ec
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Nodes.java
@@ -0,0 +1,98 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.node;
+
+import java.util.Iterator;
+
+/**
+ * Utility class for working with multiple {@link Node}s.
+ *
+ * @since 0.16.0
+ */
+public class Nodes {
+
+ private Nodes() {
+ }
+
+ /**
+ * The nodes between (not including) start and end.
+ */
+ public static Iterable between(Node start, Node end) {
+ return new NodeIterable(start.getNext(), end);
+ }
+
+ private static class NodeIterable implements Iterable {
+
+ private final Node first;
+ private final Node end;
+
+ private NodeIterable(Node first, Node end) {
+ this.first = first;
+ this.end = end;
+ }
+
+ @Override
+ public Iterator iterator() {
+ return new NodeIterator(first, end);
+ }
+ }
+
+ private static class NodeIterator implements Iterator {
+
+ private Node node;
+ private final Node end;
+
+ private NodeIterator(Node first, Node end) {
+ node = first;
+ this.end = end;
+ }
+
+ @Override
+ public boolean hasNext() {
+ return node != null && node != end;
+ }
+
+ @Override
+ public Node next() {
+ Node result = node;
+ node = node.getNext();
+ return result;
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException("remove");
+ }
+ }
+}
+
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/OrderedList.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/OrderedList.java
new file mode 100644
index 0000000000000..5fa309dd1265c
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/OrderedList.java
@@ -0,0 +1,61 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.node;
+
+public class OrderedList extends ListBlock {
+
+ private int startNumber;
+ private char delimiter;
+
+ @Override
+ public void accept(Visitor visitor) {
+ visitor.visit(this);
+ }
+
+ public int getStartNumber() {
+ return startNumber;
+ }
+
+ public void setStartNumber(int startNumber) {
+ this.startNumber = startNumber;
+ }
+
+ public char getDelimiter() {
+ return delimiter;
+ }
+
+ public void setDelimiter(char delimiter) {
+ this.delimiter = delimiter;
+ }
+
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Paragraph.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Paragraph.java
new file mode 100644
index 0000000000000..32168c0251e11
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Paragraph.java
@@ -0,0 +1,44 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.node;
+
+/**
+ * A paragraph block, contains inline nodes such as {@link Text}
+ */
+public class Paragraph extends Block {
+
+ @Override
+ public void accept(Visitor visitor) {
+ visitor.visit(this);
+ }
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/SoftLineBreak.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/SoftLineBreak.java
new file mode 100644
index 0000000000000..1fedc5c899b22
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/SoftLineBreak.java
@@ -0,0 +1,41 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.node;
+
+public class SoftLineBreak extends Node {
+
+ @Override
+ public void accept(Visitor visitor) {
+ visitor.visit(this);
+ }
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/SourceSpan.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/SourceSpan.java
new file mode 100644
index 0000000000000..b816a8bf437f0
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/SourceSpan.java
@@ -0,0 +1,122 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.node;
+
+import java.util.Objects;
+
+/**
+ * A source span references a snippet of text from the source input.
+ *
+ * It has a starting position (line and column index) and a length of how many characters it spans.
+ *
+ * For example, this CommonMark source text:
+ *
+ * > foo
+ *
+ * The {@link BlockQuote} node would have this source span: line 0, column 0, length 5.
+ *
+ * The {@link Paragraph} node inside it would have: line 0, column 2, length 3.
+ *
+ * If a block has multiple lines, it will have a source span for each line.
+ *
+ * Note that the column index and length are measured in Java characters (UTF-16 code units). If you're outputting them
+ * to be consumed by another programming language, e.g. one that uses UTF-8 strings, you will need to translate them,
+ * otherwise characters such as emojis will result in incorrect positions.
+ *
+ * @since 0.16.0
+ */
+public class SourceSpan {
+
+ private final int lineIndex;
+ private final int columnIndex;
+ private final int length;
+
+ public static SourceSpan of(int lineIndex, int columnIndex, int length) {
+ return new SourceSpan(lineIndex, columnIndex, length);
+ }
+
+ private SourceSpan(int lineIndex, int columnIndex, int length) {
+ this.lineIndex = lineIndex;
+ this.columnIndex = columnIndex;
+ this.length = length;
+ }
+
+ /**
+ * @return 0-based index of line in source
+ */
+ public int getLineIndex() {
+ return lineIndex;
+ }
+
+ /**
+ * @return 0-based index of column (character on line) in source
+ */
+ public int getColumnIndex() {
+ return columnIndex;
+ }
+
+ /**
+ * @return length of the span in characters
+ */
+ public int getLength() {
+ return length;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ SourceSpan that = (SourceSpan) o;
+ return lineIndex == that.lineIndex &&
+ columnIndex == that.columnIndex &&
+ length == that.length;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(lineIndex, columnIndex, length);
+ }
+
+ @Override
+ public String toString() {
+ return "SourceSpan{" +
+ "line=" + lineIndex +
+ ", column=" + columnIndex +
+ ", length=" + length +
+ "}";
+ }
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/SourceSpans.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/SourceSpans.java
new file mode 100644
index 0000000000000..4a35a89840d19
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/SourceSpans.java
@@ -0,0 +1,85 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.node;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A list of source spans that can be added to. Takes care of merging adjacent source spans.
+ *
+ * @since 0.16.0
+ */
+public class SourceSpans {
+
+ private List sourceSpans;
+
+ public static SourceSpans empty() {
+ return new SourceSpans();
+ }
+
+ public List getSourceSpans() {
+ return sourceSpans != null ? sourceSpans : Collections.emptyList();
+ }
+
+ public void addAllFrom(Iterable extends Node> nodes) {
+ for (Node node : nodes) {
+ addAll(node.getSourceSpans());
+ }
+ }
+
+ public void addAll(List other) {
+ if (other.isEmpty()) {
+ return;
+ }
+
+ if (sourceSpans == null) {
+ sourceSpans = new ArrayList<>();
+ }
+
+ if (sourceSpans.isEmpty()) {
+ sourceSpans.addAll(other);
+ } else {
+ int lastIndex = sourceSpans.size() - 1;
+ SourceSpan a = sourceSpans.get(lastIndex);
+ SourceSpan b = other.get(0);
+ if (a.getLineIndex() == b.getLineIndex() && a.getColumnIndex() + a.getLength() == b.getColumnIndex()) {
+ sourceSpans.set(lastIndex, SourceSpan.of(a.getLineIndex(), a.getColumnIndex(), a.getLength() + b.getLength()));
+ sourceSpans.addAll(other.subList(1, other.size()));
+ } else {
+ sourceSpans.addAll(other);
+ }
+ }
+ }
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/StrongEmphasis.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/StrongEmphasis.java
new file mode 100644
index 0000000000000..1efed6d99294b
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/StrongEmphasis.java
@@ -0,0 +1,64 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.node;
+
+public class StrongEmphasis extends Node implements Delimited {
+
+ private String delimiter;
+
+ public StrongEmphasis() {
+ }
+
+ public StrongEmphasis(String delimiter) {
+ this.delimiter = delimiter;
+ }
+
+ public void setDelimiter(String delimiter) {
+ this.delimiter = delimiter;
+ }
+
+ @Override
+ public String getOpeningDelimiter() {
+ return delimiter;
+ }
+
+ @Override
+ public String getClosingDelimiter() {
+ return delimiter;
+ }
+
+ @Override
+ public void accept(Visitor visitor) {
+ visitor.visit(this);
+ }
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Text.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Text.java
new file mode 100644
index 0000000000000..976134d1dd015
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Text.java
@@ -0,0 +1,63 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.node;
+
+public class Text extends Node {
+
+ private String literal;
+
+ public Text() {
+ }
+
+ public Text(String literal) {
+ this.literal = literal;
+ }
+
+ @Override
+ public void accept(Visitor visitor) {
+ visitor.visit(this);
+ }
+
+ public String getLiteral() {
+ return literal;
+ }
+
+ public void setLiteral(String literal) {
+ this.literal = literal;
+ }
+
+ @Override
+ protected String toStringAttributes() {
+ return "literal=" + literal;
+ }
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/ThematicBreak.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/ThematicBreak.java
new file mode 100644
index 0000000000000..e337dc9e71176
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/ThematicBreak.java
@@ -0,0 +1,41 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.node;
+
+public class ThematicBreak extends Block {
+
+ @Override
+ public void accept(Visitor visitor) {
+ visitor.visit(this);
+ }
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Visitor.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Visitor.java
new file mode 100644
index 0000000000000..460a6876c28d7
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Visitor.java
@@ -0,0 +1,87 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.node;
+
+/**
+ * Node visitor.
+ *
+ * Implementations should subclass {@link AbstractVisitor} instead of implementing this directly.
+ */
+public interface Visitor {
+
+ void visit(BlockQuote blockQuote);
+
+ void visit(BulletList bulletList);
+
+ void visit(Code code);
+
+ void visit(Document document);
+
+ void visit(Emphasis emphasis);
+
+ void visit(FencedCodeBlock fencedCodeBlock);
+
+ void visit(HardLineBreak hardLineBreak);
+
+ void visit(Heading heading);
+
+ void visit(ThematicBreak thematicBreak);
+
+ void visit(HtmlInline htmlInline);
+
+ void visit(HtmlBlock htmlBlock);
+
+ void visit(Image image);
+
+ void visit(IndentedCodeBlock indentedCodeBlock);
+
+ void visit(Link link);
+
+ void visit(ListItem listItem);
+
+ void visit(OrderedList orderedList);
+
+ void visit(Paragraph paragraph);
+
+ void visit(SoftLineBreak softLineBreak);
+
+ void visit(StrongEmphasis strongEmphasis);
+
+ void visit(Text text);
+
+ void visit(LinkReferenceDefinition linkReferenceDefinition);
+
+ void visit(CustomBlock customBlock);
+
+ void visit(CustomNode customNode);
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/package-info.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/package-info.java
new file mode 100644
index 0000000000000..e957ac5b7725f
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/package-info.java
@@ -0,0 +1,36 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+/**
+ * AST node types (see {@link org.commonmark.node.Node}) and visitors (see {@link org.commonmark.node.AbstractVisitor})
+ */
+package jdk.internal.org.commonmark.node;
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/package-info.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/package-info.java
new file mode 100644
index 0000000000000..99e6b39ca43ec
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/package-info.java
@@ -0,0 +1,41 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+/**
+ * Root package of commonmark-java
+ *
+ *
{@link org.commonmark.parser} for parsing input text to AST nodes
+ *
{@link org.commonmark.node} for AST node types and visitors
+ *
{@link org.commonmark.renderer.html} for HTML rendering
+ *
+ */
+package jdk.internal.org.commonmark;
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/IncludeSourceSpans.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/IncludeSourceSpans.java
new file mode 100644
index 0000000000000..27d91f96e7644
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/IncludeSourceSpans.java
@@ -0,0 +1,54 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.parser;
+
+/**
+ * Whether to include {@link org.commonmark.node.SourceSpan} or not while parsing,
+ * see {@link Parser.Builder#includeSourceSpans(IncludeSourceSpans)}.
+ *
+ * @since 0.16.0
+ */
+public enum IncludeSourceSpans {
+ /**
+ * Do not include source spans.
+ */
+ NONE,
+ /**
+ * Include source spans on {@link org.commonmark.node.Block} nodes.
+ */
+ BLOCKS,
+ /**
+ * Include source spans on block nodes and inline nodes.
+ */
+ BLOCKS_AND_INLINES,
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/InlineParser.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/InlineParser.java
new file mode 100644
index 0000000000000..e0e4246eb095a
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/InlineParser.java
@@ -0,0 +1,47 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.parser;
+
+import jdk.internal.org.commonmark.node.Node;
+
+/**
+ * Parser for inline content (text, links, emphasized text, etc).
+ */
+public interface InlineParser {
+
+ /**
+ * @param lines the source content to parse as inline
+ * @param node the node to append resulting nodes to (as children)
+ */
+ void parse(SourceLines lines, Node node);
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/InlineParserContext.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/InlineParserContext.java
new file mode 100644
index 0000000000000..b28faff22be5d
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/InlineParserContext.java
@@ -0,0 +1,59 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.parser;
+
+import jdk.internal.org.commonmark.node.LinkReferenceDefinition;
+import jdk.internal.org.commonmark.parser.delimiter.DelimiterProcessor;
+
+import java.util.List;
+
+/**
+ * Context for inline parsing.
+ */
+public interface InlineParserContext {
+
+ /**
+ * @return custom delimiter processors that have been configured with {@link Parser.Builder#customDelimiterProcessor(DelimiterProcessor)}
+ */
+ List getCustomDelimiterProcessors();
+
+ /**
+ * Look up a {@link LinkReferenceDefinition} for a given label.
+ *
+ * Note that the label is not normalized yet; implementations are responsible for normalizing before lookup.
+ *
+ * @param label the link label to look up
+ * @return the definition if one exists, {@code null} otherwise
+ */
+ LinkReferenceDefinition getLinkReferenceDefinition(String label);
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/InlineParserFactory.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/InlineParserFactory.java
new file mode 100644
index 0000000000000..6fb0860f48e1a
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/InlineParserFactory.java
@@ -0,0 +1,40 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.parser;
+
+/**
+ * Factory for custom inline parser.
+ */
+public interface InlineParserFactory {
+ InlineParser create(InlineParserContext inlineParserContext);
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/Parser.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/Parser.java
new file mode 100644
index 0000000000000..40712b9787379
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/Parser.java
@@ -0,0 +1,316 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.parser;
+
+import jdk.internal.org.commonmark.Extension;
+import jdk.internal.org.commonmark.internal.DocumentParser;
+import jdk.internal.org.commonmark.internal.InlineParserContextImpl;
+import jdk.internal.org.commonmark.internal.InlineParserImpl;
+import jdk.internal.org.commonmark.internal.LinkReferenceDefinitions;
+import jdk.internal.org.commonmark.node.*;
+import jdk.internal.org.commonmark.parser.block.BlockParserFactory;
+import jdk.internal.org.commonmark.parser.delimiter.DelimiterProcessor;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+
+/**
+ * Parses input text to a tree of nodes.
+ *
+ * Start with the {@link #builder} method, configure the parser and build it. Example:
+ *
+ */
+public class Parser {
+
+ private final List blockParserFactories;
+ private final List delimiterProcessors;
+ private final InlineParserFactory inlineParserFactory;
+ private final List postProcessors;
+ private final IncludeSourceSpans includeSourceSpans;
+
+ private Parser(Builder builder) {
+ this.blockParserFactories = DocumentParser.calculateBlockParserFactories(builder.blockParserFactories, builder.enabledBlockTypes);
+ this.inlineParserFactory = builder.getInlineParserFactory();
+ this.postProcessors = builder.postProcessors;
+ this.delimiterProcessors = builder.delimiterProcessors;
+ this.includeSourceSpans = builder.includeSourceSpans;
+
+ // Try to construct an inline parser. Invalid configuration might result in an exception, which we want to
+ // detect as soon as possible.
+ this.inlineParserFactory.create(new InlineParserContextImpl(delimiterProcessors, new LinkReferenceDefinitions()));
+ }
+
+ /**
+ * Create a new builder for configuring a {@link Parser}.
+ *
+ * @return a builder
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Parse the specified input text into a tree of nodes.
+ *
+ * This method is thread-safe (a new parser state is used for each invocation).
+ *
+ * @param input the text to parse - must not be null
+ * @return the root node
+ */
+ public Node parse(String input) {
+ if (input == null) {
+ throw new NullPointerException("input must not be null");
+ }
+ DocumentParser documentParser = createDocumentParser();
+ Node document = documentParser.parse(input);
+ return postProcess(document);
+ }
+
+ /**
+ * Parse the specified reader into a tree of nodes. The caller is responsible for closing the reader.
+ *
+ * Note that if you have a file with a byte order mark (BOM), you need to skip it before handing the reader to this
+ * library. There's existing classes that do that, e.g. see {@code BOMInputStream} in Commons IO.
+ *
+ * This method is thread-safe (a new parser state is used for each invocation).
+ *
+ * @param input the reader to parse - must not be null
+ * @return the root node
+ * @throws IOException when reading throws an exception
+ */
+ public Node parseReader(Reader input) throws IOException {
+ if (input == null) {
+ throw new NullPointerException("input must not be null");
+ }
+
+ DocumentParser documentParser = createDocumentParser();
+ Node document = documentParser.parse(input);
+ return postProcess(document);
+ }
+
+ private DocumentParser createDocumentParser() {
+ return new DocumentParser(blockParserFactories, inlineParserFactory, delimiterProcessors, includeSourceSpans);
+ }
+
+ private Node postProcess(Node document) {
+ for (PostProcessor postProcessor : postProcessors) {
+ document = postProcessor.process(document);
+ }
+ return document;
+ }
+
+ /**
+ * Builder for configuring a {@link Parser}.
+ */
+ public static class Builder {
+ private final List blockParserFactories = new ArrayList<>();
+ private final List delimiterProcessors = new ArrayList<>();
+ private final List postProcessors = new ArrayList<>();
+ private Set> enabledBlockTypes = DocumentParser.getDefaultBlockParserTypes();
+ private InlineParserFactory inlineParserFactory;
+ private IncludeSourceSpans includeSourceSpans = IncludeSourceSpans.NONE;
+
+ /**
+ * @return the configured {@link Parser}
+ */
+ public Parser build() {
+ return new Parser(this);
+ }
+
+ /**
+ * @param extensions extensions to use on this parser
+ * @return {@code this}
+ */
+ public Builder extensions(Iterable extends Extension> extensions) {
+ if (extensions == null) {
+ throw new NullPointerException("extensions must not be null");
+ }
+ for (Extension extension : extensions) {
+ if (extension instanceof ParserExtension) {
+ ParserExtension parserExtension = (ParserExtension) extension;
+ parserExtension.extend(this);
+ }
+ }
+ return this;
+ }
+
+ /**
+ * Describe the list of markdown features the parser will recognize and parse.
+ *
+ * By default, CommonMark will recognize and parse the following set of "block" elements:
+ *
+ *
+ * @param enabledBlockTypes A list of block nodes the parser will parse.
+ * If this list is empty, the parser will not recognize any CommonMark core features.
+ * @return {@code this}
+ */
+ public Builder enabledBlockTypes(Set> enabledBlockTypes) {
+ if (enabledBlockTypes == null) {
+ throw new NullPointerException("enabledBlockTypes must not be null");
+ }
+ DocumentParser.checkEnabledBlockTypes(enabledBlockTypes);
+ this.enabledBlockTypes = enabledBlockTypes;
+ return this;
+ }
+
+ /**
+ * Whether to calculate {@link org.commonmark.node.SourceSpan} for {@link Node}.
+ *
+ * By default, source spans are disabled.
+ *
+ * @param includeSourceSpans which kind of source spans should be included
+ * @return {@code this}
+ * @since 0.16.0
+ */
+ public Builder includeSourceSpans(IncludeSourceSpans includeSourceSpans) {
+ this.includeSourceSpans = includeSourceSpans;
+ return this;
+ }
+
+ /**
+ * Adds a custom block parser factory.
+ *
+ * Note that custom factories are applied before the built-in factories. This is so that
+ * extensions can change how some syntax is parsed that would otherwise be handled by built-in factories.
+ * "With great power comes great responsibility."
+ *
+ * @param blockParserFactory a block parser factory implementation
+ * @return {@code this}
+ */
+ public Builder customBlockParserFactory(BlockParserFactory blockParserFactory) {
+ if (blockParserFactory == null) {
+ throw new NullPointerException("blockParserFactory must not be null");
+ }
+ blockParserFactories.add(blockParserFactory);
+ return this;
+ }
+
+ /**
+ * Adds a custom delimiter processor.
+ *
+ * Note that multiple delimiter processors with the same characters can be added, as long as they have a
+ * different minimum length. In that case, the processor with the shortest matching length is used. Adding more
+ * than one delimiter processor with the same character and minimum length is invalid.
+ *
+ * @param delimiterProcessor a delimiter processor implementation
+ * @return {@code this}
+ */
+ public Builder customDelimiterProcessor(DelimiterProcessor delimiterProcessor) {
+ if (delimiterProcessor == null) {
+ throw new NullPointerException("delimiterProcessor must not be null");
+ }
+ delimiterProcessors.add(delimiterProcessor);
+ return this;
+ }
+
+ public Builder postProcessor(PostProcessor postProcessor) {
+ if (postProcessor == null) {
+ throw new NullPointerException("postProcessor must not be null");
+ }
+ postProcessors.add(postProcessor);
+ return this;
+ }
+
+ /**
+ * Overrides the parser used for inline markdown processing.
+ *
+ * Provide an implementation of InlineParserFactory which provides a custom inline parser
+ * to modify how the following are parsed:
+ * bold (**)
+ * italic (*)
+ * strikethrough (~~)
+ * backtick quote (`)
+ * link ([title](http://))
+ * image ()
+ *
+ * Note that if this method is not called or the inline parser factory is set to null, then the default
+ * implementation will be used.
+ *
+ * @param inlineParserFactory an inline parser factory implementation
+ * @return {@code this}
+ */
+ public Builder inlineParserFactory(InlineParserFactory inlineParserFactory) {
+ this.inlineParserFactory = inlineParserFactory;
+ return this;
+ }
+
+ private InlineParserFactory getInlineParserFactory() {
+ if (inlineParserFactory != null) {
+ return inlineParserFactory;
+ }
+ return new InlineParserFactory() {
+ @Override
+ public InlineParser create(InlineParserContext inlineParserContext) {
+ return new InlineParserImpl(inlineParserContext);
+ }
+ };
+ }
+ }
+
+ /**
+ * Extension for {@link Parser}.
+ */
+ public interface ParserExtension extends Extension {
+ void extend(Builder parserBuilder);
+ }
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/PostProcessor.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/PostProcessor.java
new file mode 100644
index 0000000000000..5cf4f480ae0ca
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/PostProcessor.java
@@ -0,0 +1,45 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.parser;
+
+import jdk.internal.org.commonmark.node.Node;
+
+public interface PostProcessor {
+
+ /**
+ * @param node the node to post-process
+ * @return the result of post-processing, may be a modified {@code node} argument
+ */
+ Node process(Node node);
+
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/SourceLine.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/SourceLine.java
new file mode 100644
index 0000000000000..4008e6bece098
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/SourceLine.java
@@ -0,0 +1,79 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.parser;
+
+import jdk.internal.org.commonmark.node.SourceSpan;
+
+/**
+ * A line or part of a line from the input source.
+ *
+ * @since 0.16.0
+ */
+public class SourceLine {
+
+ private final CharSequence content;
+ private final SourceSpan sourceSpan;
+
+ public static SourceLine of(CharSequence content, SourceSpan sourceSpan) {
+ return new SourceLine(content, sourceSpan);
+ }
+
+ private SourceLine(CharSequence content, SourceSpan sourceSpan) {
+ if (content == null) {
+ throw new NullPointerException("content must not be null");
+ }
+ this.content = content;
+ this.sourceSpan = sourceSpan;
+ }
+
+ public CharSequence getContent() {
+ return content;
+ }
+
+ public SourceSpan getSourceSpan() {
+ return sourceSpan;
+ }
+
+ public SourceLine substring(int beginIndex, int endIndex) {
+ CharSequence newContent = content.subSequence(beginIndex, endIndex);
+ SourceSpan newSourceSpan = null;
+ if (sourceSpan != null) {
+ int columnIndex = sourceSpan.getColumnIndex() + beginIndex;
+ int length = endIndex - beginIndex;
+ if (length != 0) {
+ newSourceSpan = SourceSpan.of(sourceSpan.getLineIndex(), columnIndex, length);
+ }
+ }
+ return SourceLine.of(newContent, newSourceSpan);
+ }
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/SourceLines.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/SourceLines.java
new file mode 100644
index 0000000000000..59bed5c5bf614
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/SourceLines.java
@@ -0,0 +1,98 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.parser;
+
+import jdk.internal.org.commonmark.node.SourceSpan;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A set of lines ({@link SourceLine}) from the input source.
+ *
+ * @since 0.16.0
+ */
+public class SourceLines {
+
+ private final List lines = new ArrayList<>();
+
+ public static SourceLines empty() {
+ return new SourceLines();
+ }
+
+ public static SourceLines of(SourceLine sourceLine) {
+ SourceLines sourceLines = new SourceLines();
+ sourceLines.addLine(sourceLine);
+ return sourceLines;
+ }
+
+ public static SourceLines of(List sourceLines) {
+ SourceLines result = new SourceLines();
+ result.lines.addAll(sourceLines);
+ return result;
+ }
+
+ public void addLine(SourceLine sourceLine) {
+ lines.add(sourceLine);
+ }
+
+ public List getLines() {
+ return lines;
+ }
+
+ public boolean isEmpty() {
+ return lines.isEmpty();
+ }
+
+ public String getContent() {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < lines.size(); i++) {
+ if (i != 0) {
+ sb.append('\n');
+ }
+ sb.append(lines.get(i).getContent());
+ }
+ return sb.toString();
+ }
+
+ public List getSourceSpans() {
+ List sourceSpans = new ArrayList<>();
+ for (SourceLine line : lines) {
+ SourceSpan sourceSpan = line.getSourceSpan();
+ if (sourceSpan != null) {
+ sourceSpans.add(sourceSpan);
+ }
+ }
+ return sourceSpans;
+ }
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/block/AbstractBlockParser.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/block/AbstractBlockParser.java
new file mode 100644
index 0000000000000..26cd6220daf1c
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/block/AbstractBlockParser.java
@@ -0,0 +1,74 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.parser.block;
+
+import jdk.internal.org.commonmark.node.Block;
+import jdk.internal.org.commonmark.node.SourceSpan;
+import jdk.internal.org.commonmark.parser.InlineParser;
+import jdk.internal.org.commonmark.parser.SourceLine;
+
+public abstract class AbstractBlockParser implements BlockParser {
+
+ @Override
+ public boolean isContainer() {
+ return false;
+ }
+
+ @Override
+ public boolean canHaveLazyContinuationLines() {
+ return false;
+ }
+
+ @Override
+ public boolean canContain(Block childBlock) {
+ return false;
+ }
+
+ @Override
+ public void addLine(SourceLine line) {
+ }
+
+ @Override
+ public void addSourceSpan(SourceSpan sourceSpan) {
+ getBlock().addSourceSpan(sourceSpan);
+ }
+
+ @Override
+ public void closeBlock() {
+ }
+
+ @Override
+ public void parseInlines(InlineParser inlineParser) {
+ }
+
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/block/AbstractBlockParserFactory.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/block/AbstractBlockParserFactory.java
new file mode 100644
index 0000000000000..40c2bf91b6666
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/block/AbstractBlockParserFactory.java
@@ -0,0 +1,36 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.parser.block;
+
+public abstract class AbstractBlockParserFactory implements BlockParserFactory {
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/block/BlockContinue.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/block/BlockContinue.java
new file mode 100644
index 0000000000000..5fc4e028d2200
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/block/BlockContinue.java
@@ -0,0 +1,61 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.parser.block;
+
+import jdk.internal.org.commonmark.internal.BlockContinueImpl;
+
+/**
+ * Result object for continuing parsing of a block, see static methods for constructors.
+ */
+public class BlockContinue {
+
+ protected BlockContinue() {
+ }
+
+ public static BlockContinue none() {
+ return null;
+ }
+
+ public static BlockContinue atIndex(int newIndex) {
+ return new BlockContinueImpl(newIndex, -1, false);
+ }
+
+ public static BlockContinue atColumn(int newColumn) {
+ return new BlockContinueImpl(-1, newColumn, false);
+ }
+
+ public static BlockContinue finished() {
+ return new BlockContinueImpl(-1, -1, true);
+ }
+
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/block/BlockParser.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/block/BlockParser.java
new file mode 100644
index 0000000000000..76888922abcb3
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/block/BlockParser.java
@@ -0,0 +1,84 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.parser.block;
+
+import jdk.internal.org.commonmark.node.Block;
+import jdk.internal.org.commonmark.node.SourceSpan;
+import jdk.internal.org.commonmark.parser.InlineParser;
+import jdk.internal.org.commonmark.parser.SourceLine;
+
+/**
+ * Parser for a specific block node.
+ *
+ * Implementations should subclass {@link AbstractBlockParser} instead of implementing this directly.
+ */
+public interface BlockParser {
+
+ /**
+ * Return true if the block that is parsed is a container (contains other blocks), or false if it's a leaf.
+ */
+ boolean isContainer();
+
+ /**
+ * Return true if the block can have lazy continuation lines.
+ *
+ * Lazy continuation lines are lines that were rejected by this {@link #tryContinue(ParserState)} but didn't match
+ * any other block parsers either.
+ *
+ * If true is returned here, those lines will get added via {@link #addLine(SourceLine)}. For false, the block is
+ * closed instead.
+ */
+ boolean canHaveLazyContinuationLines();
+
+ boolean canContain(Block childBlock);
+
+ Block getBlock();
+
+ BlockContinue tryContinue(ParserState parserState);
+
+ void addLine(SourceLine line);
+
+ /**
+ * Add a source span of the currently parsed block. The default implementation in {@link AbstractBlockParser} adds
+ * it to the block. Unless you have some complicated parsing where you need to check source positions, you don't
+ * need to override this.
+ *
+ * @since 0.16.0
+ */
+ void addSourceSpan(SourceSpan sourceSpan);
+
+ void closeBlock();
+
+ void parseInlines(InlineParser inlineParser);
+
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/block/BlockParserFactory.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/block/BlockParserFactory.java
new file mode 100644
index 0000000000000..53cc8d35ca705
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/block/BlockParserFactory.java
@@ -0,0 +1,44 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.parser.block;
+
+/**
+ * Parser factory for a block node for determining when a block starts.
+ *
+ * Implementations should subclass {@link AbstractBlockParserFactory} instead of implementing this directly.
+ */
+public interface BlockParserFactory {
+
+ BlockStart tryStart(ParserState state, MatchedBlockParser matchedBlockParser);
+
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/block/BlockStart.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/block/BlockStart.java
new file mode 100644
index 0000000000000..a927abe968b28
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/block/BlockStart.java
@@ -0,0 +1,59 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.parser.block;
+
+import jdk.internal.org.commonmark.internal.BlockStartImpl;
+
+/**
+ * Result object for starting parsing of a block, see static methods for constructors.
+ */
+public abstract class BlockStart {
+
+ protected BlockStart() {
+ }
+
+ public static BlockStart none() {
+ return null;
+ }
+
+ public static BlockStart of(BlockParser... blockParsers) {
+ return new BlockStartImpl(blockParsers);
+ }
+
+ public abstract BlockStart atIndex(int newIndex);
+
+ public abstract BlockStart atColumn(int newColumn);
+
+ public abstract BlockStart replaceActiveBlockParser();
+
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/block/MatchedBlockParser.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/block/MatchedBlockParser.java
new file mode 100644
index 0000000000000..76a9669d069b5
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/block/MatchedBlockParser.java
@@ -0,0 +1,53 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.parser.block;
+
+import jdk.internal.org.commonmark.parser.SourceLines;
+
+/**
+ * Open block parser that was last matched during the continue phase. This is different from the currently active
+ * block parser, as an unmatched block is only closed when a new block is started.
+ *
This interface is not intended to be implemented by clients.
+ */
+public interface MatchedBlockParser {
+
+ BlockParser getMatchedBlockParser();
+
+ /**
+ * Returns the current paragraph lines if the matched block is a paragraph.
+ *
+ * @return paragraph content or an empty list
+ */
+ SourceLines getParagraphLines();
+
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/block/ParserState.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/block/ParserState.java
new file mode 100644
index 0000000000000..7bda0620a8a7d
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/block/ParserState.java
@@ -0,0 +1,82 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.parser.block;
+
+import jdk.internal.org.commonmark.parser.SourceLine;
+
+/**
+ * State of the parser that is used in block parsers.
+ *
This interface is not intended to be implemented by clients.
+ */
+public interface ParserState {
+
+ /**
+ * @return the current source line being parsed (full line)
+ */
+ SourceLine getLine();
+
+ /**
+ * @return the current index within the line (0-based)
+ */
+ int getIndex();
+
+ /**
+ * @return the index of the next non-space character starting from {@link #getIndex()} (may be the same) (0-based)
+ */
+ int getNextNonSpaceIndex();
+
+ /**
+ * The column is the position within the line after tab characters have been processed as 4-space tab stops.
+ * If the line doesn't contain any tabs, it's the same as the {@link #getIndex()}. If the line starts with a tab,
+ * followed by text, then the column for the first character of the text is 4 (the index is 1).
+ *
+ * @return the current column within the line (0-based)
+ */
+ int getColumn();
+
+ /**
+ * @return the indentation in columns (either by spaces or tab stop of 4), starting from {@link #getColumn()}
+ */
+ int getIndent();
+
+ /**
+ * @return true if the current line is blank starting from the index
+ */
+ boolean isBlank();
+
+ /**
+ * @return the deepest open block parser
+ */
+ BlockParser getActiveBlockParser();
+
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/block/package-info.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/block/package-info.java
new file mode 100644
index 0000000000000..7bcb61e8a8691
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/block/package-info.java
@@ -0,0 +1,36 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+/**
+ * Types for extending block parsing
+ */
+package jdk.internal.org.commonmark.parser.block;
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/delimiter/DelimiterProcessor.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/delimiter/DelimiterProcessor.java
new file mode 100644
index 0000000000000..592f6a5e4335f
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/delimiter/DelimiterProcessor.java
@@ -0,0 +1,76 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.parser.delimiter;
+
+import jdk.internal.org.commonmark.node.Text;
+
+/**
+ * Custom delimiter processor for additional delimiters besides {@code _} and {@code *}.
+ *
+ * Note that implementations of this need to be thread-safe, the same instance may be used by multiple parsers.
+ */
+public interface DelimiterProcessor {
+
+ /**
+ * @return the character that marks the beginning of a delimited node, must not clash with any built-in special
+ * characters
+ */
+ char getOpeningCharacter();
+
+ /**
+ * @return the character that marks the the ending of a delimited node, must not clash with any built-in special
+ * characters. Note that for a symmetric delimiter such as "*", this is the same as the opening.
+ */
+ char getClosingCharacter();
+
+ /**
+ * Minimum number of delimiter characters that are needed to activate this. Must be at least 1.
+ */
+ int getMinLength();
+
+ /**
+ * Process the delimiter runs.
+ *
+ * The processor can examine the runs and the nodes and decide if it wants to process or not. If not, it should not
+ * change any nodes and return 0. If yes, it should do the processing (wrapping nodes, etc) and then return how many
+ * delimiters were used.
+ *
+ * Note that removal (unlinking) of the used delimiter {@link Text} nodes is done by the caller.
+ *
+ * @param openingRun the opening delimiter run
+ * @param closingRun the closing delimiter run
+ * @return how many delimiters were used; must not be greater than length of either opener or closer
+ */
+ int process(DelimiterRun openingRun, DelimiterRun closingRun);
+
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/delimiter/DelimiterRun.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/delimiter/DelimiterRun.java
new file mode 100644
index 0000000000000..9672c1906cfe0
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/delimiter/DelimiterRun.java
@@ -0,0 +1,90 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.parser.delimiter;
+
+import jdk.internal.org.commonmark.node.Text;
+
+/**
+ * A delimiter run is one or more of the same delimiter character, e.g. {@code ***}.
+ */
+public interface DelimiterRun {
+
+ /**
+ * @return whether this can open a delimiter
+ */
+ boolean canOpen();
+
+ /**
+ * @return whether this can close a delimiter
+ */
+ boolean canClose();
+
+ /**
+ * @return the number of characters in this delimiter run (that are left for processing)
+ */
+ int length();
+
+ /**
+ * @return the number of characters originally in this delimiter run; at the start of processing, this is the same
+ * as {{@link #length()}}
+ */
+ int originalLength();
+
+ /**
+ * @return the innermost opening delimiter, e.g. for {@code ***} this is the last {@code *}
+ */
+ Text getOpener();
+
+ /**
+ * @return the innermost closing delimiter, e.g. for {@code ***} this is the first {@code *}
+ */
+ Text getCloser();
+
+ /**
+ * Get the opening delimiter nodes for the specified length of delimiters. Length must be between 1 and
+ * {@link #length()}.
+ *
+ * For example, for a delimiter run {@code ***}, calling this with 1 would return the last {@code *}.
+ * Calling it with 2 would return the second last {@code *} and the last {@code *}.
+ */
+ Iterable getOpeners(int length);
+
+ /**
+ * Get the closing delimiter nodes for the specified length of delimiters. Length must be between 1 and
+ * {@link #length()}.
+ *
+ * For example, for a delimiter run {@code ***}, calling this with 1 would return the first {@code *}.
+ * Calling it with 2 would return the first {@code *} and the second {@code *}.
+ */
+ Iterable getClosers(int length);
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/package-info.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/package-info.java
new file mode 100644
index 0000000000000..b7bc957cc06fb
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/package-info.java
@@ -0,0 +1,36 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+/**
+ * Parsing input text to AST nodes (see {@link org.commonmark.parser.Parser})
+ */
+package jdk.internal.org.commonmark.parser;
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/NodeRenderer.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/NodeRenderer.java
new file mode 100644
index 0000000000000..00d6c185920f3
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/NodeRenderer.java
@@ -0,0 +1,55 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.renderer;
+
+import jdk.internal.org.commonmark.node.Node;
+
+import java.util.Set;
+
+/**
+ * A renderer for a set of node types.
+ */
+public interface NodeRenderer {
+
+ /**
+ * @return the types of nodes that this renderer handles
+ */
+ Set> getNodeTypes();
+
+ /**
+ * Render the specified node.
+ *
+ * @param node the node to render, will be an instance of one of {@link #getNodeTypes()}
+ */
+ void render(Node node);
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/Renderer.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/Renderer.java
new file mode 100644
index 0000000000000..9cee90af7d9de
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/Renderer.java
@@ -0,0 +1,54 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.renderer;
+
+import jdk.internal.org.commonmark.node.Node;
+
+public interface Renderer {
+
+ /**
+ * Render the tree of nodes to output.
+ *
+ * @param node the root node
+ * @param output output for rendering
+ */
+ void render(Node node, Appendable output);
+
+ /**
+ * Render the tree of nodes to string.
+ *
+ * @param node the root node
+ * @return the rendered string
+ */
+ String render(Node node);
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/html/AttributeProvider.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/html/AttributeProvider.java
new file mode 100644
index 0000000000000..4c9b33adb80a9
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/html/AttributeProvider.java
@@ -0,0 +1,61 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.renderer.html;
+
+import jdk.internal.org.commonmark.node.Node;
+
+import java.util.Map;
+
+/**
+ * Extension point for adding/changing attributes on HTML tags for a node.
+ */
+public interface AttributeProvider {
+
+ /**
+ * Set the attributes for a HTML tag of the specified node by modifying the provided map.
+ *
+ * This allows to change or even remove default attributes. With great power comes great responsibility.
+ *
+ * The attribute key and values will be escaped (preserving character entities), so don't escape them here,
+ * otherwise they will be double-escaped.
+ *
+ * This method may be called multiple times for the same node, if the node is rendered using multiple nested
+ * tags (e.g. code blocks).
+ *
+ * @param node the node to set attributes for
+ * @param tagName the HTML tag name that these attributes are for (e.g. {@code h1}, {@code pre}, {@code code}).
+ * @param attributes the attributes, with any default attributes already set in the map
+ */
+ void setAttributes(Node node, String tagName, Map attributes);
+
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/html/AttributeProviderContext.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/html/AttributeProviderContext.java
new file mode 100644
index 0000000000000..92ca0383b748c
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/html/AttributeProviderContext.java
@@ -0,0 +1,41 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.renderer.html;
+
+/**
+ * The context for attribute providers.
+ *
Note: There are currently no methods here, this is for future extensibility.
+ *
This interface is not intended to be implemented by clients.
+ */
+public interface AttributeProviderContext {
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/html/AttributeProviderFactory.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/html/AttributeProviderFactory.java
new file mode 100644
index 0000000000000..e4503daffaba2
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/html/AttributeProviderFactory.java
@@ -0,0 +1,47 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.renderer.html;
+
+/**
+ * Factory for instantiating new attribute providers when rendering is done.
+ */
+public interface AttributeProviderFactory {
+
+ /**
+ * Create a new attribute provider.
+ *
+ * @param context for this attribute provider
+ * @return an AttributeProvider
+ */
+ AttributeProvider create(AttributeProviderContext context);
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/html/CoreHtmlNodeRenderer.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/html/CoreHtmlNodeRenderer.java
new file mode 100644
index 0000000000000..05b34a485fd35
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/html/CoreHtmlNodeRenderer.java
@@ -0,0 +1,352 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.renderer.html;
+
+import jdk.internal.org.commonmark.node.*;
+import jdk.internal.org.commonmark.renderer.NodeRenderer;
+
+import java.util.*;
+
+/**
+ * The node renderer that renders all the core nodes (comes last in the order of node renderers).
+ */
+public class CoreHtmlNodeRenderer extends AbstractVisitor implements NodeRenderer {
+
+ protected final HtmlNodeRendererContext context;
+ private final HtmlWriter html;
+
+ public CoreHtmlNodeRenderer(HtmlNodeRendererContext context) {
+ this.context = context;
+ this.html = context.getWriter();
+ }
+
+ @Override
+ public Set> getNodeTypes() {
+ return new HashSet<>(Arrays.asList(
+ Document.class,
+ Heading.class,
+ Paragraph.class,
+ BlockQuote.class,
+ BulletList.class,
+ FencedCodeBlock.class,
+ HtmlBlock.class,
+ ThematicBreak.class,
+ IndentedCodeBlock.class,
+ Link.class,
+ ListItem.class,
+ OrderedList.class,
+ Image.class,
+ Emphasis.class,
+ StrongEmphasis.class,
+ Text.class,
+ Code.class,
+ HtmlInline.class,
+ SoftLineBreak.class,
+ HardLineBreak.class
+ ));
+ }
+
+ @Override
+ public void render(Node node) {
+ node.accept(this);
+ }
+
+ @Override
+ public void visit(Document document) {
+ // No rendering itself
+ visitChildren(document);
+ }
+
+ @Override
+ public void visit(Heading heading) {
+ String htag = "h" + heading.getLevel();
+ html.line();
+ html.tag(htag, getAttrs(heading, htag));
+ visitChildren(heading);
+ html.tag('/' + htag);
+ html.line();
+ }
+
+ @Override
+ public void visit(Paragraph paragraph) {
+ boolean inTightList = isInTightList(paragraph);
+ if (!inTightList) {
+ html.line();
+ html.tag("p", getAttrs(paragraph, "p"));
+ }
+ visitChildren(paragraph);
+ if (!inTightList) {
+ html.tag("/p");
+ html.line();
+ }
+ }
+
+ @Override
+ public void visit(BlockQuote blockQuote) {
+ html.line();
+ html.tag("blockquote", getAttrs(blockQuote, "blockquote"));
+ html.line();
+ visitChildren(blockQuote);
+ html.line();
+ html.tag("/blockquote");
+ html.line();
+ }
+
+ @Override
+ public void visit(BulletList bulletList) {
+ renderListBlock(bulletList, "ul", getAttrs(bulletList, "ul"));
+ }
+
+ @Override
+ public void visit(FencedCodeBlock fencedCodeBlock) {
+ String literal = fencedCodeBlock.getLiteral();
+ Map attributes = new LinkedHashMap<>();
+ String info = fencedCodeBlock.getInfo();
+ if (info != null && !info.isEmpty()) {
+ int space = info.indexOf(" ");
+ String language;
+ if (space == -1) {
+ language = info;
+ } else {
+ language = info.substring(0, space);
+ }
+ attributes.put("class", "language-" + language);
+ }
+ renderCodeBlock(literal, fencedCodeBlock, attributes);
+ }
+
+ @Override
+ public void visit(HtmlBlock htmlBlock) {
+ html.line();
+ if (context.shouldEscapeHtml()) {
+ html.tag("p", getAttrs(htmlBlock, "p"));
+ html.text(htmlBlock.getLiteral());
+ html.tag("/p");
+ } else {
+ html.raw(htmlBlock.getLiteral());
+ }
+ html.line();
+ }
+
+ @Override
+ public void visit(ThematicBreak thematicBreak) {
+ html.line();
+ html.tag("hr", getAttrs(thematicBreak, "hr"), true);
+ html.line();
+ }
+
+ @Override
+ public void visit(IndentedCodeBlock indentedCodeBlock) {
+ renderCodeBlock(indentedCodeBlock.getLiteral(), indentedCodeBlock, Collections.emptyMap());
+ }
+
+ @Override
+ public void visit(Link link) {
+ Map attrs = new LinkedHashMap<>();
+ String url = link.getDestination();
+
+ if (context.shouldSanitizeUrls()) {
+ url = context.urlSanitizer().sanitizeLinkUrl(url);
+ attrs.put("rel", "nofollow");
+ }
+
+ url = context.encodeUrl(url);
+ attrs.put("href", url);
+ if (link.getTitle() != null) {
+ attrs.put("title", link.getTitle());
+ }
+ html.tag("a", getAttrs(link, "a", attrs));
+ visitChildren(link);
+ html.tag("/a");
+ }
+
+ @Override
+ public void visit(ListItem listItem) {
+ html.tag("li", getAttrs(listItem, "li"));
+ visitChildren(listItem);
+ html.tag("/li");
+ html.line();
+ }
+
+ @Override
+ public void visit(OrderedList orderedList) {
+ int start = orderedList.getStartNumber();
+ Map attrs = new LinkedHashMap<>();
+ if (start != 1) {
+ attrs.put("start", String.valueOf(start));
+ }
+ renderListBlock(orderedList, "ol", getAttrs(orderedList, "ol", attrs));
+ }
+
+ @Override
+ public void visit(Image image) {
+ String url = image.getDestination();
+
+ AltTextVisitor altTextVisitor = new AltTextVisitor();
+ image.accept(altTextVisitor);
+ String altText = altTextVisitor.getAltText();
+
+ Map attrs = new LinkedHashMap<>();
+ if (context.shouldSanitizeUrls()) {
+ url = context.urlSanitizer().sanitizeImageUrl(url);
+ }
+
+ attrs.put("src", context.encodeUrl(url));
+ attrs.put("alt", altText);
+ if (image.getTitle() != null) {
+ attrs.put("title", image.getTitle());
+ }
+
+ html.tag("img", getAttrs(image, "img", attrs), true);
+ }
+
+ @Override
+ public void visit(Emphasis emphasis) {
+ html.tag("em", getAttrs(emphasis, "em"));
+ visitChildren(emphasis);
+ html.tag("/em");
+ }
+
+ @Override
+ public void visit(StrongEmphasis strongEmphasis) {
+ html.tag("strong", getAttrs(strongEmphasis, "strong"));
+ visitChildren(strongEmphasis);
+ html.tag("/strong");
+ }
+
+ @Override
+ public void visit(Text text) {
+ html.text(text.getLiteral());
+ }
+
+ @Override
+ public void visit(Code code) {
+ html.tag("code", getAttrs(code, "code"));
+ html.text(code.getLiteral());
+ html.tag("/code");
+ }
+
+ @Override
+ public void visit(HtmlInline htmlInline) {
+ if (context.shouldEscapeHtml()) {
+ html.text(htmlInline.getLiteral());
+ } else {
+ html.raw(htmlInline.getLiteral());
+ }
+ }
+
+ @Override
+ public void visit(SoftLineBreak softLineBreak) {
+ html.raw(context.getSoftbreak());
+ }
+
+ @Override
+ public void visit(HardLineBreak hardLineBreak) {
+ html.tag("br", getAttrs(hardLineBreak, "br"), true);
+ html.line();
+ }
+
+ @Override
+ protected void visitChildren(Node parent) {
+ Node node = parent.getFirstChild();
+ while (node != null) {
+ Node next = node.getNext();
+ context.render(node);
+ node = next;
+ }
+ }
+
+ private void renderCodeBlock(String literal, Node node, Map attributes) {
+ html.line();
+ html.tag("pre", getAttrs(node, "pre"));
+ html.tag("code", getAttrs(node, "code", attributes));
+ html.text(literal);
+ html.tag("/code");
+ html.tag("/pre");
+ html.line();
+ }
+
+ private void renderListBlock(ListBlock listBlock, String tagName, Map attributes) {
+ html.line();
+ html.tag(tagName, attributes);
+ html.line();
+ visitChildren(listBlock);
+ html.line();
+ html.tag('/' + tagName);
+ html.line();
+ }
+
+ private boolean isInTightList(Paragraph paragraph) {
+ Node parent = paragraph.getParent();
+ if (parent != null) {
+ Node gramps = parent.getParent();
+ if (gramps instanceof ListBlock) {
+ ListBlock list = (ListBlock) gramps;
+ return list.isTight();
+ }
+ }
+ return false;
+ }
+
+ private Map getAttrs(Node node, String tagName) {
+ return getAttrs(node, tagName, Collections.emptyMap());
+ }
+
+ private Map getAttrs(Node node, String tagName, Map defaultAttributes) {
+ return context.extendAttributes(node, tagName, defaultAttributes);
+ }
+
+ private static class AltTextVisitor extends AbstractVisitor {
+
+ private final StringBuilder sb = new StringBuilder();
+
+ String getAltText() {
+ return sb.toString();
+ }
+
+ @Override
+ public void visit(Text text) {
+ sb.append(text.getLiteral());
+ }
+
+ @Override
+ public void visit(SoftLineBreak softLineBreak) {
+ sb.append('\n');
+ }
+
+ @Override
+ public void visit(HardLineBreak hardLineBreak) {
+ sb.append('\n');
+ }
+ }
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/html/DefaultUrlSanitizer.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/html/DefaultUrlSanitizer.java
new file mode 100644
index 0000000000000..16e094afe16cd
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/html/DefaultUrlSanitizer.java
@@ -0,0 +1,115 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.renderer.html;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ *
+ * Allows http, https and mailto protocols for url.
+ * Also allows protocol relative urls, and relative urls.
+ * Implementation based on https://github.com/OWASP/java-html-sanitizer/blob/f07e44b034a45d94d6fd010279073c38b6933072/src/main/java/org/owasp/html/FilterUrlByProtocolAttributePolicy.java
+ */
+public class DefaultUrlSanitizer implements UrlSanitizer {
+ private Set protocols;
+
+ public DefaultUrlSanitizer() {
+ this(Arrays.asList("http", "https", "mailto"));
+ }
+
+ public DefaultUrlSanitizer(Collection protocols) {
+ this.protocols = new HashSet<>(protocols);
+ }
+
+ @Override
+ public String sanitizeLinkUrl(String url) {
+ url = stripHtmlSpaces(url);
+ protocol_loop:
+ for (int i = 0, n = url.length(); i < n; ++i) {
+ switch (url.charAt(i)) {
+ case '/':
+ case '#':
+ case '?': // No protocol.
+ break protocol_loop;
+ case ':':
+ String protocol = url.substring(0, i).toLowerCase();
+ if (!protocols.contains(protocol)) {
+ return "";
+ }
+ break protocol_loop;
+ }
+ }
+ return url;
+ }
+
+
+ @Override
+ public String sanitizeImageUrl(String url) {
+ return sanitizeLinkUrl(url);
+ }
+
+ private String stripHtmlSpaces(String s) {
+ int i = 0, n = s.length();
+ for (; n > i; --n) {
+ if (!isHtmlSpace(s.charAt(n - 1))) {
+ break;
+ }
+ }
+ for (; i < n; ++i) {
+ if (!isHtmlSpace(s.charAt(i))) {
+ break;
+ }
+ }
+ if (i == 0 && n == s.length()) {
+ return s;
+ }
+ return s.substring(i, n);
+ }
+
+ private boolean isHtmlSpace(int ch) {
+ switch (ch) {
+ case ' ':
+ case '\t':
+ case '\n':
+ case '\u000c':
+ case '\r':
+ return true;
+ default:
+ return false;
+
+ }
+ }
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/html/HtmlNodeRendererContext.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/html/HtmlNodeRendererContext.java
new file mode 100644
index 0000000000000..78dc295cb21e0
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/html/HtmlNodeRendererContext.java
@@ -0,0 +1,93 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.renderer.html;
+
+import jdk.internal.org.commonmark.node.Image;
+import jdk.internal.org.commonmark.node.Link;
+import jdk.internal.org.commonmark.node.Node;
+
+import java.util.Map;
+
+public interface HtmlNodeRendererContext {
+
+ /**
+ * @param url to be encoded
+ * @return an encoded URL (depending on the configuration)
+ */
+ String encodeUrl(String url);
+
+ /**
+ * Let extensions modify the HTML tag attributes.
+ *
+ * @param node the node for which the attributes are applied
+ * @param tagName the HTML tag name that these attributes are for (e.g. {@code h1}, {@code pre}, {@code code}).
+ * @param attributes the attributes that were calculated by the renderer
+ * @return the extended attributes with added/updated/removed entries
+ */
+ Map extendAttributes(Node node, String tagName, Map attributes);
+
+ /**
+ * @return the HTML writer to use
+ */
+ HtmlWriter getWriter();
+
+ /**
+ * @return HTML that should be rendered for a soft line break
+ */
+ String getSoftbreak();
+
+ /**
+ * Render the specified node and its children using the configured renderers. This should be used to render child
+ * nodes; be careful not to pass the node that is being rendered, that would result in an endless loop.
+ *
+ * @param node the node to render
+ */
+ void render(Node node);
+
+ /**
+ * @return whether HTML blocks and tags should be escaped or not
+ */
+ boolean shouldEscapeHtml();
+
+ /**
+ * @return true if the {@link UrlSanitizer} should be used.
+ * @since 0.14.0
+ */
+ boolean shouldSanitizeUrls();
+
+ /**
+ * @return Sanitizer to use for securing {@link Link} href and {@link Image} src if {@link #shouldSanitizeUrls()} is true.
+ * @since 0.14.0
+ */
+ UrlSanitizer urlSanitizer();
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/html/HtmlNodeRendererFactory.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/html/HtmlNodeRendererFactory.java
new file mode 100644
index 0000000000000..c86d976ae0bf1
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/html/HtmlNodeRendererFactory.java
@@ -0,0 +1,49 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.renderer.html;
+
+import jdk.internal.org.commonmark.renderer.NodeRenderer;
+
+/**
+ * Factory for instantiating new node renderers when rendering is done.
+ */
+public interface HtmlNodeRendererFactory {
+
+ /**
+ * Create a new node renderer for the specified rendering context.
+ *
+ * @param context the context for rendering (normally passed on to the node renderer)
+ * @return a node renderer
+ */
+ NodeRenderer create(HtmlNodeRendererContext context);
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/html/HtmlRenderer.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/html/HtmlRenderer.java
new file mode 100644
index 0000000000000..d4b5214211af6
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/html/HtmlRenderer.java
@@ -0,0 +1,337 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.renderer.html;
+
+import jdk.internal.org.commonmark.Extension;
+import jdk.internal.org.commonmark.internal.renderer.NodeRendererMap;
+import jdk.internal.org.commonmark.internal.util.Escaping;
+import jdk.internal.org.commonmark.node.*;
+import jdk.internal.org.commonmark.renderer.NodeRenderer;
+import jdk.internal.org.commonmark.renderer.Renderer;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Renders a tree of nodes to HTML.
+ *
+ * Start with the {@link #builder} method to configure the renderer. Example:
+ *
+ */
+public class HtmlRenderer implements Renderer {
+
+ private final String softbreak;
+ private final boolean escapeHtml;
+ private final boolean sanitizeUrls;
+ private final UrlSanitizer urlSanitizer;
+ private final boolean percentEncodeUrls;
+ private final List attributeProviderFactories;
+ private final List nodeRendererFactories;
+
+ private HtmlRenderer(Builder builder) {
+ this.softbreak = builder.softbreak;
+ this.escapeHtml = builder.escapeHtml;
+ this.sanitizeUrls = builder.sanitizeUrls;
+ this.percentEncodeUrls = builder.percentEncodeUrls;
+ this.urlSanitizer = builder.urlSanitizer;
+ this.attributeProviderFactories = new ArrayList<>(builder.attributeProviderFactories);
+
+ this.nodeRendererFactories = new ArrayList<>(builder.nodeRendererFactories.size() + 1);
+ this.nodeRendererFactories.addAll(builder.nodeRendererFactories);
+ // Add as last. This means clients can override the rendering of core nodes if they want.
+ this.nodeRendererFactories.add(new HtmlNodeRendererFactory() {
+ @Override
+ public NodeRenderer create(HtmlNodeRendererContext context) {
+ return new CoreHtmlNodeRenderer(context);
+ }
+ });
+ }
+
+ /**
+ * Create a new builder for configuring an {@link HtmlRenderer}.
+ *
+ * @return a builder
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ @Override
+ public void render(Node node, Appendable output) {
+ if (node == null) {
+ throw new NullPointerException("node must not be null");
+ }
+ RendererContext context = new RendererContext(new HtmlWriter(output));
+ context.render(node);
+ }
+
+ @Override
+ public String render(Node node) {
+ if (node == null) {
+ throw new NullPointerException("node must not be null");
+ }
+ StringBuilder sb = new StringBuilder();
+ render(node, sb);
+ return sb.toString();
+ }
+
+ /**
+ * Builder for configuring an {@link HtmlRenderer}. See methods for default configuration.
+ */
+ public static class Builder {
+
+ private String softbreak = "\n";
+ private boolean escapeHtml = false;
+ private boolean sanitizeUrls = false;
+ private UrlSanitizer urlSanitizer = new DefaultUrlSanitizer();
+ private boolean percentEncodeUrls = false;
+ private List attributeProviderFactories = new ArrayList<>();
+ private List nodeRendererFactories = new ArrayList<>();
+
+ /**
+ * @return the configured {@link HtmlRenderer}
+ */
+ public HtmlRenderer build() {
+ return new HtmlRenderer(this);
+ }
+
+ /**
+ * The HTML to use for rendering a softbreak, defaults to {@code "\n"} (meaning the rendered result doesn't have
+ * a line break).
+ *
+ * Set it to {@code " "} (or {@code " "} to make them hard breaks.
+ *
+ * Set it to {@code " "} to ignore line wrapping in the source.
+ *
+ * @param softbreak HTML for softbreak
+ * @return {@code this}
+ */
+ public Builder softbreak(String softbreak) {
+ this.softbreak = softbreak;
+ return this;
+ }
+
+ /**
+ * Whether {@link HtmlInline} and {@link HtmlBlock} should be escaped, defaults to {@code false}.
+ *
+ * Note that {@link HtmlInline} is only a tag itself, not the text between an opening tag and a closing tag. So
+ * markup in the text will be parsed as normal and is not affected by this option.
+ *
+ * @param escapeHtml true for escaping, false for preserving raw HTML
+ * @return {@code this}
+ */
+ public Builder escapeHtml(boolean escapeHtml) {
+ this.escapeHtml = escapeHtml;
+ return this;
+ }
+
+ /**
+ * Whether {@link Image} src and {@link Link} href should be sanitized, defaults to {@code false}.
+ *
+ * @param sanitizeUrls true for sanitization, false for preserving raw attribute
+ * @return {@code this}
+ * @since 0.14.0
+ */
+ public Builder sanitizeUrls(boolean sanitizeUrls) {
+ this.sanitizeUrls = sanitizeUrls;
+ return this;
+ }
+
+ /**
+ * {@link UrlSanitizer} used to filter URL's if {@link #sanitizeUrls} is true.
+ *
+ * @param urlSanitizer Filterer used to filter {@link Image} src and {@link Link}.
+ * @return {@code this}
+ * @since 0.14.0
+ */
+ public Builder urlSanitizer(UrlSanitizer urlSanitizer) {
+ this.urlSanitizer = urlSanitizer;
+ return this;
+ }
+
+ /**
+ * Whether URLs of link or images should be percent-encoded, defaults to {@code false}.
+ *
+ * If enabled, the following is done:
+ *
+ *
Existing percent-encoded parts are preserved (e.g. "%20" is kept as "%20")
+ *
Reserved characters such as "/" are preserved, except for "[" and "]" (see encodeURI in JS)
+ *
Unreserved characters such as "a" are preserved
+ *
Other characters such umlauts are percent-encoded
+ *
+ *
+ * @param percentEncodeUrls true to percent-encode, false for leaving as-is
+ * @return {@code this}
+ */
+ public Builder percentEncodeUrls(boolean percentEncodeUrls) {
+ this.percentEncodeUrls = percentEncodeUrls;
+ return this;
+ }
+
+ /**
+ * Add a factory for an attribute provider for adding/changing HTML attributes to the rendered tags.
+ *
+ * @param attributeProviderFactory the attribute provider factory to add
+ * @return {@code this}
+ */
+ public Builder attributeProviderFactory(AttributeProviderFactory attributeProviderFactory) {
+ if (attributeProviderFactory == null) {
+ throw new NullPointerException("attributeProviderFactory must not be null");
+ }
+ this.attributeProviderFactories.add(attributeProviderFactory);
+ return this;
+ }
+
+ /**
+ * Add a factory for instantiating a node renderer (done when rendering). This allows to override the rendering
+ * of node types or define rendering for custom node types.
+ *
+ * If multiple node renderers for the same node type are created, the one from the factory that was added first
+ * "wins". (This is how the rendering for core node types can be overridden; the default rendering comes last.)
+ *
+ * @param nodeRendererFactory the factory for creating a node renderer
+ * @return {@code this}
+ */
+ public Builder nodeRendererFactory(HtmlNodeRendererFactory nodeRendererFactory) {
+ if (nodeRendererFactory == null) {
+ throw new NullPointerException("nodeRendererFactory must not be null");
+ }
+ this.nodeRendererFactories.add(nodeRendererFactory);
+ return this;
+ }
+
+ /**
+ * @param extensions extensions to use on this HTML renderer
+ * @return {@code this}
+ */
+ public Builder extensions(Iterable extends Extension> extensions) {
+ if (extensions == null) {
+ throw new NullPointerException("extensions must not be null");
+ }
+ for (Extension extension : extensions) {
+ if (extension instanceof HtmlRendererExtension) {
+ HtmlRendererExtension htmlRendererExtension = (HtmlRendererExtension) extension;
+ htmlRendererExtension.extend(this);
+ }
+ }
+ return this;
+ }
+ }
+
+ /**
+ * Extension for {@link HtmlRenderer}.
+ */
+ public interface HtmlRendererExtension extends Extension {
+ void extend(Builder rendererBuilder);
+ }
+
+ private class RendererContext implements HtmlNodeRendererContext, AttributeProviderContext {
+
+ private final HtmlWriter htmlWriter;
+ private final List attributeProviders;
+ private final NodeRendererMap nodeRendererMap = new NodeRendererMap();
+
+ private RendererContext(HtmlWriter htmlWriter) {
+ this.htmlWriter = htmlWriter;
+
+ attributeProviders = new ArrayList<>(attributeProviderFactories.size());
+ for (AttributeProviderFactory attributeProviderFactory : attributeProviderFactories) {
+ attributeProviders.add(attributeProviderFactory.create(this));
+ }
+
+ // The first node renderer for a node type "wins".
+ for (int i = nodeRendererFactories.size() - 1; i >= 0; i--) {
+ HtmlNodeRendererFactory nodeRendererFactory = nodeRendererFactories.get(i);
+ NodeRenderer nodeRenderer = nodeRendererFactory.create(this);
+ nodeRendererMap.add(nodeRenderer);
+ }
+ }
+
+ @Override
+ public boolean shouldEscapeHtml() {
+ return escapeHtml;
+ }
+
+ @Override
+ public boolean shouldSanitizeUrls() {
+ return sanitizeUrls;
+ }
+
+ @Override
+ public UrlSanitizer urlSanitizer() {
+ return urlSanitizer;
+ }
+
+ @Override
+ public String encodeUrl(String url) {
+ if (percentEncodeUrls) {
+ return Escaping.percentEncodeUrl(url);
+ } else {
+ return url;
+ }
+ }
+
+ @Override
+ public Map extendAttributes(Node node, String tagName, Map attributes) {
+ Map attrs = new LinkedHashMap<>(attributes);
+ setCustomAttributes(node, tagName, attrs);
+ return attrs;
+ }
+
+ @Override
+ public HtmlWriter getWriter() {
+ return htmlWriter;
+ }
+
+ @Override
+ public String getSoftbreak() {
+ return softbreak;
+ }
+
+ @Override
+ public void render(Node node) {
+ nodeRendererMap.render(node);
+ }
+
+ private void setCustomAttributes(Node node, String tagName, Map attrs) {
+ for (AttributeProvider attributeProvider : attributeProviders) {
+ attributeProvider.setAttributes(node, tagName, attrs);
+ }
+ }
+ }
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/html/HtmlWriter.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/html/HtmlWriter.java
new file mode 100644
index 0000000000000..341c428a3ee1d
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/html/HtmlWriter.java
@@ -0,0 +1,107 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.renderer.html;
+
+import jdk.internal.org.commonmark.internal.util.Escaping;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Map;
+
+public class HtmlWriter {
+
+ private static final Map NO_ATTRIBUTES = Collections.emptyMap();
+
+ private final Appendable buffer;
+ private char lastChar = 0;
+
+ public HtmlWriter(Appendable out) {
+ if (out == null) {
+ throw new NullPointerException("out must not be null");
+ }
+ this.buffer = out;
+ }
+
+ public void raw(String s) {
+ append(s);
+ }
+
+ public void text(String text) {
+ append(Escaping.escapeHtml(text));
+ }
+
+ public void tag(String name) {
+ tag(name, NO_ATTRIBUTES);
+ }
+
+ public void tag(String name, Map attrs) {
+ tag(name, attrs, false);
+ }
+
+ public void tag(String name, Map attrs, boolean voidElement) {
+ append("<");
+ append(name);
+ if (attrs != null && !attrs.isEmpty()) {
+ for (Map.Entry attrib : attrs.entrySet()) {
+ append(" ");
+ append(Escaping.escapeHtml(attrib.getKey()));
+ append("=\"");
+ append(Escaping.escapeHtml(attrib.getValue()));
+ append("\"");
+ }
+ }
+ if (voidElement) {
+ append(" /");
+ }
+
+ append(">");
+ }
+
+ public void line() {
+ if (lastChar != 0 && lastChar != '\n') {
+ append("\n");
+ }
+ }
+
+ protected void append(String s) {
+ try {
+ buffer.append(s);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ int length = s.length();
+ if (length != 0) {
+ lastChar = s.charAt(length - 1);
+ }
+ }
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/html/UrlSanitizer.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/html/UrlSanitizer.java
new file mode 100644
index 0000000000000..12c69a7382154
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/html/UrlSanitizer.java
@@ -0,0 +1,62 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.renderer.html;
+
+import jdk.internal.org.commonmark.node.Image;
+import jdk.internal.org.commonmark.node.Link;
+
+/**
+ * Sanitizes urls for img and a elements by whitelisting protocols.
+ * This is intended to prevent XSS payloads like [Click this totally safe url](javascript:document.xss=true;)
+ *
+ * Implementation based on https://github.com/OWASP/java-html-sanitizer/blob/f07e44b034a45d94d6fd010279073c38b6933072/src/main/java/org/owasp/html/FilterUrlByProtocolAttributePolicy.java
+ *
+ * @since 0.14.0
+ */
+public interface UrlSanitizer {
+ /**
+ * Sanitize a url for use in the href attribute of a {@link Link}.
+ *
+ * @param url Link to sanitize
+ * @return Sanitized link
+ */
+ String sanitizeLinkUrl(String url);
+
+ /**
+ * Sanitize a url for use in the src attribute of a {@link Image}.
+ *
+ * @param url Link to sanitize
+ * @return Sanitized link {@link Image}
+ */
+ String sanitizeImageUrl(String url);
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/html/package-info.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/html/package-info.java
new file mode 100644
index 0000000000000..41dd2c6c6cc9d
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/html/package-info.java
@@ -0,0 +1,36 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+/**
+ * HTML rendering (see {@link org.commonmark.renderer.html.HtmlRenderer})
+ */
+package jdk.internal.org.commonmark.renderer.html;
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/text/CoreTextContentNodeRenderer.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/text/CoreTextContentNodeRenderer.java
new file mode 100644
index 0000000000000..1321d5ad78502
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/text/CoreTextContentNodeRenderer.java
@@ -0,0 +1,312 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.renderer.text;
+
+import jdk.internal.org.commonmark.node.*;
+import jdk.internal.org.commonmark.renderer.NodeRenderer;
+import jdk.internal.org.commonmark.internal.renderer.text.BulletListHolder;
+import jdk.internal.org.commonmark.internal.renderer.text.ListHolder;
+import jdk.internal.org.commonmark.internal.renderer.text.OrderedListHolder;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * The node renderer that renders all the core nodes (comes last in the order of node renderers).
+ */
+public class CoreTextContentNodeRenderer extends AbstractVisitor implements NodeRenderer {
+
+ protected final TextContentNodeRendererContext context;
+ private final TextContentWriter textContent;
+
+ private ListHolder listHolder;
+
+ public CoreTextContentNodeRenderer(TextContentNodeRendererContext context) {
+ this.context = context;
+ this.textContent = context.getWriter();
+ }
+
+ @Override
+ public Set> getNodeTypes() {
+ return new HashSet<>(Arrays.asList(
+ Document.class,
+ Heading.class,
+ Paragraph.class,
+ BlockQuote.class,
+ BulletList.class,
+ FencedCodeBlock.class,
+ HtmlBlock.class,
+ ThematicBreak.class,
+ IndentedCodeBlock.class,
+ Link.class,
+ ListItem.class,
+ OrderedList.class,
+ Image.class,
+ Emphasis.class,
+ StrongEmphasis.class,
+ Text.class,
+ Code.class,
+ HtmlInline.class,
+ SoftLineBreak.class,
+ HardLineBreak.class
+ ));
+ }
+
+ @Override
+ public void render(Node node) {
+ node.accept(this);
+ }
+
+ @Override
+ public void visit(Document document) {
+ // No rendering itself
+ visitChildren(document);
+ }
+
+ @Override
+ public void visit(BlockQuote blockQuote) {
+ textContent.write('\u00ab');
+ visitChildren(blockQuote);
+ textContent.write('\u00bb');
+
+ writeEndOfLineIfNeeded(blockQuote, null);
+ }
+
+ @Override
+ public void visit(BulletList bulletList) {
+ if (listHolder != null) {
+ writeEndOfLine();
+ }
+ listHolder = new BulletListHolder(listHolder, bulletList);
+ visitChildren(bulletList);
+ writeEndOfLineIfNeeded(bulletList, null);
+ if (listHolder.getParent() != null) {
+ listHolder = listHolder.getParent();
+ } else {
+ listHolder = null;
+ }
+ }
+
+ @Override
+ public void visit(Code code) {
+ textContent.write('\"');
+ textContent.write(code.getLiteral());
+ textContent.write('\"');
+ }
+
+ @Override
+ public void visit(FencedCodeBlock fencedCodeBlock) {
+ if (context.stripNewlines()) {
+ textContent.writeStripped(fencedCodeBlock.getLiteral());
+ writeEndOfLineIfNeeded(fencedCodeBlock, null);
+ } else {
+ textContent.write(fencedCodeBlock.getLiteral());
+ }
+ }
+
+ @Override
+ public void visit(HardLineBreak hardLineBreak) {
+ writeEndOfLineIfNeeded(hardLineBreak, null);
+ }
+
+ @Override
+ public void visit(Heading heading) {
+ visitChildren(heading);
+ writeEndOfLineIfNeeded(heading, ':');
+ }
+
+ @Override
+ public void visit(ThematicBreak thematicBreak) {
+ if (!context.stripNewlines()) {
+ textContent.write("***");
+ }
+ writeEndOfLineIfNeeded(thematicBreak, null);
+ }
+
+ @Override
+ public void visit(HtmlInline htmlInline) {
+ writeText(htmlInline.getLiteral());
+ }
+
+ @Override
+ public void visit(HtmlBlock htmlBlock) {
+ writeText(htmlBlock.getLiteral());
+ }
+
+ @Override
+ public void visit(Image image) {
+ writeLink(image, image.getTitle(), image.getDestination());
+ }
+
+ @Override
+ public void visit(IndentedCodeBlock indentedCodeBlock) {
+ if (context.stripNewlines()) {
+ textContent.writeStripped(indentedCodeBlock.getLiteral());
+ writeEndOfLineIfNeeded(indentedCodeBlock, null);
+ } else {
+ textContent.write(indentedCodeBlock.getLiteral());
+ }
+ }
+
+ @Override
+ public void visit(Link link) {
+ writeLink(link, link.getTitle(), link.getDestination());
+ }
+
+ @Override
+ public void visit(ListItem listItem) {
+ if (listHolder != null && listHolder instanceof OrderedListHolder) {
+ OrderedListHolder orderedListHolder = (OrderedListHolder) listHolder;
+ String indent = context.stripNewlines() ? "" : orderedListHolder.getIndent();
+ textContent.write(indent + orderedListHolder.getCounter() + orderedListHolder.getDelimiter() + " ");
+ visitChildren(listItem);
+ writeEndOfLineIfNeeded(listItem, null);
+ orderedListHolder.increaseCounter();
+ } else if (listHolder != null && listHolder instanceof BulletListHolder) {
+ BulletListHolder bulletListHolder = (BulletListHolder) listHolder;
+ if (!context.stripNewlines()) {
+ textContent.write(bulletListHolder.getIndent() + bulletListHolder.getMarker() + " ");
+ }
+ visitChildren(listItem);
+ writeEndOfLineIfNeeded(listItem, null);
+ }
+ }
+
+ @Override
+ public void visit(OrderedList orderedList) {
+ if (listHolder != null) {
+ writeEndOfLine();
+ }
+ listHolder = new OrderedListHolder(listHolder, orderedList);
+ visitChildren(orderedList);
+ writeEndOfLineIfNeeded(orderedList, null);
+ if (listHolder.getParent() != null) {
+ listHolder = listHolder.getParent();
+ } else {
+ listHolder = null;
+ }
+ }
+
+ @Override
+ public void visit(Paragraph paragraph) {
+ visitChildren(paragraph);
+ // Add "end of line" only if its "root paragraph.
+ if (paragraph.getParent() == null || paragraph.getParent() instanceof Document) {
+ writeEndOfLineIfNeeded(paragraph, null);
+ }
+ }
+
+ @Override
+ public void visit(SoftLineBreak softLineBreak) {
+ writeEndOfLineIfNeeded(softLineBreak, null);
+ }
+
+ @Override
+ public void visit(Text text) {
+ writeText(text.getLiteral());
+ }
+
+ @Override
+ protected void visitChildren(Node parent) {
+ Node node = parent.getFirstChild();
+ while (node != null) {
+ Node next = node.getNext();
+ context.render(node);
+ node = next;
+ }
+ }
+
+ private void writeText(String text) {
+ if (context.stripNewlines()) {
+ textContent.writeStripped(text);
+ } else {
+ textContent.write(text);
+ }
+ }
+
+ private void writeLink(Node node, String title, String destination) {
+ boolean hasChild = node.getFirstChild() != null;
+ boolean hasTitle = title != null && !title.equals(destination);
+ boolean hasDestination = destination != null && !destination.equals("");
+
+ if (hasChild) {
+ textContent.write('"');
+ visitChildren(node);
+ textContent.write('"');
+ if (hasTitle || hasDestination) {
+ textContent.whitespace();
+ textContent.write('(');
+ }
+ }
+
+ if (hasTitle) {
+ textContent.write(title);
+ if (hasDestination) {
+ textContent.colon();
+ textContent.whitespace();
+ }
+ }
+
+ if (hasDestination) {
+ textContent.write(destination);
+ }
+
+ if (hasChild && (hasTitle || hasDestination)) {
+ textContent.write(')');
+ }
+ }
+
+ private void writeEndOfLineIfNeeded(Node node, Character c) {
+ if (context.stripNewlines()) {
+ if (c != null) {
+ textContent.write(c);
+ }
+ if (node.getNext() != null) {
+ textContent.whitespace();
+ }
+ } else {
+ if (node.getNext() != null) {
+ textContent.line();
+ }
+ }
+ }
+
+ private void writeEndOfLine() {
+ if (context.stripNewlines()) {
+ textContent.whitespace();
+ } else {
+ textContent.line();
+ }
+ }
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/text/TextContentNodeRendererContext.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/text/TextContentNodeRendererContext.java
new file mode 100644
index 0000000000000..c00be3cca416e
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/text/TextContentNodeRendererContext.java
@@ -0,0 +1,57 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.renderer.text;
+
+import jdk.internal.org.commonmark.node.Node;
+
+public interface TextContentNodeRendererContext {
+
+ /**
+ * @return true for stripping new lines and render text as "single line",
+ * false for keeping all line breaks.
+ */
+ boolean stripNewlines();
+
+ /**
+ * @return the writer to use
+ */
+ TextContentWriter getWriter();
+
+ /**
+ * Render the specified node and its children using the configured renderers. This should be used to render child
+ * nodes; be careful not to pass the node that is being rendered, that would result in an endless loop.
+ *
+ * @param node the node to render
+ */
+ void render(Node node);
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/text/TextContentNodeRendererFactory.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/text/TextContentNodeRendererFactory.java
new file mode 100644
index 0000000000000..5c110ba4d2947
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/text/TextContentNodeRendererFactory.java
@@ -0,0 +1,49 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.renderer.text;
+
+import jdk.internal.org.commonmark.renderer.NodeRenderer;
+
+/**
+ * Factory for instantiating new node renderers when rendering is done.
+ */
+public interface TextContentNodeRendererFactory {
+
+ /**
+ * Create a new node renderer for the specified rendering context.
+ *
+ * @param context the context for rendering (normally passed on to the node renderer)
+ * @return a node renderer
+ */
+ NodeRenderer create(TextContentNodeRendererContext context);
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/text/TextContentRenderer.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/text/TextContentRenderer.java
new file mode 100644
index 0000000000000..3dc36fabd3025
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/text/TextContentRenderer.java
@@ -0,0 +1,181 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.renderer.text;
+
+import jdk.internal.org.commonmark.Extension;
+import jdk.internal.org.commonmark.internal.renderer.NodeRendererMap;
+import jdk.internal.org.commonmark.node.Node;
+import jdk.internal.org.commonmark.renderer.NodeRenderer;
+import jdk.internal.org.commonmark.renderer.Renderer;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class TextContentRenderer implements Renderer {
+
+ private final boolean stripNewlines;
+
+ private final List nodeRendererFactories;
+
+ private TextContentRenderer(Builder builder) {
+ this.stripNewlines = builder.stripNewlines;
+
+ this.nodeRendererFactories = new ArrayList<>(builder.nodeRendererFactories.size() + 1);
+ this.nodeRendererFactories.addAll(builder.nodeRendererFactories);
+ // Add as last. This means clients can override the rendering of core nodes if they want.
+ this.nodeRendererFactories.add(new TextContentNodeRendererFactory() {
+ @Override
+ public NodeRenderer create(TextContentNodeRendererContext context) {
+ return new CoreTextContentNodeRenderer(context);
+ }
+ });
+ }
+
+ /**
+ * Create a new builder for configuring an {@link TextContentRenderer}.
+ *
+ * @return a builder
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ @Override
+ public void render(Node node, Appendable output) {
+ RendererContext context = new RendererContext(new TextContentWriter(output));
+ context.render(node);
+ }
+
+ @Override
+ public String render(Node node) {
+ StringBuilder sb = new StringBuilder();
+ render(node, sb);
+ return sb.toString();
+ }
+
+ /**
+ * Builder for configuring an {@link TextContentRenderer}. See methods for default configuration.
+ */
+ public static class Builder {
+
+ private boolean stripNewlines = false;
+ private List nodeRendererFactories = new ArrayList<>();
+
+ /**
+ * @return the configured {@link TextContentRenderer}
+ */
+ public TextContentRenderer build() {
+ return new TextContentRenderer(this);
+ }
+
+ /**
+ * Set the value of flag for stripping new lines.
+ *
+ * @param stripNewlines true for stripping new lines and render text as "single line",
+ * false for keeping all line breaks
+ * @return {@code this}
+ */
+ public Builder stripNewlines(boolean stripNewlines) {
+ this.stripNewlines = stripNewlines;
+ return this;
+ }
+
+ /**
+ * Add a factory for instantiating a node renderer (done when rendering). This allows to override the rendering
+ * of node types or define rendering for custom node types.
+ *
+ * If multiple node renderers for the same node type are created, the one from the factory that was added first
+ * "wins". (This is how the rendering for core node types can be overridden; the default rendering comes last.)
+ *
+ * @param nodeRendererFactory the factory for creating a node renderer
+ * @return {@code this}
+ */
+ public Builder nodeRendererFactory(TextContentNodeRendererFactory nodeRendererFactory) {
+ this.nodeRendererFactories.add(nodeRendererFactory);
+ return this;
+ }
+
+ /**
+ * @param extensions extensions to use on this text content renderer
+ * @return {@code this}
+ */
+ public Builder extensions(Iterable extends Extension> extensions) {
+ for (Extension extension : extensions) {
+ if (extension instanceof TextContentRenderer.TextContentRendererExtension) {
+ TextContentRenderer.TextContentRendererExtension textContentRendererExtension =
+ (TextContentRenderer.TextContentRendererExtension) extension;
+ textContentRendererExtension.extend(this);
+ }
+ }
+ return this;
+ }
+ }
+
+ /**
+ * Extension for {@link TextContentRenderer}.
+ */
+ public interface TextContentRendererExtension extends Extension {
+ void extend(TextContentRenderer.Builder rendererBuilder);
+ }
+
+ private class RendererContext implements TextContentNodeRendererContext {
+ private final TextContentWriter textContentWriter;
+ private final NodeRendererMap nodeRendererMap = new NodeRendererMap();
+
+ private RendererContext(TextContentWriter textContentWriter) {
+ this.textContentWriter = textContentWriter;
+
+ // The first node renderer for a node type "wins".
+ for (int i = nodeRendererFactories.size() - 1; i >= 0; i--) {
+ TextContentNodeRendererFactory nodeRendererFactory = nodeRendererFactories.get(i);
+ NodeRenderer nodeRenderer = nodeRendererFactory.create(this);
+ nodeRendererMap.add(nodeRenderer);
+ }
+ }
+
+ @Override
+ public boolean stripNewlines() {
+ return stripNewlines;
+ }
+
+ @Override
+ public TextContentWriter getWriter() {
+ return textContentWriter;
+ }
+
+ @Override
+ public void render(Node node) {
+ nodeRendererMap.render(node);
+ }
+ }
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/text/TextContentWriter.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/text/TextContentWriter.java
new file mode 100644
index 0000000000000..edbb7ae2839eb
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/text/TextContentWriter.java
@@ -0,0 +1,99 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+package jdk.internal.org.commonmark.renderer.text;
+
+import java.io.IOException;
+
+public class TextContentWriter {
+
+ private final Appendable buffer;
+
+ private char lastChar;
+
+ public TextContentWriter(Appendable out) {
+ buffer = out;
+ }
+
+ public void whitespace() {
+ if (lastChar != 0 && lastChar != ' ') {
+ append(' ');
+ }
+ }
+
+ public void colon() {
+ if (lastChar != 0 && lastChar != ':') {
+ append(':');
+ }
+ }
+
+ public void line() {
+ if (lastChar != 0 && lastChar != '\n') {
+ append('\n');
+ }
+ }
+
+ public void writeStripped(String s) {
+ append(s.replaceAll("[\\r\\n\\s]+", " "));
+ }
+
+ public void write(String s) {
+ append(s);
+ }
+
+ public void write(char c) {
+ append(c);
+ }
+
+ private void append(String s) {
+ try {
+ buffer.append(s);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+
+ int length = s.length();
+ if (length != 0) {
+ lastChar = s.charAt(length - 1);
+ }
+ }
+
+ private void append(char c) {
+ try {
+ buffer.append(c);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+
+ lastChar = c;
+ }
+}
diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/text/package-info.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/text/package-info.java
new file mode 100644
index 0000000000000..e861e2457c77e
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/text/package-info.java
@@ -0,0 +1,36 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, a notice that is now available elsewhere in this distribution
+ * accompanied the original version of this file, and, per its terms,
+ * should not be removed.
+ */
+
+/**
+ * Text content rendering (see {@link org.commonmark.renderer.text.TextContentRenderer})
+ */
+package jdk.internal.org.commonmark.renderer.text;
diff --git a/src/jdk.internal.md/share/classes/module-info.java b/src/jdk.internal.md/share/classes/module-info.java
new file mode 100644
index 0000000000000..6103521c20d98
--- /dev/null
+++ b/src/jdk.internal.md/share/classes/module-info.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * Internal support for Markdown.
+ *
+ * @since 21
+ */
+module jdk.internal.md {
+ exports jdk.internal.org.commonmark.node to jdk.javadoc, jdk.jshell;
+ exports jdk.internal.org.commonmark.parser to jdk.javadoc, jdk.jshell;
+ exports jdk.internal.org.commonmark.renderer.html to jdk.javadoc, jdk.jshell;
+}
diff --git a/src/jdk.internal.md/share/legal/commonmark.md b/src/jdk.internal.md/share/legal/commonmark.md
new file mode 100644
index 0000000000000..7fb0ee0c090a8
--- /dev/null
+++ b/src/jdk.internal.md/share/legal/commonmark.md
@@ -0,0 +1,29 @@
+## CommonMark 0.21.0
+
+### CommonMark License
+```
+Copyright (c) 2015, Atlassian Pty Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+```
diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDocletWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDocletWriter.java
index 7fd4f7d14d181..1c16c4a348ab4 100644
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDocletWriter.java
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDocletWriter.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -36,7 +36,6 @@
import java.util.ListIterator;
import java.util.Locale;
import java.util.Map;
-import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.regex.Matcher;
@@ -44,7 +43,6 @@
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element;
-import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.ModuleElement;
import javax.lang.model.element.Name;
@@ -70,9 +68,10 @@
import com.sun.source.doctree.ErroneousTree;
import com.sun.source.doctree.IndexTree;
import com.sun.source.doctree.InheritDocTree;
+import com.sun.source.doctree.InlineTagTree;
import com.sun.source.doctree.LinkTree;
import com.sun.source.doctree.LiteralTree;
-import com.sun.source.doctree.SeeTree;
+import com.sun.source.doctree.MarkdownTree;
import com.sun.source.doctree.StartElementTree;
import com.sun.source.doctree.SummaryTree;
import com.sun.source.doctree.SystemPropertyTree;
@@ -80,6 +79,9 @@
import com.sun.source.util.DocTreePath;
import com.sun.source.util.SimpleDocTreeVisitor;
+import jdk.internal.org.commonmark.node.Node;
+import jdk.internal.org.commonmark.parser.Parser;
+import jdk.internal.org.commonmark.renderer.html.HtmlRenderer;
import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder;
import jdk.javadoc.internal.doclets.formats.html.markup.Entity;
import jdk.javadoc.internal.doclets.formats.html.markup.Head;
@@ -1199,10 +1201,13 @@ public ContentBuilder add(CharSequence text) {
configuration.tagletManager.checkTags(element, trees);
commentRemoved = false;
- for (ListIterator extends DocTree> iterator = trees.listIterator(); iterator.hasNext();) {
+ var useMarkdown = trees.stream().anyMatch(t -> t.getKind() == Kind.MARKDOWN);
+ var markdownHandler = useMarkdown ? new MarkdownHandler() : null;
+
+ for (ListIterator extends DocTree> iterator = trees.listIterator(); iterator.hasNext(); ) {
boolean isFirstNode = !iterator.hasPrevious();
DocTree tag = iterator.next();
- boolean isLastNode = !iterator.hasNext();
+ boolean isLastNode = !iterator.hasNext();
if (context.isFirstSentence) {
// Ignore block tags
@@ -1222,242 +1227,357 @@ public ContentBuilder add(CharSequence text) {
}
}
- var docTreeVisitor = new SimpleDocTreeVisitor() {
+ var docTreeVisitor = new InlineVisitor(element, tag, isLastNode, context, ch, trees);
- private boolean inAnAtag() {
- return (tag instanceof StartElementTree st) && equalsIgnoreCase(st.getName(), "a");
- }
+ boolean allDone = useMarkdown
+ ? markdownHandler.handle(tag, docTreeVisitor)
+ : docTreeVisitor.visit(tag, result);
+ commentRemoved = false;
- @Override
- public Boolean visitAttribute(AttributeTree node, Content content) {
- if (!content.isEmpty()) {
- content.add(" ");
- }
- content.add(node.getName());
- if (node.getValueKind() == ValueKind.EMPTY) {
- return false;
- }
- content.add("=");
- String quote = switch (node.getValueKind()) {
- case DOUBLE -> "\"";
- case SINGLE -> "'";
- default -> "";
- };
- content.add(quote);
-
- /* In the following code for an attribute value:
- * 1. {@docRoot} followed by text beginning "/.." is replaced by the value
- * of the docrootParent option, followed by the remainder of the text
- * 2. in the value of an "href" attribute in a tag, an initial text
- * value will have a relative link redirected.
- * Note that, realistically, it only makes sense to ever use {@docRoot}
- * at the beginning of a URL in an attribute value, but this is not
- * required or enforced.
- */
- boolean isHRef = inAnAtag() && equalsIgnoreCase(node.getName(), "href");
- boolean first = true;
- DocRootTree pendingDocRoot = null;
- for (DocTree dt : node.getValue()) {
- if (pendingDocRoot != null) {
- if (dt instanceof TextTree tt) {
- String text = tt.getBody();
- if (text.startsWith("/..") && !options.docrootParent().isEmpty()) {
- content.add(options.docrootParent());
- content.add(textCleanup(text.substring(3), isLastNode));
- pendingDocRoot = null;
- continue;
- }
- }
- pendingDocRoot.accept(this, content);
- pendingDocRoot = null;
- }
+ if (allDone)
+ break;
+ }
- if (dt instanceof TextTree tt) {
- String text = tt.getBody();
- if (first && isHRef) {
- text = redirectRelativeLinks(element, tt);
- }
- content.add(textCleanup(text, isLastNode));
- } else if (dt instanceof DocRootTree drt) {
- // defer until we see what, if anything, follows this node
- pendingDocRoot = drt;
- } else {
- dt.accept(this, content);
- }
- first = false;
- }
- if (pendingDocRoot != null) {
- pendingDocRoot.accept(this, content);
- }
+ if (useMarkdown) {
+ markdownHandler.addContent(result);
+ }
- content.add(quote);
- return false;
- }
+ return result;
+ }
- @Override
- public Boolean visitComment(CommentTree node, Content content) {
- content.add(RawHtml.comment(node.getBody()));
- return false;
+ private class MarkdownHandler {
+ private static final char PLACEHOLDER = '\uFFFC'; // Unicode Object Replacement Character
+ StringBuilder markdownInput = new StringBuilder() ;
+ ArrayList fffcObjects = new ArrayList<>();
+
+ Parser parser = Parser.builder().build();
+ HtmlRenderer renderer = HtmlRenderer.builder().build();
+
+ boolean handle(DocTree tree, InlineVisitor visitor) {
+ boolean allDone;
+ if (tree instanceof MarkdownTree t) {
+ String code = t.getContent();
+ // handle the (unlikely) case of FFFC characters existing in the code
+ int start = 0;
+ int pos;
+ while ((pos = code.indexOf(PLACEHOLDER, start)) != -1) {
+ markdownInput.append(code.substring(start, pos));
+ markdownInput.append(PLACEHOLDER);
+ fffcObjects.add(Text.of(String.valueOf(PLACEHOLDER)));
+ start = pos + 1;
}
+ markdownInput.append(code.substring(start));
+ allDone = false;
+ } else {
+ Content embeddedContent = new ContentBuilder();
+ allDone = visitor.visit(tree, embeddedContent);
+ fffcObjects.add(embeddedContent);
+ markdownInput.append(PLACEHOLDER);
+ }
+ return allDone;
+ }
- @Override
- public Boolean visitDocRoot(DocRootTree node, Content content) {
- content.add(getInlineTagOutput(element, node, context));
- return false;
- }
+ void addContent(Content result) {
+ Node document = parser.parse(markdownInput.toString());
+ String markdownOutput = unwrap(renderer.render(document));
+
+ int start = 0;
+ int pos;
+ int fffcObjectIndex = 0;
+ while ((pos = markdownOutput.indexOf(PLACEHOLDER, start)) != -1) {
+ result.add(RawHtml.markdown(markdownOutput.substring(start, pos)));
+ result.add(fffcObjects.get(fffcObjectIndex++));
+ start = pos + 1;
+ }
+ if (start < markdownOutput.length()) {
+ result.add(RawHtml.of(markdownOutput.substring(start)));
+ }
+ }
+ }
- @Override
- public Boolean visitEndElement(EndElementTree node, Content content) {
- content.add(RawHtml.endElement(node.getName()));
- return false;
+ /*
+ * If a string contains a simple HTML paragraph, beginning with