Fallback to HTML markup inside HTML blocks
Call the Xhtml5BaseSink (super class) whenever markup is not allowed (in
an HTML context)
Add test for link inside html block
This closes #1015
diff --git a/doxia-core/src/test/java/org/apache/maven/doxia/sink/impl/AbstractSinkTest.java b/doxia-core/src/test/java/org/apache/maven/doxia/sink/impl/AbstractSinkTest.java
index 87658e8..5e5cc0c 100644
--- a/doxia-core/src/test/java/org/apache/maven/doxia/sink/impl/AbstractSinkTest.java
+++ b/doxia-core/src/test/java/org/apache/maven/doxia/sink/impl/AbstractSinkTest.java
@@ -471,7 +471,7 @@
/**
* Checks that the sequence <code>[footer(), footer_()]</code>,
* invoked on the current sink, produces the same result as
- * {@link #getHeaderBlock getHeaderBlock()}.
+ * {@link #getFooterBlock()}.
*/
@Test
public void footer() {
diff --git a/doxia-modules/doxia-module-markdown/src/main/java/org/apache/maven/doxia/module/markdown/MarkdownSink.java b/doxia-modules/doxia-module-markdown/src/main/java/org/apache/maven/doxia/module/markdown/MarkdownSink.java
index 1464343..e40d59d 100644
--- a/doxia-modules/doxia-module-markdown/src/main/java/org/apache/maven/doxia/module/markdown/MarkdownSink.java
+++ b/doxia-modules/doxia-module-markdown/src/main/java/org/apache/maven/doxia/module/markdown/MarkdownSink.java
@@ -18,8 +18,6 @@
*/
package org.apache.maven.doxia.module.markdown;
-import javax.swing.text.MutableAttributeSet;
-
import java.io.PrintWriter;
import java.io.Writer;
import java.util.ArrayList;
@@ -34,12 +32,9 @@
import org.apache.maven.doxia.sink.Sink;
import org.apache.maven.doxia.sink.SinkEventAttributes;
-import org.apache.maven.doxia.sink.impl.AbstractTextSink;
import org.apache.maven.doxia.sink.impl.SinkEventAttributeSet;
-import org.apache.maven.doxia.sink.impl.SinkUtils;
import org.apache.maven.doxia.sink.impl.Xhtml5BaseSink;
import org.apache.maven.doxia.util.DoxiaStringUtils;
-import org.apache.maven.doxia.util.DoxiaUtils;
import org.apache.maven.doxia.util.HtmlTools;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -48,8 +43,9 @@
* Markdown generator implementation.
* <br>
* <b>Note</b>: The encoding used is UTF-8.
+ * Extends the Xhtml5 sink as in some context HTML needs to be emitted.
*/
-public class MarkdownSink extends AbstractTextSink implements MarkdownMarkup {
+public class MarkdownSink extends Xhtml5BaseSink implements MarkdownMarkup {
private static final Logger LOGGER = LoggerFactory.getLogger(MarkdownSink.class);
// ----------------------------------------------------------------------
@@ -93,10 +89,10 @@
private final LastTwoLinesBufferingWriter bufferingWriter;
/** Keep track of end markup for inline events. */
- protected Queue<Queue<String>> inlineStack = Collections.asLifoQueue(new LinkedList<>());
+ protected Queue<Queue<String>> inlineStack;
/** The context of the surrounding elements as stack (LIFO) */
- protected Queue<ElementContext> elementContextStack = Collections.asLifoQueue(new LinkedList<>());
+ protected Queue<ElementContext> elementContextStack;
private String figureSrc;
@@ -222,6 +218,13 @@
/**
*
+ * @return {@code true} if only HTML is allowed in this context
+ */
+ boolean isHtml() {
+ return this.equals(HTML_BLOCK);
+ }
+ /**
+ *
* @return {@code true} for all containers (allowing block elements as children), {@code false} otherwise
*/
boolean isContainer() {
@@ -332,14 +335,20 @@
// Public protected methods
// ----------------------------------------------------------------------
+ protected static MarkdownSink newInstance(Writer writer) {
+ LastTwoLinesBufferingWriter bufferingWriter = new LastTwoLinesBufferingWriter(writer);
+ return new MarkdownSink(bufferingWriter, new PrintWriter(bufferingWriter));
+ }
+
/**
* Constructor, initialize the Writer and the variables.
*
* @param writer not null writer to write the result. <b>Should</b> be an UTF-8 Writer.
*/
- protected MarkdownSink(Writer writer) {
- this.bufferingWriter = new LastTwoLinesBufferingWriter(writer);
- this.writer = new PrintWriter(bufferingWriter);
+ private MarkdownSink(LastTwoLinesBufferingWriter bufferingWriter, PrintWriter writer) {
+ super(writer);
+ this.bufferingWriter = bufferingWriter;
+ this.writer = writer;
init();
}
@@ -467,8 +476,8 @@
this.tableHeaderCellFlag = false;
this.cellCount = 0;
this.cellJustif = null;
- this.elementContextStack.clear();
- this.inlineStack.clear();
+ this.elementContextStack = Collections.asLifoQueue(new LinkedList<>());
+ this.inlineStack = Collections.asLifoQueue(new LinkedList<>());
// always set a default context (at least for tests not emitting a body)
elementContextStack.add(ElementContext.BODY);
}
@@ -540,6 +549,26 @@
}
@Override
+ public void section(int level, SinkEventAttributes attributes) {
+ // not supported as often used around sectionTitles which would otherwise no longer be emitted as markdown
+ }
+
+ @Override
+ public void section_(int level) {
+ // not supported as often used around sectionTitles which would otherwise no longer be emitted as markdown
+ }
+
+ @Override
+ public void header(SinkEventAttributes attributes) {
+ // not supported as often used around sectionTitles which would otherwise no longer be emitted as markdown
+ }
+
+ @Override
+ public void header_() {
+ // not supported as often used around sectionTitles which would otherwise no longer be emitted as markdown
+ }
+
+ @Override
public void sectionTitle(int level, SinkEventAttributes attributes) {
startContext(ElementContext.HEADING);
if (level > 0) {
@@ -557,6 +586,13 @@
}
@Override
+ public void list(SinkEventAttributes attributes) {
+ if (elementContextStack.element().isHtml()) {
+ super.list(attributes);
+ }
+ }
+
+ @Override
public void list_() {
ensureBeginningOfLine();
}
@@ -641,9 +677,9 @@
@Override
public void paragraph(SinkEventAttributes attributes) {
+ ensureBlankLine();
// ignore paragraphs outside container contexts
if (elementContextStack.element().isContainer()) {
- ensureBlankLine();
writeUnescaped(getLinePrefix());
}
}
@@ -658,33 +694,49 @@
@Override
public void verbatim(SinkEventAttributes attributes) {
- // if no source attribute, then don't emit an info string
- startContext(ElementContext.CODE_BLOCK);
- writeUnescaped(VERBATIM_START_MARKUP);
- if (attributes != null && attributes.containsAttributes(SinkEventAttributeSet.SOURCE)) {
- writeUnescaped("unknown"); // unknown language
+ if (elementContextStack.element().isHtml()) {
+ super.verbatim(attributes);
+ } else {
+ // if no source attribute, then don't emit an info string
+ startContext(ElementContext.CODE_BLOCK);
+ writeUnescaped(VERBATIM_START_MARKUP);
+ if (attributes != null && attributes.containsAttributes(SinkEventAttributeSet.SOURCE)) {
+ writeUnescaped("unknown"); // unknown language
+ }
+ writeUnescaped(EOL);
+ writeUnescaped(getLinePrefix());
}
- writeUnescaped(EOL);
- writeUnescaped(getLinePrefix());
}
@Override
public void verbatim_() {
- ensureBeginningOfLine();
- writeUnescaped(getLinePrefix());
- writeUnescaped(VERBATIM_END_MARKUP + BLANK_LINE);
- endContext(ElementContext.CODE_BLOCK);
+ if (elementContextStack.element().isHtml()) {
+ super.verbatim_();
+ } else {
+ ensureBeginningOfLine();
+ writeUnescaped(getLinePrefix());
+ writeUnescaped(VERBATIM_END_MARKUP + BLANK_LINE);
+ endContext(ElementContext.CODE_BLOCK);
+ }
}
@Override
public void blockquote(SinkEventAttributes attributes) {
- startContext(ElementContext.BLOCKQUOTE);
- writeUnescaped(BLOCKQUOTE_START_MARKUP);
+ if (elementContextStack.element().isHtml()) {
+ super.blockquote(attributes);
+ } else {
+ startContext(ElementContext.BLOCKQUOTE);
+ writeUnescaped(BLOCKQUOTE_START_MARKUP);
+ }
}
@Override
public void blockquote_() {
- endContext(ElementContext.BLOCKQUOTE);
+ if (elementContextStack.element().isHtml()) {
+ super.blockquote_();
+ } else {
+ endContext(ElementContext.BLOCKQUOTE);
+ }
}
@Override
@@ -696,58 +748,82 @@
@Override
public void table(SinkEventAttributes attributes) {
- ensureBlankLine();
- writeUnescaped(getLinePrefix());
+ if (elementContextStack.element().isHtml()) {
+ super.table(attributes);
+ } else {
+ ensureBlankLine();
+ writeUnescaped(getLinePrefix());
+ }
+ }
+
+ @Override
+ public void table_() {
+ if (elementContextStack.element().isHtml()) {
+ super.table_();
+ }
}
@Override
public void tableRows(int[] justification, boolean grid) {
- if (justification != null) {
- cellJustif = Arrays.stream(justification).boxed().collect(Collectors.toCollection(ArrayList::new));
+ if (elementContextStack.element().isHtml()) {
+ super.tableRows(justification, grid);
} else {
- cellJustif = new ArrayList<>();
+ if (justification != null) {
+ cellJustif = Arrays.stream(justification).boxed().collect(Collectors.toCollection(ArrayList::new));
+ } else {
+ cellJustif = new ArrayList<>();
+ }
+ // grid flag is not supported
+ isFirstTableRow = true;
}
- // grid flag is not supported
- isFirstTableRow = true;
}
@Override
public void tableRows_() {
- cellJustif = null;
+ if (elementContextStack.element().isHtml()) {
+ super.tableRows_();
+ } else {
+ cellJustif = null;
+ }
}
@Override
public void tableRow(SinkEventAttributes attributes) {
- startContext(ElementContext.TABLE_ROW);
- cellCount = 0;
+ if (elementContextStack.element().isHtml()) {
+ super.tableRow(attributes);
+ } else {
+ startContext(ElementContext.TABLE_ROW);
+ cellCount = 0;
+ }
}
@Override
public void tableRow_() {
- String buffer = consumeBuffer();
- endContext(ElementContext.TABLE_ROW);
- if (isFirstTableRow && !tableHeaderCellFlag) {
- // emit empty table header as this is mandatory for GFM table extension
- // (https://stackoverflow.com/a/17543474/5155923)
- writeEmptyTableHeader();
- writeTableDelimiterRow();
- tableHeaderCellFlag = false;
- isFirstTableRow = false;
- // afterwards emit the first row
+ if (elementContextStack.element().isHtml()) {
+ super.tableRow_();
+ } else {
+ String buffer = consumeBuffer();
+ endContext(ElementContext.TABLE_ROW);
+ if (isFirstTableRow && !tableHeaderCellFlag) {
+ // emit empty table header as this is mandatory for GFM table extension
+ // (https://stackoverflow.com/a/17543474/5155923)
+ writeEmptyTableHeader();
+ writeTableDelimiterRow();
+ tableHeaderCellFlag = false;
+ isFirstTableRow = false;
+ // afterwards emit the first row
+ }
+ writeUnescaped(TABLE_ROW_PREFIX);
+ writeUnescaped(buffer);
+ writeUnescaped(EOL);
+ if (isFirstTableRow) {
+ // emit delimiter row
+ writeTableDelimiterRow();
+ isFirstTableRow = false;
+ }
+ // only reset cell count if this is the last row
+ cellCount = 0;
}
-
- writeUnescaped(TABLE_ROW_PREFIX);
- writeUnescaped(buffer);
- writeUnescaped(EOL);
-
- if (isFirstTableRow) {
- // emit delimiter row
- writeTableDelimiterRow();
- isFirstTableRow = false;
- }
-
- // only reset cell count if this is the last row
- cellCount = 0;
}
private void writeEmptyTableHeader() {
@@ -789,30 +865,34 @@
@Override
public void tableCell(SinkEventAttributes attributes) {
- startContext(ElementContext.TABLE_CELL);
- if (attributes != null) {
- // evaluate alignment attributes
- final int cellJustification;
- if (attributes.containsAttributes(SinkEventAttributeSet.LEFT)) {
- cellJustification = Sink.JUSTIFY_LEFT;
- } else if (attributes.containsAttributes(SinkEventAttributeSet.RIGHT)) {
- cellJustification = Sink.JUSTIFY_RIGHT;
- } else if (attributes.containsAttributes(SinkEventAttributeSet.CENTER)) {
- cellJustification = Sink.JUSTIFY_CENTER;
- } else {
- cellJustification = -1;
- }
- if (cellJustification > -1) {
- if (cellJustif.size() > cellCount) {
- cellJustif.set(cellCount, cellJustification);
- } else if (cellJustif.size() == cellCount) {
- cellJustif.add(cellJustification);
+ if (elementContextStack.element().isHtml()) {
+ super.tableCell(attributes);
+ } else {
+ startContext(ElementContext.TABLE_CELL);
+ if (attributes != null) {
+ // evaluate alignment attributes
+ final int cellJustification;
+ if (attributes.containsAttributes(SinkEventAttributeSet.LEFT)) {
+ cellJustification = Sink.JUSTIFY_LEFT;
+ } else if (attributes.containsAttributes(SinkEventAttributeSet.RIGHT)) {
+ cellJustification = Sink.JUSTIFY_RIGHT;
+ } else if (attributes.containsAttributes(SinkEventAttributeSet.CENTER)) {
+ cellJustification = Sink.JUSTIFY_CENTER;
} else {
- // create non-existing justifications for preceding columns
- for (int precedingCol = cellJustif.size(); precedingCol < cellCount; precedingCol++) {
- cellJustif.add(Sink.JUSTIFY_DEFAULT);
+ cellJustification = -1;
+ }
+ if (cellJustification > -1) {
+ if (cellJustif.size() > cellCount) {
+ cellJustif.set(cellCount, cellJustification);
+ } else if (cellJustif.size() == cellCount) {
+ cellJustif.add(cellJustification);
+ } else {
+ // create non-existing justifications for preceding columns
+ for (int precedingCol = cellJustif.size(); precedingCol < cellCount; precedingCol++) {
+ cellJustif.add(Sink.JUSTIFY_DEFAULT);
+ }
+ cellJustif.add(cellJustification);
}
- cellJustif.add(cellJustification);
}
}
}
@@ -820,18 +900,30 @@
@Override
public void tableHeaderCell(SinkEventAttributes attributes) {
- tableCell(attributes);
- tableHeaderCellFlag = true;
+ if (elementContextStack.element().isHtml()) {
+ super.tableHeaderCell(attributes);
+ } else {
+ tableCell(attributes);
+ tableHeaderCellFlag = true;
+ }
}
@Override
public void tableCell_() {
- endTableCell();
+ if (elementContextStack.element().isHtml()) {
+ super.tableCell_();
+ } else {
+ endTableCell();
+ }
}
@Override
public void tableHeaderCell_() {
- endTableCell();
+ if (elementContextStack.element().isHtml()) {
+ super.tableHeaderCell_();
+ } else {
+ endTableCell();
+ }
}
/**
@@ -845,42 +937,76 @@
@Override
public void tableCaption(SinkEventAttributes attributes) {
- elementContextStack.add(ElementContext.TABLE_CAPTION);
+ if (elementContextStack.element().isHtml()) {
+ super.tableCaption(attributes);
+ } else {
+ elementContextStack.add(ElementContext.TABLE_CAPTION);
+ }
}
@Override
public void tableCaption_() {
- endContext(ElementContext.TABLE_CAPTION);
+ if (elementContextStack.element().isHtml()) {
+ super.tableCaption_();
+ } else {
+ endContext(ElementContext.TABLE_CAPTION);
+ }
}
@Override
public void figure(SinkEventAttributes attributes) {
- figureSrc = null;
- startContext(ElementContext.FIGURE);
+ if (elementContextStack.element().isHtml()) {
+ super.figure(attributes);
+ } else {
+ figureSrc = null;
+ startContext(ElementContext.FIGURE);
+ }
+ }
+
+ @Override
+ public void figureCaption(SinkEventAttributes attributes) {
+ if (elementContextStack.element().isHtml()) {
+ super.figureCaption(attributes);
+ }
+ }
+
+ @Override
+ public void figureCaption_() {
+ if (elementContextStack.element().isHtml()) {
+ super.figureCaption_();
+ }
}
@Override
public void figureGraphics(String name, SinkEventAttributes attributes) {
- figureSrc = name;
- // is it a standalone image (outside a figure)?
- if (elementContextStack.peek() != ElementContext.FIGURE) {
- Object alt = attributes.getAttribute(SinkEventAttributes.ALT);
- if (alt == null) {
- alt = "";
+ if (elementContextStack.element().isHtml()) {
+ super.figureGraphics(name, attributes);
+ } else {
+ figureSrc = name;
+ // is it a standalone image (outside a figure)?
+ if (elementContextStack.peek() != ElementContext.FIGURE) {
+ Object alt = attributes.getAttribute(SinkEventAttributes.ALT);
+ if (alt == null) {
+ alt = "";
+ }
+ writeImage(elementContextStack.element().escape(bufferingWriter, alt.toString()), name);
}
- writeImage(elementContextStack.element().escape(bufferingWriter, alt.toString()), name);
}
}
@Override
public void figure_() {
- StringBuilder buffer = getCurrentBuffer();
- String label = "";
- if (buffer != null) {
- label = buffer.toString();
+ if (elementContextStack.element().isHtml()) {
+ super.figure_();
+ } else {
+ StringBuilder buffer = getCurrentBuffer();
+ String label = "";
+ if (buffer != null) {
+ label = buffer.toString();
+ }
+ endContext(ElementContext.FIGURE);
+ writeImage(label, figureSrc);
}
- endContext(ElementContext.FIGURE);
- writeImage(label, figureSrc);
}
private void writeImage(String alt, String src) {
@@ -890,123 +1016,129 @@
}
public void anchor(String name, SinkEventAttributes attributes) {
- // emit html anchor as markdown does not support anchors
- MutableAttributeSet atts = SinkUtils.filterAttributes(attributes, SinkUtils.SINK_BASE_ATTRIBUTES);
-
- String id = name;
-
- if (!DoxiaUtils.isValidId(id)) {
- id = DoxiaUtils.encodeId(name);
-
- LOGGER.debug("{}Modified invalid anchor name '{}' to '{}'", getLocationLogPrefix(), name, id);
+ super.anchor(name, attributes);
+ if (!elementContextStack.element().isHtml()) {
+ // close anchor tag immediately otherwise markdown would not be allowed afterwards
+ writeUnescaped("</a>");
}
-
- MutableAttributeSet att = new SinkEventAttributeSet();
- att.addAttribute(SinkEventAttributes.ID, id);
- att.addAttributes(atts);
- StringBuilder htmlAnchor = new StringBuilder("<a");
- htmlAnchor.append(SinkUtils.getAttributeString(att));
- htmlAnchor.append(">");
- htmlAnchor.append("</a>"); // close anchor tag immediately otherwise markdown would not be allowed afterwards
- writeUnescaped(htmlAnchor.toString());
}
@Override
public void anchor_() {
- // anchor is always empty html element, i.e. already closed with anchor()
+ if (elementContextStack.element().isHtml()) {
+ super.anchor_();
+ } else {
+ // anchor is always empty html element, i.e. already closed with anchor()
+ }
}
public void link(String name, SinkEventAttributes attributes) {
- if (elementContextStack.element() == ElementContext.CODE_BLOCK) {
- LOGGER.warn("{}Ignoring unsupported link inside code block", getLocationLogPrefix());
- } else if (elementContextStack.element() == ElementContext.CODE_SPAN) {
- // emit link outside the code span, i.e. insert at the beginning of the buffer
- getCurrentBuffer().insert(0, LINK_START_1_MARKUP);
- linkName = name;
+ if (elementContextStack.element().isHtml()) {
+ super.link(name, attributes);
} else {
- writeUnescaped(LINK_START_1_MARKUP);
- linkName = name;
+ if (elementContextStack.element() == ElementContext.CODE_BLOCK) {
+ LOGGER.warn("{}Ignoring unsupported link inside code block", getLocationLogPrefix());
+ } else if (elementContextStack.element() == ElementContext.CODE_SPAN) {
+ // emit link outside the code span, i.e. insert at the beginning of the buffer
+ getCurrentBuffer().insert(0, LINK_START_1_MARKUP);
+ linkName = name;
+ } else {
+ writeUnescaped(LINK_START_1_MARKUP);
+ linkName = name;
+ }
}
}
@Override
public void link_() {
- if (elementContextStack.element() == ElementContext.CODE_BLOCK) {
- return;
- } else if (elementContextStack.element() == ElementContext.CODE_SPAN) {
- // defer emitting link end markup until inline_() is called
- StringBuilder linkEndMarkup = new StringBuilder();
- linkEndMarkup.append(LINK_START_2_MARKUP);
- linkEndMarkup.append(linkName);
- linkEndMarkup.append(LINK_END_MARKUP);
- Queue<String> endMarkups = new LinkedList<>(inlineStack.poll());
- endMarkups.add(linkEndMarkup.toString());
- inlineStack.add(endMarkups);
+ if (elementContextStack.element().isHtml()) {
+ super.link_();
} else {
- writeUnescaped(LINK_START_2_MARKUP + linkName + LINK_END_MARKUP);
+ if (elementContextStack.element() == ElementContext.CODE_BLOCK) {
+ return;
+ } else if (elementContextStack.element() == ElementContext.CODE_SPAN) {
+ // defer emitting link end markup until inline_() is called
+ StringBuilder linkEndMarkup = new StringBuilder();
+ linkEndMarkup.append(LINK_START_2_MARKUP);
+ linkEndMarkup.append(linkName);
+ linkEndMarkup.append(LINK_END_MARKUP);
+ Queue<String> endMarkups = new LinkedList<>(inlineStack.poll());
+ endMarkups.add(linkEndMarkup.toString());
+ inlineStack.add(endMarkups);
+ } else {
+ writeUnescaped(LINK_START_2_MARKUP + linkName + LINK_END_MARKUP);
+ }
+ linkName = null;
}
- linkName = null;
}
@Override
public void inline(SinkEventAttributes attributes) {
- Queue<String> endMarkups = Collections.asLifoQueue(new LinkedList<>());
+ if (elementContextStack.element().isHtml()) {
+ super.inline(attributes);
+ } else {
+ Queue<String> endMarkups = Collections.asLifoQueue(new LinkedList<>());
- boolean requiresHtml = elementContextStack.element() == ElementContext.HTML_BLOCK;
- if (attributes != null
- && elementContextStack.element() != ElementContext.CODE_BLOCK
- && elementContextStack.element() != ElementContext.CODE_SPAN) {
- // code excludes other styles in markdown
- if (attributes.containsAttribute(SinkEventAttributes.SEMANTICS, "code")
- || attributes.containsAttribute(SinkEventAttributes.SEMANTICS, "monospaced")
- || attributes.containsAttribute(SinkEventAttributes.STYLE, "monospaced")) {
- if (requiresHtml) {
- writeUnescaped("<code>");
- endMarkups.add("</code>");
- } else {
- startContext(ElementContext.CODE_SPAN);
- writeUnescaped(MONOSPACED_START_MARKUP);
- endMarkups.add(MONOSPACED_END_MARKUP);
- }
- } else {
- // in XHTML "<em>" is used, but some tests still rely on the outdated "<italic>"
- if (attributes.containsAttribute(SinkEventAttributes.SEMANTICS, "emphasis")
- || attributes.containsAttribute(SinkEventAttributes.SEMANTICS, "italic")
- || attributes.containsAttribute(SinkEventAttributes.STYLE, "italic")) {
+ boolean requiresHtml = elementContextStack.element() == ElementContext.HTML_BLOCK;
+ if (attributes != null
+ && elementContextStack.element() != ElementContext.CODE_BLOCK
+ && elementContextStack.element() != ElementContext.CODE_SPAN) {
+ // code excludes other styles in markdown
+ if (attributes.containsAttribute(SinkEventAttributes.SEMANTICS, "code")
+ || attributes.containsAttribute(SinkEventAttributes.SEMANTICS, "monospaced")
+ || attributes.containsAttribute(SinkEventAttributes.STYLE, "monospaced")) {
if (requiresHtml) {
- writeUnescaped("<em>");
- endMarkups.add("</em>");
+ writeUnescaped("<code>");
+ endMarkups.add("</code>");
} else {
- writeUnescaped(ITALIC_START_MARKUP);
- endMarkups.add(ITALIC_END_MARKUP);
+ startContext(ElementContext.CODE_SPAN);
+ writeUnescaped(MONOSPACED_START_MARKUP);
+ endMarkups.add(MONOSPACED_END_MARKUP);
}
- }
- // in XHTML "<strong>" is used, but some tests still rely on the outdated "<bold>"
- if (attributes.containsAttribute(SinkEventAttributes.SEMANTICS, "strong")
- || attributes.containsAttribute(SinkEventAttributes.SEMANTICS, "bold")
- || attributes.containsAttribute(SinkEventAttributes.STYLE, "bold")) {
- if (requiresHtml) {
- writeUnescaped("<strong>");
- endMarkups.add("</strong>");
- } else {
- writeUnescaped(BOLD_START_MARKUP);
- endMarkups.add(BOLD_END_MARKUP);
+ } else {
+ // in XHTML "<em>" is used, but some tests still rely on the outdated "<italic>"
+ if (attributes.containsAttribute(SinkEventAttributes.SEMANTICS, "emphasis")
+ || attributes.containsAttribute(SinkEventAttributes.SEMANTICS, "italic")
+ || attributes.containsAttribute(SinkEventAttributes.STYLE, "italic")) {
+ if (requiresHtml) {
+ writeUnescaped("<em>");
+ endMarkups.add("</em>");
+ } else {
+ writeUnescaped(ITALIC_START_MARKUP);
+ endMarkups.add(ITALIC_END_MARKUP);
+ }
+ }
+ // in XHTML "<strong>" is used, but some tests still rely on the outdated "<bold>"
+ if (attributes.containsAttribute(SinkEventAttributes.SEMANTICS, "strong")
+ || attributes.containsAttribute(SinkEventAttributes.SEMANTICS, "bold")
+ || attributes.containsAttribute(SinkEventAttributes.STYLE, "bold")) {
+ if (requiresHtml) {
+ writeUnescaped("<strong>");
+ endMarkups.add("</strong>");
+ } else {
+ writeUnescaped(BOLD_START_MARKUP);
+ endMarkups.add(BOLD_END_MARKUP);
+ }
}
}
}
+ inlineStack.add(endMarkups);
}
- inlineStack.add(endMarkups);
}
@Override
public void inline_() {
- for (String endMarkup : inlineStack.remove()) {
- if (endMarkup.equals(MONOSPACED_END_MARKUP)) {
- String buffer = getCurrentBuffer().toString();
- endContext(ElementContext.CODE_SPAN);
- writeUnescaped(buffer);
+ if (elementContextStack.element().isHtml()) {
+ super.inline_();
+ } else {
+ for (String endMarkup : inlineStack.remove()) {
+ if (endMarkup.equals(MONOSPACED_END_MARKUP)) {
+ String buffer = getCurrentBuffer().toString();
+ endContext(ElementContext.CODE_SPAN);
+ writeUnescaped(buffer);
+ }
+ writeUnescaped(endMarkup);
}
- writeUnescaped(endMarkup);
}
}
@@ -1057,27 +1189,32 @@
@Override
public void text(String text, SinkEventAttributes attributes) {
- if (attributes != null) {
- inline(attributes);
- }
- ElementContext currentContext = elementContextStack.element();
- if (currentContext == ElementContext.TABLE_CAPTION) {
- // table caption cannot even be emitted via XHTML in markdown as there is no suitable location
- LOGGER.warn("{}Ignoring unsupported table caption in Markdown", getLocationLogPrefix());
+ if (elementContextStack.element().isHtml()) {
+ super.text(text, attributes);
} else {
- String unifiedText = currentContext.escape(bufferingWriter, unifyEOLs(text));
- // ignore newlines only, because those are emitted often coming from linebreaks in HTML with no semantical
- // meaning
- if (!unifiedText.equals(EOL)) {
- String prefix = getLinePrefix();
- if (prefix.length() > 0) {
- unifiedText = unifiedText.replaceAll(EOL, EOL + prefix);
- }
+ if (attributes != null) {
+ inline(attributes);
}
- writeUnescaped(unifiedText);
- }
- if (attributes != null) {
- inline_();
+ ElementContext currentContext = elementContextStack.element();
+ if (currentContext == ElementContext.TABLE_CAPTION) {
+ // table caption cannot even be emitted via XHTML in markdown as there is no suitable location
+ LOGGER.warn("{}Ignoring unsupported table caption in Markdown", getLocationLogPrefix());
+ } else {
+ String unifiedText = currentContext.escape(bufferingWriter, unifyEOLs(text));
+ // ignore newlines only, because those are emitted often coming from linebreaks in HTML with no
+ // semantical
+ // meaning
+ if (!unifiedText.equals(EOL)) {
+ String prefix = getLinePrefix();
+ if (prefix.length() > 0) {
+ unifiedText = unifiedText.replaceAll(EOL, EOL + prefix);
+ }
+ }
+ writeUnescaped(unifiedText);
+ }
+ if (attributes != null) {
+ inline_();
+ }
}
}
@@ -1086,16 +1223,6 @@
writeUnescaped(text);
}
- @Override
- public void comment(String comment) {
- comment(comment, false);
- }
-
- @Override
- public void comment(String comment, boolean endsWithLineBreak) {
- rawText(Xhtml5BaseSink.encodeAsHtmlComment(comment, endsWithLineBreak, getLocationLogPrefix()));
- }
-
/**
* {@inheritDoc}
*
diff --git a/doxia-modules/doxia-module-markdown/src/main/java/org/apache/maven/doxia/module/markdown/MarkdownSinkFactory.java b/doxia-modules/doxia-module-markdown/src/main/java/org/apache/maven/doxia/module/markdown/MarkdownSinkFactory.java
index 6c0b842..4703a57 100644
--- a/doxia-modules/doxia-module-markdown/src/main/java/org/apache/maven/doxia/module/markdown/MarkdownSinkFactory.java
+++ b/doxia-modules/doxia-module-markdown/src/main/java/org/apache/maven/doxia/module/markdown/MarkdownSinkFactory.java
@@ -35,6 +35,6 @@
protected Sink createSink(Writer writer, String encoding) {
// encoding can safely be ignored since it isn't written into the generated Markdown source
- return new MarkdownSink(writer);
+ return MarkdownSink.newInstance(writer);
}
}
diff --git a/doxia-modules/doxia-module-markdown/src/test/java/org/apache/maven/doxia/module/markdown/MarkdownSinkTest.java b/doxia-modules/doxia-module-markdown/src/test/java/org/apache/maven/doxia/module/markdown/MarkdownSinkTest.java
index f8b847c..f2f7850 100644
--- a/doxia-modules/doxia-module-markdown/src/test/java/org/apache/maven/doxia/module/markdown/MarkdownSinkTest.java
+++ b/doxia-modules/doxia-module-markdown/src/test/java/org/apache/maven/doxia/module/markdown/MarkdownSinkTest.java
@@ -51,7 +51,7 @@
}
protected Sink createSink(Writer writer) {
- return new MarkdownSink(writer);
+ return MarkdownSink.newInstance(writer);
}
protected boolean isXmlSink() {
@@ -79,15 +79,15 @@
}
protected String getArticleBlock() {
- return "";
+ return "<article></article>";
}
protected String getNavigationBlock() {
- return "";
+ return "<nav></nav>";
}
protected String getSidebarBlock() {
- return "";
+ return "<aside></aside>";
}
protected String getSectionBlock(String title, int level) {
@@ -119,15 +119,16 @@
}
protected String getHeaderBlock() {
+ // never emitted by Markdown sink as otherwise too often markdown could not be used
return "";
}
protected String getContentBlock() {
- return "";
+ return "<main>" + EOL + "<div class=\"content\"></div></main>";
}
protected String getFooterBlock() {
- return "";
+ return "<footer></footer>";
}
protected String getListBlock(String item) {
@@ -194,15 +195,15 @@
}
protected String getDataBlock(String value, String text) {
- return text;
+ return "<data value=\"" + value + "\">" + text + "</data>";
}
protected String getTimeBlock(String datetime, String text) {
- return text;
+ return "<time datetime=\"" + datetime + "\">" + text + "</time>";
}
protected String getAddressBlock(String text) {
- return text;
+ return "<address>" + text + "</address>";
}
protected String getBlockquoteBlock(String text) {
@@ -210,7 +211,7 @@
}
protected String getDivisionBlock(String text) {
- return text;
+ return "<div>" + text + "</div>";
}
protected String getVerbatimBlock(String text) {
@@ -275,7 +276,7 @@
}
protected String getLineBreakOpportunityBlock() {
- return "";
+ return "<wbr />";
}
protected String getNonBreakingSpaceBlock() {
@@ -576,4 +577,26 @@
+ "- item 3" + EOL;
assertEquals(expected, getSinkContent());
}
+
+ @Test
+ public void testLinkInsideHtmlSection() {
+ try (Sink sink = getSink()) {
+ sink.definitionList();
+ sink.definedTerm();
+ sink.text("question1");
+ sink.definedTerm_();
+ sink.definition();
+ sink.link("#top");
+ sink.text("[top]");
+ sink.link_();
+ sink.definition_();
+ sink.definitionList_();
+ }
+ String expected = "<dl>" + EOL
+ + "<dt>question1</dt>" + EOL
+ + "<dd><a href=\"#top\">[top]</a></dd>" + EOL
+ + "</dl>" + EOL
+ + EOL;
+ assertEquals(expected, getSinkContent());
+ }
}