Add HtmlRenderer asbtraction
This commit is contained in:
parent
84a50be0dd
commit
617a1c8d8f
@ -45,6 +45,16 @@ public interface HtmlTag {
|
|||||||
@NonNull
|
@NonNull
|
||||||
Map<String, String> attributes();
|
Map<String, String> attributes();
|
||||||
|
|
||||||
|
boolean isInline();
|
||||||
|
|
||||||
|
boolean isBlock();
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
Inline getAsInline();
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
Block getAsBlock();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents <em>really</em> inline HTML tags (unlile commonmark definitions)
|
* Represents <em>really</em> inline HTML tags (unlile commonmark definitions)
|
||||||
*/
|
*/
|
||||||
|
@ -79,6 +79,28 @@ abstract class HtmlTagImpl implements HtmlTag {
|
|||||||
", attributes=" + attributes +
|
", attributes=" + attributes +
|
||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isInline() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isBlock() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Inline getAsInline() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Block getAsBlock() {
|
||||||
|
throw new ClassCastException("Cannot cast Inline instance to Block");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static class BlockImpl extends HtmlTagImpl implements Block {
|
static class BlockImpl extends HtmlTagImpl implements Block {
|
||||||
@ -151,6 +173,28 @@ abstract class HtmlTagImpl implements HtmlTag {
|
|||||||
return attributes;
|
return attributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isInline() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isBlock() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Inline getAsInline() {
|
||||||
|
throw new ClassCastException("Cannot cast Block instance to Inline");
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Block getAsBlock() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "BlockImpl{" +
|
return "BlockImpl{" +
|
||||||
|
@ -2,6 +2,7 @@ package ru.noties.markwon.html.impl;
|
|||||||
|
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.annotation.VisibleForTesting;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -41,7 +42,8 @@ public class MarkwonHtmlParserImpl extends MarkwonHtmlParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/HTML/Inline_elements
|
// https://developer.mozilla.org/en-US/docs/Web/HTML/Inline_elements
|
||||||
private static final Set<String> INLINE_TAGS;
|
@VisibleForTesting
|
||||||
|
static final Set<String> INLINE_TAGS;
|
||||||
|
|
||||||
private static final Set<String> VOID_TAGS;
|
private static final Set<String> VOID_TAGS;
|
||||||
|
|
||||||
|
@ -42,22 +42,7 @@ public class MarkwonHtmlParserImplTest {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// all inline tags are parsed as ones
|
// all inline tags are parsed as ones
|
||||||
final List<String> tags = Arrays.asList(
|
final List<String> tags = new ArrayList<>(MarkwonHtmlParserImpl.INLINE_TAGS);
|
||||||
"a", "abbr", "acronym",
|
|
||||||
"b", "bdo", "big", "br", "button",
|
|
||||||
"cite", "code",
|
|
||||||
"dfn",
|
|
||||||
"em",
|
|
||||||
"i", "img", "input",
|
|
||||||
"kbd",
|
|
||||||
"label",
|
|
||||||
"map",
|
|
||||||
"object",
|
|
||||||
"q",
|
|
||||||
"samp", "script", "select", "small", "span", "strong", "sub", "sup",
|
|
||||||
"textarea", "time", "tt",
|
|
||||||
"var"
|
|
||||||
);
|
|
||||||
|
|
||||||
final StringBuilder html = new StringBuilder();
|
final StringBuilder html = new StringBuilder();
|
||||||
for (String tag : tags) {
|
for (String tag : tags) {
|
||||||
|
@ -15,6 +15,9 @@ android {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|
||||||
|
api project(':markwon-html-parser-api')
|
||||||
|
api project(':markwon-html-parser-impl')
|
||||||
|
|
||||||
deps.with {
|
deps.with {
|
||||||
api it['support-annotations']
|
api it['support-annotations']
|
||||||
api it['commonmark']
|
api it['commonmark']
|
||||||
|
@ -3,9 +3,10 @@ package ru.noties.markwon;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
|
import ru.noties.markwon.html.api.MarkwonHtmlParser;
|
||||||
import ru.noties.markwon.renderer.ImageSizeResolver;
|
import ru.noties.markwon.renderer.ImageSizeResolver;
|
||||||
import ru.noties.markwon.renderer.ImageSizeResolverDef;
|
import ru.noties.markwon.renderer.ImageSizeResolverDef;
|
||||||
import ru.noties.markwon.renderer.html.SpannableHtmlParser;
|
import ru.noties.markwon.renderer.html2.MarkwonHtmlRenderer;
|
||||||
import ru.noties.markwon.spans.AsyncDrawable;
|
import ru.noties.markwon.spans.AsyncDrawable;
|
||||||
import ru.noties.markwon.spans.LinkSpan;
|
import ru.noties.markwon.spans.LinkSpan;
|
||||||
import ru.noties.markwon.spans.SpannableTheme;
|
import ru.noties.markwon.spans.SpannableTheme;
|
||||||
@ -29,11 +30,13 @@ public class SpannableConfiguration {
|
|||||||
private final SyntaxHighlight syntaxHighlight;
|
private final SyntaxHighlight syntaxHighlight;
|
||||||
private final LinkSpan.Resolver linkResolver;
|
private final LinkSpan.Resolver linkResolver;
|
||||||
private final UrlProcessor urlProcessor;
|
private final UrlProcessor urlProcessor;
|
||||||
private final SpannableHtmlParser htmlParser;
|
// private final SpannableHtmlParser htmlParser;
|
||||||
private final ImageSizeResolver imageSizeResolver;
|
private final ImageSizeResolver imageSizeResolver;
|
||||||
private final SpannableFactory factory; // @since 1.1.0
|
private final SpannableFactory factory; // @since 1.1.0
|
||||||
private final boolean softBreakAddsNewLine; // @since 1.1.1
|
private final boolean softBreakAddsNewLine; // @since 1.1.1
|
||||||
private final boolean trimWhiteSpaceEnd; // @since 2.0.0
|
private final boolean trimWhiteSpaceEnd; // @since 2.0.0
|
||||||
|
private final MarkwonHtmlParser htmlParser; // @since 2.0.0
|
||||||
|
private final MarkwonHtmlRenderer htmlRenderer; // @since 2.0.0
|
||||||
|
|
||||||
private SpannableConfiguration(@NonNull Builder builder) {
|
private SpannableConfiguration(@NonNull Builder builder) {
|
||||||
this.theme = builder.theme;
|
this.theme = builder.theme;
|
||||||
@ -41,11 +44,13 @@ public class SpannableConfiguration {
|
|||||||
this.syntaxHighlight = builder.syntaxHighlight;
|
this.syntaxHighlight = builder.syntaxHighlight;
|
||||||
this.linkResolver = builder.linkResolver;
|
this.linkResolver = builder.linkResolver;
|
||||||
this.urlProcessor = builder.urlProcessor;
|
this.urlProcessor = builder.urlProcessor;
|
||||||
this.htmlParser = builder.htmlParser;
|
// this.htmlParser = builder.htmlParser;
|
||||||
this.imageSizeResolver = builder.imageSizeResolver;
|
this.imageSizeResolver = builder.imageSizeResolver;
|
||||||
this.factory = builder.factory;
|
this.factory = builder.factory;
|
||||||
this.softBreakAddsNewLine = builder.softBreakAddsNewLine;
|
this.softBreakAddsNewLine = builder.softBreakAddsNewLine;
|
||||||
this.trimWhiteSpaceEnd = builder.trimWhiteSpaceEnd;
|
this.trimWhiteSpaceEnd = builder.trimWhiteSpaceEnd;
|
||||||
|
this.htmlParser = builder.htmlParser;
|
||||||
|
this.htmlRenderer = builder.htmlRenderer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@ -73,10 +78,10 @@ public class SpannableConfiguration {
|
|||||||
return urlProcessor;
|
return urlProcessor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
// @NonNull
|
||||||
public SpannableHtmlParser htmlParser() {
|
// public SpannableHtmlParser htmlParser() {
|
||||||
return htmlParser;
|
// return htmlParser;
|
||||||
}
|
// }
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
public ImageSizeResolver imageSizeResolver() {
|
public ImageSizeResolver imageSizeResolver() {
|
||||||
@ -104,6 +109,22 @@ public class SpannableConfiguration {
|
|||||||
return trimWhiteSpaceEnd;
|
return trimWhiteSpaceEnd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 2.0.0
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
public MarkwonHtmlParser htmlParser() {
|
||||||
|
return htmlParser;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 2.0.0
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
public MarkwonHtmlRenderer htmlRenderer() {
|
||||||
|
return htmlRenderer;
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public static class Builder {
|
public static class Builder {
|
||||||
|
|
||||||
@ -113,11 +134,13 @@ public class SpannableConfiguration {
|
|||||||
private SyntaxHighlight syntaxHighlight;
|
private SyntaxHighlight syntaxHighlight;
|
||||||
private LinkSpan.Resolver linkResolver;
|
private LinkSpan.Resolver linkResolver;
|
||||||
private UrlProcessor urlProcessor;
|
private UrlProcessor urlProcessor;
|
||||||
private SpannableHtmlParser htmlParser;
|
// private SpannableHtmlParser htmlParser;
|
||||||
private ImageSizeResolver imageSizeResolver;
|
private ImageSizeResolver imageSizeResolver;
|
||||||
private SpannableFactory factory; // @since 1.1.0
|
private SpannableFactory factory; // @since 1.1.0
|
||||||
private boolean softBreakAddsNewLine; // @since 1.1.1
|
private boolean softBreakAddsNewLine; // @since 1.1.1
|
||||||
private boolean trimWhiteSpaceEnd = true; // @since 2.0.0
|
private boolean trimWhiteSpaceEnd = true; // @since 2.0.0
|
||||||
|
private MarkwonHtmlParser htmlParser; // @since 2.0.0
|
||||||
|
private MarkwonHtmlRenderer htmlRenderer; // @since 2.0.0
|
||||||
|
|
||||||
Builder(@NonNull Context context) {
|
Builder(@NonNull Context context) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
@ -153,11 +176,11 @@ public class SpannableConfiguration {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
// @NonNull
|
||||||
public Builder htmlParser(@NonNull SpannableHtmlParser htmlParser) {
|
// public Builder htmlParser(@NonNull SpannableHtmlParser htmlParser) {
|
||||||
this.htmlParser = htmlParser;
|
// this.htmlParser = htmlParser;
|
||||||
return this;
|
// return this;
|
||||||
}
|
// }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @since 1.0.1
|
* @since 1.0.1
|
||||||
@ -202,6 +225,24 @@ public class SpannableConfiguration {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 2.0.0
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
public Builder htmlParser(@NonNull MarkwonHtmlParser htmlParser) {
|
||||||
|
this.htmlParser = htmlParser;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 2.0.0
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
public Builder htmlRenderer(@NonNull MarkwonHtmlRenderer htmlRenderer) {
|
||||||
|
this.htmlRenderer = htmlRenderer;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
public SpannableConfiguration build() {
|
public SpannableConfiguration build() {
|
||||||
|
|
||||||
@ -234,15 +275,30 @@ public class SpannableConfiguration {
|
|||||||
factory = SpannableFactoryDef.create();
|
factory = SpannableFactoryDef.create();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @since 2.0.0
|
||||||
if (htmlParser == null) {
|
if (htmlParser == null) {
|
||||||
htmlParser = SpannableHtmlParser.create(
|
try {
|
||||||
factory,
|
// if impl artifact was excluded -> fallback to no-op implementation
|
||||||
theme,
|
htmlParser = ru.noties.markwon.html.impl.MarkwonHtmlParserImpl.create();
|
||||||
asyncDrawableLoader,
|
} catch (Throwable t) {
|
||||||
urlProcessor,
|
htmlParser = MarkwonHtmlParser.noOp();
|
||||||
linkResolver,
|
|
||||||
imageSizeResolver);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// @since 2.0.0
|
||||||
|
if (htmlRenderer == null) {
|
||||||
|
htmlRenderer = MarkwonHtmlRenderer.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (htmlParser == null) {
|
||||||
|
// htmlParser = SpannableHtmlParser.create(
|
||||||
|
// factory,
|
||||||
|
// theme,
|
||||||
|
// asyncDrawableLoader,
|
||||||
|
// urlProcessor,
|
||||||
|
// linkResolver,
|
||||||
|
// imageSizeResolver);
|
||||||
|
// }
|
||||||
|
|
||||||
return new SpannableConfiguration(this);
|
return new SpannableConfiguration(this);
|
||||||
}
|
}
|
||||||
|
@ -43,6 +43,7 @@ import java.util.List;
|
|||||||
import ru.noties.markwon.SpannableBuilder;
|
import ru.noties.markwon.SpannableBuilder;
|
||||||
import ru.noties.markwon.SpannableConfiguration;
|
import ru.noties.markwon.SpannableConfiguration;
|
||||||
import ru.noties.markwon.SpannableFactory;
|
import ru.noties.markwon.SpannableFactory;
|
||||||
|
import ru.noties.markwon.html.api.MarkwonHtmlParser;
|
||||||
import ru.noties.markwon.renderer.html.SpannableHtmlParser;
|
import ru.noties.markwon.renderer.html.SpannableHtmlParser;
|
||||||
import ru.noties.markwon.spans.SpannableTheme;
|
import ru.noties.markwon.spans.SpannableTheme;
|
||||||
import ru.noties.markwon.spans.TableRowSpan;
|
import ru.noties.markwon.spans.TableRowSpan;
|
||||||
@ -54,7 +55,8 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
|
|||||||
|
|
||||||
private final SpannableConfiguration configuration;
|
private final SpannableConfiguration configuration;
|
||||||
private final SpannableBuilder builder;
|
private final SpannableBuilder builder;
|
||||||
private final Deque<HtmlInlineItem> htmlInlineItems;
|
private final MarkwonHtmlParser htmlParser;
|
||||||
|
// private final Deque<HtmlInlineItem> htmlInlineItems;
|
||||||
|
|
||||||
private final SpannableTheme theme;
|
private final SpannableTheme theme;
|
||||||
private final SpannableFactory factory;
|
private final SpannableFactory factory;
|
||||||
@ -72,7 +74,8 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
|
|||||||
) {
|
) {
|
||||||
this.configuration = configuration;
|
this.configuration = configuration;
|
||||||
this.builder = builder;
|
this.builder = builder;
|
||||||
this.htmlInlineItems = new ArrayDeque<>(2);
|
this.htmlParser = configuration.htmlParser();
|
||||||
|
// this.htmlInlineItems = new ArrayDeque<>(2);
|
||||||
|
|
||||||
this.theme = configuration.theme();
|
this.theme = configuration.theme();
|
||||||
this.factory = configuration.factory();
|
this.factory = configuration.factory();
|
||||||
@ -82,6 +85,8 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
|
|||||||
public void visit(Document document) {
|
public void visit(Document document) {
|
||||||
super.visit(document);
|
super.visit(document);
|
||||||
|
|
||||||
|
configuration.htmlRenderer().render(configuration, builder, htmlParser);
|
||||||
|
|
||||||
if (configuration.trimWhiteSpaceEnd()) {
|
if (configuration.trimWhiteSpaceEnd()) {
|
||||||
builder.trimWhiteSpaceEnd();
|
builder.trimWhiteSpaceEnd();
|
||||||
}
|
}
|
||||||
@ -445,47 +450,59 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(HtmlBlock htmlBlock) {
|
public void visit(HtmlBlock htmlBlock) {
|
||||||
// http://spec.commonmark.org/0.18/#html-blocks
|
// // http://spec.commonmark.org/0.18/#html-blocks
|
||||||
final Spanned spanned = configuration.htmlParser().getSpanned(null, htmlBlock.getLiteral());
|
// final Spanned spanned = configuration.htmlParser().getSpanned(null, htmlBlock.getLiteral());
|
||||||
if (!TextUtils.isEmpty(spanned)) {
|
// if (!TextUtils.isEmpty(spanned)) {
|
||||||
builder.append(spanned);
|
// builder.append(spanned);
|
||||||
}
|
// }
|
||||||
|
// htmlParser.processFragment(builder, htmlBlock.getLiteral());
|
||||||
|
visitHtml(htmlBlock.getLiteral());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(HtmlInline htmlInline) {
|
public void visit(HtmlInline htmlInline) {
|
||||||
|
|
||||||
final SpannableHtmlParser htmlParser = configuration.htmlParser();
|
visitHtml(htmlInline.getLiteral());
|
||||||
final SpannableHtmlParser.Tag tag = htmlParser.parseTag(htmlInline.getLiteral());
|
|
||||||
|
|
||||||
if (tag != null) {
|
// htmlParser.processFragment(builder, htmlInline.getLiteral());
|
||||||
|
|
||||||
final boolean voidTag = tag.voidTag();
|
// final SpannableHtmlParser htmlParser = configuration.htmlParser();
|
||||||
if (!voidTag && tag.opening()) {
|
// final SpannableHtmlParser.Tag tag = htmlParser.parseTag(htmlInline.getLiteral());
|
||||||
// push in stack
|
//
|
||||||
htmlInlineItems.push(new HtmlInlineItem(tag, builder.length()));
|
// if (tag != null) {
|
||||||
visitChildren(htmlInline);
|
//
|
||||||
} else {
|
// final boolean voidTag = tag.voidTag();
|
||||||
|
// if (!voidTag && tag.opening()) {
|
||||||
if (!voidTag) {
|
// // push in stack
|
||||||
if (htmlInlineItems.size() > 0) {
|
// htmlInlineItems.push(new HtmlInlineItem(tag, builder.length()));
|
||||||
final HtmlInlineItem item = htmlInlineItems.pop();
|
// visitChildren(htmlInline);
|
||||||
final Object span = htmlParser.getSpanForTag(item.tag);
|
// } else {
|
||||||
setSpan(item.start, span);
|
//
|
||||||
}
|
// if (!voidTag) {
|
||||||
} else {
|
// if (htmlInlineItems.size() > 0) {
|
||||||
|
// final HtmlInlineItem item = htmlInlineItems.pop();
|
||||||
final Spanned html = htmlParser.getSpanned(tag, htmlInline.getLiteral());
|
// final Object span = htmlParser.getSpanForTag(item.tag);
|
||||||
if (!TextUtils.isEmpty(html)) {
|
// setSpan(item.start, span);
|
||||||
builder.append(html);
|
// }
|
||||||
|
// } else {
|
||||||
|
//
|
||||||
|
// final Spanned html = htmlParser.getSpanned(tag, htmlInline.getLiteral());
|
||||||
|
// if (!TextUtils.isEmpty(html)) {
|
||||||
|
// builder.append(html);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// // todo, should we append just literal?
|
||||||
|
//// builder.append(htmlInline.getLiteral());
|
||||||
|
// visitChildren(htmlInline);
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
private void visitHtml(@Nullable String html) {
|
||||||
}
|
if (html != null) {
|
||||||
} else {
|
htmlParser.processFragment(builder, html);
|
||||||
// todo, should we append just literal?
|
|
||||||
// builder.append(htmlInline.getLiteral());
|
|
||||||
visitChildren(htmlInline);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -552,14 +569,14 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class HtmlInlineItem {
|
// private static class HtmlInlineItem {
|
||||||
|
//
|
||||||
final SpannableHtmlParser.Tag tag;
|
// final SpannableHtmlParser.Tag tag;
|
||||||
final int start;
|
// final int start;
|
||||||
|
//
|
||||||
HtmlInlineItem(SpannableHtmlParser.Tag tag, int start) {
|
// HtmlInlineItem(SpannableHtmlParser.Tag tag, int start) {
|
||||||
this.tag = tag;
|
// this.tag = tag;
|
||||||
this.start = start;
|
// this.start = start;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,82 @@
|
|||||||
|
package ru.noties.markwon.renderer.html2;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import ru.noties.markwon.SpannableBuilder;
|
||||||
|
import ru.noties.markwon.SpannableConfiguration;
|
||||||
|
import ru.noties.markwon.html.api.MarkwonHtmlParser;
|
||||||
|
import ru.noties.markwon.renderer.html2.tag.EmphasisHandler;
|
||||||
|
import ru.noties.markwon.renderer.html2.tag.LinkHandler;
|
||||||
|
import ru.noties.markwon.renderer.html2.tag.StrikeHandler;
|
||||||
|
import ru.noties.markwon.renderer.html2.tag.StrongEmphasisHandler;
|
||||||
|
import ru.noties.markwon.renderer.html2.tag.SubScriptHandler;
|
||||||
|
import ru.noties.markwon.renderer.html2.tag.SuperScriptHandler;
|
||||||
|
import ru.noties.markwon.renderer.html2.tag.TagHandler;
|
||||||
|
import ru.noties.markwon.renderer.html2.tag.UnderlineHandler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 2.0.0
|
||||||
|
*/
|
||||||
|
public abstract class MarkwonHtmlRenderer {
|
||||||
|
|
||||||
|
public abstract void render(
|
||||||
|
@NonNull SpannableConfiguration configuration,
|
||||||
|
@NonNull SpannableBuilder builder,
|
||||||
|
@NonNull MarkwonHtmlParser parser
|
||||||
|
);
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public abstract TagHandler tagHandler(@NonNull String tagName);
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static MarkwonHtmlRenderer create() {
|
||||||
|
|
||||||
|
final EmphasisHandler emphasisHandler = new EmphasisHandler();
|
||||||
|
final StrongEmphasisHandler strongEmphasisHandler = new StrongEmphasisHandler();
|
||||||
|
final StrikeHandler strikeHandler = new StrikeHandler();
|
||||||
|
|
||||||
|
return builder()
|
||||||
|
.handler("i", emphasisHandler)
|
||||||
|
.handler("em", emphasisHandler)
|
||||||
|
.handler("cite", emphasisHandler)
|
||||||
|
.handler("dfn", emphasisHandler)
|
||||||
|
.handler("b", strongEmphasisHandler)
|
||||||
|
.handler("strong", strongEmphasisHandler)
|
||||||
|
.handler("sup", new SuperScriptHandler())
|
||||||
|
.handler("sub", new SubScriptHandler())
|
||||||
|
.handler("u", new UnderlineHandler())
|
||||||
|
.handler("del", strikeHandler)
|
||||||
|
.handler("s", strikeHandler)
|
||||||
|
.handler("strike", strikeHandler)
|
||||||
|
.handler("a", new LinkHandler())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static Builder builder() {
|
||||||
|
return new Builder();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Builder {
|
||||||
|
|
||||||
|
private final Map<String, TagHandler> tagHandlers = new HashMap<>(2);
|
||||||
|
|
||||||
|
public Builder handler(@NonNull String tagName, @NonNull TagHandler tagHandler) {
|
||||||
|
tagHandlers.put(tagName.toLowerCase(Locale.US), tagHandler);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public MarkwonHtmlRenderer build() {
|
||||||
|
return new MarkwonHtmlRendererImpl(
|
||||||
|
Collections.unmodifiableMap(tagHandlers)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,81 @@
|
|||||||
|
package ru.noties.markwon.renderer.html2;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.text.Spanned;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import ru.noties.markwon.SpannableBuilder;
|
||||||
|
import ru.noties.markwon.SpannableConfiguration;
|
||||||
|
import ru.noties.markwon.html.api.HtmlTag;
|
||||||
|
import ru.noties.markwon.html.api.MarkwonHtmlParser;
|
||||||
|
import ru.noties.markwon.renderer.html2.tag.TagHandler;
|
||||||
|
|
||||||
|
class MarkwonHtmlRendererImpl extends MarkwonHtmlRenderer {
|
||||||
|
|
||||||
|
private final Map<String, TagHandler> tagHandlers;
|
||||||
|
|
||||||
|
MarkwonHtmlRendererImpl(@NonNull Map<String, TagHandler> tagHandlers) {
|
||||||
|
this.tagHandlers = tagHandlers;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void render(
|
||||||
|
@NonNull final SpannableConfiguration configuration,
|
||||||
|
@NonNull final SpannableBuilder builder,
|
||||||
|
@NonNull MarkwonHtmlParser parser) {
|
||||||
|
|
||||||
|
final int length = builder.length();
|
||||||
|
|
||||||
|
parser.flushInlineTags(length, new MarkwonHtmlParser.FlushAction<HtmlTag.Inline>() {
|
||||||
|
@Override
|
||||||
|
public void apply(@NonNull List<HtmlTag.Inline> tags) {
|
||||||
|
TagHandler handler;
|
||||||
|
for (HtmlTag.Inline inline : tags) {
|
||||||
|
handler = tagHandler(inline.name());
|
||||||
|
if (handler != null) {
|
||||||
|
setSpans(builder, handler.getSpans(configuration, inline), inline.start(), inline.end());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
parser.flushBlockTags(length, new MarkwonHtmlParser.FlushAction<HtmlTag.Block>() {
|
||||||
|
@Override
|
||||||
|
public void apply(@NonNull List<HtmlTag.Block> tags) {
|
||||||
|
TagHandler handler;
|
||||||
|
for (HtmlTag.Block block : tags) {
|
||||||
|
handler = tagHandler(block.name());
|
||||||
|
if (handler != null) {
|
||||||
|
setSpans(builder, handler.getSpans(configuration, block), block.start(), block.end());
|
||||||
|
} else {
|
||||||
|
// see if any of children can be handled
|
||||||
|
apply(block.children());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
parser.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public TagHandler tagHandler(@NonNull String tagName) {
|
||||||
|
return tagHandlers.get(tagName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void setSpans(@NonNull SpannableBuilder builder, @Nullable Object spans, int start, int end) {
|
||||||
|
if (spans != null) {
|
||||||
|
if (spans.getClass().isArray()) {
|
||||||
|
for (Object o : ((Object[]) spans)) {
|
||||||
|
builder.setSpan(o, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
builder.setSpan(spans, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package ru.noties.markwon.renderer.html2.tag;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
|
import ru.noties.markwon.SpannableConfiguration;
|
||||||
|
import ru.noties.markwon.html.api.HtmlTag;
|
||||||
|
|
||||||
|
public class EmphasisHandler implements TagHandler {
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public Object getSpans(@NonNull SpannableConfiguration configuration, @NonNull HtmlTag tag) {
|
||||||
|
return configuration.factory().emphasis();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
package ru.noties.markwon.renderer.html2.tag;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
|
import ru.noties.markwon.SpannableConfiguration;
|
||||||
|
import ru.noties.markwon.html.api.HtmlTag;
|
||||||
|
|
||||||
|
public class LinkHandler implements TagHandler {
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public Object getSpans(@NonNull SpannableConfiguration configuration, @NonNull HtmlTag tag) {
|
||||||
|
final String destination = tag.attributes().get("src");
|
||||||
|
if (!TextUtils.isEmpty(destination)) {
|
||||||
|
return configuration.factory().link(
|
||||||
|
configuration.theme(),
|
||||||
|
configuration.urlProcessor().process(destination),
|
||||||
|
configuration.linkResolver()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package ru.noties.markwon.renderer.html2.tag;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
|
import ru.noties.markwon.SpannableConfiguration;
|
||||||
|
import ru.noties.markwon.html.api.HtmlTag;
|
||||||
|
|
||||||
|
public class StrikeHandler implements TagHandler {
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public Object getSpans(@NonNull SpannableConfiguration configuration, @NonNull HtmlTag tag) {
|
||||||
|
return configuration.factory().strikethrough();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package ru.noties.markwon.renderer.html2.tag;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
|
import ru.noties.markwon.SpannableConfiguration;
|
||||||
|
import ru.noties.markwon.html.api.HtmlTag;
|
||||||
|
|
||||||
|
public class StrongEmphasisHandler implements TagHandler {
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public Object getSpans(@NonNull SpannableConfiguration configuration, @NonNull HtmlTag tag) {
|
||||||
|
return configuration.factory().strongEmphasis();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package ru.noties.markwon.renderer.html2.tag;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
|
import ru.noties.markwon.SpannableConfiguration;
|
||||||
|
import ru.noties.markwon.html.api.HtmlTag;
|
||||||
|
|
||||||
|
public class SubScriptHandler implements TagHandler {
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public Object getSpans(@NonNull SpannableConfiguration configuration, @NonNull HtmlTag tag) {
|
||||||
|
return configuration.factory().subScript(configuration.theme());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package ru.noties.markwon.renderer.html2.tag;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
|
import ru.noties.markwon.SpannableConfiguration;
|
||||||
|
import ru.noties.markwon.html.api.HtmlTag;
|
||||||
|
|
||||||
|
public class SuperScriptHandler implements TagHandler {
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public Object getSpans(@NonNull SpannableConfiguration configuration, @NonNull HtmlTag tag) {
|
||||||
|
return configuration.factory().superScript(configuration.theme());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
package ru.noties.markwon.renderer.html2.tag;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
|
import ru.noties.markwon.SpannableConfiguration;
|
||||||
|
import ru.noties.markwon.html.api.HtmlTag;
|
||||||
|
|
||||||
|
public interface TagHandler {
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
Object getSpans(@NonNull SpannableConfiguration configuration, @NonNull HtmlTag tag);
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package ru.noties.markwon.renderer.html2.tag;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
|
import ru.noties.markwon.SpannableConfiguration;
|
||||||
|
import ru.noties.markwon.html.api.HtmlTag;
|
||||||
|
|
||||||
|
public class UnderlineHandler implements TagHandler {
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public Object getSpans(@NonNull SpannableConfiguration configuration, @NonNull HtmlTag tag) {
|
||||||
|
return configuration.factory().underline();
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user