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 87658e824..5e5cc0c24 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 @@ public void content() { /** * Checks that the sequence [footer(), footer_()], * 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 1464343c6..e40d59dd8 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. *
* Note: 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 @@ public class MarkdownSink extends AbstractTextSink implements MarkdownMarkup { private final LastTwoLinesBufferingWriter bufferingWriter; /** Keep track of end markup for inline events. */ - protected Queue> inlineStack = Collections.asLifoQueue(new LinkedList<>()); + protected Queue> inlineStack; /** The context of the surrounding elements as stack (LIFO) */ - protected Queue elementContextStack = Collections.asLifoQueue(new LinkedList<>()); + protected Queue elementContextStack; private String figureSrc; @@ -220,6 +216,13 @@ boolean isBlock() { return type == Type.CONTAINER_BLOCK || type == Type.LEAF_BLOCK; } + /** + * + * @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 @@ -332,14 +335,20 @@ private String escapeForTableCell(LastTwoLinesBufferingWriter writer, String tex // 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. Should 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 @@ protected void init() { 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); } @@ -539,6 +548,26 @@ public void date_() { } } + @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); @@ -556,6 +585,13 @@ public void sectionTitle_(int level) { } } + @Override + public void list(SinkEventAttributes attributes) { + if (elementContextStack.element().isHtml()) { + super.list(attributes); + } + } + @Override public void list_() { ensureBeginningOfLine(); @@ -641,9 +677,9 @@ public void pageBreak() { @Override public void paragraph(SinkEventAttributes attributes) { + ensureBlankLine(); // ignore paragraphs outside container contexts if (elementContextStack.element().isContainer()) { - ensureBlankLine(); writeUnescaped(getLinePrefix()); } } @@ -658,33 +694,49 @@ public void paragraph_() { @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 @@ public void horizontalRule(SinkEventAttributes attributes) { @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 - } - - writeUnescaped(TABLE_ROW_PREFIX); - writeUnescaped(buffer); - writeUnescaped(EOL); - - if (isFirstTableRow) { - // emit delimiter row - writeTableDelimiterRow(); - isFirstTableRow = false; + 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; } - - // only reset cell count if this is the last row - cellCount = 0; } private void writeEmptyTableHeader() { @@ -789,30 +865,34 @@ private void writeTableDelimiterRow() { @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 @@ public void tableCell(SinkEventAttributes attributes) { @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 @@ private void endTableCell() { @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 @@ private void writeImage(String alt, String src) { } 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(""); } - - MutableAttributeSet att = new SinkEventAttributeSet(); - att.addAttribute(SinkEventAttributes.ID, id); - att.addAttributes(atts); - StringBuilder htmlAnchor = new StringBuilder(""); - htmlAnchor.append(""); // 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 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 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 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(""); - endMarkups.add(""); - } else { - startContext(ElementContext.CODE_SPAN); - writeUnescaped(MONOSPACED_START_MARKUP); - endMarkups.add(MONOSPACED_END_MARKUP); - } - } else { - // in XHTML "" is used, but some tests still rely on the outdated "" - if (attributes.containsAttribute(SinkEventAttributes.SEMANTICS, "emphasis") - || attributes.containsAttribute(SinkEventAttributes.SEMANTICS, "italic") - || attributes.containsAttribute(SinkEventAttributes.STYLE, "italic")) { + if (elementContextStack.element().isHtml()) { + super.inline(attributes); + } else { + Queue 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(""); - endMarkups.add(""); + writeUnescaped(""); + endMarkups.add(""); } 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 "" is used, but some tests still rely on the outdated "" - if (attributes.containsAttribute(SinkEventAttributes.SEMANTICS, "strong") - || attributes.containsAttribute(SinkEventAttributes.SEMANTICS, "bold") - || attributes.containsAttribute(SinkEventAttributes.STYLE, "bold")) { - if (requiresHtml) { - writeUnescaped(""); - endMarkups.add(""); - } else { - writeUnescaped(BOLD_START_MARKUP); - endMarkups.add(BOLD_END_MARKUP); + } else { + // in XHTML "" is used, but some tests still rely on the outdated "" + if (attributes.containsAttribute(SinkEventAttributes.SEMANTICS, "emphasis") + || attributes.containsAttribute(SinkEventAttributes.SEMANTICS, "italic") + || attributes.containsAttribute(SinkEventAttributes.STYLE, "italic")) { + if (requiresHtml) { + writeUnescaped(""); + endMarkups.add(""); + } else { + writeUnescaped(ITALIC_START_MARKUP); + endMarkups.add(ITALIC_END_MARKUP); + } + } + // in XHTML "" is used, but some tests still rely on the outdated "" + if (attributes.containsAttribute(SinkEventAttributes.SEMANTICS, "strong") + || attributes.containsAttribute(SinkEventAttributes.SEMANTICS, "bold") + || attributes.containsAttribute(SinkEventAttributes.STYLE, "bold")) { + if (requiresHtml) { + writeUnescaped(""); + endMarkups.add(""); + } 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 @@ public void nonBreakingSpace() { @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); + } + 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_(); } - writeUnescaped(unifiedText); - } - if (attributes != null) { - inline_(); } } @@ -1086,16 +1223,6 @@ public void rawText(String text) { 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 6c0b84216..4703a5742 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 @@ public class MarkdownSinkFactory extends AbstractTextSinkFactory { 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 f8b847c3c..f2f785048 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 String outputExtension() { } protected Sink createSink(Writer writer) { - return new MarkdownSink(writer); + return MarkdownSink.newInstance(writer); } protected boolean isXmlSink() { @@ -79,15 +79,15 @@ protected String getBodyBlock() { } protected String getArticleBlock() { - return ""; + return "
"; } protected String getNavigationBlock() { - return ""; + return ""; } protected String getSidebarBlock() { - return ""; + return ""; } protected String getSectionBlock(String title, int level) { @@ -119,15 +119,16 @@ protected String getSection6Block(String title) { } protected String getHeaderBlock() { + // never emitted by Markdown sink as otherwise too often markdown could not be used return ""; } protected String getContentBlock() { - return ""; + return "
" + EOL + "
"; } protected String getFooterBlock() { - return ""; + return "
"; } protected String getListBlock(String item) { @@ -194,15 +195,15 @@ protected String getParagraphBlock(String text) { } protected String getDataBlock(String value, String text) { - return text; + return "" + text + ""; } protected String getTimeBlock(String datetime, String text) { - return text; + return ""; } protected String getAddressBlock(String text) { - return text; + return "
" + text + "
"; } protected String getBlockquoteBlock(String text) { @@ -210,7 +211,7 @@ protected String getBlockquoteBlock(String text) { } protected String getDivisionBlock(String text) { - return text; + return "
" + text + "
"; } protected String getVerbatimBlock(String text) { @@ -275,7 +276,7 @@ protected String getLineBreakBlock() { } protected String getLineBreakOpportunityBlock() { - return ""; + return ""; } protected String getNonBreakingSpaceBlock() { @@ -576,4 +577,26 @@ void nestedListBeingTight() { + "- 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 = "
" + EOL + + "
question1
" + EOL + + "
[top]
" + EOL + + "
" + EOL + + EOL; + assertEquals(expected, getSinkContent()); + } }