diff --git a/src/main/java/io/github/robwin/markup/builder/MarkupTableColumn.java b/src/main/java/io/github/robwin/markup/builder/MarkupTableColumn.java index 5a041574..2ac5cb96 100644 --- a/src/main/java/io/github/robwin/markup/builder/MarkupTableColumn.java +++ b/src/main/java/io/github/robwin/markup/builder/MarkupTableColumn.java @@ -49,7 +49,7 @@ public class MarkupTableColumn { * Set column width ratio for this column.
* Limited support : Markdown does not support column width specifiers and will ignore {@code widthRatio}. * - * @param widthRatio width ratio integer value [0-100]. Accept relative width specifiers [0-9] for languages supporting it. + * @param widthRatio width ratio integer value [0-100]. Accept relative width specifiers (e.g.: 1, 2, 3, .. with Sum{i=0->nbCols}(widthRatio(i)) != 100). * @return this builder */ public MarkupTableColumn withWidthRatio(Integer widthRatio) { diff --git a/src/main/java/io/github/robwin/markup/builder/internal/AbstractMarkupDocBuilder.java b/src/main/java/io/github/robwin/markup/builder/internal/AbstractMarkupDocBuilder.java index f39d47ad..2497248c 100644 --- a/src/main/java/io/github/robwin/markup/builder/internal/AbstractMarkupDocBuilder.java +++ b/src/main/java/io/github/robwin/markup/builder/internal/AbstractMarkupDocBuilder.java @@ -228,18 +228,31 @@ public abstract class AbstractMarkupDocBuilder implements MarkupDocBuilder { return listing(replaceNewLines(text), null); } - protected void delimitedBlockText(Markup markup, String text) { - if (markup != null) - documentBuilder.append(markup).append(newLine); + protected void delimitedBlockText(Markup begin, String text, Markup end) { + Validate.notBlank(text, "text must not be null"); + if (!StringUtils.isBlank(begin.toString())) + documentBuilder.append(begin).append(newLine); documentBuilder.append(replaceNewLines(text)).append(newLine); - if (markup != null) - documentBuilder.append(markup).append(newLine); + if (!StringUtils.isBlank(end.toString())) + documentBuilder.append(end).append(newLine); documentBuilder.append(newLine); } - protected void delimitedTextWithoutLineBreaks(Markup markup, String text) { + protected void delimitedTextWithoutLineBreaks(Markup begin, String text, Markup end) { Validate.notBlank(text, "text must not be null"); - documentBuilder.append(markup).append(replaceNewLines(text)).append(markup); + if (!StringUtils.isBlank(begin.toString())) + documentBuilder.append(begin); + documentBuilder.append(replaceNewLines(text)); + if (!StringUtils.isBlank(end.toString())) + documentBuilder.append(end); + } + + protected void delimitedBlockText(Markup markup, String text) { + delimitedBlockText(markup, text, markup); + } + + protected void delimitedTextWithoutLineBreaks(Markup markup, String text) { + delimitedTextWithoutLineBreaks(markup, text, markup); } protected void boldText(Markup markup, String text) { @@ -255,7 +268,7 @@ public abstract class AbstractMarkupDocBuilder implements MarkupDocBuilder { } @Override - public MarkupDocBuilder boldTextLine(String text) { + public MarkupDocBuilder boldTextLine(String text) { return boldTextLine(text, LINE_BREAK_DEFAULT); } @@ -457,12 +470,12 @@ public abstract class AbstractMarkupDocBuilder implements MarkupDocBuilder { logger.info("Markup document written to: {}", file); } } - - public String replaceNewLines(String content){ + + public String replaceNewLines(String content) { return content.replaceAll(NEW_LINES, newLine); } - public String replaceNewLinesWithWhiteSpace(String content){ + public String replaceNewLinesWithWhiteSpace(String content) { return content.replaceAll(NEW_LINES, WHITESPACE); } diff --git a/src/main/java/io/github/robwin/markup/builder/internal/asciidoc/AsciiDocBuilder.java b/src/main/java/io/github/robwin/markup/builder/internal/asciidoc/AsciiDocBuilder.java index aa1d9928..4ba9e88b 100644 --- a/src/main/java/io/github/robwin/markup/builder/internal/asciidoc/AsciiDocBuilder.java +++ b/src/main/java/io/github/robwin/markup/builder/internal/asciidoc/AsciiDocBuilder.java @@ -92,7 +92,6 @@ public class AsciiDocBuilder extends AbstractMarkupDocBuilder { delimitedBlockText(new Markup() { public String toString() { - assert (BLOCK_STYLE.containsKey(style)); return BLOCK_STYLE.get(style); } }, text); diff --git a/src/main/java/io/github/robwin/markup/builder/internal/confluenceMarkup/ConfluenceMarkup.java b/src/main/java/io/github/robwin/markup/builder/internal/confluenceMarkup/ConfluenceMarkup.java index 86cf0b34..5fbb9e4e 100644 --- a/src/main/java/io/github/robwin/markup/builder/internal/confluenceMarkup/ConfluenceMarkup.java +++ b/src/main/java/io/github/robwin/markup/builder/internal/confluenceMarkup/ConfluenceMarkup.java @@ -25,6 +25,13 @@ import io.github.robwin.markup.builder.internal.Markup; * @author Robert Winkler */ public enum ConfluenceMarkup implements Markup { + TABLE_COLUMN_DELIMITER("|"), + TABLE_COLUMN_DELIMITER_ESCAPE("\\|"), + BOLD("*"), + ITALIC("_"), + LIST_ENTRY("* "), + ANCHOR_START("{anchor:"), + ANCHOR_END("}"), FILE_EXTENSION("txt"), SPACE_ESCAPE("_"), LINE_BREAK("\\\\ "); diff --git a/src/main/java/io/github/robwin/markup/builder/internal/confluenceMarkup/ConfluenceMarkupBuilder.java b/src/main/java/io/github/robwin/markup/builder/internal/confluenceMarkup/ConfluenceMarkupBuilder.java index dce8c763..76c23e99 100644 --- a/src/main/java/io/github/robwin/markup/builder/internal/confluenceMarkup/ConfluenceMarkupBuilder.java +++ b/src/main/java/io/github/robwin/markup/builder/internal/confluenceMarkup/ConfluenceMarkupBuilder.java @@ -23,18 +23,45 @@ import io.github.robwin.markup.builder.MarkupBlockStyle; import io.github.robwin.markup.builder.MarkupDocBuilder; import io.github.robwin.markup.builder.MarkupTableColumn; import io.github.robwin.markup.builder.internal.AbstractMarkupDocBuilder; +import io.github.robwin.markup.builder.internal.Markup; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.Validate; import java.io.IOException; import java.io.Reader; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.regex.Pattern; -import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; public final class ConfluenceMarkupBuilder extends AbstractMarkupDocBuilder { private static final Pattern TITLE_PATTERN = Pattern.compile("^h([0-9])\\.\\s+(.*)$"); + private static final String TITLE_FORMAT = "h%d. %s"; + + /** + * Associate macro name to block style.
+ * ending ':' means the macro supports title attribute.
+ * '>ADMONITION_BLOCK' means value should refer to {@link #ADMONITION_BLOCK_STYLE}. + */ + private static final Map BLOCK_STYLE = new HashMap() {{ + put(MarkupBlockStyle.EXAMPLE, ">ADMONITION_BLOCK"); + put(MarkupBlockStyle.LISTING, "code:"); + put(MarkupBlockStyle.LITERAL, "noformat"); + put(MarkupBlockStyle.PASSTHROUGH, "html"); + put(MarkupBlockStyle.SIDEBAR, ">ADMONITION_BLOCK"); + }}; + + private static final Map ADMONITION_BLOCK_STYLE = new HashMap() {{ + put(null, "panel:"); + put(MarkupAdmonition.CAUTION, "note:"); + put(MarkupAdmonition.IMPORTANT, "alert:"); + put(MarkupAdmonition.NOTE, "info:"); + put(MarkupAdmonition.TIP, "tip:"); + put(MarkupAdmonition.WARNING, "warning:"); + }}; public ConfluenceMarkupBuilder() { super(); @@ -56,107 +83,123 @@ public final class ConfluenceMarkupBuilder extends AbstractMarkupDocBuilder { @Override public MarkupDocBuilder documentTitle(String title) { + Validate.notBlank(title, "title must not be null"); + documentBuilder.append(String.format(TITLE_FORMAT, 0, title)); + documentBuilder.append(newLine).append(newLine); return this; } @Override public MarkupDocBuilder sectionTitleWithAnchorLevel(int level, String title, String anchor) { + Validate.notBlank(title, "title must not be null"); + Validate.inclusiveBetween(1, MAX_TITLE_LEVEL, level); + documentBuilder.append(newLine); - documentBuilder.append("h").append(level + 1).append(". ").append(title); + documentBuilder.append(String.format(TITLE_FORMAT, level + 1, title)); if (isNotBlank(anchor)) { - documentBuilder.append(" {anchor:").append(anchor).append("}"); + documentBuilder.append(" "); + anchor(anchor); + documentBuilder.append(newLine); } - documentBuilder.append(newLine).append(newLine); - return this; - } - - @Override - public MarkupDocBuilder block(String text, MarkupBlockStyle style, String title, MarkupAdmonition admonition) { - switch (style) { - case SIDEBAR: - documentBuilder.append(newLine).append("{quote}").append(newLine); - if (isNotBlank(title)) { - documentBuilder.append(title); - newLine(true); - } - documentBuilder.append(text); - documentBuilder.append(newLine).append("{quote}").append(newLine); - break; - case EXAMPLE: - case LITERAL: - documentBuilder.append(newLine); - if (isBlank(title)) { - documentBuilder.append("{panel}"); - } else { - documentBuilder.append("{panel:title=").append(title).append("}"); - } - documentBuilder.append(newLine); - documentBuilder.append(text); - documentBuilder.append(newLine).append("{panel}").append(newLine); - break; - case LISTING: - documentBuilder.append(newLine); - if (isBlank(title)) { - documentBuilder.append("{code}"); - } else { - documentBuilder.append("{code:title=").append(title).append("}"); - } - documentBuilder.append(newLine); - documentBuilder.append(text); - documentBuilder.append(newLine).append("{code}").append(newLine); - break; - case PASSTHROUGH: - documentBuilder.append(newLine).append("{noformat}").append(newLine); - if (isNotBlank(title)) { - documentBuilder.append(title); - documentBuilder.append(newLine); - } - documentBuilder.append(text); - documentBuilder.append(newLine).append("{noformat}").append(newLine); - break; - } - return this; - } - - @Override - public MarkupDocBuilder listing(String text, String language) { documentBuilder.append(newLine); - if (isBlank(language)) { - documentBuilder.append("{code}"); + return this; + } + + @Override + public MarkupDocBuilder block(String text, final MarkupBlockStyle style, String title, MarkupAdmonition admonition) { + + String block = BLOCK_STYLE.get(style); + + boolean admonitionBlock = block.equals(">ADMONITION_BLOCK"); + if (admonitionBlock) { + block = ADMONITION_BLOCK_STYLE.get(admonition); + } + + boolean supportTitle = false; + if (block.endsWith(":")) { + supportTitle = true; + block = StringUtils.stripEnd(block, ":"); + } + + String titleString = null; + if (admonition != null && !admonitionBlock) { + titleString = StringUtils.capitalize(admonition.name().toLowerCase()); + } + if (title != null) { + titleString = (titleString == null ? "" : titleString + " | ") + title; + } + + final String finalBlock = block; + Markup blockMarkup = new Markup() { + @Override + public String toString() { + return String.format("{%s}", finalBlock); + } + }; + + if (!supportTitle) { + if (titleString != null) + documentBuilder.append(titleString).append(" : ").append(newLine); + delimitedBlockText(blockMarkup, text); } else { - documentBuilder.append("{code:language=").append(language).append("}"); + final String finalTitleString = titleString; + delimitedBlockText(new Markup() { + @Override + public String toString() { + if (finalTitleString == null) + return String.format("{%s}", finalBlock); + else + return String.format("{%s:title=%s}", finalBlock, finalTitleString); + } + }, text, blockMarkup); + } + + return this; + } + + @Override + public MarkupDocBuilder listing(String text, final String language) { + Markup blockMarkup = new Markup() { + @Override + public String toString() { + return String.format("{%s}", "code"); + } + }; + + if (language != null) { + delimitedBlockText(new Markup() { + @Override + public String toString() { + return String.format("{code:language=%s}", language); + } + }, text, blockMarkup); + } else { + delimitedBlockText(blockMarkup, text); } - documentBuilder.append(newLine); - documentBuilder.append(text); - documentBuilder.append(newLine).append("{code}").append(newLine).append(newLine); return this; } @Override public MarkupDocBuilder boldText(String text) { - documentBuilder.append("*").append(text).append("*"); + boldText(ConfluenceMarkup.BOLD, text); return this; } @Override public MarkupDocBuilder italicText(String text) { - documentBuilder.append("_").append(text).append("_"); + italicText(ConfluenceMarkup.ITALIC, text); return this; } @Override public MarkupDocBuilder unorderedList(List list) { - documentBuilder.append(newLine).append(newLine); - for (String item : list) { - documentBuilder.append("* ").append(item).append(newLine); - } - documentBuilder.append(newLine); + unorderedList(ConfluenceMarkup.LIST_ENTRY, list); return this; } @Override public MarkupDocBuilder unorderedListItem(String item) { - documentBuilder.append("* ").append(item); + unorderedListItem(ConfluenceMarkup.LIST_ENTRY, item); return this; } @@ -172,9 +215,9 @@ public final class ConfluenceMarkupBuilder extends AbstractMarkupDocBuilder { } if (cells != null) { for (List row : cells) { - documentBuilder.append("|"); + documentBuilder.append(ConfluenceMarkup.TABLE_COLUMN_DELIMITER); for (String cell : row) { - documentBuilder.append(escapeCellContent(cell)).append("|"); + documentBuilder.append(escapeCellContent(cell)).append(ConfluenceMarkup.TABLE_COLUMN_DELIMITER); } documentBuilder.append(newLine); } @@ -186,7 +229,8 @@ public final class ConfluenceMarkupBuilder extends AbstractMarkupDocBuilder { if (content == null) { return " "; } - return content.replace("|", "\\|").replace(newLine, ConfluenceMarkup.LINE_BREAK.toString()); + return content.replace(ConfluenceMarkup.TABLE_COLUMN_DELIMITER.toString(), ConfluenceMarkup.TABLE_COLUMN_DELIMITER_ESCAPE.toString()) + .replace(newLine, ConfluenceMarkup.LINE_BREAK.toString()); } private String normalizeAnchor(String anchor) { @@ -196,13 +240,13 @@ public final class ConfluenceMarkupBuilder extends AbstractMarkupDocBuilder { @Override public MarkupDocBuilder anchor(String anchor, String text) { - documentBuilder.append("{anchor:").append(normalizeAnchor(anchor)).append("}"); + documentBuilder.append(ConfluenceMarkup.ANCHOR_START).append(normalizeAnchor(anchor)).append(ConfluenceMarkup.ANCHOR_END); return this; } @Override public MarkupDocBuilder crossReference(String document, String anchor, String text) { - crossReferenceRaw(document, anchor, text); + crossReferenceRaw(document, normalizeAnchor(anchor), text); return this; } @@ -212,7 +256,7 @@ public final class ConfluenceMarkupBuilder extends AbstractMarkupDocBuilder { if (isNotBlank(document)) { documentBuilder.append(document); } - documentBuilder.append("#").append(normalizeAnchor(anchor)); + documentBuilder.append("#").append(anchor); if (isNotBlank(text)) { documentBuilder.append("|").append(text); } @@ -228,7 +272,7 @@ public final class ConfluenceMarkupBuilder extends AbstractMarkupDocBuilder { @Override public MarkupDocBuilder importMarkup(Reader markupText, int levelOffset) throws IOException { - importMarkupStyle2(TITLE_PATTERN, "h%d. %s", false, markupText, levelOffset); + importMarkupStyle2(TITLE_PATTERN, TITLE_FORMAT, false, markupText, levelOffset); return this; } diff --git a/src/main/java/io/github/robwin/markup/builder/internal/markdown/MarkdownBuilder.java b/src/main/java/io/github/robwin/markup/builder/internal/markdown/MarkdownBuilder.java index 68b41577..71fce6a0 100644 --- a/src/main/java/io/github/robwin/markup/builder/internal/markdown/MarkdownBuilder.java +++ b/src/main/java/io/github/robwin/markup/builder/internal/markdown/MarkdownBuilder.java @@ -48,11 +48,11 @@ public class MarkdownBuilder extends AbstractMarkupDocBuilder { private static final Pattern TITLE_PATTERN = Pattern.compile(String.format("^(%s{1,%d})\\s+(.*)$", Markdown.TITLE, MAX_TITLE_LEVEL + 1)); private static final Map BLOCK_STYLE = new HashMap() {{ - put(MarkupBlockStyle.EXAMPLE, null); + put(MarkupBlockStyle.EXAMPLE, ""); put(MarkupBlockStyle.LISTING, Markdown.LISTING.toString()); put(MarkupBlockStyle.LITERAL, Markdown.LISTING.toString()); - put(MarkupBlockStyle.PASSTHROUGH, null); - put(MarkupBlockStyle.SIDEBAR, null); + put(MarkupBlockStyle.PASSTHROUGH, ""); + put(MarkupBlockStyle.SIDEBAR, ""); }}; public MarkdownBuilder(){ @@ -94,19 +94,18 @@ public class MarkdownBuilder extends AbstractMarkupDocBuilder { @Override public MarkupDocBuilder block(String text, final MarkupBlockStyle style, String title, MarkupAdmonition admonition) { if (admonition != null) - documentBuilder.append(StringUtils.capitalize(admonition.name())).append(" : "); + documentBuilder.append(StringUtils.capitalize(admonition.name().toLowerCase())); if (title != null) { if (admonition != null) documentBuilder.append(" | "); - documentBuilder.append(title).append(" : "); + documentBuilder.append(title); } if (admonition != null || title != null) - documentBuilder.append(newLine); + documentBuilder.append(" : ").append(newLine); delimitedBlockText(new Markup() { public String toString() { - String separator = BLOCK_STYLE.get(style); - return separator; + return BLOCK_STYLE.get(style); } }, text); return this; diff --git a/src/test/java/io/github/robwin/markup/builder/MarkupDocBuilderTest.java b/src/test/java/io/github/robwin/markup/builder/MarkupDocBuilderTest.java index 5d0bb22b..3c66eecf 100644 --- a/src/test/java/io/github/robwin/markup/builder/MarkupDocBuilderTest.java +++ b/src/test/java/io/github/robwin/markup/builder/MarkupDocBuilderTest.java @@ -18,7 +18,6 @@ */ package io.github.robwin.markup.builder; -import io.github.robwin.markup.builder.internal.AbstractMarkupDocBuilder; import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -217,9 +216,9 @@ public class MarkupDocBuilderTest { .anchor("anchor", "text").newLine() .anchor(" Simple anchor").newLine() .anchor(" \u0240 µ&|ù This .:/-_# ").newLine() - .crossReferenceRaw("./document.md", "anchor", "text").newLine(true) + .crossReferenceRaw("./document.txt", "anchor", "text").newLine(true) .crossReferenceRaw(" \u0240 µ&|ù This .:/-_ ").newLine(true) - .crossReference("./document.md", "anchor", "text").newLine(true) + .crossReference("./document.txt", "anchor", "text").newLine(true) .crossReference(" \u0240 µ&|ù This .:/-_ ").newLine(true); builder.writeToFileWithoutExtension(builder.addFileExtension(Paths.get("build/tmp/test")), StandardCharsets.UTF_8);